user address
This commit is contained in:
@@ -1,25 +0,0 @@
|
||||
import { Controller, Get, UseGuards, Query, ValidationPipe } from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth, ApiOperation, ApiOkResponse } from '@nestjs/swagger';
|
||||
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
||||
import { UserService } from '../user.service';
|
||||
import { FindUsersDto } from '../dto/find-user.dto';
|
||||
import { RestId } from 'src/common/decorators';
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@ApiTags('Admin/User')
|
||||
@Controller('admin/user')
|
||||
export class AdminUserController {
|
||||
constructor(private readonly userService: UserService) {}
|
||||
|
||||
@ApiOperation({ summary: 'Get paginated list of users with filters' })
|
||||
@ApiOkResponse({ description: 'Paginated list of users' })
|
||||
@Get('/')
|
||||
async findAll(
|
||||
@Query(new ValidationPipe({ transform: true, whitelist: true }))
|
||||
query: FindUsersDto,
|
||||
@RestId() restId: string,
|
||||
) {
|
||||
return this.userService.findAll(restId, query);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Controller, Get, UseGuards, Patch, Body, ValidationPipe, Post } 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 { UserId } from 'src/common/decorators/user-id.decorator';
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@ApiTags('User')
|
||||
@Controller('user')
|
||||
export class PublicUserController {
|
||||
constructor(private readonly userService: UserService) {}
|
||||
|
||||
@ApiOperation({ summary: 'Get the current authenticated user profile' })
|
||||
@Get('/me')
|
||||
async getUser(@UserId() userId: string) {
|
||||
const user = await this.userService.findById(userId);
|
||||
return user;
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: 'Update the authenticated user profile' })
|
||||
@ApiBody({ type: UpdateUserDto })
|
||||
@Patch('/')
|
||||
async updateUser(
|
||||
@UserId() userId: string,
|
||||
@Body(new ValidationPipe({ transform: true, whitelist: true })) dto: UpdateUserDto,
|
||||
) {
|
||||
const user = await this.userService.updateUser(userId, dto);
|
||||
return user;
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: 'Get all addresses for the authenticated user' })
|
||||
@ApiOkResponse({ description: 'List of user addresses' })
|
||||
@Get('/addresses')
|
||||
async getUserAddresses(@UserId() userId: string) {
|
||||
const addresses = await this.userService.getUserAddresses(userId);
|
||||
return addresses;
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: 'Create a new address for the authenticated user' })
|
||||
@ApiBody({ type: CreateUserAddressDto })
|
||||
@ApiOkResponse({ description: 'Created user address' })
|
||||
@Post('/addresses')
|
||||
async createUserAddress(
|
||||
@UserId() userId: string,
|
||||
@Body(new ValidationPipe({ transform: true, whitelist: true })) dto: CreateUserAddressDto,
|
||||
) {
|
||||
const address = await this.userService.createUserAddress(userId, dto);
|
||||
return address;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,25 @@
|
||||
import { Controller, Get, UseGuards, Patch, Body, ValidationPipe } from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth, ApiOperation, ApiBody } from '@nestjs/swagger';
|
||||
import { Controller, Get, UseGuards, Query, ValidationPipe } from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth, ApiOperation, ApiOkResponse } from '@nestjs/swagger';
|
||||
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
||||
import { UserService } from '../user.service';
|
||||
import { UpdateUserDto } from '../dto/update-user.dto';
|
||||
import { UserId } from 'src/common/decorators/user-id.decorator';
|
||||
import { FindUsersDto } from '../dto/find-user.dto';
|
||||
import { RestId } from 'src/common/decorators';
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@ApiTags('User')
|
||||
@Controller('user')
|
||||
export class UserController {
|
||||
@ApiTags('Admin/User')
|
||||
@Controller('admin/user')
|
||||
export class UsersController {
|
||||
constructor(private readonly userService: UserService) {}
|
||||
|
||||
@ApiOperation({ summary: 'Get the current authenticated user profile' })
|
||||
@Get('/me')
|
||||
async getUser(@UserId() userId: string) {
|
||||
const user = await this.userService.findById(userId);
|
||||
return user;
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: 'Update the authenticated user profile' })
|
||||
@ApiBody({ type: UpdateUserDto })
|
||||
@Patch('/')
|
||||
async updateUser(
|
||||
@UserId() userId: string,
|
||||
@Body(new ValidationPipe({ transform: true, whitelist: true })) dto: UpdateUserDto,
|
||||
@ApiOperation({ summary: 'Get paginated list of users with filters' })
|
||||
@ApiOkResponse({ description: 'Paginated list of users' })
|
||||
@Get('/')
|
||||
async findAll(
|
||||
@Query(new ValidationPipe({ transform: true, whitelist: true }))
|
||||
query: FindUsersDto,
|
||||
@RestId() restId: string,
|
||||
) {
|
||||
const user = await this.userService.updateUser(userId, dto);
|
||||
return user;
|
||||
return this.userService.findAll(restId, query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { IsString, IsOptional, IsNumber, IsBoolean, Min, Max } from 'class-validator';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
/**
|
||||
* DTO for creating a new user address.
|
||||
*/
|
||||
export class CreateUserAddressDto {
|
||||
@ApiProperty({ description: 'Address title/label (e.g., "Home", "Work")', example: 'Home' })
|
||||
@IsString()
|
||||
title!: string;
|
||||
|
||||
@ApiProperty({ description: 'Street address', example: '123 Main Street' })
|
||||
@IsString()
|
||||
address!: string;
|
||||
|
||||
@ApiProperty({ description: 'City name', example: 'Tehran' })
|
||||
@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;
|
||||
|
||||
@ApiProperty({ description: 'Latitude coordinate', example: 35.6892, minimum: -90, maximum: 90 })
|
||||
@IsNumber()
|
||||
@Min(-90)
|
||||
@Max(90)
|
||||
latitude!: number;
|
||||
|
||||
@ApiProperty({ description: 'Longitude coordinate', example: 51.3890, minimum: -180, maximum: 180 })
|
||||
@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, default: false })
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isDefault?: boolean;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Entity, Property, ManyToOne } from '@mikro-orm/core';
|
||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
import { User } from './user.entity';
|
||||
|
||||
@Entity({ tableName: 'user_addresses' })
|
||||
export class UserAddress extends BaseEntity {
|
||||
@ManyToOne(() => User)
|
||||
user!: User;
|
||||
|
||||
@Property()
|
||||
title!: string; // e.g., "Home", "Work", "Office"
|
||||
|
||||
@Property()
|
||||
address!: string;
|
||||
|
||||
@Property()
|
||||
city!: string;
|
||||
|
||||
@Property({ nullable: true })
|
||||
province?: string;
|
||||
|
||||
@Property({ nullable: true })
|
||||
postalCode?: string | null = null;
|
||||
|
||||
@Property({ type: 'float' })
|
||||
latitude!: number;
|
||||
|
||||
@Property({ type: 'float' })
|
||||
longitude!: number;
|
||||
|
||||
@Property({ nullable: true })
|
||||
phone?: string; // Contact phone for this address
|
||||
|
||||
@Property({ default: false })
|
||||
isDefault: boolean = false; // Whether this is the default address
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Entity, Property, OneToOne } from '@mikro-orm/core';
|
||||
import { Entity, Property, OneToOne, OneToMany, Collection, Cascade } from '@mikro-orm/core';
|
||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
|
||||
import { UserAddress } from './user-address.entity';
|
||||
|
||||
@Entity({ tableName: 'users' })
|
||||
export class User extends BaseEntity {
|
||||
@@ -36,4 +37,10 @@ export class User extends BaseEntity {
|
||||
|
||||
@Property({ default: 0, type: 'int' })
|
||||
points: number = 0;
|
||||
|
||||
@OneToMany(() => UserAddress, address => address.user, {
|
||||
cascade: [Cascade.ALL],
|
||||
orphanRemoval: true,
|
||||
})
|
||||
addresses = new Collection<UserAddress>(this);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UserService } from './user.service';
|
||||
import { AdminUserController } from './controllers/adminUser.controller';
|
||||
import { UsersController } from './controllers/user.controller';
|
||||
import { PublicUserController } from './controllers/public-user.controller';
|
||||
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
||||
import { User } from './entities/user.entity';
|
||||
import { UserAddress } from './entities/user-address.entity';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { UserRepository } from './repositories/user.repository';
|
||||
import { RefreshToken } from './entities/refresh-token.entity';
|
||||
|
||||
@Module({
|
||||
providers: [UserService, UserRepository],
|
||||
controllers: [AdminUserController],
|
||||
imports: [MikroOrmModule.forFeature([User, RefreshToken]), JwtModule],
|
||||
controllers: [UsersController, PublicUserController],
|
||||
imports: [MikroOrmModule.forFeature([User, UserAddress, RefreshToken]), JwtModule],
|
||||
exports: [UserService, UserRepository],
|
||||
})
|
||||
export class UserModule {}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { FilterQuery, RequiredEntityData } from '@mikro-orm/core';
|
||||
import { User } from './entities/user.entity';
|
||||
import { UserAddress } from './entities/user-address.entity';
|
||||
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 { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
||||
import { UserRepository } from './repositories/user.repository';
|
||||
|
||||
@@ -137,4 +139,48 @@ export class UserService {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async getUserAddresses(userId: string): Promise<UserAddress[]> {
|
||||
const user = await this.userRepository.findOne({ id: userId }, { populate: ['addresses'] });
|
||||
if (!user) {
|
||||
throw new NotFoundException(`User with ID ${userId} not found.`);
|
||||
}
|
||||
|
||||
// Load addresses if not already loaded
|
||||
await user.addresses.loadItems();
|
||||
return user.addresses.getItems();
|
||||
}
|
||||
|
||||
async createUserAddress(userId: string, dto: CreateUserAddressDto): Promise<UserAddress> {
|
||||
const user = await this.userRepository.findOne({ id: userId });
|
||||
if (!user) {
|
||||
throw new NotFoundException(`User with ID ${userId} not found.`);
|
||||
}
|
||||
|
||||
// If setting as default, unset other default addresses
|
||||
if (dto.isDefault) {
|
||||
const existingAddresses = await this.em.find(UserAddress, { user: { id: userId }, isDefault: true });
|
||||
for (const address of existingAddresses) {
|
||||
address.isDefault = false;
|
||||
}
|
||||
await this.em.flush();
|
||||
}
|
||||
|
||||
// Create new address
|
||||
const address = this.em.create(UserAddress, {
|
||||
user,
|
||||
title: dto.title,
|
||||
address: dto.address,
|
||||
city: dto.city,
|
||||
province: dto.province,
|
||||
postalCode: dto.postalCode,
|
||||
latitude: dto.latitude,
|
||||
longitude: dto.longitude,
|
||||
phone: dto.phone,
|
||||
isDefault: dto.isDefault ?? false,
|
||||
});
|
||||
|
||||
await this.em.persistAndFlush(address);
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user