|
|
@@ -29,7 +29,7 @@ $CATALOG = [
|
|
|
['id' => 4, 'name' => 'Gizmo', 'price' => 29.99, 'featured' => true, 'inStock' => true],
|
|
|
];
|
|
|
|
|
|
-$TYPES = ['counter', 'products', 'cart', 'user', 'notice', 'markdown'];
|
|
|
+$TYPES = ['counter', 'products', 'cart', 'user', 'notice', 'markdown', 'profile'];
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
// Helpers
|
|
|
@@ -234,6 +234,24 @@ MD;
|
|
|
return ['body' => $body];
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Profile/settings form state for the stm-prefill section. Every control of
|
|
|
+ * the type matrix (text, select, checkbox, radio group) round-trips through
|
|
|
+ * the session; `saveAction` is the form's stm-action and the same snapshot
|
|
|
+ * re-sets after the save so the prefill bindings and the echo update.
|
|
|
+ */
|
|
|
+function build_profile(): array
|
|
|
+{
|
|
|
+ $p = $_SESSION['profile'] ?? [];
|
|
|
+ return [
|
|
|
+ 'name' => (string)($p['name'] ?? 'Ada Lovelace'),
|
|
|
+ 'theme' => (string)($p['theme'] ?? 'light'),
|
|
|
+ 'news' => (bool)($p['news'] ?? true),
|
|
|
+ 'cadence' => (string)($p['cadence'] ?? 'weekly'),
|
|
|
+ 'saveAction' => ['uri' => '/api/profile/save', 'method' => 'POST'],
|
|
|
+ ];
|
|
|
+}
|
|
|
+
|
|
|
/** Build the snapshot for a type with its demo scope/options. */
|
|
|
function snap(string $type): ?array
|
|
|
{
|
|
|
@@ -244,6 +262,7 @@ function snap(string $type): ?array
|
|
|
case 'user': return make_snapshot('user', 'session', build_user(), ['stale' => true]);
|
|
|
case 'notice': return make_snapshot('notice', 'flow', build_notice());
|
|
|
case 'markdown': return make_snapshot('markdown', 'session', build_markdown());
|
|
|
+ case 'profile': return make_snapshot('profile', 'session', build_profile());
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
@@ -358,10 +377,11 @@ function initial_frame_for_key(string $key): ?array
|
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
|
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
|
|
|
|
|
-// Static assets.
|
|
|
-if ($path === '/statum.js') {
|
|
|
+// Static assets: the Statum client modules, in load order (core first — the
|
|
|
+// later modules register into the seams it installs).
|
|
|
+if (preg_match('#^/(statum-(core|document|markdown|forms))\.js$#', $path, $m)) {
|
|
|
header('Content-Type: application/javascript');
|
|
|
- readfile(__DIR__ . '/../js/statum.js');
|
|
|
+ readfile(__DIR__ . '/../js/' . $m[1] . '.js');
|
|
|
exit;
|
|
|
}
|
|
|
|
|
|
@@ -545,6 +565,27 @@ if ($path === '/api/user/toggle' && $method === 'POST') {
|
|
|
statum_respond([set_dir(snap('user'))]);
|
|
|
}
|
|
|
|
|
|
+// The stm-prefill demo form's save. The form's controls carry the session
|
|
|
+// values via stm-prefill; this action stores the submitted edits and re-sets
|
|
|
+// the profile snapshot (which also re-runs the prefill bindings — on the
|
|
|
+// non-dirty controls — and the stm-text echo below the form).
|
|
|
+if ($path === '/api/profile/save' && $method === 'POST') {
|
|
|
+ $theme = (string)($_POST['theme'] ?? 'light');
|
|
|
+ $cadence = (string)($_POST['cadence'] ?? 'weekly');
|
|
|
+ $allowedThemes = ['light', 'dark', 'auto'];
|
|
|
+ $allowedCadences = ['daily', 'weekly', 'never'];
|
|
|
+ $_SESSION['profile'] = [
|
|
|
+ 'name' => trim((string)($_POST['name'] ?? '')),
|
|
|
+ 'theme' => in_array($theme, $allowedThemes, true) ? $theme : 'light',
|
|
|
+ 'news' => ($_POST['news'] ?? '') !== '',
|
|
|
+ 'cadence' => in_array($cadence, $allowedCadences, true) ? $cadence : 'weekly',
|
|
|
+ ];
|
|
|
+ statum_respond([
|
|
|
+ set_dir(snap('profile')),
|
|
|
+ ['type' => 'notify', 'kind' => 'success', 'message' => 'Profile saved'],
|
|
|
+ ]);
|
|
|
+}
|
|
|
+
|
|
|
if ($path === '/api/notice/set' && $method === 'POST') {
|
|
|
$_SESSION['notice'] = '<div class="ok"><strong>Notice:</strong> updated at ' . date('H:i:s') . '</div>';
|
|
|
statum_respond([set_dir(snap('notice'))]);
|