quest-vorteq/src/app/(portal)/coil-activity/receipts/page.tsx

93 lines
2.6 KiB
TypeScript
Raw Normal View History

'use client';
import { useEffect, useState, useCallback } from 'react';
import { subDays } from 'date-fns';
import type { CoilReceiptRow } from '@/types/coil-activity';
import { ReceiptsTable } from '@/components/coil-activity/receipts-table';
import { DateRangePicker } from '@/components/ui/date-range-picker';
import { Card, CardContent } from '@/components/ui/card';
function LoadingSkeleton() {
return (
<Card>
<CardContent className="p-6">
<div className="space-y-4">
{[...Array(10)].map((_, i) => (
<div
key={i}
className="h-12 w-full animate-pulse rounded bg-muted"
/>
))}
</div>
</CardContent>
</Card>
);
}
export default function CoilReceiptsPage() {
const [data, setData] = useState<CoilReceiptRow[] | null>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
const [dateRange, setDateRange] = useState(() => ({
from: subDays(new Date(), 9),
to: new Date(),
}));
const fetchData = useCallback(async () => {
setLoading(true);
setError(null);
try {
const startDate = dateRange.from.toISOString().split('T')[0];
const endDate = dateRange.to.toISOString().split('T')[0];
const res = await fetch(
`/api/coil-activity/receipts?startDate=${startDate}&endDate=${endDate}`
);
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw new Error(body.error || `HTTP ${res.status}`);
}
const json = await res.json();
setData(json);
} catch (err) {
setError(err instanceof Error ? err.message : String(err));
} finally {
setLoading(false);
}
}, [dateRange]);
useEffect(() => {
fetchData();
}, [fetchData]);
return (
<div>
<div className="mb-6 flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
<div>
<h1 className="mb-2 text-3xl font-bold">Coil Activity Receipts</h1>
<p className="text-muted-foreground">
View coil material receipts for the selected date range
</p>
</div>
<DateRangePicker
from={dateRange.from}
to={dateRange.to}
onUpdate={setDateRange}
maxDays={31}
/>
</div>
{error ? (
<Card>
<CardContent className="p-6 text-center text-destructive">
Failed to load receipts data: {error}
</CardContent>
</Card>
) : loading || data === null ? (
<LoadingSkeleton />
) : (
<ReceiptsTable data={data} />
)}
</div>
);
}