-
Notifications
You must be signed in to change notification settings - Fork 0
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.
- Zero friction — every unauthenticated visitor is automatically in guest mode. No button click required.
- Full reachability — every patient-facing page is browsable.
- Honest boundaries — any write action clearly prompts for sign-up instead of silently failing.
- Database safety — guests are never a real session; no writes are possible.
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.
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.
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 defaultPure, 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.
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.
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.
- 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 byrequireRealUser().
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.