MovementsPage.tsx 2.64 KB
import { useEffect, useState } from 'react'
import { inventory } from '@/api/client'
import type { Location, StockMovement } from '@/types/api'
import { PageHeader } from '@/components/PageHeader'
import { Loading } from '@/components/Loading'
import { ErrorBox } from '@/components/ErrorBox'
import { DataTable, type Column } from '@/components/DataTable'

interface Row extends Record<string, unknown> {
  id: string
  occurredAt: string
  itemCode: string
  locationCode: string
  delta: string | number
  reason: string
  reference: string | null
}

export function MovementsPage() {
  const [rows, setRows] = useState<Row[]>([])
  const [error, setError] = useState<Error | null>(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    Promise.all([inventory.listMovements(), inventory.listLocations()])
      .then(([moves, locs]: [StockMovement[], Location[]]) => {
        const byId = new Map(locs.map((l) => [l.id, l.code]))
        const sorted = [...moves].sort((a, b) =>
          (b.occurredAt ?? '').localeCompare(a.occurredAt ?? ''),
        )
        setRows(
          sorted.map((m) => ({
            id: m.id,
            occurredAt: m.occurredAt,
            itemCode: m.itemCode,
            locationCode: byId.get(m.locationId) ?? m.locationId,
            delta: m.delta,
            reason: m.reason,
            reference: m.reference,
          })),
        )
      })
      .catch((e: unknown) => setError(e instanceof Error ? e : new Error(String(e))))
      .finally(() => setLoading(false))
  }, [])

  const columns: Column<Row>[] = [
    {
      header: 'Occurred',
      key: 'occurredAt',
      render: (r) =>
        r.occurredAt ? new Date(r.occurredAt).toLocaleString() : '—',
    },
    { header: 'Item', key: 'itemCode', render: (r) => <span className="font-mono">{r.itemCode}</span> },
    { header: 'Location', key: 'locationCode', render: (r) => <span className="font-mono">{r.locationCode}</span> },
    {
      header: 'Δ',
      key: 'delta',
      render: (r) => {
        const n = Number(r.delta)
        const cls = n >= 0 ? 'text-emerald-600' : 'text-rose-600'
        return <span className={`font-mono tabular-nums ${cls}`}>{String(r.delta)}</span>
      },
    },
    { header: 'Reason', key: 'reason' },
    { header: 'Reference', key: 'reference', render: (r) => r.reference ?? '—' },
  ]

  return (
    <div>
      <PageHeader
        title="Stock Movements"
        subtitle="Append-only ledger of every change to inventory. Most-recent first."
      />
      {loading && <Loading />}
      {error && <ErrorBox error={error} />}
      {!loading && !error && <DataTable rows={rows} columns={columns} />}
    </div>
  )
}