import React from 'react'; import { useMutation } from '@tanstack/react-query'; import { Gauge, RefreshCw } from 'lucide-react'; import { Button, EmptyState, SectionHeader, Stack, Surface } from '@/components/ds'; import { fetchBatchEfficiency } from '@/api/doormile'; import { messageOf } from '@/api/doormile/notify'; import { number } from '@/lib/doormileFormat'; /** * The solver's own read on how well a wave was packed. * * This calls the dispatch optimiser, not the Doormile API, and its response has * no agreed schema — so nothing here assumes a field name. Whatever the service * returns is rendered: numbers get formatted, nested objects are shown as JSON, * and a shape nobody has seen before is still readable rather than blank. * * It is behind a button rather than fetched with the board because it is an * analysis an operator asks for, and running it automatically would put a call * to an external service on every date and wave change. */ const titleise = (key) => String(key) .replace(/[_-]+/g, ' ') .replace(/\b\w/g, (c) => c.toUpperCase()); const renderValue = (value) => { if (value === null || value === undefined || value === '') return '—'; if (typeof value === 'number') return number(value); if (typeof value === 'boolean') return value ? 'Yes' : 'No'; if (typeof value === 'object') return JSON.stringify(value); return String(value); }; /** @param {any} props */ export function BatchEfficiency({ batch, orders = [], tenantId }) { const analyse = useMutation({ mutationFn: () => fetchBatchEfficiency({ batch, tenantId }), }); const result = analyse.data?.data ?? analyse.data; const entries = result && typeof result === 'object' && !Array.isArray(result) ? Object.entries(result) : []; return ( analyse.mutate()} loading={analyse.isPending} disabled={orders.length === 0} > Analyse } className="mb-4" /> {orders.length === 0 ? ( ) : analyse.isError ? (

{messageOf(analyse.error, 'The solver did not answer')}

) : !analyse.data ? (

{orders.length} order{orders.length === 1 ? '' : 's'} in this wave. Run the analysis to see how the solver rates it.

) : entries.length === 0 ? ( /* A response that is not a flat object — an array, a scalar, an error envelope. Shown raw rather than dropped. */
          {JSON.stringify(result, null, 2)}
        
) : ( {entries.map(([key, value]) => (
{titleise(key)} {renderValue(value)}
))}
)}
); }