A study dashboard for tracking subjects, tasks (with subtasks), notes, weekly planning, focus sessions, and analytics.
- React 19 + TypeScript + Vite
- Tailwind CSS
- React Router 7
- Supabase (auth + cloud data persistence)
npm install
npm run dev| Command | Description |
|---|---|
npm run dev |
Start the dev server |
npm run build |
Typecheck + production build |
npm run preview |
Serve the production build |
npm run lint |
ESLint |
npm run typecheck |
TypeScript check |
npm test |
Build + Playwright smoke tests (Phase 1) |
StudyOS works without any backend:
- Local-only mode — default. Set no env vars: everything is stored in
localStorageviaDataProvider, and auth screens are not enforced. - Authenticated mode — copy
.env.exampleto.envand fill in your Supabase project URL and anon key. Sign in / sign up at/loginand/signup; all app routes are then protected, the signed-in email shows in the sidebar, and sign out is available there.
Keep DataProvider as the storage layer. In local-only mode it stores data in localStorage; with Supabase configured and a signed-in user, the same data layer reads and writes the cloud tables (profiles, subjects, tasks, notes, focus_sessions), so data persists across devices.
The schema lives in two migrations under supabase/migrations/:
20260810000000_studyos_schema.sql— core tables20260812000000_studyos_profile_phase.sql— addsfull_name,class_year, andavatar_urltoprofiles(nullable, so existing rows are preserved)
Apply them to a fresh Supabase project with the Supabase CLI:
supabase link --project-ref <your-project-ref>
supabase db pushor open your project's SQL editor in the Supabase dashboard and paste the file contents.
Tables:
| Table | Purpose |
|---|---|
profiles |
One profile per auth user; created automatically on sign-up |
subjects |
Study subjects (labels for tasks and notes) |
tasks |
Tasks with priority, due_date, completed, subtasks as JSONB |
notes |
Markdown study notes |
focus_sessions |
Completed pomodoro sessions |
Security: every user-owned table has a user_id column referencing auth.users(id), Row Level Security is enabled, and the anon role has no table privileges. Policies allow SELECT/INSERT/UPDATE/DELETE only where auth.uid() = user_id, so users can never read or modify another user's rows. Profiles are only selectable/updatable by their owner and are inserted by a trigger (handle_new_user) when an auth account is created.
After signing in, if the signed-in profile has no full_name or class_year yet, the app redirects to /profile-setup before the dashboard. Completing the form saves the profile to Supabase. The profile (name, class year, avatar) can be edited any time from /profile (avatar section in the sidebar / top bar). Avatars currently use a safe initials fallback; image upload via Supabase Storage is a later step, but profiles.avatar_url already exists for it. In local-only mode the same profile fields are stored in localStorage.
npm test builds the app and runs tests/phase1-smoke.mjs with Playwright. These tests assume local-only mode, so run them without a .env containing VITE_SUPABASE_* values (or temporarily rename .env).