|
|
|
@ -1,13 +1,17 @@ |
|
|
|
"use client"; |
|
|
|
|
|
|
|
import { useEffect, useRef, useState } from "react"; |
|
|
|
import { MdOutlineSwitchAccount, MdDragIndicator } from "react-icons/md"; |
|
|
|
import { useEffect, useState } from "react"; |
|
|
|
import { MdOutlineSwitchAccount } from "react-icons/md"; |
|
|
|
import { getClientCookie, setClientCookie } from "@/lib/cookies"; |
|
|
|
import { setCachedMarriageEntryPath } from "@/lib/entry-route-cache"; |
|
|
|
|
|
|
|
export const FEMALE_TOKEN = "545f61bb3e061ccb9b19f84715eb1b1ed4740331"; |
|
|
|
export const MALE_TOKEN = "f3a7543b44ef0a713d1ee0d4f7866b3825cf1308"; |
|
|
|
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 MALE_EMAIL = "muhammadamin.ghorbani@gmail.com"; |
|
|
|
@ -30,18 +34,7 @@ export function TokenSwitcher({ |
|
|
|
const [isOpen, setIsOpen] = useState(false); |
|
|
|
const [currentToken, setCurrentToken] = useState<string>(""); |
|
|
|
const [customTokenInput, setCustomTokenInput] = useState<string>(""); |
|
|
|
const [mounted, setMounted] = useState(false); |
|
|
|
const [position, setPosition] = useState<{ x: number; y: number } | null>(null); |
|
|
|
const [isDragging, setIsDragging] = useState(false); |
|
|
|
|
|
|
|
const containerRef = useRef<HTMLDivElement>(null); |
|
|
|
const dragStart = useRef({ |
|
|
|
x: 0, |
|
|
|
y: 0, |
|
|
|
startX: 0, |
|
|
|
startY: 0, |
|
|
|
hasMoved: false, |
|
|
|
}); |
|
|
|
const [isCustomInputOpen, setIsCustomInputOpen] = useState(false); |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
setMounted(true); |
|
|
|
@ -97,7 +90,15 @@ export function TokenSwitcher({ |
|
|
|
const isMale2 = currentToken === MALE_2_TOKEN; |
|
|
|
const isFemale = currentToken === FEMALE_TOKEN; |
|
|
|
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 trimmed = newToken.trim(); |
|
|
|
@ -193,346 +194,291 @@ export function TokenSwitcher({ |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// Pointer drag handlers
|
|
|
|
const handlePointerDown = (e: React.PointerEvent<HTMLDivElement>) => { |
|
|
|
if (!isFloating || isOpen) return; |
|
|
|
|
|
|
|
e.currentTarget.setPointerCapture(e.pointerId); |
|
|
|
setIsDragging(true); |
|
|
|
const startX = position?.x ?? 12; |
|
|
|
const startY = position?.y ?? 12; |
|
|
|
dragStart.current = { |
|
|
|
x: e.clientX, |
|
|
|
y: e.clientY, |
|
|
|
startX, |
|
|
|
startY, |
|
|
|
hasMoved: false, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
const handlePointerMove = (e: React.PointerEvent<HTMLDivElement>) => { |
|
|
|
if (!isDragging || !isFloating) return; |
|
|
|
|
|
|
|
const dx = e.clientX - dragStart.current.x; |
|
|
|
const dy = e.clientY - dragStart.current.y; |
|
|
|
|
|
|
|
if (Math.hypot(dx, dy) > 4) { |
|
|
|
dragStart.current.hasMoved = true; |
|
|
|
} |
|
|
|
|
|
|
|
const width = containerRef.current?.offsetWidth || 140; |
|
|
|
const height = containerRef.current?.offsetHeight || 40; |
|
|
|
|
|
|
|
const newX = Math.max( |
|
|
|
10, |
|
|
|
Math.min(window.innerWidth - width - 10, dragStart.current.startX + dx) |
|
|
|
); |
|
|
|
const newY = Math.max( |
|
|
|
10, |
|
|
|
Math.min(window.innerHeight - height - 10, dragStart.current.startY + dy) |
|
|
|
); |
|
|
|
|
|
|
|
setPosition({ x: newX, y: newY }); |
|
|
|
}; |
|
|
|
|
|
|
|
const handlePointerUp = (e: React.PointerEvent<HTMLDivElement>) => { |
|
|
|
if (!isDragging || !isFloating) return; |
|
|
|
try { |
|
|
|
e.currentTarget.releasePointerCapture(e.pointerId); |
|
|
|
} catch (err) {} |
|
|
|
setIsDragging(false); |
|
|
|
|
|
|
|
if (dragStart.current.hasMoved) { |
|
|
|
if (position) { |
|
|
|
try { |
|
|
|
localStorage.setItem(STORAGE_POS_KEY, JSON.stringify(position)); |
|
|
|
} catch (e) {} |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const handleBadgeClick = (e: React.MouseEvent) => { |
|
|
|
if (dragStart.current.hasMoved) { |
|
|
|
e.preventDefault(); |
|
|
|
e.stopPropagation(); |
|
|
|
return; |
|
|
|
} |
|
|
|
setIsOpen(true); |
|
|
|
}; |
|
|
|
|
|
|
|
const currentRoleLabel = isMale |
|
|
|
? "آقا 👨" |
|
|
|
: isMale2 |
|
|
|
? "آقا ۲ 👨" |
|
|
|
: isFemale |
|
|
|
? "خانم 👩" |
|
|
|
: isNoToken |
|
|
|
? "بدون توکن 👤" |
|
|
|
: "سفارشی 🔑"; |
|
|
|
|
|
|
|
if (!mounted && isFloating) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
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, |
|
|
|
}, |
|
|
|
]; |
|
|
|
|
|
|
|
const badgeContent = ( |
|
|
|
<div |
|
|
|
ref={containerRef} |
|
|
|
onPointerDown={handlePointerDown} |
|
|
|
onPointerMove={handlePointerMove} |
|
|
|
onPointerUp={handlePointerUp} |
|
|
|
onPointerCancel={() => setIsDragging(false)} |
|
|
|
onClick={handleBadgeClick} |
|
|
|
style={ |
|
|
|
isFloating |
|
|
|
? { |
|
|
|
position: "fixed", |
|
|
|
left: position ? `${position.x}px` : "12px", |
|
|
|
top: position ? `${position.y}px` : "12px", |
|
|
|
zIndex: 99999, |
|
|
|
touchAction: "none", |
|
|
|
userSelect: "none", |
|
|
|
} |
|
|
|
: undefined |
|
|
|
} |
|
|
|
return ( |
|
|
|
<> |
|
|
|
<button |
|
|
|
type="button" |
|
|
|
onClick={() => setIsOpen(true)} |
|
|
|
aria-label="تغییر توکن کاربر (تست)" |
|
|
|
title="تغییر توکن (آقا / خانم)" |
|
|
|
className={[ |
|
|
|
isFloating |
|
|
|
? "fixed flex items-center gap-1.5 px-3 py-2 rounded-[15px] backdrop-blur-md shadow-md border select-none transition-shadow cursor-pointer" |
|
|
|
: "inline-flex items-center gap-1.5 rounded-[15px] px-3 py-2 shadow-md border backdrop-blur-md cursor-pointer", |
|
|
|
"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", |
|
|
|
variant === "transparent" |
|
|
|
? "bg-black/60 text-white border-white/20 hover:bg-black/70" |
|
|
|
: "bg-white/95 text-slate-800 border-slate-200/80 hover:bg-white dark:bg-slate-900/95 dark:text-slate-100 dark:border-slate-700/80", |
|
|
|
isDragging |
|
|
|
? "cursor-grabbing shadow-2xl ring-2 ring-rose-500/50 scale-[1.02]" |
|
|
|
: "cursor-grab active:scale-95", |
|
|
|
? "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", |
|
|
|
className, |
|
|
|
] |
|
|
|
.filter(Boolean) |
|
|
|
.join(" ")} |
|
|
|
title="تغییر توکن کاربر (تست) - برای جابجایی بکشید / برای انتخاب کلیک کنید" |
|
|
|
> |
|
|
|
{/* Drag handle */} |
|
|
|
{isFloating && ( |
|
|
|
<div |
|
|
|
className="flex items-center justify-center text-slate-400 dark:text-slate-500 shrink-0" |
|
|
|
title="برای جابجایی درگ کنید" |
|
|
|
> |
|
|
|
<MdDragIndicator className="size-4" /> |
|
|
|
</div> |
|
|
|
)} |
|
|
|
|
|
|
|
{/* Role icon & selected account label only */} |
|
|
|
<MdOutlineSwitchAccount className="size-5 text-rose-500 shrink-0" /> |
|
|
|
<span className="text-xs font-bold whitespace-nowrap">{currentRoleLabel}</span> |
|
|
|
</div> |
|
|
|
); |
|
|
|
|
|
|
|
return ( |
|
|
|
<> |
|
|
|
{badgeContent} |
|
|
|
<MdOutlineSwitchAccount className="size-5 text-rose-500" /> |
|
|
|
<span> |
|
|
|
{isMale |
|
|
|
? "آقا 👨" |
|
|
|
: isMale2 |
|
|
|
? "آقا ۲ 👨" |
|
|
|
: isFemale |
|
|
|
? "خانم 👩" |
|
|
|
: isNoToken |
|
|
|
? "بدون توکن 👤" |
|
|
|
: "سفارشی 🔑"} |
|
|
|
</span> |
|
|
|
</button> |
|
|
|
|
|
|
|
{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-[100000] flex items-center justify-center bg-black/60 p-4 backdrop-blur-xs animate-in fade-in duration-200" |
|
|
|
onClick={() => setIsOpen(false)} |
|
|
|
> |
|
|
|
<div |
|
|
|
className="w-full max-w-sm rounded-2xl bg-white p-5 shadow-2xl dark:bg-slate-900 border border-slate-100 dark:border-slate-800 max-h-[90vh] flex flex-col" |
|
|
|
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" |
|
|
|
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" /> |
|
|
|
{/* 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 |
|
|
|
type="button" |
|
|
|
onClick={() => setIsOpen(false)} |
|
|
|
className="text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 text-sm p-1 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors" |
|
|
|
className="text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 text-sm p-1" |
|
|
|
> |
|
|
|
✕ |
|
|
|
<IoClose className="size-5" /> |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div className="mt-4 flex flex-col gap-2.5 overflow-y-auto pr-0.5 flex-1"> |
|
|
|
{/* Male option card */} |
|
|
|
{/* 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 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", |
|
|
|
"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(" ")} |
|
|
|
> |
|
|
|
<button |
|
|
|
type="button" |
|
|
|
onClick={() => handleSelectToken(MALE_TOKEN)} |
|
|
|
className="flex-1 flex items-center gap-3 p-3.5 text-right min-w-0" |
|
|
|
<div |
|
|
|
onClick={() => handleSelectToken(acc.token)} |
|
|
|
className="flex-1 min-w-0 flex items-center gap-3 text-right cursor-pointer" |
|
|
|
> |
|
|
|
<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> |
|
|
|
<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> |
|
|
|
<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> |
|
|
|
</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"> |
|
|
|
فعال |
|
|
|
<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> |
|
|
|
)} |
|
|
|
<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(" ")} |
|
|
|
<span |
|
|
|
className="text-xs text-slate-500 dark:text-slate-400 font-medium truncate block leading-tight text-right" |
|
|
|
dir="ltr" |
|
|
|
> |
|
|
|
<button |
|
|
|
type="button" |
|
|
|
onClick={() => handleSelectToken(MALE_2_TOKEN)} |
|
|
|
className="flex-1 flex items-center gap-3 p-3.5 text-right min-w-0" |
|
|
|
{acc.subtitle} |
|
|
|
</span> |
|
|
|
<span |
|
|
|
className="text-[10px] font-mono text-slate-400 dark:text-slate-500 truncate block text-left leading-tight" |
|
|
|
dir="ltr" |
|
|
|
> |
|
|
|
<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} |
|
|
|
{acc.token} |
|
|
|
</span> |
|
|
|
</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"> |
|
|
|
|
|
|
|
<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(146024, "حساب آقا ۲")} |
|
|
|
onClick={() => handleResetUser(acc.id, acc.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> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
))} |
|
|
|
|
|
|
|
{/* Female option card */} |
|
|
|
{/* Guest Accounts */} |
|
|
|
{guestAccounts.map((acc) => ( |
|
|
|
<div |
|
|
|
key={acc.token} |
|
|
|
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", |
|
|
|
"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(" ")} |
|
|
|
> |
|
|
|
<button |
|
|
|
type="button" |
|
|
|
onClick={() => handleSelectToken(FEMALE_TOKEN)} |
|
|
|
className="flex-1 flex items-center gap-3 p-3.5 text-right min-w-0" |
|
|
|
<div |
|
|
|
onClick={() => handleSelectToken(acc.token)} |
|
|
|
className="flex-1 min-w-0 flex items-center gap-3 text-right cursor-pointer" |
|
|
|
> |
|
|
|
<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 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 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> |
|
|
|
</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"> |
|
|
|
|
|
|
|
<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(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> |
|
|
|
</div> |
|
|
|
))} |
|
|
|
|
|
|
|
{/* Custom Token Card (if active) */} |
|
|
|
{/* Custom Active Token Card (if any custom token is set) */} |
|
|
|
{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} |
|
|
|
</span> |
|
|
|
</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> |
|
|
|
<button |
|
|
|
type="button" |
|
|
|
onClick={() => handleResetUser(null, "توکن سفارشی")} |
|
|
|
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> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
)} |
|
|
|
|
|
|
|
{/* No token option card */} |
|
|
|
{/* No Token Option Card */} |
|
|
|
<div |
|
|
|
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 |
|
|
|
? "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(" ")} |
|
|
|
> |
|
|
|
<button |
|
|
|
type="button" |
|
|
|
<div |
|
|
|
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"> |
|
|
|
حالت مهمان / بدون احراز هویت |
|
|
|
</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</button> |
|
|
|
<div className="flex items-center gap-1.5 pl-3"> |
|
|
|
|
|
|
|
<div className="flex items-center gap-1.5 shrink-0 pr-1"> |
|
|
|
{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> |
|
|
|
)} |
|
|
|
@ -540,18 +486,18 @@ export function TokenSwitcher({ |
|
|
|
type="button" |
|
|
|
onClick={() => handleResetUser(null, "کاربر بدون توکن")} |
|
|
|
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> |
|
|
|
</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 className="flex items-center gap-2"> |
|
|
|
<input |
|
|
|
@ -560,7 +506,7 @@ export function TokenSwitcher({ |
|
|
|
onChange={(e) => setCustomTokenInput(e.target.value)} |
|
|
|
placeholder="توکن ۳۲/۴۰ کاراکتری..." |
|
|
|
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) => { |
|
|
|
if (e.key === "Enter" && customTokenInput.trim()) { |
|
|
|
handleSelectToken(customTokenInput); |
|
|
|
@ -571,7 +517,7 @@ export function TokenSwitcher({ |
|
|
|
type="button" |
|
|
|
disabled={!customTokenInput.trim()} |
|
|
|
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> |
|
|
|
@ -579,13 +525,14 @@ export function TokenSwitcher({ |
|
|
|
</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 |
|
|
|
type="button" |
|
|
|
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> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|