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
+116
View File
@@ -0,0 +1,116 @@
# سایت دامنه اختصاصی نماینده سراسری — تشخیص دامنه، فیلتر پزشکان/کلینیک‌ها
## پروژه
`nobat724_front`**پیش‌نیاز:** پرامپت backend اول اجرا شود: `clinicpro/.claude/prompt/representation-multi-city-domain-commission.md`. این پرامپت مصرف‌کننده قراردادهای آن است:
- `GET /api/v1/site-context?domain=<host>` (عمومی) → `{ type: "city"|"representation"|"unknown", representation: {uuid, full_name, is_global}|null, city: {...}|null }`
- پارامتر جدید `domain` روی `GET /api/v1/doctors` و `GET /api/v1/clinics` — اگر دامنه متعلق به نماینده سراسری باشد، backend فقط پزشکان/کلینیک‌های همان نماینده را برمی‌گرداند.
## زمینه
سایت multi-domain است و دامنه فقط با `data/city.json` تطبیق داده می‌شود (`lib/getStateInfo.js`). نماینده سراسری دامنه اختصاصی خودش را دارد (مثل `x-nobat.ir`) که در city.json نیست → الان چنین دامنه‌ای مثل «بدون شهر» رفتار می‌کند و همه پزشکان را نشان می‌دهد. باید: دامنه نماینده سراسری تشخیص داده شود و فقط پزشکان/کلینیک‌های ثبت‌شده توسط همان نماینده نمایش یابند. کمیسیون خودش backend-side است (از `frontend_address` پرداخت) — فرانت فقط باید مثل الان دامنه درست را در `frontend_address` بفرستد (بدون تغییر).
## مشکل / هدف
۱. `getStateInfo` برای دامنه‌های خارج از city.json از API زمینه بگیرد (`site-context`) و `repContext` برگرداند.
۲. صفحات لیست پزشکان/کلینیک‌ها روی دامنه نماینده سراسری، پارامتر `domain` را به API پاس بدهند.
۳. متادیتا/برندینگ صفحات روی دامنه نماینده از `full_name` نماینده ساخته شود (fallback «نوبت 724»).
## فایل‌های مرتبط
| فایل | نقش |
|------|-----|
| `lib/getStateInfo.js` | تشخیص دامنه — فقط city.json؛ باید repContext هم بدهد |
| `app/doctors/page.js` | لیست پزشکان — fetch `/api/v1/doctors` |
| `app/clinics/page.js` | لیست کلینیک‌ها — fetch `/api/v1/clinics` |
| `app/layout.js` | متادیتای پایه از matchedCity |
| `components/home/*` (سرچ صفحه اصلی) | روی دامنه rep هم باید `domain` را پاس بدهد |
| `lib/req.js` | `fetchReq` برای server-side |
## وضعیت فعلی
`lib/getStateInfo.js` (کامل — کپی واقعی):
```js
export async function getStateInfo() {
const headersList = await headers();
const host = headersList.get("host") || "";
const subdomain = host.split(".")[0];
const matchedCity = citiesData.find((city) => {
const cityDomain = city.domain.split(".")[0];
return cityDomain === subdomain;
});
const matchedState =
matchedCity && statesData.find((state) => state.id === matchedCity.province_id);
return { matchedCity, matchedState, isRoot: isRootCity(matchedCity) };
}
```
`app/doctors/page.js` (بخش fetch — کپی واقعی):
```js
const { matchedCity, matchedState, isRoot } = await getStateInfo();
// ...
if (cityParams) newSearchParams.city = cityParams;
else if (matchedCity && !isRoot) newSearchParams.city = matchedCity.name;
const params = buildDoctorParams(newSearchParams);
doctors = await fetchReq(`${API_URL}/api/v1/doctors`, { params });
```
## وظایف
### ۱. توسعه `getStateInfo` — repContext
خروجی جدید: `{ matchedCity, matchedState, isRoot, repContext }` که `repContext = { uuid, full_name, is_global } | null`.
```js
export async function getStateInfo() {
// ... منطق فعلی city.json دست‌نخورده ...
let repContext = null;
if (!matchedCity && host) {
repContext = await fetchSiteContext(host); // فقط وقتی city match نشد
}
return { matchedCity, matchedState, isRoot: isRootCity(matchedCity), repContext };
}
```
- `fetchSiteContext(host)`: صدا زدن `GET ${NEXT_PUBLIC_API_URL}/api/v1/site-context?domain=${host}` با `fetchReq`؛ اگر `type === "representation"` → آبجکت representation، وگرنه null. **خطای شبکه هرگز صفحه را نشکند** (try/catch → null) و پاسخ برای هر host **cache شود** (in-memory `Map` در سطح ماژول + `next: { revalidate: 300 }` اگر با fetch native؛ با axios همان Map با TTL ۵ دقیقه کافی است) — این تابع در هر render صدا می‌خورد.
- `localhost` و host خالی → بدون درخواست، null.
- تمام call-siteهای فعلی `getStateInfo` بدون تغییر کار کنند (فیلد اضافه فقط additive است).
### ۲. پاس دادن `domain` در لیست‌ها
در `app/doctors/page.js` و `app/clinics/page.js`:
```js
const { matchedCity, matchedState, isRoot, repContext } = await getStateInfo();
// ...
if (repContext?.is_global) {
params.domain = host; // host از headers — از طریق getStateInfo برگردان یا headers() مستقیم؟
// الگو: getStateInfo مقدار host را هم برگرداند تا صفحات دوباره parse نکنند
delete params.city; delete params.state; // روی دامنه نماینده، فیلتر شهر بی‌معنی است
}
doctors = await fetchReq(`${API_URL}/api/v1/doctors`, { params });
```
- `getStateInfo` فیلد `host` را هم برگرداند (نرمال‌شده) تا هیچ صفحه‌ای خودش `headers()` را برای دامنه parse نکند — هم‌راستا با اصل «سرویس مرکزی دامنه» در backend.
- جستجوی صفحه اصلی (`components/home/search/*`) که client-side به `/api/v1/doctors` می‌زند: از `window.location.hostname` همان پارامتر `domain` را وقتی سایتِ rep است اضافه کند — تشخیص client-side: مقدار repContext از server از طریق props/context (ساده‌ترین راه: `ProvinceProvider` یا prop از layout؛ الگوی موجود client-side پروژه را دنبال کن).
### ۳. متادیتا و برندینگ دامنه نماینده
- `app/layout.js` و `generateMetadata` صفحات doctors/clinics: وقتی `repContext` هست:
- `siteName = repContext.full_name`
- title الگو: `نوبت‌دهی آنلاین پزشکان | ${repContext.full_name}`
- description عمومی (بدون نام شهر).
- Header/Footer: جایی که `matchedCity?.site_name` مصرف می‌شود (`components/layout/*`, `app/component/Logo.js`) fallback به `repContext?.full_name` قبل از «نوبت 724».
- صفحات وابسته به شهر (مثل انتخاب شهر در سرچ): روی دامنه rep رفتار «ریشه» (همه شهرها) بماند — گیت اضافه نزن؛ فقط لیست نتایج فیلتر می‌شود.
## نکات مهم
- **هیچ regression روی دامنه‌های شهری**: مسیر `matchedCity` پیدا شد → `fetchSiteContext` اصلاً صدا زده نشود؛ رفتار فعلی بایت‌به‌بایت حفظ.
- `DEV_MODE=TRUE` مثل قبل noindex — دامنه‌های rep هم مشمول همان robots.
- تست local: `HOST=x-nobat.localhost npm run dev` کار نمی‌کند مگر backend لوکال یک rep با دامنه `x-nobat.localhost` داشته باشد — در گزارش، دستور ساخت rep تستی (از پنل ادمین clinicpro لوکال) را ذکر کن.
- خطای API سایت‌کانتکست → سایت مثل دامنه ناشناخته (رفتار فعلی) — هرگز 500 نشود (درس صفحه contact-us).
- build کامل (`npm run build`) و تست دستی سه حالت: دامنه شهر (yazd-nobat.localhost)، دامنه ریشه، دامنه ناشناخته.
- بعد از پیاده‌سازی: مستندات backend (`clinicpro/docs/api/doctor.md`/`clinic.md`) باید با مصرف واقعی این فرانت هم‌خوان باشد — اگر اختلافی دیدی همان‌جا اصلاح کن.
- **عملیاتی**: هر دامنه نماینده سراسری باید در Coolify به سرویس فرانت و به `ALLOWED_FRONTEND_HOSTS` بک‌اند اضافه شود (CORS/TLS) — در گزارش نهایی یادآوری کن.
+10 -3
View File
@@ -18,8 +18,8 @@ function listingRobots(params) {
export async function generateMetadata({ searchParams }) {
const awaitedParams = await searchParams;
const { matchedCity, matchedState } = await getStateInfo();
const siteName = matchedCity?.site_name || "نوبت 724";
const { matchedCity, matchedState, repContext } = await getStateInfo();
const siteName = matchedCity?.site_name || repContext?.full_name || "نوبت 724";
const cityName = matchedCity?.name || matchedState?.name || "";
const title = cityName
? `کلینیک‌های ${cityName} | جستجو و رزرو نوبت | ${siteName}`
@@ -39,7 +39,7 @@ export async function generateMetadata({ searchParams }) {
export default async function Clinics({ searchParams }) {
const awaitedSearchParams = await searchParams;
const { matchedCity, matchedState, isRoot } = await getStateInfo();
const { matchedCity, matchedState, isRoot, repContext, host } = await getStateInfo();
const API_URL = process.env.NEXT_PUBLIC_API_URL;
const stateParams = awaitedSearchParams.state;
const cityParams = awaitedSearchParams.city;
@@ -59,6 +59,13 @@ export default async function Clinics({ searchParams }) {
const params = buildClinicParams(newSearchParams);
// دامنه‌ی نماینده سراسری: backend فقط کلینیک‌های همان نماینده را برمی‌گرداند؛ فیلتر شهر بی‌معنی است.
if (repContext?.is_global) {
delete params.city;
delete params.state;
params.domain = host;
}
// Req
clinics = await fetchReq(`${API_URL}/api/v1/clinics`, {
params,
+10 -3
View File
@@ -18,8 +18,8 @@ function listingRobots(params) {
export async function generateMetadata({ searchParams }) {
const awaitedParams = await searchParams;
const { matchedCity, matchedState } = await getStateInfo();
const siteName = matchedCity?.site_name || "نوبت 724";
const { matchedCity, matchedState, repContext } = await getStateInfo();
const siteName = matchedCity?.site_name || repContext?.full_name || "نوبت 724";
const cityName = matchedCity?.name || matchedState?.name || "";
const title = cityName
? `پزشکان ${cityName} | جستجو و رزرو نوبت | ${siteName}`
@@ -39,7 +39,7 @@ export async function generateMetadata({ searchParams }) {
async function Doctors({ searchParams }) {
const awaitedSearchParams = await searchParams;
const { matchedCity, matchedState, isRoot } = await getStateInfo();
const { matchedCity, matchedState, isRoot, repContext, host } = await getStateInfo();
const API_URL = process.env.NEXT_PUBLIC_API_URL;
let doctors = null;
@@ -59,6 +59,13 @@ async function Doctors({ searchParams }) {
const params = buildDoctorParams(newSearchParams);
// دامنه‌ی نماینده سراسری: backend فقط پزشکان همان نماینده را برمی‌گرداند؛ فیلتر شهر بی‌معنی است.
if (repContext?.is_global) {
delete params.city_id;
delete params.state_id;
params.domain = host;
}
doctors = await fetchReq(`${API_URL}/api/v1/doctors`, {
params,
});
+5 -3
View File
@@ -16,7 +16,7 @@ export const viewport = {
};
export async function generateMetadata() {
const { matchedCity } = await getStateInfo();
const { matchedCity, repContext } = await getStateInfo();
const headersList = await headers();
const metadataBase = new URL(buildCanonicalUrl(headersList.get("host"), "/"));
@@ -27,7 +27,9 @@ export async function generateMetadata() {
matchedCity?.title ||
(matchedCity?.name
? `نوبت‌دهی آنلاین پزشکان ${matchedCity.name} | نوبت 724`
: "نوبت 724 | سیستم نوبت‌دهی آنلاین پزشکی");
: repContext?.full_name
? `نوبت‌دهی آنلاین پزشکان | ${repContext.full_name}`
: "نوبت 724 | سیستم نوبت‌دهی آنلاین پزشکی");
const description = matchedCity?.description || "نوبت 724 - سیستم آنلاین نوبت‌دهی برای پزشکان و کلینیک‌ها. با استفاده از نوبت 724، به راحتی نوبت پزشکی خود را به صورت آنلاین رزرو کنید و از خدمات سریع و کارآمد ما بهره‌مند شوید";
const baseMetadata = {
@@ -42,7 +44,7 @@ export async function generateMetadata() {
description,
type: "website",
locale: "fa_IR",
siteName: matchedCity?.site_name || "نوبت 724",
siteName: matchedCity?.site_name || repContext?.full_name || "نوبت 724",
images: ["https://nobat724.com/assets/images/logo.png"],
},
twitter: {
+2 -1
View File
@@ -371,5 +371,6 @@
"369": "Community 369",
"370": "Community 370",
"371": "Community 371",
"372": "Community 372"
"372": "Community 372",
"373": "Community 373"
}
+37 -32
View File
@@ -1,16 +1,16 @@
# Graph Report - nobat724_front (2026-07-08)
# Graph Report - nobat724_front (2026-07-09)
## Corpus Check
- 594 files · ~536,128 words
- 595 files · ~537,275 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 2037 nodes · 2019 edges · 373 communities (343 shown, 30 thin omitted)
- Extraction: 95% EXTRACTED · 5% INFERRED · 0% AMBIGUOUS · INFERRED: 98 edges (avg confidence: 0.8)
- 2051 nodes · 2034 edges · 374 communities (344 shown, 30 thin omitted)
- Extraction: 95% EXTRACTED · 5% INFERRED · 0% AMBIGUOUS · INFERRED: 99 edges (avg confidence: 0.8)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `ee47b535`
- Built from commit: `a56b7e8d`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
@@ -148,9 +148,10 @@
- [[_COMMUNITY_Community 370|Community 370]]
- [[_COMMUNITY_Community 371|Community 371]]
- [[_COMMUNITY_Community 372|Community 372]]
- [[_COMMUNITY_Community 373|Community 373]]
## God Nodes (most connected - your core abstractions)
1. `getStateInfo()` - 23 edges
1. `getStateInfo()` - 24 edges
2. `🏥 نوبت724 (Nobat724) - سیستم نوبت‌دهی آنلاین پزشکی` - 19 edges
3. `imageUrl()` - 15 edges
4. `Dashboard()` - 10 edges
@@ -162,8 +163,6 @@
10. `Architecture Overview` - 10 edges
## Surprising Connections (you probably didn't know these)
- `Dashboard()` --calls--> `safeJsonParse()` [INFERRED]
app/dashboard/page.js → lib/sanitize.js
- `AboutUsPage()` --calls--> `getStateInfo()` [INFERRED]
components/aboutUs/index.js → lib/getStateInfo.js
- `ContactUsPage()` --calls--> `getStateInfo()` [INFERRED]
@@ -172,11 +171,13 @@
components/home/search/index.js → lib/getStateInfo.js
- `StLayout()` --calls--> `getStateInfo()` [INFERRED]
components/layout/StLayout.js → lib/getStateInfo.js
- `generateMetadata()` --calls--> `getStateInfo()` [INFERRED]
app/about-us/page.js → lib/getStateInfo.js
## Import Cycles
- None detected.
## Communities (373 total, 30 thin omitted)
## Communities (374 total, 30 thin omitted)
### Community 0 - "Community 0"
Cohesion: 0.05
@@ -191,8 +192,8 @@ Cohesion: 0.04
Nodes (45): dependencies, aos, axios, @casl/ability, @casl/react, date-fns, dayjs, @emotion/cache (+37 more)
### Community 3 - "Community 3"
Cohesion: 0.10
Nodes (12): ProvinceContext, ProvinceProvider(), Probe(), useProvince(), Content(), Form(), SubmitData(), EditField() (+4 more)
Cohesion: 0.24
Nodes (14): getCurrentDomain(), robots(), cityFilterParams(), fetchAllPages(), getBlogUrls(), getCityScope(), getClinicUrls(), getCurrentDomain() (+6 more)
### Community 4 - "Community 4"
Cohesion: 0.07
@@ -203,12 +204,12 @@ Cohesion: 0.08
Nodes (18): AppointmentList(), ItemAppointment(), AboutDcotor(), DetailDoctor(), Link(), listLink, List(), VAZIR_WEIGHTS (+10 more)
### Community 6 - "Community 6"
Cohesion: 0.15
Nodes (8): Dashboard(), metadata, defineAbilitiesFor(), getUser(), buildPatientUser(), getServerAccessToken(), LogIn(), metadata
Cohesion: 0.06
Nodes (17): Dashboard(), metadata, defineAbilitiesFor(), getUser(), formatToman(), rialToToman(), buildPatientUser(), safeJsonParse() (+9 more)
### Community 8 - "Community 8"
Cohesion: 0.07
Nodes (28): devDependencies, babel-plugin-react-compiler, cross-env, eslint, eslint-config-next, @eslint/eslintrc, jsdom, prettier (+20 more)
Cohesion: 0.13
Nodes (15): devDependencies, babel-plugin-react-compiler, cross-env, eslint, eslint-config-next, @eslint/eslintrc, jsdom, prettier (+7 more)
### Community 9 - "Community 9"
Cohesion: 0.07
@@ -291,8 +292,8 @@ Cohesion: 0.12
Nodes (16): دیپلوی nobat724_front روی Liara (پلتفرم Next.js), زمینه, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی, وظایف, پروژه (+8 more)
### Community 29 - "Community 29"
Cohesion: 0.13
Nodes (8): generateMetadata(), generateMetadata(), Appointment(), metadata, getStateInfo(), ContentLogin(), generateMetadata(), Specialties()
Cohesion: 0.12
Nodes (10): generateMetadata(), generateMetadata(), Appointment(), metadata, fetchSiteContext(), getStateInfo(), siteContextCache, ContentLogin() (+2 more)
### Community 30 - "Community 30"
Cohesion: 0.12
@@ -355,8 +356,8 @@ Cohesion: 0.14
Nodes (13): `hours/List.js`, آبجکت اسلات (از `adaptSlots` / API), تأیید و سخت‌سازی غیرفعال‌بودن اسلات‌های گذشته در صفحه‌ی نوبت, زمینه, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی (کد واقعی) (+5 more)
### Community 45 - "Community 45"
Cohesion: 0.31
Nodes (9): generateMetadata(), generateMetadata(), imageUrl(), normalizeBlog(), Blog(), Doctor(), getBlog, getDoctor (+1 more)
Cohesion: 0.25
Nodes (12): generateMetadata(), generateMetadata(), RootLayout(), imageUrl(), normalizeBlog(), getRequestOrigin(), safeJsonLd(), Blog() (+4 more)
### Community 46 - "Community 46"
Cohesion: 0.22
@@ -379,8 +380,8 @@ Cohesion: 0.15
Nodes (12): زمینه, فایل‌های مرتبط, مشکل / هدف, نمایش تعداد پزشکان هر تخصص در صفحه /specialties, نکات مهم, وضعیت فعلی (کد واقعی), وظایف, پروژه (+4 more)
### Community 51 - "Community 51"
Cohesion: 0.09
Nodes (25): getCurrentDomain(), robots(), cityFilterParams(), fetchAllPages(), getBlogUrls(), getCityScope(), getClinicUrls(), getCurrentDomain() (+17 more)
Cohesion: 0.06
Nodes (20): ClinicsPage(), ProvinceContext, ProvinceProvider(), Probe(), useProvince(), Content(), Form(), SubmitData() (+12 more)
### Community 52 - "Community 52"
Cohesion: 0.24
@@ -463,8 +464,8 @@ Cohesion: 0.22
Nodes (7): degree, Form(), gender, degree, Form(), gender, filterList()
### Community 72 - "Community 72"
Cohesion: 0.46
Nodes (7): generateMetadata(), buildCanonicalPath(), buildCanonicalUrl(), buildMainCanonicalUrl(), getCanonicalUrl(), getRequestOrigin(), normalizeHost()
Cohesion: 0.52
Nodes (6): generateMetadata(), buildCanonicalPath(), buildCanonicalUrl(), buildMainCanonicalUrl(), getCanonicalUrl(), normalizeHost()
### Community 73 - "Community 73"
Cohesion: 0.32
@@ -484,7 +485,7 @@ Nodes (5): HeadTab(), setNewData(), DetailUser(), Disease(), IsTurnsDetails()
### Community 79 - "Community 79"
Cohesion: 0.17
Nodes (5): clearAccessToken(), api, handleSessionExpired(), request, removeToken()
Nodes (11): زمینه, سایت دامنه اختصاصی نماینده سراسری — تشخیص دامنه، فیلتر پزشکان/کلینیک‌ها, فایل‌های مرتبط, مشکل / هدف, نکات مهم, وضعیت فعلی, وظایف, پروژه (+3 more)
### Community 81 - "Community 81"
Cohesion: 0.24
@@ -563,8 +564,8 @@ Cohesion: 0.70
Nodes (4): generateMetadata(), Clinic(), computeClinicRating(), getClinic
### Community 366 - "Community 366"
Cohesion: 0.50
Nodes (3): RootLayout(), safeJsonLd(), safeJsonParse()
Cohesion: 0.25
Nodes (8): scripts, build, dev, lint, start, test, test:cov, test:watch
### Community 367 - "Community 367"
Cohesion: 0.19
@@ -590,25 +591,29 @@ Nodes (3): ModalAddRelatives(), Relatives(), ItemRelatives()
Cohesion: 0.40
Nodes (3): ModalAddSurgeries(), Surgeries(), ItemSurgeries()
### Community 373 - "Community 373"
Cohesion: 0.33
Nodes (5): engines, node, name, private, version
## Knowledge Gaps
- **687 isolated node(s):** `metadata`, `FILTER_KEYS`, `fallbackLabels`, `MaterialUISwitch`, `fixedIconData` (+682 more)
- **697 isolated node(s):** `metadata`, `FILTER_KEYS`, `fallbackLabels`, `MaterialUISwitch`, `fixedIconData` (+692 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **30 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `getStateInfo()` connect `Community 29` to `Community 0`, `Community 67`, `Community 70`, `Community 6`, `Community 72`, `Community 365`, `Community 45`, `Community 366`, `Community 16`, `Community 14`, `Community 51`, `Community 85`, `Community 86`?**
_High betweenness centrality (0.040) - this node is a cross-community bridge._
- **Why does `getStateInfo()` connect `Community 29` to `Community 0`, `Community 67`, `Community 70`, `Community 6`, `Community 72`, `Community 365`, `Community 45`, `Community 14`, `Community 16`, `Community 51`, `Community 85`, `Community 86`?**
_High betweenness centrality (0.031) - this node is a cross-community bridge._
- **Why does `imageUrl()` connect `Community 45` to `Community 0`, `Community 99`, `Community 4`, `Community 7`, `Community 365`, `Community 81`, `Community 55`, `Community 56`, `Community 89`?**
_High betweenness centrality (0.039) - this node is a cross-community bridge._
- **Why does `Dashboard()` connect `Community 6` to `Community 3`, `Community 70`, `Community 366`, `Community 79`, `Community 29`?**
_High betweenness centrality (0.025) - this node is a cross-community bridge._
- **Why does `isRootCity()` connect `Community 51` to `Community 3`, `Community 29`?**
_High betweenness centrality (0.015) - this node is a cross-community bridge._
- **Are the 22 inferred relationships involving `getStateInfo()` (e.g. with `generateMetadata()` and `AboutUsPage()`) actually correct?**
_`getStateInfo()` has 22 INFERRED edges - model-reasoned connections that need verification._
- **Are the 14 inferred relationships involving `imageUrl()` (e.g. with `generateMetadata()` and `generateMetadata()`) actually correct?**
_`imageUrl()` has 14 INFERRED edges - model-reasoned connections that need verification._
- **What connects `metadata`, `FILTER_KEYS`, `fallbackLabels` to the rest of the system?**
_687 weakly-connected nodes found - possible documentation gaps or missing edges._
_697 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `Community 0` be split into smaller, more focused modules?**
_Cohesion score 0.05263157894736842 - nodes in this community are weakly interconnected._
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+370 -78
View File
@@ -1380,7 +1380,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "fields_editfield",
"community": 3,
"community": 51,
"norm_label": "editfield.js"
},
{
@@ -1390,7 +1390,7 @@
"source_location": "L6",
"_origin": "ast",
"id": "fields_editfield_convertpersiantoenglish",
"community": 3,
"community": 51,
"norm_label": "convertpersiantoenglish()"
},
{
@@ -1400,7 +1400,7 @@
"source_location": "L20",
"_origin": "ast",
"id": "fields_editfield_editfield",
"community": 3,
"community": 51,
"norm_label": "editfield()"
},
{
@@ -2127,10 +2127,10 @@
"label": "RootLayout()",
"file_type": "code",
"source_file": "app/layout.js",
"source_location": "L82",
"source_location": "L84",
"_origin": "ast",
"id": "app_layout_rootlayout",
"community": 366,
"community": 45,
"norm_label": "rootlayout()"
},
{
@@ -2320,7 +2320,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "app_robots",
"community": 51,
"community": 3,
"norm_label": "robots.js"
},
{
@@ -2330,7 +2330,7 @@
"source_location": "L4",
"_origin": "ast",
"id": "app_robots_getcurrentdomain",
"community": 51,
"community": 3,
"norm_label": "getcurrentdomain()"
},
{
@@ -2340,7 +2340,7 @@
"source_location": "L13",
"_origin": "ast",
"id": "app_robots_robots",
"community": 51,
"community": 3,
"norm_label": "robots()"
},
{
@@ -2350,7 +2350,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "app_sitemap",
"community": 51,
"community": 3,
"norm_label": "sitemap.js"
},
{
@@ -2360,7 +2360,7 @@
"source_location": "L17",
"_origin": "ast",
"id": "app_sitemap_tosafedate",
"community": 51,
"community": 3,
"norm_label": "tosafedate()"
},
{
@@ -2370,7 +2370,7 @@
"source_location": "L23",
"_origin": "ast",
"id": "app_sitemap_withlastmodified",
"community": 51,
"community": 3,
"norm_label": "withlastmodified()"
},
{
@@ -2380,7 +2380,7 @@
"source_location": "L28",
"_origin": "ast",
"id": "app_sitemap_getcurrentdomain",
"community": 51,
"community": 3,
"norm_label": "getcurrentdomain()"
},
{
@@ -2390,7 +2390,7 @@
"source_location": "L38",
"_origin": "ast",
"id": "app_sitemap_getcityscope",
"community": 51,
"community": 3,
"norm_label": "getcityscope()"
},
{
@@ -2400,7 +2400,7 @@
"source_location": "L49",
"_origin": "ast",
"id": "app_sitemap_getstaticpages",
"community": 51,
"community": 3,
"norm_label": "getstaticpages()"
},
{
@@ -2410,7 +2410,7 @@
"source_location": "L61",
"_origin": "ast",
"id": "app_sitemap_fetchallpages",
"community": 51,
"community": 3,
"norm_label": "fetchallpages()"
},
{
@@ -2420,7 +2420,7 @@
"source_location": "L90",
"_origin": "ast",
"id": "app_sitemap_cityfilterparams",
"community": 51,
"community": 3,
"norm_label": "cityfilterparams()"
},
{
@@ -2430,7 +2430,7 @@
"source_location": "L98",
"_origin": "ast",
"id": "app_sitemap_getdoctorurls",
"community": 51,
"community": 3,
"norm_label": "getdoctorurls()"
},
{
@@ -2440,7 +2440,7 @@
"source_location": "L114",
"_origin": "ast",
"id": "app_sitemap_getclinicurls",
"community": 51,
"community": 3,
"norm_label": "getclinicurls()"
},
{
@@ -2450,7 +2450,7 @@
"source_location": "L130",
"_origin": "ast",
"id": "app_sitemap_getblogurls",
"community": 51,
"community": 3,
"norm_label": "getblogurls()"
},
{
@@ -2460,7 +2460,7 @@
"source_location": "L146",
"_origin": "ast",
"id": "app_sitemap_sitemap",
"community": 51,
"community": 3,
"norm_label": "sitemap()"
},
{
@@ -2710,7 +2710,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "detail_content",
"community": 3,
"community": 51,
"norm_label": "content.js"
},
{
@@ -2720,7 +2720,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "detail_content_content",
"community": 3,
"community": 51,
"norm_label": "content()"
},
{
@@ -2730,7 +2730,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "detail_form",
"community": 3,
"community": 51,
"norm_label": "form.js"
},
{
@@ -2740,7 +2740,7 @@
"source_location": "L5",
"_origin": "ast",
"id": "detail_form_form",
"community": 3,
"community": 51,
"norm_label": "form()"
},
{
@@ -2750,7 +2750,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "detail_submitdata",
"community": 3,
"community": 51,
"norm_label": "submitdata.js"
},
{
@@ -2760,7 +2760,7 @@
"source_location": "L10",
"_origin": "ast",
"id": "detail_submitdata_submitdata",
"community": 3,
"community": 51,
"norm_label": "submitdata()"
},
{
@@ -2770,7 +2770,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "components_appointment_detail_index_js_detail_index",
"community": 3,
"community": 51,
"norm_label": "index.js"
},
{
@@ -2780,7 +2780,7 @@
"source_location": "L9",
"_origin": "ast",
"id": "components_appointment_detail_index_js_detail_index_detail",
"community": 3,
"community": 51,
"norm_label": "detail()"
},
{
@@ -2970,7 +2970,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "paying_buttonfixed",
"community": 3,
"community": 51,
"norm_label": "buttonfixed.js"
},
{
@@ -2980,7 +2980,7 @@
"source_location": "L3",
"_origin": "ast",
"id": "paying_buttonfixed_buttonfixed",
"community": 3,
"community": 51,
"norm_label": "buttonfixed()"
},
{
@@ -2990,7 +2990,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "paying_index",
"community": 3,
"community": 51,
"norm_label": "index.js"
},
{
@@ -3000,7 +3000,7 @@
"source_location": "L11",
"_origin": "ast",
"id": "paying_index_paying",
"community": 3,
"community": 6,
"norm_label": "paying()"
},
{
@@ -11070,7 +11070,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "context_provinceprovider",
"community": 3,
"community": 51,
"norm_label": "provinceprovider.js"
},
{
@@ -11080,7 +11080,7 @@
"source_location": "L7",
"_origin": "ast",
"id": "context_provinceprovider_provincecontext",
"community": 3,
"community": 51,
"norm_label": "provincecontext"
},
{
@@ -11090,7 +11090,7 @@
"source_location": "L9",
"_origin": "ast",
"id": "context_provinceprovider_provinceprovider",
"community": 3,
"community": 51,
"norm_label": "provinceprovider()"
},
{
@@ -11100,7 +11100,7 @@
"source_location": "L30",
"_origin": "ast",
"id": "context_provinceprovider_useprovince",
"community": 3,
"community": 51,
"norm_label": "useprovince()"
},
{
@@ -11110,7 +11110,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "context_provinceprovider_test",
"community": 3,
"community": 51,
"norm_label": "provinceprovider.test.jsx"
},
{
@@ -11120,7 +11120,7 @@
"source_location": "L5",
"_origin": "ast",
"id": "context_provinceprovider_test_probe",
"community": 3,
"community": 51,
"norm_label": "probe()"
},
{
@@ -11130,7 +11130,7 @@
"source_location": "L10",
"_origin": "ast",
"id": "context_provinceprovider_test_sethostname",
"community": 3,
"community": 51,
"norm_label": "sethostname()"
},
{
@@ -11760,7 +11760,7 @@
"source_location": "L31",
"_origin": "ast",
"id": "lib_getcanonicalurl_getrequestorigin",
"community": 72,
"community": 45,
"norm_label": "getrequestorigin()"
},
{
@@ -11793,11 +11793,31 @@
"community": 29,
"norm_label": "getstateinfo.js"
},
{
"label": "siteContextCache",
"file_type": "code",
"source_file": "lib/getStateInfo.js",
"source_location": "L17",
"_origin": "ast",
"id": "lib_getstateinfo_sitecontextcache",
"community": 29,
"norm_label": "sitecontextcache"
},
{
"label": "fetchSiteContext()",
"file_type": "code",
"source_file": "lib/getStateInfo.js",
"source_location": "L19",
"_origin": "ast",
"id": "lib_getstateinfo_fetchsitecontext",
"community": 29,
"norm_label": "fetchsitecontext()"
},
{
"label": "getStateInfo()",
"file_type": "code",
"source_file": "lib/getStateInfo.js",
"source_location": "L10",
"source_location": "L41",
"_origin": "ast",
"id": "lib_getstateinfo_getstateinfo",
"community": 29,
@@ -11860,7 +11880,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "lib_money",
"community": 3,
"community": 6,
"norm_label": "money.js"
},
{
@@ -11870,7 +11890,7 @@
"source_location": "L4",
"_origin": "ast",
"id": "lib_money_rialtotoman",
"community": 3,
"community": 6,
"norm_label": "rialtotoman()"
},
{
@@ -11880,7 +11900,7 @@
"source_location": "L5",
"_origin": "ast",
"id": "lib_money_tomantorial",
"community": 3,
"community": 6,
"norm_label": "tomantorial()"
},
{
@@ -11890,7 +11910,7 @@
"source_location": "L8",
"_origin": "ast",
"id": "lib_money_formattoman",
"community": 3,
"community": 6,
"norm_label": "formattoman()"
},
{
@@ -12030,7 +12050,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "lib_sanitize",
"community": 366,
"community": 6,
"norm_label": "sanitize.js"
},
{
@@ -12050,7 +12070,7 @@
"source_location": "L18",
"_origin": "ast",
"id": "lib_sanitize_safejsonld",
"community": 366,
"community": 45,
"norm_label": "safejsonld()"
},
{
@@ -12060,7 +12080,7 @@
"source_location": "L22",
"_origin": "ast",
"id": "lib_sanitize_safejsonparse",
"community": 366,
"community": 6,
"norm_label": "safejsonparse()"
},
{
@@ -12090,7 +12110,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "lib_tokenstore",
"community": 79,
"community": 6,
"norm_label": "tokenstore.js"
},
{
@@ -12100,7 +12120,7 @@
"source_location": "L3",
"_origin": "ast",
"id": "lib_tokenstore_getaccesstoken",
"community": 79,
"community": 6,
"norm_label": "getaccesstoken()"
},
{
@@ -12110,7 +12130,7 @@
"source_location": "L7",
"_origin": "ast",
"id": "lib_tokenstore_setaccesstoken",
"community": 79,
"community": 6,
"norm_label": "setaccesstoken()"
},
{
@@ -12120,7 +12140,7 @@
"source_location": "L11",
"_origin": "ast",
"id": "lib_tokenstore_clearaccesstoken",
"community": 79,
"community": 6,
"norm_label": "clearaccesstoken()"
},
{
@@ -12180,7 +12200,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "package",
"community": 8,
"community": 373,
"norm_label": "package.json"
},
{
@@ -12190,7 +12210,7 @@
"source_location": "L2",
"_origin": "ast",
"id": "package_name",
"community": 8,
"community": 373,
"norm_label": "name"
},
{
@@ -12200,7 +12220,7 @@
"source_location": "L3",
"_origin": "ast",
"id": "package_version",
"community": 8,
"community": 373,
"norm_label": "version"
},
{
@@ -12210,7 +12230,7 @@
"source_location": "L4",
"_origin": "ast",
"id": "package_private",
"community": 8,
"community": 373,
"norm_label": "private"
},
{
@@ -12220,7 +12240,7 @@
"source_location": "L5",
"_origin": "ast",
"id": "package_engines",
"community": 8,
"community": 373,
"norm_label": "engines"
},
{
@@ -12230,7 +12250,7 @@
"source_location": "L6",
"_origin": "ast",
"id": "package_engines_node",
"community": 8,
"community": 373,
"norm_label": "node"
},
{
@@ -12240,7 +12260,7 @@
"source_location": "L8",
"_origin": "ast",
"id": "package_scripts",
"community": 8,
"community": 366,
"norm_label": "scripts"
},
{
@@ -12250,7 +12270,7 @@
"source_location": "L9",
"_origin": "ast",
"id": "package_scripts_dev",
"community": 8,
"community": 366,
"norm_label": "dev"
},
{
@@ -12260,7 +12280,7 @@
"source_location": "L10",
"_origin": "ast",
"id": "package_scripts_build",
"community": 8,
"community": 366,
"norm_label": "build"
},
{
@@ -12270,7 +12290,7 @@
"source_location": "L11",
"_origin": "ast",
"id": "package_scripts_start",
"community": 8,
"community": 366,
"norm_label": "start"
},
{
@@ -12280,7 +12300,7 @@
"source_location": "L12",
"_origin": "ast",
"id": "package_scripts_lint",
"community": 8,
"community": 366,
"norm_label": "lint"
},
{
@@ -12290,7 +12310,7 @@
"source_location": "L13",
"_origin": "ast",
"id": "package_scripts_test",
"community": 8,
"community": 366,
"norm_label": "test"
},
{
@@ -12300,7 +12320,7 @@
"source_location": "L14",
"_origin": "ast",
"id": "package_scripts_test_watch",
"community": 8,
"community": 366,
"norm_label": "test:watch"
},
{
@@ -12310,7 +12330,7 @@
"source_location": "L15",
"_origin": "ast",
"id": "package_scripts_test_cov",
"community": 8,
"community": 366,
"norm_label": "test:cov"
},
{
@@ -12970,7 +12990,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "services_api",
"community": 79,
"community": 6,
"norm_label": "api.js"
},
{
@@ -12980,7 +13000,7 @@
"source_location": "L6",
"_origin": "ast",
"id": "services_api_extracterrormessage",
"community": 79,
"community": 6,
"norm_label": "extracterrormessage()"
},
{
@@ -12990,7 +13010,7 @@
"source_location": "L19",
"_origin": "ast",
"id": "services_api_api",
"community": 79,
"community": 6,
"norm_label": "api"
},
{
@@ -13000,7 +13020,7 @@
"source_location": "L28",
"_origin": "ast",
"id": "services_api_refreshaccesstoken",
"community": 79,
"community": 6,
"norm_label": "refreshaccesstoken()"
},
{
@@ -13010,7 +13030,7 @@
"source_location": "L45",
"_origin": "ast",
"id": "services_api_handlesessionexpired",
"community": 79,
"community": 6,
"norm_label": "handlesessionexpired()"
},
{
@@ -13040,7 +13060,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "services_response",
"community": 79,
"community": 6,
"norm_label": "response.js"
},
{
@@ -13050,7 +13070,7 @@
"source_location": "L4",
"_origin": "ast",
"id": "services_response_request",
"community": 79,
"community": 6,
"norm_label": "request"
},
{
@@ -13110,7 +13130,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "utils_index",
"community": 79,
"community": 6,
"norm_label": "index.js"
},
{
@@ -13120,7 +13140,7 @@
"source_location": "L4",
"_origin": "ast",
"id": "utils_index_removetoken",
"community": 79,
"community": 6,
"norm_label": "removetoken()"
},
{
@@ -13130,7 +13150,7 @@
"source_location": "L1",
"_origin": "ast",
"id": "utils_sitemap",
"community": 51,
"community": 3,
"norm_label": "sitemap.js"
},
{
@@ -13140,7 +13160,7 @@
"source_location": "L3",
"_origin": "ast",
"id": "utils_sitemap_getbaseurl",
"community": 51,
"community": 3,
"norm_label": "getbaseurl()"
},
{
@@ -17403,6 +17423,126 @@
"community": 48,
"norm_label": "\u0646\u06a9\u0627\u062a \u0645\u0647\u0645"
},
{
"label": "global-rep-domain-site.md",
"file_type": "document",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L1",
"_origin": "ast",
"id": "prompt_global_rep_domain_site",
"community": 79,
"norm_label": "global-rep-domain-site.md"
},
{
"label": "\u0633\u0627\u06cc\u062a \u062f\u0627\u0645\u0646\u0647 \u0627\u062e\u062a\u0635\u0627\u0635\u06cc \u0646\u0645\u0627\u06cc\u0646\u062f\u0647 \u0633\u0631\u0627\u0633\u0631\u06cc \u2014 \u062a\u0634\u062e\u06cc\u0635 \u062f\u0627\u0645\u0646\u0647\u060c \u0641\u06cc\u0644\u062a\u0631 \u067e\u0632\u0634\u06a9\u0627\u0646/\u06a9\u0644\u06cc\u0646\u06cc\u06a9\u200c\u0647\u0627",
"file_type": "document",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L1",
"_origin": "ast",
"id": "prompt_global_rep_domain_site_\u0633\u0627\u06cc\u062a_\u062f\u0627\u0645\u0646\u0647_\u0627\u062e\u062a\u0635\u0627\u0635\u06cc_\u0646\u0645\u0627\u06cc\u0646\u062f\u0647_\u0633\u0631\u0627\u0633\u0631\u06cc_\u062a\u0634\u062e\u06cc\u0635_\u062f\u0627\u0645\u0646\u0647_\u0641\u06cc\u0644\u062a\u0631_\u067e\u0632\u0634\u06a9\u0627\u0646_\u06a9\u0644\u06cc\u0646\u06cc\u06a9_\u0647\u0627",
"community": 79,
"norm_label": "\u0633\u0627\u06cc\u062a \u062f\u0627\u0645\u0646\u0647 \u0627\u062e\u062a\u0635\u0627\u0635\u06cc \u0646\u0645\u0627\u06cc\u0646\u062f\u0647 \u0633\u0631\u0627\u0633\u0631\u06cc \u2014 \u062a\u0634\u062e\u06cc\u0635 \u062f\u0627\u0645\u0646\u0647\u060c \u0641\u06cc\u0644\u062a\u0631 \u067e\u0632\u0634\u06a9\u0627\u0646/\u06a9\u0644\u06cc\u0646\u06cc\u06a9\u200c\u0647\u0627"
},
{
"label": "\u067e\u0631\u0648\u0698\u0647",
"file_type": "document",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L3",
"_origin": "ast",
"id": "prompt_global_rep_domain_site_\u067e\u0631\u0648\u0698\u0647",
"community": 79,
"norm_label": "\u067e\u0631\u0648\u0698\u0647"
},
{
"label": "\u0632\u0645\u06cc\u0646\u0647",
"file_type": "document",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L9",
"_origin": "ast",
"id": "prompt_global_rep_domain_site_\u0632\u0645\u06cc\u0646\u0647",
"community": 79,
"norm_label": "\u0632\u0645\u06cc\u0646\u0647"
},
{
"label": "\u0645\u0634\u06a9\u0644 / \u0647\u062f\u0641",
"file_type": "document",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L13",
"_origin": "ast",
"id": "prompt_global_rep_domain_site_\u0645\u0634\u06a9\u0644_\u0647\u062f\u0641",
"community": 79,
"norm_label": "\u0645\u0634\u06a9\u0644 / \u0647\u062f\u0641"
},
{
"label": "\u0641\u0627\u06cc\u0644\u200c\u0647\u0627\u06cc \u0645\u0631\u062a\u0628\u0637",
"file_type": "document",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L19",
"_origin": "ast",
"id": "prompt_global_rep_domain_site_\u0641\u0627\u06cc\u0644_\u0647\u0627\u06cc_\u0645\u0631\u062a\u0628\u0637",
"community": 79,
"norm_label": "\u0641\u0627\u06cc\u0644\u200c\u0647\u0627\u06cc \u0645\u0631\u062a\u0628\u0637"
},
{
"label": "\u0648\u0636\u0639\u06cc\u062a \u0641\u0639\u0644\u06cc",
"file_type": "document",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L30",
"_origin": "ast",
"id": "prompt_global_rep_domain_site_\u0648\u0636\u0639\u06cc\u062a_\u0641\u0639\u0644\u06cc",
"community": 79,
"norm_label": "\u0648\u0636\u0639\u06cc\u062a \u0641\u0639\u0644\u06cc"
},
{
"label": "\u0648\u0638\u0627\u06cc\u0641",
"file_type": "document",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L60",
"_origin": "ast",
"id": "prompt_global_rep_domain_site_\u0648\u0638\u0627\u06cc\u0641",
"community": 79,
"norm_label": "\u0648\u0638\u0627\u06cc\u0641"
},
{
"label": "\u06f1. \u062a\u0648\u0633\u0639\u0647 `getStateInfo` \u2014 repContext",
"file_type": "document",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L62",
"_origin": "ast",
"id": "prompt_global_rep_domain_site_\u06f1_\u062a\u0648\u0633\u0639\u0647_getstateinfo_repcontext",
"community": 79,
"norm_label": "\u06f1. \u062a\u0648\u0633\u0639\u0647 `getstateinfo` \u2014 repcontext"
},
{
"label": "\u06f2. \u067e\u0627\u0633 \u062f\u0627\u062f\u0646 `domain` \u062f\u0631 \u0644\u06cc\u0633\u062a\u200c\u0647\u0627",
"file_type": "document",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L81",
"_origin": "ast",
"id": "prompt_global_rep_domain_site_\u06f2_\u067e\u0627\u0633_\u062f\u0627\u062f\u0646_domain_\u062f\u0631_\u0644\u06cc\u0633\u062a_\u0647\u0627",
"community": 79,
"norm_label": "\u06f2. \u067e\u0627\u0633 \u062f\u0627\u062f\u0646 `domain` \u062f\u0631 \u0644\u06cc\u0633\u062a\u200c\u0647\u0627"
},
{
"label": "\u06f3. \u0645\u062a\u0627\u062f\u06cc\u062a\u0627 \u0648 \u0628\u0631\u0646\u062f\u06cc\u0646\u06af \u062f\u0627\u0645\u0646\u0647 \u0646\u0645\u0627\u06cc\u0646\u062f\u0647",
"file_type": "document",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L99",
"_origin": "ast",
"id": "prompt_global_rep_domain_site_\u06f3_\u0645\u062a\u0627\u062f\u06cc\u062a\u0627_\u0648_\u0628\u0631\u0646\u062f\u06cc\u0646\u06af_\u062f\u0627\u0645\u0646\u0647_\u0646\u0645\u0627\u06cc\u0646\u062f\u0647",
"community": 79,
"norm_label": "\u06f3. \u0645\u062a\u0627\u062f\u06cc\u062a\u0627 \u0648 \u0628\u0631\u0646\u062f\u06cc\u0646\u06af \u062f\u0627\u0645\u0646\u0647 \u0646\u0645\u0627\u06cc\u0646\u062f\u0647"
},
{
"label": "\u0646\u06a9\u0627\u062a \u0645\u0647\u0645",
"file_type": "document",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L108",
"_origin": "ast",
"id": "prompt_global_rep_domain_site_\u0646\u06a9\u0627\u062a_\u0645\u0647\u0645",
"community": 79,
"norm_label": "\u0646\u06a9\u0627\u062a \u0645\u0647\u0645"
},
{
"label": "liara-deploy.md",
"file_type": "document",
@@ -33176,6 +33316,16 @@
"source": "lib_getcanonicalurl_getcanonicalurl",
"target": "lib_getcanonicalurl_buildmaincanonicalurl"
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "lib/getStateInfo.js",
"source_location": "L19",
"weight": 1.0,
"source": "lib_getstateinfo",
"target": "lib_getstateinfo_fetchsitecontext",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
@@ -33186,6 +33336,38 @@
"source": "lib_getstateinfo",
"target": "lib_getstateinfo_getstateinfo"
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "lib/getStateInfo.js",
"source_location": "L17",
"weight": 1.0,
"source": "lib_getstateinfo",
"target": "lib_getstateinfo_sitecontextcache",
"confidence_score": 1.0
},
{
"relation": "calls",
"context": "call",
"confidence": "INFERRED",
"confidence_score": 0.8,
"source_file": "lib/getStateInfo.js",
"source_location": "L27",
"weight": 1.0,
"source": "lib_getstateinfo_fetchsitecontext",
"target": "lib_req_fetchreq"
},
{
"relation": "calls",
"context": "call",
"confidence": "EXTRACTED",
"source_file": "lib/getStateInfo.js",
"source_location": "L59",
"weight": 1.0,
"source": "lib_getstateinfo_getstateinfo",
"target": "lib_getstateinfo_fetchsitecontext",
"confidence_score": 1.0
},
{
"relation": "calls",
"context": "call",
@@ -38324,6 +38506,116 @@
"source": "prompt_fix_token_storage_xss_headers_\u0648\u0638\u0627\u06cc\u0641",
"target": "prompt_fix_token_storage_xss_headers_\u06f4_security_headers_\u062f\u0631_next_config_js_h_2"
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L1",
"weight": 1.0,
"source": "prompt_global_rep_domain_site",
"target": "prompt_global_rep_domain_site_\u0633\u0627\u06cc\u062a_\u062f\u0627\u0645\u0646\u0647_\u0627\u062e\u062a\u0635\u0627\u0635\u06cc_\u0646\u0645\u0627\u06cc\u0646\u062f\u0647_\u0633\u0631\u0627\u0633\u0631\u06cc_\u062a\u0634\u062e\u06cc\u0635_\u062f\u0627\u0645\u0646\u0647_\u0641\u06cc\u0644\u062a\u0631_\u067e\u0632\u0634\u06a9\u0627\u0646_\u06a9\u0644\u06cc\u0646\u06cc\u06a9_\u0647\u0627",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L9",
"weight": 1.0,
"source": "prompt_global_rep_domain_site_\u0633\u0627\u06cc\u062a_\u062f\u0627\u0645\u0646\u0647_\u0627\u062e\u062a\u0635\u0627\u0635\u06cc_\u0646\u0645\u0627\u06cc\u0646\u062f\u0647_\u0633\u0631\u0627\u0633\u0631\u06cc_\u062a\u0634\u062e\u06cc\u0635_\u062f\u0627\u0645\u0646\u0647_\u0641\u06cc\u0644\u062a\u0631_\u067e\u0632\u0634\u06a9\u0627\u0646_\u06a9\u0644\u06cc\u0646\u06cc\u06a9_\u0647\u0627",
"target": "prompt_global_rep_domain_site_\u0632\u0645\u06cc\u0646\u0647",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L19",
"weight": 1.0,
"source": "prompt_global_rep_domain_site_\u0633\u0627\u06cc\u062a_\u062f\u0627\u0645\u0646\u0647_\u0627\u062e\u062a\u0635\u0627\u0635\u06cc_\u0646\u0645\u0627\u06cc\u0646\u062f\u0647_\u0633\u0631\u0627\u0633\u0631\u06cc_\u062a\u0634\u062e\u06cc\u0635_\u062f\u0627\u0645\u0646\u0647_\u0641\u06cc\u0644\u062a\u0631_\u067e\u0632\u0634\u06a9\u0627\u0646_\u06a9\u0644\u06cc\u0646\u06cc\u06a9_\u0647\u0627",
"target": "prompt_global_rep_domain_site_\u0641\u0627\u06cc\u0644_\u0647\u0627\u06cc_\u0645\u0631\u062a\u0628\u0637",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L13",
"weight": 1.0,
"source": "prompt_global_rep_domain_site_\u0633\u0627\u06cc\u062a_\u062f\u0627\u0645\u0646\u0647_\u0627\u062e\u062a\u0635\u0627\u0635\u06cc_\u0646\u0645\u0627\u06cc\u0646\u062f\u0647_\u0633\u0631\u0627\u0633\u0631\u06cc_\u062a\u0634\u062e\u06cc\u0635_\u062f\u0627\u0645\u0646\u0647_\u0641\u06cc\u0644\u062a\u0631_\u067e\u0632\u0634\u06a9\u0627\u0646_\u06a9\u0644\u06cc\u0646\u06cc\u06a9_\u0647\u0627",
"target": "prompt_global_rep_domain_site_\u0645\u0634\u06a9\u0644_\u0647\u062f\u0641",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L108",
"weight": 1.0,
"source": "prompt_global_rep_domain_site_\u0633\u0627\u06cc\u062a_\u062f\u0627\u0645\u0646\u0647_\u0627\u062e\u062a\u0635\u0627\u0635\u06cc_\u0646\u0645\u0627\u06cc\u0646\u062f\u0647_\u0633\u0631\u0627\u0633\u0631\u06cc_\u062a\u0634\u062e\u06cc\u0635_\u062f\u0627\u0645\u0646\u0647_\u0641\u06cc\u0644\u062a\u0631_\u067e\u0632\u0634\u06a9\u0627\u0646_\u06a9\u0644\u06cc\u0646\u06cc\u06a9_\u0647\u0627",
"target": "prompt_global_rep_domain_site_\u0646\u06a9\u0627\u062a_\u0645\u0647\u0645",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L30",
"weight": 1.0,
"source": "prompt_global_rep_domain_site_\u0633\u0627\u06cc\u062a_\u062f\u0627\u0645\u0646\u0647_\u0627\u062e\u062a\u0635\u0627\u0635\u06cc_\u0646\u0645\u0627\u06cc\u0646\u062f\u0647_\u0633\u0631\u0627\u0633\u0631\u06cc_\u062a\u0634\u062e\u06cc\u0635_\u062f\u0627\u0645\u0646\u0647_\u0641\u06cc\u0644\u062a\u0631_\u067e\u0632\u0634\u06a9\u0627\u0646_\u06a9\u0644\u06cc\u0646\u06cc\u06a9_\u0647\u0627",
"target": "prompt_global_rep_domain_site_\u0648\u0636\u0639\u06cc\u062a_\u0641\u0639\u0644\u06cc",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L60",
"weight": 1.0,
"source": "prompt_global_rep_domain_site_\u0633\u0627\u06cc\u062a_\u062f\u0627\u0645\u0646\u0647_\u0627\u062e\u062a\u0635\u0627\u0635\u06cc_\u0646\u0645\u0627\u06cc\u0646\u062f\u0647_\u0633\u0631\u0627\u0633\u0631\u06cc_\u062a\u0634\u062e\u06cc\u0635_\u062f\u0627\u0645\u0646\u0647_\u0641\u06cc\u0644\u062a\u0631_\u067e\u0632\u0634\u06a9\u0627\u0646_\u06a9\u0644\u06cc\u0646\u06cc\u06a9_\u0647\u0627",
"target": "prompt_global_rep_domain_site_\u0648\u0638\u0627\u06cc\u0641",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L3",
"weight": 1.0,
"source": "prompt_global_rep_domain_site_\u0633\u0627\u06cc\u062a_\u062f\u0627\u0645\u0646\u0647_\u0627\u062e\u062a\u0635\u0627\u0635\u06cc_\u0646\u0645\u0627\u06cc\u0646\u062f\u0647_\u0633\u0631\u0627\u0633\u0631\u06cc_\u062a\u0634\u062e\u06cc\u0635_\u062f\u0627\u0645\u0646\u0647_\u0641\u06cc\u0644\u062a\u0631_\u067e\u0632\u0634\u06a9\u0627\u0646_\u06a9\u0644\u06cc\u0646\u06cc\u06a9_\u0647\u0627",
"target": "prompt_global_rep_domain_site_\u067e\u0631\u0648\u0698\u0647",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L62",
"weight": 1.0,
"source": "prompt_global_rep_domain_site_\u0648\u0638\u0627\u06cc\u0641",
"target": "prompt_global_rep_domain_site_\u06f1_\u062a\u0648\u0633\u0639\u0647_getstateinfo_repcontext",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L81",
"weight": 1.0,
"source": "prompt_global_rep_domain_site_\u0648\u0638\u0627\u06cc\u0641",
"target": "prompt_global_rep_domain_site_\u06f2_\u067e\u0627\u0633_\u062f\u0627\u062f\u0646_domain_\u062f\u0631_\u0644\u06cc\u0633\u062a_\u0647\u0627",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": ".claude/prompt/global-rep-domain-site.md",
"source_location": "L99",
"weight": 1.0,
"source": "prompt_global_rep_domain_site_\u0648\u0638\u0627\u06cc\u0641",
"target": "prompt_global_rep_domain_site_\u06f3_\u0645\u062a\u0627\u062f\u06cc\u062a\u0627_\u0648_\u0628\u0631\u0646\u062f\u06cc\u0646\u06af_\u062f\u0627\u0645\u0646\u0647_\u0646\u0645\u0627\u06cc\u0646\u062f\u0647",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
@@ -41136,5 +41428,5 @@
}
],
"hyperedges": [],
"built_at_commit": "ee47b535d878f1744ea163606a0d759167dd0a36"
"built_at_commit": "a56b7e8de50645802f13f7723934a78f8bb01fd3"
}
+13 -8
View File
@@ -70,8 +70,8 @@
"semantic_hash": ""
},
"app/clinics/page.js": {
"mtime": 1783253595.1569397,
"ast_hash": "08304d49bbdda4c447abe043f3d92be7",
"mtime": 1783569646.0080516,
"ast_hash": "57a8ff41a6d9eb082c62179a15e5dc82",
"semantic_hash": ""
},
"app/component/AnimationTextHead.js": {
@@ -430,8 +430,8 @@
"semantic_hash": ""
},
"app/doctors/page.js": {
"mtime": 1783253595.1556766,
"ast_hash": "c2cbe5caa8996f5a7b4e764a32271350",
"mtime": 1783569643.148586,
"ast_hash": "d0beacb3f322c3a94f04b23c41542ce3",
"semantic_hash": ""
},
"app/error.js": {
@@ -440,8 +440,8 @@
"semantic_hash": ""
},
"app/layout.js": {
"mtime": 1783253481.1810312,
"ast_hash": "7f0b2edd763fa7df467a638e8a68682d",
"mtime": 1783569669.0787313,
"ast_hash": "2b4b26060d096c6810fadbe8e9d562f1",
"semantic_hash": ""
},
"app/login-verify/page.js": {
@@ -2610,8 +2610,8 @@
"semantic_hash": ""
},
"lib/getStateInfo.js": {
"mtime": 1783016454.4175303,
"ast_hash": "71610167fccfe0d1cec30a7b4b36ce44",
"mtime": 1783569569.9403472,
"ast_hash": "cc7a38cc74db3022578f3fdde5b30ff2",
"semantic_hash": ""
},
"lib/getStateInfo.test.js": {
@@ -3413,5 +3413,10 @@
"mtime": 1783498761.5902257,
"ast_hash": "e2a4e8b2fe6f75af3c709c23075e9647",
"semantic_hash": ""
},
".claude/prompt/global-rep-domain-site.md": {
"mtime": 1783568045.290634,
"ast_hash": "468c80e35c433c7a03e87bb8481e37d7",
"semantic_hash": ""
}
}
+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,
};
}