// A page above any project, on the shell's F10 contract: `///modules/notes`. It lists the // workspace's projects, each linked to its notes, and `?q=` filters them on the server, so the first byte // already holds the result and a filter is a link anyone can copy. // // The shell answers 403 for `permission` before `load` runs; the rest is a `ProjectPage` with two slugs. This is // the worked example for a catalogue, a list or a setting that belongs to a workspace, not to a project, and for // a page's strings as one record, with a count only the browser knows formatted there (project-picker.tsx). import type { Container, TransportCaller } from "@conusai-kit/core" import type { I18n } from "@conusai-kit/i18n" import { projectList } from "@conusai-kit/tenancy" import { type WorkspacePage, workspacePage } from "@conusai-kit/ui" import { FilterBar, Page, PageHeader, PageState, TextField } from "@conusai-kit/ui/patterns" import { Button } from "@conusai-kit/ui/primitives" import { ProjectPicker } from "./project-picker.js" /** Every string of the page, as one record. `selected` keeps its placeholder: only the browser knows the count. */ export const WORKSPACE_LABEL_KEYS = [ "title", "intro", "filter", "apply", "clear", "empty", "noProjects", "selected", ] as const export interface NotesWorkspaceData { readonly labels: Readonly> readonly query: string readonly projects: readonly { readonly slug: string; readonly name: string }[] } export type NotesWorkspacePage = WorkspacePage export const notesWorkspacePage: NotesWorkspacePage = workspacePage({ permission: "notes.note.read", async load({ slugs, search, caller, i18n }): Promise { const query = search.q ?? "" const wanted = query.trim().toLowerCase() const { projects } = await caller.call(projectList, slugs) return { labels: Object.fromEntries( WORKSPACE_LABEL_KEYS.map((key) => [key, i18n.message(`notes.workspace.${key}`)]), ) as NotesWorkspaceData["labels"], query, projects: projects .filter((project) => project.name.toLowerCase().includes(wanted)) .map(({ slug, name }) => ({ slug, name })), } }, View({ slugs, locale, data }) { const { labels } = data return ( {data.query.trim() && data.projects.length > 0 && ( )} } > {data.projects.length === 0 ? ( {labels.clear} ) : undefined } /> ) : ( ({ ...project, href: `/${slugs.org}/${slugs.workspace}/${project.slug}/notes`, }))} selected={labels.selected} locale={locale} /> )} ) }, })