// The notes module's page, on the shell's F10 contract (docs/reference/app-kit.md): a loader the web // shell runs on the server under `////notes`, and the view of what it returned. // // /notes the list, on `ResourcePage`: a note opens, and a new one starts, in the right panel // /notes/ one note in full: its text, its attachments, deleting it // // Every read is a procedure through the same caller the API uses (spec ยง6.2: one API path), and every // label is translated here, on the server: the view receives strings and rows, never a caller or a // catalogue. Every write is a client component that calls a procedure through the shell's transport. // This is the worked example the other modules' pages follow (apps/web/ROUTES.md). 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 { Page, PageHeader, PageSection } from "@conusai-kit/ui/patterns" import { Button } from "@conusai-kit/ui/primitives" import type { ReactElement } from "react" import { ATTACHMENT_CONTENT_TYPES, type AttachmentView, type NoteView, noteGet, noteList } from "../contract/index.js" import { FeedbackButton } from "../guides/feedback-button.js" // kit:guides import { type FeedbackButtonLabels, feedbackLabels } from "../guides/feedback-labels.js" // kit:guides import { type AttachmentLabels, Attachments } from "./attachments.js" import { DeleteNote, type DeleteNoteLabels } from "./delete-note.js" import { attachmentLabels, deleteLabels, type NotesResourceLabels, resourceLabels } from "./labels.js" import { NotesResource } from "./notes-resource.js" /** The list: its labels and the first page of notes, the resource's seed. */ export interface NotesListData { readonly kind: "list" readonly intro: string readonly feedback: FeedbackButtonLabels // kit:guides readonly labels: NotesResourceLabels readonly notes: readonly NoteView[] } /** One note, with its attachments as the live list's seed. */ export interface NoteDetailData { readonly kind: "note" readonly back: string readonly changed: string readonly note: { readonly noteId: string; readonly title: string; readonly body: string } /** What the link to the note's panel on the list says. */ readonly edit: string readonly attachments: readonly AttachmentView[] readonly attachmentsEnabled: boolean readonly attachmentLabels: AttachmentLabels readonly remove: DeleteNoteLabels } export type NotesPageData = NotesListData | NoteDetailData export type NotesPage = ProjectPage const listHrefOf = ({ org, workspace, project }: ProjectPageViewProps["slugs"]): string => `/${org}/${workspace}/${project}/notes` export const notesPage: NotesPage = projectPage({ async load({ slugs, segments, caller, i18n }): Promise { const t = (key: string, values?: Record) => i18n.message(`notes.${key}`, values) if (segments.length === 0) { const { notes } = await caller.call(noteList, slugs) return { kind: "list", intro: t("intro"), feedback: feedbackLabels(t), // kit:guides labels: resourceLabels(t), notes, } } const [noteId, ...rest] = segments if (noteId === undefined || rest.length > 0) throw notFound("no such page") const { note, attachments, attachmentsEnabled } = await caller.call(noteGet, { ...slugs, noteId }) return { kind: "note", back: t("detail.back"), changed: t("detail.changed", { date: i18n.date(new Date(note.updatedAt)) }), note: { noteId: note.noteId, title: note.title, body: note.body }, edit: t("detail.edit"), attachments, attachmentsEnabled, attachmentLabels: attachmentLabels(t), remove: deleteLabels(t), } }, View({ slugs, locale, data }: ProjectPageViewProps): ReactElement { const listHref = listHrefOf(slugs) if (data.kind === "list") { return ( {data.intro} } /> ) } const { note } = data return ( {data.back}} actions={ <> } />

{note.body}

) }, })