Enhance application metadata and user interface with bilingual support

- Added bilingual comments and metadata in `layout.tsx`, including application title and description in both English and Vietnamese.
- Updated `page.tsx` to include bilingual loading messages, welcome prompts, and login instructions for improved user experience.
- Enhanced `login/page.tsx` with bilingual labels and messages for form fields and error handling.
- Improved `auth.api.ts` with detailed comments on authentication processes, including registration, login, logout, and token management.
- Updated `auth.store.ts` to include bilingual comments for state management and authentication methods.
- Enhanced API response types in `api.types.ts` with bilingual documentation for better clarity.
- Improved user management routes and services in `user.controller.ts` and `user.service.ts` with bilingual comments for better maintainability.
This commit is contained in:
Ho Ngoc Hai
2025-12-27 01:41:42 +07:00
parent 526f376c5f
commit 79866e22cf
13 changed files with 330 additions and 22 deletions

View File

@@ -1,17 +1,29 @@
import type { Metadata } from 'next';
import './globals.css';
/**
* EN: Metadata for the application
* VI: Metadata cho ứng dụng
*/
export const metadata: Metadata = {
title: 'GoodGo Platform',
description: 'Enterprise microservices platform',
description: 'Enterprise microservices platform / Nền tảng microservices doanh nghiệp',
};
/**
* EN: Root layout component for the entire application
* VI: Component layout gốc cho toàn bộ ứng dụng
*
* @param children - Child components to render / Components con để render
*/
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
// EN: Root HTML structure with English language
// VI: Cấu trúc HTML gốc với ngôn ngữ tiếng Anh
<html lang="en">
<body>{children}</body>
</html>

View File

@@ -4,31 +4,57 @@ import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { useAuthStore } from '@/stores/auth.store';
/**
* EN: Login page component for user authentication
* VI: Component trang đăng nhập để xác thực người dùng
*/
export default function LoginPage() {
// EN: Next.js router for navigation
// VI: Next.js router để điều hướng
const router = useRouter();
// EN: Auth store hooks for login functionality
// VI: Auth store hooks cho chức năng đăng nhập
const { login, isLoading } = useAuthStore();
// EN: Form state management
// VI: Quản lý trạng thái form
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
/**
* EN: Handle form submission for login
* VI: Xử lý submit form để đăng nhập
*/
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
try {
// EN: Attempt login through auth store
// VI: Thử đăng nhập thông qua auth store
await login(email, password);
// EN: Redirect to home page on successful login
// VI: Chuyển hướng về trang chủ khi đăng nhập thành công
router.push('/');
} catch (err: any) {
setError(err.message || 'Login failed');
setError(err.message || 'Login failed / Đăng nhập thất bại');
}
};
return (
// EN: Centered login form layout
// VI: Layout form đăng nhập được căn giữa
<div className="min-h-screen flex items-center justify-center">
<form onSubmit={handleSubmit} className="bg-white p-8 rounded shadow-md w-96">
<h2 className="text-2xl font-bold mb-4">Login</h2>
<h2 className="text-2xl font-bold mb-4">Login / Đăng nhập</h2>
{/* EN: Error message display / VI: Hiển thị thông báo lỗi */}
{error && <div className="text-red-500 mb-4">{error}</div>}
{/* EN: Email input field / VI: Trường nhập email */}
<div className="mb-4">
<label className="block mb-2">Email</label>
<label className="block mb-2">Email / Email</label>
<input
type="email"
value={email}
@@ -37,8 +63,10 @@ export default function LoginPage() {
required
/>
</div>
{/* EN: Password input field / VI: Trường nhập mật khẩu */}
<div className="mb-4">
<label className="block mb-2">Password</label>
<label className="block mb-2">Password / Mật khẩu</label>
<input
type="password"
value={password}
@@ -47,12 +75,14 @@ export default function LoginPage() {
required
/>
</div>
{/* EN: Submit button with loading state / VI: Nút submit với trạng thái loading */}
<button
type="submit"
disabled={isLoading}
className="w-full bg-blue-500 text-white p-2 rounded hover:bg-blue-600 disabled:opacity-50"
>
{isLoading ? 'Logging in...' : 'Login'}
{isLoading ? 'Logging in... / Đang đăng nhập...' : 'Login / Đăng nhập'}
</button>
</form>
</div>

View File

@@ -3,30 +3,46 @@
import { useAuthStore } from '@/stores/auth.store';
import { useEffect } from 'react';
/**
* EN: Home page component - main application entry point
* VI: Component trang chủ - điểm vào chính của ứng dụng
*/
export default function Home() {
// EN: Get authentication state from store
// VI: Lấy trạng thái xác thực từ store
const { user, isAuthenticated, isLoading, fetchUser } = useAuthStore();
// EN: Fetch user data on component mount if not authenticated
// VI: Fetch dữ liệu user khi component mount nếu chưa xác thực
useEffect(() => {
if (!isAuthenticated && !isLoading) {
fetchUser();
}
}, [isAuthenticated, isLoading, fetchUser]);
// EN: Show loading state while checking authentication
// VI: Hiển thị trạng thái loading trong khi kiểm tra xác thực
if (isLoading) {
return <div className="p-8">Loading...</div>;
return <div className="p-8">Loading... / Đang tải...</div>;
}
return (
// EN: Main content area with responsive padding
// VI: Khu vực nội dung chính với padding responsive
<main className="min-h-screen p-8">
<h1 className="text-4xl font-bold mb-4">GoodGo Platform</h1>
<h1 className="text-4xl font-bold mb-4">GoodGo Platform / Nền tảng GoodGo</h1>
{/* EN: Conditional rendering based on authentication status / VI: Render có điều kiện dựa trên trạng thái xác thực */}
{isAuthenticated && user ? (
// EN: Authenticated user welcome message / VI: Thông báo chào mừng người dùng đã xác thực
<div>
<p>Welcome, {user.email}!</p>
<p>Role: {user.role}</p>
<p>Welcome, {user.email}! / Chào mng, {user.email}!</p>
<p>Role: {user.role} / Vai trò: {user.role}</p>
</div>
) : (
// EN: Login prompt for unauthenticated users / VI: Nhắc đăng nhập cho người dùng chưa xác thực
<div>
<p>Please log in to continue.</p>
<p>Please log in to continue. / Vui lòng đăng nhập đ tiếp tục.</p>
</div>
)}
</main>

View File

@@ -1,13 +1,27 @@
import { apiClient } from './client';
import { LoginDto, RegisterDto, AuthResponse, ApiResponse, UserResponse } from '@goodgo/types';
/**
* EN: Authentication API service for frontend
* VI: Service API xác thực cho frontend
*/
export const authApi = {
/**
* EN: Register new user account
* VI: Đăng ký tài khoản người dùng mới
*/
register: async (data: RegisterDto): Promise<ApiResponse<AuthResponse>> => {
return apiClient.post('/auth/register', data);
},
/**
* EN: Login user and store tokens
* VI: Đăng nhập người dùng và lưu tokens
*/
login: async (data: LoginDto): Promise<ApiResponse<AuthResponse>> => {
const response = await apiClient.post('/auth/login', data);
// EN: Store tokens in client and localStorage on successful login
// VI: Lưu tokens trong client và localStorage khi đăng nhập thành công
if (response.success && response.data) {
apiClient.setAuthToken(response.data.accessToken);
if (typeof window !== 'undefined') {
@@ -17,9 +31,17 @@ export const authApi = {
return response;
},
/**
* EN: Logout user and clear tokens
* VI: Đăng xuất người dùng và xóa tokens
*/
logout: async (): Promise<ApiResponse> => {
// EN: Get refresh token from localStorage for logout request
// VI: Lấy refresh token từ localStorage cho request logout
const refreshToken = typeof window !== 'undefined' ? localStorage.getItem('refreshToken') : null;
const response = await apiClient.post('/auth/logout', { refreshToken });
// EN: Clear tokens from client and localStorage
// VI: Xóa tokens khỏi client và localStorage
apiClient.removeAuthToken();
if (typeof window !== 'undefined') {
localStorage.removeItem('refreshToken');
@@ -27,18 +49,32 @@ export const authApi = {
return response;
},
/**
* EN: Refresh access token using refresh token
* VI: Làm mới access token sử dụng refresh token
*/
refreshToken: async (refreshToken: string): Promise<ApiResponse<{ accessToken: string }>> => {
const response = await apiClient.post('/auth/refresh', { refreshToken });
// EN: Update access token in client on successful refresh
// VI: Cập nhật access token trong client khi refresh thành công
if (response.success && response.data) {
apiClient.setAuthToken(response.data.accessToken);
}
return response;
},
/**
* EN: Get current authenticated user profile
* VI: Lấy hồ sơ người dùng đã xác thực hiện tại
*/
getMe: async (): Promise<ApiResponse<UserResponse>> => {
return apiClient.get('/users/me');
},
/**
* EN: Change user password
* VI: Thay đổi mật khẩu người dùng
*/
changePassword: async (currentPassword: string, newPassword: string): Promise<ApiResponse> => {
return apiClient.put('/auth/password', { currentPassword, newPassword });
},

View File

@@ -1,8 +1,14 @@
import { createHttpClient } from '@goodgo/http-client';
// EN: Get API base URL from environment or use default
// VI: Lấy API base URL từ environment hoặc sử dụng mặc định
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost/api/v1';
/**
* EN: HTTP client instance configured for API calls
* VI: Instance HTTP client đã cấu hình cho API calls
*/
export const apiClient = createHttpClient({
baseURL: API_URL,
timeout: 30000,
timeout: 30000, // EN: 30 second timeout / VI: Timeout 30 giây
});

View File

@@ -3,16 +3,31 @@ import { persist } from 'zustand/middleware';
import { UserResponse } from '@goodgo/types';
import { authApi } from '../services/api/auth.api';
/**
* EN: Authentication state interface for Zustand store
* VI: Interface trạng thái xác thực cho Zustand store
*/
interface AuthState {
/** EN: Current authenticated user / VI: Người dùng đã xác thực hiện tại */
user: UserResponse | null;
/** EN: Authentication status / VI: Trạng thái xác thực */
isAuthenticated: boolean;
/** EN: Loading state for async operations / VI: Trạng thái loading cho async operations */
isLoading: boolean;
/** EN: Login method / VI: Method đăng nhập */
login: (email: string, password: string) => Promise<void>;
/** EN: Register method / VI: Method đăng ký */
register: (email: string, password: string, confirmPassword: string) => Promise<void>;
/** EN: Logout method / VI: Method đăng xuất */
logout: () => Promise<void>;
/** EN: Fetch current user method / VI: Method lấy thông tin user hiện tại */
fetchUser: () => Promise<void>;
}
/**
* EN: Zustand store for authentication state management with persistence
* VI: Zustand store để quản lý trạng thái xác thực với persistence
*/
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
@@ -20,6 +35,10 @@ export const useAuthStore = create<AuthState>()(
isAuthenticated: false,
isLoading: false,
/**
* EN: Login user and update store state
* VI: Đăng nhập người dùng và cập nhật trạng thái store
*/
login: async (email: string, password: string) => {
set({ isLoading: true });
try {
@@ -31,7 +50,7 @@ export const useAuthStore = create<AuthState>()(
isLoading: false,
});
} else {
throw new Error(response.error?.message || 'Login failed');
throw new Error(response.error?.message || 'Login failed / Đăng nhập thất bại');
}
} catch (error) {
set({ isLoading: false });
@@ -39,6 +58,10 @@ export const useAuthStore = create<AuthState>()(
}
},
/**
* EN: Register new user and update store state
* VI: Đăng ký người dùng mới và cập nhật trạng thái store
*/
register: async (email: string, password: string, confirmPassword: string) => {
set({ isLoading: true });
try {
@@ -50,7 +73,7 @@ export const useAuthStore = create<AuthState>()(
isLoading: false,
});
} else {
throw new Error(response.error?.message || 'Registration failed');
throw new Error(response.error?.message || 'Registration failed / Đăng ký thất bại');
}
} catch (error) {
set({ isLoading: false });
@@ -58,6 +81,10 @@ export const useAuthStore = create<AuthState>()(
}
},
/**
* EN: Logout user and clear store state
* VI: Đăng xuất người dùng và xóa trạng thái store
*/
logout: async () => {
try {
await authApi.logout();
@@ -69,6 +96,10 @@ export const useAuthStore = create<AuthState>()(
}
},
/**
* EN: Fetch current user profile from API
* VI: Lấy hồ sơ người dùng hiện tại từ API
*/
fetchUser: async () => {
set({ isLoading: true });
try {
@@ -87,6 +118,8 @@ export const useAuthStore = create<AuthState>()(
});
}
} catch (error) {
// EN: Clear user state on fetch failure
// VI: Xóa trạng thái user khi fetch thất bại
set({
user: null,
isAuthenticated: false,
@@ -96,7 +129,11 @@ export const useAuthStore = create<AuthState>()(
},
}),
{
// EN: Persist auth state to localStorage
// VI: Persist trạng thái auth vào localStorage
name: 'auth-storage',
// EN: Only persist user and isAuthenticated, exclude isLoading
// VI: Chỉ persist user và isAuthenticated, loại trừ isLoading
partialize: (state) => ({ user: state.user, isAuthenticated: state.isAuthenticated }),
}
)

View File

@@ -1,29 +1,62 @@
/**
* EN: Standardized API response interface
* VI: Interface phản hồi API chuẩn hóa
*/
export interface ApiResponse<T = any> {
/** EN: Operation success status / VI: Trạng thái thành công của thao tác */
success: boolean;
/** EN: Response data payload / VI: Payload dữ liệu phản hồi */
data?: T;
/** EN: Success message / VI: Thông báo thành công */
message?: string;
/** EN: Error details (if failed) / VI: Chi tiết lỗi (nếu thất bại) */
error?: ApiError;
/** EN: Response timestamp / VI: Timestamp phản hồi */
timestamp: string;
}
/**
* EN: API error structure
* VI: Cấu trúc lỗi API
*/
export interface ApiError {
/** EN: Error code identifier / VI: Mã định danh lỗi */
code: string;
/** EN: Human-readable error message / VI: Thông báo lỗi dễ đọc */
message: string;
/** EN: Additional error details / VI: Chi tiết lỗi bổ sung */
details?: Record<string, any>;
}
/**
* EN: Paginated API response interface
* VI: Interface phản hồi API có phân trang
*/
export interface PaginatedResponse<T> extends ApiResponse<T[]> {
/** EN: Pagination metadata / VI: Metadata phân trang */
pagination: {
/** EN: Current page number / VI: Số trang hiện tại */
page: number;
/** EN: Items per page / VI: Số items mỗi trang */
limit: number;
/** EN: Total number of items / VI: Tổng số items */
total: number;
/** EN: Total number of pages / VI: Tổng số trang */
totalPages: number;
};
}
/**
* EN: Query parameters for pagination and sorting
* VI: Tham số query cho phân trang và sắp xếp
*/
export interface PaginationQuery {
/** EN: Page number (1-based) / VI: Số trang (bắt đầu từ 1) */
page?: number;
/** EN: Items per page / VI: Số items mỗi trang */
limit?: number;
/** EN: Field to sort by / VI: Trường để sắp xếp */
sortBy?: string;
/** EN: Sort order / VI: Thứ tự sắp xếp */
sortOrder?: 'asc' | 'desc';
}

View File

@@ -1,3 +1,9 @@
// EN: Re-export all user-related types
// VI: Re-export tất cả types liên quan đến user
export * from './user.types';
// EN: Re-export all authentication-related types
// VI: Re-export tất cả types liên quan đến authentication
export * from './auth.types';
// EN: Re-export all API-related types
// VI: Re-export tất cả types liên quan đến API
export * from './api.types';

View File

@@ -2,13 +2,26 @@ import { Router } from 'express';
import { AuthController } from './auth.controller';
import { authenticate } from '../../middlewares/auth.middleware';
/**
* EN: Create and configure authentication routes
* VI: Tạo và cấu hình routes xác thực
*
* @returns Configured Express router for auth endpoints / Router Express đã cấu hình cho auth endpoints
*/
export const createAuthRouter = (): Router => {
// EN: Create router instance
// VI: Tạo instance router
const router = Router();
const authController = new AuthController();
// EN: Public authentication routes (no auth required)
// VI: Routes xác thực công khai (không yêu cầu auth)
router.post('/register', authController.register);
router.post('/login', authController.login);
router.post('/refresh', authController.refresh);
// EN: Protected routes (authentication required)
// VI: Routes được bảo vệ (yêu cầu authentication)
router.post('/logout', authenticate, authController.logout);
router.put('/password', authenticate, authController.changePassword);

View File

@@ -4,13 +4,29 @@ import { updateUserDtoSchema } from './user.dto';
import { AuthRequest } from '../../middlewares/auth.middleware';
import { ApiResponse, PaginatedResponse, UserResponse } from '@goodgo/types';
/**
* EN: User management controller handling user-related HTTP requests
* VI: Controller quản lý người dùng xử lý các yêu cầu HTTP liên quan đến người dùng
*/
export class UserController {
/** EN: User service instance / VI: Instance user service */
private userService: UserService;
/**
* EN: Initialize user controller with service dependency
* VI: Khởi tạo user controller với dependency service
*/
constructor() {
this.userService = new UserService();
}
/**
* EN: Get current authenticated user profile
* VI: Lấy hồ sơ người dùng đã xác thực hiện tại
*
* @param req - Authenticated request with user info / Request đã xác thực với thông tin người dùng
* @param res - Express response object / Đối tượng response của Express
*/
getMe = async (req: AuthRequest, res: Response): Promise<void> => {
try {
const user = await this.userService.getCurrentUser(req.user!.userId);
@@ -34,8 +50,17 @@ export class UserController {
}
};
/**
* EN: Get paginated list of users (admin endpoint)
* VI: Lấy danh sách người dùng có phân trang (endpoint admin)
*
* @param req - Express request with query parameters / Request Express với query parameters
* @param res - Express response object / Đối tượng response của Express
*/
getUsers = async (req: Request, res: Response): Promise<void> => {
try {
// EN: Parse pagination parameters from query
// VI: Parse tham số phân trang từ query
const page = parseInt(req.query.page as string) || 1;
const limit = parseInt(req.query.limit as string) || 20;
@@ -54,13 +79,20 @@ export class UserController {
success: false,
error: {
code: 'USER_002',
message: error.message || 'Failed to fetch users',
message: error.message || 'Failed to fetch users / Lấy danh sách người dùng thất bại',
},
timestamp: new Date().toISOString(),
});
}
};
/**
* EN: Get user by ID (admin endpoint)
* VI: Lấy người dùng theo ID (endpoint admin)
*
* @param req - Express request with user ID in params / Request Express với user ID trong params
* @param res - Express response object / Đối tượng response của Express
*/
getUserById = async (req: Request, res: Response): Promise<void> => {
try {
const user = await this.userService.getUserById(req.params.id);
@@ -84,15 +116,24 @@ export class UserController {
}
};
/**
* EN: Update user information (admin endpoint)
* VI: Cập nhật thông tin người dùng (endpoint admin)
*
* @param req - Express request with user ID in params and update data in body / Request Express với user ID trong params và dữ liệu cập nhật trong body
* @param res - Express response object / Đối tượng response của Express
*/
updateUser = async (req: Request, res: Response): Promise<void> => {
try {
// EN: Validate update data using Zod schema
// VI: Xác thực dữ liệu cập nhật sử dụng Zod schema
const data = updateUserDtoSchema.parse(req.body);
const user = await this.userService.updateUser(req.params.id, data);
const response: ApiResponse<UserResponse> = {
success: true,
data: user,
message: 'User updated successfully',
message: 'User updated successfully / Người dùng đã cập nhật thành công',
timestamp: new Date().toISOString(),
};
@@ -102,20 +143,29 @@ export class UserController {
success: false,
error: {
code: 'USER_004',
message: error.message || 'Failed to update user',
message: error.message || 'Failed to update user / Cập nhật người dùng thất bại',
},
timestamp: new Date().toISOString(),
});
}
};
/**
* EN: Delete user (admin endpoint)
* VI: Xóa người dùng (endpoint admin)
*
* @param req - Express request with user ID in params / Request Express với user ID trong params
* @param res - Express response object / Đối tượng response của Express
*/
deleteUser = async (req: Request, res: Response): Promise<void> => {
try {
// EN: Delete user through service
// VI: Xóa người dùng thông qua service
await this.userService.deleteUser(req.params.id);
const response: ApiResponse = {
success: true,
message: 'User deleted successfully',
message: 'User deleted successfully / Người dùng đã xóa thành công',
timestamp: new Date().toISOString(),
};
@@ -125,7 +175,7 @@ export class UserController {
success: false,
error: {
code: 'USER_005',
message: error.message || 'Failed to delete user',
message: error.message || 'Failed to delete user / Xóa người dùng thất bại',
},
timestamp: new Date().toISOString(),
});

View File

@@ -1,9 +1,17 @@
import { z } from 'zod';
/**
* EN: Zod schema for user update validation
* VI: Schema Zod để xác thực cập nhật người dùng
*/
export const updateUserDtoSchema = z.object({
/** EN: Optional email update / VI: Cập nhật email tùy chọn */
email: z.string().email().optional(),
/** EN: Optional role update / VI: Cập nhật vai trò tùy chọn */
role: z.enum(['USER', 'ADMIN', 'SUPER_ADMIN']).optional(),
/** EN: Optional active status update / VI: Cập nhật trạng thái hoạt động tùy chọn */
isActive: z.boolean().optional(),
});
/** EN: TypeScript type inferred from update user schema / VI: Type TypeScript suy ra từ schema cập nhật người dùng */
export type UpdateUserDto = z.infer<typeof updateUserDtoSchema>;

View File

@@ -2,11 +2,24 @@ import { Router } from 'express';
import { UserController } from './user.controller';
import { authenticate, authorize } from '../../middlewares/auth.middleware';
/**
* EN: Create and configure user management routes
* VI: Tạo và cấu hình routes quản lý người dùng
*
* @returns Configured Express router for user endpoints / Router Express đã cấu hình cho user endpoints
*/
export const createUserRouter = (): Router => {
// EN: Create router instance
// VI: Tạo instance router
const router = Router();
const userController = new UserController();
// EN: Public route for authenticated user profile
// VI: Route công khai cho hồ sơ người dùng đã xác thực
router.get('/me', authenticate, userController.getMe);
// EN: Admin-only routes for user management
// VI: Routes chỉ dành cho admin để quản lý người dùng
router.get('/', authenticate, authorize('ADMIN', 'SUPER_ADMIN'), userController.getUsers);
router.get('/:id', authenticate, authorize('ADMIN', 'SUPER_ADMIN'), userController.getUserById);
router.put('/:id', authenticate, authorize('ADMIN', 'SUPER_ADMIN'), userController.updateUser);

View File

@@ -3,7 +3,19 @@ import { logger } from '@goodgo/logger';
import { UpdateUserDto } from './user.dto';
import { UserResponse, Role as TypeRole } from '@goodgo/types';
/**
* EN: User management service handling CRUD operations
* VI: Service quản lý người dùng xử lý các thao tác CRUD
*/
export class UserService {
/**
* EN: Get current authenticated user information
* VI: Lấy thông tin người dùng đã xác thực hiện tại
*
* @param userId - Unique user identifier / Mã định danh duy nhất người dùng
* @returns User response data / Dữ liệu phản hồi người dùng
* @throws Error if user not found / Lỗi nếu không tìm thấy người dùng
*/
async getCurrentUser(userId: string): Promise<UserResponse> {
const user = await prisma.user.findUnique({
where: { id: userId },
@@ -23,13 +35,23 @@ export class UserService {
};
}
/**
* EN: Get user by ID (admin function)
* VI: Lấy người dùng theo ID (hàm admin)
*
* @param id - User identifier / Mã định danh người dùng
* @returns User response data / Dữ liệu phản hồi người dùng
* @throws Error if user not found / Lỗi nếu không tìm thấy người dùng
*/
async getUserById(id: string): Promise<UserResponse> {
// EN: Find user in database
// VI: Tìm người dùng trong database
const user = await prisma.user.findUnique({
where: { id },
});
if (!user) {
throw new Error('User not found');
throw new Error('User not found / Không tìm thấy người dùng');
}
return {
@@ -42,6 +64,14 @@ export class UserService {
};
}
/**
* EN: Get paginated list of users (admin function)
* VI: Lấy danh sách người dùng có phân trang (hàm admin)
*
* @param page - Page number (default: 1) / Số trang (mặc định: 1)
* @param limit - Items per page (default: 20) / Số items mỗi trang (mặc định: 20)
* @returns Paginated user list / Danh sách người dùng có phân trang
*/
async getUsers(page: number = 1, limit: number = 20) {
const skip = (page - 1) * limit;
@@ -72,7 +102,17 @@ export class UserService {
};
}
/**
* EN: Update user information (admin function)
* VI: Cập nhật thông tin người dùng (hàm admin)
*
* @param id - User identifier / Mã định danh người dùng
* @param data - Update data / Dữ liệu cập nhật
* @returns Updated user response / Phản hồi người dùng đã cập nhật
*/
async updateUser(id: string, data: UpdateUserDto): Promise<UserResponse> {
// EN: Update user in database
// VI: Cập nhật người dùng trong database
const user = await prisma.user.update({
where: { id },
data: {
@@ -82,7 +122,7 @@ export class UserService {
},
});
logger.info('User updated', { userId: id });
logger.info('User updated / Người dùng đã cập nhật', { userId: id });
return {
id: user.id,
@@ -94,11 +134,19 @@ export class UserService {
};
}
/**
* EN: Delete user (admin function)
* VI: Xóa người dùng (hàm admin)
*
* @param id - User identifier to delete / Mã định danh người dùng cần xóa
*/
async deleteUser(id: string): Promise<void> {
// EN: Delete user from database
// VI: Xóa người dùng khỏi database
await prisma.user.delete({
where: { id },
});
logger.info('User deleted', { userId: id });
logger.info('User deleted / Người dùng đã xóa', { userId: id });
}
}