# GoodGo Platform - Quick Reference Guide ## 🗂️ File Structure Quick Links ### Pages (where to place new features) - **Inquiry pages**: `apps/web/app/[locale]/(dashboard)/inquiries/` - **Lead pages**: `apps/web/app/[locale]/(dashboard)/leads/` - **Example pages**: `apps/web/app/[locale]/(dashboard)/listings/` (reference) ### API Layer - **Inquiry API**: Create `apps/web/lib/inquiries-api.ts` - **Lead API**: Create `apps/web/lib/leads-api.ts` - **Base client**: `apps/web/lib/api-client.ts` ← reuse this ### Components - **UI base components**: `apps/web/components/ui/` (Button, Card, Badge, Table, Select, Input) - **Domain components**: `apps/web/components/inquiries/`, `apps/web/components/leads/` - **Example domain component**: `apps/web/components/listings/listing-status-badge.tsx` ### Hooks - **Create hooks**: `apps/web/lib/hooks/use-inquiries.ts`, `apps/web/lib/hooks/use-leads.ts` - **Example hook**: `apps/web/lib/hooks/use-listings.ts` ### Stores (if needed) - **Location**: `apps/web/lib/` (e.g., `inquiry-store.ts`, `lead-store.ts`) - **Example**: `apps/web/lib/comparison-store.ts` ### Backend API - **Inquiries controller**: `apps/api/src/modules/inquiries/presentation/controllers/inquiries.controller.ts` - **Leads controller**: `apps/api/src/modules/leads/presentation/controllers/leads.controller.ts` --- ## 🔌 Backend API Endpoints ### Inquiries ``` POST /api/v1/inquiries Create inquiry GET /api/v1/inquiries/listing/{id} List by listing (paginated) GET /api/v1/inquiries/agent/me List my inquiries (AGENT role) PATCH /api/v1/inquiries/{id}/read Mark as read (AGENT role) ``` ### Leads ``` POST /api/v1/leads Create lead (AGENT role) GET /api/v1/leads List leads (AGENT role, paginated) GET /api/v1/leads/stats Get stats (AGENT role) PATCH /api/v1/leads/{id}/status Update status (AGENT role) DELETE /api/v1/leads/{id} Delete lead (AGENT role) ``` --- ## 🎨 Component Templates ### List Page Template ```typescript 'use client'; import { useQuery } from '@tanstack/react-query'; import { useTranslations } from 'next-intl'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Badge } from '@/components/ui/badge'; import { Select } from '@/components/ui/select'; export default function InquiriesPage() { const t = useTranslations('inquiries'); const [filters, setFilters] = useState({ page: 1, status: '' }); const { data, isLoading } = useQuery({ queryKey: ['inquiries', filters], queryFn: () => inquiriesApi.list(filters), }); return (

{t('title')}

{/* Stats cards */}
{/* Filters */}
{/* Table */} {isLoading ? (
) : ( {t('name')} {t('status')} {t('actions')} {data?.items.map(item => ( {item.name} {item.status} ))}
)}
); } ``` ### API Service Template ```typescript // apps/web/lib/inquiries-api.ts import { apiClient } from './api-client'; export interface InquiryDto { id: string; listingId: string; userId: string; message: string; isRead: boolean; createdAt: string; } export interface InquiryListResponse { items: InquiryDto[]; total: number; page: number; limit: number; } export const inquiriesApi = { list: (params: { page?: number; limit?: number; status?: string }) => apiClient.get('/inquiries', params), getById: (id: string) => apiClient.get(`/inquiries/${id}`), markAsRead: (id: string) => apiClient.patch(`/inquiries/${id}/read`, {}), }; ``` ### Hook Template ```typescript // apps/web/lib/hooks/use-inquiries.ts import { useQuery } from '@tanstack/react-query'; import { inquiriesApi } from '@/lib/inquiries-api'; export const inquiriesKeys = { all: ['inquiries'] as const, list: (params: any) => ['inquiries', 'list', params] as const, detail: (id: string) => ['inquiries', 'detail', id] as const, }; export function useInquiries(params = {}) { return useQuery({ queryKey: inquiriesKeys.list(params), queryFn: () => inquiriesApi.list(params), }); } export function useInquiry(id: string) { return useQuery({ queryKey: inquiriesKeys.detail(id), queryFn: () => inquiriesApi.getById(id), enabled: !!id, }); } ``` ### Status Badge Component Template ```typescript // apps/web/components/inquiries/inquiry-status-badge.tsx import { Badge } from '@/components/ui/badge'; const INQUIRY_STATUSES = { NEW: { label: 'Mới', variant: 'info' as const }, READ: { label: 'Đã xem', variant: 'secondary' as const }, REPLIED: { label: 'Đã trả lời', variant: 'success' as const }, }; export function InquiryStatusBadge({ status }: { status: string }) { const config = INQUIRY_STATUSES[status as keyof typeof INQUIRY_STATUSES] ?? { label: status, variant: 'outline' as const, }; return {config.label}; } ``` --- ## 📝 Translations (i18n) Add to `apps/web/messages/vi.json` and `apps/web/messages/en.json`: ```json { "inquiries": { "title": "Quản lý Liên hệ", "subtitle": "Xem và quản lý các liên hệ từ khách hàng", "allStatus": "Tất cả trạng thái", "new": "Mới", "read": "Đã xem", "replied": "Đã trả lời", "total": "Tổng liên hệ", "thisMonth": "Tháng này", "message": "Tin nhắn", "from": "Từ", "date": "Ngày tạo", "markAsRead": "Đánh dấu đã xem" }, "leads": { "title": "Quản lý Khách hàng tiềm năng", "subtitle": "Theo dõi và quản lý khách hàng tiềm năng", "name": "Tên khách hàng", "phone": "Số điện thoại", "email": "Email", "source": "Nguồn", "score": "Điểm số", "status": "Trạng thái", "new": "Mới", "contacted": "Đã liên hệ", "qualified": "Đã xác nhận", "negotiating": "Đang thương lượng", "converted": "Chuyển đổi", "lost": "Mất" } } ``` Usage in components: ```typescript const t = useTranslations('inquiries'); // or const t = useTranslations('leads'); ``` --- ## 🎯 Styling Conventions ### Color Classes ```css /* Status indicators */ .success { @apply text-green-600 bg-green-50 } .warning { @apply text-yellow-600 bg-yellow-50 } .info { @apply text-blue-600 bg-blue-50 } .error { @apply text-red-600 bg-red-50 } /* Typography */ .title { @apply text-2xl font-bold } .subtitle { @apply text-muted-foreground text-sm } .label { @apply text-xs text-muted-foreground uppercase } /* Layout */ .card-grid { @apply grid gap-4 sm:grid-cols-2 lg:grid-cols-3 } .flex-between { @apply flex items-center justify-between } ``` ### Responsive Breakpoints ```typescript // Mobile first className="w-full" // Mobile: full width className="sm:w-1/2" // 640px+: 50% className="md:w-1/3" // 768px+: 33% className="lg:grid-cols-3" // 1024px+: 3 columns ``` --- ## 🔐 Authentication & Authorization ### Protected Pages ```typescript // pages automatically protected by (dashboard) group // which has JwtAuthGuard applied via middleware or layout // For role-specific pages (AGENT only): // Use guard directly or check in component const { user } = useAuthStore(); if (!user?.roles.includes('AGENT')) { // redirect or show error } ``` ### API Calls with Auth ```typescript // Automatically includes: // - httpOnly cookies (JWT) // - CSRF token from XSRF-TOKEN cookie // - X-CSRF-Token header (POST/PATCH/DELETE) const { data } = await inquiriesApi.list(); // Auth headers auto-included ``` --- ## 🧪 Testing Patterns See existing tests in `__tests__` folders for reference: - `apps/web/lib/__tests__/auth-store.spec.ts` - `apps/web/components/ui/__tests__/` --- ## ✅ Pre-Build Checklist Before creating Inquiry & Lead pages: - [ ] Create API service files (`inquiries-api.ts`, `leads-api.ts`) - [ ] Create React Query hooks (`use-inquiries.ts`, `use-leads.ts`) - [ ] Create status badge components - [ ] Add translations to `vi.json` and `en.json` - [ ] Create page components under `(dashboard)` group - [ ] Test API endpoints with backend - [ ] Verify auth guards (JwtAuthGuard, RolesGuard) - [ ] Test pagination with query params - [ ] Test loading/error states - [ ] Test responsive design (mobile/tablet/desktop) - [ ] Add JSDoc comments to reusable functions - [ ] Test dark mode colors --- ## 📚 Key Files to Reference ``` REFERENCE PAGES: - apps/web/app/[locale]/(dashboard)/listings/page.tsx ← Best example - apps/web/app/[locale]/(dashboard)/dashboard/page.tsx ← Stats & cards REFERENCE COMPONENTS: - apps/web/components/listings/listing-status-badge.tsx ← Status badge pattern - apps/web/components/search/filter-bar.tsx ← Filter pattern - apps/web/components/ui/table.tsx ← Table pattern REFERENCE HOOKS: - apps/web/lib/hooks/use-listings.ts ← React Query pattern - apps/web/lib/hooks/use-analytics.ts ← Complex data fetching REFERENCE STORES: - apps/web/lib/auth-store.ts ← Async actions pattern - apps/web/lib/comparison-store.ts ← Persistence pattern REFERENCE API: - apps/web/lib/listings-api.ts ← API service pattern - apps/web/lib/auth-api.ts ← Auth API pattern REFERENCE LAYOUT: - apps/web/app/[locale]/(dashboard)/layout.tsx ← Dashboard nav REFERENCE VALIDATION: - apps/web/lib/validations/listings.ts ← Zod schema pattern ```