Browse Source

feat: implement question list page with local data synchronization and profile completion validation

Dev
parent
commit
f5df1cc817
  1. 9
      src/app/questions-list/page.tsx
  2. 409
      src/components/Componentes/token-switcher.tsx

9
src/app/questions-list/page.tsx

@ -4,7 +4,10 @@ import Image from "next/image";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { IoClose } from "react-icons/io5"; import { IoClose } from "react-icons/io5";
import { getSubmitPath } from "@/lib/get-submit-path";
import {
getSubmitPath,
hasCompletedMarriageProfileBasics,
} from "@/lib/get-submit-path";
import QuestionCard from "@/components/Componentes/question-card"; import QuestionCard from "@/components/Componentes/question-card";
import RequiredStepsCard from "@/components/Componentes/required-steps-card"; import RequiredStepsCard from "@/components/Componentes/required-steps-card";
import Button from "@/components/Componentes/button"; import Button from "@/components/Componentes/button";
@ -53,7 +56,9 @@ export default function QuestionsListPage() {
[profile], [profile],
); );
const isProfileRedirecting = const isProfileRedirecting =
profileTargetPath !== null && profileTargetPath !== "/questions-list";
profileTargetPath !== null &&
profileTargetPath !== "/questions-list" &&
(!hasCompletedMarriageProfileBasics(profile) || profile?.can_edit_profile === false);
useEffect(() => { useEffect(() => {
if (isProfileRedirecting && profileTargetPath) { if (isProfileRedirecting && profileTargetPath) {

409
src/components/Componentes/token-switcher.tsx

@ -2,12 +2,21 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { MdOutlineSwitchAccount } from "react-icons/md"; import { MdOutlineSwitchAccount } from "react-icons/md";
import {
IoClose,
IoTrashOutline,
IoKeyOutline,
} from "react-icons/io5";
import { getClientCookie, setClientCookie } from "@/lib/cookies"; import { getClientCookie, setClientCookie } from "@/lib/cookies";
import { setCachedMarriageEntryPath } from "@/lib/entry-route-cache"; import { setCachedMarriageEntryPath } from "@/lib/entry-route-cache";
export const FEMALE_TOKEN = "545f61bb3e061ccb9b19f84715eb1b1ed4740331"; export const FEMALE_TOKEN = "545f61bb3e061ccb9b19f84715eb1b1ed4740331";
export const MALE_TOKEN = "f3a7543b44ef0a713d1ee0d4f7866b3825cf1308"; export const MALE_TOKEN = "f3a7543b44ef0a713d1ee0d4f7866b3825cf1308";
export const MALE_2_TOKEN = "56cd0fceb93f7cecf7ffa84c590135026cf54a1d"; export const MALE_2_TOKEN = "56cd0fceb93f7cecf7ffa84c590135026cf54a1d";
export const GUEST_MALE_1_TOKEN = "7490550734175c3935c54c808eba4933ee9c7633";
export const GUEST_MALE_2_TOKEN = "88813b0434f2207582f515f2c38de0cafd517fb7";
export const GUEST_FEMALE_1_TOKEN = "d9f0d61b2a7bbea89266eded6ad8d81a112f7ffe";
export const GUEST_FEMALE_2_TOKEN = "da44906159958b3f071096160b509e1479ccbc1a";
export const FEMALE_EMAIL = "m.a.ghorbani01@gmail.com"; export const FEMALE_EMAIL = "m.a.ghorbani01@gmail.com";
export const MALE_EMAIL = "muhammadamin.ghorbani@gmail.com"; export const MALE_EMAIL = "muhammadamin.ghorbani@gmail.com";
@ -27,7 +36,6 @@ export function TokenSwitcher({
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [currentToken, setCurrentToken] = useState<string>(""); const [currentToken, setCurrentToken] = useState<string>("");
const [customTokenInput, setCustomTokenInput] = useState<string>(""); const [customTokenInput, setCustomTokenInput] = useState<string>("");
const [isCustomInputOpen, setIsCustomInputOpen] = useState(false);
useEffect(() => { useEffect(() => {
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
@ -45,14 +53,21 @@ export function TokenSwitcher({
const isMale2 = currentToken === MALE_2_TOKEN; const isMale2 = currentToken === MALE_2_TOKEN;
const isFemale = currentToken === FEMALE_TOKEN; const isFemale = currentToken === FEMALE_TOKEN;
const isNoToken = currentToken === "NO_TOKEN" || !currentToken; const isNoToken = currentToken === "NO_TOKEN" || !currentToken;
const isCustomToken = !isMale && !isMale2 && !isFemale && !isNoToken;
const guestTokens = [
GUEST_MALE_1_TOKEN,
GUEST_MALE_2_TOKEN,
GUEST_FEMALE_1_TOKEN,
GUEST_FEMALE_2_TOKEN,
];
const isGuestToken = guestTokens.includes(currentToken);
const isCustomToken =
!isMale && !isMale2 && !isFemale && !isNoToken && !isGuestToken;
const handleSelectToken = (newToken: string) => { const handleSelectToken = (newToken: string) => {
const trimmed = newToken.trim(); const trimmed = newToken.trim();
if (!trimmed) return; if (!trimmed) return;
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
// Clear local storage and session storage so answers and cached state from previous user are removed
try { try {
window.localStorage.clear(); window.localStorage.clear();
window.sessionStorage.clear(); window.sessionStorage.clear();
@ -72,7 +87,6 @@ export function TokenSwitcher({
setCurrentToken(trimmed); setCurrentToken(trimmed);
setIsOpen(false); setIsOpen(false);
// Navigate to the beginning of the flow (intro page)
window.location.replace("/"); window.location.replace("/");
} }
}; };
@ -88,7 +102,6 @@ export function TokenSwitcher({
} }
try { try {
// 1. Clear local storage and session storage completely
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
try { try {
window.localStorage.clear(); window.localStorage.clear();
@ -98,7 +111,6 @@ export function TokenSwitcher({
} }
} }
// 2. Call backend reset script via API route if it is a real DB user
if (userId !== null) { if (userId !== null) {
const response = await fetch("/api/dev-reset-profile", { const response = await fetch("/api/dev-reset-profile", {
method: "POST", method: "POST",
@ -115,7 +127,6 @@ export function TokenSwitcher({
} }
} }
// 3. Set the target token in the cookies and redirect to /
const targetToken = const targetToken =
userId === 17119 userId === 17119
? MALE_TOKEN ? MALE_TOKEN
@ -146,6 +157,64 @@ export function TokenSwitcher({
} }
}; };
const mainAccounts = [
{
id: 17119,
token: MALE_TOKEN,
title: "حساب آقا",
subtitle: MALE_EMAIL,
emoji: "👨",
isActive: isMale,
},
{
id: 146024,
token: MALE_2_TOKEN,
title: "حساب آقا ۲",
subtitle: MALE_2_EMAIL,
emoji: "👨",
isActive: isMale2,
},
{
id: 147714,
token: FEMALE_TOKEN,
title: "حساب خانم",
subtitle: FEMALE_EMAIL,
emoji: "👩",
isActive: isFemale,
},
];
const guestAccounts = [
{
token: GUEST_MALE_1_TOKEN,
title: "حساب مهمان آقا ۱",
subtitle: "مهمان / بدون اطلاعات",
emoji: "👨",
isActive: currentToken === GUEST_MALE_1_TOKEN,
},
{
token: GUEST_MALE_2_TOKEN,
title: "حساب مهمان آقا ۲",
subtitle: "مهمان / بدون اطلاعات",
emoji: "👨",
isActive: currentToken === GUEST_MALE_2_TOKEN,
},
{
token: GUEST_FEMALE_1_TOKEN,
title: "حساب مهمان خانم ۱",
subtitle: "مهمان / بدون اطلاعات",
emoji: "👩",
isActive: currentToken === GUEST_FEMALE_1_TOKEN,
},
{
token: GUEST_FEMALE_2_TOKEN,
title: "حساب مهمان خانم ۲",
subtitle: "مهمان / بدون اطلاعات",
emoji: "👩",
isActive: currentToken === GUEST_FEMALE_2_TOKEN,
},
];
return ( return (
<> <>
<button <button
@ -154,7 +223,7 @@ export function TokenSwitcher({
aria-label="تغییر توکن کاربر (تست)" aria-label="تغییر توکن کاربر (تست)"
title="تغییر توکن (آقا / خانم)" title="تغییر توکن (آقا / خانم)"
className={[ className={[
"inline-flex items-center gap-1.5 rounded-[15px] px-3 py-2 text-xs font-bold transition-all active:scale-95 shadow-md border border-slate-200/80 dark:border-slate-700/80 backdrop-blur-md",
"inline-flex items-center gap-1.5 rounded-[15px] px-3 py-2 text-xs font-bold transition-all active:scale-95 shadow-md border border-slate-200/80 dark:border-slate-700/80 backdrop-blur-md cursor-pointer",
variant === "transparent" variant === "transparent"
? "bg-black/40 text-white hover:bg-black/50" ? "bg-black/40 text-white hover:bg-black/50"
: "bg-white/95 text-slate-800 hover:bg-white dark:bg-slate-900/95 dark:text-slate-100", : "bg-white/95 text-slate-800 hover:bg-white dark:bg-slate-900/95 dark:text-slate-100",
@ -178,205 +247,206 @@ export function TokenSwitcher({
</button> </button>
{isOpen && ( {isOpen && (
<div className="fixed inset-0 z-[10000] flex items-center justify-center bg-black/50 p-4 backdrop-blur-xs animate-in fade-in duration-200">
<div
className="fixed inset-0 z-[10000] flex items-center justify-center bg-black/60 p-4 backdrop-blur-xs animate-in fade-in duration-200"
dir="rtl"
onClick={() => setIsOpen(false)}
>
<div <div
className="w-full max-w-sm rounded-2xl bg-white p-5 shadow-xl dark:bg-slate-900 border border-slate-100 dark:border-slate-800 max-h-[90vh] flex flex-col"
className="w-full max-w-md rounded-2xl bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 shadow-2xl flex flex-col max-h-[88vh] overflow-hidden"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<div className="flex items-center justify-between pb-3 border-b border-slate-100 dark:border-slate-800 shrink-0">
<h3 className="font-bold text-slate-900 dark:text-white text-base flex items-center gap-2">
<MdOutlineSwitchAccount className="text-rose-500 text-xl" />
انتخاب حساب کاربری (تست)
</h3>
{/* Modal Header */}
<div className="flex items-center justify-between px-5 py-4 border-b border-slate-100 dark:border-slate-800 shrink-0 bg-slate-50/50 dark:bg-slate-900/50">
<div className="flex items-center gap-3">
<div className="size-10 rounded-xl bg-rose-500/10 text-rose-500 flex items-center justify-center">
<MdOutlineSwitchAccount className="size-5" />
</div>
<div>
<h3 className="font-extrabold text-slate-900 dark:text-white text-base leading-snug">
انتخاب حساب کاربری (تست)
</h3>
<p className="text-xs text-slate-500 dark:text-slate-400">
تغییر سریع پروفایل و توکن جهت تست
</p>
</div>
</div>
<button <button
type="button" type="button"
onClick={() => setIsOpen(false)} onClick={() => setIsOpen(false)}
className="text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 text-sm p-1"
className="size-8 rounded-xl flex items-center justify-center text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors cursor-pointer"
aria-label="بستن"
> >
<IoClose className="size-5" />
</button> </button>
</div> </div>
<div className="mt-4 flex flex-col gap-2.5 overflow-y-auto pr-0.5 flex-1">
{/* Male option card */}
<div
className={[
"flex items-center justify-between rounded-xl border transition-all dir-rtl overflow-hidden",
isMale
? "border-rose-500 bg-rose-50/60 dark:bg-rose-950/30 text-rose-900 dark:text-rose-100 font-semibold shadow-xs"
: "border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200",
].join(" ")}
>
<button
type="button"
onClick={() => handleSelectToken(MALE_TOKEN)}
className="flex-1 flex items-center gap-3 p-3.5 text-right min-w-0"
{/* Scrollable Account Cards */}
<div className="flex-1 overflow-y-auto px-5 py-4 space-y-2.5 min-h-0">
{/* Main Predefined Accounts */}
{mainAccounts.map((acc) => (
<div
key={acc.token}
className={[
"flex items-center justify-between gap-3 p-3 rounded-2xl border transition-all select-none",
acc.isActive
? "border-rose-500 bg-rose-50/70 dark:bg-rose-950/40 shadow-xs ring-1 ring-rose-500/30"
: "border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700 bg-white dark:bg-slate-800/80 hover:bg-slate-50/60 dark:hover:bg-slate-800",
].join(" ")}
> >
<span className="text-2xl shrink-0">👨</span>
<div className="min-w-0 flex-1">
<div className="text-sm font-bold">حساب آقا</div>
<div className="text-xs text-slate-500 dark:text-slate-400 font-medium truncate">
{MALE_EMAIL}
<div
onClick={() => handleSelectToken(acc.token)}
className="flex-1 min-w-0 flex items-center gap-3 text-right cursor-pointer"
>
<div className="size-11 rounded-xl bg-slate-100 dark:bg-slate-800 flex items-center justify-center text-2xl shrink-0 border border-slate-200/60 dark:border-slate-700/60 shadow-3xs">
{acc.emoji}
</div> </div>
<div className="text-[11px] font-mono text-slate-400 dark:text-slate-500 dir-ltr text-left mt-0.5 select-all truncate">
{MALE_TOKEN}
<div className="flex-1 min-w-0 flex flex-col justify-center gap-0.5 text-right">
<span className="text-sm font-bold text-slate-900 dark:text-white leading-tight">
{acc.title}
</span>
<span
className="text-xs text-slate-500 dark:text-slate-400 font-medium truncate block leading-tight text-right"
dir="ltr"
>
{acc.subtitle}
</span>
<span
className="text-[10px] font-mono text-slate-400 dark:text-slate-500 truncate block text-left leading-tight"
dir="ltr"
>
{acc.token}
</span>
</div> </div>
</div> </div>
</button>
<div className="flex items-center gap-1.5 pl-3">
{isMale && (
<span className="text-[10px] px-2 py-0.5 rounded-full bg-rose-500 text-white font-medium">
فعال
</span>
)}
<button
type="button"
onClick={() => handleResetUser(17119, "حساب آقا")}
title="پاک کردن اطلاعات و شروع مجدد"
className="p-2 text-slate-400 hover:text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-950/20 rounded-lg transition-colors"
>
🗑
</button>
</div>
</div>
{/* Male 2 option card */}
<div
className={[
"flex items-center justify-between rounded-xl border transition-all dir-rtl overflow-hidden",
isMale2
? "border-rose-500 bg-rose-50/60 dark:bg-rose-950/30 text-rose-900 dark:text-rose-100 font-semibold shadow-xs"
: "border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200",
].join(" ")}
>
<button
type="button"
onClick={() => handleSelectToken(MALE_2_TOKEN)}
className="flex-1 flex items-center gap-3 p-3.5 text-right min-w-0"
>
<span className="text-2xl shrink-0">👨</span>
<div className="min-w-0 flex-1">
<div className="text-sm font-bold">حساب آقا ۲</div>
<div className="text-xs text-slate-500 dark:text-slate-400 font-medium truncate">
{MALE_2_EMAIL}
</div>
<div className="text-[11px] font-mono text-slate-400 dark:text-slate-500 dir-ltr text-left mt-0.5 select-all truncate">
{MALE_2_TOKEN}
</div>
<div className="flex items-center gap-1.5 shrink-0 pr-1">
{acc.isActive && (
<span className="text-[10px] font-bold px-2 py-0.5 rounded-full bg-rose-500 text-white shadow-xs">
فعال
</span>
)}
<button
type="button"
onClick={() => handleResetUser(acc.id, acc.title)}
title="پاک کردن اطلاعات و شروع مجدد"
className="size-8 rounded-lg flex items-center justify-center text-slate-400 hover:text-rose-600 hover:bg-rose-50 dark:hover:bg-rose-950/40 transition-colors cursor-pointer"
>
<IoTrashOutline className="size-4" />
</button>
</div> </div>
</button>
<div className="flex items-center gap-1.5 pl-3">
{isMale2 && (
<span className="text-[10px] px-2 py-0.5 rounded-full bg-rose-500 text-white font-medium">
فعال
</span>
)}
<button
type="button"
onClick={() => handleResetUser(146024, "حساب آقا ۲")}
title="پاک کردن اطلاعات و شروع مجدد"
className="p-2 text-slate-400 hover:text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-950/20 rounded-lg transition-colors"
>
🗑
</button>
</div> </div>
</div>
))}
{/* Female option card */}
<div
className={[
"flex items-center justify-between rounded-xl border transition-all dir-rtl overflow-hidden",
isFemale
? "border-rose-500 bg-rose-50/60 dark:bg-rose-950/30 text-rose-900 dark:text-rose-100 font-semibold shadow-xs"
: "border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200",
].join(" ")}
>
<button
type="button"
onClick={() => handleSelectToken(FEMALE_TOKEN)}
className="flex-1 flex items-center gap-3 p-3.5 text-right min-w-0"
{/* Guest Accounts */}
{guestAccounts.map((acc) => (
<div
key={acc.token}
className={[
"flex items-center justify-between gap-3 p-3 rounded-2xl border transition-all select-none",
acc.isActive
? "border-rose-500 bg-rose-50/70 dark:bg-rose-950/40 shadow-xs ring-1 ring-rose-500/30"
: "border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700 bg-white dark:bg-slate-800/80 hover:bg-slate-50/60 dark:hover:bg-slate-800",
].join(" ")}
> >
<span className="text-2xl shrink-0">👩</span>
<div className="min-w-0 flex-1">
<div className="text-sm font-bold">حساب خانم</div>
<div className="text-xs text-slate-500 dark:text-slate-400 font-medium truncate">
{FEMALE_EMAIL}
<div
onClick={() => handleSelectToken(acc.token)}
className="flex-1 min-w-0 flex items-center gap-3 text-right cursor-pointer"
>
<div className="size-11 rounded-xl bg-slate-100 dark:bg-slate-800 flex items-center justify-center text-2xl shrink-0 border border-slate-200/60 dark:border-slate-700/60 shadow-3xs">
{acc.emoji}
</div> </div>
<div className="text-[11px] font-mono text-slate-400 dark:text-slate-500 dir-ltr text-left mt-0.5 select-all truncate">
{FEMALE_TOKEN}
<div className="flex-1 min-w-0 flex flex-col justify-center gap-0.5 text-right">
<span className="text-sm font-bold text-slate-900 dark:text-white leading-tight">
{acc.title}
</span>
<span className="text-xs text-slate-500 dark:text-slate-400 font-medium truncate block leading-tight">
{acc.subtitle}
</span>
<span
className="text-[10px] font-mono text-slate-400 dark:text-slate-500 truncate block text-left leading-tight"
dir="ltr"
>
{acc.token}
</span>
</div> </div>
</div> </div>
</button>
<div className="flex items-center gap-1.5 pl-3">
{isFemale && (
<span className="text-[10px] px-2 py-0.5 rounded-full bg-rose-500 text-white font-medium">
فعال
</span>
)}
<button
type="button"
onClick={() => handleResetUser(147714, "حساب خانم")}
title="پاک کردن اطلاعات و شروع مجدد"
className="p-2 text-slate-400 hover:text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-950/20 rounded-lg transition-colors"
>
🗑
</button>
<div className="flex items-center gap-1.5 shrink-0 pr-1">
{acc.isActive && (
<span className="text-[10px] font-bold px-2 py-0.5 rounded-full bg-rose-500 text-white shadow-xs">
فعال
</span>
)}
</div>
</div> </div>
</div>
))}
{/* Custom Token Card (if active) */}
{/* Custom Active Token Card (if any custom token is set) */}
{isCustomToken && ( {isCustomToken && (
<div className="flex items-center justify-between rounded-xl border border-rose-500 bg-rose-50/60 dark:bg-rose-950/30 text-rose-900 dark:text-rose-100 font-semibold shadow-xs dir-rtl overflow-hidden">
<div className="flex-1 flex items-center gap-3 p-3.5 text-right min-w-0">
<span className="text-2xl shrink-0">🔑</span>
<div className="min-w-0 flex-1">
<div className="text-sm font-bold">توکن سفارشی</div>
<div className="text-[11px] font-mono text-slate-500 dark:text-slate-400 dir-ltr text-left mt-0.5 select-all truncate">
<div className="flex items-center justify-between gap-3 p-3 rounded-2xl border border-rose-500 bg-rose-50/70 dark:bg-rose-950/40 shadow-xs ring-1 ring-rose-500/30 select-none">
<div className="flex-1 min-w-0 flex items-center gap-3 text-right">
<div className="size-11 rounded-xl bg-slate-100 dark:bg-slate-800 flex items-center justify-center text-2xl shrink-0 border border-slate-200/60 dark:border-slate-700/60 shadow-3xs">
🔑
</div>
<div className="flex-1 min-w-0 flex flex-col justify-center gap-0.5 text-right">
<span className="text-sm font-bold text-slate-900 dark:text-white leading-tight">
توکن سفارشی
</span>
<span
className="text-[10px] font-mono text-slate-500 dark:text-slate-400 truncate block text-left leading-tight"
dir="ltr"
>
{currentToken} {currentToken}
</div>
</span>
</div> </div>
</div> </div>
<div className="flex items-center gap-1.5 pl-3">
<span className="text-[10px] px-2 py-0.5 rounded-full bg-rose-500 text-white font-medium">
<div className="flex items-center gap-1.5 shrink-0 pr-1">
<span className="text-[10px] font-bold px-2 py-0.5 rounded-full bg-rose-500 text-white shadow-xs">
فعال فعال
</span> </span>
<button <button
type="button" type="button"
onClick={() => handleResetUser(null, "توکن سفارشی")} onClick={() => handleResetUser(null, "توکن سفارشی")}
title="پاک کردن اطلاعات و شروع مجدد" title="پاک کردن اطلاعات و شروع مجدد"
className="p-2 text-slate-400 hover:text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-950/20 rounded-lg transition-colors"
className="size-8 rounded-lg flex items-center justify-center text-slate-400 hover:text-rose-600 hover:bg-rose-50 dark:hover:bg-rose-950/40 transition-colors cursor-pointer"
> >
🗑
<IoTrashOutline className="size-4" />
</button> </button>
</div> </div>
</div> </div>
)} )}
{/* No token option card */}
{/* No Token Option Card */}
<div <div
className={[ className={[
"flex items-center justify-between rounded-xl border transition-all dir-rtl overflow-hidden",
"flex items-center justify-between gap-3 p-3 rounded-2xl border transition-all select-none",
isNoToken isNoToken
? "border-rose-500 bg-rose-50/60 dark:bg-rose-950/30 text-rose-900 dark:text-rose-100 font-semibold shadow-xs"
: "border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200",
? "border-rose-500 bg-rose-50/70 dark:bg-rose-950/40 shadow-xs ring-1 ring-rose-500/30"
: "border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700 bg-white dark:bg-slate-800/80 hover:bg-slate-50/60 dark:hover:bg-slate-800",
].join(" ")} ].join(" ")}
> >
<button
type="button"
<div
onClick={() => handleSelectToken("NO_TOKEN")} onClick={() => handleSelectToken("NO_TOKEN")}
className="flex-1 flex items-center gap-3 p-3.5 text-right min-w-0"
className="flex-1 min-w-0 flex items-center gap-3 text-right cursor-pointer"
> >
<span className="text-2xl shrink-0">👤</span>
<div>
<div className="text-sm font-bold">کاربر بدون توکن</div>
<div className="text-xs text-slate-500 dark:text-slate-400 font-medium">
<div className="size-11 rounded-xl bg-slate-100 dark:bg-slate-800 flex items-center justify-center text-2xl shrink-0 border border-slate-200/60 dark:border-slate-700/60 shadow-3xs">
👤
</div>
<div className="flex-1 min-w-0 flex flex-col justify-center gap-0.5 text-right">
<span className="text-sm font-bold text-slate-900 dark:text-white leading-tight">
کاربر بدون توکن
</span>
<span className="text-xs text-slate-500 dark:text-slate-400 font-medium truncate block leading-tight">
حالت مهمان / بدون احراز هویت حالت مهمان / بدون احراز هویت
</div>
</span>
</div> </div>
</button>
<div className="flex items-center gap-1.5 pl-3">
</div>
<div className="flex items-center gap-1.5 shrink-0 pr-1">
{isNoToken && ( {isNoToken && (
<span className="text-[10px] px-2 py-0.5 rounded-full bg-rose-500 text-white font-medium">
<span className="text-[10px] font-bold px-2 py-0.5 rounded-full bg-rose-500 text-white shadow-xs">
فعال فعال
</span> </span>
)} )}
@ -384,18 +454,18 @@ export function TokenSwitcher({
type="button" type="button"
onClick={() => handleResetUser(null, "کاربر بدون توکن")} onClick={() => handleResetUser(null, "کاربر بدون توکن")}
title="پاک کردن اطلاعات و شروع مجدد" title="پاک کردن اطلاعات و شروع مجدد"
className="p-2 text-slate-400 hover:text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-950/20 rounded-lg transition-colors"
className="size-8 rounded-lg flex items-center justify-center text-slate-400 hover:text-rose-600 hover:bg-rose-50 dark:hover:bg-rose-950/40 transition-colors cursor-pointer"
> >
🗑
<IoTrashOutline className="size-4" />
</button> </button>
</div> </div>
</div> </div>
{/* Inline Custom Token Input Box */}
<div className="rounded-xl border border-dashed border-slate-300 dark:border-slate-700 p-3 bg-slate-50/50 dark:bg-slate-800/50 dir-rtl">
<div className="text-xs font-bold text-slate-700 dark:text-slate-300 mb-2 flex items-center gap-1.5">
<span>🔑</span>
<span>افزودن / تنظیم توکن دلخواه:</span>
{/* Custom Token Input Section */}
<div className="rounded-2xl border border-dashed border-slate-300 dark:border-slate-700 p-3.5 bg-slate-50/70 dark:bg-slate-800/40 space-y-2.5 mt-3">
<div className="flex items-center gap-2 text-xs font-bold text-slate-700 dark:text-slate-300">
<IoKeyOutline className="size-4 text-amber-500" />
<span>افزودن یا تنظیم توکن دلخواه:</span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<input <input
@ -404,7 +474,7 @@ export function TokenSwitcher({
onChange={(e) => setCustomTokenInput(e.target.value)} onChange={(e) => setCustomTokenInput(e.target.value)}
placeholder="توکن ۳۲/۴۰ کاراکتری..." placeholder="توکن ۳۲/۴۰ کاراکتری..."
dir="ltr" dir="ltr"
className="flex-1 rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 px-2.5 py-1.5 text-xs font-mono text-slate-800 dark:text-slate-200 outline-none focus:border-rose-500 transition-colors"
className="flex-1 min-w-0 rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 px-3 py-2 text-xs font-mono text-slate-800 dark:text-slate-200 outline-none focus:border-rose-500 focus:ring-1 focus:ring-rose-500/20 transition-all placeholder:text-slate-400 placeholder:text-[11px]"
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === "Enter" && customTokenInput.trim()) { if (e.key === "Enter" && customTokenInput.trim()) {
handleSelectToken(customTokenInput); handleSelectToken(customTokenInput);
@ -415,7 +485,7 @@ export function TokenSwitcher({
type="button" type="button"
disabled={!customTokenInput.trim()} disabled={!customTokenInput.trim()}
onClick={() => handleSelectToken(customTokenInput)} onClick={() => handleSelectToken(customTokenInput)}
className="rounded-lg bg-rose-500 px-3 py-1.5 text-xs font-semibold text-white shadow-xs hover:bg-rose-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors shrink-0"
className="rounded-xl bg-rose-500 hover:bg-rose-600 active:scale-95 px-4 py-2 text-xs font-bold text-white shadow-xs disabled:opacity-40 disabled:cursor-not-allowed transition-all shrink-0 cursor-pointer"
> >
اعمال اعمال
</button> </button>
@ -423,13 +493,14 @@ export function TokenSwitcher({
</div> </div>
</div> </div>
<div className="mt-4 pt-3 border-t border-slate-100 dark:border-slate-800 flex flex-col gap-2 shrink-0">
{/* Modal Footer */}
<div className="px-5 py-3 border-t border-slate-100 dark:border-slate-800 shrink-0 bg-slate-50/50 dark:bg-slate-900/50">
<button <button
type="button" type="button"
onClick={() => setIsOpen(false)} onClick={() => setIsOpen(false)}
className="w-full py-2.5 rounded-xl bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300 font-medium text-sm hover:bg-slate-200 dark:hover:bg-slate-700 transition-colors"
className="w-full py-2.5 rounded-xl bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700 text-slate-700 dark:text-slate-200 font-bold text-xs transition-colors cursor-pointer"
> >
بستن
بستن پنجره
</button> </button>
</div> </div>
</div> </div>

Loading…
Cancel
Save