134 lines
4.1 KiB
TypeScript
134 lines
4.1 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { usePathname, useRouter } from "next/navigation";
|
|
import { useState } from "react";
|
|
import AccountMenu from "@/components/AccountMenu";
|
|
import { useAuth } from "@/components/AuthProvider";
|
|
import { customerNavItems } from "@/data/customer-nav";
|
|
import { en } from "@/lib/i18n/en";
|
|
import { getAdminDashboardOrigin } from "@/lib/host";
|
|
import { openBusinessDashboardAsUser } from "@/lib/session-handoff";
|
|
import { getDisplayName } from "@/lib/userName";
|
|
import styles from "./DashboardShell.module.css";
|
|
|
|
const websiteUrl =
|
|
process.env.NEXT_PUBLIC_WEBSITE_URL?.replace(/\/$/, "") ||
|
|
"http://localhost:3000";
|
|
|
|
export default function CustomerShell({
|
|
children,
|
|
}: {
|
|
children: React.ReactNode;
|
|
}) {
|
|
const pathname = usePathname();
|
|
const router = useRouter();
|
|
const { user, logout, isBusinessOwner, isSuperAdmin } = useAuth();
|
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
const displayName = getDisplayName(user);
|
|
|
|
const publicPath = pathname.startsWith("/customer")
|
|
? pathname.replace(/^\/customer/, "") || "/"
|
|
: pathname;
|
|
|
|
return (
|
|
<div className={styles.shell}>
|
|
<aside className={`${styles.sidebar} ${menuOpen ? styles.sidebarOpen : ""}`}>
|
|
<div className={styles.brand}>
|
|
<img src="/images/icons/logo.png" alt="" width={48} height={48} />
|
|
<div className={styles.brandText}>
|
|
<span>{en.brandName}</span>
|
|
<strong>{en.customerBrandSubtitle}</strong>
|
|
</div>
|
|
</div>
|
|
|
|
<nav className={styles.nav} aria-label="Account">
|
|
{customerNavItems.map((item) => {
|
|
const active =
|
|
item.href === "/"
|
|
? publicPath === "/"
|
|
: publicPath === item.href ||
|
|
publicPath.startsWith(`${item.href}/`);
|
|
return (
|
|
<Link
|
|
key={item.label}
|
|
href={item.href}
|
|
className={`${styles.navItem} ${active ? styles.navItemActive : ""}`}
|
|
onClick={() => setMenuOpen(false)}
|
|
>
|
|
<img src={item.icon} alt="" width={18} height={18} />
|
|
<span>{item.label}</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
<div className={styles.sidebarFoot}>
|
|
<p className={styles.userLabel}>{displayName}</p>
|
|
{isSuperAdmin ? (
|
|
<a
|
|
href={`${getAdminDashboardOrigin()}/`}
|
|
className={styles.siteLink}
|
|
>
|
|
{en.openAdminDashboard}
|
|
</a>
|
|
) : null}
|
|
{isBusinessOwner ? (
|
|
<button
|
|
type="button"
|
|
className={styles.siteLink}
|
|
onClick={() => openBusinessDashboardAsUser("/profile")}
|
|
>
|
|
{en.openBusinessDashboard}
|
|
</button>
|
|
) : null}
|
|
<a href={websiteUrl} className={styles.siteLink}>
|
|
{en.viewWebsite}
|
|
</a>
|
|
<button
|
|
type="button"
|
|
className={`outline-btn ${styles.logoutBtn}`}
|
|
onClick={() => {
|
|
logout();
|
|
router.replace("/login");
|
|
}}
|
|
>
|
|
{en.logOut}
|
|
</button>
|
|
</div>
|
|
</aside>
|
|
|
|
{menuOpen ? (
|
|
<button
|
|
type="button"
|
|
className={styles.backdrop}
|
|
aria-label="Close menu"
|
|
onClick={() => setMenuOpen(false)}
|
|
/>
|
|
) : null}
|
|
|
|
<div className={styles.main}>
|
|
<header className={styles.topbar}>
|
|
<button
|
|
type="button"
|
|
className={styles.menuBtn}
|
|
aria-label="Open menu"
|
|
onClick={() => setMenuOpen(true)}
|
|
>
|
|
<img src="/images/icons/menu.svg" alt="" width={22} height={22} />
|
|
</button>
|
|
<div className={styles.topbarCopy}>
|
|
<p className={styles.eyebrow}>{en.customerEyebrow}</p>
|
|
<p className={styles.topbarHint}>{en.customerHint}</p>
|
|
</div>
|
|
<div className={styles.topbarActions}>
|
|
<AccountMenu />
|
|
</div>
|
|
</header>
|
|
|
|
<div className={styles.content}>{children}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|