feat(login): add redirect parameter to login links for better user experience
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# بازگشت به صفحهٔ مبدأ بعد از لاگین (redirect-back)
|
||||
|
||||
## پروژه
|
||||
|
||||
`nobat724_front` (سایت عمومی)
|
||||
|
||||
## زمینه
|
||||
|
||||
مکانیزم بازگشت بعد از لاگین **از قبل وجود دارد**: مرحلهٔ تأیید کد (`SendReq.js`) بعد از ورود موفق، پارامتر `?redirect=` را از URL میخواند و کاربر را به همان مسیر برمیگرداند (فقط مسیر داخلی امن). اما **لینکها/ناوبریهای لاگین در سایت این پارامتر را نمیسازند** — بهجز یک مورد (`components/doctor/claim/index.js`). نتیجه: کاربر از هر صفحهای (مثلاً صفحهٔ پزشک برای گرفتن نوبت) روی «ورود | ثبت نام» بزند، بعد از لاگین به `/` (خانه) میرود، نه صفحهٔ مبدأ.
|
||||
|
||||
## مشکل / هدف
|
||||
|
||||
هر جای سایت که به `/login` میرویم، باید مسیر فعلی را بهصورت `?redirect=<current-path>` به لینک لاگین اضافه کنیم تا بعد از ورود موفق کاربر به همان صفحه برگردد. الگوی درست از قبل در پروژه هست و فقط باید به بقیهٔ نقاط تعمیم داده شود.
|
||||
|
||||
## فایلهای مرتبط
|
||||
|
||||
| فایل | نقش |
|
||||
|------|-----|
|
||||
| `components/register/verificationPage/SendReq.js` | (بدون تغییر) منطق بازگشت با `?redirect=` — L89–95 |
|
||||
| `components/doctor/claim/index.js` | (مرجع الگوی درست) L191 — `/login?redirect=${encodeURIComponent(pathname)}` |
|
||||
| `components/layout/header/Content.js` | لینک اصلی «ورود \| ثبت نام» هدر — L43 |
|
||||
| `app/payment/[uuid]/page.js` | `router.push("/login")` هنگام نبود auth — L35 |
|
||||
| `app/dashboard/page.js` | `redirect("/login")` سمت سرور برای صفحهٔ محافظتشده — L29 |
|
||||
| `app/component/ModalLogout.js` | `router.replace("/login")` بعد از خروج — L19 (به «نکات» رجوع کن) |
|
||||
|
||||
## وضعیت فعلی
|
||||
|
||||
منطق بازگشت که **باید حفظ شود** (`SendReq.js` L89–95):
|
||||
|
||||
```jsx
|
||||
// بازگشت به صفحهٔ مبدأ اگر ?redirect= داده شده (فقط مسیر داخلی امن)
|
||||
const redirect = new URLSearchParams(window.location.search).get("redirect");
|
||||
window.location.href =
|
||||
redirect && redirect.startsWith("/") && !redirect.startsWith("//")
|
||||
? redirect
|
||||
: "/";
|
||||
```
|
||||
|
||||
الگوی درستِ موجود (`components/doctor/claim/index.js` L191):
|
||||
|
||||
```jsx
|
||||
<Link href={`/login?redirect=${encodeURIComponent(pathname)}`} className="w-full">
|
||||
```
|
||||
|
||||
لینک هدر که پارامتر ندارد (`components/layout/header/Content.js` L40–47) — این کامپوننت `"use client"` است و همین حالا `const pathname = usePathname();` را دارد (L17):
|
||||
|
||||
```jsx
|
||||
{isLogged ? (
|
||||
<ProfileUser />
|
||||
) : (
|
||||
<Link href="/login">
|
||||
<Button variant="outlined" color="primary">
|
||||
ورود | ثبت نام
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
```
|
||||
|
||||
## وظایف
|
||||
|
||||
### ۱. لینک لاگین هدر (اصلیترین)
|
||||
|
||||
در `components/layout/header/Content.js` L43، `pathname` که همینجا موجود است را به لینک اضافه کن. از رفتن به `/login?redirect=/login` جلوگیری کن:
|
||||
|
||||
```jsx
|
||||
<Link
|
||||
href={
|
||||
pathname && pathname !== "/login"
|
||||
? `/login?redirect=${encodeURIComponent(pathname)}`
|
||||
: "/login"
|
||||
}
|
||||
>
|
||||
<Button variant="outlined" color="primary">
|
||||
ورود | ثبت نام
|
||||
</Button>
|
||||
</Link>
|
||||
```
|
||||
|
||||
### ۲. ناوبری لاگین صفحهٔ پرداخت
|
||||
|
||||
در `app/payment/[uuid]/page.js` L35 (`router.push("/login")`)، مسیر فعلی را ضمیمه کن. این کامپوننت client است؛ اگر `usePathname` وارد نشده، آن را از `next/navigation` وارد کن و استفاده کن:
|
||||
|
||||
```jsx
|
||||
import { usePathname } from "next/navigation";
|
||||
// ...
|
||||
const pathname = usePathname();
|
||||
// ...
|
||||
router.push(`/login?redirect=${encodeURIComponent(pathname)}`);
|
||||
```
|
||||
|
||||
### ۳. redirect سمت سرور داشبورد
|
||||
|
||||
در `app/dashboard/page.js` L29، صفحهٔ محافظتشده هنگام نبود auth به لاگین میرود؛ مقصد بازگشت را ثابت `/dashboard` بگذار:
|
||||
|
||||
```jsx
|
||||
return redirect("/login?redirect=/dashboard");
|
||||
```
|
||||
|
||||
(چون این redirect سمت سرور است و به مسیر ثابت داشبورد مربوط است، نیازی به `usePathname` نیست.)
|
||||
|
||||
## نکات مهم
|
||||
|
||||
- **پارامتر در طول جریان لاگین حفظ میشود:** صفحهٔ `/login` → `ContentLogin` → `RegisterPage` مرحلهٔ ارسال کد و تأیید را با state داخلی (`isSendMsg`) عوض میکند و **ناوبری جدید انجام نمیدهد**، پس URL روی `/login?redirect=...` میماند و `SendReq.js` با `window.location.search` آن را میخواند. نیازی به پاسدادن prop اضافه نیست.
|
||||
- **امنیت redirect از قبل هندل شده:** فقط مسیرِ داخلی که با `/` شروع شود و با `//` شروع نشود پذیرفته میشود (`SendReq.js` L92). مقدار را همیشه با `encodeURIComponent(pathname)` بساز.
|
||||
- **`app/login/page.js`**: کاربرِ از قبل لاگینشده به `/` هدایت میشود (`ability.can("access","Login")`). این رفتار درست است و نباید تغییر کند؛ فقط برای کاربرِ مهمان `?redirect=` معنا دارد.
|
||||
- **`ModalLogout.js` (L19) را تغییر نده:** بعد از *خروج* بردن کاربر به `/login` با `?redirect=` به صفحهٔ محافظتشده باعث حلقه/بازگشت ناخواسته میشود. لاگاوت باید به `/login` سادهٔ فعلی برود.
|
||||
- **صفحهٔ appointment inline login دارد:** جریان گرفتن نوبت با `setStep(3)` بعد از لاگین در همان صفحه میماند (`SendReq.js` L86–87) و ناوبری نمیکند؛ نیازی به `?redirect=` ندارد و نباید دست بخورد.
|
||||
- `usePathname` فقط در Client Component کار میکند؛ برای redirectهای سمت سرور (وظیفهٔ ۳) از مسیر ثابت استفاده کن.
|
||||
- بعد از تغییر: `npm run build` بدون خطا.
|
||||
Reference in New Issue
Block a user