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,6 +1,6 @@
# Agent Public Profile Page — Code Examples & Implementation Templates
# Trang Hồ Sơ Công Khai Môi Giới — Ví Dụ Mã Nguồn & Mẫu Triển Khai
## 1⃣ BACKEND: API Endpoint Creation
## 1⃣ BACKEND: Tạo Endpoint API
### File: `apps/api/src/modules/agents/application/queries/get-agent-profile/get-agent-profile.query.ts`
@@ -93,7 +93,7 @@ export class AgentPublicProfileDto {
}
```
### File: Update `apps/api/src/modules/agents/presentation/controllers/agents.controller.ts`
### File: Cập nhật `apps/api/src/modules/agents/presentation/controllers/agents.controller.ts`
```typescript
import { Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
@@ -111,7 +111,7 @@ export class AgentsController {
private readonly queryBus: QueryBus,
) {}
// ── Public endpoint ────────────────────────────────────────
// ── Endpoint công khai ────────────────────────────────────────
@ApiOperation({ summary: 'Get public agent profile' })
@ApiParam({ name: 'agentId', description: 'Agent ID' })
@ApiResponse({ status: 200, description: 'Agent profile', type: AgentPublicProfileDto })
@@ -121,12 +121,12 @@ export class AgentsController {
return this.queryBus.execute(new GetAgentProfileQuery(agentId));
}
// ── Existing endpoints (unchanged) ─────────────────────────
// ... rest of controller
// ── Các endpoint hiện có (không thay đổi) ─────────────────────────
// ... phần còn lại của controller
}
```
### File: Update `apps/api/src/modules/agents/domain/repositories/agent.repository.ts`
### File: Cập nhật `apps/api/src/modules/agents/domain/repositories/agent.repository.ts`
```typescript
export interface IAgentRepository {
@@ -135,12 +135,12 @@ export interface IAgentRepository {
updateQualityScore(agentId: string, score: number): Promise<void>;
getDashboard(agentId: string): Promise<AgentDashboardData>;
// NEW METHOD:
// PHƯƠNG THỨC MỚI:
getPublicProfile(agentId: string): Promise<AgentPublicProfileDto>;
}
```
### File: Update `apps/api/src/modules/agents/infrastructure/repositories/prisma-agent.repository.ts`
### File: Cập nhật `apps/api/src/modules/agents/infrastructure/repositories/prisma-agent.repository.ts`
```typescript
async getPublicProfile(agentId: string) {
@@ -160,7 +160,7 @@ async getPublicProfile(agentId: string) {
if (!agent) return null;
// Get stats in parallel
// Lấy thống kê song song
const [totalListings, activeListings, reviewStats] = await Promise.all([
this.prisma.listing.count({
where: { agentId },
@@ -236,7 +236,7 @@ const API_BASE_URL = process.env['NEXT_PUBLIC_API_URL'] || 'http://localhost:300
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
next: { revalidate: 3600 }, // ISR: làm mới mỗi 1 giờ
});
if (!res.ok) return null;
return res.json();
@@ -248,7 +248,7 @@ export async function fetchAgentById(id: string) {
---
## 3⃣ FRONTEND: Server Component (Page)
## 3⃣ FRONTEND: Server Component (Trang)
### File: `apps/web/app/[locale]/(public)/agents/[id]/page.tsx`
@@ -361,7 +361,7 @@ export default async function AgentProfilePage({ params }: PageProps) {
---
## 4⃣ FRONTEND: Client Components
## 4⃣ FRONTEND: Các Client Component
### File: `apps/web/components/agents/agent-detail-client.tsx`
@@ -411,7 +411,7 @@ export function AgentDetailClient({ agent }: AgentDetailClientProps) {
<Card>
<CardContent className="p-6 md:p-8">
<div className="flex flex-col gap-6 md:flex-row">
{/* Avatar */}
{/* Ảnh đại diện */}
<div className="flex-shrink-0">
{agent.avatarUrl ? (
<Image
@@ -428,11 +428,11 @@ export function AgentDetailClient({ agent }: AgentDetailClientProps) {
)}
</div>
{/* Info */}
{/* Thông tin */}
<div className="flex-1">
<h1 className="text-3xl font-bold">{agent.fullName}</h1>
{/* Badges */}
{/* Huy hiệu */}
<div className="mt-3 flex flex-wrap gap-2">
{agent.isVerified && (
<Badge variant="default"> Verified</Badge>
@@ -442,7 +442,7 @@ export function AgentDetailClient({ agent }: AgentDetailClientProps) {
</Badge>
</div>
{/* Details */}
{/* Chi tiết */}
<dl className="mt-4 grid grid-cols-2 gap-4 md:grid-cols-3">
{agent.licenseNumber && (
<>
@@ -468,12 +468,12 @@ export function AgentDetailClient({ agent }: AgentDetailClientProps) {
</div>
</dl>
{/* Bio */}
{/* Tiểu sử */}
{agent.bio && (
<p className="mt-4 text-sm text-muted-foreground">{agent.bio}</p>
)}
{/* Service Areas */}
{/* Khu vực phục vụ */}
{agent.serviceAreas.length > 0 && (
<div className="mt-4">
<p className="text-xs font-medium text-muted-foreground">Serves:</p>
@@ -608,15 +608,15 @@ export function AgentReviewsSection({ agentId }: AgentReviewsSectionProps) {
<section className="py-16 md:py-24 bg-muted/50">
<div className="mx-auto max-w-7xl px-4">
<div className="mb-8">
<h2 className="text-2xl font-bold">Customer Reviews</h2>
<h2 className="text-2xl font-bold">Đánh Giá Của Khách Hàng</h2>
{stats && (
<div className="mt-4 flex items-center gap-4">
<div>
<div className="text-3xl font-bold">{stats.averageRating.toFixed(1)}</div>
<div className="text-sm text-muted-foreground">out of 5.0</div>
<div className="text-sm text-muted-foreground">trên 5,0</div>
</div>
<div className="text-sm text-muted-foreground">
Based on {stats.totalReviews} reviews
Dựa trên {stats.totalReviews} đánh giá
</div>
</div>
)}
@@ -651,7 +651,7 @@ export function AgentReviewsSection({ agentId }: AgentReviewsSectionProps) {
</div>
) : (
<div className="text-center text-muted-foreground">
No reviews yet
Chưa đánh giá
</div>
)}
</div>
@@ -662,45 +662,45 @@ export function AgentReviewsSection({ agentId }: AgentReviewsSectionProps) {
---
## 5STYLING REFERENCE
## 5THAM KHẢO GIAO DIỆN
All components use:
- **Tailwind CSS** classes directly (no CSS modules)
- **Responsive breakpoints**: `md:`, `lg:`
- **Dark mode**: Uses CSS variables in `globals.css`
- **Component pattern**: Card → CardContent
Tất cả các component đều sử dụng:
- Các class **Tailwind CSS** trực tiếp (không dùng CSS module)
- **Breakpoint responsive**: `md:`, `lg:`
- **Chế độ tối**: Dùng biến CSS trong `globals.css`
- **Mẫu component**: Card → CardContent
### Common spacing patterns:
### Các mẫu khoảng cách thông dụng:
```typescript
// Sections
// Các phần
<section className="py-16 md:py-24">
<div className="mx-auto max-w-7xl px-4">
{/* content */}
{/* nội dung */}
</div>
</section>
// Cards
// Card
<Card>
<CardContent className="p-4 md:p-6">
{/* content */}
{/* nội dung */}
</CardContent>
</Card>
// Grid
// Lưới
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{/* items */}
{/* các phần tử */}
</div>
```
---
## 🧪 Testing Checklist
## 🧪 Danh Sách Kiểm Thử
```bash
# Backend API Test
# Kiểm thử API Backend
curl http://localhost:3001/api/v1/agents/[valid-agent-id]/profile
# Expected Response
# Phản hồi kỳ vọng
{
"id": "...",
"fullName": "...",
@@ -714,10 +714,9 @@ curl http://localhost:3001/api/v1/agents/[valid-agent-id]/profile
```
```typescript
// Frontend Test
// Kiểm thử Frontend
import { agentsApi } from '@/lib/agents-api';
const agent = await agentsApi.getById('agent-id-here');
console.log(agent); // Should match structure above
console.log(agent); // Phải khớp với cấu trúc trên
```