251210:1709 Frontend: reeactor organization and run build
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { IsOptional, IsString, IsInt, Min } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
export class SearchUserDto {
|
||||
@ApiPropertyOptional({
|
||||
description: 'Search term (username, email, or name)',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
search?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Filter by Role ID' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
roleId?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Filter by Organization ID' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
primaryOrganizationId?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Page number', default: 1 })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Type(() => Number)
|
||||
page?: number = 1;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Items per page', default: 10 })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Type(() => Number)
|
||||
limit?: number = 10;
|
||||
}
|
||||
@@ -40,6 +40,15 @@ export class User {
|
||||
@Column({ name: 'is_active', default: true })
|
||||
isActive!: boolean;
|
||||
|
||||
@Column({ name: 'failed_attempts', default: 0 })
|
||||
failedAttempts!: number;
|
||||
|
||||
@Column({ name: 'locked_until', type: 'datetime', nullable: true })
|
||||
lockedUntil?: Date;
|
||||
|
||||
@Column({ name: 'last_login_at', type: 'timestamp', nullable: true })
|
||||
lastLoginAt?: Date;
|
||||
|
||||
@Column({ name: 'line_id', nullable: true, length: 100 })
|
||||
lineId?: string;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
Query,
|
||||
UseGuards,
|
||||
ParseIntPipe,
|
||||
} from '@nestjs/common';
|
||||
@@ -24,6 +25,7 @@ import { UserPreferenceService } from './user-preference.service';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { AssignRoleDto } from './dto/assign-role.dto';
|
||||
import { SearchUserDto } from './dto/search-user.dto';
|
||||
import { UpdatePreferenceDto } from './dto/update-preference.dto';
|
||||
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
@@ -106,8 +108,8 @@ export class UserController {
|
||||
@ApiOperation({ summary: 'List all users' })
|
||||
@ApiResponse({ status: 200, description: 'List of users' })
|
||||
@RequirePermission('user.view')
|
||||
findAll() {
|
||||
return this.userService.findAll();
|
||||
findAll(@Query() query: SearchUserDto) {
|
||||
return this.userService.findAll(query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
|
||||
@@ -50,20 +50,68 @@ export class UserService {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. ดึงข้อมูลทั้งหมด
|
||||
async findAll(): Promise<User[]> {
|
||||
return this.usersRepository.find({
|
||||
select: [
|
||||
'user_id',
|
||||
'username',
|
||||
'email',
|
||||
'firstName',
|
||||
'lastName',
|
||||
'isActive',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
],
|
||||
});
|
||||
// 2. ดึงข้อมูลทั้งหมด (Search & Pagination)
|
||||
async findAll(params?: any): Promise<any> {
|
||||
const {
|
||||
search,
|
||||
roleId,
|
||||
primaryOrganizationId,
|
||||
page = 1,
|
||||
limit = 100,
|
||||
} = params || {};
|
||||
|
||||
// Create query builder
|
||||
const query = this.usersRepository
|
||||
.createQueryBuilder('user')
|
||||
.leftJoinAndSelect('user.preference', 'preference') // Optional
|
||||
.leftJoinAndSelect('user.assignments', 'assignments')
|
||||
.leftJoinAndSelect('assignments.role', 'role')
|
||||
.select([
|
||||
'user.user_id',
|
||||
'user.username',
|
||||
'user.email',
|
||||
'user.firstName',
|
||||
'user.lastName',
|
||||
'user.lineId',
|
||||
'user.primaryOrganizationId',
|
||||
'user.isActive',
|
||||
'user.createdAt',
|
||||
'user.updatedAt',
|
||||
'assignments.id',
|
||||
'role.roleId',
|
||||
'role.roleName',
|
||||
]);
|
||||
|
||||
// Apply Filters
|
||||
if (search) {
|
||||
query.andWhere(
|
||||
'(user.username LIKE :search OR user.email LIKE :search OR user.firstName LIKE :search OR user.lastName LIKE :search)',
|
||||
{ search: `%${search}%` }
|
||||
);
|
||||
}
|
||||
|
||||
if (primaryOrganizationId) {
|
||||
query.andWhere('user.primaryOrganizationId = :orgId', {
|
||||
orgId: primaryOrganizationId,
|
||||
});
|
||||
}
|
||||
|
||||
if (roleId) {
|
||||
query.andWhere('role.roleId = :roleId', { roleId });
|
||||
}
|
||||
|
||||
// Pagination
|
||||
query.skip((page - 1) * limit).take(limit);
|
||||
|
||||
const [data, total] = await query.getManyAndCount();
|
||||
|
||||
return {
|
||||
data,
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
};
|
||||
}
|
||||
|
||||
// 3. ดึงข้อมูลรายคน
|
||||
|
||||
Reference in New Issue
Block a user