Add loading sheet view fix
This commit is contained in:
@@ -6,7 +6,6 @@ Running log of backend API failures found during testing and the corresponding f
|
|||||||
|
|
||||||
| # | Area | Backend API Failure | Frontend Change Needed | Date |
|
| # | Area | Backend API Failure | Frontend Change Needed | Date |
|
||||||
|---|------|----------------------|--------------------------|------|
|
|---|------|----------------------|--------------------------|------|
|
||||||
| 2 | Operations → Loading Sheets | | Add a View icon to the action column in Operations → Loading Sheets table | 2026-09-01 |
|
|
||||||
| 3 | Sales (Create Sale) | Sale create not working | Update the Create Sale modal UI |Sale summary calculation not display | Add view icon to the action | 2026-09-01 |
|
| 3 | Sales (Create Sale) | Sale create not working | Update the Create Sale modal UI |Sale summary calculation not display | Add view icon to the action | 2026-09-01 |
|
||||||
| 4 | Orders | | Add a View icon to the action column in Orders table | 2026-09-01 |
|
| 4 | Orders | | Add a View icon to the action column in Orders table | 2026-09-01 |
|
||||||
| 5 | Payments (Record Payment) | Cannot add new payment | Add a View icon to the action column in Payments table | 2026-09-01 |
|
| 5 | Payments (Record Payment) | Cannot add new payment | Add a View icon to the action column in Payments table | 2026-09-01 |
|
||||||
@@ -40,6 +39,7 @@ Running log of backend API failures found during testing and the corresponding f
|
|||||||
|---|------|----------------------|--------------------------|----------|------|
|
|---|------|----------------------|--------------------------|----------|------|
|
||||||
| 1 | Operations → Rides (Create Ride) | Create Ride failed with "Vehicle not found" | — | Fixed DTO field naming mismatch | 2026-09-02 |
|
| 1 | Operations → Rides (Create Ride) | Create Ride failed with "Vehicle not found" | — | Fixed DTO field naming mismatch | 2026-09-02 |
|
||||||
| 29 | Operations → Rides | Cancel-request API routes mismatched | Added View, Edit, and Cancel icons to Rides table | Fixed API routes + added action icons | 2026-09-02 |
|
| 29 | Operations → Rides | Cancel-request API routes mismatched | Added View, Edit, and Cancel icons to Rides table | Fixed API routes + added action icons | 2026-09-02 |
|
||||||
|
| 2 | Operations → Loading Sheets | — | Missing View icon | Added View icon + details modal (status, dispatch/receipt confirmer + timestamps, item-level dispatched/received/resolved breakdown) | 2026-09-02 |
|
||||||
|
|
||||||
## Implementation Guide
|
## Implementation Guide
|
||||||
|
|
||||||
|
|||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog'
|
||||||
|
import { Separator } from '@/components/ui/separator'
|
||||||
|
import type { LoadingSheetResponse, LoadingSheetStatus } from '@/types/api'
|
||||||
|
|
||||||
|
const LS_STATUS_VARIANT: Record<LoadingSheetStatus, 'default' | 'secondary' | 'destructive'> = {
|
||||||
|
Pending: 'secondary',
|
||||||
|
DispatchConfirmed: 'default',
|
||||||
|
ReceiptConfirmed: 'default',
|
||||||
|
DiscrepancyFlagged: 'destructive',
|
||||||
|
Resolved: 'secondary',
|
||||||
|
}
|
||||||
|
|
||||||
|
const fmtDateTime = (v: string | null) =>
|
||||||
|
v
|
||||||
|
? new Date(v).toLocaleString('en-GB', {
|
||||||
|
day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit',
|
||||||
|
})
|
||||||
|
: '—'
|
||||||
|
|
||||||
|
interface LoadingSheetViewSheetProps {
|
||||||
|
open: boolean
|
||||||
|
onClose: () => void
|
||||||
|
sheet: LoadingSheetResponse | null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Row({ label, value }: { label: string; value: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between py-2 text-sm border-b last:border-0">
|
||||||
|
<span className="text-muted-foreground">{label}</span>
|
||||||
|
<span className="font-medium text-right">{value}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LoadingSheetViewSheet({ open, onClose, sheet }: LoadingSheetViewSheetProps) {
|
||||||
|
if (!sheet) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={(v) => !v && onClose()}>
|
||||||
|
<DialogContent className="overflow-y-auto sm:max-w-2xl">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Loading Sheet Details</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<Row label="Sheet ID" value={<span className="font-mono">{sheet.id.substring(0, 8)}</span>} />
|
||||||
|
<Row label="Warehouse" value={sheet.warehouseName} />
|
||||||
|
<Row label="Status" value={<Badge variant={LS_STATUS_VARIANT[sheet.status]}>{sheet.status}</Badge>} />
|
||||||
|
<Row label="Dispatch Confirmed By" value={sheet.dispatchConfirmedBy ?? '—'} />
|
||||||
|
<Row label="Dispatch Confirmed At" value={fmtDateTime(sheet.dispatchConfirmedAt)} />
|
||||||
|
<Row label="Receipt Confirmed By" value={sheet.receiptConfirmedBy ?? '—'} />
|
||||||
|
<Row label="Receipt Confirmed At" value={fmtDateTime(sheet.receiptConfirmedAt)} />
|
||||||
|
<Row label="Created At" value={fmtDateTime(sheet.createdAt)} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Separator />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium mb-2">Items ({sheet.items.length})</p>
|
||||||
|
<div className="overflow-x-auto rounded-md border">
|
||||||
|
<table className="w-full text-sm">
|
||||||
|
<thead className="bg-muted/50">
|
||||||
|
<tr>
|
||||||
|
<th className="text-left font-medium px-3 py-2">Item</th>
|
||||||
|
<th className="text-right font-medium px-3 py-2">Dispatched</th>
|
||||||
|
<th className="text-right font-medium px-3 py-2">Received</th>
|
||||||
|
<th className="text-right font-medium px-3 py-2">Resolved</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{sheet.items.length === 0 ? (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={4} className="text-center text-muted-foreground px-3 py-4">
|
||||||
|
No items on this sheet.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
) : (
|
||||||
|
sheet.items.map((item) => (
|
||||||
|
<tr key={item.id} className="border-t">
|
||||||
|
<td className="px-3 py-2">{item.itemName}</td>
|
||||||
|
<td className="text-right px-3 py-2">{item.quantityDispatched}</td>
|
||||||
|
<td className="text-right px-3 py-2">{item.quantityReceived}</td>
|
||||||
|
<td className="text-right px-3 py-2">{item.quantityResolved}</td>
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
+21
@@ -8,6 +8,8 @@ import { toast } from '@/components/ui/toast'
|
|||||||
import { loadingSheets as loadingSheetsApi, getApiError, getPaginatedItems } from '@/lib/api/index'
|
import { loadingSheets as loadingSheetsApi, getApiError, getPaginatedItems } from '@/lib/api/index'
|
||||||
import type { LoadingSheetResponse, LoadingSheetStatus } from '@/types/api'
|
import type { LoadingSheetResponse, LoadingSheetStatus } from '@/types/api'
|
||||||
|
|
||||||
|
import { LoadingSheetViewSheet } from './loading-sheet-view-sheet'
|
||||||
|
|
||||||
const fmtDate = (v: string) =>
|
const fmtDate = (v: string) =>
|
||||||
new Date(v).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })
|
new Date(v).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })
|
||||||
|
|
||||||
@@ -28,6 +30,9 @@ export function LoadingSheetsClient() {
|
|||||||
const [page, setPage] = useState(1)
|
const [page, setPage] = useState(1)
|
||||||
const [pageSize, setPageSize] = useState(20)
|
const [pageSize, setPageSize] = useState(20)
|
||||||
|
|
||||||
|
const [viewOpen, setViewOpen] = useState(false)
|
||||||
|
const [selectedSheet, setSelectedSheet] = useState<LoadingSheetResponse | null>(null)
|
||||||
|
|
||||||
const fetchData = useCallback(async (p: number, ps: number) => {
|
const fetchData = useCallback(async (p: number, ps: number) => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
@@ -45,6 +50,11 @@ export function LoadingSheetsClient() {
|
|||||||
fetchData(page, pageSize)
|
fetchData(page, pageSize)
|
||||||
}, [fetchData, page, pageSize])
|
}, [fetchData, page, pageSize])
|
||||||
|
|
||||||
|
function handleView(row: LoadingSheetResponse) {
|
||||||
|
setSelectedSheet(row)
|
||||||
|
setViewOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
async function handleConfirmDispatch(row: LoadingSheetResponse) {
|
async function handleConfirmDispatch(row: LoadingSheetResponse) {
|
||||||
try {
|
try {
|
||||||
await loadingSheetsApi.confirmDispatch(row.id)
|
await loadingSheetsApi.confirmDispatch(row.id)
|
||||||
@@ -114,6 +124,11 @@ export function LoadingSheetsClient() {
|
|||||||
],
|
],
|
||||||
rowActions: {
|
rowActions: {
|
||||||
actions: () => [
|
actions: () => [
|
||||||
|
{
|
||||||
|
action: 'view',
|
||||||
|
label: 'View sheet',
|
||||||
|
onClick: handleView,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
action: 'confirm',
|
action: 'confirm',
|
||||||
label: 'Confirm dispatch',
|
label: 'Confirm dispatch',
|
||||||
@@ -151,6 +166,12 @@ export function LoadingSheetsClient() {
|
|||||||
setPageSize(p.pageSize)
|
setPageSize(p.pageSize)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<LoadingSheetViewSheet
|
||||||
|
open={viewOpen}
|
||||||
|
onClose={() => setViewOpen(false)}
|
||||||
|
sheet={selectedSheet}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user