@@ -0,0 +1,91 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@conusai-kit/ui/primitives"
|
||||
|
||||
// What an administrator does to a document's retention: place or lift a legal hold, and dispose of it.
|
||||
// The service decides whether disposal is allowed (a hold blocks it, and the retention must have ended):
|
||||
// a refusal is shown here, and a disposal asks first, because it cannot be undone.
|
||||
|
||||
import type { ProjectSlugs } from "@conusai-kit/ui"
|
||||
import { describeError, useWrite } from "@conusai-kit/ui/data"
|
||||
import { ConfirmDialog, serviceNotice, usePageNavigation } from "@conusai-kit/ui/patterns"
|
||||
import { Alert, AlertDescription } from "@conusai-kit/ui/primitives"
|
||||
import type { ReactElement } from "react"
|
||||
import { useState } from "react"
|
||||
|
||||
export interface DocumentActionsLabels {
|
||||
readonly heading: string
|
||||
readonly hold: string
|
||||
readonly release: string
|
||||
readonly dispose: string
|
||||
readonly disposeConfirm: string
|
||||
readonly busy: string
|
||||
readonly failed: string
|
||||
/** The text of each business refusal by problem code (`businessRefusalNotices` of `@conusai-kit/business/refusals`). */
|
||||
readonly refusals: Readonly<Record<string, string>>
|
||||
}
|
||||
|
||||
export function DocumentActions({
|
||||
slugs,
|
||||
documentId,
|
||||
revision,
|
||||
legalHold,
|
||||
labels,
|
||||
}: {
|
||||
slugs: ProjectSlugs
|
||||
documentId: string
|
||||
revision: string
|
||||
legalHold: boolean
|
||||
labels: DocumentActionsLabels
|
||||
}): ReactElement {
|
||||
const navigation = usePageNavigation()
|
||||
const hold = useWrite<Record<string, unknown>, unknown>("archive.document.hold")
|
||||
const dispose = useWrite<Record<string, unknown>, unknown>("archive.document.dispose")
|
||||
const [state, setState] = useState<{ kind: "idle" | "busy" } | { kind: "failed"; detail: string }>({ kind: "idle" })
|
||||
|
||||
const act = async (write: () => Promise<unknown>): Promise<void> => {
|
||||
setState({ kind: "busy" })
|
||||
try {
|
||||
await write()
|
||||
await navigation.refresh()
|
||||
setState({ kind: "idle" })
|
||||
} catch (error) {
|
||||
setState({ kind: "failed", detail: serviceNotice(error, labels.refusals)?.text ?? describeError(error) })
|
||||
}
|
||||
}
|
||||
|
||||
const request = { ...slugs, documentId, revision, requestId: crypto.randomUUID() }
|
||||
|
||||
return (
|
||||
<section>
|
||||
<h2>{labels.heading}</h2>
|
||||
<Button
|
||||
type="button"
|
||||
disabled={state.kind === "busy"}
|
||||
onClick={() => void act(() => hold.mutate({ ...request, legalHold: !legalHold }))}
|
||||
>
|
||||
{state.kind === "busy" ? labels.busy : legalHold ? labels.release : labels.hold}
|
||||
</Button>
|
||||
<ConfirmDialog
|
||||
title={labels.dispose}
|
||||
description={labels.disposeConfirm}
|
||||
confirmLabel={labels.dispose}
|
||||
busyLabel={labels.busy}
|
||||
trigger={
|
||||
<Button type="button" variant="destructive" disabled={state.kind === "busy" || legalHold}>
|
||||
{labels.dispose}
|
||||
</Button>
|
||||
}
|
||||
onConfirm={async () => {
|
||||
await dispose.mutate(request)
|
||||
await navigation.refresh()
|
||||
}}
|
||||
/>
|
||||
{state.kind === "failed" ? (
|
||||
<Alert variant="destructive">
|
||||
<AlertDescription>{labels.failed.replace("{detail}", state.detail)}</AlertDescription>
|
||||
</Alert>
|
||||
) : null}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user