Component names now reflect filesystem location using / as path separator and : as namespace separator for shared components: ~sx-header → ~layouts/header ~layout-app-body → ~shared:layout/app-body ~blog-admin-dashboard → ~admin/dashboard 209 files, 4,941 replacements across all services. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
82 lines
4.3 KiB
Plaintext
82 lines
4.3 KiB
Plaintext
;; Optimistic update demo — exercises Phase 7c client-side predicted mutations.
|
|
;;
|
|
;; This page shows a todo list with optimistic add/remove.
|
|
;; Mutations are predicted client-side, sent to server, and confirmed/reverted.
|
|
;;
|
|
;; Open browser console and look for:
|
|
;; "sx:optimistic confirmed" — server accepted the mutation
|
|
;; "sx:optimistic reverted" — server rejected, data rolled back
|
|
|
|
(defcomp ~optimistic-demo/content (&key items server-time)
|
|
(div :class "space-y-8"
|
|
(div :class "border-b border-stone-200 pb-6"
|
|
(h1 :class "text-2xl font-bold text-stone-900" "Optimistic Updates")
|
|
(p :class "mt-2 text-stone-600"
|
|
"This page tests Phase 7c optimistic data mutations. Items are updated "
|
|
"instantly on the client, then confirmed or reverted when the server responds."))
|
|
|
|
;; Server metadata
|
|
(div :class "rounded-lg border border-stone-200 bg-white p-6 space-y-3"
|
|
(h2 :class "text-lg font-semibold text-stone-800" "Current state")
|
|
(dl :class "grid grid-cols-2 gap-2 text-sm"
|
|
(dt :class "font-medium text-stone-600" "Server time")
|
|
(dd :class "font-mono text-stone-900" server-time)
|
|
(dt :class "font-medium text-stone-600" "Item count")
|
|
(dd :class "text-stone-900" (str (len items)))))
|
|
|
|
;; Item list
|
|
(div :class "space-y-3"
|
|
(h2 :class "text-lg font-semibold text-stone-800" "Items")
|
|
(div :id "optimistic-items" :class "space-y-2"
|
|
(map (fn (item)
|
|
(div :class "flex items-center justify-between rounded border border-stone-100 bg-white p-3"
|
|
(div :class "flex items-center gap-3"
|
|
(span :class "flex-none rounded-full bg-violet-100 text-violet-700 w-6 h-6 flex items-center justify-center text-xs font-bold"
|
|
(str (get item "id")))
|
|
(span :class "text-stone-900" (get item "label")))
|
|
(span :class "text-xs px-2 py-0.5 rounded-full"
|
|
:class (case (get item "status")
|
|
"confirmed" "bg-green-100 text-green-700"
|
|
"pending" "bg-amber-100 text-amber-700"
|
|
"reverted" "bg-red-100 text-red-700"
|
|
:else "bg-stone-100 text-stone-500")
|
|
(get item "status"))))
|
|
items))
|
|
|
|
;; Add button — triggers optimistic mutation
|
|
(div :class "pt-2"
|
|
(button :class "px-4 py-2 bg-violet-500 text-white rounded hover:bg-violet-600 text-sm"
|
|
:sx-post "/sx/action/add-demo-item"
|
|
:sx-target "#main-panel"
|
|
:sx-vals "{\"label\": \"New item\"}"
|
|
"Add item (optimistic)")))
|
|
|
|
;; How it works
|
|
(div :class "space-y-4"
|
|
(h2 :class "text-lg font-semibold text-stone-800" "How it works")
|
|
(div :class "space-y-2"
|
|
(map-indexed
|
|
(fn (i step)
|
|
(div :class "flex items-start gap-3 rounded border border-stone-100 bg-white p-3"
|
|
(span :class "flex-none rounded-full bg-stone-100 text-stone-700 w-6 h-6 flex items-center justify-center text-xs font-bold"
|
|
(str (+ i 1)))
|
|
(div
|
|
(div :class "font-medium text-stone-900" (get step "label"))
|
|
(div :class "text-sm text-stone-500" (get step "detail")))))
|
|
(list
|
|
(dict :label "Predict" :detail "Client applies mutator function to cached data immediately")
|
|
(dict :label "Snapshot" :detail "Pre-mutation data saved in _optimistic-snapshots for rollback")
|
|
(dict :label "Re-render" :detail "Page content re-evaluated and swapped with predicted data")
|
|
(dict :label "Submit" :detail "Mutation sent to server via POST /sx/action/<name>")
|
|
(dict :label "Confirm or revert" :detail "Server responds — cache updated with truth, or reverted to snapshot")))))
|
|
|
|
;; How to verify
|
|
(div :class "rounded-lg border border-amber-200 bg-amber-50 p-4 text-sm space-y-2"
|
|
(p :class "font-semibold text-amber-800" "How to verify")
|
|
(ol :class "list-decimal list-inside text-amber-700 space-y-1"
|
|
(li "Open the browser console (F12)")
|
|
(li "Navigate to this page from another isomorphism page")
|
|
(li "Click \"Add item\" — item appears instantly with \"pending\" status")
|
|
(li "Watch console for " (code :class "bg-amber-100 px-1 rounded" "sx:optimistic confirmed"))
|
|
(li "Item status changes to \"confirmed\" when server responds")))))
|