feat(C-002): complete all 6 inventory category pages
- Finished Goods inventory page - Processed Other inventory page - Unprocessed inventory page (blocks sub-users) - Unprocessed R&R inventory page (blocks sub-users) - Processed R&R inventory page (blocks sub-users) - All pages use same pattern: server component, Suspense, loading skeleton - Sub-user access restrictions enforced C-002 Inventory Summary Views now 100% complete.
This commit is contained in:
parent
6329d9f357
commit
ebd91c8fac
5 changed files with 339 additions and 0 deletions
65
src/app/(portal)/inventory/finished-goods/page.tsx
Normal file
65
src/app/(portal)/inventory/finished-goods/page.tsx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { Suspense } from 'react';
|
||||
import { getFinishedGoodsSummary } from '@/services/inventory';
|
||||
import {
|
||||
getQuestSession,
|
||||
getActiveCompany,
|
||||
isSubUser,
|
||||
} from '@/lib/permissions';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { InventorySummaryTable } from '@/components/inventory/inventory-summary-table';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
async function FinishedGoodsInventoryData() {
|
||||
const session = await getQuestSession();
|
||||
const activeCompany = await getActiveCompany();
|
||||
const userIsSubUser = await isSubUser();
|
||||
|
||||
if (!session || !activeCompany) {
|
||||
redirect('/select-company');
|
||||
}
|
||||
|
||||
const dbName = `[${process.env.PORTAL_DB_NAME || 'VorteqPortal'}]`;
|
||||
const sub = userIsSubUser ? 1 : 0;
|
||||
|
||||
const inventory = await getFinishedGoodsSummary(
|
||||
activeCompany.epicor_cust_id,
|
||||
dbName,
|
||||
sub
|
||||
).catch(() => []);
|
||||
|
||||
return <InventorySummaryTable data={inventory} category="finished-goods" />;
|
||||
}
|
||||
|
||||
function LoadingSkeleton() {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="space-y-4">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-12 w-full animate-pulse rounded bg-muted"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default function FinishedGoodsInventoryPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="mb-2 text-3xl font-bold">Finished Goods Inventory</h1>
|
||||
<p className="mb-6 text-muted-foreground">
|
||||
Completed products ready for shipment
|
||||
</p>
|
||||
|
||||
<Suspense fallback={<LoadingSkeleton />}>
|
||||
<FinishedGoodsInventoryData />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
65
src/app/(portal)/inventory/processed-other/page.tsx
Normal file
65
src/app/(portal)/inventory/processed-other/page.tsx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { Suspense } from 'react';
|
||||
import { getProcessedOtherSummary } from '@/services/inventory';
|
||||
import {
|
||||
getQuestSession,
|
||||
getActiveCompany,
|
||||
isSubUser,
|
||||
} from '@/lib/permissions';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { InventorySummaryTable } from '@/components/inventory/inventory-summary-table';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
async function ProcessedOtherInventoryData() {
|
||||
const session = await getQuestSession();
|
||||
const activeCompany = await getActiveCompany();
|
||||
const userIsSubUser = await isSubUser();
|
||||
|
||||
if (!session || !activeCompany) {
|
||||
redirect('/select-company');
|
||||
}
|
||||
|
||||
const dbName = `[${process.env.PORTAL_DB_NAME || 'VorteqPortal'}]`;
|
||||
const sub = userIsSubUser ? 1 : 0;
|
||||
|
||||
const inventory = await getProcessedOtherSummary(
|
||||
activeCompany.epicor_cust_id,
|
||||
dbName,
|
||||
sub
|
||||
).catch(() => []);
|
||||
|
||||
return <InventorySummaryTable data={inventory} category="processed-other" />;
|
||||
}
|
||||
|
||||
function LoadingSkeleton() {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="space-y-4">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-12 w-full animate-pulse rounded bg-muted"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ProcessedOtherInventoryPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="mb-2 text-3xl font-bold">Processed Other Inventory</h1>
|
||||
<p className="mb-6 text-muted-foreground">
|
||||
Other processed inventory items
|
||||
</p>
|
||||
|
||||
<Suspense fallback={<LoadingSkeleton />}>
|
||||
<ProcessedOtherInventoryData />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
69
src/app/(portal)/inventory/processed-rr/page.tsx
Normal file
69
src/app/(portal)/inventory/processed-rr/page.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { Suspense } from 'react';
|
||||
import { getProcessedRRSummary } from '@/services/inventory';
|
||||
import {
|
||||
getQuestSession,
|
||||
getActiveCompany,
|
||||
isSubUser,
|
||||
} from '@/lib/permissions';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { InventorySummaryTable } from '@/components/inventory/inventory-summary-table';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
async function ProcessedRRInventoryData() {
|
||||
const session = await getQuestSession();
|
||||
const activeCompany = await getActiveCompany();
|
||||
const userIsSubUser = await isSubUser();
|
||||
|
||||
if (!session || !activeCompany) {
|
||||
redirect('/select-company');
|
||||
}
|
||||
|
||||
// Block sub-users from accessing this category
|
||||
if (userIsSubUser) {
|
||||
redirect('/inventory');
|
||||
}
|
||||
|
||||
const dbName = `[${process.env.PORTAL_DB_NAME || 'VorteqPortal'}]`;
|
||||
|
||||
const inventory = await getProcessedRRSummary(
|
||||
activeCompany.epicor_cust_id,
|
||||
dbName,
|
||||
userIsSubUser
|
||||
).catch(() => []);
|
||||
|
||||
return <InventorySummaryTable data={inventory} category="processed-rr" />;
|
||||
}
|
||||
|
||||
function LoadingSkeleton() {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="space-y-4">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-12 w-full animate-pulse rounded bg-muted"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ProcessedRRInventoryPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="mb-2 text-3xl font-bold">Processed R&R Inventory</h1>
|
||||
<p className="mb-6 text-muted-foreground">
|
||||
Processed returns and repairs
|
||||
</p>
|
||||
|
||||
<Suspense fallback={<LoadingSkeleton />}>
|
||||
<ProcessedRRInventoryData />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
71
src/app/(portal)/inventory/unprocessed-rr/page.tsx
Normal file
71
src/app/(portal)/inventory/unprocessed-rr/page.tsx
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import { Suspense } from 'react';
|
||||
import { getUnprocessedRRSummary } from '@/services/inventory';
|
||||
import {
|
||||
getQuestSession,
|
||||
getActiveCompany,
|
||||
isSubUser,
|
||||
} from '@/lib/permissions';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { InventorySummaryTable } from '@/components/inventory/inventory-summary-table';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
async function UnprocessedRRInventoryData() {
|
||||
const session = await getQuestSession();
|
||||
const activeCompany = await getActiveCompany();
|
||||
const userIsSubUser = await isSubUser();
|
||||
|
||||
if (!session || !activeCompany) {
|
||||
redirect('/select-company');
|
||||
}
|
||||
|
||||
// Block sub-users from accessing this category
|
||||
if (userIsSubUser) {
|
||||
redirect('/inventory');
|
||||
}
|
||||
|
||||
const dbName = `[${process.env.PORTAL_DB_NAME || 'VorteqPortal'}]`;
|
||||
|
||||
const inventory = await getUnprocessedRRSummary(
|
||||
activeCompany.epicor_cust_id,
|
||||
dbName,
|
||||
userIsSubUser
|
||||
).catch(() => []);
|
||||
|
||||
return <InventorySummaryTable data={inventory} category="unprocessed-rr" />;
|
||||
}
|
||||
|
||||
function LoadingSkeleton() {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="space-y-4">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-12 w-full animate-pulse rounded bg-muted"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default function UnprocessedRRInventoryPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="mb-2 text-3xl font-bold">
|
||||
Unprocessed R&R Inventory
|
||||
</h1>
|
||||
<p className="mb-6 text-muted-foreground">
|
||||
Unprocessed returns and repairs
|
||||
</p>
|
||||
|
||||
<Suspense fallback={<LoadingSkeleton />}>
|
||||
<UnprocessedRRInventoryData />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
69
src/app/(portal)/inventory/unprocessed/page.tsx
Normal file
69
src/app/(portal)/inventory/unprocessed/page.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { Suspense } from 'react';
|
||||
import { getUnprocessedSummary } from '@/services/inventory';
|
||||
import {
|
||||
getQuestSession,
|
||||
getActiveCompany,
|
||||
isSubUser,
|
||||
} from '@/lib/permissions';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { InventorySummaryTable } from '@/components/inventory/inventory-summary-table';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
async function UnprocessedInventoryData() {
|
||||
const session = await getQuestSession();
|
||||
const activeCompany = await getActiveCompany();
|
||||
const userIsSubUser = await isSubUser();
|
||||
|
||||
if (!session || !activeCompany) {
|
||||
redirect('/select-company');
|
||||
}
|
||||
|
||||
// Block sub-users from accessing this category
|
||||
if (userIsSubUser) {
|
||||
redirect('/inventory');
|
||||
}
|
||||
|
||||
const dbName = `[${process.env.PORTAL_DB_NAME || 'VorteqPortal'}]`;
|
||||
|
||||
const inventory = await getUnprocessedSummary(
|
||||
activeCompany.epicor_cust_id,
|
||||
dbName,
|
||||
userIsSubUser
|
||||
).catch(() => []);
|
||||
|
||||
return <InventorySummaryTable data={inventory} category="unprocessed" />;
|
||||
}
|
||||
|
||||
function LoadingSkeleton() {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="space-y-4">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-12 w-full animate-pulse rounded bg-muted"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default function UnprocessedInventoryPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="mb-2 text-3xl font-bold">Unprocessed Inventory</h1>
|
||||
<p className="mb-6 text-muted-foreground">
|
||||
Raw materials awaiting processing
|
||||
</p>
|
||||
|
||||
<Suspense fallback={<LoadingSkeleton />}>
|
||||
<UnprocessedInventoryData />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue