139 lines
4.5 KiB
TypeScript
139 lines
4.5 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef, useState, type MouseEvent } from "react";
|
|
import { businessDashboardUrl, customerDashboardUrl } from "@/lib/customer";
|
|
import { useSession } from "@/lib/session";
|
|
import { IconChevron, IconUser } from "./Icons";
|
|
|
|
const itemClass =
|
|
"block w-full px-3 py-2.5 text-right text-sm text-ink transition-colors hover:bg-brand-soft hover:text-brand";
|
|
|
|
export function AccountMenu({
|
|
displayName,
|
|
isWebsiteAdmin,
|
|
onLogout,
|
|
compact = false,
|
|
}: {
|
|
displayName: string;
|
|
isWebsiteAdmin: boolean;
|
|
onLogout: () => void;
|
|
compact?: boolean;
|
|
}) {
|
|
const [open, setOpen] = useState(false);
|
|
const [handoffBusy, setHandoffBusy] = useState(false);
|
|
const menuRef = useRef<HTMLDivElement>(null);
|
|
const { getAccessToken } = useSession();
|
|
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
const onClickOutside = (event: Event) => {
|
|
if (menuRef.current && event.target instanceof Node && !menuRef.current.contains(event.target)) {
|
|
setOpen(false);
|
|
}
|
|
};
|
|
const onKeyDown = (event: KeyboardEvent) => {
|
|
if (event.key === "Escape") setOpen(false);
|
|
};
|
|
document.addEventListener("mousedown", onClickOutside);
|
|
document.addEventListener("keydown", onKeyDown);
|
|
return () => {
|
|
document.removeEventListener("mousedown", onClickOutside);
|
|
document.removeEventListener("keydown", onKeyDown);
|
|
};
|
|
}, [open]);
|
|
|
|
async function openBusinessDashboard(event: MouseEvent<HTMLAnchorElement>) {
|
|
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0) {
|
|
return;
|
|
}
|
|
event.preventDefault();
|
|
if (handoffBusy) return;
|
|
|
|
const baseUrl = businessDashboardUrl();
|
|
const popup = window.open("about:blank", "_blank");
|
|
setHandoffBusy(true);
|
|
try {
|
|
const token = getAccessToken();
|
|
const res = await fetch("/api/auth/handoff", {
|
|
method: "POST",
|
|
headers: token ? { Authorization: `Bearer ${token}` } : undefined,
|
|
});
|
|
if (!res.ok) throw new Error("handoff failed");
|
|
const data = (await res.json()) as { ticket?: string };
|
|
const url = new URL(baseUrl);
|
|
if (data.ticket) url.searchParams.set("handoff", data.ticket);
|
|
if (popup) {
|
|
popup.opener = null;
|
|
popup.location.replace(url.toString());
|
|
} else {
|
|
window.open(url.toString(), "_blank", "noopener,noreferrer");
|
|
}
|
|
} catch {
|
|
if (popup) popup.location.replace(baseUrl);
|
|
else window.open(baseUrl, "_blank", "noopener,noreferrer");
|
|
} finally {
|
|
setHandoffBusy(false);
|
|
setOpen(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div ref={menuRef} className="relative">
|
|
<button
|
|
type="button"
|
|
onClick={() => setOpen((value) => !value)}
|
|
aria-expanded={open}
|
|
aria-haspopup="menu"
|
|
className={`flex max-w-[12rem] items-center gap-2 rounded-full px-3 py-2 text-sm hover:bg-zinc-100 ${
|
|
compact ? "w-full justify-between px-0 hover:bg-transparent" : ""
|
|
}`}
|
|
>
|
|
<span className="flex min-w-0 items-center gap-2">
|
|
<IconUser />
|
|
<span className="truncate font-medium">
|
|
{displayName} عزیز
|
|
</span>
|
|
</span>
|
|
<IconChevron className={`w-3.5 h-3.5 shrink-0 transition-transform ${open ? "rotate-90" : "-rotate-90"}`} />
|
|
</button>
|
|
|
|
{open && (
|
|
<div
|
|
role="menu"
|
|
className={`absolute z-50 mt-2 min-w-[11rem] overflow-hidden rounded-2xl border border-black/5 bg-white py-1 shadow-xl ${
|
|
compact ? "start-0 end-0" : "start-0"
|
|
}`}
|
|
>
|
|
<a role="menuitem" href={customerDashboardUrl("/profile")} className={itemClass} onClick={() => setOpen(false)}>
|
|
پروفایل من
|
|
</a>
|
|
<a role="menuitem" href={customerDashboardUrl("/orders")} className={itemClass} onClick={() => setOpen(false)}>
|
|
سفارشات من
|
|
</a>
|
|
{isWebsiteAdmin && (
|
|
<a
|
|
role="menuitem"
|
|
href={businessDashboardUrl()}
|
|
className={itemClass}
|
|
onClick={openBusinessDashboard}
|
|
>
|
|
{handoffBusy ? "در حال انتقال…" : "مدیریت وبسایت"}
|
|
</a>
|
|
)}
|
|
<button
|
|
type="button"
|
|
role="menuitem"
|
|
className={`${itemClass} text-brand hover:text-[#e51f1f]`}
|
|
onClick={() => {
|
|
setOpen(false);
|
|
onLogout();
|
|
}}
|
|
>
|
|
خروج
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|