Skip to content

Web app: responsive desktop panel, guest mode and GitHub Pages deploy - #21

Merged
Teksiak merged 16 commits into
developfrom
feature/web
Oct 4, 2026
Merged

Teksiak merged 16 commits into
developfrom
feature/web

Conversation

@Teksiak

@Teksiak Teksiak commented Oct 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

The whole app now runs on the web as a responsive panel, with sign-in or Continue as guest on the real API.

  • Web app = the Expo app's web build. All logic, hooks, the Supabase/Gemini clients and copy are shared with the phone apps. On the web from 768 px a desktop layout takes over: phones and web below 768 px keep the phone layout, and iOS/Android are unchanged (everything is gated by useLayout()).
  • Desktop panel:
    • Sidebar (folds to an icon rail at 768–1199 px).
    • Coach chat docked on the right (resizable) or as a drawer; ⌘K opens it.
    • Desktop layouts for Today, Calendar, plan history, You/profile, Settings, session, Log it (with FIT/GPX drag and drop), the gym runner (keyboard driven) and the onboarding wizard.
    • Feedback and Edit answers open as dialogs.
    • Hover, focus and keyboard support across the UI kit.
  • Guest mode (web only): "Continue as guest" on Welcome and the landing page, plus /app/guest.
    • A guest is an anonymous Supabase user named Guest. It goes through the real onboarding, gets a Gemini plan, and can log, give feedback and chat.
    • From Settings it can save its progress (email + password on the same user) or leave guest mode.
  • Landing page: the fake demo is removed (−4.5k lines). Its calls to action open the app: "Try it as a guest" and "Sign in".
  • Deploy: npm run build:pages builds the landing page at /hackyeah-2026/ and the app at /hackyeah-2026/app/. The app is a single-page export, with a 404.html fallback so deep links and reloads work.
    • The Pages workflow builds pull requests and deploys from main/develop.
    • npm run preview:pages serves the result the way GitHub Pages does.

Docs: new web app feature doc; AGENTS.md, development, deployment, design and website docs updated.

Acceptance criteria

  • Landing → Try it as a guest → anonymous user on hosted Supabase → onboarding → Build plan (Gemini) → Today shows the plan. This flow was run on the production build.
  • Guest card in Settings: save progress or leave guest mode. Checked in mock mode, and covered by tests.
  • Guest off → "Guest mode isn't available right now. Sign in with email instead." Covered by tests.
  • Auth redirects include the base path (…/hackyeah-2026/app/auth/callback, /auth/reset). Both URLs are in the Supabase allowlist.
  • Desktop shell, coach dock and desktop layouts render from 768 px; phone layout below.
  • Deep links and reloads on the Pages build open the right page (404.html fallback).
  • Email and Google sign-in on the deployed site. Not run against the real backend; checked by the redirect URL code and tests.

Verification (4 October 2026)

Commands (in apps/mobile unless noted):

Check Result
npm run typecheck (app + 3 test projects) clean
npx eslint src clean
npm run test:remote 173/173, including 13 new guest tests
npm test 13/13
npm run test:calendar 77/77, in two time zones
npm run test:activity-import 30/30, including 4 new browser-file tests
npm run typecheck --workspace @hackyeah/website clean
npm run build:pages (repo root) builds in about 14 s; app 7.2 MB, 30 files

Production build, served with preview:pages at 1440 px, against the hosted Supabase project:

  • Landing renders; Try it as a guest creates the guest and opens the onboarding wizard.
  • The wizard's rail, Enter-to-continue and the Review cards all work.
  • Build plan gave a Gemini week: "Gentle outdoor walk" today and 3 sessions next week.
  • Today dashboard and the coach dock work. The page's global styles and icon are present. The only console error is the expected 404 status of the deep-link fallback.

Mock mode: each area was checked at 1440/1024/390 by the agent that built it. Browser checks were trimmed late on to save credits, so some widths and states were checked by DOM only; the PR notes per area are in the commit messages.

Notes for reviewers

  • Supabase (hosted): anonymous sign-ins are on, and the two Pages redirect URLs are allowlisted (done 4 October). Optional: set the Site URL to https://taqix.github.io/hackyeah-2026/app/.
  • Workflow values: the workflow carries the publishable Supabase URL and key as fallbacks, because a write-access account can't set Actions variables. They ship in the client bundle anyway and RLS protects the data. Repository variables of the same name override them.
  • Google Calendar for a guest: it links Google to the anonymous user (manual linking is on). Not tested against the real backend.
  • Phone-only features on the web: the step counter and device calendar don't exist in browsers. The web hides those rows and plans from preferred times or Google Calendar.

🤖 Generated with Claude Code

Teksiak and others added 16 commits October 4, 2026 05:29
The landing page no longer ships the clickable fake demo. Its calls to
action open the real app instead: "Try it as a guest" goes to app/guest and
"Sign in" to app/welcome, relative to the site root so they work under the
GitHub Pages project path. VITE_APP_URL points them at a local dev server.
Old #/try links forward to the guest entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the web, Welcome offers "Continue as guest" and /guest starts one
directly (the landing page links there). A guest is an anonymous Supabase
user named Guest that uses the real product API and starts at onboarding.
It can save its progress by adding an email and password to the same user,
or leave guest mode. Phones have no guest entry.

Desktop widths show the sign-in screens as a split layout with a brand
panel. Auth redirects on the web now include the router base path, so they
return to the GitHub Pages app URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the web, every interactive kit component gets hover feedback, a pointer
cursor and a keyboard focus ring; sliders, Segmented and radio groups work
with the mouse and the keyboard. On desktop widths a Sheet opens as a
centered dialog that closes on Escape or a click outside and returns focus.
New desktop pieces: Panel, StatTile, Tooltip (IconButton shows one from its
label), Kbd, HoverBand, webStyle and useWebKeyboard. Native rendering is
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From 768 px the Calendar tab is a month board of day tiles with session
chips beside a sticky day panel, with keyboard navigation (arrows, T, P/N).
Plan history shows a version timeline next to the selected version's
changes and sessions. On the web the device calendar is reported as
unsupported, so sync and planning never ask for it. Phones keep their
layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ChatPanel renders the conversation either as the /coach page or inside the
desktop shell's dock and drawer, taking the prefill, attached session and
Move it intent as props. On the web Enter sends and Shift+Enter adds a line,
Escape closes the dock, new messages ease in, and the thread no longer
jumps to the end after scrolling up. /coach on desktop centres the thread;
phones keep the same screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From 768 px the You tab is a dashboard: the assistant's summary, the
answers that shape the plan, how Movo sees the person, progress across the
weeks, feedback and shortcuts, with an Edit answers menu. Profile pages,
Settings (two columns, appearance preview tiles) and Data & privacy get
desktop layouts. Guests see no email row and leave through the guest card
instead of Sign out. On the web, phone-only calendar and step rows are
replaced by one note. Phones keep their layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the web from 768 px, signed-in pages render inside a shell: a sidebar
(Today, Calendar, You, Plan history, account card, "Ask your coach" with
Cmd/Ctrl K) that folds to an icon rail at medium widths, and the coach chat
as a resizable docked column on wide screens or a drawer at medium widths.
useOpenChat opens the dock there and still pushes /coach on phones.
Feedback, Log it and Edit answers open as centred dialogs; the gym runner
stays full screen.

The responsive foundation (useLayout, Columns, Grid, PageHeader and a
desktop-aware Content) gates every desktop layout; native and web below
768 px keep the phone layout. Pages get per-route titles, theme colours,
scrollbars and focus rings on the web.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From 768 px onboarding shows a step rail with each answer and the progress,
the questions in a card with a short "why we ask" aside, inline Back and
Continue (Enter continues) and slide transitions. Choices, places and sports
become card grids; Review shows the answers as cards before Build plan. On
the web Review offers only the Google Calendar row. Phones keep the same
steps and order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the dock over a nested page, router.navigate pushed a second set of
tabs. It now goes through the shell's navigation, which goes back down to
the tabs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GitHub Pages export renders from Expo's plain SPA template, so
app/+html.tsx (scrollbars, selection, focus ring, background, Movo icon)
never ran there. The style sheet and icon now live in one module that
+html.tsx uses and DocumentHead adds at runtime when the document lacks them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p web

From 768 px the session page has a main column (hero, coach tip, the plan
at a larger media size) and a sticky action column (Start or Log it, Move
it, Skip it, Ask your coach, and the done state). Log it is a page with a
summary card; FIT and GPX files import from the browser's picker or by drag
and drop, read in memory. Feedback opens as a dialog and its options take
Space and arrow keys. The gym runner is a full-screen focus mode with
keyboard shortcuts; a late timer in a background tab still ends on time.
Phones keep their layouts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From 768 px Today shows a page header with week paging (arrows, T) and
"Ask your coach", a larger hero for the selected day with the session's
steps, a seven-day week board, and a sticky side column: week progress,
next week, why this plan and quick plan changes. Building, failed, empty
and past-week states have desktop layouts. The step counter reports itself
unsupported on the web. Phones keep their layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
npm run build:pages builds the landing page and exports the Expo app as a
single-page app under /hackyeah-2026/app (app.config.ts sets the base path
only when EXPO_WEB_BASE_URL is set; local and native builds are unchanged),
then copies the app page to 404.html so deep links and reloads work. It
refuses to build without Supabase values or with a secret key.

The Pages workflow typechecks and builds on pull requests without
deploying, and deploys from main and develop. It uses repository variables
when set and otherwise the publishable Supabase URL and key, which the
client bundle carries anyway.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds the web app feature doc (desktop panel, shell, guest mode, redirects,
hosted settings) and updates AGENTS.md, the development and design docs and
the website README: the web app is the Expo web build, guests are anonymous
Supabase users on the real API, and the website demo is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
npm run preview:pages serves dist/pages under /hackyeah-2026/ the way
GitHub Pages does (file, .html, index.html, else 404.html), so reloading an
app deep link works as on the real site. The pages-preview launch
configuration runs it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@prelint

prelint Bot commented Oct 4, 2026

Copy link
Copy Markdown

Ship with changes Web app launch: guest mode, desktop panel and live API replace fake demo

Product decisions in this change

Agree with concerns 1. Guest mode is web-only and will never appear in the phone apps.

The design doc is explicit: no guest entry on phone apps. For the web launch this is reasonable — the friction of app store sign-in is different from a browser visit, and mobile OSes provide system sign-in paths that reduce the need for anonymous access. The concern is forward compatibility. The AuthProvider type now includes 'guest' and isGuest() lives in the shared API types layer, which means the mobile app already knows about the concept. If the mobile app ships in the future and wants to add a guest path, the contract is already there. But if the decision is permanent — mobile never gets guest — then the web-only gate should be enforced at the UI layer only, not leaked into shared types. Users who start on web as guests and then want to continue on mobile will find the concept simply absent, with no explanation.

Option What it gives users What it costs Effort to change later
Current approach (web only) Low friction for web visitors Mobile users cannot try before signing up Low: add a mobile entry point later
Also on mobile Consistent try-before-sign-up on all platforms More surface to maintain; OS sign-in already reduces friction N/A

The concern is worth one sentence in Settings or a tooltip that tells a web guest they will need to sign in on the phone app.

Agree with concerns 2. Guest data is framed as "stays in this browser" even though it is backed by Supabase's server-side anonymous auth.

The copy GUEST_PROMISE = 'Try everything without an account. Your guest plan stays in this browser.' sets a clear, low-anxiety expectation: no account, no risk. The actual mechanism is a Supabase anonymous user session stored in browser storage; the plan itself lives on the server. The framing is practically accurate — lose the session (clear storage, switch browsers) and access is gone — but it may surprise users who expect their data to survive a browser reinstall or who try to open the plan on a second device. No prior art here is perfect: Linear's guest boards say "public link" and Notion's guests say "account required for access." The current copy is good enough for a hackathon launch, but before broad public promotion the description should clarify that the plan itself is saved and can be retrieved by saving progress, not that the plan is stored locally.

Agree with concerns 3. Email confirmation after upgrading a guest account leaves the person in a liminal state: still a guest until the email is confirmed, but appearing to have saved their progress.

When upgradeGuest returns confirmation_required, the docs say the account stays a guest until confirmed. This is correct Supabase behavior. The product risk is the gap: the person has entered an email, seen some confirmation-sent message, and now sits in a browser session that is still technically anonymous. If they clear their browser, restart, or the session expires before confirming, they may lose their plan with no way to recover it (the email is attached but the account is not yet persistent). This is a known edge case for any confirmation-required upgrade flow. The question is whether the UI makes this risk clear. The diff shows the error codes are handled (confirmation_required, email_taken, etc.) but does not show the message the person reads. The acceptance criteria list does not include this state. It warrants an explicit check: does the save-progress screen tell the guest to keep this browser tab open until confirming, and what happens if they do not?

Agree 4. Upgrading a guest to a full account preserves the existing plan and history — the Supabase user ID does not change.

upgradeGuest updates the email and password on the same user; it does not create a new user and copy data. This is the right call. Users build a plan during onboarding and would find it unacceptable if saving progress reset their journey. The onSuccess handler updates only the session and account query cache, and the plan and calendar data stay untouched. This matches how well-known products handle the anonymous-to-registered upgrade: Canva, Figma and Linear all keep design or board data when an anonymous user registers.

Agree 5. The landing page's fake demo layer is entirely removed, replaced by two CTAs that open the real app.

~4,500 lines of simulated logic (chat pipeline, plan builder, session data) maintained a demo that diverged from the real product. The removal is clean: the landing page still shows phone mockups (with fixed illustration data) and the two CTAs — "Try it as a guest" and "Sign in" — route to live Supabase. The PR verifies the end-to-end flow on the production build. For a product in active development, a fake demo is a maintenance liability and a trust risk (visitors see behavior the real product may not match). The guest mode makes this removal safe, because a first-time visitor can still try the product without an account.

Agree with concerns 6. The desktop web app is the Expo mobile app's web build, sharing all business logic, hooks and copy with iOS and Android.

A single codebase gated by useLayout() is pragmatic for a hackathon. Logic is never duplicated, the Supabase and Gemini clients are shared, and phone behavior is unchanged. The concern is experiential: users on a 1440 px screen are running code that was designed mobile-first, with desktop layout grafted on. The desktop shell is extensive (sidebar, resizable coach dock, keyboard shortcuts, drag-and-drop) and clearly thoughtful, but the underlying component tree is still a React Native tree rendered via React Native Web. Some web-native affordances — browser back button behavior, URL bookmarking of specific days, text selection in chat — may feel off. This is not a reason to reject the PR, but product decisions made later for the web (pagination, multi-column editing, hover states with rich previews) will compete with what React Native Web can express.

Option What it gives users What it costs Effort to change later
Current approach (shared Expo) Same logic, one codebase, faster iteration Some web-native idioms are harder to reach High: extracting a separate web UI is a large project
Separate web product Web-native feel, URL-driven navigation, text selection Double the UI surface, two design systems N/A

For a hackathon, the current approach is the right one.

Agree 7. 768 px is the threshold where the phone layout switches to the desktop shell: below it, even in a browser, the person sees the floating tab bar and the phone screen structure.

768 px is a standard tablet-wide breakpoint and matches the repo's own BREAKPOINTS token. A phone-like layout at narrow browser widths is correct: a sidebar at 400 px would collapse everything. The choice means a user on a small tablet in portrait mode gets the phone layout; at 768 px landscape they get the sidebar. This matches how well-known apps (Notion, Linear, Todoist) handle the same range. The floating tab bar is already hidden on desktop via sidebar ? null : <FloatingTabBar {...props} />, so the two modes are cleanly separated.

Agree 8. Phone-only features (device calendar and step counter) are shown on the web as a single explanatory row rather than shown individually as unavailable.

The PhoneOnlyRow component shows "Phone calendar and steps" with "These connect in the Movo app on your phone". This is better than two rows that each say "Not available" because it explains the gap positively and points to where the feature lives. It avoids the cognitive load of reading two disabled rows and wondering whether they are broken or simply not implemented for the platform. The decision aligns with the product's positioning: the web app is the entry point, the phone app is where the full sensor stack lives.

Agree 9. The coach chat becomes a persistent docked panel beside the page on desktop, not a full-screen overlay.

On mobile, chat takes over the screen. On desktop at 768 px+ the coach opens as a drawer; on wide screens it is a resizable panel beside the page. This matches how Intercom, Notion AI, and GitHub Copilot Chat model the same conversation-in-context pattern: the page you are looking at stays visible while you talk about it. The ⌘K shortcut (⌘K on Apple, Ctrl+K elsewhere) follows VS Code's command palette convention and is discoverable from the sidebar coach button. The resizable width is persisted per browser via localStorage, which is a small but thoughtful detail — the person sets it once and it stays.

Agree with concerns 10. Supabase anonymous sign-in backs the guest feature; guest accounts are real server-side users.

Using Supabase's built-in anonymous auth is the simplest correct choice: RLS protects data, the upgrade path is a first-class Supabase feature, and no custom session store is needed. The concern is operational: Supabase has limits on anonymous users and, depending on the project's retention policy, may clean up inactive anonymous accounts. The PR notes acknowledge that anonymous sign-ins are enabled and the redirect URLs are allowlisted, but does not state whether a retention policy is set. An unconverted guest who returns after 30 or 90 days may find their session invalid and their plan gone with no warning.

Agree with concerns 11. The app is deployed under the sub-path `/hackyeah-2026/app/` on GitHub Pages, requiring a custom `webAppUrl` helper for all auth redirects.

GitHub Pages forces a project path unless a custom domain is configured. The webAppUrl helper compensates by prepending the base path so OAuth callbacks, email confirmations and password resets land in the right place. The PR description confirms the two URLs are in the Supabase allowlist. The concern is discoverability: a URL like https://taqix.github.io/hackyeah-2026/app/ is hard to communicate, hard to remember, and looks like a development preview rather than a product. The README now shows this URL as the primary entry point. For a hackathon this is acceptable, but any move toward real users should add a custom domain so the sub-path complexity disappears.

Agree 12. The onboarding wizard gets a dedicated step rail layout on desktop, while mobile keeps the top progress bar and single-column steps.

The wizard rail shows all steps, marks how far the person has gone, and lets them jump to any reached step. This is a richer wayfinding model than the mobile progress bar and is warranted by the desktop's available screen width. The diff shows the rail and the step stack co-exist in one layout; crossing the 768 px threshold does not remount the steps, which means state is preserved if a user resizes the window mid-onboarding. This detail is correct and avoids a subtle data loss scenario.

Open questions

  • What does the user see and what is the recommended action when confirmation_required is returned after upgrading a guest? Is there a way to resend the confirmation email from within the app? If the guest clears their browser before confirming, is the plan lost permanently?

  • Does Supabase have a cleanup or expiry policy set for anonymous users on this project? A guest who returns after weeks or months may find their session and plan gone without warning.

  • Is there a way for a guest to open their plan on a second device or browser before upgrading? If not, is this surfaced anywhere so users do not expect cross-device access?

  • Will the mobile app ever support guest mode? If not, the AuthProvider = 'guest' type and isGuest() helper in shared types are misleading — shared types suggest a shared capability.

  • Google Calendar for a guest (manual linking to the anonymous Supabase user) is noted as untested against the real backend. What is the expected behavior if Google linking fails mid-onboarding for a guest? Does the guest lose their session or proceed without calendar access?

Recommendation

Ship with changes
Every decision is either sound or agree-with-concerns at a level that does not block the PR. The one issue worth resolving before broad promotion is the liminal guest state during email confirmation: if the guest clears their browser between clicking "Save progress" and confirming their email, their plan may be unrecoverable, and it is not clear the current UI warns them. The anonymous-user retention question should also be answered by the team before pointing real traffic at the URL. Neither of these is a code change; both are a design question and an ops check.

@Teksiak
Teksiak merged commit b8609a0 into develop Oct 4, 2026
4 checks passed
@vende11s
vende11s deleted the feature/web branch October 4, 2026 06:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant