Add comprehensive project documentation including changelog, QA tracker, code quality audit, implementation guide, K6 load testing guide, frontend exploration notes, and file mapping reference. Co-Authored-By: Paperclip <noreply@paperclip.ing>
19 KiB
GoodGo Platform Frontend Exploration Report
apps/web (Next.js 14 with App Router)
Date: April 9, 2026
Status: Pre-i18n (No existing i18n setup detected)
Next.js Version: 14.2.0 | React: 18.3.0
Primary Language: Vietnamese (vi_VN)
📁 Directory Structure Overview
apps/web/
├── app/ # Next.js App Router (main application)
│ ├── layout.tsx # Root layout with metadata & providers
│ ├── globals.css # Global Tailwind styles & theme variables
│ ├── middleware.ts # Already exists (auth routing middleware)
│ ├── loading.tsx # Root loading state
│ ├── error.tsx # Root error boundary
│ ├── not-found.tsx # 404 page
│ │
│ ├── (public)/ # Public route group
│ │ ├── layout.tsx # Public layout with header/footer
│ │ ├── page.tsx # Landing page (hero + featured listings)
│ │ ├── search/ # Search results page
│ │ │ ├── page.tsx
│ │ │ ├── layout.tsx
│ │ │ ├── error.tsx
│ │ │ ├── loading.tsx
│ │ │ └── __tests__/
│ │ └── listings/[id]/ # Listing detail page
│ │ └── page.tsx
│ │
│ ├── (auth)/ # Auth route group
│ │ ├── layout.tsx
│ │ ├── login/page.tsx # Login form
│ │ ├── register/page.tsx # Registration form
│ │ ├── error.tsx
│ │ ├── loading.tsx
│ │ └── __tests__/
│ │
│ ├── (dashboard)/ # Protected dashboard route group
│ │ ├── layout.tsx # Dashboard layout with sidebar nav
│ │ ├── dashboard/page.tsx # Main dashboard
│ │ ├── listings/
│ │ │ ├── page.tsx # User listings list
│ │ │ ├── new/page.tsx # Create listing (multi-step form)
│ │ │ └── [id]/edit/page.tsx
│ │ ├── analytics/page.tsx
│ │ ├── profile/page.tsx # User profile settings
│ │ ├── subscription/page.tsx # Subscription plans
│ │ ├── payments/page.tsx # Payment history
│ │ ├── valuation/page.tsx # AI property valuation
│ │ ├── error.tsx
│ │ ├── loading.tsx
│ │ └── __tests__/
│ │
│ ├── (admin)/ # Admin route group
│ │ ├── layout.tsx
│ │ ├── admin/page.tsx # Admin dashboard
│ │ ├── admin/users/page.tsx
│ │ ├── admin/kyc/page.tsx
│ │ ├── admin/moderation/page.tsx
│ │ ├── error.tsx
│ │ └── loading.tsx
│ │
│ ├── auth/ # Auth callbacks
│ │ └── callback/
│ │ ├── google/page.tsx
│ │ └── zalo/page.tsx
│ │
│ ├── api/ # API routes
│ │ └── health/route.ts
│ │
│ ├── robots.ts
│ └── sitemap.ts
│
├── components/ # Reusable React components
│ ├── providers/ # Context providers
│ │ ├── auth-provider.tsx # Auth context & store wrapper
│ │ ├── query-provider.tsx # TanStack React Query provider
│ │ └── theme-provider.tsx # Dark/light mode provider
│ │
│ ├── ui/ # Unstyled base UI components
│ │ ├── button.tsx # CVA-based button variants
│ │ ├── input.tsx
│ │ ├── label.tsx
│ │ ├── card.tsx
│ │ ├── dialog.tsx # Modal dialog
│ │ ├── tabs.tsx
│ │ ├── select.tsx # Custom select component
│ │ ├── badge.tsx
│ │ ├── textarea.tsx
│ │ ├── table.tsx
│ │ └── __tests__/ # Component tests
│ │
│ ├── auth/
│ │ └── oauth-buttons.tsx # Google & Zalo OAuth buttons
│ │
│ ├── search/
│ │ ├── filter-bar.tsx # Search filters (transaction, property, price range)
│ │ ├── property-card.tsx # Property listing card
│ │ └── search-results.tsx # Results container
│ │
│ ├── listings/
│ │ ├── listing-form-steps.tsx # Multi-step create/edit form
│ │ ├── image-upload.tsx # Image upload component
│ │ ├── image-gallery.tsx # Image gallery viewer
│ │ └── listing-status-badge.tsx # Status display badge
│ │
│ ├── map/
│ │ └── listing-map.tsx # Mapbox GL integration
│ │
│ ├── valuation/
│ │ ├── valuation-form.tsx
│ │ ├── valuation-results.tsx
│ │ ├── valuation-history.tsx
│ │ └── ai-estimate-button.tsx
│ │
│ └── charts/
│ ├── price-trend-chart.tsx
│ ├── agent-performance.tsx
│ └── district-heatmap.tsx
│
├── lib/ # Utilities and hooks
│ ├── utils.ts # cn() - clsx + tailwind-merge
│ ├── auth-store.ts # Zustand auth state management
│ ├── api-client.ts # Axios/fetch wrapper
│ ├── query-client.ts # TanStack React Query config
│ │
│ ├── hooks/
│ │ ├── use-listings.ts
│ │ ├── use-analytics.ts
│ │ ├── use-valuation.ts
│ │ ├── use-payments.ts
│ │ └── use-subscription.ts
│ │
│ ├── validations/ # Zod schemas
│ │ ├── auth.ts # Login/register schemas
│ │ ├── listings.ts # Multi-step listing schemas
│ │ └── valuation.ts
│ │
│ ├── *-api.ts # API clients
│ │ ├── auth-api.ts
│ │ ├── listings-api.ts
│ │ ├── profile-api.ts
│ │ ├── payment-api.ts
│ │ ├── subscription-api.ts
│ │ ├── analytics-api.ts
│ │ ├── valuation-api.ts
│ │ └── admin-api.ts
│ │
│ └── __tests__/ # Unit tests (Vitest + React Testing Library)
│ ├── auth-store.spec.ts
│ ├── auth-validations.spec.ts
│ ├── listing-validations.spec.ts
│ └── utils.spec.ts
│
├── public/ # Static assets
│ └── [images, icons, etc.]
│
├── .next/ # Build output (generated)
├── node_modules/
│
└── Configuration Files:
├── package.json # Dependencies & scripts
├── next.config.js # Next.js config (Sentry, CSP, headers)
├── tailwind.config.ts # Tailwind CSS config
├── postcss.config.js # PostCSS config (Tailwind + Autoprefixer)
├── tsconfig.json # TypeScript config (extends base)
├── vitest.config.ts # Testing framework config
├── vitest.setup.ts # Test setup
├── middleware.ts # Auth routing middleware
├── sentry.*.config.ts # Sentry error tracking (3 files)
├── instrumentation.ts # Server instrumentation
└── global.d.ts # Global TypeScript definitions
📦 Package.json Dependencies
Production Dependencies:
{
"@hookform/resolvers": "^5.2.2", // Form validation resolver
"@sentry/nextjs": "^10.47.0", // Error tracking
"@tanstack/react-query": "^5.96.2", // Data fetching & caching
"class-variance-authority": "^0.7.1", // Component variant utilities
"clsx": "^2.1.1", // Class name utility
"lucide-react": "^1.7.0", // Icon library
"mapbox-gl": "^3.21.0", // Map library
"next": "^14.2.0", // Framework
"react": "^18.3.0",
"react-dom": "^18.3.0",
"react-hook-form": "^7.72.1", // Form state management
"recharts": "^3.8.1", // Chart library
"tailwind-merge": "^3.5.0", // Merge Tailwind conflicts
"zod": "^4.3.6", // Schema validation
"zustand": "^5.0.12" // Lightweight state management
}
Dev Dependencies (including testing):
{
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@vitejs/plugin-react": "^4.7.0",
"vitest": "^4.1.3",
"tailwindcss": "^3.4.0",
"tailwindcss-animate": "^1.0.7",
"msw": "^2.13.2", // Mock Service Worker
"typescript": "^6.0.2"
}
🎯 Root Layout (app/layout.tsx)
Current Implementation:
- HTML language:
lang="vi"(Vietnamese hardcoded) - Metadata structure: Vietnamese title & description
- OpenGraph: Locale set to
vi_VN - Providers stacked:
ThemeProvider → QueryProvider → AuthProvider - Accessibility: Includes skip-to-main-content link (already A11y compliant)
- Theme color:
#15803d(primary green)
Current Metadata:
title: 'GoodGo — Nền tảng Bất động sản Việt Nam'
description: 'GoodGo — nền tảng bất động sản thông minh tại Việt Nam...'
openGraph: { locale: 'vi_VN', ... }
🔐 Middleware (middleware.ts)
Current Auth Routing:
- Public paths: /login, /register, /search, /auth/callback, / (root)
- Protected paths: Anything else requires 'goodgo_authenticated' cookie
- Auth-only paths: /login, /register (redirects to /dashboard if authenticated)
- Redirect param: Adds ?redirect=[original-path] on unauthorized access
Key Entry Points to Update for i18n:
- Locale prefix detection needed (e.g.,
/en/dashboard,/vi/dashboard) - Cookie/header locale detection
🎨 Tailwind Configuration
Theme Setup (tailwind.config.ts):
- Dark mode: 'class' based
- Content paths: ./app/**, ./components/**, ./lib/**
- Colors: HSL-based CSS variables (--primary, --secondary, etc.)
- Border radius: Customizable via --radius CSS variable
- Animation plugin: tailwindcss-animate
Global Styles (app/globals.css):
- CSS Variables: Light mode + dark mode color schemes
- Primary color: HSL(142.1, 76.2%, 36.3%) — green
- All components: Use @apply border-border for consistency
- Root background: HSL variables applied
🗣️ Text Content & i18n Points
Hardcoded Vietnamese Text Locations:
Layout & Navigation:
app/(public)/layout.tsx— Header nav: "Trang chủ", "Tìm kiếm", "Đăng nhập", "Đăng ký"app/(dashboard)/layout.tsx— Dashboard nav items (8 items + theme toggle label)- Footer in public layout — Section headings, links
Pages:
app/(public)/page.tsx— Landing page (hero, search bar, districts, stats, CTA)app/(auth)/login/page.tsx— Form labels, error messages (OAUTH_ERROR_MESSAGES object)app/(auth)/register/page.tsx— Similar form structure
Components:
components/search/filter-bar.tsx— Filter labels (PRICE_RANGES), city namescomponents/search/property-card.tsx— Property info badges, direction labelscomponents/listings/listing-form-steps.tsx— Form labels, validation messagescomponents/ui/label.tsx— Form labels across app
API Error Messages & Zod Validation:
lib/validations/listings.ts— Zod error messages (Vietnamese)lib/validations/auth.ts— Auth validation messagescomponents/auth/oauth-buttons.tsx— Button text ("Google", "Zalo")
🧩 Key Components Requiring Translation
Forms (Form validation + labels):
-
Login Form (
app/(auth)/login/page.tsx)- Phone input label, password label, errors
- OAuth button labels
- Link text: "Chưa có tài khoản? Đăng ký"
-
Register Form (
app/(auth)/register/page.tsx)- Similar structure to login
-
Listing Creation (
components/listings/listing-form-steps.tsx)- Multi-step form with labels for:
- Transaction type (Bán/Cho thuê)
- Property type (Căn hộ/Nhà riêng/etc)
- Location (address, ward, district, city)
- Details (area, bedrooms, bathrooms, direction)
- Pricing
- Multi-step form with labels for:
-
Search Filter (
components/search/filter-bar.tsx)- Transaction/Property/Price/Area selects
- City options (13 Vietnamese cities)
UI Components:
- Buttons: Text labels ("Đăng nhập", "Tìm kiếm", "Gửi", etc.)
- Badge: Labels for property types, statuses, directions
- Input labels: Across all forms
- Error messages: Alert text
Navigation:
- Public header: 4 main nav items + user menu
- Dashboard nav: 8 main sections + theme toggle
- Footer: 4 columns of links + copyright
♿ Accessibility (Current State - WCAG 2.1 AA)
Already Implemented ✅:
- Skip-to-main-content link (hidden, appears on focus)
- Semantic HTML:
<header>,<nav>,<main>,<footer> aria-labelon navigation itemsaria-labelon property cards (for screen readers)role="alert"on error messagesaria-invalidon form inputs- Form labels linked with
htmlFor - Image alt text on property images
aria-hidden="true"on decorative elements
Accessibility Gaps to Fix 🔧:
- Color contrast: Need to verify against WCAG AA standards
- Focus indicators: Ensure visible focus states on all interactive elements
- Dialog/Modal: Need proper focus management in dialogs
- Forms: Ensure field grouping with
<fieldset>where applicable - Error handling: Some error messages lack clear labels
- Loading states: Spinner needs
aria-busyoraria-label - Tables: Data tables need proper headers (
<th>,scope) - Links: "Xem tất cả" links should have context or aria-labels
- Icon-only buttons: Need proper
aria-label - Text alternatives: Ensure all meaningful icons have descriptive labels
ARIA Implementation Points:
- Dropdown navigation (if complex)
- Tab interfaces (recharts/charts)
- File upload components
- Date/time inputs (if added)
🔗 Current Locale Setup
Status: NO EXISTING i18n
- No
next-intlpackage - No translation files (JSON/YAML)
- No locale routes (
/en/*,/vi/*) - No i18n middleware
- Language is hardcoded to Vietnamese everywhere
Where i18n Will Be Integrated:
- Middleware: Detect locale from URL, cookie, or
Accept-Languageheader - Layout wrapper:
[locale]/layout.tsxfolder structure - Message providers:
next-intlProvider in root layout - API responses: May need to internationalize error messages from backend
🔒 Security Configuration
Next.js Config Security Headers:
X-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockContent-Security-Policywith Mapbox domains whitelistedPermissions-Policyrestricting camera/microphone/geolocation
Auth Middleware:
- Cookie-based auth check (
goodgo_authenticated) - Protected routes redirect to
/login?redirect=... - Public route protection in middleware
📊 Key Data Structures & Enums
Transaction Types:
const TRANSACTION_TYPES = [
{ value: 'SALE', label: 'Bán' },
{ value: 'RENT', label: 'Cho thuê' },
];
Property Types:
const PROPERTY_TYPES = [
{ value: 'APARTMENT', label: 'Căn hộ' },
{ value: 'HOUSE', label: 'Nhà riêng' },
{ value: 'VILLA', label: 'Biệt thự' },
{ value: 'LAND', label: 'Đất nền' },
{ value: 'OFFICE', label: 'Văn phòng' },
{ value: 'SHOPHOUSE', label: 'Shophouse' },
];
Listing Statuses:
const LISTING_STATUSES = {
DRAFT, PENDING_REVIEW, ACTIVE, RESERVED, SOLD, RENTED, EXPIRED, REJECTED
};
Directions:
const DIRECTIONS = [
{ value: 'NORTH', label: 'Bắc' },
{ value: 'SOUTH', label: 'Nam' },
{ value: 'EAST', label: 'Đông' },
{ value: 'WEST', label: 'Tây' },
// ... and diagonal combinations
];
Cities (13 total):
Hồ Chí Minh, Hà Nội, Đà Nẵng, Nha Trang, Cần Thơ, Hải Phòng,
Bình Dương, Đồng Nai, Long An, Bà Rịa - Vũng Tàu, [+ more]
🧪 Testing Setup
Test Framework: Vitest + React Testing Library
- Config file:
vitest.config.ts - Setup file:
vitest.setup.ts - Test files: Located alongside source (
__tests__folders)
Current Test Coverage:
- Component tests for UI library
- Auth store tests
- Validation schema tests
- Utility function tests
Testing Best Practices for i18n:
- Mock i18n provider in test setup
- Test both locale variants
- Verify translations render correctly
🚀 Implementation Readiness
Ready for i18n Implementation:
✅ Centralized validation messages (Zod schemas)
✅ Enum/constant-based UI text (TRANSACTION_TYPES, PROPERTY_TYPES, etc.)
✅ Component library with consistent patterns
✅ TypeScript for type safety
✅ Middleware support for locale routing
Minor Refactoring Needed:
⚠️ Extract some hardcoded strings from components
⚠️ Move form error messages to message files
⚠️ Centralize page metadata for i18n
📝 Summary: i18n & A11y Implementation Points
| Area | Current State | Needs Work |
|---|---|---|
| Locale Support | Hardcoded to Vietnamese | Implement next-intl with routing |
| Translation Keys | Scattered throughout code | Centralize in message files |
| Validation Messages | In Zod schemas (Vietnamese) | Extract to i18n messages |
| Component Text | Hardcoded strings | Use i18n hook |
| Metadata/SEO | Hardcoded Vietnamese | Generate for each locale |
| Color Contrast | Likely AA compliant | Audit and verify |
| Focus Management | Partial (buttons/links ok) | Add to modals & dropdowns |
| ARIA Labels | Good coverage | Complete missing labels |
| Error Messages | Most have aria-invalid | Add more context to some |
| Loading States | Spinner exists | Add aria-busy, better labels |
| Tables | Basic structure | Add proper header semantics |
🗂️ File Count Summary
- App routes: ~15 page files
- Components: ~35 component files
- Lib utilities: ~20 files (hooks, APIs, validations)
- Tests: ~15 test files
- Config files: ~8 configuration files
- Total TypeScript/TSX files: ~90+
Next Steps for Implementation
- Install i18n: Add
next-intlto dependencies - Create message files: Set up
messages/en.jsonandmessages/vi.json - Refactor middleware: Add locale detection & routing
- Update root layout: Wrap with i18n provider
- Update all components: Replace hardcoded strings with
useTranslations() - Test both locales: Ensure all pages render correctly
- A11y audit: Use axe DevTools to identify remaining issues
- Focus management: Add focus trapping in modals
- Testing: Update test setup for i18n mocking
Report Generated: April 9, 2026
Exploration Scope: Thorough
Confidence: High