@@ -0,0 +1,100 @@
|
||||
"use client"
|
||||
|
||||
// The stock this project holds: its open reservations, read again after a release. Releasing is for
|
||||
// whoever holds `inventory.stock.reserve`; the procedure checks again, and a reservation of another
|
||||
// project is not found there.
|
||||
|
||||
import type { ProjectSlugs } from "@conusai-kit/ui"
|
||||
import { Can, describeError, useRead, useWrite } from "@conusai-kit/ui/data"
|
||||
import { ConfirmDialog, PageSection, type ResourceColumn, ResourceList, serviceNotice } from "@conusai-kit/ui/patterns"
|
||||
import { Button, useUiLabels } from "@conusai-kit/ui/primitives"
|
||||
import type { ReactElement } from "react"
|
||||
import type { ReservationView } from "../contract/index.js"
|
||||
|
||||
export interface ReservationListLabels {
|
||||
readonly title: string
|
||||
readonly caption: string
|
||||
readonly empty: string
|
||||
readonly item: string
|
||||
readonly quantity: string
|
||||
readonly outstanding: string
|
||||
readonly actions: string
|
||||
/** `Release the stock held of {item}`: the button's accessible name. */
|
||||
readonly release: string
|
||||
readonly releaseShort: string
|
||||
readonly releasing: string
|
||||
/** What releasing does to the stock held. */
|
||||
readonly releaseConfirm: string
|
||||
/** The text of each business refusal by problem code (`businessRefusalNotices` of `@conusai-kit/business/refusals`). */
|
||||
readonly refusals: Readonly<Record<string, string>>
|
||||
}
|
||||
|
||||
type Page = { rows: ReservationView[]; after?: string | undefined }
|
||||
|
||||
export function ReservationList({
|
||||
slugs,
|
||||
seed,
|
||||
itemLabel,
|
||||
listHref,
|
||||
labels,
|
||||
}: {
|
||||
slugs: ProjectSlugs
|
||||
seed: Page
|
||||
/** What a person reads for an item, by id, for the items the page knows; the id otherwise. */
|
||||
itemLabel: Readonly<Record<string, string>>
|
||||
listHref: string
|
||||
labels: ReservationListLabels
|
||||
}): ReactElement {
|
||||
const list = useRead<Page>("inventory.reservation.list", { ...slugs, status: "open" }, { initial: seed })
|
||||
const release = useWrite<Record<string, unknown>, ReservationView>("inventory.stock.release")
|
||||
const failed = useUiLabels()?.failed ?? "{detail}"
|
||||
const nameOf = (row: ReservationView): string => itemLabel[row.itemId] ?? row.itemId
|
||||
const describe = (error: unknown): string => serviceNotice(error, labels.refusals)?.text ?? describeError(error)
|
||||
const columns: ResourceColumn<ReservationView>[] = [
|
||||
{ id: "itemId", header: labels.item, wrap: true, cell: nameOf },
|
||||
{ id: "quantity", header: labels.quantity, align: "end" },
|
||||
{ id: "outstanding", header: labels.outstanding, align: "end" },
|
||||
{
|
||||
id: "actions",
|
||||
header: labels.actions,
|
||||
cell: (row) => {
|
||||
const name = labels.release.replace("{item}", nameOf(row))
|
||||
return (
|
||||
<Can permission="inventory.stock.reserve">
|
||||
<ConfirmDialog
|
||||
trigger={
|
||||
<Button variant="outline" size="sm" aria-label={name}>
|
||||
{labels.releaseShort}
|
||||
</Button>
|
||||
}
|
||||
title={name}
|
||||
description={labels.releaseConfirm}
|
||||
confirmLabel={labels.releaseShort}
|
||||
busyLabel={labels.releasing}
|
||||
describeError={describe}
|
||||
onConfirm={async () => {
|
||||
await release.mutate({ ...slugs, reservationId: row.reservationId, requestId: crypto.randomUUID() })
|
||||
await list.refetch()
|
||||
}}
|
||||
/>
|
||||
</Can>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
return (
|
||||
<PageSection title={labels.title}>
|
||||
<ResourceList
|
||||
label={labels.caption}
|
||||
columns={columns}
|
||||
data={list.data?.rows ?? []}
|
||||
rowId={(row) => row.reservationId}
|
||||
rowHref={(row) => `${listHref}/${row.itemId}`}
|
||||
rowLabel={nameOf}
|
||||
empty={{ title: labels.empty }}
|
||||
{...(list.error === undefined ? {} : { error: failed.replace("{detail}", describe(list.error)) })}
|
||||
onRetry={() => void list.refetch()}
|
||||
/>
|
||||
</PageSection>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user