This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
// The project's documents, one row each, with where they are in the pipeline and how many lines wait
|
||||
// for a person. A server component: it holds no state, and every string is passed in.
|
||||
|
||||
import type { ReactElement } from "react"
|
||||
import type { DocumentView } from "../contract/index.js"
|
||||
|
||||
export interface DocumentListLabels {
|
||||
readonly caption: string
|
||||
readonly empty: string
|
||||
readonly file: string
|
||||
readonly status: string
|
||||
readonly pages: string
|
||||
readonly pending: string
|
||||
readonly approved: string
|
||||
readonly uploaded: string
|
||||
readonly actions: string
|
||||
/** `Download {filename}`: a same-origin, project-scoped file stream. */
|
||||
readonly download: string
|
||||
/** `Review {filename}`: the link's accessible name, so a list of links is not a list of "Review". */
|
||||
readonly review: string
|
||||
/** The status and failure texts, by code. */
|
||||
readonly statuses: Readonly<Record<DocumentView["status"], string>>
|
||||
readonly failures: Readonly<Record<NonNullable<DocumentView["failureCode"]>, string>>
|
||||
}
|
||||
|
||||
export function DocumentList({
|
||||
documents,
|
||||
hrefFor,
|
||||
downloadHrefFor,
|
||||
labels,
|
||||
formatDate,
|
||||
}: {
|
||||
documents: readonly DocumentView[]
|
||||
hrefFor: (fileId: string) => string
|
||||
downloadHrefFor: (fileId: string) => string
|
||||
labels: DocumentListLabels
|
||||
formatDate: (iso: string) => string
|
||||
}): ReactElement {
|
||||
if (documents.length === 0) {
|
||||
return <p>{labels.empty}</p>
|
||||
}
|
||||
return (
|
||||
<table>
|
||||
<caption>{labels.caption}</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{labels.file}</th>
|
||||
<th scope="col">{labels.status}</th>
|
||||
<th scope="col">{labels.pages}</th>
|
||||
<th scope="col">{labels.pending}</th>
|
||||
<th scope="col">{labels.approved}</th>
|
||||
<th scope="col">{labels.uploaded}</th>
|
||||
<th scope="col">{labels.actions}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{documents.map((document) => (
|
||||
<tr key={document.fileId}>
|
||||
<th scope="row">
|
||||
<a href={hrefFor(document.fileId)} aria-label={labels.review.replace("{filename}", document.filename)}>
|
||||
{document.filename}
|
||||
</a>
|
||||
</th>
|
||||
<td>
|
||||
{labels.statuses[document.status]}
|
||||
{document.failureCode ? ` ${labels.failures[document.failureCode]}` : ""}
|
||||
</td>
|
||||
<td>{document.pageCount ?? ""}</td>
|
||||
<td>{document.pending}</td>
|
||||
<td>{document.approved}</td>
|
||||
<td>{formatDate(document.uploadedAt)}</td>
|
||||
<td>
|
||||
<a
|
||||
href={downloadHrefFor(document.fileId)}
|
||||
aria-label={labels.download.replace("{filename}", document.filename)}
|
||||
>
|
||||
{labels.download.replace("{filename}", document.filename)}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user