Dashboard & workspace
Student home after login. Course study tabs are in 06-study-views.md. Settings overlay is 05-settings.md. Flows: 00-how-everything-connects.md.
Access: Workspace layout requires logged-in + onboarded (else /welcome or /login?next=/dashboard).
/dashboard is the only student home. Course workspaces (/my-courses/{slug}) stay entitlement-gated (proxy → /pricing if unpaid). Admins count as subscribed for that gate. Marketing / is separate: only users with current active access are redirected there from /.
Canonical home: /dashboard. /my-courses and /dashboard/my-packs redirect here (query q and error=no_access preserved). Course study URLs stay at /my-courses/{slug}.
Redirects: /my-courses/library|progress|semester-plan → /dashboard/…. /dashboard/course/[slug]/… → /my-courses/[slug].
Shared chrome (every workspace page)
SECTION: Desktop sidebar
Copy: Expanded wordmark Gradeful; collapsed G. Nav labels below. Streak card This week plus a dynamic rhythm title. Account chip shows profiles.full_name or Your account, campus line or email.
Data shown: profiles.*; streak from user_streaks / study_sessions; feature flag aiTutorEnabled; profiles.role for Admin
ELEMENT: Gradeful / G (brand)
Action: navigate
Destination: /dashboard
Writes to: none
Reads from: none
ELEMENT: Collapse / Expand sidebar
Action: toggle
Destination: none
Writes to: cookie via setWorkspaceSidebarCollapsed (except on course routes: local only)
Reads from: cookie
ELEMENT: Dashboard
Action: navigate
Destination: /dashboard
Writes to: none
Reads from: active on /dashboard (aliases /my-courses, /dashboard/my-packs redirect here)
ELEMENT: Library
Action: navigate
Destination: /dashboard/library
Writes to: none
Reads from: none
ELEMENT: Progress
Action: navigate
Destination: /dashboard/progress
Writes to: none
Reads from: none
ELEMENT: Semester Plan
Action: navigate
Destination: /dashboard/semester-plan
Writes to: none
Reads from: none
ELEMENT: AI Tutor
Action: navigate
Destination: On a course: same course URL + ?panel=ai; else /dashboard/ai-tutor
Writes to: none
Reads from: Visible when aiTutorEnabled
ELEMENT: Admin dashboard
Action: navigate
Destination: /admin
Writes to: none
Reads from: profiles.role is admin
ELEMENT: Streak card / flame (collapsed)
Action: open modal
Destination: calendar dialog (month grid; legend Streak day Missed Today)
Writes to: none
Reads from: user_streaks, study_sessions, profiles, platform weeklyStudyGoalMinutes
ELEMENT: Account chip (opens flyout)
Action: open modal
Destination: account menu
Writes to: none
Reads from: profiles.avatar_url, full_name, campus fields, Auth email
ELEMENT: Settings (flyout)
Action: open modal
Destination: Settings tab profile
Writes to: none
Reads from: none
ELEMENT: Get help (flyout)
Action: open modal
Destination: Settings tab help
Writes to: none
Reads from: none
ELEMENT: Upgrade plan (flyout)
Action: navigate
Destination: /pricing
Writes to: none
Reads from: none
ELEMENT: Dashboard (flyout)
Action: navigate
Destination: /dashboard
Writes to: none
Reads from: none
ELEMENT: Learn more (flyout) Action: toggle Destination: submenu Writes to: none Reads from: none
ELEMENT: About Gradeful / Privacy policy / Terms of service / Refund policy / Academic integrity / All policies
Action: navigate
Destination: /about /privacy /terms /refunds /academic-integrity /policies
Writes to: none
Reads from: none
ELEMENT: Log out / Logging out…
Action: submit
Destination: /
Writes to: clears session
Reads from: none
SECTION: Mobile header
Copy: Logo Gradeful
Data shown: same as sidebar account
ELEMENT: Open workspace menu (hamburger)
Action: toggle
Destination: drawer with full sidebar (showCollapseToggle=false)
Writes to: none
Reads from: none
ELEMENT: Close menu / backdrop / Escape Action: toggle Destination: closes drawer Writes to: none Reads from: none
ELEMENT: Gradeful
Action: navigate
Destination: /dashboard
Writes to: none
Reads from: none
ELEMENT: Open settings (gear)
Action: open modal
Destination: Settings profile
Writes to: none
Reads from: none
ELEMENT: Open account menu
Action: open modal
Destination: same flyout as sidebar
Writes to: none
Reads from: profiles.*
SECTION: Course right panel (course player only)
Copy: Collapsed Open stats / Open AI tutor. Expanded tabs Stats AI Tutor. Stats labels Notes Quizzes Exam prep plus notes %, flashcards due, quiz scores, exam days. AI loading Loading AI tutor... Modes Explain Guide me Practice me Review my mistake Quiz me. Suggestions Explain this concept Help me practice What should I review for my upcoming exam?
Data shown: content_progress, flashcard due counts, quiz scores, course_events
ELEMENT: Open stats / Open AI tutor / Collapse panel
Action: toggle
Destination: none
Writes to: cookie setWorkspaceAiPanelExpanded
Reads from: cookie; aiTutorEnabled
ELEMENT: Stats / AI Tutor tabs Action: toggle Destination: none Writes to: none Reads from: none
ELEMENT: Module list row
Action: navigate
Destination: /my-courses/{slug}?module=&tab=notes
Writes to: none
Reads from: course_modules
ELEMENT: View all chapters → Action: navigate Destination: course overview Writes to: none Reads from: none
ELEMENT: AI suggestion chips / chat send
Action: submit
Destination: none
Writes to: POST /api/ai-tutor with course/module IDs (usage metrics only; no transcript table)
Reads from: entitled Gradeful pack items, student progress, student-entered exam dates
ELEMENT: Ask AI Tutor (floating)
Action: toggle
Destination: opens AI panel
Writes to: none
Reads from: aiTutorEnabled
PAGE: /dashboard
Purpose: Restored pre-redesign student home (Git 89d0991): greeting, resume card, exam-mode banner, course cards, what needs attention, Today calendar, study hours, recent activity. Not the later next-study-action redesign.
Access: logged-in + onboarded (not subscriber-gated). Unpaid accounts see the empty/onboarding pack state, not a marketing homepage inside the workspace.
Entry points: / when the user has active access; /auth/home; welcome submit; login next; sidebar Dashboard; public header Dashboard; checkout Go to my courses → /dashboard
SECTION: Top bar
Copy: Greeting Good morning, {firstName}. Search Search your courses...
Data shown: profiles.full_name first name
ELEMENT: Search
Action: submit
Destination: paid /dashboard?q=…; free /courses?q=…
Writes to: none
Reads from: query q filters owned courses on the paid home
ELEMENT: Notification bell
Action: toggle
Destination: notification panel
Writes to: notifications.is_read (markAllNotificationsRead; auto after 2s open)
Reads from: notifications (limit 20) + realtime INSERT
Empty: No notifications yet
Badge: unread count
Row: navigates action_url when set
Note: evening streak-risk notifications are not generated
ELEMENT: Mark all read
Action: submit
Destination: none
Writes to: notifications.is_read
Reads from: unread rows
ELEMENT: Open settings (md+)
Action: open modal
Destination: Settings profile
Writes to: none
Reads from: none
ELEMENT: AccountChrome (mobile top bar)
Action: open modal
Destination: account flyout / settings
Writes to: none
Reads from: profiles.*
SECTION: Access error
Copy: We couldn't open that course. Make sure it's in your plan, or try opening it again from the list below.
Data shown: query error=no_access
SECTION: Resume where you left off
Copy: Resume where you left off plus last content type / chapter.
Data shown: latest content_progress.last_accessed_at among owned modules
ELEMENT: Resume CTA
Action: navigate
Destination: /my-courses/{slug}?module=&tab=
Writes to: none
Reads from: content_progress
SECTION: Exam mode banner
Copy: Exam mode plus nearest exam headline. CTA into that course.
Data shown: exam_schedules / course_events on owned courses
ELEMENT: Open course
Action: navigate
Destination: /my-courses/{slug}
Writes to: none
Reads from: exam-mode builder
SECTION: Your courses
Copy: Your courses or Search your courses. Cards: progress bar, Content coming soon, flashcards due, exam badge. Empty: Your study space is waiting. / No courses yet with Browse the catalog and get access to your first pack.
Data shown: user_access.course_ids via getCachedUserOwnedCourses; content_progress; flashcard due counts
ELEMENT: Course card
Action: navigate
Destination: /my-courses/{slug}
Writes to: none
Reads from: owned courses
ELEMENT: View all courses / Show fewer / Clear search
Action: toggle / navigate
Destination: /dashboard for clear search
Writes to: none
Reads from: owned course count
SECTION: What needs attention
Copy: What needs attention / You're all caught up for now. Rows for due flashcards, quizzes, exams, unpublished content.
Data shown: lib/workspace/attention-data.ts plus flashcard due/leech counts
ELEMENT: Attention row
Action: navigate
Destination: study tab href when set
Writes to: none
Reads from: attention items
ELEMENT: View all
Action: navigate
Destination: /dashboard/progress
Writes to: none
Reads from: attention total count
SECTION: Today (right column)
Copy: Today plus month label, week strip, You're all caught up / {n} items need attention. Link Open semester plan.
Data shown: calendar from now; stats.tasksDueToday
ELEMENT: Open semester plan
Action: navigate
Destination: /dashboard/semester-plan
Writes to: none
Reads from: none
SECTION: Study hours
Copy: weekly hours vs goal, 7-day bars.
Data shown: study_sessions.active_minutes; platform weeklyStudyGoalMinutes
SECTION: Recent activity
Copy: Recent activity / Activity appears after you open lessons, quizzes, or flashcards. (tool names follow restored Notes / Quiz / Flashcards labels.)
Data shown: recent content_progress rows
Public weekly rank / “students studying now” are not shown (leaderboards stay retired). Schedule-confirm and syllabus-upload gates stay retired.
PAGE: /my-courses
Purpose: Alias of /dashboard (redirect, query preserved).
Access: logged-in + onboarded. Home path is not subscriber-gated.
Entry points: old bookmarks; emails
ELEMENT: none — redirect only. /my-courses/{slug} remains the course workspace.
PAGE: /dashboard/my-packs
Purpose: Alias of /dashboard (redirect, query preserved).
Access: logged-in + onboarded
Entry points: old checkout CTA
ELEMENT: none — redirect only
PAGE: /dashboard/library
Purpose: Saved notes, favorited flashcards/quizzes, personal uploads.
Access: logged-in + onboarded
Entry points: Sidebar Library. Alias /my-courses/library redirects here.
SECTION: Header
Copy: My Library Saved notes, flashcards, and your own materials
Data shown: static
ELEMENT: All / Notes / Flashcards / Uploads
Action: toggle
Destination: none (filter)
Writes to: none
Reads from: library_items.item_type
SECTION: Cards
Copy: Empty Nothing saved here yet. Uploads empty No uploads yet. Upload zone Upload your own materials PDF·images·Word ≤10MB.
Data shown: library_items + course/module joins
ELEMENT: View → Action: navigate Destination: notes study href Writes to: none Reads from: saved note target
ELEMENT: Review →
Action: navigate
Destination: flashcards (+ optional &card=)
Writes to: none
Reads from: flashcard library item
ELEMENT: Retake → Action: navigate Destination: quiz Writes to: none Reads from: quiz library item
ELEMENT: Delete
Action: submit
Destination: none
Writes to: deletes library_items (deleteLibraryItem)
Reads from: item id
ELEMENT: Upload file
Action: submit
Destination: none
Writes to: library_items via uploadLibraryFile
Reads from: file
PAGE: /dashboard/progress
Purpose: Weekly streak objective, course breakdown, 12-week heatmap, quiz notes.
Access: logged-in + onboarded
Entry points: Sidebar Progress. Alias /my-courses/progress redirects here.
SECTION: This week / Progress
Copy: This week Progress plus objective copy (weekly goal + miss budget). Summary: all-time screen time, current streak, course completion. Course bars: Notes / Flashcards / Quizzes. Last 12 weeks. Quiz notes table headers Module, Attempts, Best, Latest, Direction. How streaks work (explanatory copy in the view).
Data shown: profiles, owned courses, course_modules, study_sessions, course_xp, quiz_attempts, course_events, streak dates
ELEMENT: Keep studying
Action: navigate
Destination: first course overview or /courses
Writes to: none
Reads from: owned courses
ELEMENT: Open course →
Action: navigate
Destination: /my-courses/{slug}
Writes to: none
Reads from: courses.slug
PAGE: /dashboard/semester-plan
Purpose: Upcoming assessments, recommended prep, course progress. Not a giant calendar. Syllabus is optional and private.
Access: logged-in + onboarded
Entry points: Sidebar Semester Plan. Alias /my-courses/semester-plan redirects here.
SECTION: Header
Copy: Semester Plan Upcoming assessments, a short prep list, and course progress. CTA Add exam session length 20 min 45 min 90 min Exam cram
Data shown: cookie gradeful_study_session; owned courses
ELEMENT: Add exam
Action: open dialog
Destination: AddExamDateDialog
Writes to: exam_schedules (addStudentExamDate)
Reads from: owned courses
ELEMENT: Session length
Action: submit
Destination: none
Writes to: cookie via setStudySessionLength
Reads from: cookie
SECTION: Upcoming assessments
Copy: empty No assessments yet Add an exam in a few seconds. list shows course, title, when, optional topics, source when dates disagree
Data shown: merged exam_schedules + course_events (lib/workspace/assessments.ts)
ELEMENT: Remove
Action: submit
Destination: none
Writes to: deletes student exam_schedules row
Reads from: student-owned assessment id
SECTION: Recommended prep
Copy: session titles from generateStudyPlan; empty Add an exam or open a course
Data shown: assessments, progress, flashcard due counts
ELEMENT: Open →
Action: navigate
Destination: course overview or study href
Writes to: none
Reads from: session.href
SECTION: Course progress
Copy: {code} {n}% complete
Data shown: content_progress
ELEMENT: Open →
Action: navigate
Destination: /my-courses/{slug}
SECTION: Syllabus (optional)
Copy: Private to you. Upload / Delete
Data shown: syllabuses
ELEMENT: Upload
Action: open modal
Destination: SyllabusUploadModal (skippable)
Writes to: syllabuses
ELEMENT: Delete
Action: submit
Destination: none
Writes to: deletes storage object + syllabuses row
PAGE: /dashboard/ai-tutor
Purpose: Standalone AI page when opened off-course.
Access: logged-in; if aiTutorEnabled false → /dashboard
Entry points: Sidebar AI Tutor when not in a course.
SECTION: Help
Copy: AI Tutor plus instruction to open a course and use the right AI panel
Data shown: static
ELEMENT: Back to dashboard
Action: navigate
Destination: /dashboard
Writes to: none
Reads from: none
PAGE: /my-courses/[courseSlug]
Purpose: Course overview + in-place study player (?module=&tab=&variant=&chapter=&panel=).
Access: logged-in + onboarded; must own the course else /dashboard?error=no_access; anonymous → login with next
Entry points: dashboard cards, resume, attention, progress, library, right panel, public catalog formula-sheet preview (may bounce to login)
SECTION: Course shell header
Copy: Breadcrumb Dashboard / {courseCode} / compact chapter picker / current study tool / progress. Tabs: Overview Notes Formula sheet Flashcards Quiz Exam Exam review Mistake fixes Exercises Ask Tutor (Ask Tutor hidden unless aiTutorEnabled). Locked title Not published yet. Picker trigger Ch. {n} · {title} or Select chapter. Layout: restored pre-redesign tab strip + chapter picker (Git c7fc162). No Learn / Practice / Review strip and no course-outline column. Right AI panel is a full-screen drawer on small screens.
Data shown: courses.code, course_modules, available module_pack_items mapped through lib/workspace/learning-intent.ts
ELEMENT: Dashboard
Action: navigate
Destination: /dashboard
Writes to: none
Reads from: none
ELEMENT: {courseCode}
Action: navigate
Destination: /my-courses/{slug} overview
Writes to: none
Reads from: courses.slug
ELEMENT: Course settings (gear)
Action: navigate
Destination: /my-courses/{slug}/settings
Writes to: none
Reads from: none
ELEMENT: Overview / Notes / Formula sheet / Flashcards / Quiz / Exam / Exam review / Mistake fixes / Exercises
Action: navigate
Destination: overview, or /my-courses/{slug}?module={id}&tab={item_type}. Ask Tutor still uses ?panel=ai. Legacy ?intent=learn URLs may still resolve to the first matching tab, but Learn / Practice / Review is not the student IA.
Writes to: none
Reads from: published module_pack_items for the chapter
ELEMENT: Chapter option Ch. {n} · {title}
Action: navigate
Destination: same intent, new module
Writes to: none
Reads from: course_modules
Study body for each tab: 06-study-views.md.
PAGE: /my-courses/[courseSlug]/settings
Purpose: Per-course flashcard scheduler, syllabus, exam dates.
Access: logged-in; must own course else public /courses/{slug}
Entry points: Course shell settings gear. /dashboard/course/{slug}/settings redirects here.
SECTION: Header
Copy: {code} settings
Data shown: courses.code, courses.title
ELEMENT: ← Back to course Action: navigate Destination: course overview Writes to: none Reads from: none
SECTION: Flashcard study
Copy: Flashcard study How well should cards stick? New cards per day More options Maximum reviews per day placeholder No limit Study order options mixed / Reviews first / New cards first related-cards checkbox Timezone Developer settings
Data shown: flashcard_deck_settings. Raw scheduler parameter vectors are not shown.
ELEMENT: Save flashcard settings / Saving…
Action: submit
Destination: none
Writes to: flashcard_deck_settings (updateFlashcardDeckSettings)
Reads from: form
Toast: Flashcard settings saved.
SECTION: Syllabus
Copy: Syllabus Optional and private. empty No syllabus uploaded.
Data shown: syllabuses
ELEMENT: Upload / Replace
Action: open modal
Destination: SyllabusUploadModal
Writes to: syllabuses
Reads from: existing row
ELEMENT: Delete syllabus
Action: submit
Destination: none
Writes to: deletes storage object + syllabuses row
SECTION: Exams
Copy: Exams empty No exams yet. Add exam
Data shown: merged exam_schedules + course_events
ELEMENT: Add exam
Action: open dialog
Destination: AddExamDateDialog
Writes to: exam_schedules