Build dashboards you can actually look at and press
Widgets bind to a message name and read it live off the socket the editor already had — lifted out of the flow editor so a dashboard route gets the same values, which also gives the home page live data for free. The input widgets close the loop the other way: a slider publishes into the graph and whatever consumes that message runs. Verified end to end in the running app — moving a slider set a flow input, and the stat bound to what the flow computed from it followed. View mode is plain CSS grid. A wall panel that only displays should not download the code that lets someone drag things around, and it now does not. Editing is a widget picker, a per-widget width control and a settings card fed by the message catalog. No new dependencies: the slider is a range input, the gauge is an arc, and the markdown is a five-line subset. Charts are the one widget still missing — they need a charting library and the chart tokens the design guidelines reserved — so they are stored and validated but not offered. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011LF61rxW1FG5YCD2J9YqjY
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query"
|
||||
|
||||
import {
|
||||
type DashboardDef_Input,
|
||||
DashboardsService,
|
||||
MessagesService,
|
||||
} from "@/client"
|
||||
|
||||
export const dashboardKeys = {
|
||||
all: ["dashboards"] as const,
|
||||
detail: (name: string) => ["dashboards", name] as const,
|
||||
messages: ["messages"] as const,
|
||||
history: (message: string) => ["messages", message, "history"] as const,
|
||||
}
|
||||
|
||||
export const dashboardsQueryOptions = () => ({
|
||||
queryKey: dashboardKeys.all,
|
||||
queryFn: () => DashboardsService.readDashboards(),
|
||||
})
|
||||
|
||||
export const dashboardQueryOptions = (name: string) => ({
|
||||
queryKey: dashboardKeys.detail(name),
|
||||
queryFn: () => DashboardsService.readDashboard({ name }),
|
||||
})
|
||||
|
||||
/** Every message any flow declares — what a widget can be pointed at. */
|
||||
export const messageCatalogQueryOptions = () => ({
|
||||
queryKey: dashboardKeys.messages,
|
||||
queryFn: () => MessagesService.readMessages(),
|
||||
})
|
||||
|
||||
export const messageHistoryQueryOptions = (message: string) => ({
|
||||
queryKey: dashboardKeys.history(message),
|
||||
queryFn: () => MessagesService.readMessageHistory({ name: message }),
|
||||
})
|
||||
|
||||
/** Saving a dashboard, carrying the version it was based on. */
|
||||
export function useSaveDashboard(name: string) {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (body: DashboardDef_Input) =>
|
||||
DashboardsService.saveDashboard({ name, requestBody: body }),
|
||||
onSuccess: (saved) => {
|
||||
queryClient.setQueryData(dashboardKeys.detail(name), saved)
|
||||
queryClient.invalidateQueries({ queryKey: dashboardKeys.all })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/** What an input widget does: put a value into the graph. */
|
||||
export function usePublishMessage() {
|
||||
return useMutation({
|
||||
mutationFn: ({ name, value }: { name: string; value: unknown }) =>
|
||||
MessagesService.publishMessage({ name, requestBody: { value } }),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user