quest-vorteq/src/app/(portal)/inventory/page.tsx
Lorentz 7774af03a5
Some checks failed
Build and Deploy / build (push) Failing after 10m59s
Build and Deploy / deploy (push) Has been skipped
feat(C-001,C-002): implement dashboard and begin inventory views
Implements complete dashboard with metrics, recent activity, and quick navigation.
Begins inventory system with service layer and category selector.

Dashboard (C-001) - Complete:
- Server component with parallel data fetching
- Summary cards: WIP, Finished Goods, Total Weight, Recent Orders
- Recent activity tables: top 5 orders and shipments
- Alert banner for important notifications
- Quick navigation cards to all major features
- Loading skeletons with Suspense boundaries
- Reusable components: SummaryCard, QuickNavCard, tables
- Dashboard service with typed Epicor queries

Inventory (C-002) - Core Complete:
- Complete service layer for all 6 inventory categories
- Category selector page with permission-based visibility
- Reusable inventory summary table with search and CSV export
- WIP inventory page as template for other categories
- Sub-user permission enforcement at service layer
- Type-safe stored procedure calls with proper error handling

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-16 11:41:56 +00:00

110 lines
3 KiB
TypeScript

import {
Package,
Cog,
CheckCircle,
Circle,
RotateCcw,
Recycle,
} from 'lucide-react';
import { Card, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import Link from 'next/link';
import { isSubUser } from '@/lib/permissions';
const inventoryCategories = [
{
id: 'wip',
title: 'Work in Progress',
description: 'Active jobs and materials being processed',
icon: Cog,
color: 'text-blue-600',
href: '/inventory/wip',
requiresNonSubUser: false,
},
{
id: 'finished-goods',
title: 'Finished Goods',
description: 'Completed products ready for shipment',
icon: CheckCircle,
color: 'text-green-600',
href: '/inventory/finished-goods',
requiresNonSubUser: false,
},
{
id: 'processed-other',
title: 'Processed Other',
description: 'Other processed inventory items',
icon: Package,
color: 'text-purple-600',
href: '/inventory/processed-other',
requiresNonSubUser: false,
},
{
id: 'unprocessed',
title: 'Unprocessed',
description: 'Raw materials awaiting processing',
icon: Circle,
color: 'text-orange-600',
href: '/inventory/unprocessed',
requiresNonSubUser: true,
},
{
id: 'unprocessed-rr',
title: 'Unprocessed R&R',
description: 'Unprocessed returns and repairs',
icon: RotateCcw,
color: 'text-amber-600',
href: '/inventory/unprocessed-rr',
requiresNonSubUser: true,
},
{
id: 'processed-rr',
title: 'Processed R&R',
description: 'Processed returns and repairs',
icon: Recycle,
color: 'text-teal-600',
href: '/inventory/processed-rr',
requiresNonSubUser: true,
},
];
export default async function InventoryPage() {
const userIsSubUser = await isSubUser();
const availableCategories = inventoryCategories.filter(
(cat) => !cat.requiresNonSubUser || !userIsSubUser
);
return (
<div>
<h1 className="mb-2 text-3xl font-bold">Inventory</h1>
<p className="mb-8 text-muted-foreground">
Browse inventory by category
</p>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{availableCategories.map((category) => (
<Link key={category.id} href={category.href}>
<Card className="transition-all hover:border-primary/50 hover:shadow-md">
<CardHeader>
<div className="mb-4 flex items-center justify-between">
<category.icon className={`h-10 w-10 ${category.color}`} />
</div>
<CardTitle className="mb-2">{category.title}</CardTitle>
<CardDescription>{category.description}</CardDescription>
</CardHeader>
</Card>
</Link>
))}
</div>
{userIsSubUser && (
<div className="mt-6 rounded-lg border border-amber-200 bg-amber-50 p-4">
<p className="text-sm text-amber-800">
<strong>Note:</strong> Some inventory categories are restricted
for sub-users.
</p>
</div>
)}
</div>
);
}