diff --git a/frontend/app/(auth)/admin/dashboard/DashboardClient.tsx b/frontend/app/(auth)/admin/dashboard/DashboardClient.tsx
new file mode 100644
index 0000000..438907f
--- /dev/null
+++ b/frontend/app/(auth)/admin/dashboard/DashboardClient.tsx
@@ -0,0 +1,131 @@
+"use client";
+
+import { useQuery } from "@tanstack/react-query";
+import { adminApi } from "@/lib/api/admin";
+import { useAuthStore } from "@/lib/store/authStore";
+import { AdminRole } from "@/lib/types/user";
+import RoleProtectedPage from "@/app/components/admin/RoleProtectedPage";
+import PopularItemsChart from "@/app/components/admin/dashboard/PopularItemsChart";
+import RecentActivity from "@/app/components/admin/dashboard/RecentActivity";
+
+function StatCard({ label, value }: { label: string; value: number | string }) {
+ return (
+
+ );
+}
+
+function QuickActions() {
+ const links = [
+ { label: "Add Menu Item", href: "/admin/menu/new" },
+ { label: "View Contacts", href: "/admin/contacts" },
+ { label: "Manage Users", href: "/admin/users" },
+ ];
+ return (
+
+ );
+}
+
+export default function DashboardClient() {
+ const user = useAuthStore((s) => s.user);
+ const isStaff = user?.role === AdminRole.STAFF;
+
+ const {
+ data: stats,
+ isLoading: loadingStats,
+ error: statsError,
+ } = useQuery({
+ queryKey: ["admin-dashboard"],
+ queryFn: adminApi.getDashboard,
+ staleTime: 60_000,
+ });
+
+ const {
+ data: analytics,
+ isLoading: loadingAnalytics,
+ error: analyticsError,
+ } = useQuery({
+ queryKey: ["menu-analytics"],
+ queryFn: adminApi.getMenuAnalytics,
+ staleTime: 60_000,
+ });
+
+ const {
+ data: auditLogs,
+ isLoading: loadingLogs,
+ error: logsError,
+ } = useQuery({
+ queryKey: ["audit-logs", 10],
+ queryFn: () => adminApi.getAuditLogs(10),
+ staleTime: 30_000,
+ });
+
+ const isLoading = loadingStats || loadingAnalytics || loadingLogs;
+ const error = statsError || analyticsError || logsError;
+
+ return (
+
+
+
Dashboard
+
+ {error && (
+
+ Failed to load dashboard data.
+
+ )}
+
+ {isLoading ? (
+
+ {Array.from({ length: 4 }).map((_, i) => (
+
+ ))}
+
+ ) : (
+
+
+
+
+ {!isStaff && (
+
+ )}
+
+ )}
+
+
+ {loadingAnalytics ? (
+
+ ) : (
+
+ )}
+
+
+
+ {loadingLogs ? (
+
+ ) : (
+
+ )}
+
+
+ );
+}
diff --git a/frontend/app/(auth)/admin/dashboard/page.tsx b/frontend/app/(auth)/admin/dashboard/page.tsx
new file mode 100644
index 0000000..134d7f5
--- /dev/null
+++ b/frontend/app/(auth)/admin/dashboard/page.tsx
@@ -0,0 +1,8 @@
+import { Metadata } from "next";
+import DashboardClient from "./DashboardClient";
+
+export const metadata: Metadata = { title: "Dashboard" };
+
+export default function DashboardPage() {
+ return ;
+}
diff --git a/frontend/app/(auth)/admin/menu/MenuClient.tsx b/frontend/app/(auth)/admin/menu/MenuClient.tsx
new file mode 100644
index 0000000..b2805b6
--- /dev/null
+++ b/frontend/app/(auth)/admin/menu/MenuClient.tsx
@@ -0,0 +1,83 @@
+"use client";
+
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
+import Link from "next/link";
+import { adminApi } from "@/lib/api/admin";
+import MenuTable from "@/app/components/admin/menu/MenuTable";
+
+function StatCard({ label, value }: { label: string; value: number }) {
+ return (
+
+ );
+}
+
+export default function MenuClient() {
+ const queryClient = useQueryClient();
+
+ const { data: items = [], isLoading, error } = useQuery({
+ queryKey: ["admin-menu-items"],
+ queryFn: adminApi.getAllMenuItems,
+ staleTime: 60_000,
+ });
+
+ const toggleMutation = useMutation({
+ mutationFn: (id: string) => adminApi.toggleMenuItemAvailability(id),
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ["admin-menu-items"] }),
+ });
+
+ const deleteMutation = useMutation({
+ mutationFn: (id: string) => adminApi.deleteMenuItem(id),
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ["admin-menu-items"] }),
+ });
+
+ const total = items.length;
+ const available = items.filter((i: any) => i.isAvailable).length;
+ const unavailable = total - available;
+
+ return (
+
+
+
Menu Management
+
+ Add Item
+
+
+
+ {error && (
+
+ Failed to load menu items.
+
+ )}
+
+ {isLoading ? (
+
+ {Array.from({ length: 3 }).map((_, i) => (
+
+ ))}
+
+ ) : (
+
+
+
+
+
+ )}
+
+ {isLoading ? (
+
+ ) : (
+
toggleMutation.mutate(id)}
+ onDelete={(id) => deleteMutation.mutateAsync(id)}
+ />
+ )}
+
+ );
+}
diff --git a/frontend/app/(auth)/admin/menu/page.tsx b/frontend/app/(auth)/admin/menu/page.tsx
new file mode 100644
index 0000000..8e2393e
--- /dev/null
+++ b/frontend/app/(auth)/admin/menu/page.tsx
@@ -0,0 +1,8 @@
+import { Metadata } from "next";
+import MenuClient from "./MenuClient";
+
+export const metadata: Metadata = { title: "Menu" };
+
+export default function MenuPage() {
+ return ;
+}
diff --git a/frontend/app/(auth)/admin/users/UsersClient.tsx b/frontend/app/(auth)/admin/users/UsersClient.tsx
new file mode 100644
index 0000000..a3f7d34
--- /dev/null
+++ b/frontend/app/(auth)/admin/users/UsersClient.tsx
@@ -0,0 +1,105 @@
+"use client";
+
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
+import Link from "next/link";
+import { adminApi } from "@/lib/api/admin";
+import { useAuthStore } from "@/lib/store/authStore";
+import { AdminRole, AdminUser } from "@/lib/types/user";
+import RoleProtectedPage from "@/app/components/admin/RoleProtectedPage";
+import UserTable from "@/app/components/admin/users/UserTable";
+
+function StatCard({ label, value }: { label: string; value: number }) {
+ return (
+
+ );
+}
+
+export default function UsersClient() {
+ const queryClient = useQueryClient();
+ const user = useAuthStore((s) => s.user);
+ const isSuperAdmin = user?.role === AdminRole.SUPER_ADMIN;
+
+ const { data: allUsers = [], isLoading, error } = useQuery({
+ queryKey: ["admin-users"],
+ queryFn: adminApi.getAdmins,
+ staleTime: 60_000,
+ });
+
+ const users = isSuperAdmin
+ ? allUsers
+ : allUsers.filter((u) => u.role !== AdminRole.SUPER_ADMIN);
+
+ const toggleMutation = useMutation({
+ mutationFn: ({ id, isActive }: { id: string; isActive: boolean }) =>
+ adminApi.toggleAdminStatus(id, isActive),
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ["admin-users"] }),
+ });
+
+ const roleMutation = useMutation({
+ mutationFn: ({ id, role }: { id: string; role: AdminRole }) =>
+ adminApi.updateAdminRole(id, role),
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ["admin-users"] }),
+ });
+
+ const total = users.length;
+ const active = users.filter((u) => u.isActive).length;
+ const inactive = total - active;
+ const adminCount = users.filter(
+ (u) => u.role === AdminRole.ADMIN || u.role === AdminRole.SUPER_ADMIN
+ ).length;
+
+ return (
+
+
+
+
Users Management
+ {isSuperAdmin && (
+
+ Add User
+
+ )}
+
+
+ {error && (
+
+ Failed to load users.
+
+ )}
+
+ {isLoading ? (
+
+ {Array.from({ length: 4 }).map((_, i) => (
+
+ ))}
+
+ ) : (
+
+
+
+
+
+
+ )}
+
+ {isLoading ? (
+
+ ) : (
+
toggleMutation.mutate({ id, isActive })}
+ onRoleChange={(id, role) => roleMutation.mutate({ id, role })}
+ />
+ )}
+
+
+ );
+}
diff --git a/frontend/app/(auth)/admin/users/new/page.tsx b/frontend/app/(auth)/admin/users/new/page.tsx
new file mode 100644
index 0000000..1b3871d
--- /dev/null
+++ b/frontend/app/(auth)/admin/users/new/page.tsx
@@ -0,0 +1,139 @@
+"use client";
+
+import { useState } from "react";
+import { useRouter } from "next/navigation";
+import { useForm } from "react-hook-form";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { z } from "zod";
+import { useMutation } from "@tanstack/react-query";
+import { Eye, EyeOff } from "lucide-react";
+import { adminApi } from "@/lib/api/admin";
+import { AdminRole } from "@/lib/types/user";
+
+const schema = z.object({
+ username: z.string().min(1, "Username is required"),
+ email: z.string().email("Valid email required"),
+ password: z.string().min(8, "Password must be at least 8 characters"),
+ role: z.enum([AdminRole.STAFF, AdminRole.MANAGER, AdminRole.ADMIN], {
+ required_error: "Role is required",
+ }),
+});
+
+type FormData = z.infer;
+
+const rolePermissions = [
+ { role: "Staff", desc: "View orders, manage table status, limited menu access." },
+ { role: "Manager", desc: "All Staff permissions + manage menu items and view reports." },
+ { role: "Admin", desc: "All Manager permissions + manage users and system settings." },
+];
+
+export default function CreateUserPage() {
+ const router = useRouter();
+ const [showPassword, setShowPassword] = useState(false);
+
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ } = useForm({ resolver: zodResolver(schema) });
+
+ const mutation = useMutation({
+ mutationFn: (data: FormData) => adminApi.createUser(data),
+ onSuccess: () => {
+ alert("User created successfully!");
+ router.push("/admin/users");
+ },
+ onError: (err: any) => {
+ alert(err?.message ?? "Failed to create user.");
+ },
+ });
+
+ return (
+
+
Create Admin User
+
+
+
+
+
Role Permissions
+
+ {rolePermissions.map(({ role, desc }) => (
+ -
+ {role}:
+ {desc}
+
+ ))}
+
+
+
+ );
+}
diff --git a/frontend/app/(auth)/admin/users/page.tsx b/frontend/app/(auth)/admin/users/page.tsx
new file mode 100644
index 0000000..8734132
--- /dev/null
+++ b/frontend/app/(auth)/admin/users/page.tsx
@@ -0,0 +1,8 @@
+import { Metadata } from "next";
+import UsersClient from "./UsersClient";
+
+export const metadata: Metadata = { title: "Users" };
+
+export default function UsersPage() {
+ return ;
+}