# Trang Hồ Sơ Công Khai Môi Giới GoodGo — Báo Cáo Khám Phá Toàn Diện

**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. CẤU TRÚC ỨNG DỤNG WEB & ĐỊNH TUYẾN

### Cấu Trúc Tệp
```
apps/web/
├── app/                          # Next.js 15 App Router (Server Components)
│   ├── [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          # Trang đích
│   ├── layout.tsx                # Layout gốc
│   ├── robots.ts                 # SEO: robots.txt
│   └── sitemap.ts                # SEO: sitemap.xml
├── components/
│   ├── 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 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
```

### Các Mẫu Định Tuyến

**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á

**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. MÃ LIÊN QUAN ĐẾN MÔI GIỚI HIỆN CÓ

### Kiểu Hồ Sơ Môi Giới (Frontend)
**Tệp:** `apps/web/lib/profile-api.ts`

```typescript
export interface AgentProfile {
  id: string;
  email: string | null;
  phone: string;
  fullName: string;
  avatarUrl: string | null;
  role: string;
  kycStatus: string;
  isActive: boolean;
  createdAt: string;
  licenseNumber: string | null;
  agency: string | null;
  qualityScore: number | null;
  serviceAreas: string[];
  isVerified: boolean;
}
```

### 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          # 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)

// Trả về:
interface AgentDashboardData {
  agentId: string;
  qualityScore: number;
  totalDeals: number;
  responseTimeAvg: number | null;
  isVerified: boolean;
  totalLeads: number;
  leadsByStatus: Record<string, number>;
  conversionRate: number;
  totalInquiries: number;
  unreadInquiries: number;
  totalListings: number;
  activeListings: number;
  avgReviewRating: number;
  totalReviews: number;
}
```

### Schema Prisma — Model Agent
**Tệp:** `prisma/schema.prisma`

```prisma
model Agent {
  id              String   @id @default(cuid())
  userId          String   @unique
  user            User     @relation(fields: [userId], references: [id])
  licenseNumber   String?
  agency          String?
  qualityScore    Float    @default(0)
  totalDeals      Int      @default(0)
  responseTimeAvg Int?
  bio             String?
  serviceAreas    Json     // JSON array: ["quan-1", "quan-7", "thu-duc"]
  isVerified      Boolean  @default(false)
  createdAt       DateTime @default(now())
  updatedAt       DateTime @updatedAt

  listings Listing[]
  leads    Lead[]

  @@index([qualityScore])
  @@index([isVerified])
}
```

**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. CÁC ENDPOINT API MÔI GIỚI CẦN THIẾT CHO HỒ SƠ CÔNG KHAI

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:

### Endpoint Đề Xuất

```http
GET /agents/:agentId/profile
```

**Cấu Trúc Phản Hồi (DTO Hồ Sơ Công Khai):**
```typescript
interface AgentPublicProfile {
  id: string;
  fullName: string;
  avatarUrl: string | null;
  
  // Các trường dành riêng cho môi giới
  licenseNumber: string | null;
  agency: string | null;
  qualityScore: number;
  bio: string | null;
  serviceAreas: string[];
  isVerified: boolean;
  
  // Thống kê
  totalListings: number;
  activeListings: number;
  avgReviewRating: number;
  totalReviews: number;
  
  // Liên hệ (tùy chọn, có thể yêu cầu tùy chọn người dùng)
  phone?: string;
  
  // Dấu thời gian
  createdAt: string;
  updatedAt: string;
}
```

**Các Endpoint Liên Quan Cần Thiết:**
```http
GET /listings?agentId=:agentId&status=ACTIVE     # Bất động sản đang hoạt động của môi giới
GET /reviews/stats?targetType=AGENT&targetId=:agentId  # Thống kê đánh giá môi giới
GET /reviews?targetType=AGENT&targetId=:agentId&limit=10  # Đánh giá gần đây của môi giới
```

Các endpoint này đã tồn tại và công khai (không yêu cầu xác thực).

---

## 4. CÁC COMPONENT UI DÙNG CHUNG & MẪU THIẾT KẾ

### Tailwind/Hệ Thống Thiết Kế
**Tệp:** `apps/web/tailwind.config.ts`

```typescript
// Các biến CSS được sử dụng (hỗ trợ chế độ tối)
colors: {
  primary, primary-foreground
  secondary, secondary-foreground
  destructive, destructive-foreground
  muted, muted-foreground
  accent, accent-foreground
  card, card-foreground
}

// Biến Radius: var(--radius)
borderRadius: {
  lg: 'var(--radius)',
  md: 'calc(var(--radius) - 2px)',
  sm: 'calc(var(--radius) - 4px)',
}
```

### Các Component UI Có Sẵn
**Tệp:** `apps/web/components/ui/`

- `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

### Các Mẫu Component Chính

#### Component Badge
```typescript
<Badge variant="default">Đã xác minh</Badge>
<Badge variant="secondary">Info badge</Badge>
<Badge variant="outline">Outline badge</Badge>
<Badge variant="destructive">Danger</Badge>
```

#### Mẫu Card
```typescript
<Card>
  <CardContent className="p-4">
    {/* Nội dung */}
  </CardContent>
</Card>
```

#### 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`

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

**Có thể tái sử dụng cho:** Hiển thị bất động sản của môi giới

---

## 5. KIỂU DÁNG & MẪU THIẾT KẾ

### CSS Toàn Cục
**Tệp:** `apps/web/app/globals.css`

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(...)
/* Hỗ trợ chế độ tối qua [data-theme="dark"] */
```

### 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`

### 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

### Mẫu Layout Ví Dụ
```typescript
<section className="py-16 md:py-24">
  <div className="mx-auto max-w-7xl px-4">
    {/* Nội dung */}
  </div>
</section>
```

---

## 6. QUẢN LÝ TRẠNG THÁI & LẤY DỮ LIỆU

### Mẫu API Client
**Tệp:** `apps/web/lib/api-client.ts`

```typescript
// 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 }),
  patch: <T>(endpoint: string, body?: unknown, headers?: HeadersInit) => request<T>(endpoint, { method: 'PATCH', body, headers }),
  delete: <T>(endpoint: string, headers?: HeadersInit) => request<T>(endpoint, { method: 'DELETE', headers }),
};

// Bảo vệ CSRF được bao gồm tự động
```

### Mẫu Lấy Dữ Liệu Phía Server
**Tệp:** `apps/web/lib/listings-server.ts`

```typescript
// 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: xác thực lại mỗi 1 giờ
    });
    if (!res.ok) return null;
    return res.json();
  } catch {
    return null;
  }
}
```

### Mẫu Lấy Dữ Liệu Phía Client
```typescript
// Trong React component (sử dụng 'use client')
const [data, setData] = useState(null);

React.useEffect(() => {
  apiClient
    .get('/endpoint')
    .then((res) => setData(res))
    .catch(() => setError(true))
    .finally(() => setLoading(false));
}, []);
```

### 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. MẪU SEO & DỮ LIỆU CÓ CẤU TRÚC

### 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> {
  const listing = await fetchListingById(params.id);
  if (!listing) {
    return { title: 'Không tìm thấy' };
  }

  return {
    title: `${property.title} - ${formatPrice(listing.priceVND)} VND`,
    description: '...',
    alternates: {
      canonical: `${siteUrl}/${params.locale}/listings/${params.id}`,
      languages: { vi: '...', en: '...' }
    },
    openGraph: {
      type: 'article',
      locale: params.locale === 'vi' ? 'vi_VN' : 'en_US',
      url: canonicalUrl,
      title,
      images: [{ url: firstImage.url, width: 1200, height: 630 }],
    },
    twitter: {
      card: 'summary_large_image',
      title,
      images: [firstImage.url],
    },
  };
}
```

### Dữ Liệu Có Cấu Trúc JSON-LD
**Tệp:** `apps/web/components/seo/json-ld.tsx`

```typescript
// Ví dụ: Schema RealEstateListing
export function generateListingJsonLd(listing, siteUrl) {
  return {
    '@context': 'https://schema.org',
    '@type': 'RealEstateListing',
    name: property.title,
    url: `${siteUrl}/listings/${listing.id}`,
    offers: { '@type': 'Offer', price: priceNum, priceCurrency: 'VND' },
    // ... thêm thuộc tính
  };
}

// Cách dùng trong trang:
<JsonLd data={listingJsonLd} />
<JsonLd data={breadcrumbJsonLd} />
```

### Cho Hồ Sơ Môi Giới (Schema.org)
**Schema phù hợp:** `LocalBusiness` hoặc `ProfessionalService`

```json
{
  "@context": "https://schema.org",
  "@type": "LocalBusiness",
  "name": "Agent Full Name",
  "description": "Bio",
  "url": "https://goodgo.vn/en/agents/[id]",
  "image": "avatarUrl",
  "address": {
    "@type": "PostalAddress",
    "addressLocality": "Ho Chi Minh"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": avgReviewRating,
    "reviewCount": totalReviews
  }
}
```

---

## 8. CÁC COMPONENT THẺ BẤT ĐỘNG SẢN HIỆN CÓ

### Component Thẻ Bất Động Sản
**Tệp:** `apps/web/components/search/property-card.tsx`

```typescript
interface PropertyCardProps {
  listing: ListingDetail;
  compact?: boolean;
}

// 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
```

### Đượ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

**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. CÁC COMPONENT ĐÁNH GIÁ & XẾP HẠNG

### Các Endpoint API Đánh Giá
**Tệp:** `apps/api/src/modules/reviews/presentation/controllers/reviews.controller.ts`

```typescript
// Endpoints:
GET    /reviews                     # Danh sách đánh giá theo đối tượng (phân trang)
GET    /reviews/stats               # Lấy thống kê 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

// Tham số truy vấn:
GET /reviews?targetType=AGENT&targetId=:id&page=1&limit=20
GET /reviews/stats?targetType=AGENT&targetId=:id
```

### DTO Đánh Giá
```typescript
interface ReviewItemData {
  id: string;
  userId: string;
  targetType: string;
  targetId: string;
  rating: number;        // 1-5
  comment: string | null;
  createdAt: string;
  // Thông tin người dùng:
  user: {
    id: string;
    fullName: string;
    avatarUrl: string | null;
  };
}

interface ReviewStatsData {
  targetType: string;
  targetId: string;
  totalReviews: number;
  averageRating: number;
  ratingDistribution: {
    "1": number;
    "2": number;
    "3": number;
    "4": number;
    "5": number;
  };
}
```

### Chưa Có Component Hiển Thị Đánh Giá
- **Hồ sơ bảng điều khiển** hiển thị `agentProfile.totalReviews` và `avgReviewRating`
- **Cơ hội:** Tạo component `ReviewCard` và `RatingStars` có thể tái sử dụng cho hồ sơ môi giới

---

## 10. ĐỊNH NGHĨA KIỂU & GIAO DIỆN

### Các Kiểu Cốt Lõi Sử Dụng Ở Frontend

```typescript
// 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';

// Từ profile-api.ts
export interface AgentProfile {
  id: string;
  email: string | null;
  phone: string;
  fullName: string;
  avatarUrl: string | null;
  role: string;
  kycStatus: string;
  isActive: boolean;
  createdAt: string;
  licenseNumber: string | null;
  agency: string | null;
  qualityScore: number | null;
  serviceAreas: string[];
  isVerified: boolean;
}

// Từ listings-api.ts
export interface ListingDetail {
  id: string;
  status: ListingStatus;
  transactionType: TransactionType;
  priceVND: string;
  publishedAt: string | null;
  property: {
    id: string;
    propertyType: PropertyType;
    title: string;
    areaM2: number;
    address: string;
    ward: string;
    district: string;
    city: string;
    media: PropertyMedia[];
    // ... hơn 15 thuộc tính khác
  };
}
```

### Các Schema Xác Thực (Zod)
**Tệp:** `apps/web/lib/validations/listings.ts`

```typescript
export const TRANSACTION_TYPES = [
  { value: 'SALE', label: 'Bán' },
  { value: 'RENT', label: 'Cho thuê' },
] as const;

export const PROPERTY_TYPES = [
  { value: 'APARTMENT', label: 'Căn hộ' },
  { value: 'HOUSE', label: 'Nhà riêng' },
  // ... thêm
] as const;

export const LISTING_STATUSES = {
  DRAFT: { label: 'Nháp', variant: 'secondary' },
  ACTIVE: { label: 'Đang bán', variant: 'success' },
  // ...
};
```

---

## 11. DANH SÁCH KIỂM TRA TRIỂN KHAI TRANG HỒ SƠ MÔI GIỚI

### Backend (NestJS API)

**DTO & Giao Diện Mới:**
```
✓ CreateGetAgentPublicProfileQuery
✓ GetAgentPublicProfileHandler
✓ 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
```

**Endpoint Mới:**
```
✓ 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
```

**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)

**Các Tệp Mới:**
```
✓ 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 (tùy chọn)
✓ apps/web/lib/agents-api.ts (API client)
✓ 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
```

**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> {
  // Lấy thông tin môi giới
  // Trả về title, description, OG image, canonical URL
}

export default async function AgentProfilePage({ params }) {
  // Lấy hồ sơ môi giới (phía server, ISR)
  // Render JsonLd breadcrumb
  // Render JsonLd LocalBusiness hoặc ProfessionalService
  // Truyền xuống client component
  return <>
    <JsonLd data={agentJsonLd} />
    <AgentDetailClient agent={agent} />
  </>
}
```

**Client Component:**
- 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 sản
- Nút liên hệ/yêu cầu tư vấn (tùy chọn)

**API Client:**
```typescript
export const agentsApi = {
  getById: (id: string) => apiClient.get<AgentPublicProfile>(`/agents/${id}/profile`),
};
```

---

## 12. CÁC TỆP CHÍNH CẦN THAM KHẢO/ĐIỀU CHỈNH

### Backend

| Tệp | Mục Đích |
|------|---------|
| `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 — Ví Dụ Tham Khảo

| Tệp | Mục Đích | Mẫu Tái Sử Dụng |
|------|---------|---|
| `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. TÓM TẮT CÁC PHÁT HIỆN CHÍNH

### 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  

### 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  

### 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. 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)
