feat: public demo experience (Vue SPA with demo mode) #103

Open
pyr0ball wants to merge 9 commits from feature/demo-experience into main
Owner

Summary

  • Adds a full-featured public demo mode for the Vue SPA at demo.circuitforge.tech/peregrine
  • Write-blocked at the API layer (DEMO_MODE=true) with a toast notification on blocked actions
  • Pre-seeded job data via tmpfs + SQL seed file — ephemeral, resets on every container start
  • Welcome modal on first visit (localStorage-gated, no re-show on reload)
  • Per-view HintChips guiding new users through the job search flow (localStorage-dismissed)
  • DemoBanner with accessible CTA buttons (WCAG-compliant contrast in both light and dark themes)
  • resetCard() exposed on JobCardStack so blocked swipes animate back cleanly
  • compose.demo.yml updated from Streamlit app to FastAPI api + Vue web architecture

Files

Area What changed
dev-api.py DEMO_MODE write-block guard on all mutating endpoints
demo/seed.sql Pre-seeded jobs, contacts, interviews for demo
scripts/generate_demo_seed.py Script to regenerate seed SQL from a staging DB
migrations/006_missing_columns.sql Schema columns required by Vue API
web/src/components/DemoBanner.vue Sticky banner with Get free key / Self-host CTAs
web/src/components/WelcomeModal.vue First-visit welcome modal
web/src/components/HintChip.vue Dismissible per-view hint chip
web/src/components/JobCardStack.vue Added resetCard() for blocked-swipe recovery
web/src/composables/useApi.ts demo-blocked error kind; toast on 403 write-block
web/src/views/*View.vue HintChips wired into all main views
compose.demo.yml Replaced Streamlit app with FastAPI api; web on port 8504

Test plan

  • docker compose -f compose.demo.yml --project-name peregrine-demo up -d starts cleanly
  • Navigating to port 8504 shows WelcomeModal on first visit, not on reload
  • DemoBanner visible at top; "Get free key" button readable in both light and dark themes
  • HintChips appear in Job Review, Apply, Interviews, Contacts views; dismiss button works
  • Swipe approve/reject on a job card shows toast "Demo mode — sign in to save changes"; card snaps back
  • Keyboard approve (→) also snaps back with toast
  • pytest tests/test_demo_guard.py passes
## Summary - Adds a full-featured public demo mode for the Vue SPA at `demo.circuitforge.tech/peregrine` - Write-blocked at the API layer (`DEMO_MODE=true`) with a toast notification on blocked actions - Pre-seeded job data via tmpfs + SQL seed file — ephemeral, resets on every container start - Welcome modal on first visit (localStorage-gated, no re-show on reload) - Per-view HintChips guiding new users through the job search flow (localStorage-dismissed) - DemoBanner with accessible CTA buttons (WCAG-compliant contrast in both light and dark themes) - `resetCard()` exposed on `JobCardStack` so blocked swipes animate back cleanly - `compose.demo.yml` updated from Streamlit `app` to FastAPI `api` + Vue `web` architecture ## Files | Area | What changed | |------|-------------| | `dev-api.py` | `DEMO_MODE` write-block guard on all mutating endpoints | | `demo/seed.sql` | Pre-seeded jobs, contacts, interviews for demo | | `scripts/generate_demo_seed.py` | Script to regenerate seed SQL from a staging DB | | `migrations/006_missing_columns.sql` | Schema columns required by Vue API | | `web/src/components/DemoBanner.vue` | Sticky banner with Get free key / Self-host CTAs | | `web/src/components/WelcomeModal.vue` | First-visit welcome modal | | `web/src/components/HintChip.vue` | Dismissible per-view hint chip | | `web/src/components/JobCardStack.vue` | Added `resetCard()` for blocked-swipe recovery | | `web/src/composables/useApi.ts` | `demo-blocked` error kind; toast on 403 write-block | | `web/src/views/*View.vue` | HintChips wired into all main views | | `compose.demo.yml` | Replaced Streamlit `app` with FastAPI `api`; web on port 8504 | ## Test plan - [ ] `docker compose -f compose.demo.yml --project-name peregrine-demo up -d` starts cleanly - [ ] Navigating to port 8504 shows WelcomeModal on first visit, not on reload - [ ] DemoBanner visible at top; "Get free key" button readable in both light and dark themes - [ ] HintChips appear in Job Review, Apply, Interviews, Contacts views; dismiss button works - [ ] Swipe approve/reject on a job card shows toast "Demo mode — sign in to save changes"; card snaps back - [ ] Keyboard approve (→) also snaps back with toast - [ ] `pytest tests/test_demo_guard.py` passes
pyr0ball added 9 commits 2026-04-16 07:31:10 -07:00
- JobCardStack: expose resetCard() to restore card after a blocked action
- JobReviewView: call resetCard() when approve/reject returns false; prevents
  card going blank after demo guard blocks the action
- useApi: add 'demo-blocked' to ApiError union; return truthy error from the
  403 interceptor so store callers bail early (no optimistic UI update)
- ApplyView: add HintChip to desktop split-pane layout (was mobile-only)
- HintChip: fix text color — --app-primary-light is near-white in light theme,
  causing invisible text; switch to --color-text for cross-theme contrast
- vite.config.ts: support VITE_API_TARGET env var for dev proxy override
- migrations/006: add date_posted, hired_feedback columns and references_ table
  (columns existed in live DB but were missing from migration history)
- DemoBanner: commit component and test (were untracked)
--color-primary in dark mode is a medium-light green (#6ab870); white on green
yields ~2.2:1 contrast (fails WCAG AA 4.5:1 minimum). Using --color-surface
(dark navy in dark mode, near-white in light mode) ensures the text always
contrasts strongly with the primary background regardless of theme.

Also tints banner background with 8% primary via color-mix() so it reads as
visually distinct from the page surface without being loud.
chore: update compose.demo.yml for Vue/FastAPI architecture
Some checks failed
CI / Backend (Python) (push) Failing after 1m18s
CI / Frontend (Vue) (push) Failing after 19s
CI / Backend (Python) (pull_request) Failing after 1m13s
CI / Frontend (Vue) (pull_request) Failing after 18s
96a03260f6
Replace the legacy Streamlit `app` service with a FastAPI `api` service
running dev_api:app on port 8601. The Vue SPA (nginx) proxies /api/ →
api:8601 internally so no host port is needed on the api container.

Move web service from port 8507 → 8504 to match the documented demo URL
(demo.circuitforge.tech/peregrine via Caddy → host:8504).
Some checks failed
CI / Backend (Python) (push) Failing after 1m18s
CI / Frontend (Vue) (push) Failing after 19s
CI / Backend (Python) (pull_request) Failing after 1m13s
CI / Frontend (Vue) (pull_request) Failing after 18s
This pull request has changes conflicting with the target branch.
  • dev-api.py
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feature/demo-experience:feature/demo-experience
git checkout feature/demo-experience

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git checkout main
git merge --no-ff feature/demo-experience
git checkout feature/demo-experience
git rebase main
git checkout main
git merge --ff-only feature/demo-experience
git checkout feature/demo-experience
git rebase main
git checkout main
git merge --no-ff feature/demo-experience
git checkout main
git merge --squash feature/demo-experience
git checkout main
git merge --ff-only feature/demo-experience
git checkout main
git merge feature/demo-experience
git push origin main
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Circuit-Forge/peregrine#103
No description provided.