mirror of
https://github.com/Sea-Haven-Industries/proposal-system.git
synced 2026-10-05 00:12:05 +00:00
91 lines
2.9 KiB
TypeScript
91 lines
2.9 KiB
TypeScript
|
|
import { useQuery } from '@tanstack/react-query';
|
||
|
|
import { Box, Typography, Skeleton, Chip } from '@mui/material';
|
||
|
|
import apiClient from '../../lib/api/client';
|
||
|
|
|
||
|
|
interface VendorProposal {
|
||
|
|
id: string;
|
||
|
|
vendorName: string;
|
||
|
|
fileName: string;
|
||
|
|
totalVendorCost: number;
|
||
|
|
processingStatus: string;
|
||
|
|
extractedData: {
|
||
|
|
lineItems?: Array<{
|
||
|
|
description: string;
|
||
|
|
quantity?: number;
|
||
|
|
unitPrice?: number;
|
||
|
|
total?: number;
|
||
|
|
}>;
|
||
|
|
} | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
interface VendorDataPanelProps {
|
||
|
|
proposalId: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function VendorDataPanel({ proposalId }: VendorDataPanelProps) {
|
||
|
|
const { data: vendors, isLoading } = useQuery<VendorProposal[]>({
|
||
|
|
queryKey: ['vendorProposals', proposalId],
|
||
|
|
queryFn: async () => {
|
||
|
|
const res = await apiClient.get(`/proposals/${proposalId}/vendors`);
|
||
|
|
return res.data;
|
||
|
|
},
|
||
|
|
enabled: !!proposalId,
|
||
|
|
});
|
||
|
|
|
||
|
|
if (isLoading) {
|
||
|
|
return <Skeleton variant="rectangular" height={60} />;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!vendors || vendors.length === 0) {
|
||
|
|
return (
|
||
|
|
<Box>
|
||
|
|
<Typography variant="subtitle2" sx={{ mb: 0.5 }}>Vendor Data</Typography>
|
||
|
|
<Typography variant="body2" color="text.secondary">No vendor proposals attached</Typography>
|
||
|
|
</Box>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Box>
|
||
|
|
<Typography variant="subtitle2" sx={{ mb: 1 }}>Vendor Data</Typography>
|
||
|
|
{vendors.map((vp) => (
|
||
|
|
<Box key={vp.id} sx={{ mb: 2 }}>
|
||
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 0.5 }}>
|
||
|
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>{vp.vendorName}</Typography>
|
||
|
|
<Chip
|
||
|
|
label={vp.processingStatus}
|
||
|
|
size="small"
|
||
|
|
color={vp.processingStatus === 'Complete' ? 'success' : 'default'}
|
||
|
|
variant="outlined"
|
||
|
|
/>
|
||
|
|
</Box>
|
||
|
|
<Typography variant="caption" color="text.secondary">{vp.fileName}</Typography>
|
||
|
|
|
||
|
|
{vp.totalVendorCost > 0 && (
|
||
|
|
<Typography variant="body2" sx={{ mt: 0.5, fontWeight: 600 }}>
|
||
|
|
Vendor Total: {new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(vp.totalVendorCost)}
|
||
|
|
</Typography>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{vp.extractedData?.lineItems && vp.extractedData.lineItems.length > 0 && (
|
||
|
|
<Box sx={{ mt: 1, pl: 1, borderLeft: 2, borderColor: 'divider' }}>
|
||
|
|
{vp.extractedData.lineItems.map((li, i) => (
|
||
|
|
<Box key={i} sx={{ mb: 0.5 }}>
|
||
|
|
<Typography variant="caption" sx={{ display: 'block' }}>
|
||
|
|
{li.description}
|
||
|
|
</Typography>
|
||
|
|
{li.total != null && (
|
||
|
|
<Typography variant="caption" color="text.secondary">
|
||
|
|
${li.total.toFixed(2)}
|
||
|
|
</Typography>
|
||
|
|
)}
|
||
|
|
</Box>
|
||
|
|
))}
|
||
|
|
</Box>
|
||
|
|
)}
|
||
|
|
</Box>
|
||
|
|
))}
|
||
|
|
</Box>
|
||
|
|
);
|
||
|
|
}
|