# Agent Public Profile Page — Quick Reference Guide

## 🎯 Implementation Overview

### URL Pattern
```
/agents/[id]                    # Desktop
/agents/[id]?locale=vi          # With locale (i18n)
/en/agents/[id]                 # Explicit locale
```

### Page Location
```
apps/web/app/[locale]/(public)/agents/[id]/page.tsx
```

---

## 📦 Backend Setup (API Changes Required)

### New Public Endpoint
```http
GET /agents/:agentId/profile
```

### Response DTO
```typescript
{
  id: string;
  fullName: string;
  avatarUrl: string | null;
  licenseNumber: string | null;
  agency: string | null;
  qualityScore: number;
  bio: string | null;
  serviceAreas: string[];
  isVerified: boolean;
  totalListings: number;
  activeListings: number;
  avgReviewRating: number;
  totalReviews: number;
  phone?: string;
  createdAt: string;
  updatedAt: string;
}
```

### Query Handler Files to Create
```
apps/api/src/modules/agents/application/queries/get-agent-profile/
├── get-agent-profile.query.ts
└── get-agent-profile.handler.ts

apps/api/src/modules/agents/presentation/dto/
└── agent-public-profile.dto.ts
```

---

## 🎨 Frontend Architecture

### Directory Structure to Create
```
apps/web/
├── app/[locale]/(public)/agents/
│   └── [id]/
│       ├── page.tsx              ← Server component (metadata, ISR)
│       └── layout.tsx            ← Optional shared layout
│
├── components/agents/
│   ├── agent-detail-client.tsx   ← Main interactive component
│   ├── agent-header.tsx          ← Profile info section
│   ├── agent-listings-section.tsx ← Grid of listings
│   └── agent-reviews-section.tsx  ← Reviews with stars
│
└── lib/
    ├── agents-api.ts            ← API client
    └── agents-server.ts         ← Server-side ISR fetch
```

---

## 🔄 Data Flow

```
1. Browser requests: /agents/[id]
                ↓
2. Next.js generates metadata (SEO)
   - Title: "Agent Name — Real Estate Agent at GoodGo"
   - Description: Bio + stats
   - Image: Avatar
   - Canonical URL
                ↓
3. Server-side fetch agent profile data
   - GET /api/v1/agents/:id/profile (ISR: revalidate 3600s)
                ↓
4. Render Server Component with metadata + JSON-LD
                ↓
5. Pass data to Client Component for interactivity
   - Fetch reviews in parallel
   - Fetch listings in parallel
                ↓
6. Client renders:
   - Agent header (avatar, name, stats, badges)
   - Reviews section (star ratings, comment cards)
   - Listings section (reuse PropertyCard component)
```

---

## 🎭 Component Composition

### Server Component (page.tsx)
```typescript
export async function generateMetadata({ params }): Promise<Metadata> {
  // 1. Fetch agent using agents-server.ts
  // 2. Build SEO metadata
  // 3. Return title, description, OG, canonical
}

export default async function AgentProfilePage({ params }) {
  // 1. Fetch agent profile (with ISR)
  // 2. Generate JSON-LD (LocalBusiness schema)
  // 3. Render <JsonLd> for structured data
  // 4. Pass agent to client component
  return <>
    <JsonLd data={agentJsonLd} />
    <AgentDetailClient agent={agent} />
  </>
}
```

### Client Component (agent-detail-client.tsx)
```typescript
'use client';

export function AgentDetailClient({ agent }: Props) {
  const [listings, setListings] = useState([]);
  const [reviews, setReviews] = useState([]);
  const [reviewStats, setReviewStats] = useState(null);

  useEffect(() => {
    // Fetch agent's active listings
    // Fetch reviews & stats
  }, [agent.id]);

  return <>
    <AgentHeader agent={agent} />
    <AgentReviewsSection reviews={reviews} stats={reviewStats} />
    <AgentListingsSection listings={listings} />
  </>
}
```

---

## 🔗 API Calls Used

### Existing Public Endpoints (No Auth Required)
```http
# Get agent profile (NEW - to be created)
GET /api/v1/agents/:agentId/profile

# Get agent's listings (EXISTING)
GET /api/v1/listings?agentId=:agentId&status=ACTIVE

# Get agent reviews (EXISTING)
GET /api/v1/reviews?targetType=AGENT&targetId=:agentId&limit=20

# Get agent review stats (EXISTING)
GET /api/v1/reviews/stats?targetType=AGENT&targetId=:agentId
```

---

## 🎨 UI Sections & Reusable Components

### 1. Agent Header Section
```
┌─────────────────────────────────┐
│ [Avatar] Name                   │
│          License: ABC123        │
│          Agency: XYZ Agency     │
│          ✓ Verified             │
│                                 │
│ ⭐ 4.5 (120 reviews)            │
│ 📍 Serves: Quan 1, Quan 7, ...  │
│ 🏠 45 active listings           │
└─────────────────────────────────┘

Components: Card, Badge, Image, Text
Styling: Tailwind (p-6, flex, gap-4)
```

### 2. Reviews Section
```
┌─────────────────────────────────┐
│ Customer Reviews (120 total)    │
├─────────────────────────────────┤
│ [Review Card]                   │
│ ⭐⭐⭐⭐⭐ John Doe  │ 2 days ago │
│ "Great agent, very professional" │
├─────────────────────────────────┤
│ [Review Card]                   │
│ ⭐⭐⭐⭐ Jane Smith │ 1 week ago │
│ "Good communication"             │
└─────────────────────────────────┘

Components: Card, Badge (rating), Avatar
Reuse: PropertyCard padding/spacing pattern
```

### 3. Listings Section
```
┌─────────────────────────────────┐
│ Active Listings (45 total)      │
├─────────────────────────────────┤
│ [PropertyCard]  [PropertyCard]   │
│ [PropertyCard]  [PropertyCard]   │
│ [PropertyCard]  [PropertyCard]   │
└─────────────────────────────────┘

Components: PropertyCard (reuse from search/property-card.tsx)
Grid: grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4
```

---

## 🎯 Copy-Paste Templates

### agents-api.ts
```typescript
import { apiClient } from './api-client';

export interface AgentPublicProfile {
  id: string;
  fullName: string;
  avatarUrl: string | null;
  licenseNumber: string | null;
  agency: string | null;
  qualityScore: number;
  bio: string | null;
  serviceAreas: string[];
  isVerified: boolean;
  totalListings: number;
  activeListings: number;
  avgReviewRating: number;
  totalReviews: number;
  createdAt: string;
  updatedAt: string;
}

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

### agents-server.ts
```typescript
const API_BASE_URL = process.env['NEXT_PUBLIC_API_URL'] || 'http://localhost:3001/api/v1';

export async function fetchAgentById(id: string) {
  try {
    const res = await fetch(`${API_BASE_URL}/agents/${id}/profile`, {
      next: { revalidate: 3600 } // ISR: revalidate every 1 hour
    });
    if (!res.ok) return null;
    return res.json();
  } catch {
    return null;
  }
}
```

---

## 🔍 SEO & Structured Data

### Metadata Hints
```typescript
title: `${agent.fullName} — Real Estate Agent at GoodGo`
description: `${agent.bio}. Quality Score: ${agent.qualityScore}. ${agent.activeListings} active listings. ⭐ ${agent.avgReviewRating} (${agent.totalReviews} reviews)`

// OG Image: Use avatar or placeholder
```

### JSON-LD Schema
```json
{
  "@context": "https://schema.org",
  "@type": "LocalBusiness",
  "name": "Agent Full Name",
  "description": "Bio",
  "url": "https://goodgo.vn/en/agents/[id]",
  "image": "avatarUrl",
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": 4.5,
    "reviewCount": 120
  },
  "areaServed": ["Quan 1", "Quan 7", "Thu Duc"],
  "knowsAbout": "Real Estate"
}
```

---

## 🚀 Implementation Phases

### Phase 1: Backend (1-2 hours)
- [ ] Create GetAgentProfileQuery
- [ ] Create GetAgentProfileHandler
- [ ] Create AgentPublicProfileDto
- [ ] Add endpoint to AgentsController
- [ ] Update AgentRepository interface
- [ ] Implement in PrismaAgentRepository
- [ ] Test with Postman/curl

### Phase 2: Frontend Setup (1 hour)
- [ ] Create agents-api.ts
- [ ] Create agents-server.ts
- [ ] Create agents folder structure
- [ ] Create [id]/page.tsx (stub)
- [ ] Import types from agents-api.ts

### Phase 3: UI Components (2-3 hours)
- [ ] Create AgentHeader component
- [ ] Create AgentReviewsSection component
- [ ] Create AgentListingsSection component
- [ ] Create RatingStars/ReviewCard components
- [ ] Wire up data fetching

### Phase 4: SEO & Polish (1 hour)
- [ ] Add generateMetadata()
- [ ] Generate JSON-LD schemas
- [ ] Test OG preview
- [ ] Mobile responsive check
- [ ] Dark mode testing

### Phase 5: Testing (1 hour)
- [ ] Manual e2e test
- [ ] Check 404 handling
- [ ] Verify ISR revalidation
- [ ] Test pagination (listings/reviews)
- [ ] SEO audit (Lighthouse)

---

## 📊 Example Response Structure

```json
{
  "id": "clu1x2y3z4a5b6c7d8e9f0",
  "fullName": "Nguyễn Văn A",
  "avatarUrl": "https://cdn.goodgo.vn/avatars/agent-123.jpg",
  "licenseNumber": "DA123456",
  "agency": "GoodGo Agency",
  "qualityScore": 4.8,
  "bio": "Specialized in high-end real estate in District 1 and 7",
  "serviceAreas": ["quan-1", "quan-7", "thu-duc"],
  "isVerified": true,
  "totalListings": 45,
  "activeListings": 32,
  "avgReviewRating": 4.7,
  "totalReviews": 120,
  "createdAt": "2024-01-15T10:30:00Z",
  "updatedAt": "2024-04-10T15:45:00Z"
}
```

---

## 🎯 Key Files Reference

| Phase | Files to Create/Modify |
|-------|------------------------|
| Backend | `agents/application/queries/get-agent-profile/*` |
| Backend | `agents/presentation/controllers/agents.controller.ts` |
| Backend | `agents/presentation/dto/agent-public-profile.dto.ts` |
| Frontend | `lib/agents-api.ts` |
| Frontend | `lib/agents-server.ts` |
| Frontend | `app/[locale]/(public)/agents/[id]/page.tsx` |
| Frontend | `components/agents/agent-detail-client.tsx` |
| Frontend | `components/agents/agent-header.tsx` |
| Frontend | `components/agents/agent-reviews-section.tsx` |
| Frontend | `components/agents/agent-listings-section.tsx` |

