Files
nobat724_front/.claude/prompt/fix-header-logged-state.md
hamedandClaude Opus 4.8 83b2ec21b7 fix(header): detect login state live from client cookie
The header decided between profile and login button only from the
server-passed logged prop (read once via cookies() in StLayout), so a
user who logged in client-side still saw ورود | ثبت نام until a hard
reload. Seed isLogged from the server prop (correct first paint, no
hydration mismatch) then sync it from the access_token cookie on mount
and on every route change, so login/logout reflect immediately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:41:58 +03:30

114 lines
7.1 KiB
Markdown

# رفع نمایش «ورود | ثبت‌نام» در هدر با وجود لاگین‌بودن کاربر
## پروژه
`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 ? (
<ProfileUser />
) : (
<Link href="/login">
<Button variant="outlined" color="primary">ورود | ثبت نام</Button>
</Link>
)}
}
```
### `StLayout.js` — مقدار اولیه از کوکی سرور
```jsx
const cookieStore = await cookies();
const token = cookieStore.get("access_token");
// ...
<Header matchedCity={matchedCity} name={name} logged={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 ? <ProfileUser /> : <Link href="/login">...دکمه ورود...</Link>}
}
```
- `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 با پیام توصیفی.