plans
2026-05-05
2026 05 05 Admin Dashboard Redesign

Admin Dashboard Pages Redesign

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Design each admin dashboard page with consistent layout, proper spacing, and professional UI matching the premium gold accent design system.

Architecture: Each page follows a consistent structure with AdminCard wrappers, StatBadge status indicators, and proper spacing using Tailwind.

Tech Stack: Next.js, Tailwind CSS, shadcn/ui components, lucide-react icons


File Inventory

Existing Components (to enhance)

  • apps/frontend/components/admin/metric-card.tsx - Metric display cards
  • apps/frontend/components/admin/sidebar.tsx - Left navigation sidebar
  • apps/frontend/components/admin/reservation-filters.tsx - Filter controls
  • apps/frontend/components/admin/show-table-row.tsx - Table row with status

New Components to Create

  • apps/frontend/components/admin/admin-card.tsx - Card wrapper with header/content
  • apps/frontend/components/admin/stat-badge.tsx - Colored status badge
  • apps/frontend/components/admin/page-header.tsx - Page title + actions header

Task 1: Create AdminCard Component

Files:

  • Create: apps/frontend/components/admin/admin-card.tsx

  • Step 1: Create AdminCard component

// apps/frontend/components/admin/admin-card.tsx
// ipsoc checked: 2026-05-05
// SoC: Pure presentation - card container for dashboard sections
 
import { cn } from "~/lib/utils";
 
interface AdminCardProps {
  title?: string;
  children: React.ReactNode;
  className?: string;
  action?: React.ReactNode;
}
 
export function AdminCard({
  title,
  children,
  className,
  action,
}: AdminCardProps) {
  return (
    <div
      className={cn(
        "bg-[#1a1a1a] border border-[#333333] rounded-lg",
        className,
      )}
    >
      {title && (
        <div className="flex items-center justify-between px-4 py-3 border-b border-[#333333]">
          <h3 className="text-lg font-serif text-[#e6e6e6]">{title}</h3>
          {action}
        </div>
      )}
      <div className="p-4">{children}</div>
    </div>
  );
}

Task 2: Create StatBadge Component

Files:

  • Create: apps/frontend/components/admin/stat-badge.tsx

  • Step 1: Create StatBadge component

// apps/frontend/components/admin/stat-badge.tsx
// ipsoc checked: 2026-05-05
// SoC: Pure presentation - colored status badge
 
import { cn } from "~/lib/utils";
 
type BadgeVariant = "success" | "warning" | "danger" | "neutral" | "gold";
 
interface StatBadgeProps {
  children: React.ReactNode;
  variant?: BadgeVariant;
  className?: string;
}
 
const variantStyles: Record<BadgeVariant, string> = {
  success: "bg-green-500/20 text-green-400 border-green-500/30",
  warning: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30",
  danger: "bg-red-500/20 text-red-400 border-red-500/30",
  neutral: "bg-[#2E2E2E] text-[#808080] border-[#333333]",
  gold: "bg-[#C5A059]/20 text-[#C5A059] border-[#C5A059]/30",
};
 
export function StatBadge({
  children,
  variant = "neutral",
  className,
}: StatBadgeProps) {
  return (
    <span
      className={cn(
        "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium border",
        variantStyles[variant],
        className,
      )}
    >
      {children}
    </span>
  );
}

Task 3: Create PageHeader Component

Files:

  • Create: apps/frontend/components/admin/page-header.tsx

  • Step 1: Create PageHeader component

// apps/frontend/components/admin/page-header.tsx
// ipsoc checked: 2026-05-05
// SoC: Pure presentation - consistent page header
 
import { cn } from "~/lib/utils";
 
interface PageHeaderProps {
  title: string;
  subtitle?: string;
  actions?: React.ReactNode;
  className?: string;
}
 
export function PageHeader({
  title,
  subtitle,
  actions,
  className,
}: PageHeaderProps) {
  return (
    <div className={cn("flex items-center justify-between", className)}>
      <div>
        <h1 className="text-2xl font-serif text-[#C5A059]">{title}</h1>
        {subtitle && <p className="text-sm text-[#808080] mt-1">{subtitle}</p>}
      </div>
      {actions && <div className="flex items-center gap-2">{actions}</div>}
    </div>
  );
}

Task 4: Update Dashboard Overview Page

Files:

  • Modify: apps/frontend/app/[locale]/dashboard/admin/page.tsx

  • Step 1: Read current admin dashboard page

cat apps/frontend/app/[locale]/dashboard/admin/page.tsx
  • Step 2: Update imports to use new components

Add imports for AdminCard, StatBadge, PageHeader.

  • Step 3: Wrap AtRiskList in AdminCard
<AdminCard title={t("atRiskOccurrences")}>
  <AtRiskList occurrences={atRiskOccurrences ?? []} />
</AdminCard>
  • Step 4: Wrap RecentReservations in AdminCard
<AdminCard title={t("recentReservations")}>
  <RecentReservations reservations={recentReservations ?? []} />
</AdminCard>

Task 5: Update Shows Page

Files:

  • Modify: apps/frontend/app/[locale]/dashboard/admin/shows/page.tsx

  • Step 1: Read current shows page

cat apps/frontend/app/[locale]/dashboard/admin/shows/page.tsx
  • Step 2: Add PageHeader with Add button
import { PageHeader } from "~/components/admin/page-header";
 
<PageHeader
  title={t("title")}
  actions={
    <Button variant="outlineGold" size="sm" onClick={handleAddNew}>
      <Plus className="w-4 h-4 mr-2" />
      {t("addNew")}
    </Button>
  }
/>;
  • Step 3: Wrap table in AdminCard
<AdminCard>
  <Table>...</Table>
</AdminCard>

Task 6: Update Analytics Page

Files:

  • Modify: apps/frontend/app/[locale]/dashboard/admin/analytics/page.tsx

  • Step 1: Read current analytics page

cat apps/frontend/app/[locale]/dashboard/admin/analytics/page.tsx
  • Step 2: Add PageHeader
import { PageHeader } from "~/components/admin/page-header";
 
<PageHeader title={t("title")} />;
  • Step 3: Wrap revenue section in AdminCard
<AdminCard title={t("revenueByShow")}>
  <div className="space-y-2">
    {topShows?.map((show: TopShowRevenue, idx: number) => (
      <div
        key={show.showTitle}
        className="flex justify-between items-center p-3 bg-[#2E2E2E] rounded"
      >
        <span className="text-[#e6e6e6]">
          {idx + 1}. {show.showTitle}
        </span>
        <span className="text-[#C5A059] font-serif">
          {show.revenue.toLocaleString()} VND
        </span>
      </div>
    ))}
  </div>
</AdminCard>

Task 7: Update Occurrences Page

Files:

  • Modify: apps/frontend/app/[locale]/dashboard/admin/occurrences/page.tsx

  • Step 1: Read current occurrences page

cat apps/frontend/app/[locale]/dashboard/admin/occurrences/page.tsx
  • Step 2: Add PageHeader with batch create button
import { PageHeader } from "~/components/admin/page-header";
 
<PageHeader
  title={t("title")}
  actions={
    <LocaleLink href="/dashboard/admin/occurrences/batch">
      <Button variant="outlineGold" size="sm">
        <Plus className="w-4 h-4 mr-2" />
        {t("batchCreate")}
      </Button>
    </LocaleLink>
  }
/>;

Task 8: Update Checkin Page

Files:

  • Modify: apps/frontend/app/[locale]/dashboard/admin/checkin/page.tsx

  • Step 1: Read current checkin page

cat apps/frontend/app/[locale]/dashboard/admin/checkin/page.tsx
  • Step 2: Wrap scanner section in AdminCard
<AdminCard title="Scanner">{/* Scanner content */}</AdminCard>
  • Step 3: Wrap ticket detail in AdminCard
<AdminCard title="Ticket Details">{/* Ticket detail content */}</AdminCard>

Task 9: Update POS Reception Page

Files:

  • Modify: apps/frontend/app/[locale]/dashboard/pos/reception/page.tsx

  • Step 1: Read current reception page

cat apps/frontend/app/[locale]/dashboard/pos/reception/page.tsx
  • Step 2: Add PageHeader
<PageHeader title={t("pos.reception.title")} />
  • Step 3: Wrap floor plan in AdminCard
<AdminCard>
  <Suspense fallback={<ReceptionLoading />}>
    <FloorPlan />
  </Suspense>
</AdminCard>

Task 10: Update POS Kitchen Page

Files:

  • Modify: apps/frontend/app/[locale]/dashboard/pos/kitchen/page.tsx

  • Step 1: Read current kitchen page

cat apps/frontend/app/[locale]/dashboard/pos/kitchen/page.tsx
  • Step 2: Add PageHeader with station tabs
<PageHeader
  title={t("pos.kitchen.title")}
  actions={
    <div className="flex gap-2">
      {STATIONS.map((station) => (
        <a key={station.key} href={`?station=${station.key}`}>
          <Button
            variant={currentStation === station.key ? "default" : "outlineGold"}
            size="sm"
          >
            {t(station.labelKey)}
          </Button>
        </a>
      ))}
    </div>
  }
/>

Verification

After all tasks, verify each page:

  1. /dashboard - Metric cards + AtRiskList + RecentReservations with AdminCard wrappers
  2. /dashboard/admin/shows - Table in AdminCard with PageHeader
  3. /dashboard/admin/occurrences - Calendar with PageHeader
  4. /dashboard/admin/reservations - Filters + Table in AdminCard
  5. /dashboard/admin/checkin - Scanner + Ticket detail in AdminCards
  6. /dashboard/admin/addons - Addon cards in AdminCard
  7. /dashboard/admin/analytics - Revenue list in AdminCard with PageHeader
  8. /dashboard/pos/reception - Floor plan in AdminCard with PageHeader
  9. /dashboard/pos/kitchen - Kitchen board with PageHeader + station tabs
  10. /dashboard/pos/staff - Table selection grid + ordering view