diff --git a/.claude/prompt/fix-header-logged-state.md b/.claude/prompt/fix-header-logged-state.md new file mode 100644 index 0000000..68b8e70 --- /dev/null +++ b/.claude/prompt/fix-header-logged-state.md @@ -0,0 +1,113 @@ +# رفع نمایش «ورود | ثبت‌نام» در هدر با وجود لاگین‌بودن کاربر + +## پروژه + +`nobat724_front` — سایت عمومی. این کار **صرفاً frontend** است؛ backend تغییر نمی‌کند. + +## زمینه + +هدر سایت عمومی بسته به وضعیت لاگین، یا دکمه‌ی «ورود | ثبت نام» را نشان می‌دهد یا آیکن پروفایل کاربر (`ProfileUser`). تصمیم بر اساس prop `logged` گرفته می‌شود که در `components/layout/StLayout.js` (Server Component) از روی کوکی `access_token` با `cookies()` خوانده و به هدر پاس داده می‌شود. + +مشکل: کاربر بعد از لاگین موفق (کوکی‌های `access_token`/`refresh_token`/`uuid`/`userInfo` با `js-cookie` ست می‌شوند)، **هنوز در هدر «ورود | ثبت نام» می‌بیند**. علت: `logged` فقط یک‌بار سمت سرور هنگام render محاسبه می‌شود و به ناوبری‌های client-side (soft navigation) و کوکی‌هایی که سمت کلاینت ست شده‌اند واکنش نشان نمی‌دهد؛ هدر تا یک hard-reload کامل وضعیت تازه را منعکس نمی‌کند و در بسیاری از مسیرها همان حالت اولیه‌ی «لاگین‌نشده» باقی می‌ماند. + +## مشکل / هدف + +هدر باید وضعیت لاگین را **به‌صورت زنده سمت کلاینت** هم تشخیص دهد: اگر کوکی `access_token` وجود دارد → پروفایل، وگرنه → دکمه‌ی ورود. prop سرور (`logged`) به‌عنوان مقدار اولیه‌ی paint اول حفظ شود تا flash/hydration mismatch ندهد، ولی منبع نهایی تصمیم، کوکی سمت کلاینت باشد. + +## فایل‌های مرتبط + +| فایل | نقش | +|------|-----| +| `components/layout/header/Content.js` | `"use client"`؛ بر اساس `logged` بین `ProfileUser` و دکمه‌ی ورود سوییچ می‌کند — محل اصلی رفع | +| `components/layout/StLayout.js` | Server Component؛ `logged={token}` را از `cookies().get("access_token")` می‌دهد (مقدار اولیه) | +| `components/layout/index.js` | layout کلاینتیِ پنل/داشبورد؛ `logged` را به‌صورت prop می‌گیرد | +| `components/layout/header/index.js` | فقط prop را پاس می‌دهد | +| `components/layout/header/profile/index.js` | `ProfileUser` — حالت لاگین‌شده | +| `utils/index.js` | `removeToken()` — خروج | + +## وضعیت فعلی (کد واقعی) + +### `header/Content.js` — تصمیم فقط بر اساس prop سرور + +```jsx +"use client"; +// ... +function Content({ matchedCity, name, logged }) { + // ... + {logged ? ( + + ) : ( + + + + )} +} +``` + +### `StLayout.js` — مقدار اولیه از کوکی سرور + +```jsx +const cookieStore = await cookies(); +const token = cookieStore.get("access_token"); +// ... +
+``` + +> `token` یک شیء `{ name, value }` یا `undefined` است؛ پس `logged` truthy/undefined می‌شود. این فقط در render سرور محاسبه می‌شود. + +### کوکی‌ها سمت کلاینت ست می‌شوند (`SendReq.js`) + +```js +Cookies.set("access_token", response.access_token, cookieOptions); // js-cookie, غیر httpOnly +``` + +## وظایف + +### ۱. تشخیص زنده‌ی لاگین در `header/Content.js` + +- یک state کلاینتی برای لاگین‌بودن اضافه کن که مقدار اولیه‌اش از prop `logged` (سرور) می‌آید تا paint اول درست باشد، سپس در `useEffect` با کوکی `access_token` همگام شود: + +```jsx +"use client"; +import { useEffect, useState } from "react"; +import Cookies from "js-cookie"; +// ... +function Content({ matchedCity, name, logged }) { + const [isLogged, setIsLogged] = useState(Boolean(logged)); + + useEffect(() => { + setIsLogged(Boolean(Cookies.get("access_token"))); + }, []); + // ... + {isLogged ? : ...دکمه ورود...} +} +``` + +- `Boolean(logged)` مهم است: `logged` ممکن است شیء `{name,value}` یا `undefined` باشد؛ تبدیل به boolean کن. +- چون مقدار اولیه از سرور می‌آید، hydration mismatch رخ نمی‌دهد؛ بعد از mount با کوکی واقعی کلاینت تصحیح می‌شود. + +### ۲. واکنش به تغییر مسیر (اختیاری ولی توصیه‌شده) + +- اگر لازم بود وضعیت بعد از login/logout بدون hard-reload به‌روز شود، می‌توانی `usePathname()` را به deps افزودن useEffect اضافه کنی تا با هر تغییر مسیر کوکی دوباره چک شود: + +```jsx +import { usePathname } from "next/navigation"; +const pathname = usePathname(); +useEffect(() => { + setIsLogged(Boolean(Cookies.get("access_token"))); +}, [pathname]); +``` + +> اگر فلوی فعلی login با `window.location.href` ریدایرکت می‌کند (hard reload)، paint اول سرور هم درست خواهد بود؛ این مرحله بیشتر برای ناوبری‌های soft و logout است. + +### ۳. سازگاری logout + +- `ProfileUser`/`DetailProfile` هنگام logout `removeToken()` را صدا می‌زنند که کوکی‌ها را پاک می‌کند. مطمئن شو بعد از logout، هدر دوباره دکمه‌ی ورود را نشان می‌دهد (با همان مکانیزم کوکی‌خوانی + ناوبری/ریلود فعلی). اگر logout فقط state را عوض می‌کند و ریدایرکت ندارد، بررسی کن هدر به‌روز شود. + +## نکات مهم + +- **هیچ تغییری در backend لازم نیست.** کوکی `access_token` غیر httpOnly است و سمت کلاینت با `js-cookie` قابل‌خواندن است. +- **Hydration:** مقدار اولیه‌ی state حتماً از prop `logged` سرور باشد، نه مستقیماً از `Cookies.get` در زمان render؛ خواندن کوکی فقط داخل `useEffect` (بعد از mount) تا SSR و کلاینت در paint اول یکی باشند. +- **multi-domain / RTL** را خراب نکن؛ منطق `matchedCity`/`name` دست‌نخورده بماند. +- این هدر هم در `StLayout` (صفحات عمومی، logged از سرور) و هم در `components/layout/index.js` (پنل/داشبورد، logged از prop) استفاده می‌شود؛ هر دو مسیر باید بعد از تغییر کار کنند (در هر دو، fallback به prop + همگام‌سازی با کوکی درست عمل می‌کند). +- **تست:** `npm run build`؛ سپس دستی: قبل از لاگین → دکمه‌ی «ورود | ثبت نام»؛ بعد از لاگین با `12345` و بازگشت به صفحه → آیکن پروفایل؛ بعد از logout → دوباره دکمه‌ی ورود. سپس commit با پیام توصیفی. diff --git a/components/layout/header/Content.js b/components/layout/header/Content.js index 85fae32..26e1342 100644 --- a/components/layout/header/Content.js +++ b/components/layout/header/Content.js @@ -7,11 +7,19 @@ import Row from "./list/Row"; import Col from "./list/col"; import ButtonMenu from "./list/col/ButtonMenu"; import BgGray from "./list/col/BgGray"; -import { useState } from "react"; +import { useEffect, useState } from "react"; +import { usePathname } from "next/navigation"; +import Cookies from "js-cookie"; import ProfileUser from "./profile"; function Content({ matchedCity, name, logged }) { const [isActive, setIsActive] = useState(false); + const [isLogged, setIsLogged] = useState(Boolean(logged)); + const pathname = usePathname(); + + useEffect(() => { + setIsLogged(Boolean(Cookies.get("access_token"))); + }, [pathname]); return (
- {logged ? ( + {isLogged ? ( ) : (