Files
whatsapp-inbox-platform/components/app-shell.tsx
Wira Basalamah adde003fba
Some checks failed
CI - Production Readiness / Verify (push) Has been cancelled
chore: initial project import
2026-04-21 09:29:29 +07:00

177 lines
7.0 KiB
TypeScript

import Image from "next/image";
import Link from "next/link";
import { ReactNode } from "react";
import { getLocale, getTranslator, type NavKey } from "@/lib/i18n";
import type { NavItem } from "@/lib/mock-data";
type ShellContext = {
userName: string;
roleLabel: string;
tenantName: string;
};
const navIconByKey: Record<NavKey, string> = {
dashboard: "dashboard",
shared_inbox: "inbox",
inbox: "inbox",
contacts: "contacts",
broadcast: "campaign",
templates: "article",
team: "group",
reports: "bar_chart",
settings: "settings",
billing: "payments",
audit_log: "history",
tenants: "domain",
channels: "settings_input_antenna",
security_events: "notifications_active",
webhook_logs: "webhook",
alerts: "warning",
quick_tools: "auto_fix_high",
performance: "trending_up",
new_chat: "chat",
search: "search",
logout: "logout",
global_search: "search",
campaign: "campaign"
};
function initials(name: string) {
return name
.split(" ")
.filter(Boolean)
.map((part) => part[0]?.toUpperCase())
.slice(0, 2)
.join("");
}
function LocaleSwitcher({ locale }: { locale: "id" | "en" }) {
const nextLocale = locale === "id" ? "en" : "id";
return (
<Link
href={`/locale?to=${nextLocale}`}
className="rounded-full border border-line bg-surface-container px-3 py-1 text-xs font-semibold text-on-surface"
>
{nextLocale.toUpperCase()}
</Link>
);
}
export async function AppShell({
title,
subtitle,
nav,
context,
children
}: {
title: string;
subtitle: string;
nav: NavItem[];
context: ShellContext;
children: ReactNode;
}) {
const locale = await getLocale();
const t = getTranslator(locale);
return (
<div className="min-h-screen bg-background text-on-surface">
<div className="mx-auto flex min-h-screen max-w-[1700px]">
<aside className="hidden w-[268px] shrink-0 flex-col space-y-4 border-r border-line bg-surface-container-low px-4 py-6 lg:flex">
<div className="mb-6 rounded-[1.25rem] bg-surface-container-lowest px-3 py-3 shadow-sm">
<div className="flex items-center gap-3 px-2">
<Image
src="/logo_zappcare.png"
alt="ZappCare"
width={36}
height={36}
className="h-9 w-auto rounded-full"
/>
<div>
<p className="text-xs font-extrabold uppercase tracking-[0.24em] text-outline">{t("common", "zappcare")}</p>
<p className="text-lg font-black leading-tight font-headline text-on-surface">{t("common", "business_suite")}</p>
</div>
</div>
</div>
<div className="px-2">
<button className="flex w-full items-center justify-center gap-2 rounded-full bg-gradient-to-br from-primary to-primary-container px-4 py-3 text-sm font-bold text-white">
<span className="material-symbols-outlined text-sm">add_comment</span>
<span>{t("nav", "new_chat")}</span>
</button>
</div>
<nav className="space-y-1">
{nav.map((item) => {
const label = t("nav", item.labelKey);
const isDashboard = item.labelKey === "dashboard";
return (
<Link
key={item.href}
href={item.href}
className={`flex items-center gap-3 rounded-xl px-4 py-2.5 text-sm font-semibold font-headline transition-all ${
isDashboard
? "bg-primary-container/40 text-on-primary-container"
: "text-on-surface-variant hover:bg-surface-container-high hover:text-on-surface"
}`}
>
<span
className="material-symbols-outlined text-sm"
style={{ fontVariationSettings: "'FILL' 1", fontSize: "20px" }}
>
{navIconByKey[item.labelKey]}
</span>
<span>{label}</span>
</Link>
);
})}
</nav>
<div className="mt-auto border-t border-line px-2 pt-5">
<Link
href="/auth/logout"
className="flex items-center gap-3 rounded-xl px-4 py-2.5 text-sm font-semibold text-on-surface-variant transition hover:text-on-surface"
>
<span className="material-symbols-outlined text-sm">logout</span>
<span>{t("nav", "logout")}</span>
</Link>
</div>
</aside>
<div className="flex min-h-screen flex-1 flex-col">
<header className="border-b border-line bg-surface-container-lowest/85 backdrop-blur">
<div className="flex flex-wrap items-center justify-between gap-4 px-5 py-4 md:px-7">
<div>
<p className="text-xs font-bold uppercase tracking-[0.2em] text-outline">{subtitle}</p>
<h1 className="text-2xl font-black font-headline text-on-surface">{title}</h1>
</div>
<div className="flex items-center gap-3">
<div className="relative hidden w-72 rounded-full border border-line bg-surface-container-low px-4 py-2 md:block">
<span className="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-outline text-sm">search</span>
<input
placeholder={t("common", "search_placeholder")}
className="w-full border-none bg-transparent pl-8 pr-2 text-sm outline-none placeholder:text-outline-variant"
/>
</div>
<LocaleSwitcher locale={locale} />
<button className="flex h-9 w-9 items-center justify-center rounded-full text-outline transition hover:bg-surface-container-low">
<span className="material-symbols-outlined text-sm">notifications</span>
</button>
<button className="flex h-9 w-9 items-center justify-center rounded-full text-outline transition hover:bg-surface-container-low">
<span className="material-symbols-outlined text-sm">help_outline</span>
</button>
<button className="flex h-9 w-9 items-center justify-center rounded-full text-outline transition hover:bg-surface-container-low">
<span className="material-symbols-outlined text-sm">app_shortcut</span>
</button>
<button className="ml-2 flex h-9 items-center justify-center rounded-full border border-line bg-surface-container px-3 text-xs font-bold text-on-surface-variant">
{initials(context.userName)}
</button>
</div>
</div>
<div className="px-7 pb-4 text-sm text-on-surface-variant">
{context.userName} {context.roleLabel} {context.tenantName}
</div>
</header>
<main className="min-h-0 flex-1 px-6 py-6 md:px-8">{children}</main>
</div>
</div>
</div>
);
}