Skip to content

Guest Mode

harshcode1 edited this page Jun 21, 2026 · 2 revisions

Guest Mode

Guest Mode lets anyone explore the entire product with realistic demo data without creating an account, while ensuring no guest activity ever touches the database.

Goals

  1. Zero friction — every unauthenticated visitor is automatically in guest mode. No button click required.
  2. Full reachability — every patient-facing page is browsable.
  3. Honest boundaries — any write action clearly prompts for sign-up instead of silently failing.
  4. Database safety — guests are never a real session; no writes are possible.

How it works

Guest Mode is implemented entirely client-side. No fake server session or demo user is created — user stays null. Guest mode is not a stored state; it is derived automatically:

// AuthContext.js
const isGuest = !user && !loading;

Any visitor who is not authenticated and whose auth state has finished loading is a guest. There is no localStorage flag, no opt-in button, and no separate mode to enter or exit.

State (in AuthContext)

isGuest            // derived boolean: !user && !loading
requireRealUser()  // the auth-gate — opens the sign-up modal for guests
guestPrompt        // { action } | null  → drives the GuestGateModal
clearGuest()       // clears guestPrompt (called on login/register)

enterGuestMode and exitGuestMode are kept as no-ops for call-site compatibility but perform no state changes.

Middleware behaviour

API routes return 401 JSON for unauthenticated requests — not a 307 redirect to /login. This means fetch().json() always gets a parseable response, not an HTML login page:

// middleware.js
if (isProtectedApi && !authenticated) {
  return new NextResponse(
    JSON.stringify({ error: 'Not authenticated' }),
    { status: 401, headers: { 'Content-Type': 'application/json' } }
  );
}
// Page routes: all visitors are allowed through — guest mode is default

Demo data (lib/demoData.js)

Pure, deterministic generators (seeded pseudo-random for stable output) that match each API's response shape exactly:

Generator Feeds Notes
demoMoods(days) Dashboard, Mood ~30 days with a gentle upward "recovery" trend
demoAssessments() Dashboard, History, Details 5 PHQ-9/GAD-7 results trending downward (improving)
demoDoctors() Doctors list 6 verified specialists with ratings/reviews
demoAppointments() Appointments upcoming, pending, past & cancelled
demoChat() AI Chat a sample anxiety/sleep conversation

Because the demo objects mirror the real API shapes, pages render them with no special-casing in the presentational components.

Page wiring pattern

Every guest-enabled page follows the same minimal pattern:

// Use demo data for guests, real fetch for authenticated users
useEffect(() => {
  if (user) fetchData();
  else if (isGuest) { setData(demoX()); setLoading(false); }
}, [user, isGuest]);

Pages do not redirect unauthenticated visitors to /login — they render demo data instead.

The auth-gate

Write actions call requireRealUser('<action>'):

const handleSubmit = (e) => {
  e.preventDefault();
  if (!requireRealUser('save your mood')) return;  // opens the modal for guests
  // …real submit for authenticated users…
};

For a real user it returns true and the action proceeds. For a guest it sets guestPrompt and returns false, which surfaces a global modal: "Sign in to {action}" with Create free account / Sign in / Keep exploring.

Gated actions include: saving a mood, sending an AI chat message, booking/cancelling an appointment, syncing Google Calendar. Taking an assessment is allowed (results compute live) but the save step is skipped for guests.

UI affordances

  • Entry — no button needed. Every unauthenticated visit automatically sees demo data.
  • GuestBanner — a sticky banner under the navbar ("Demo mode — you're viewing demo data") with Sign in and Create account links. Shown on all pages except /, /login, /signup, and /signup/doctor.
  • Navbar — guests get the full patient navigation plus a Sign In button.
  • GuestGateModal — the global sign-in prompt, mounted once in the root layout, triggered by requireRealUser().

Why client-side only?

Keeping Guest Mode out of the server means:

  • The database is structurally incapable of receiving guest writes.
  • No cleanup jobs for throwaway demo accounts.
  • Auth, RBAC, and the real data path stay completely untouched and uncomplicated.

Related

Clone this wiki locally