category
This commit is contained in:
@@ -1,53 +1,55 @@
|
||||
import { IsString, IsNotEmpty, IsBoolean, IsNumber, MinLength } from 'class-validator';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsString, IsOptional, IsBoolean, IsNumber } from 'class-validator';
|
||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
/**
|
||||
* DTO for updating user profile.
|
||||
* Mirrors fields defined on the User entity and keeps everything optional
|
||||
* to allow partial updates.
|
||||
*/
|
||||
export class UpdateUserDto {
|
||||
@ApiProperty({ example: ' ', description: "User's first name" })
|
||||
@IsNotEmpty()
|
||||
@ApiPropertyOptional({ description: "User's first name", example: 'John' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
firstName: string;
|
||||
firstName?: string;
|
||||
|
||||
@ApiProperty({ example: ' ', description: "User's last name" })
|
||||
@IsNotEmpty()
|
||||
@ApiPropertyOptional({ description: "User's last name", example: 'Doe' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
lastName: string;
|
||||
lastName?: string;
|
||||
|
||||
@ApiProperty({ example: ' ', description: "User's father's name" })
|
||||
@IsNotEmpty()
|
||||
@ApiPropertyOptional({ description: "User's birth date (ISO)", example: '1990-01-01' })
|
||||
@IsOptional()
|
||||
// keep as string here, caller should send ISO date; service will assign to Date
|
||||
@IsString()
|
||||
fatherName: string;
|
||||
birthDate?: string;
|
||||
|
||||
// --- Personal Details ---
|
||||
@ApiPropertyOptional({ description: "User's marriage date (ISO)", example: '2015-06-01' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
marriageDate?: string;
|
||||
|
||||
@ApiPropertyOptional({ example: true, description: 'Gender: true for male, false for female' })
|
||||
@IsNotEmpty()
|
||||
@ApiPropertyOptional({ description: "Referrer's identifier (optional)", example: 'abc123' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
referrer?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Is the user active?', example: true })
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isMale: boolean;
|
||||
isActive?: boolean;
|
||||
|
||||
@ApiPropertyOptional({ example: '1234567890', description: 'Unique national code' })
|
||||
@IsNotEmpty()
|
||||
@MinLength(10)
|
||||
nationalCode: string;
|
||||
@ApiPropertyOptional({ description: 'Gender flag (boolean)', example: true })
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
gender?: boolean;
|
||||
|
||||
@ApiPropertyOptional({ example: '1001', description: 'Employee personal code' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
personalCode: string;
|
||||
|
||||
// --- Employment Details ---
|
||||
|
||||
@ApiPropertyOptional({ example: 'قراردادی', description: 'Type of hire (e.g., permanent, temporary)' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
hireType: string;
|
||||
|
||||
@ApiPropertyOptional({ example: 'اراک', description: 'Primary work location' })
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
workLocation: string;
|
||||
|
||||
@ApiPropertyOptional({ example: 5, description: 'Years of work experience' })
|
||||
@IsNotEmpty()
|
||||
@ApiPropertyOptional({ description: 'Wallet balance (integer)', example: 0 })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
workExperienceYear: number;
|
||||
wallet?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Reward points (integer)', example: 0 })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
points?: number;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
|
||||
@Entity({ tableName: 'users' })
|
||||
export class User extends BaseEntity {
|
||||
@Property({ nullable: true })
|
||||
firstName?: string;
|
||||
@Property()
|
||||
firstName!: string;
|
||||
|
||||
@Property({ nullable: true })
|
||||
lastName?: string;
|
||||
@@ -13,8 +13,26 @@ export class User extends BaseEntity {
|
||||
@Property({ unique: true })
|
||||
phone!: string;
|
||||
|
||||
@Property({ default: null, type: 'date' })
|
||||
birthDate!: Date;
|
||||
|
||||
@Property({ default: null, type: 'date' })
|
||||
marriageDate!: Date;
|
||||
|
||||
@Property({ nullable: true })
|
||||
referrer?: string;
|
||||
|
||||
@Property({ default: true })
|
||||
isActive?: boolean;
|
||||
isActive?: boolean = true;
|
||||
|
||||
@Property({ default: true })
|
||||
gender?: boolean;
|
||||
|
||||
@Property({ default: 0, type: 'int' })
|
||||
wallet: number = 0;
|
||||
|
||||
@Property({ default: 0, type: 'int' })
|
||||
points: number = 0;
|
||||
|
||||
@OneToMany(() => RefreshToken, token => token.user)
|
||||
refreshTokens = new Collection<RefreshToken>(this);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Controller, Get, Req, UseGuards } from '@nestjs/common';
|
||||
import { Controller, Get, Req, UseGuards, Patch, Body, ValidationPipe } from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
|
||||
import { AuthGuard, type AuthRequest } from 'src/modules/auth/guards/auth.guard';
|
||||
import { UserService } from './user.service';
|
||||
// import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
|
||||
@ApiTags('User')
|
||||
@Controller('user')
|
||||
@@ -24,22 +24,23 @@ export class UserController {
|
||||
restId,
|
||||
};
|
||||
}
|
||||
|
||||
// 2. Update User Specification (PATCH /user/spec)
|
||||
// @UseGuards(AuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @ApiOperation({ summary: 'Update the user specification' })
|
||||
// @ApiBody({ type: UpdateUserDto })
|
||||
// @Patch('/')
|
||||
// async updateUserSpec(@Req() req: AuthRequest, @Body() dto: UpdateUserDto) {
|
||||
// const userId = req.user.sub;
|
||||
// const user = await this.userService.updateUser(userId, dto);
|
||||
// return {
|
||||
// message: `PATCH request: Updating specification for user ${userId} `,
|
||||
// userId,
|
||||
// user,
|
||||
// };
|
||||
// }
|
||||
// 2. Update User Specification (PATCH /user)
|
||||
@UseGuards(AuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Update the authenticated user profile' })
|
||||
@Patch('/')
|
||||
async updateUser(
|
||||
@Req() req: AuthRequest,
|
||||
@Body(new ValidationPipe({ transform: true, whitelist: true })) dto: UpdateUserDto,
|
||||
) {
|
||||
const userId = req.userId;
|
||||
const user = await this.userService.updateUser(userId, dto);
|
||||
return {
|
||||
message: `PATCH request: Updating profile for user ${userId}`,
|
||||
userId,
|
||||
user,
|
||||
};
|
||||
}
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { FilterQuery } from '@mikro-orm/core';
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { FilterQuery, RequiredEntityData } from '@mikro-orm/core';
|
||||
import { User } from './entities/user.entity';
|
||||
import { EntityManager } from '@mikro-orm/postgresql';
|
||||
// import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { FindUsersDto } from './dto/find-user.dto';
|
||||
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
||||
import { UserRepository } from './repositories/user.repository';
|
||||
@@ -18,9 +18,20 @@ export class UserService {
|
||||
let user = await this.userRepository.findOne({ phone });
|
||||
|
||||
if (!user) {
|
||||
user = this.userRepository.create({
|
||||
// firstName is required on the entity; use phone as a sensible default
|
||||
const _raw = {
|
||||
phone,
|
||||
});
|
||||
firstName: phone,
|
||||
// keep dates undefined (no value) — cast through unknown to satisfy TS typing
|
||||
birthDate: undefined,
|
||||
marriageDate: undefined,
|
||||
wallet: 0,
|
||||
points: 0,
|
||||
};
|
||||
|
||||
const createData = _raw as unknown as RequiredEntityData<User>;
|
||||
|
||||
user = this.userRepository.create(createData);
|
||||
await this.em.persistAndFlush(user);
|
||||
}
|
||||
|
||||
@@ -48,11 +59,43 @@ export class UserService {
|
||||
}
|
||||
|
||||
async create(phone: string): Promise<User> {
|
||||
const user = this.userRepository.create({ phone });
|
||||
const _raw = {
|
||||
phone,
|
||||
firstName: phone,
|
||||
birthDate: undefined,
|
||||
marriageDate: undefined,
|
||||
wallet: 0,
|
||||
points: 0,
|
||||
};
|
||||
|
||||
const createData = _raw as unknown as RequiredEntityData<User>;
|
||||
|
||||
const user = this.userRepository.create(createData);
|
||||
await this.em.persistAndFlush(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
async updateUser(userId: string, dto: UpdateUserDto): Promise<User> {
|
||||
const user = await this.userRepository.findOne({ id: userId });
|
||||
if (!user) {
|
||||
throw new NotFoundException(`User with ID ${userId} not found.`);
|
||||
}
|
||||
|
||||
// Normalize date strings into Date objects if provided
|
||||
const assignData: Partial<User> = { ...dto } as Partial<User>;
|
||||
if (dto.birthDate) {
|
||||
assignData.birthDate = new Date(dto.birthDate);
|
||||
}
|
||||
if (dto.marriageDate) {
|
||||
assignData.marriageDate = new Date(dto.marriageDate);
|
||||
}
|
||||
|
||||
this.em.assign(user, assignData);
|
||||
await this.em.flush();
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async findAll(dto: FindUsersDto): Promise<PaginatedResult<User>> {
|
||||
const { page = 1, limit = 10, search, orderBy = 'createdAt', order = 'desc' } = dto;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user