feat: enhance domain handling for global representatives

- Updated `getStateInfo` to fetch site context for domains not in city.json, returning `repContext` with representative details.
- Implemented caching for site context requests to optimize performance.
- Modified doctor and clinic listing pages to pass the `domain` parameter when fetching data for global representatives.
- Adjusted metadata generation in layout and pages to reflect representative branding based on `repContext`.
- Added documentation for the new functionality in `.claude/prompt/global-rep-domain-site.md`.
This commit is contained in:
hamed
2026-07-09 07:34:09 +03:30
parent a56b7e8de5
commit ab2ab0dea2
12 changed files with 612 additions and 135 deletions
+43 -2
View File
@@ -3,14 +3,46 @@ import { headers } from "next/headers";
// Data
import citiesData from "@/data/city.json";
import statesData from "@/data/state.json";
import { fetchReq } from "@/lib/req";
import { ROOT_CITY_ID, isRootCity } from "@/lib/rootCity";
export { ROOT_CITY_ID, isRootCity };
const API_URL = process.env.NEXT_PUBLIC_API_URL;
// دامنه‌های خارج از city.json (دامنه اختصاصی نمایندگان سراسری) از backend پرسیده می‌شوند
// (GET /api/v1/site-context). cache ماژول‌سطح تا هر render یک درخواست نزند؛
// خطای شبکه هرگز صفحه را نمی‌شکند (null = مثل دامنه ناشناخته).
const SITE_CONTEXT_TTL_MS = 5 * 60 * 1000;
const siteContextCache = new Map();
async function fetchSiteContext(host) {
if (!host || host === "localhost" || host === "127.0.0.1" || !API_URL) return null;
const cached = siteContextCache.get(host);
if (cached && cached.expires > Date.now()) return cached.value;
let value = null;
try {
const json = await fetchReq(
`${API_URL}/api/v1/site-context?domain=${encodeURIComponent(host)}`
);
if (json?.data?.type === "representation") {
value = json.data.representation; // { uuid, full_name, is_global }
}
} catch {
value = null;
}
siteContextCache.set(host, { value, expires: Date.now() + SITE_CONTEXT_TTL_MS });
return value;
}
export async function getStateInfo() {
const headersList = await headers();
const host = headersList.get("host") || "";
const rawHost = headersList.get("host") || "";
const host = rawHost.split(":")[0];
const subdomain = host.split(".")[0];
// Match city by domain (supports both full domain and subdomain)
@@ -23,5 +55,14 @@ export async function getStateInfo() {
matchedCity &&
statesData.find((state) => state.id === matchedCity.province_id);
return { matchedCity, matchedState, isRoot: isRootCity(matchedCity) };
// فقط وقتی هیچ شهری match نشد سراغ backend می‌رویم — رفتار دامنه‌های شهری دست‌نخورده می‌ماند.
const repContext = matchedCity ? null : await fetchSiteContext(host);
return {
matchedCity,
matchedState,
isRoot: isRootCity(matchedCity),
repContext,
host,
};
}