chore: update project documentation, audit reports, and initialize IDE configuration files
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 29s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 2m42s
Deploy / Build Web Image (push) Failing after 27s
Deploy / Build AI Services Image (push) Failing after 29s
E2E Tests / Playwright E2E (push) Failing after 43s
Deploy / Build API Image (push) Failing after 1m31s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 6s
Security Scanning / Trivy Scan — API Image (push) Failing after 5m35s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 3m45s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
Security Scanning / Trivy Scan — Web Image (push) Failing after 13m51s
Security Scanning / Trivy Filesystem Scan (push) Failing after 14m46s
Security Scanning / Security Gate (push) Has been cancelled

This commit is contained in:
Ho Ngoc Hai
2026-04-19 03:12:54 +07:00
parent 3be106074d
commit 11f2bf26e6
101 changed files with 21312 additions and 20672 deletions

View File

@@ -1,63 +1,63 @@
# GoodGo Agent Public Profile Page — Comprehensive Exploration Report
# Trang Hồ Sơ Công Khai Môi Giới GoodGo — Báo Cáo Khám Phá Toàn Diện
**Date:** April 11, 2026
**Scope:** Full stack exploration for implementing `/agents/[id]` public profile page
**Ngày:** 11 tháng 4 năm 2026
**Phạm vi:** Khám phá toàn stack để triển khai trang hồ sơ công khai `/agents/[id]`
**Codebase:** GoodGo Platform (Next.js 15 + NestJS 10 + PostgreSQL + Prisma)
---
## 1. WEB APP STRUCTURE & ROUTING
## 1. CẤU TRÚC ỨNG DỤNG WEB & ĐỊNH TUYẾN
### File Structure
### Cấu Trúc Tệp
```
apps/web/
├── app/ # Next.js 15 App Router (Server Components)
│ ├── [locale]/ # Internationalization (i18n) at root level
│ │ ├── (admin)/ # Admin routes (protected)
│ │ ├── (auth)/ # Auth routes (sign-in, etc.)
│ │ ├── (dashboard)/ # Authenticated user dashboard
│ │ └── (public)/ # Public-facing routes
│ │ ├── listings/[id]/ # Existing listing detail page pattern
│ ├── [locale]/ # Quốc tế hóa (i18n) ở cấp gốc
│ │ ├── (admin)/ # Các tuyến admin (được bảo vệ)
│ │ ├── (auth)/ # Các tuyến xác thực (đăng nhập, v.v.)
│ │ ├── (dashboard)/ # Bảng điều khiển người dùng đã xác thực
│ │ └── (public)/ # Các tuyến công khai
│ │ ├── listings/[id]/ # Mẫu trang chi tiết bất động sản hiện có
│ │ ├── search/
│ │ ├── compare/
│ │ ├── pricing/
│ │ └── page.tsx # Landing page
│ ├── layout.tsx # Root layout
│ │ └── page.tsx # Trang đích
│ ├── layout.tsx # Layout gốc
│ ├── robots.ts # SEO: robots.txt
│ └── sitemap.ts # SEO: sitemap.xml
├── components/
│ ├── ui/ # UI library (button, card, badge, input, etc.)
│ ├── listings/ # Listing-specific components
│ ├── search/ # Search & property card components
│ ├── seo/ # JSON-LD, structured data
│ └── providers/ # Context providers
│ ├── ui/ # Thư viện UI (button, card, badge, input, v.v.)
│ ├── listings/ # Các component dành riêng cho bất động sản
│ ├── search/ # Các component tìm kiếm & thẻ bất động sản
│ ├── seo/ # JSON-LD, dữ liệu có cấu trúc
│ └── providers/ # Các context provider
├── lib/
│ ├── api-client.ts # Fetch wrapper with CSRF protection
│ ├── listings-api.ts # API client for listings
│ ├── profile-api.ts # Auth/agent profile API client
│ ├── listings-server.ts # Server-side data fetching
│ ├── currency.ts # Currency formatting utilities
│ └── validations/ # Zod schemas (listings, auth, etc.)
└── public/ # Static assets
│ ├── api-client.ts # Fetch wrapper với bảo vệ CSRF
│ ├── listings-api.ts # API client cho bất động sản
│ ├── profile-api.ts # API client hồ sơ xác thực/môi giới
│ ├── listings-server.ts # Lấy dữ liệu phía server
│ ├── currency.ts # Tiện ích định dạng tiền tệ
│ └── validations/ # Zod schemas (bất động sản, xác thực, v.v.)
└── public/ # Tài nguyên tĩnh
```
### Routing Patterns
### Các Mẫu Định Tuyến
**Public Routes (under `(public)`):**
- `/`Home/landing page
- `/listings/[id]`Listing detail (EXISTING PATTERN)
- `/search`Search results page
- `/compare`Property comparison page
- `/pricing`Pricing page
**Các Tuyến Công Khai (dưới `(public)`):**
- `/`Trang chủ/trang đích
- `/listings/[id]`Chi tiết bất động sản (MẪU HIỆN CÓ)
- `/search`Trang kết quả tìm kiếm
- `/compare`Trang so sánh bất động sản
- `/pricing`Trang bảng giá
**Key Insight:** The `(public)` route group is for unauthenticated users. **Agent profiles should follow the same pattern: `/agents/[id]`** under the `(public)` group.
**Nhận Xét Chính:** Nhóm tuyến `(public)` dành cho người dùng chưa xác thực. **Hồ sơ môi giới nên theo cùng mẫu: `/agents/[id]`** trong nhóm `(public)`.
---
## 2. EXISTING AGENT-RELATED CODE
## 2. MÃ LIÊN QUAN ĐẾN MÔI GIỚI HIỆN CÓ
### Agent Profile Type (Frontend)
**File:** `apps/web/lib/profile-api.ts`
### Kiểu Hồ Sơ Môi Giới (Frontend)
**Tệp:** `apps/web/lib/profile-api.ts`
```typescript
export interface AgentProfile {
@@ -78,15 +78,15 @@ export interface AgentProfile {
}
```
### Existing Agent API Endpoints (Backend)
**File:** `apps/api/src/modules/agents/presentation/controllers/agents.controller.ts`
### Các Endpoint API Môi Giới Hiện Có (Backend)
**Tệp:** `apps/api/src/modules/agents/presentation/controllers/agents.controller.ts`
```typescript
// Endpoints:
GET /agents/me/dashboard # Agent dashboard (authenticated)
POST /agents/:agentId/recalculate-score # Recalculate quality score (admin)
GET /agents/me/dashboard # Bảng điều khiển môi giới (đã xác thực)
POST /agents/:agentId/recalculate-score # Tính lại điểm chất lượng (admin)
// Returns:
// Trả về:
interface AgentDashboardData {
agentId: string;
qualityScore: number;
@@ -105,8 +105,8 @@ interface AgentDashboardData {
}
```
### Prisma Schema — Agent Model
**File:** `prisma/schema.prisma`
### Schema Prisma — Model Agent
**Tệp:** `prisma/schema.prisma`
```prisma
model Agent {
@@ -132,31 +132,31 @@ model Agent {
}
```
**Related models:**
- `User`Agent's user account (fullName, avatarUrl, phone, email, role, kycStatus)
- `Listing`Properties agent represents (has `agentId` foreign key)
- `Lead`Leads tracked by agent
**Các model liên quan:**
- `User`Tài khoản người dùng của môi giới (fullName, avatarUrl, phone, email, role, kycStatus)
- `Listing`Các bất động sản môi giới đại diện (có khóa ngoại `agentId`)
- `Lead`Các đầu mối được môi giới theo dõi
---
## 3. AGENT API ENDPOINTS NEEDED FOR PUBLIC PROFILE
## 3. CÁC ENDPOINT API MÔI GIỚI CẦN THIẾT CHO HỒ SƠ CÔNG KHAI
Based on the existing architecture, we need to create a **public endpoint** to fetch agent profile data:
Dựa trên kiến trúc hiện có, chúng ta cần tạo một **endpoint công khai** để lấy dữ liệu hồ sơ môi giới:
### Proposed Endpoint
### Endpoint Đề Xuất
```http
GET /agents/:agentId/profile
```
**Response Structure (Public Profile DTO):**
**Cấu Trúc Phản Hồi (DTO Hồ Sơ Công Khai):**
```typescript
interface AgentPublicProfile {
id: string;
fullName: string;
avatarUrl: string | null;
// Agent-specific fields
// Các trường dành riêng cho môi giới
licenseNumber: string | null;
agency: string | null;
qualityScore: number;
@@ -164,39 +164,39 @@ interface AgentPublicProfile {
serviceAreas: string[];
isVerified: boolean;
// Stats
// Thống kê
totalListings: number;
activeListings: number;
avgReviewRating: number;
totalReviews: number;
// Contact (optional, may require user preferences)
// Liên hệ (tùy chọn, có thể yêu cầu tùy chọn người dùng)
phone?: string;
// Timestamps
// Dấu thời gian
createdAt: string;
updatedAt: string;
}
```
**Related Endpoints Needed:**
**Các Endpoint Liên Quan Cần Thiết:**
```http
GET /listings?agentId=:agentId&status=ACTIVE # Agent's active listings
GET /reviews/stats?targetType=AGENT&targetId=:agentId # Agent reviews stats
GET /reviews?targetType=AGENT&targetId=:agentId&limit=10 # Recent agent reviews
GET /listings?agentId=:agentId&status=ACTIVE # Bt đng sn đang hot đng ca môi gii
GET /reviews/stats?targetType=AGENT&targetId=:agentId # Thng kê đánh giá môi gii
GET /reviews?targetType=AGENT&targetId=:agentId&limit=10 # Đánh giá gn đây ca môi gii
```
These endpoints already exist and are public (no authentication required).
Các endpoint này đã tồn tại và công khai (không yêu cầu xác thực).
---
## 4. SHARED UI COMPONENTS & DESIGN PATTERNS
## 4. CÁC COMPONENT UI DÙNG CHUNG & MẪU THIẾT KẾ
### Tailwind/Design System
**File:** `apps/web/tailwind.config.ts`
### Tailwind/Hệ Thống Thiết Kế
**Tệp:** `apps/web/tailwind.config.ts`
```typescript
// CSS Variables used (dark mode support)
// Các biến CSS được sử dụng (hỗ trợ chế độ tối)
colors: {
primary, primary-foreground
secondary, secondary-foreground
@@ -206,7 +206,7 @@ colors: {
card, card-foreground
}
// Radius variable: var(--radius)
// Biến Radius: var(--radius)
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
@@ -214,20 +214,20 @@ borderRadius: {
}
```
### Available UI Components
**File:** `apps/web/components/ui/`
### Các Component UI Có Sẵn
**Tệp:** `apps/web/components/ui/`
- `button.tsx`Styled button with variants
- `card.tsx`Card (CardContent, etc.)
- `badge.tsx`Badge with variants
- `input.tsx`, `label.tsx`Form controls
- `dialog.tsx`Modal dialog
- `tabs.tsx`Tab navigation
- `table.tsx`Data table
- `button.tsx`Nút có kiểu dáng với các biến thể
- `card.tsx`Thẻ (CardContent, v.v.)
- `badge.tsx`Huy hiệu với các biến thể
- `input.tsx`, `label.tsx`Các điều khiển biểu mẫu
- `dialog.tsx`Hộp thoại modal
- `tabs.tsx`Điều hướng tab
- `table.tsx`Bảng dữ liệu
### Key Component Patterns
### Các Mẫu Component Chính
#### Badge Component
#### Component Badge
```typescript
<Badge variant="default">Đã xác minh</Badge>
<Badge variant="secondary">Info badge</Badge>
@@ -235,70 +235,70 @@ borderRadius: {
<Badge variant="destructive">Danger</Badge>
```
#### Card Pattern
#### Mẫu Card
```typescript
<Card>
<CardContent className="p-4">
{/* Content */}
{/* Nội dung */}
</CardContent>
</Card>
```
#### Property Card (Listing Display) - REUSE THIS
**File:** `apps/web/components/search/property-card.tsx`
#### Thẻ Bất Động Sản (Hiển Thị Bất Động Sản) - TÁI SỬ DỤNG CÁI NÀY
**Tệp:** `apps/web/components/search/property-card.tsx`
Shows a property with:
- Image gallery
- Price (formatted)
- Title & address
- Type, area, bedrooms badges
- Transaction type badge
Hiển thị bất động sản với:
- Thư viện ảnh
- Giá (đã định dạng)
- Tiêu đề & địa chỉ
- Huy hiệu loại, diện tích, số phòng ngủ
- Huy hiệu loại giao dịch
**Reusable for:** Agent's listings display
**Có thể tái sử dụng cho:** Hiển thị bất động sản của môi giới
---
## 5. STYLING & DESIGN PATTERNS
## 5. KIỂU DÁNG & MẪU THIẾT KẾ
### Global CSS
**File:** `apps/web/app/globals.css`
### CSS Toàn Cục
**Tệp:** `apps/web/app/globals.css`
Uses CSS variables for theming:
Sử dụng biến CSS để tạo chủ đề:
```css
--primary: hsl(var(--primary-h), var(--primary-s), var(--primary-l))
--background: hsl(...)
--card: hsl(...)
/* Dark mode support via [data-theme="dark"] */
/* Hỗ trợ chế độ tối qua [data-theme="dark"] */
```
### Typography
- Font: Inter (configured in tailwind.config.ts via CSS variable `--font-inter`)
- Heading levels: h1, h2, h3, h4
- Use classes: `text-lg font-bold`, `text-sm text-muted-foreground`
### Kiểu Chữ
- Font: Inter (được cấu hình trong tailwind.config.ts qua biến CSS `--font-inter`)
- Các cấp tiêu đề: h1, h2, h3, h4
- Sử dụng các lớp: `text-lg font-bold`, `text-sm text-muted-foreground`
### Spacing
- Tailwind standard: `p-4`, `mt-8`, `gap-3`, etc.
- Card padding: `p-4`
- Section padding: `py-16`, `py-24` for hero sections
### Khoảng Cách
- Tailwind tiêu chuẩn: `p-4`, `mt-8`, `gap-3`, v.v.
- Padding card: `p-4`
- Padding phần: `py-16`, `py-24` cho các phần hero
### Example Layout Pattern
### Mẫu Layout Ví Dụ
```typescript
<section className="py-16 md:py-24">
<div className="mx-auto max-w-7xl px-4">
{/* Content */}
{/* Nội dung */}
</div>
</section>
```
---
## 6. STATE MANAGEMENT & DATA FETCHING
## 6. QUẢN LÝ TRẠNG THÁI & LẤY DỮ LIỆU
### API Client Pattern
**File:** `apps/web/lib/api-client.ts`
### Mẫu API Client
**Tệp:** `apps/web/lib/api-client.ts`
```typescript
// Usage:
// Cách sử dụng:
const apiClient = {
get: <T>(endpoint: string, headers?: HeadersInit) => request<T>(endpoint, { method: 'GET', headers }),
post: <T>(endpoint: string, body?: unknown, headers?: HeadersInit) => request<T>(endpoint, { method: 'POST', body, headers }),
@@ -306,18 +306,18 @@ const apiClient = {
delete: <T>(endpoint: string, headers?: HeadersInit) => request<T>(endpoint, { method: 'DELETE', headers }),
};
// CSRF protection included automatically
// Bảo vệ CSRF được bao gồm tự động
```
### Server-Side Data Fetching Pattern
**File:** `apps/web/lib/listings-server.ts`
### Mẫu Lấy Dữ Liệu Phía Server
**Tệp:** `apps/web/lib/listings-server.ts`
```typescript
// Example: fetch on server at build time or request time
// Ví dụ: lấy dữ liệu trên server tại thời điểm build hoặc thời điểm request
export async function fetchListingById(id: string) {
try {
const res = await fetch(`${API_BASE_URL}/listings/${id}`, {
next: { revalidate: 3600 } // ISR: revalidate every 1 hour
next: { revalidate: 3600 } // ISR: xác thực lại mỗi 1 giờ
});
if (!res.ok) return null;
return res.json();
@@ -327,9 +327,9 @@ export async function fetchListingById(id: string) {
}
```
### Client-Side Data Fetching Pattern
### Mẫu Lấy Dữ Liệu Phía Client
```typescript
// In React component (using 'use client')
// Trong React component (sử dụng 'use client')
const [data, setData] = useState(null);
React.useEffect(() => {
@@ -341,17 +341,17 @@ React.useEffect(() => {
}, []);
```
### No Global State (Zustand)
- **Currently no Zustand stores** for agent data in codebase
- **Pattern:** Fetch data in page component → pass to child components
- Dashboard uses local useState for profile fetch
### Không Có Trạng Thái Toàn Cục (Zustand)
- **Hiện tại không có Zustand store** cho dữ liệu môi giới trong codebase
- **Mẫu:** Lấy dữ liệu trong page component → truyền xuống các component con
- Dashboard sử dụng useState cục bộ để lấy hồ sơ
---
## 7. SEO PATTERNS & STRUCTURED DATA
## 7. MẪU SEO & DỮ LIỆU CÓ CẤU TRÚC
### Metadata Generation Pattern
**File:** `apps/web/app/[locale]/(public)/listings/[id]/page.tsx`
### Mẫu Tạo Metadata
**Tệp:** `apps/web/app/[locale]/(public)/listings/[id]/page.tsx`
```typescript
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
@@ -383,11 +383,11 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
}
```
### JSON-LD Structured Data
**File:** `apps/web/components/seo/json-ld.tsx`
### Dữ Liệu Có Cấu Trúc JSON-LD
**Tệp:** `apps/web/components/seo/json-ld.tsx`
```typescript
// Example: RealEstateListing schema
// Ví dụ: Schema RealEstateListing
export function generateListingJsonLd(listing, siteUrl) {
return {
'@context': 'https://schema.org',
@@ -395,17 +395,17 @@ export function generateListingJsonLd(listing, siteUrl) {
name: property.title,
url: `${siteUrl}/listings/${listing.id}`,
offers: { '@type': 'Offer', price: priceNum, priceCurrency: 'VND' },
// ... more properties
// ... thêm thuộc tính
};
}
// Usage in page:
// Cách dùng trong trang:
<JsonLd data={listingJsonLd} />
<JsonLd data={breadcrumbJsonLd} />
```
### For Agent Profile (Schema.org)
**Appropriate schema:** `LocalBusiness` or `ProfessionalService`
### Cho Hồ Sơ Môi Giới (Schema.org)
**Schema phù hợp:** `LocalBusiness` hoặc `ProfessionalService`
```json
{
@@ -429,10 +429,10 @@ export function generateListingJsonLd(listing, siteUrl) {
---
## 8. EXISTING LISTING CARD COMPONENTS
## 8. CÁC COMPONENT THẺ BẤT ĐỘNG SẢN HIỆN CÓ
### Property Card Component
**File:** `apps/web/components/search/property-card.tsx`
### Component Thẻ Bất Động Sản
**Tệp:** `apps/web/components/search/property-card.tsx`
```typescript
interface PropertyCardProps {
@@ -440,42 +440,42 @@ interface PropertyCardProps {
compact?: boolean;
}
// Displays:
// - Image gallery (with count badge)
// - Price (formatted)
// - Title & address
// - Badges: Transaction type, property type, area, bedrooms, bathrooms, direction
// - Compare button
// Hiển thị:
// - Thư viện ảnh (với huy hiệu đếm)
// - Giá (đã định dạng)
// - Tiêu đề & địa chỉ
// - Huy hiệu: Loại giao dịch, loại bất động sản, diện tích, phòng ngủ, phòng tắm, hướng
// - Nút so sánh
```
### Used in:
- Home page featured listings
- Search results
- Comparison page
### Được Sử Dụng Trong:
- Bất động sản nổi bật trên trang chủ
- Kết quả tìm kiếm
- Trang so sánh
**For Agent Profile:** Can reuse this component to display agent's listings!
**Cho Hồ Sơ Môi Giới:** Có thể tái sử dụng component này để hiển thị bất động sản của môi giới!
---
## 9. REVIEW & RATING COMPONENTS
## 9. CÁC COMPONENT ĐÁNH GIÁ & XẾP HẠNG
### Review API Endpoints
**File:** `apps/api/src/modules/reviews/presentation/controllers/reviews.controller.ts`
### Các Endpoint API Đánh Giá
**Tệp:** `apps/api/src/modules/reviews/presentation/controllers/reviews.controller.ts`
```typescript
// Endpoints:
GET /reviews # List reviews by target (pagination)
GET /reviews/stats # Get aggregate rating stats
GET /reviews/me # Get authenticated user's reviews
POST /reviews # Create review (authenticated)
DELETE /reviews/:id # Delete own review
GET /reviews # Danh sách đánh giá theo đi tượng (phân trang)
GET /reviews/stats # Lấy thống xếp hạng tổng hợp
GET /reviews/me # Lấy đánh giá của người dùng đã xác thực
POST /reviews # Tạo đánh giá (đã xác thực)
DELETE /reviews/:id # Xóa đánh giá của bản thân
// Query params:
// Tham số truy vấn:
GET /reviews?targetType=AGENT&targetId=:id&page=1&limit=20
GET /reviews/stats?targetType=AGENT&targetId=:id
```
### Review DTO
### DTO Đánh Giá
```typescript
interface ReviewItemData {
id: string;
@@ -485,7 +485,7 @@ interface ReviewItemData {
rating: number; // 1-5
comment: string | null;
createdAt: string;
// User info:
// Thông tin người dùng:
user: {
id: string;
fullName: string;
@@ -508,24 +508,24 @@ interface ReviewStatsData {
}
```
### No Review Display Component Yet
- **Dashboard profile** shows `agentProfile.totalReviews` and `avgReviewRating`
- **Opportunity:** Create a reusable `ReviewCard` and `RatingStars` component for agent profile
### Chưa Có Component Hiển Thị Đánh Giá
- **Hồ sơ bảng điều khiển** hiển thị `agentProfile.totalReviews` `avgReviewRating`
- **Cơ hội:** Tạo component `ReviewCard` `RatingStars` có thể tái sử dụng cho hồ sơ môi giới
---
## 10. TYPE DEFINITIONS & INTERFACES
## 10. ĐỊNH NGHĨA KIỂU & GIAO DIỆN
### Core Types Used Frontend
### Các Kiểu Cốt Lõi Sử Dụng Ở Frontend
```typescript
// From listings-api.ts
// Từ listings-api.ts
export type TransactionType = 'SALE' | 'RENT';
export type PropertyType = 'APARTMENT' | 'HOUSE' | 'VILLA' | 'LAND' | 'OFFICE' | 'SHOPHOUSE';
export type ListingStatus = 'DRAFT' | 'PENDING_REVIEW' | 'ACTIVE' | 'RESERVED' | 'SOLD' | 'RENTED' | 'EXPIRED' | 'REJECTED';
export type Direction = 'NORTH' | 'SOUTH' | 'EAST' | 'WEST' | 'NORTHEAST' | 'NORTHWEST' | 'SOUTHEAST' | 'SOUTHWEST';
// From profile-api.ts
// Từ profile-api.ts
export interface AgentProfile {
id: string;
email: string | null;
@@ -543,7 +543,7 @@ export interface AgentProfile {
isVerified: boolean;
}
// From listings-api.ts
// Từ listings-api.ts
export interface ListingDetail {
id: string;
status: ListingStatus;
@@ -560,13 +560,13 @@ export interface ListingDetail {
district: string;
city: string;
media: PropertyMedia[];
// ... 15+ other properties
// ... hơn 15 thuộc tính khác
};
}
```
### Validation Schemas (Zod)
**File:** `apps/web/lib/validations/listings.ts`
### Các Schema Xác Thực (Zod)
**Tệp:** `apps/web/lib/validations/listings.ts`
```typescript
export const TRANSACTION_TYPES = [
@@ -577,7 +577,7 @@ export const TRANSACTION_TYPES = [
export const PROPERTY_TYPES = [
{ value: 'APARTMENT', label: 'Căn hộ' },
{ value: 'HOUSE', label: 'Nhà riêng' },
// ... more
// ... thêm
] as const;
export const LISTING_STATUSES = {
@@ -589,61 +589,61 @@ export const LISTING_STATUSES = {
---
## 11. IMPLEMENTATION CHECKLIST FOR AGENT PROFILE PAGE
## 11. DANH SÁCH KIỂM TRA TRIỂN KHAI TRANG HỒ SƠ MÔI GIỚI
### Backend (NestJS API)
**New DTO & Interfaces:**
**DTO & Giao Diện Mới:**
```
✓ CreateGetAgentPublicProfileQuery
✓ GetAgentPublicProfileHandler
✓ AgentPublicProfileDto (response)
Update agent.repository.ts with method: getPublicProfile(agentId: string)
Update prisma-agent.repository.ts to fetch via Prisma
✓ AgentPublicProfileDto (phản hồi)
Cập nhật agent.repository.ts với phương thức: getPublicProfile(agentId: string)
Cập nhật prisma-agent.repository.ts để lấy dữ liệu qua Prisma
```
**New Endpoint:**
**Endpoint Mới:**
```
✓ GET /agents/:agentId/profile (public, no auth)
Returns: AgentPublicProfileDto
Validates agentId exists
Returns 404 if not found
✓ GET /agents/:agentId/profile (công khai, không xác thực)
Trả về: AgentPublicProfileDto
Xác thực agentId tồn tại
Trả về 404 nếu không tìm thấy
```
**Leverage Existing:**
- Review queries: Already public
- Listings queries: Already public
- User profile: Linking to agent.userId
**Tận Dụng Hiện Có:**
- Các truy vấn đánh giá: Đã công khai
- Các truy vấn bất động sản: Đã công khai
- Hồ sơ người dùng: Liên kết đến agent.userId
### Frontend (Next.js)
**New Files:**
**Các Tệp Mới:**
```
✓ apps/web/app/[locale]/(public)/agents/ (directory)
✓ apps/web/app/[locale]/(public)/agents/[id]/ (directory)
✓ apps/web/app/[locale]/(public)/agents/ (thư mục)
✓ apps/web/app/[locale]/(public)/agents/[id]/ (thư mục)
✓ apps/web/app/[locale]/(public)/agents/[id]/page.tsx (server component)
✓ apps/web/app/[locale]/(public)/agents/[id]/layout.tsx (optional)
✓ apps/web/app/[locale]/(public)/agents/[id]/layout.tsx (tùy chọn)
✓ apps/web/lib/agents-api.ts (API client)
✓ apps/web/lib/agents-server.ts (server-side fetch for ISR)
✓ apps/web/components/agents/ (new directory)
✓ apps/web/lib/agents-server.ts (lấy dữ liệu phía server cho ISR)
✓ apps/web/components/agents/ (thư mục mới)
✓ apps/web/components/agents/agent-detail-client.tsx (client component)
✓ apps/web/components/agents/agent-listings-section.tsx
✓ apps/web/components/agents/agent-reviews-section.tsx
```
**Page Structure (follows listing pattern):**
**Cấu Trúc Trang (theo mẫu bất động sản):**
```typescript
// [id]/page.tsx (Server Component)
export async function generateMetadata({ params }): Promise<Metadata> {
// Fetch agent
// Return title, description, OG image, canonical URL
// Lấy thông tin môi giới
// Trả về title, description, OG image, canonical URL
}
export default async function AgentProfilePage({ params }) {
// Fetch agent profile (server-side, ISR)
// Lấy hồ sơ môi giới (phía server, ISR)
// Render JsonLd breadcrumb
// Render JsonLd LocalBusiness or ProfessionalService
// Pass to client component
// Render JsonLd LocalBusiness hoặc ProfessionalService
// Truyền xuống client component
return <>
<JsonLd data={agentJsonLd} />
<AgentDetailClient agent={agent} />
@@ -652,11 +652,11 @@ export default async function AgentProfilePage({ params }) {
```
**Client Component:**
- Display agent info (name, avatar, bio, license, agency)
- Show quality score & badges
- Render reviews section
- Render listings section
- Contact/inquiry button (optional)
- Hiển thị thông tin môi giới (tên, avatar, bio, giấy phép, công ty)
- Hiển thị điểm chất lượng & huy hiệu
- Render phần đánh giá
- Render phần bất động sn
- Nút liên hệ/yêu cầu tư vấn (tùy chọn)
**API Client:**
```typescript
@@ -667,77 +667,76 @@ export const agentsApi = {
---
## 12. KEY FILES TO REFERENCE/ADAPT
## 12. CÁC TỆP CHÍNH CẦN THAM KHẢO/ĐIỀU CHỈNH
### Backend
| File | Purpose |
| Tệp | Mục Đích |
|------|---------|
| `apps/api/src/modules/agents/presentation/controllers/agents.controller.ts` | Add new public endpoint |
| `apps/api/src/modules/agents/domain/repositories/agent.repository.ts` | Add interface for public profile method |
| `apps/api/src/modules/agents/infrastructure/repositories/prisma-agent.repository.ts` | Implement public profile fetch |
| `apps/api/src/modules/agents/application/queries/` | Create new query handler for public profile |
| `prisma/schema.prisma` | Reference for Agent model |
| `apps/api/src/modules/agents/presentation/controllers/agents.controller.ts` | Thêm endpoint công khai mới |
| `apps/api/src/modules/agents/domain/repositories/agent.repository.ts` | Thêm giao diện cho phương thức hồ sơ công khai |
| `apps/api/src/modules/agents/infrastructure/repositories/prisma-agent.repository.ts` | Triển khai lấy hồ sơ công khai |
| `apps/api/src/modules/agents/application/queries/` | Tạo query handler mới cho hồ sơ công khai |
| `prisma/schema.prisma` | Tham chiếu cho model Agent |
### Frontend — Reference Examples
### Frontend — Ví Dụ Tham Khảo
| File | Purpose | Reuse Pattern |
| Tệp | Mục Đích | Mẫu Tái Sử Dụng |
|------|---------|---|
| `apps/web/app/[locale]/(public)/listings/[id]/page.tsx` | Listing detail page | Use as template for agent page |
| `apps/web/components/search/property-card.tsx` | Property card | Reuse for agent's listings |
| `apps/web/lib/listings-api.ts` | Listings API client | Create similar agents-api.ts |
| `apps/web/lib/listings-server.ts` | Server-side fetch | Create similar agents-server.ts |
| `apps/web/components/seo/json-ld.tsx` | Structured data | Adapt for LocalBusiness schema |
| `apps/web/lib/currency.ts` | Price formatting | Reuse for listing prices |
| `apps/web/tailwind.config.ts` | Design system | Reference for styling |
| `apps/web/app/[locale]/(public)/listings/[id]/page.tsx` | Trang chi tiết bất động sản | Dùng làm template cho trang môi giới |
| `apps/web/components/search/property-card.tsx` | Thẻ bất động sản | Tái sử dụng cho bất động sản của môi giới |
| `apps/web/lib/listings-api.ts` | API client bất động sản | Tạo agents-api.ts tương tự |
| `apps/web/lib/listings-server.ts` | Lấy dữ liệu phía server | Tạo agents-server.ts tương tự |
| `apps/web/components/seo/json-ld.tsx` | Dữ liệu có cấu trúc | Điều chỉnh cho schema LocalBusiness |
| `apps/web/lib/currency.ts` | Định dạng giá | Tái sử dụng cho giá bất động sản |
| `apps/web/tailwind.config.ts` | Hệ thống thiết kế | Tham khảo cho kiểu dáng |
---
## 13. SUMMARY OF KEY FINDINGS
## 13. TÓM TẮT CÁC PHÁT HIỆN CHÍNH
### What Exists (Reusable)
Agent model in Prisma with all needed fields
API endpoints for listings, reviews (public)
UI components: Card, Badge, Button, etc.
Tailwind design system with dark mode
SEO pattern with metadata generation & JSON-LD
Image gallery component for listings
✅ PropertyCard component for listings display
✅ API client with CSRF protection
Server-side data fetching with ISR pattern
### Những Gì Đã Có (Có Thể Tái Sử Dụng)
Model Agent trong Prisma với tất cả các trường cần thiết
Các endpoint API cho bất động sản, đánh giá (công khai)
Các component UI: Card, Badge, Button, v.v.
Hệ thống thiết kế Tailwind với chế độ tối
Mẫu SEO với tạo metadata & JSON-LD
Component thư viện ảnh cho bất động sản
Component PropertyCard để hiển thị bất động sản
✅ API client với bảo vệ CSRF
Lấy dữ liệu phía server với mẫu ISR
### What Needs Building (Agent Profile)
🔨 `/agents/[id]` page (server component with metadata)
🔨 `AgentDetailClient` component (client-side rendering)
🔨 Public endpoint: `GET /agents/:agentId/profile`
🔨 Agent listings section (reuse PropertyCard)
🔨 Agent reviews section (fetch & display reviews)
🔨 Rating stars/aggregate display component
🔨 agents-api.ts (fetch agent profile)
🔨 agents-server.ts (server-side fetch for ISR)
🔨 JSON-LD LocalBusiness schema for agent
### Những Gì Cần Xây Dựng (Hồ Sơ Môi Giới)
🔨 Trang `/agents/[id]` (server component với metadata)
🔨 Component `AgentDetailClient` (render phía client)
🔨 Endpoint công khai: `GET /agents/:agentId/profile`
🔨 Phần bất động sản của môi giới (tái sử dụng PropertyCard)
🔨 Phần đánh giá của môi giới (lấy & hiển thị đánh giá)
🔨 Component hiển thị sao xếp hạng/tổng hợp
🔨 agents-api.ts (lấy hồ sơ môi giới)
🔨 agents-server.ts (lấy dữ liệu phía server cho ISR)
🔨 Schema JSON-LD LocalBusiness cho môi giới
### Architecture Decisions
- **Routing:** Place at `apps/web/app/[locale]/(public)/agents/[id]/page.tsx`
- **Pattern:** Follow listing detail page pattern exactly
- **Metadata:** Use generateMetadata() server function
- **Components:** Split into Server Component (page) + Client Component (interactive)
- **SEO:** Include breadcrumb + LocalBusiness JSON-LD
- **Styling:** Use existing Tailwind tokens + components
- **Data Fetching:** Server-side fetch with ISR revalidation (3600s)
### Các Quyết Định Kiến Trúc
- **Định tuyến:** Đặt tại `apps/web/app/[locale]/(public)/agents/[id]/page.tsx`
- **Mẫu:** Theo mẫu trang chi tiết bất động sản chính xác
- **Metadata:** Sử dụng hàm server generateMetadata()
- **Component:** Chia thành Server Component (trang) + Client Component (tương tác)
- **SEO:** Bao gồm breadcrumb + JSON-LD LocalBusiness
- **Kiểu dáng:** Sử dụng các token Tailwind hiện có + component
- **Lấy dữ liệu:** Lấy dữ liệu phía server với ISR revalidation (3600 giây)
---
## 14. NEXT STEPS
1. **Design API DTO** for public agent profile
2. **Create backend query handler** for fetching public agent profile
3. **Create frontend API client** (agents-api.ts)
4. **Build page structure** following listing detail pattern
5. **Create agent detail client component** with sections
6. **Add reviews display section** with star ratings
7. **Add listings display section** reusing PropertyCard
8. **Generate JSON-LD** structured data for SEO
9. **Test ISR & metadata** generation
10. **Add international routes** (e.g., /en/agents/[id] via locale)
## 14. CÁC BƯỚC TIẾP THEO
1. **Thiết kế API DTO** cho hồ sơ môi giới công khai
2. **Tạo backend query handler** để lấy hồ sơ môi giới công khai
3. **Tạo frontend API client** (agents-api.ts)
4. **Xây dựng cấu trúc trang** theo mẫu trang chi tiết bất động sản
5. **Tạo agent detail client component** với các phần
6. **Thêm phần hiển thị đánh giá** với xếp hạng sao
7. **Thêm phần hiển thị bất động sản** tái sử dụng PropertyCard
8. **Tạo JSON-LD** dữ liệu có cấu trúc cho SEO
9. **Kiểm tra ISR & metadata** generation
10. **Thêm các tuyến quốc tế** (ví dụ: /en/agents/[id] qua locale)