Phase 7c + 7d: optimistic data updates and offline mutation queue

7c — Optimistic Data Updates:
- orchestration.sx: optimistic-cache-update/revert/confirm + submit-mutation
- pages.py: mount_action_endpoint at /sx/action/<name> for client mutations
- optimistic-demo.sx: live demo with todo list, pending/confirmed/reverted states
- helpers.py: demo data + add-demo-item action handler

7d — Offline Data Layer:
- orchestration.sx: connectivity tracking, offline-queue-mutation, offline-sync,
  offline-aware-mutation (routes online→submit, offline→queue)
- offline-demo.sx: live demo with notes, connectivity indicator, sync timeline
- helpers.py: offline demo data

Also updates plans.sx: marks Phase 7 fully complete (all 6 sub-phases 7a-7f).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 01:30:12 +00:00
parent 4c1853bc7b
commit 5f20a16aa0
8 changed files with 483 additions and 7 deletions

View File

@@ -525,6 +525,30 @@
:data (affinity-demo-data)
:content (~affinity-demo-content :components components :page-plans page-plans))
(defpage optimistic-demo
:path "/isomorphism/optimistic"
:auth :public
:layout (:sx-section
:section "Isomorphism"
:sub-label "Isomorphism"
:sub-href "/isomorphism/"
:sub-nav (~section-nav :items isomorphism-nav-items :current "Optimistic")
:selected "Optimistic")
:data (optimistic-demo-data)
:content (~optimistic-demo-content :items items :server-time server-time))
(defpage offline-demo
:path "/isomorphism/offline"
:auth :public
:layout (:sx-section
:section "Isomorphism"
:sub-label "Isomorphism"
:sub-href "/isomorphism/"
:sub-nav (~section-nav :items isomorphism-nav-items :current "Offline")
:selected "Offline")
:data (offline-demo-data)
:content (~offline-demo-content :notes notes :server-time server-time))
;; Wildcard must come AFTER specific routes (first-match routing)
(defpage isomorphism-page
:path "/isomorphism/<slug>"

View File

@@ -28,6 +28,9 @@ def _register_sx_helpers() -> None:
"run-modular-tests": _run_modular_tests,
"streaming-demo-data": _streaming_demo_data,
"affinity-demo-data": _affinity_demo_data,
"optimistic-demo-data": _optimistic_demo_data,
"action:add-demo-item": _add_demo_item,
"offline-demo-data": _offline_demo_data,
})
@@ -922,3 +925,45 @@ def _affinity_demo_data() -> dict:
})
return {"components": components, "page-plans": page_plans}
def _optimistic_demo_data() -> dict:
"""Return demo data for the optimistic update test page."""
from datetime import datetime, timezone
return {
"items": [
{"id": 1, "label": "First item", "status": "confirmed"},
{"id": 2, "label": "Second item", "status": "confirmed"},
{"id": 3, "label": "Third item", "status": "confirmed"},
],
"server-time": datetime.now(timezone.utc).isoformat(timespec="seconds"),
}
def _add_demo_item(**kwargs) -> dict:
"""Action: add a demo item. Returns confirmation with new item."""
from datetime import datetime, timezone
import random
label = kwargs.get("label", "Untitled")
return {
"id": random.randint(100, 9999),
"label": label,
"status": "confirmed",
"added-at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
}
def _offline_demo_data() -> dict:
"""Return demo data for the offline data layer test page."""
from datetime import datetime, timezone
return {
"notes": [
{"id": 1, "text": "First note", "created": "2026-03-08T10:00:00Z"},
{"id": 2, "text": "Second note", "created": "2026-03-08T11:30:00Z"},
{"id": 3, "text": "Third note", "created": "2026-03-08T14:15:00Z"},
],
"server-time": datetime.now(timezone.utc).isoformat(timespec="seconds"),
}