fix(du-an): stop detail page crash from thin backend payload + client/server flag boundary
Some checks failed
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 1m28s
Deploy / Build API Image (push) Failing after 26s
Deploy / Build Web Image (push) Failing after 16s
Deploy / Build AI Services Image (push) Failing after 11s
E2E Tests / Playwright E2E (push) Failing after 23s
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 11s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 3s
Deploy / Smoke Test Staging (push) Has been cancelled
Deploy / Deploy to Production (push) Has been cancelled
Deploy / Deploy to Staging (push) Has been cancelled
Deploy / Rollback Staging (push) Has been cancelled
Deploy / Smoke Test Production (push) Has been cancelled
Deploy / Rollback Production (push) Has been cancelled
Security Scanning / Trivy Scan — Web Image (push) Has been cancelled
Security Scanning / Trivy Scan — AI Services Image (push) Has been cancelled
Security Scanning / Trivy Filesystem Scan (push) Has been cancelled
Security Scanning / Security Gate (push) Has been cancelled
Security Scanning / Trivy Scan — API Image (push) Has been cancelled

- 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>
This commit is contained in:
Ho Ngoc Hai
2026-04-19 10:11:06 +07:00
parent 2f07b374d9
commit 6ff039db1e
5 changed files with 115 additions and 7 deletions

View File

@@ -35,6 +35,16 @@ function shapeProject<T extends RawProjectListItem>(row: T) {
};
}
function shapeProjectDetail<T extends RawProjectListItem>(row: T) {
// Detail endpoint: preserve `media` so the gallery on the frontend can
// render, and keep the same shape as the list response for everything else.
const shaped = shapeProject(row);
return {
...shaped,
media: Array.isArray(row.media) ? row.media : [],
};
}
@ApiTags('projects')
@Controller('projects')
export class ProjectsController {
@@ -78,7 +88,7 @@ export class ProjectsController {
if (!result) {
throw new NotFoundException('Dự án', slugOrId);
}
return shapeProject(result);
return shapeProjectDetail(result);
}
// ── Admin endpoints ───────────────────────────────────────────────