fix: better frontend (#43)
Release package / release (push) Successful in 4m38s

Co-authored-by: Thanh Quy - wolf <524H0124@student.tdtu.edu.vn>
Co-authored-by: Thanh Quy- wolf <524H0124@student.tdtu.edu.vn>
Reviewed-on: #43
This commit was merged in pull request #43.
This commit is contained in:
2026-05-14 15:52:48 +00:00
parent 378e381454
commit b37bf5d088
38 changed files with 1849 additions and 1053 deletions
+204 -36
View File
@@ -1,13 +1,16 @@
"use client";
import { ProductsTab } from "@/components/organisms/manager";
import { ProductsTab, ReviewsTab } from "@/components/organisms/manager";
import { useAuth } from "@/lib/auth-context";
import { useManager } from "@/lib/manager-context";
import Link from "next/link";
import { useEffect, useRef, useState } from "react";
export default function ManagerPage() {
const { user, logout } = useAuth();
const { activeTab, setActiveTab, products } = useManager();
const [dropdownOpen, setDropdownOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const tabs = [
{
@@ -16,11 +19,30 @@ export default function ManagerPage() {
icon: "fa-solid fa-utensils",
count: products.length,
},
{
id: "reviews" as const,
label: "Reviews",
icon: "fa-solid fa-star",
count: null,
},
];
useEffect(() => {
function handleOutsideClick(e: MouseEvent) {
if (
dropdownRef.current &&
!dropdownRef.current.contains(e.target as Node)
) {
setDropdownOpen(false);
}
}
document.addEventListener("mousedown", handleOutsideClick);
return () => document.removeEventListener("mousedown", handleOutsideClick);
}, []);
return (
<div className="flex min-h-screen">
{/* ── Sidebar ── */}
{/* ── Sidebar (lg+) ── */}
<aside className="hidden w-64 shrink-0 flex-col border-r border-(--color-border-light) bg-white shadow-sm lg:flex">
<div className="flex items-center gap-3 border-b border-(--color-border-light) px-5 py-5">
<div className="flex h-9 w-9 items-center justify-center rounded-xl bg-(--color-primary)">
@@ -86,6 +108,18 @@ export default function ManagerPage() {
</span>
</Link>
</div>
<div className="mt-3 border-t border-(--color-border-light) pt-3">
<p className="mb-2 px-3 text-[11px] font-semibold tracking-wider text-(--color-text-muted) uppercase">
Staff
</p>
<Link
href="/manager/create-staff"
className="hover:bg-background flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium text-(--color-text-secondary) no-underline transition-all hover:text-(--color-primary-dark)"
>
<i className="fa-solid fa-user-plus w-4 text-center"></i>
<span className="flex-1 text-left">Create Staff</span>
</Link>
</div>
</nav>
<div className="border-t border-(--color-border-light) p-3">
@@ -121,50 +155,176 @@ export default function ManagerPage() {
{/* ── Main content ── */}
<div className="flex min-w-0 flex-1 flex-col">
<header className="sticky top-0 z-40 flex items-center justify-between border-b border-(--color-border-light) bg-white px-5 py-4 shadow-sm">
<div>
<h1 className="text-foreground text-lg font-bold">
<header className="sticky top-0 z-40 flex items-center justify-between gap-3 border-b border-(--color-border-light) bg-white px-5 py-4 shadow-sm">
{/* Title */}
<div className="min-w-0 shrink">
<h1 className="text-foreground truncate text-lg font-bold">
{tabs.find((t) => t.id === activeTab)?.label ?? "Manager"}
</h1>
<p className="text-xs text-(--color-text-muted)">
Manage{" "}
<p className="truncate text-xs text-(--color-text-muted)">
{activeTab === "products"
? "menu items"
: activeTab === "combos"
? "combos"
: activeTab === "categories"
? "categories"
: "menu"}{" "}
for your store
? "Manage menu items for your store"
: "Customer reviews for your eatery"}
</p>
</div>
{/* Mobile tabs */}
<div className="flex items-center gap-1 lg:hidden">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`flex cursor-pointer items-center gap-1.5 rounded-xl border-none px-3 py-2 text-xs font-medium transition ${
activeTab === tab.id
? "bg-(--color-primary) text-white"
: "bg-background text-(--color-text-secondary) hover:text-(--color-primary)"
}`}
{/* ── Actions: hidden on lg+ (sidebar handles nav there) ── */}
<div className="flex shrink-0 items-center gap-1.5 lg:hidden">
{/* smlg: inline icon+label buttons */}
<div className="hidden items-center gap-1.5 sm:flex">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`flex cursor-pointer items-center gap-1.5 rounded-xl border-none px-3 py-2 text-xs font-medium transition ${
activeTab === tab.id
? "bg-(--color-primary) text-white"
: "bg-background text-(--color-text-secondary) hover:text-(--color-primary)"
}`}
>
<i className={tab.icon}></i>
<span>{tab.label}</span>
</button>
))}
<Link
href="/manager/analytics"
className="flex items-center gap-1.5 rounded-xl border-none bg-(--color-accent-light) px-3 py-2 text-xs font-medium text-(--color-primary) no-underline transition hover:opacity-80"
>
<i className={tab.icon}></i>
<span className="hidden sm:inline">{tab.label}</span>
<i className="fa-solid fa-chart-line"></i>
<span>Finance</span>
</Link>
<Link
href="/staff/schedule"
className="hover:bg-background flex items-center gap-1.5 rounded-xl border border-(--color-border-light) bg-transparent px-3 py-2 text-xs font-medium text-(--color-text-secondary) no-underline transition hover:text-(--color-primary-dark)"
>
<i className="fa-solid fa-calendar-days"></i>
<span>Shifts</span>
</Link>
<Link
href="/manager/create-staff"
className="hover:bg-background flex items-center gap-1.5 rounded-xl border border-(--color-border-light) bg-transparent px-3 py-2 text-xs font-medium text-(--color-text-secondary) no-underline transition hover:text-(--color-primary-dark)"
>
<i className="fa-solid fa-user-plus"></i>
<span>Create Staff</span>
</Link>
<Link
href="/"
className="hover:bg-background flex items-center gap-1.5 rounded-xl border border-(--color-border-light) bg-transparent px-3 py-2 text-xs font-medium text-(--color-text-secondary) no-underline transition"
>
<i className="fa-solid fa-house"></i>
<span>Home</span>
</Link>
<button
onClick={logout}
className="flex cursor-pointer items-center gap-1.5 rounded-xl border border-red-200 bg-transparent px-3 py-2 text-xs font-medium text-red-500 transition hover:bg-red-50"
>
<i className="fa-solid fa-right-from-bracket"></i>
<span>Logout</span>
</button>
))}
<Link
href="/manager/analytics"
className="flex items-center gap-1.5 rounded-xl border-none bg-(--color-accent-light) px-3 py-2 text-xs font-medium text-(--color-primary) no-underline transition hover:bg-(--color-accent-light)/70"
>
<i className="fa-solid fa-chart-line"></i>
<span className="hidden sm:inline">Finance</span>
</Link>
</div>
{/* < sm: hamburger → dropdown */}
<div className="relative sm:hidden" ref={dropdownRef}>
<button
onClick={() => setDropdownOpen((prev) => !prev)}
aria-label="Open menu"
className="flex cursor-pointer items-center justify-center rounded-xl border border-(--color-border-light) bg-transparent p-2 text-sm text-(--color-text-secondary) transition hover:bg-(--color-border-light)"
>
<i
className={
dropdownOpen ? "fa-solid fa-xmark" : "fa-solid fa-bars"
}
></i>
</button>
{dropdownOpen && (
<div className="absolute right-0 top-full z-50 mt-2 w-52 overflow-hidden rounded-2xl border border-(--color-border-light) bg-white shadow-xl">
<div className="p-1.5">
{/* Tab buttons */}
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => {
setActiveTab(tab.id);
setDropdownOpen(false);
}}
className={`flex w-full cursor-pointer items-center gap-2.5 rounded-xl border-none px-3 py-2.5 text-sm font-medium transition ${
activeTab === tab.id
? "bg-(--color-primary) text-white"
: "bg-transparent text-(--color-text-secondary) hover:bg-(--color-border-light)"
}`}
>
<i className={`${tab.icon} w-4 text-center`}></i>
<span className="flex-1 text-left">{tab.label}</span>
{tab.count !== null && (
<span
className={`rounded-full px-2 py-0.5 text-xs font-semibold ${
activeTab === tab.id
? "bg-white/20 text-white"
: "bg-(--color-border-light) text-(--color-text-muted)"
}`}
>
{tab.count}
</span>
)}
</button>
))}
<div className="my-1.5 border-t border-(--color-border-light)"></div>
{/* Navigation links */}
<Link
href="/manager/analytics"
onClick={() => setDropdownOpen(false)}
className="flex items-center gap-2.5 rounded-xl px-3 py-2.5 text-sm font-medium text-(--color-primary) no-underline transition hover:bg-(--color-accent-light)"
>
<i className="fa-solid fa-chart-line w-4 text-center"></i>
<span>Finance</span>
</Link>
<Link
href="/staff/schedule"
onClick={() => setDropdownOpen(false)}
className="flex items-center gap-2.5 rounded-xl px-3 py-2.5 text-sm font-medium text-(--color-text-secondary) no-underline transition hover:bg-(--color-border-light)"
>
<i className="fa-solid fa-calendar-days w-4 text-center"></i>
<span>Shifts</span>
</Link>
<Link
href="/manager/create-staff"
onClick={() => setDropdownOpen(false)}
className="flex items-center gap-2.5 rounded-xl px-3 py-2.5 text-sm font-medium text-(--color-text-secondary) no-underline transition hover:bg-(--color-border-light)"
>
<i className="fa-solid fa-user-plus w-4 text-center"></i>
<span>Create Staff</span>
</Link>
<Link
href="/"
onClick={() => setDropdownOpen(false)}
className="flex items-center gap-2.5 rounded-xl px-3 py-2.5 text-sm font-medium text-(--color-text-secondary) no-underline transition hover:bg-(--color-border-light)"
>
<i className="fa-solid fa-house w-4 text-center"></i>
<span>Home</span>
</Link>
<div className="my-1.5 border-t border-(--color-border-light)"></div>
<button
onClick={() => {
setDropdownOpen(false);
logout();
}}
className="flex w-full cursor-pointer items-center gap-2.5 rounded-xl border-none bg-transparent px-3 py-2.5 text-sm font-medium text-red-500 transition hover:bg-red-50"
>
<i className="fa-solid fa-right-from-bracket w-4 text-center"></i>
<span>Logout</span>
</button>
</div>
</div>
)}
</div>
</div>
{/* Desktop actions */}
{/* ── Desktop actions (lg+): sidebar handles nav, just show shortcut ── */}
<div className="hidden items-center gap-2 lg:flex">
<Link
href="/manager/analytics"
@@ -173,6 +333,13 @@ export default function ManagerPage() {
<i className="fa-solid fa-chart-line"></i>
Financial Analytics
</Link>
<Link
href="/manager/create-staff"
className="hover:bg-background flex items-center gap-1.5 rounded-xl border border-(--color-border-light) bg-transparent px-3 py-2 text-xs font-medium text-(--color-text-secondary) no-underline transition hover:text-(--color-primary-dark)"
>
<i className="fa-solid fa-user-plus"></i>
Create Staff
</Link>
<Link
href="/"
className="hover:bg-background flex items-center gap-1.5 rounded-xl border border-(--color-border-light) bg-transparent px-3 py-2 text-xs font-medium text-(--color-text-secondary) no-underline transition"
@@ -185,6 +352,7 @@ export default function ManagerPage() {
<main className="flex-1 p-5 md:p-8">
{activeTab === "products" && <ProductsTab />}
{activeTab === "reviews" && <ReviewsTab />}
</main>
</div>
</div>