// ================================================================ // frontend/src/components/dashboard/PurchaseOrderDetailsModal.tsx // ================================================================ /** * Purchase Order Details Modal * Quick view of PO details from the Action Queue */ import React from 'react'; import { X, Package, Building2, Calendar, Truck, Euro, FileText, CheckCircle, Clock, AlertCircle, } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { usePurchaseOrder } from '../../api/hooks/purchase-orders'; import { formatDistanceToNow } from 'date-fns'; import { es, enUS, eu as euLocale } from 'date-fns/locale'; interface PurchaseOrderDetailsModalProps { poId: string; tenantId: string; isOpen: boolean; onClose: () => void; onApprove?: (poId: string) => void; onModify?: (poId: string) => void; } const localeMap = { es: es, en: enUS, eu: euLocale, }; export const PurchaseOrderDetailsModal: React.FC = ({ poId, tenantId, isOpen, onClose, onApprove, onModify, }) => { const { t, i18n } = useTranslation(); const { data: po, isLoading } = usePurchaseOrder(tenantId, poId); if (!isOpen) return null; const dateLocale = localeMap[i18n.language as keyof typeof localeMap] || enUS; // Format currency const formatCurrency = (value: any) => { const num = Number(value); return isNaN(num) ? '0.00' : num.toFixed(2); }; const getStatusBadge = (status: string) => { const statusConfig = { draft: { bg: 'var(--color-gray-100)', text: 'var(--color-gray-700)', label: t('purchase_orders:status.draft'), }, pending_approval: { bg: 'var(--color-warning-100)', text: 'var(--color-warning-700)', label: t('purchase_orders:status.pending_approval'), }, approved: { bg: 'var(--color-success-100)', text: 'var(--color-success-700)', label: t('purchase_orders:status.approved'), }, sent: { bg: 'var(--color-info-100)', text: 'var(--color-info-700)', label: t('purchase_orders:status.sent'), }, partially_received: { bg: 'var(--color-warning-100)', text: 'var(--color-warning-700)', label: t('purchase_orders:status.partially_received'), }, received: { bg: 'var(--color-success-100)', text: 'var(--color-success-700)', label: t('purchase_orders:status.received'), }, cancelled: { bg: 'var(--color-error-100)', text: 'var(--color-error-700)', label: t('purchase_orders:status.cancelled'), }, }; const config = statusConfig[status as keyof typeof statusConfig] || statusConfig.draft; return ( {config.label} ); }; return (
e.stopPropagation()} style={{ backgroundColor: 'var(--bg-primary)', animation: 'slideUp 0.3s ease-out' }} > {/* Header */}

{isLoading ? t('common:loading') : po?.po_number || t('purchase_orders:purchase_order')}

{po && (

{t('purchase_orders:created')} {formatDistanceToNow(new Date(po.created_at), { addSuffix: true, locale: dateLocale })}

)}
{/* Content */}
{isLoading ? (
) : po ? (
{/* Status and Key Info */}
{getStatusBadge(po.status)}

{t('purchase_orders:total_amount')}

{formatCurrency(po.total_amount)}
{/* Supplier Info */}

{t('purchase_orders:supplier')}

{po.supplier_name || t('common:unknown')}

{/* Dates */}

{t('purchase_orders:order_date')}

{new Date(po.order_date).toLocaleDateString(i18n.language, { year: 'numeric', month: 'short', day: 'numeric' })}

{po.expected_delivery_date && (

{t('purchase_orders:expected_delivery')}

{new Date(po.expected_delivery_date).toLocaleDateString(i18n.language, { year: 'numeric', month: 'short', day: 'numeric' })}

)}
{/* Items */}

{t('purchase_orders:items')}

{po.items && po.items.length > 0 && ( {po.items.length} {po.items.length === 1 ? t('common:item') : t('common:items')} )}
{po.items && po.items.length > 0 ? ( po.items.map((item: any, index: number) => (

{item.ingredient_name || item.product_name}

{item.quantity} {item.unit} × €{formatCurrency(item.unit_price)}

€{formatCurrency(item.subtotal)}

)) ) : (

{t('purchase_orders:no_items')}

)}
{/* Notes */} {po.notes && (

{t('purchase_orders:notes')}

{po.notes}

)}
) : (

{t('purchase_orders:not_found')}

)}
{/* Footer Actions */} {po && po.status === 'pending_approval' && (
)}
); };