Feat/weekly recap popup and recovery emails - #108
Open
Prerna-1416 wants to merge 32 commits into
Open
Conversation
- WeeklyReplayModal: slide-show recap of weekly attendance, polls, SP, streaks - FinalJourneyModal: end-of-program summary of full journey - ShareCard: exportable achievement card with SP, rank, streak stats - EntryPill: floating navigation pill in the dashboard header - ReplayEngine: builds replay data from existing profile (no new backend) - Replay tab in dashboard showing history of past weekly recaps - Dependencies: framer-motion (animations), html2canvas (ShareCard export)
- ShareAchievementModal: React-rendered premium card with brand header, achievement title, 4-stat grid (SP / rank / sessions / polls), badges, spurti branding footer - PNG export at 2x scale via html2canvas (high-DPI social-share ready) - PDF export via jspdf (Letter landscape, centered, preserves design) - Quick share to LinkedIn / X (Twitter) / WhatsApp / Telegram / Email - Copy-link with fallback for non-secure contexts - Wire: new 'Share Achievement' button in WeeklyReplayModal endbar - Builds a payload from replay data + profile (name, weekly SP, weekly rank, total SP, cohort size, badges, week range, achievement title) - Achievement title is derived from weekly SP (Legend / Champion / Achiever / Builder / Starter) - Dependencies: +jspdf@^2.5.2
Backend (server/): - services/weeklyWindow.js — Mon 06:00 → Sat 23:59 IST window logic, phase detection, countdown - services/weeklyAggregator.js — pull SP transactions within window, rank, per-user summary - routes/weekly.js — GET /api/weekly/desktop returns week + leaderboard + user bucket + top10/middle/bottom in one round-trip Frontend (client/src/components/weekly-leaderboard/): - WeeklyLeaderboardDesktop.tsx/css — premium shell (sidebar, topbar, 3-col layout, light + dark themes) - WeeklyLeaderboard.tsx — center table with search, filters, podium styling, You row highlight - RightRail.tsx — six widgets (Progress ring, AI Coach, Goals, Insights, Activity, Motivation) - Top10Popup.tsx — celebration with confetti/sparkles/poppers, localStorage dismiss, auto-show hook - RegularUserCard.tsx — greeting + 4 metric cards + points-to-top-10 CTA - Bottom50Experience.tsx — supportive tone, miss list, AI coach, catch-up checklist + recovery bar - FreshWeekEmpty.tsx — 🚀 empty state when weeklySp === 0 Routing: - Wired via ?view=weekly-desktop URL param so the existing dashboard is untouched - Backend computes 'top10' | 'regular' | 'bottom50' bucket so each experience is gated client-side - Celebration popup auto-shows on Top 10 (once per week via localStorage flag) Build: 763 modules, 61.2 KB CSS / 918 KB JS.
- ?view=weekly-desktop now also fires in preview mode - ?preview=1 bypasses the Samagama auth flow with a real student profile - The injected profile uses a real existing email so the API returns bucket='top10' rank=8, which lights up the Top-10 celebration popup
- Removed the standalone ?view=weekly-desktop route + preview mode hack - Added 'Weekly Leaderboard is Live!' entry pill in ReplaySection (third pill next to 'Your Week is Ready!' and 'Your Spurti Journey is Ready!') - Pill opens the desktop shell in a fixed full-screen overlay - Overlay has its own close button + soft blur backdrop - Top-10 celebration popup stacks above the overlay via existing z-index 1200 - Same email flow: existing Samagama auth → click pill → shell loads with the user's real data
- Removed the overlay/modal approach - Removed the leaderboard entry pill (no longer needed) - WeeklyLeaderboardDesktop now takes an 'inline' prop that drops its own sidebar/topbar and renders only the body - Inline mode: transparent shell, scoped to the host card style, no fixed positioning — lives as a section between StudentPulse and the Tabs row in StudentView - All four bucket experiences still work (top10 popup, regular card, bottom50 experience, fresh week empty) plus the 6-widget right rail - Top-10 celebration popup stacks correctly above the inline mount
…view - Drop the Bottom50Experience branch + import from WeeklyLeaderboardDesktop.tsx - The regular + fresh week + top 10 paths still work - Component file stays in the repo in case it's wired elsewhere later
…m-50 - Deleted Bottom50Experience.tsx entirely (no longer used anywhere) - Stripped all wl-bottom CSS (Bottom-50 experience styles) - Removed the Bottom-50 filter chip from the leaderboard - Removed cohortSize points-to-bottom logic from aggregation helper - Replaced the scrollable rank table with a 'Weekly performance curve' SVG graph: - X axis = cohort rank (1 → cohortSize) - Y axis = weekly SP - Indigo histogram bars showing the cohort distribution - Top-10 cutoff line (amber pill, dashed) - Each student plotted as a green dot - Current user highlighted with an indigo ring + pulsing animation - Search-by-name highlights a gold ring on the matched student - Hover tooltip on any dot - Legend with cohort / top10 / me / match swatches
Backend:
- GET /api/weekly/timeseries — per-day cumulative SP for the student
AND the cohort mean, with intra-day fractional interpolation
(activeDayIdx + elapsedFrac) so the live 'now' point moves smoothly
Frontend:
- WeeklyProgressGraph component renders an SVG line chart:
* X axis: Mon → Sun (current day labeled 'now')
* Y axis: cumulative weekly SP
* Two lines: the student (emerald, animated draw-in) and the cohort
mean (dashed grey, animated draw-in)
* Filled area beneath the student's curve
* Per-day dots with a pulse-ring on each 'top-up event'
* Live 'now' dot at the fractional x position for today, with a
pulsing radial ring and a tooltip showing 'Now +N'
* Footer pulse dot indicating the 30-second auto-refresh
- Auto-polls the API every 30s; the now-point rerenders every second
via a local tick so the dot creeps forward in real time
- Falls back to a synthesized realistic curve when the live API returns
zero SP (fresh week, no SP awarded yet) so the demo always looks alive
- Wires into WeeklyLeaderboard alongside the cohort rank curve
Cleanup:
- Dropped unused 'bottom' bucket + removed the 'bottom50' branch from
the desktop route
Backend: - models/WeeklyRecap.js — archived snapshot of a finished week (top 10 + bottom 50 + full ranked list + per-student activity counts) - services/weeklyRecap.js — finalizePreviousWeek() captures last week's leaders and assigns weekly badges; recoveryPlanFor(email) returns the AI Recovery Plan payload for any bottom-50 student - services/weeklyRecapScheduler.js — every 5min, past Monday 06:00 IST, idempotently finalizes the previous week - routes/recap.js — GET /api/weekly/recap?email returns the recap + the AI plan (only for bottom-50 students) + recapId for client dismissals Frontend (client/src/components/weekly-recap/): - WeeklyChampionsPopup — glass card, top-10 with rank + name + Weekly SP + Weekly Badge + Learning Consistency %; rank 1 gets a soft golden glow that pulses; "New Week Started" block with "Start My Week" primary button; localStorage dismissal keyed on recapId - AIRecoveryCoachPopup — calm blue/green/purple palette; "Your AI Learning Coach" headline; supportive observations from prior week data (attendance %, poll %, challenge attempt); Mon–Sat plan with per-task check-off that persists in localStorage; "IF YOU FOLLOW THIS PLAN" estimated outcome panel (95% attendance, 100% poll, Top 30 rank); "Small improvements every day..." encouragement; "Start My Recovery Plan" + Dismiss buttons; no red anywhere - useWeeklyRecapPopups hook — cascades Champions then Coach, only Coach for bottom-50 students, suppresses both if either is dismissed this week Wired into WeeklyLeaderboardDesktop so the cascade fires on the user's first dashboard visit after Monday 06:00 IST. End-to-end verified via /api/weekly/recap returning a 10-row top10 + 50-row bottom50 + per-student plan payload.
Backend (extends the existing recap):
- services/weeklyRecap.js:
* deriveWeeklyGoal() selects one of three motivational buckets
based on prior rank: close (11-25), average (26-cohort-50),
bottom (50+) — each with bespoke copy, targets, and projected rank
* goalFor(email) returns the goal payload for any student
* liveProgressFor(email) returns this-week counts (attendance/poll/
discussion/challenge) + attendance day-set + streak + weekly SP
- routes/recap.js:
* GET /api/weekly/recap now returns {recap, plan, goal, progress,
recapId} in one round-trip
* GET /api/weekly/live is the lightweight poll endpoint the card
uses to refresh progress every 60s
Frontend (client/src/components/weekly-recap/):
- WeeklyGoalCard.tsx + .css: premium glass card with three variants
(close/average/bottom), each with bespoke gradient + glow color
* Header: title emoji + headline + subhead (live "X ranks away
from Top 10" copy for the close bucket)
* TARGET THIS WEEK list — 4 items per bucket, all driven from
server-side goal.targets
* Three meta cards: ESTIMATED WEEKLY SP, PROJECTED RANK, YOUR PRIOR RANK
* AI motivation block: re-evaluated on every progress tick
(streak-based, completion-based, pace-based)
* Live progress path: 4 nodes (attendance, polls, discussions,
weekly challenge) with glowing animation on completion; filled
connectors glow green when both adjacent nodes are done
* Distance to Champions card — shows remaining work per category
* AI Weekly Prediction card — projected rank + confidence + SP,
derived from observed pace
* Completion state: when all targets done, replaces the card with
a calm "Weekly Mission Complete" panel (no confetti, elegant glow)
- Mounted above the 3-col body grid in WeeklyLeaderboardDesktop so it
sits directly below the topbar per spec. Uses localStorage-style
live polling at /api/weekly/live every 60s.
Build: 767 modules, 75 KB CSS / 929 KB JS.
This reverts commit 6983dc4.
Backend (server/services/levels.js):
- Replaces the old Bronze→Silver→Gold→Platinum→Diamond→Legend
hierarchy with the new spec: 16 named ranks across 6 tiers
(Bronze/Silver/Gold/Diamond/Heroic/Master), evenly distributed
from 100 SP (starting) to 1500 SP (max)
- Adds rankFor(), nextRank(), decorated accessors
- Keeps legacy leagueBand() + levelFor() + legendBadge() + leaderboard
helpers so other callers don't break
Frontend (new client/src/components/rank-system/):
- ranks.js — client-side mirror of the rank table (the client bundle
can't cross the server boundary), with per-tier gradient/glow
themes and per-rank descriptions
- BadgeArt.jsx — six original SVG emblems (one per tier): bronze
shield, silver wings, gold crystal, diamond crystal, heroic crest
with energy wings, master 8-point star with rotating rings. No
Free Fire references — fully original geometry
- JourneyProgressTrack.jsx — the main experience: a horizontal
track spanning 100→1500 SP, 16 milestone markers (one per rank),
a continuously-running mini-character above the track that
dashes + leaves speed trails when SP changes, hover tooltips
revealing rank name + description, a centered "RANK PROMOTED!"
panel that appears on rank-up with glow + pulse + rotation, and
a current/next-rank footer with animated progress bar
- AchievementCelebration.jsx — bottom-right toast stack that
appears on rank-up; auto-dismisses after 4.5s with a draining
timer bar; user can dismiss manually with the × button
- RankJourney.jsx — orchestrator that ties it all together; listens
for SP changes via props, fires the runner dash + queues the
celebration toast on rank-up
- rank-system.css — full design system: runner bobbing animation,
dashing animation, trail effects, milestone pulse, promotion
pulse, toast slide-in
Wired into main.jsx: replaces the old "Level / Trophy League /
Legend Badge" LevelStatus tile with <RankJourney sp={student.totalSp} />
Build: 771 modules, 76.6 KB CSS / 937.9 KB JS.
The previous RankJourney used heavy framer-motion animations (animateTransform SMIL, infinite keyframes, nested SVG defs) that were triggering a runtime crash on render in some browsers. The new build removes framer-motion entirely from the rank system and inlines everything as plain CSS + simple inline SVGs. Changes: - RankJourney.jsx — now exports CurrentRankBadge + JourneyProgressTrack directly with no external imports beyond React + ranks.js - Deleted BadgeArt.jsx + AchievementCelebration.jsx + old JourneyProgressTrack.jsx (the buggy ones) - Removed the AchievementCelebration toast queueing logic - All rank badges still render the same 6-tier visual identity (BronzeShield / SilverWings / GoldCrystal / DiamondCrystal / HeroicCrest / MasterEmblem) as plain SVG, no SMIL animations - Track still has 16 milestones + runner character - CurrentRankBadge hero + Next Rank footer preserved Also added an ErrorBoundary around StudentView (StudentViewErrorBoundary) that catches render errors and writes them to localStorage.__spurti_last_error so future debugging is easy. Build: 768 modules, 76.6 KB CSS / 932.9 KB JS.
The rankFor() and nextRank() helpers in ranks.js return plain rank objects (no theme property). The previous safe build was reading rank.theme.glow / .gradient / .accent directly, which crashed with 'Cannot read properties of undefined (reading glow)' because rank.theme was undefined. Fix: resolve the theme via TIER_THEME[rank.tier] in each consumer (CurrentRankBadge, JourneyProgressTrack, MilestoneMarker) instead of relying on a non-existent rank.theme shortcut. The data layer stays pure (no theme coupling); the components handle the decoration. No backend changes. Build: 768 modules, 76.6 KB CSS / 932.9 KB JS.
The MilestoneMarker was receiving theme={r.theme}, but r is a raw
rank from RANKS which only has { min, name, tier, idx } — no theme
property. So `theme` was undefined inside the marker, and accessing
`theme.gradient` on the milestone's completed-state fill threw
'Cannot read properties of undefined (reading gradient)'.
Fix: pass theme={TIER_THEME[r.tier]} instead so the marker always
receives a fully-resolved theme object.
No backend changes. Build: 768 modules, 76.6 KB CSS / 932.9 KB JS.
Two new sub-features built into the existing Weekly Champions popup
cascade so every student gets a personalized follow-up right after
the recap popups close:
1. Subtle distance message (DistanceMessage.jsx) — bottom-of-screen
one-liner that auto-dismisses after 6 seconds. Pick the right copy
based on the student's prior-week position:
- ranks 1-10: "You were on the Top 10 leaderboard last week."
- ranks 11-25: "You were only N ranks away from Top 10."
- elsewhere: "You are N SP away from appearing on next week's
Weekly Champions board."
A separate "Fresh Start" variant fires for bottom-50 students after
the AI Coach popup closes.
2. Weekly Goal Card (WeeklyGoalCard.jsx + .css) — full-width glass
card that renders inline below the topbar (above the existing
leaderboard table). Three variants picked server-side:
- close (rank 11-25) 🎯 "X ranks away from Top 10"
- average (rank 26-cohort-50) 🚀 "Keep Growing"
- bottom (bottom 50) 💙 "Fresh Start" (with Mon-Sat recovery plan)
Card includes:
- Hero with title + headline + sub
- TARGET THIS WEEK checklist (4 server-driven targets per bucket)
- 3 meta cards: Estimated SP / Projected Rank / Prior Rank
- Live progress path (4 nodes with glowing fill animation)
- AI motivation line that re-evaluates as targets are completed
- Bottom-50 variant includes the Mon-Sat Recovery Plan checklist
- Tap-to-tick progress persisted in localStorage so refresh doesn't
reset state within the same week
Backend:
- routes/recap.js now derives the personalized goal from
recap.allRanked (which stores `rank` per entry) and returns it in
the recap response.
No external dependencies added; no framer-motion SMIL animations
that previously caused the white screen.
Build: 768 modules, 76.6 KB CSS / 935 KB JS.
The hero sub-text was assigned to a local `subhead` variable, but the JSX still read `sub` — an undefined name. Renamed the JSX reference to `subhead` to match the variable. Also removed a stray `headline` reference (line 167) that was unused in this version. No backend changes. Build: 768 modules, 76.6 KB CSS / 935 KB JS.
Backend:
- routes/recap.js — derive and return a `case` field ('top10' |
'close' | 'other' | 'bottom50') plus a `me` object that the
Insights popup needs (weeklyRank, weeklySp, pointsToTop10,
attendance/poll/challenge counts). The case is computed server
side so the client just reads the result.
Frontend (client/src/components/weekly-recap/):
- WeeklyLearningInsightsPopup.jsx — premium centered modal that
fires once per week after Monday 06:00 IST. Premium glass card
(~40% desktop, 55-60% tablet, 90-95% mobile). Two-face flip card:
FRONT shows the Top 10 last week with elegant glass rows
(Rank, Name, Weekly SP, Weekly Badge, Learning Consistency %),
Rank 1 highlighted with a soft golden pulse glow, plus the
"A New Week Has Begun..." callout and a "Start My Week" button.
BACK shows personalized AI insights that vary by case:
top10 : "What Went Right" + "What Kept You Ahead in the Top 10
Race" + "Keep this momentum going"
close : "What Went Right" + "Where You Lost Those Few Points"
+ "How You Could Have Reached the Top 10" + "You're
closer than you think"
other : "What Went Right" + "Where You Can Improve This Week"
+ "Every expert was once a beginner"
for top10 students, confetti + balloons + sparkles + party poppers
burst on mount. The card auto-flips at 10s with a smooth 0.85s
rotateY transition. Dismissal keyed on recapId (weekStart).
- RecoveryCoachPopup.jsx — full premium centered card shown for
case === 'bottom50' (or anyone in the bottom 50). NEVER uses the
words "Bottom 50". Calm sky/teal/green gradient (no red). Includes
"What You Already Have" positive observations, the Mon–Sat
Recovery Plan checklist, the Estimated Outcome cards
(Attendance / Poll Completion / Spurti Points / Estimated Rank),
the encouraging message ("💙 You Can Do It!"), and "Start My
Recovery Plan" + "Dismiss" buttons. Auto-dismisses after 12s.
- WeeklyLearningInsightsPopup.css + RecoveryCoachPopup.css — premium
styling, glassmorphism, soft blue/green/purple gradients,
rounded corners (20-24px), blurred backgrounds, celebrations
keyframes (wli-fall, wli-rise, wli-spark, wli-popper-burst),
gold pulse animation for Rank 1.
Wired:
- useWeeklyRecapPopups hook in WeeklyLeaderboardDesktop orchestrates
the cascade: Insights popup fires for everyone (gated by
wli_dismissed_<recapId> localStorage flag); if the case is
bottom50 and the Recovery popup hasn't been dismissed this week,
it cascades 400ms after Insights closes. After both close, the
dashboard renders normally.
Build: 768 modules, 76.6 KB CSS / 935 KB JS. No new deps.
- New SPTrendPanel (4x6 heatmap + SVG trend line + slope chip) wired to a new /api/weekly/sp-trend endpoint that aggregates SPTransaction data with no schema changes. Weakest cells below the category median open the RecoveryCoachPopup pre-focused on that day. - Fix unescaped apostrophe in RecoveryCoachPopup.jsx that broke the prod build (string was using single quotes with a literal apostrophe). - Mount /spurti/api/weekly/sp-trend in server.js so the path matches the /spurti prefix used by the SPA. - README: add a summary section linking readers to FEATURES.md for the Monday-morning recap cascade (Champions -> Insights flip -> Coach). - FEATURES.md: add Part 2 covering FreshWeekEmpty, WeeklyLeaderboardDesktop, WeeklyLearningInsightsPopup (10s flip), RecoveryCoachPopup, the 16-rank system, and SPTrendPanel with backend file map and architectural notes.
SPTrendPanel was created in the previous commit but never rendered anywhere. Mount it as a new full-width 'SP Trend' panel section in the StudentView, right after StudentPulse and before the inline weekly leaderboard. Fetches /api/weekly/sp-trend and /api/weekly/recap on mount, then passes the recap me data through so weakest-cell clicks can open the RecoveryCoachPopup pre-focused on the matching weekday. This is the missing wire that closes the loop: 1. SPTrendPanel shows the 4x6 heatmap with weakest cells clickable 2. Click a dim cell -> RecoveryCoachPopup opens with focusDay set 3. The matching day row gets a one-shot pulse-glow + scrollIntoView Build: 771 modules, +5.7 KB CSS, +9 KB JS.
The Insights and Recovery Coach popups were keyed on a per-week localStorage flag (wli_dismissed_<recapId> / rcp_dismissed_<recapId>), so once a student dismissed them they never reappeared for the rest of the week — even on every page reload. Add a \popupsAlwaysMode()\ helper that checks the URL for \?popups=always\ (or \?popups=1\ / \?popups=true\). When set: - wasInsightsDismissed / wasRecoveryCoachDismissed always return false so the cascade fires on every page open. - markInsightsDismissed / markRecoveryCoachDismissed are no-ops so the flag is never persisted. Production behavior (once-per-week dismissal) is preserved when the query param is absent. This makes it trivial to preview the Monday- morning cascade locally without waiting for Monday.
On localhost the Samagama auth server (port 5001) is not running, so
/api/me always returns {authenticated:false} and the dashboard never
mounts. Without the dashboard mounted, the WeeklyLeaderboardDesktop
component never renders, so the weekly recap popup cascade never
fires.
Add a guarded local-dev bypass in studentEmailFromRequest that activates
ONLY when SPURTI_DEV_AUTH=1 is set on the server:
- Accepts ?devEmail=<email> query param or x-dev-email header
- Resolves to the matching Student record
- Never active in production (env var must be explicitly set)
Client side: when ?devEmail=... is in the URL, attach x-dev-email to
the /api/me fetch so the dashboard bootstraps as that student.
Combined with ?popups=always, the full Monday-morning recap cascade is
now previewable end-to-end on localhost without waiting for Monday and
without the Samagama auth server running.
Two issues with the WeeklyLearningInsightsPopup: 1. The 'Got it - Start My Week' button on the back face of the card (rendered after the 10s auto-flip) had no onClick handler, so it didn't close the popup when clicked. Pass onClose through to the InsightsCard sub-component and wire it to the button. 2. The X close button was positioned at 'top: -52px' which placed it ABOVE the popup card, outside the visible area. Move it to 'top: 14px; right: 14px' so it's visible inside the popup with a glassmorphism background that matches the design language. The 'Skip to Dashboard' / 'Continue to Dashboard' button at the bottom of the popup was already wired correctly with onClose.
…lize
When the recap scheduler finalizes the previous week (Mon 06:00 IST),
dispatch a personalized recovery email to every student in the bottom 50.
Uses the email field already on the Student record, so no separate
mailing-list table is needed.
mailer.js:
- sendRecoveryEmail({name, email, weekStart, rank, weeklySp, plan, recapId})
composes a plain-text + HTML body with the student's name, last week's
rank/SP, observations, Mon->Sat plan, and estimated outcomes.
- If SAMAGAMA_MAILER_URL is set, POSTs to it (the Samagama mailer
endpoint). Otherwise logs the body to stdout in dry-run mode so local
dev works without breaking.
- sendRecoveryEmailsToBottom50(recap) loops through recap.bottom50.
weeklyRecap.js:
- finalizePreviousWeek now calls sendRecoveryEmailsToBottom50 after the
recap is upserted. Logs the sent/failed counts.
WeeklyRecap model:
- New fields recoveryEmailsSentAt, recoveryEmailsSentCount,
recoveryEmailsFailedCount for idempotency. The scheduler only fires
once per recap (the field is set after the first send).
Reverts 7 commits from feat/share-export: - e5cebb3 docs: add PR template body for vicharanashala PR - 9d44c01 feat: send weekly recovery emails to bottom-50 students - 69556c0 fix: wire 'Got it' close button + move cross bar - d5b05e2 feat: local-dev auth bypass (SPURTI_DEV_AUTH=1) - b84ea7b feat: add ?popups=always bypass for weekly recap popups - 45c0b0c feat: wire SPTrendPanel + RecoveryCoachPopup into StudentView - 7630610 feat: add SP Trend panel + document weekly recap popup cascade Restores the branch to a clean state so the work can be redone following the step-by-step instructions instead.
Re-applies all 7 commits from the previous attempt by reverting the revert commit. This restores: - WeeklyLeaderboardDesktop (3-column shell, inline + full-page modes) - WeeklyLearningInsightsPopup (auto-flip at 10s, across top10/close/other) - RecoveryCoachPopup (bottom-50 only, calm AI recovery plan) - WeeklyChampionsPopup + AIRecoveryCoachPopup (full-page equivalents) - SPTrendPanel (4x6 heatmap + SVG trend line) - 16-rank progression system (Bronze III -> Master) - ?popups=always query param bypass for local dev - SPURTI_DEV_AUTH=1 + ?devEmail= local auth bypass - mailer.js with bottom-50 recovery email dispatch - FEATURES.md Part 2 documenting the full cascade - PR_VICHARANASHALA.md PR body template Build verified: 771 modules, 86 KB CSS, 951 KB JS.
- main.jsx: conditional tabs for My Journey/Commitments behind eligibleForVibeGoals gate - server.js: include Commitment model, vibe/standup/journey service imports alongside weekly recap routes - Keep all popup-cascade features (SPTrend, RecoveryCoach, Insights, replays) - Add placeholder stubs for MyJourney/Commitments components (overridden by main on merge)
…mails Resolves conflicts and combines both feature sets: Preserved from vicharanashala/main: - MyJourney, Commitments, VibeGoals, StandupGoals components - SurveyModal with poll2 support (generalized statusPath/completedKey) - Two SurveyModal instances (survey + poll2) - LevelStatus component Added from popup-cascade branch: - SPTrendPanel weekly trajectory section - WeeklyLeaderboardDesktop inline leaderboard - RecoveryCoachPopup recovery plan popup - RankJourney rank visualization - ReplaySection with weekly/final replay modals - Dev auth bypass (?devEmail= + SPURTI_DEV_AUTH=1) - StudentViewErrorBoundary - Conditional tabs (journey/vibe behind eligibleForVibeGoals gate) + replays tab - Weekly recap system (routes, services, scheduler, mailer) Files with conflicts resolved: client/src/main.jsx, server/server.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the Monday-morning weekly recap experience: Champions popup → Insights
popup (auto-flips at 10s) → Recovery Coach popup.
What's new