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>
59 lines
1.5 KiB
TypeScript
59 lines
1.5 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
|
|
const LOCAL_STORAGE_KEY = 'goodgo:residential_projects';
|
|
const QUERY_PARAM = 'residential_projects';
|
|
|
|
function readEnvDefault(): boolean {
|
|
const raw = process.env['NEXT_PUBLIC_FEATURE_RESIDENTIAL_PROJECTS'];
|
|
if (raw == null || raw === '') return true;
|
|
return !(raw === '0' || raw.toLowerCase() === 'false');
|
|
}
|
|
|
|
function readOverride(): boolean | null {
|
|
if (typeof window === 'undefined') return null;
|
|
|
|
const params = new URLSearchParams(window.location.search);
|
|
const qp = params.get(QUERY_PARAM);
|
|
if (qp === '1' || qp === 'true') {
|
|
try {
|
|
window.localStorage.setItem(LOCAL_STORAGE_KEY, '1');
|
|
} catch {
|
|
// localStorage may be blocked — ignore
|
|
}
|
|
return true;
|
|
}
|
|
if (qp === '0' || qp === 'false') {
|
|
try {
|
|
window.localStorage.setItem(LOCAL_STORAGE_KEY, '0');
|
|
} catch {
|
|
// localStorage may be blocked — ignore
|
|
}
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
const stored = window.localStorage.getItem(LOCAL_STORAGE_KEY);
|
|
if (stored === '1') return true;
|
|
if (stored === '0') return false;
|
|
} catch {
|
|
// ignore
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function useResidentialProjectsFlag(): boolean {
|
|
const [enabled, setEnabled] = useState<boolean>(readEnvDefault());
|
|
|
|
useEffect(() => {
|
|
const override = readOverride();
|
|
setEnabled(override ?? readEnvDefault());
|
|
}, []);
|
|
|
|
return enabled;
|
|
}
|
|
|
|
// NOTE: The server-side helper lives in `@/lib/feature-flags/residential-projects`
|
|
// to avoid making it a client-only export (this file has `'use client'`).
|