Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
0d002e0
docs: design webbooth improvement program
EdmundLimBoEn Jul 24, 2026
bbffbae
docs: clean improvement spec formatting
EdmundLimBoEn Jul 24, 2026
3c31c36
docs: plan release zero foundations
EdmundLimBoEn Jul 24, 2026
36242df
chore: ignore local worktrees
EdmundLimBoEn Jul 24, 2026
d9bb06e
feat: define revisioned event experience schema
EdmundLimBoEn Jul 24, 2026
fd217d7
fix: harden config revision parsing
EdmundLimBoEn Jul 24, 2026
56cbad3
fix: allowlist nested event config fields
EdmundLimBoEn Jul 24, 2026
d234e43
feat: append immutable event config revisions
EdmundLimBoEn Jul 24, 2026
308787e
fix: harden event config mutation saves
EdmundLimBoEn Jul 24, 2026
ccf9a24
fix: bind complete config mutation identity
EdmundLimBoEn Jul 24, 2026
fd3bd36
fix: validate config mutation base revisions
EdmundLimBoEn Jul 24, 2026
cf645f2
feat: restore event config from revision history
EdmundLimBoEn Jul 24, 2026
ae306f6
fix: bind config mutation operation identity
EdmundLimBoEn Jul 24, 2026
7880732
feat: expose config history and restore APIs
EdmundLimBoEn Jul 24, 2026
13624c2
fix: require own frame template keys
EdmundLimBoEn Jul 24, 2026
ca068cd
feat: add admin config history and restore
EdmundLimBoEn Jul 24, 2026
d68252f
fix: harden admin config history interactions
EdmundLimBoEn Jul 24, 2026
6288a45
fix: reconcile admin mutation retries
EdmundLimBoEn Jul 24, 2026
5feafd9
feat: define stable capture upload contract
EdmundLimBoEn Jul 24, 2026
4e176c9
feat: make photo ingest idempotent and privately indexed
EdmundLimBoEn Jul 24, 2026
50c8090
feat: validate and expose stable upload identity
EdmundLimBoEn Jul 24, 2026
35d6849
docs: plan reliable booth control
EdmundLimBoEn Jul 24, 2026
4502205
fix: validate upload identity before config migration
EdmundLimBoEn Jul 24, 2026
a550fc7
feat: upload stable identity from the durable outbox
EdmundLimBoEn Jul 24, 2026
2df641a
docs: plan guest capture improvements
EdmundLimBoEn Jul 24, 2026
2d601fd
fix: preserve durable outbox ordering and acknowledgements
EdmundLimBoEn Jul 24, 2026
3862af0
fix: isolate acknowledged outbox notifications
EdmundLimBoEn Jul 24, 2026
0198745
docs: plan gallery and moderation improvements
EdmundLimBoEn Jul 24, 2026
2ac3f54
docs: plan repeatable operations and export
EdmundLimBoEn Jul 24, 2026
94c627b
fix: make photo feed arrival ordered
EdmundLimBoEn Jul 24, 2026
fda9f43
fix: bound photo feed sequence cursors
EdmundLimBoEn Jul 24, 2026
3256a98
fix: make photo feed recovery bounded
EdmundLimBoEn Jul 24, 2026
8dad757
fix: preserve photo feed contention budget
EdmundLimBoEn Jul 24, 2026
b9467c7
feat: retry queued photos automatically
EdmundLimBoEn Jul 24, 2026
f8144e3
fix: fence queued photo retries
EdmundLimBoEn Jul 24, 2026
183ea8b
fix: preserve outbox lease renewal
EdmundLimBoEn Jul 24, 2026
3215fa5
feat: store private booth operations
EdmundLimBoEn Jul 24, 2026
efb67f3
fix: harden private booth operations
EdmundLimBoEn Jul 24, 2026
e64481d
fix: preserve opaque booth cursors
EdmundLimBoEn Jul 24, 2026
b704a6f
feat: expose booth control APIs
EdmundLimBoEn Jul 24, 2026
df1a158
test: cover booth control boundaries
EdmundLimBoEn Jul 24, 2026
ef8a7d3
feat: monitor booth operations in Admin
EdmundLimBoEn Jul 24, 2026
e6e2143
fix: harden Admin booth operations polling
EdmundLimBoEn Jul 24, 2026
7a88583
feat: unlock booths with authenticated preflight
EdmundLimBoEn Jul 24, 2026
bf2422b
fix: fence booth unlock lifecycle
EdmundLimBoEn Jul 24, 2026
7cb9882
fix: bind booth recovery to exact session state
EdmundLimBoEn Jul 24, 2026
f995a5a
fix: resume recovered booth auth item
EdmundLimBoEn Jul 24, 2026
4e39ecb
chore: remove task report artifact
EdmundLimBoEn Jul 24, 2026
1790fd5
feat: pause and monitor booth sessions
EdmundLimBoEn Jul 24, 2026
acd5d2e
fix: harden booth operational lifecycle
EdmundLimBoEn Jul 24, 2026
74eb5b2
fix: preserve preflight pause on poll failure
EdmundLimBoEn Jul 24, 2026
18ec407
fix: preserve exact auth recovery across heartbeat
EdmundLimBoEn Jul 24, 2026
e431c26
fix: stop booth controllers on event cleanup
EdmundLimBoEn Jul 24, 2026
e69a1a0
feat: configure localized guest capture
EdmundLimBoEn Jul 24, 2026
0728106
fix: harden localized capture config
EdmundLimBoEn Jul 24, 2026
7980587
feat: share adaptive photo feed polling
EdmundLimBoEn Jul 24, 2026
03ecc11
fix: serialize photo feed event changes
EdmundLimBoEn Jul 24, 2026
71efef9
fix: harden adaptive photo feed lifecycle
EdmundLimBoEn Jul 24, 2026
eebbd5c
fix: restart photo feed under strict mode
EdmundLimBoEn Jul 24, 2026
58c742d
feat: support installed booth operation
EdmundLimBoEn Jul 24, 2026
8c4dbc6
test: verify reliable booth control
EdmundLimBoEn Jul 24, 2026
48f95f0
fix: close booth control review gaps
EdmundLimBoEn Jul 24, 2026
120e5d5
feat: expose private export photo sources
EdmundLimBoEn Jul 24, 2026
ed5cec7
feat: build bounded event package artifacts
EdmundLimBoEn Jul 24, 2026
c7a52b0
fix: bound colliding export filenames
EdmundLimBoEn Jul 24, 2026
9dfa003
fix: bound dotted export collisions
EdmundLimBoEn Jul 24, 2026
7919531
fix: scale colliding export filenames
EdmundLimBoEn Jul 24, 2026
c70a398
feat: guard exact composite review decisions
EdmundLimBoEn Jul 24, 2026
fbc145a
feat: open exact handoff photos safely
EdmundLimBoEn Jul 24, 2026
1d4ccf3
fix: harden direct photo handoff
EdmundLimBoEn Jul 24, 2026
a6b5227
fix: fence capture attempts and fallback
EdmundLimBoEn Jul 24, 2026
348b149
feat: review and hand off guest captures
EdmundLimBoEn Jul 24, 2026
37ca23e
fix: harden booth accessibility and cleanup
EdmundLimBoEn Jul 24, 2026
0356ede
feat: page and rebuild private moderation index
EdmundLimBoEn Jul 24, 2026
039719f
feat: moderate photos with exact private cleanup
EdmundLimBoEn Jul 24, 2026
5a0ab30
fix: reject impossible moderation timestamps
EdmundLimBoEn Jul 24, 2026
bc5919d
feat: store reusable event presets
EdmundLimBoEn Jul 24, 2026
e5d10f3
fix: globally order preset pages
EdmundLimBoEn Jul 24, 2026
6afc78d
fix: bound globally ordered preset paging
EdmundLimBoEn Jul 24, 2026
d304a21
fix: version preset ordering indexes
EdmundLimBoEn Jul 24, 2026
6a15263
feat: apply presets through config revisions
EdmundLimBoEn Jul 24, 2026
c49454a
feat: add preset admin state
EdmundLimBoEn Jul 24, 2026
06d708e
feat: define rehearsal evidence reduction
EdmundLimBoEn Jul 24, 2026
5a2b6a2
test: verify guest capture and handoff
EdmundLimBoEn Jul 24, 2026
a429faf
feat: stream enriched event exports
EdmundLimBoEn Jul 24, 2026
814d4a0
feat: add admin post-event package export
EdmundLimBoEn Jul 24, 2026
63f7983
test: verify post-event package release
EdmundLimBoEn Jul 24, 2026
b0201d9
feat: preserve projector with shared gallery controls
EdmundLimBoEn Jul 24, 2026
a25219e
fix: fence shared photo actions
EdmundLimBoEn Jul 24, 2026
7149ce4
feat: add the phone browse gallery
EdmundLimBoEn Jul 24, 2026
5e15f9b
docs: add gallery and rehearsal staging gates
EdmundLimBoEn Jul 24, 2026
cf64aa7
fix: validate preset target before state access
EdmundLimBoEn Jul 24, 2026
fc5fda5
test: verify public gallery browser flows
EdmundLimBoEn Jul 24, 2026
af41f00
feat: scale and localize admin moderation
EdmundLimBoEn Jul 24, 2026
4341b66
feat: store private rehearsal evidence
EdmundLimBoEn Jul 24, 2026
6902975
feat: track authenticated rehearsal uploads
EdmundLimBoEn Jul 24, 2026
6ac67a5
feat: manage event presets in admin
EdmundLimBoEn Jul 24, 2026
ba99180
fix: preserve preset edit concurrency
EdmundLimBoEn Jul 24, 2026
8e1d513
test: cover admin moderation browser journeys
EdmundLimBoEn Jul 24, 2026
0072cbe
feat: guide durable real-device rehearsals
EdmundLimBoEn Jul 24, 2026
89a9536
fix: harden rehearsal evidence ingestion
EdmundLimBoEn Jul 24, 2026
78b92f6
fix: reconcile rehearsal recovery and deletion
EdmundLimBoEn Jul 24, 2026
89a68d7
test: cover preset and rehearsal operations
EdmundLimBoEn Jul 24, 2026
8b6ef00
test: verify destructive rehearsal recovery
EdmundLimBoEn Jul 24, 2026
39b7e51
fix: gate and reconcile guided rehearsals
EdmundLimBoEn Jul 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ jobs:
- run: bun test
- run: bun run validate:frames
- run: bun run build
- run: bunx playwright install --with-deps webkit
- run: bun run test:browser
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ next-env.d.ts
.gstack/
.open-next/
.wrangler/
/test-results/
/playwright-report/
scripts/.migrated.json
.claude/
.worktrees/
EDMUNDS-STUFF.md
116 changes: 116 additions & 0 deletions app/[event]/admin/admin-config-controls.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { expect, test } from "bun:test";
import { renderToStaticMarkup } from "react-dom/server";
import { GROUPS, TEMPLATES } from "../../templates";
import {
BoothKeyControls,
CaptureExperienceControls,
FrameProgrammeControls,
SaveConfigurationButton,
} from "./admin-config-controls";

test("Frame and pack controls are disabled while a configuration mutation is busy", () => {
const [defaultFrame] = Object.keys(TEMPLATES).filter((key) => !TEMPLATES[key].group);
const html = renderToStaticMarkup(
<FrameProgrammeControls
frames={new Set([defaultFrame])}
defaults={[defaultFrame]}
disabled
onToggle={() => {}}
onSetGroup={() => {}}
/>
);
const controls = html.match(/<(?:button|input)\b/g) ?? [];
const disabledControls = html.match(/disabled=""/g) ?? [];

expect(controls.length).toBeGreaterThan(Object.keys(GROUPS).length);
expect(disabledControls.length).toBe(controls.length);
});

test("Booth-key input, generate, and clear are disabled while copy remains available", () => {
const html = renderToStaticMarkup(
<BoothKeyControls
value="a-secure-booth-key"
saved
copied={false}
disabled
placeholder="Unchanged"
onChange={() => {}}
onGenerate={() => {}}
onCopy={() => {}}
onClear={() => {}}
/>
);

expect((html.match(/disabled=""/g) ?? []).length).toBe(3);
expect(html).toContain(">Generate</button>");
expect(html).toContain(">Copy generated key</button>");
expect(html).toContain(">Stored safely — clear key</button>");
});

test("Save is disabled while a configuration mutation is busy", () => {
const html = renderToStaticMarkup(
<SaveConfigurationButton disabled saving={false} onSave={() => {}} />
);

expect(html).toContain("disabled");
expect(html).toContain(">Save configuration</button>");
});

test("capture experience controls expose supported locales and plain capture settings", () => {
const html = renderToStaticMarkup(
<CaptureExperienceControls
enabledLocales={new Set(["en", "ar"])}
defaultLocale="ar"
reviewEnabled
autoAcceptSeconds={5}
countdownAudioDefault={false}
disabled={false}
onToggleLocale={() => {}}
onDefaultLocaleChange={() => {}}
onReviewEnabledChange={() => {}}
onAutoAcceptSecondsChange={() => {}}
onCountdownAudioDefaultChange={() => {}}
/>
);

expect(html).toContain("Enabled guest languages");
expect(html).toContain("English");
expect(html).toContain("简体中文");
expect(html).toContain("العربية");
expect(html).toContain('dir="rtl"');
expect(html).toContain("Default guest language");
expect(html).toContain("Show photo review");
expect(html).toContain("Auto-accept after");
expect(html).toContain("Countdown sounds on by default");
expect(html).toContain('min="1"');
expect(html).toContain('max="30"');
const englishInput = html.match(/<input[^>]*><span lang="en"/)?.[0] ?? "";
const arabicInput = html.match(/<input[^>]*><span lang="ar"/)?.[0] ?? "";
expect(englishInput).toContain('checked=""');
expect(englishInput).toContain('disabled=""');
expect(englishInput).toContain('required=""');
expect(arabicInput).toContain('checked=""');
expect(arabicInput).not.toContain("disabled");
});

test("configuration mutation guard disables every locale and capture control", () => {
const html = renderToStaticMarkup(
<CaptureExperienceControls
enabledLocales={new Set(["en"])}
defaultLocale="en"
reviewEnabled
autoAcceptSeconds={5}
countdownAudioDefault={false}
disabled
onToggleLocale={() => {}}
onDefaultLocaleChange={() => {}}
onReviewEnabledChange={() => {}}
onAutoAcceptSecondsChange={() => {}}
onCountdownAudioDefaultChange={() => {}}
/>
);

const controls = html.match(/<(?:input|select)\b/g) ?? [];
expect(controls).toHaveLength(7);
expect((html.match(/disabled=""/g) ?? [])).toHaveLength(controls.length);
});
Loading
Loading