This commit is contained in:
2025-11-30 22:21:30 +03:30
parent bfd2b753a8
commit 8b4d60618a
3 changed files with 190 additions and 1 deletions
@@ -1,9 +1,10 @@
import { Controller, Get, UseGuards, Patch, Body, ValidationPipe, Post, Query } from '@nestjs/common';
import { Controller, Get, UseGuards, Patch, Body, ValidationPipe, Post, Query, Delete, Param } from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation, ApiBody, ApiOkResponse } from '@nestjs/swagger';
import { AuthGuard } from 'src/modules/auth/guards/auth.guard';
import { UserService } from '../user.service';
import { UpdateUserDto } from '../dto/update-user.dto';
import { CreateUserAddressDto } from '../dto/create-user-address.dto';
import { UpdateUserAddressDto } from '../dto/update-user-address.dto';
import { UserId } from 'src/common/decorators/user-id.decorator';
import { RestId } from 'src/common/decorators';
import { FindUsersDto } from '../dto/find-user.dto';
@@ -59,6 +60,51 @@ export class UsersController {
return address;
}
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Get a single address by ID for the authenticated user' })
@ApiOkResponse({ description: 'User address details' })
@Get('public/user/addresses/:addressId')
async getUserAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
const address = await this.userService.getUserAddress(userId, addressId);
return address;
}
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Update an address for the authenticated user' })
@ApiBody({ type: UpdateUserAddressDto })
@ApiOkResponse({ description: 'Updated user address' })
@Patch('public/user/addresses/:addressId')
async updateUserAddress(
@UserId() userId: string,
@Param('addressId') addressId: string,
@Body(new ValidationPipe({ transform: true, whitelist: true })) dto: UpdateUserAddressDto,
) {
const address = await this.userService.updateUserAddress(userId, addressId, dto);
return address;
}
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Delete an address for the authenticated user' })
@ApiOkResponse({ description: 'Address deleted successfully' })
@Delete('public/user/addresses/:addressId')
async deleteUserAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
await this.userService.deleteUserAddress(userId, addressId);
return { message: 'Address deleted successfully' };
}
@UseGuards(AuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Set an address as default for the authenticated user' })
@ApiOkResponse({ description: 'Address set as default' })
@Patch('public/user/addresses/:addressId/default')
async setDefaultAddress(@UserId() userId: string, @Param('addressId') addressId: string) {
const address = await this.userService.setDefaultAddress(userId, addressId);
return address;
}
@UseGuards(AdminAuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Get paginated list of restuarant users with filters' })
@@ -0,0 +1,57 @@
import { IsString, IsOptional, IsNumber, IsBoolean, Min, Max } from 'class-validator';
import { ApiPropertyOptional } from '@nestjs/swagger';
/**
* DTO for updating a user address.
*/
export class UpdateUserAddressDto {
@ApiPropertyOptional({ description: 'Address title/label (e.g., "Home", "Work")', example: 'Home' })
@IsOptional()
@IsString()
title?: string;
@ApiPropertyOptional({ description: 'Street address', example: '123 Main Street' })
@IsOptional()
@IsString()
address?: string;
@ApiPropertyOptional({ description: 'City name', example: 'Tehran' })
@IsOptional()
@IsString()
city?: string;
@ApiPropertyOptional({ description: 'Province', example: 'Tehran Province' })
@IsOptional()
@IsString()
province?: string;
@ApiPropertyOptional({ description: 'Postal/ZIP code', example: '12345-6789' })
@IsOptional()
@IsString()
postalCode?: string;
@ApiPropertyOptional({ description: 'Latitude coordinate', example: 35.6892, minimum: -90, maximum: 90 })
@IsOptional()
@IsNumber()
@Min(-90)
@Max(90)
latitude?: number;
@ApiPropertyOptional({ description: 'Longitude coordinate', example: 51.3890, minimum: -180, maximum: 180 })
@IsOptional()
@IsNumber()
@Min(-180)
@Max(180)
longitude?: number;
@ApiPropertyOptional({ description: 'Contact phone for this address', example: '+989123456789' })
@IsOptional()
@IsString()
phone?: string;
@ApiPropertyOptional({ description: 'Set as default address', example: false })
@IsOptional()
@IsBoolean()
isDefault?: boolean;
}
+86
View File
@@ -6,6 +6,7 @@ import { EntityManager } from '@mikro-orm/postgresql';
import { UpdateUserDto } from './dto/update-user.dto';
import { FindUsersDto } from './dto/find-user.dto';
import { CreateUserAddressDto } from './dto/create-user-address.dto';
import { UpdateUserAddressDto } from './dto/update-user-address.dto';
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
import { UserRepository } from './repositories/user.repository';
@@ -183,4 +184,89 @@ export class UserService {
await this.em.persistAndFlush(address);
return address;
}
async getUserAddress(userId: string, addressId: string): Promise<UserAddress> {
const address = await this.em.findOne(UserAddress, {
id: addressId,
user: { id: userId },
});
if (!address) {
throw new NotFoundException(`Address with ID ${addressId} not found for user ${userId}.`);
}
return address;
}
async updateUserAddress(userId: string, addressId: string, dto: UpdateUserAddressDto): Promise<UserAddress> {
const address = await this.em.findOne(UserAddress, {
id: addressId,
user: { id: userId },
});
if (!address) {
throw new NotFoundException(`Address with ID ${addressId} not found for user ${userId}.`);
}
// If setting as default, unset other default addresses
if (dto.isDefault === true) {
const existingAddresses = await this.em.find(UserAddress, {
user: { id: userId },
isDefault: true,
id: { $ne: addressId },
});
for (const existingAddress of existingAddresses) {
existingAddress.isDefault = false;
}
await this.em.flush();
}
// Update address fields
this.em.assign(address, dto);
await this.em.flush();
return address;
}
async deleteUserAddress(userId: string, addressId: string): Promise<void> {
const address = await this.em.findOne(UserAddress, {
id: addressId,
user: { id: userId },
});
if (!address) {
throw new NotFoundException(`Address with ID ${addressId} not found for user ${userId}.`);
}
// Soft delete the address
address.deletedAt = new Date();
await this.em.persistAndFlush(address);
}
async setDefaultAddress(userId: string, addressId: string): Promise<UserAddress> {
const address = await this.em.findOne(UserAddress, {
id: addressId,
user: { id: userId },
});
if (!address) {
throw new NotFoundException(`Address with ID ${addressId} not found for user ${userId}.`);
}
// Unset all other default addresses
const existingAddresses = await this.em.find(UserAddress, {
user: { id: userId },
isDefault: true,
id: { $ne: addressId },
});
for (const existingAddress of existingAddresses) {
existingAddress.isDefault = false;
}
// Set this address as default
address.isDefault = true;
await this.em.flush();
return address;
}
}