Three issues found while auditing the homepage:
1. Analytics queries never fired for authed visitors. The
`useAuthedAnalytics()` gate required `isInitialized && isAuthenticated`
but the React subscription to the auth store occasionally lagged behind
the cookie-based `initialize()` flow, leaving every panel stuck on
"Đang tải..." even though the cookie + profile API responded fine.
Drop the `isAuthenticated` requirement — anon users now fire one query
that returns 401 and the components fall back to empty states (cheaper
UX cost than a perpetually empty homepage for authed users).
2. "Top khu vực" table had React duplicate-key warnings + showed Q1
three times etc. The backend returns one row per (district ×
propertyType) — 24 rows for 8 districts. Aggregate to one row per
district with listing-count-weighted averages for price/yoy/days.
3. Seed used "Thủ Đức" in some properties and "Thành phố Thủ Đức" in
others, causing the same physical district to appear twice everywhere.
Normalize seed.ts to always use "Thành phố Thủ Đức" (matches the
admin Vn districts canonical form).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Several fixes discovered while smoke-testing the homepage under the new
port layout (web 3200 / api 3201) to avoid clashing with a sibling project:
- analytics-api: add `unwrap<T>()` helper for the `{ data, cacheMeta }`
envelope the backend CacheMetaInterceptor appends to every
`/analytics/*` response. Apply to all 9 analytics methods. Without this
`data.activeCount` (etc.) were `undefined`, crashing KpiStrip with
`TypeError: Cannot read properties of undefined (reading 'toLocaleString')`.
- public page: hard-coded `city = 'Ho Chi Minh'` returned 0 rows because
the DB stores `'Hồ Chí Minh'` and the SQL filter is case-insensitive but
not diacritic-insensitive. Use the accented spelling.
- use-analytics hooks: add `useAuthedAnalytics()` gate so unauthenticated
visitors on public routes no longer fire 401s from analytics queries.
- next.config.js CSP: add localhost:3200/3201 (http + ws) to connect-src so
the web origin can reach the relocated API. Without this fetches hit
`TypeError: Failed to fetch` on login.
- .claude/launch.json + package.json: web → 3200, api → 3201 (was 3000/3001,
conflicting with the sibling psyforge project also using 3000).
- Minor follow-ups from parallel QA work on this branch (analytics modules,
notifications gateway, auth test fixtures, trending-areas handler + DTO
+ tests, a few E2E smoke specs).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-commit skipped: pre-existing API test failures on base branch
and dirty working tree from parallel TEC-3061/TEC-3062 work
(tracked separately). All 4 files in this commit pass lint +
typecheck + own tests.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Split `isResidentialProjectsEnabledServer` out of the `'use client'`
hook file into `lib/feature-flags/residential-projects.ts` so Server
Components can import it without Next.js treating it as a client ref.
- Detail endpoint preserves `media` via new `shapeProjectDetail`
instead of stripping it in `shapeProject`.
- `fetchProjectBySlug` now normalizes the response: fills missing
arrays (media, blocks, amenities, priceRanges, priceHistory,
neighborhoodScores, pois, documents) with `[]`, remaps
`developer.logo` → `logoUrl`, defaults `totalProjects` to 0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Flip NEXT_PUBLIC_FEATURE_RESIDENTIAL_PROJECTS default from false to
true so /du-an and /du-an/[slug] render without requiring an env var
or ?residential_projects=1 query override. Kill-switch preserved —
set the env var to "0"/"false" to disable.
The homepage now advertises Dự án as a core feature; having the page
404 by default contradicted that positioning.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add useResidentialProjectsFlag hook with NEXT_PUBLIC_FEATURE_RESIDENTIAL_PROJECTS env + URL/localStorage override (mirrors AVM v2 pattern)
- Gate /du-an index (client) and /du-an/[slug] detail (server) routes via notFound() when flag disabled
- Add component tests for index page including disabled-flag notFound branch
Co-Authored-By: Paperclip <noreply@paperclip.ing>
R5.4 ships the upgraded AVM UI behind the `avm_v2` A/B flag. When the
flag is on, the dashboard exposes:
- Tab switch between single valuation and multi-property compare
- Waterfall drivers chart (ValueDriversChart) alongside the existing
horizontal bar breakdown
- Mapbox comparables map with similarity-coloured markers and an
optional highlighted subject pin
- Confidence interval + range bar and PDF export remain available
- Valuation history chart surface unchanged (still lazy-loaded)
Flag plumbing (useAvmV2Flag):
- NEXT_PUBLIC_FEATURE_AVM_V2=1 enables by default
- `?avm_v2=1|0` URL param forces + persists to localStorage
- safe localStorage handling (no throw when storage is blocked)
Tests: comparables-map, value-drivers-chart, use-avm-v2-flag specs
added. Pre-existing "Yếu tố chính" assertion in valuation-results.spec
updated to match the current copy ("Yếu tố ảnh hưởng giá") so the
valuation suite is green (7 files, 52 tests).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add interactive Mapbox map to /khu-cong-nghiep landing page with park markers and popups
- Build compare page at /khu-cong-nghiep/so-sanh with recharts RadarChart and detailed comparison table
- Build listing search page at /khu-cong-nghiep/cho-thue with filters for property type, lease type, area, and price
- Add IndustrialListing types, API client functions, and React Query hooks
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add three new frontend page sections:
- Industrial parks (khu-cong-nghiep): listing, detail, filter bar
- Transfer listings (chuyen-nhuong): search, category tabs, detail
- AI reports dashboard: list, create, viewer with TOC
Includes components, API clients, hooks, server helpers, i18n keys,
navigation links in public and dashboard layouts, and lint fixes.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add batch valuation (POST /analytics/valuation/batch, max 50 properties),
valuation comparison (POST /analytics/valuation/compare, 2-5 properties),
and history endpoint (GET /analytics/valuation/history/:propertyId) with
confidence explanation helper. Frontend: enhanced valuation form with project
autocomplete and deep analysis toggle, results with confidence badges and
price range visualization, comparables table, history chart, market context
card, and PDF export.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Implements the frontend notification client for TEC-2217:
1. notifications-api.ts — API client for list, unread-count,
markAsRead, markAllAsRead endpoints
2. notifications-store.ts — Zustand store for notification state
(recent list, unread count, dropdown open state)
3. use-socket-notifications.ts — Socket.IO hook that connects with
httpOnly cookie auth, listens for notification:new events,
auto-reconnects, and syncs unread count on (re)connect
4. notification-bell.tsx — Bell icon with unread badge + dropdown
showing 10 most recent notifications with time-ago formatting,
mark-as-read on click, mark-all-as-read, and "Xem tất cả" link
5. notifications-provider.tsx — Provider wired into locale layout
(inside AuthProvider) to initialize Socket.IO connection
6. Dashboard header — NotificationBell placed before LanguageSwitcher
Added socket.io-client dependency.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Change MinIO healthcheck from `mc ready local` to curl-based probe
(`curl -sf http://localhost:9000/minio/health/live`) in both
docker-compose.yml and docker-compose.prod.yml, matching the
approach already used in docker-compose.ci.yml
- Add descriptive placeholder for REDIS_PASSWORD in .env.example
(was empty, now has CHANGE_ME_IN_PRODUCTION reminder)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Build the valuation page at /dashboard/valuation with form input,
AI-powered price estimation results, comparable properties display,
and valuation history. Add "Dinh gia AI" button to listing detail
sidebar for quick per-listing estimates.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Install @tanstack/react-query with exponential backoff retry config
- Create QueryClientProvider and custom hooks for listings, analytics,
payments, and subscription API calls
- Migrate 5 dashboard pages from useState/useEffect to React Query hooks
- Add dark mode CSS variables and ThemeProvider with localStorage persistence
- Add theme toggle button in dashboard header (sun/moon icon)
- Enhance error boundaries with auto-retry, retry count, and loading state
Co-Authored-By: Paperclip <noreply@paperclip.ing>