# GoodGo Platform Frontend Exploration Report ## apps/web (Next.js 15 with App Router) **Date:** April 9, 2026 **Status:** Pre-i18n (No existing i18n setup detected) **Next.js Version:** 15.5.14 | **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: ```json { "@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): ```json { "@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: ```javascript 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: ```typescript - 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): ```typescript - 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 names - `components/search/property-card.tsx` β€” Property info badges, direction labels - `components/listings/listing-form-steps.tsx` β€” Form labels, validation messages - `components/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 messages - `components/auth/oauth-buttons.tsx` β€” Button text ("Google", "Zalo") --- ## 🧩 Key Components Requiring Translation ### Forms (Form validation + labels): 1. **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Γ½" 2. **Register Form** (`app/(auth)/register/page.tsx`) - Similar structure to login 3. **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 4. **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: `
`, `