Panels: per-device dashboard sets, paired by code
A panel is one screen and the ordered set of whole dashboards it shows, so a hallway tablet and a workshop tablet carry different sets without either dashboard knowing about the other. More than one and the device draws a rail to switch between them — the same rail the editor puts on screen, because the wall has it and it takes room off the canvas. A screen has no keyboard, so it pairs rather than logs in: it shows a six-character code, somebody approves it against a panel from the dashboards overview, and the credential that mints reaches that panel's published dashboards and the message endpoints its widgets speak, and nothing else. Deleting the panel revokes it. Closes the per-device view and the kiosk credential; supersedes the multi-page/multi-section UI, since a page is now a dashboard of its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AHpLJHozysQXjsxAyU1WHj
This commit is contained in:
@@ -4,6 +4,8 @@ import {
|
||||
type DashboardDef_Input,
|
||||
DashboardsService,
|
||||
MessagesService,
|
||||
type PanelsConfig,
|
||||
PanelsService,
|
||||
} from "@/client"
|
||||
|
||||
export const dashboardKeys = {
|
||||
@@ -31,6 +33,41 @@ export const dashboardQueryOptions = (name: string, draft = false) => ({
|
||||
queryFn: () => DashboardsService.readDashboard({ name, draft }),
|
||||
})
|
||||
|
||||
export const panelKeys = {
|
||||
all: ["panels"] as const,
|
||||
detail: (id: string) => ["panels", id] as const,
|
||||
}
|
||||
|
||||
/** Every panel and what it shows. Read by the editor to know if a rail is due. */
|
||||
export const panelsQueryOptions = () => ({
|
||||
queryKey: panelKeys.all,
|
||||
queryFn: () => PanelsService.readPanels(),
|
||||
})
|
||||
|
||||
/**
|
||||
* One panel, as the device hanging on the wall reads it.
|
||||
*
|
||||
* A panel credential may read exactly this and the dashboards it names, so
|
||||
* this is the query the panel route is built on rather than the list above.
|
||||
*/
|
||||
export const panelQueryOptions = (id: string) => ({
|
||||
queryKey: panelKeys.detail(id),
|
||||
queryFn: () => PanelsService.readPanel({ panelId: id }),
|
||||
})
|
||||
|
||||
/** Replace the panels. Superuser-only on the server. */
|
||||
export function useSavePanels() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (body: PanelsConfig) =>
|
||||
PanelsService.savePanels({ requestBody: body }),
|
||||
onSuccess: (saved) => {
|
||||
queryClient.setQueryData(panelKeys.all, saved)
|
||||
queryClient.invalidateQueries({ queryKey: panelKeys.all })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/** Every message any flow declares — what a widget can be pointed at. */
|
||||
export const messageCatalogQueryOptions = () => ({
|
||||
queryKey: dashboardKeys.messages,
|
||||
|
||||
Reference in New Issue
Block a user