// The knowledge module's page, on the shell's F10 contract (docs/reference/app-kit.md): a loader the web // shell runs on the server under `////knowledge`, and the view of what it // returned. Every read is a procedure through the same caller the API uses, and every label is translated // in the loader; every write is a client component that calls a procedure through the shell's transport. // // The page renders three things separately on purpose: the service's health (the module flag and // the search service's readiness), the indexing indicator (the per-document progress states), and // the search itself — "indexing" is not "down", and the page never conflates them. import { type Container, notFound, type TransportCaller } from "@conusai-kit/core" import type { I18n } from "@conusai-kit/i18n" import { type ProjectPage, type ProjectPageViewProps, projectPage } from "@conusai-kit/ui" import { FactList, Page, PageHeader, PageSection } from "@conusai-kit/ui/patterns" import { Alert, AlertDescription } from "@conusai-kit/ui/primitives" import type { ReactElement } from "react" import { autoIndexGet, type CollectionView, collectionList, type DocumentView, documentList, knowledgeStatus, } from "../contract/index.js" import { AutoIndexForm, type AutoIndexFormLabels } from "./auto-index-form.js" import { IndexForm, type IndexFormLabels } from "./index-form.js" import { failureLabels, type RefusalNotice, refusalLabels, type SearchFormLabels, type StatusLabels, searchFormLabels, serviceStateLabel, statusLabels, } from "./labels.js" import { CollectionDocuments, type DocumentListLabels } from "./live-documents.js" import { SearchForm } from "./search-form.js" /** One collection and its documents: the seed of its live list. */ export interface KnowledgeCollectionRow { readonly name: string readonly documents: readonly DocumentView[] } export interface KnowledgePageData { readonly title: string readonly intro: string readonly status: { readonly enabled: boolean readonly labels: StatusLabels /** The service's state in the reader's language. */ readonly state: string readonly reasons: readonly string[] /** Why the service will not serve this deployment, when that is the answer. */ readonly refusal?: RefusalNotice } readonly collections: readonly KnowledgeCollectionRow[] readonly documents: DocumentListLabels readonly index: IndexFormLabels readonly search: SearchFormLabels /** The project's automatic PDF indexing: the collection it fills, or null when it is off. */ readonly autoIndex: { readonly current: string | null; readonly labels: AutoIndexFormLabels } } export type KnowledgePage = ProjectPage const indexFormLabels = (t: (key: string, values?: Record) => string): IndexFormLabels => ({ heading: t("upload.heading"), label: t("upload.label"), hint: t("upload.hint"), submit: t("upload.submit"), busy: t("upload.busy"), done: t("upload.done"), }) const documentStatusKey: Record = { queued: "documents.statusQueued", indexing: "documents.statusIndexing", indexed: "documents.statusIndexed", failed: "documents.statusFailed", cancelled: "documents.statusCancelled", } export const knowledgePage: KnowledgePage = projectPage({ async load({ slugs, segments, caller, i18n }): Promise { const t = (key: string, values?: Record) => i18n.message(`knowledge.${key}`, values) if (segments.length > 0) throw notFound("no such page") const [{ collections }, status, auto] = await Promise.all([ caller.call(collectionList, slugs), caller.call(knowledgeStatus, slugs), caller.call(autoIndexGet, slugs), ]) const rows = await Promise.all( collections.map(async (collection: CollectionView): Promise => { const { documents } = await caller.call(documentList, { ...slugs, collectionName: collection.name }) return { name: collection.name, documents } }), ) return { title: t("title"), intro: t("intro"), status: { enabled: status.enabled, labels: statusLabels(t, status), state: serviceStateLabel(t, status.service.state), reasons: status.service.reasons, ...(status.service.refusal === undefined ? {} : { refusal: { code: status.service.refusal, text: refusalLabels(t)[status.service.refusal], }, }), }, collections: rows, documents: { caption: t("documents.heading"), file: t("documents.file"), status: t("documents.status"), error: t("documents.error"), statuses: Object.fromEntries( Object.entries(documentStatusKey).map(([status, key]) => [status, t(key)]), ) as DocumentListLabels["statuses"], failures: failureLabels(t), }, index: indexFormLabels(t), search: searchFormLabels(t), autoIndex: { current: auto.collectionName, labels: { heading: t("autoIndex.heading"), hint: t("autoIndex.hint"), label: t("autoIndex.label"), off: t("autoIndex.off"), submit: t("autoIndex.submit"), busy: t("autoIndex.busy"), done: t("autoIndex.done"), }, }, } }, View({ slugs, locale, data }: ProjectPageViewProps): ReactElement { const { labels, enabled, state, reasons, refusal } = data.status return ( {refusal ? ( {refusal.text} ) : null} {enabled ? ( 0 ? [ { label: labels.reasons, value: (
    {reasons.map((reason) => (
  • {reason}
  • ))}
), }, ] : []), { label: labels.indexing, value: labels.documents }, ]} /> ) : (

{labels.flagOff}

)}
{enabled && data.collections.length > 0 ? ( collection.name)} current={data.autoIndex.current} labels={data.autoIndex.labels} /> ) : null} {enabled ? data.collections.map((collection) => ( )) : null}
) }, })