MovementsPage.tsx
2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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>
)
}