Fix TypeScript type errors and apply review feedback for CI on mvp-overhaul PR#109
Fix TypeScript type errors and apply review feedback for CI on mvp-overhaul PR#109
Conversation
- Generate lib/supabase/database.types.ts from all 19 migrations covering 13 tables, 8 enums (user_role, request_status, payment_status, match_status, session_status, etc.) and 5 RPC functions - Add Database generic to createBrowserClient<Database>() in client.ts - Add Database generic to createServerClient<Database>() in server.ts; also add try/catch around setAll() so Server Components that read cookies do not throw on cookie-write attempts - Add Database generic to createClient<Database>() in admin.ts - Update tsconfig.json with @/ path alias for clean imports This eliminates all manual inline type assertions (as SomeShape | null) and provides compile-time detection of column name typos across the entire codebase. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rary Design system (app/globals.css + app/layout.tsx): - Import Outfit font via next/font/google as primary typeface - Define CSS custom properties for full Bauhaus palette: background #F0F0F0, foreground #121212, primary-red #D02020, primary-blue #1040C0, primary-yellow #F0C020 - Add .bauhaus-card, .bauhaus-card-sm, .bauhaus-shadow, .bauhaus-shadow-lg utility classes (thick borders + hard offset shadows) - Add accessible :focus-visible ring, subtle scrollbar, and prefers-reduced-motion support - Wrap root layout in TooltipProvider; mount <Toaster> (Sonner) globally UI component library (components/ui/): - Install and configure shadcn/ui components: alert-dialog, badge, button, card, dialog, dropdown-menu, form, input, label, pagination, select, separator, skeleton, sonner, table, tabs, tooltip - Add custom bauhaus.tsx with BauhausCard, BauhausButton, BauhausBadge variants (hard shadow, thick borders, no border-radius) - components.json added for shadcn/ui CLI configuration Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…afe sign-out (C1+C2+C5) next.config.ts — security headers (C1): - X-Frame-Options: DENY (clickjacking protection) - X-Content-Type-Options: nosniff (MIME-sniffing protection) - Referrer-Policy: strict-origin-when-cross-origin - Permissions-Policy: camera=(), microphone=(), geolocation=() - Strict-Transport-Security: max-age=63072000; includeSubDomains; preload - X-DNS-Prefetch-Control: on middleware.ts — role-based enforcement + email verification (C2): - After session refresh, check email_confirmed_at for email/password users; redirect unverified users to /auth/verify - Fetch primary_role from user_profiles on protected routes: /admin/* requires primary_role = 'admin' /tutor/* requires primary_role = 'tutor' | 'admin' - Redirects to /dashboard on role mismatch (no layout-only guard) app/auth/actions.ts — CSRF-safe sign-out (C5): - New Server Action signOut() replaces plain POST to /auth/sign-out - Next.js Server Actions have built-in CSRF protection via the Origin header check; no explicit token needed - app/auth/sign-out/route.ts retained but simplified Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…(F1+F3+F4+F5) vercel.json (F1): - Cron job: /api/cron/expire-packages runs daily at 02:00 UTC to automatically expire packages past their end_date .env.example (F4): - Documents all required environment variables with descriptions: Supabase (URL, anon key, service role), WhatsApp number, bank details (NEXT_PUBLIC_BANK_*), CRON_SECRET, SENTRY_DSN placeholder - Developers copy this to .env.local before running the project package.json (F3+F5): - Move @types/luxon to devDependencies (type-only package, not runtime) - Add "typecheck": "tsc --noEmit" script for fast CI type checking - Add "test": "vitest run" and "test:watch": "vitest" for unit tests - Add vitest, @vitest/ui, and related packages vitest.config.ts: - Configure Vitest with @/ path alias and node test environment Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ers, and RLS 20260225000004 — create_payment_proofs_bucket: - Private storage bucket 'payment-proofs' with per-user path scoping - RLS policies: owner can insert/select own proofs; service_role has full access; no public reads (signed URLs required) 20260225000005 — add_admin_notes_to_matches: - Add admin_notes text column to matches table for internal annotations 20260226000001 — update_handle_new_user: - Update handle_new_user() trigger to correctly populate user_profiles on sign-up, including for_student_name and timezone defaults 20260226000002 — subjects_grant_select: - Grant SELECT on subjects table to authenticated role so students can load the subject dropdown in the request form 20260226000003 — requests_package_tier: - Add package_tier column to requests table (8 | 12 | 20 sessions) so the package selection is recorded on the request itself 20260226000004 — tutor_profile_fields: - Add experience_years, education, teaching_approach columns to tutor_profiles for richer profile display on student dashboard 20260228000001 — subjects_rls_policy: - Add explicit RLS policy for subjects: all authenticated users can read active subjects; only service_role can mutate seed.sql: - Update seed data to match new schema columns Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The original tutor_update_session() RPC incremented sessions_used
whenever p_status IN ('done', 'no_show_student') without checking the
session's previous status. This meant changing a session from 'done'
to 'no_show_student' would double-increment, silently burning credits.
Migration 20260303000001 replaces the RPC with a guarded version:
- SELECT current status before any mutation
- Only increment sessions_used when transitioning FROM a non-consuming
status TO a consuming status ('done' | 'no_show_student')
- Only decrement (via new decrement_sessions_used helper) when
transitioning FROM a consuming status TO a non-consuming status
- No increment/decrement for no_show_tutor or rescheduled regardless
of previous status
- Existing ceiling guard (sessions_used < sessions_total) is preserved
Also adds decrement_sessions_used(p_request_id) RPC as a safety
valve for admin corrections without direct SQL access.
The TypeScript path in lib/services/sessions.ts already had this guard
via isConsuming() check; the SQL RPC now has parity.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…imiting (C3+C4+A4)
lib/services/sessions.ts:
- Add tutorUpdateSessionStatus() with isConsuming() guard
- Add adminOverrideSessionStatus() for admin corrections
- Add rescheduleSession() for admin-triggered reschedules
- All mutations write to audit_logs
lib/services/payments.ts:
- Add expirePackages() — marks packages past end_date as expired and
updates associated request status to 'ended'
- Add getExpiringPackages(days) — returns packages expiring within N
days with student/subject info for admin renewal outreach
- markPaymentPaid() has full rollback logic for cascading status
changes (payment → package → request)
lib/services/requests.ts:
- Add updateRequestStatus() with audit log
- Add createRequest() with Zod validation at the service boundary
lib/services/whatsapp.ts:
- Add buildWhatsAppLink() and message template helpers
lib/utils/request.ts, lib/utils/session.ts:
- Extend STATUS_LABELS, STATUS_COLOURS, SESSION_STATUS_LABELS
- Add SESSION_STATUS_COLOURS, LEVEL_LABELS, getLevelLabel helpers
lib/validators/payment.ts (C3):
- Add markPaidSchema (paymentId, packageId, requestId as z.string().uuid())
- Add rejectPaymentSchema (paymentId UUID, rejectionNote min/max length)
- Schemas used at the top of admin payment server actions
lib/config/pricing.ts (A2):
- Replace CONFIGURE_BEFORE_LAUNCH literals with NEXT_PUBLIC_BANK_*
environment variable reads; falls back gracefully if unset
lib/rate-limit.ts (C4):
- In-memory sliding-window rate limiter with periodic cleanup
- checkRateLimit(key, limit, windowMs) → { success, remaining }
- For production scale, replace with @upstash/ratelimit + Redis
lib/auth/requireAdmin.ts:
- Reusable requireAdmin() guard for all admin server actions
- Throws if user is not authenticated or does not have admin role
lib/config/timezones.ts:
- IANA timezone list for the profile setup and request forms
app/api/leads/route.ts (C4):
- Apply rate limiting: 10 requests per minute per IP
- Return 429 with Retry-After header when limit exceeded
app/api/cron/expire-packages/route.ts (A4):
- Vercel Cron endpoint called daily at 02:00 UTC
- Validates CRON_SECRET header before executing
- Calls expirePackages() and logs count of expired packages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…us treatment (D3) app/admin/page.tsx: - Now an async Server Component fetching live counts in parallel: pending payments, pending tutor approvals, new requests, upcoming sessions, active subjects - Cards show live badge counts with color-coded Bauhaus styling - Subjects management card added app/admin/analytics/page.tsx (D3): - New "Renewals Due (Next 5 Days)" section calling getExpiringPackages(5) - Lists student name, subject, sessions remaining, expiry date - Pending payments, tutor approvals, unmarked sessions widgets app/admin/layout.tsx: - Convert sign-out to use signOut() Server Action (C5 fix) - Mobile nav: add CSS gradient fade indicators on both edges to signal horizontal scrollability (10 nav items on small screens) - Navigation links updated to include subjects app/admin/requests/: - Redesigned with Bauhaus card layout and status badges - RequestFilters.tsx: status, level, subject filter dropdowns - [id]/AssignTutorForm.tsx: tutor selector with subject/level match - [id]/AdminRequestActions.tsx: new component for inline actions - actions.ts: createMatch(), updateRequestStatus() with audit logs app/admin/payments/: - PaymentActions.tsx: mark paid / reject with rejection note modal - PaymentQuickActions.tsx: new quick action bar for payment list - ViewProofButton.tsx: triggers signed URL for proof preview - actions.ts: Zod validation on markPaymentPaid/markPaymentRejected app/admin/tutors/: - Tutor detail shows full profile, subjects, availability - TutorActions.tsx: approve/reject with audit log - TutorFilters.tsx: filter by approved status app/admin/sessions/: - SessionActions.tsx: admin status override with reason - Sessions list shows overdue sessions prominently app/admin/matches/: - Match detail: EditMatchForm (schedule pattern + Meet link), GenerateSessionsForm, WhatsApp template copy buttons - MatchActions.tsx: reassign tutor, edit match app/admin/subjects/: - New subjects management CRUD UI for admin (add/activate/deactivate subjects in the request form) app/admin/users/page.tsx: - User list with role badges and primary role display app/admin/error.tsx, app/admin/loading.tsx: - Error boundary and skeleton loading state for admin routes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ssword, tutor onboarding app/auth/sign-up/page.tsx: - Full Bauhaus-styled sign-up form with client-side Zod validation - Show/hide password toggle; clear error messages - Redirect to /auth/verify after successful sign-up app/auth/sign-up/tutor/: - Dedicated tutor sign-up flow that sets primary_role = 'tutor' and pre-populates the profile-setup step with tutor fields app/auth/sign-in/page.tsx + SignInForm.tsx: - Redesigned sign-in with Google OAuth button and email/password form - Inline error display for invalid credentials - 'Forgot password?' link app/auth/forgot-password/: - New forgot-password page: sends Supabase password reset email - Success state with resend option app/auth/reset-password/: - New reset-password page: handles magic link callback, prompts for new password with confirmation app/auth/verify/page.tsx: - Enhanced: shows email address, resend instructions, links to Gmail/Outlook - ResendButton.tsx: client component to resend verification email with 60s cooldown to prevent spam app/auth/profile-setup/page.tsx: - Collects display_name, whatsapp_number (normalises +92 prefix), timezone (IANA selector), and for_student_name (parent accounts) - Validates WhatsApp format before saving; redirects to /dashboard Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… proof URLs (A1+A3+D4+D5+D8) app/dashboard/page.tsx: - OnboardingChecklist: progress bar + step list tracking profile → request → payment → match → first session; auto-hides when all steps are complete - StatusBanner: request-status-aware banner explaining the current state and next action (payment_pending, ready_to_match, matched, active, paused, ended) - Redirects admin→/admin, tutor→/tutor based on primary_role app/dashboard/packages/[id]/page.tsx (A1+A3+D5): - Replace getPublicUrl() (broken on private buckets) with getPaymentProofSignedUrl() server action (300s signed URL, verifies user owns the payment record) - Rejected payments: show upload form again with rejection reason from payment.rejection_note; student can re-upload proof and enter a corrected reference number - resubmitRejectedPayment() server action resets status → pending app/dashboard/requests/: - RequestForm.tsx: multi-step request form with subject selector, level/exam-board pickers, goals textarea, availability windows, timezone selector - CancelRequestButton.tsx: cancel pending requests with confirmation - actions.ts: createRequest(), cancelRequest() server actions app/dashboard/sessions/page.tsx: - Sessions list sorted by scheduled_start_utc; past sessions shown with status badge; RescheduleButton on upcoming sessions app/dashboard/tutor/[id]/page.tsx (D4): - Read-only tutor profile page accessible to matched students - Shows: display name, bio, subjects by level, experience, education, teaching approach; hides WhatsApp and availability windows - Verifies match relationship before granting access app/dashboard/layout.tsx: - Dashboard layout with nav links and sign-out Server Action app/dashboard/error.tsx, app/dashboard/loading.tsx: - Error boundary and skeleton loading state components/dashboards/: - OnboardingChecklist.tsx: progress bar + step checklist component - StatusBanner.tsx: request status → friendly message + colour variant - NextSessionCard.tsx: tutor name, subject, time in user's timezone, Meet link button; empty state when no upcoming session - PackageSummary.tsx: sessions remaining, renewal alert at ≤3 left or ≤5 days - RescheduleButton.tsx: disabled with tooltip when < 24h to start (B3) - SessionCompleteForm.tsx: toast.success()/toast.error() on submit (G1) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ror boundaries app/tutor/page.tsx: - Next session hero card: time in tutor's timezone (luxon), student name, subject, level, one-tap Meet link, Google Calendar add button - Upcoming session count, completed session count quick stats - Quick links to sessions list, profile edit app/tutor/sessions/page.tsx: - Sessions sorted chronologically; past sessions show status badge - SessionCompleteForm inline: mark done / student no-show / tutor no-show with optional notes; toasts on success/error (G1) app/tutor/profile/TutorProfileForm.tsx: - WeeklyAvailabilityPicker integration for setting availability - Subjects multi-select by level (O Level, A Level, both) - Bio, experience_years, education, teaching_approach fields - Saves to tutor_profiles via updateTutorProfile() server action app/tutor/profile/actions.ts: - updateTutorProfile() validates with Zod and writes audit log app/tutor/layout.tsx: - Nav with sign-out Server Action; mobile-friendly horizontal scroll with fade indicators app/tutor/conduct/page.tsx: - Tutor code of conduct: attendance, punctuality, communication, no-show consequences, Meet link policy app/tutor/error.tsx, app/tutor/loading.tsx: - Error boundary and skeleton loading state for tutor routes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
app/page.tsx: - Bold uppercase hero headline with geometric color block accent - Thick black borders and 8px hard-shadow (bauhaus-shadow-lg) - How It Works section: 4-step process with numbered geometric squares - Subjects & Levels section with badge grid - Pricing cards for 8/12/20 session tiers with Bauhaus button style - LeadForm and WhatsApp CTA integrated at bottom - Fully responsive: stacks vertically on mobile, side-by-side on lg components/LeadForm.tsx: - Redesigned with Bauhaus input/button style - Client-side Zod validation (name, email, phone, message) - Toast feedback on success/error - Rate-limited by /api/leads (10 req/min) components/WhatsAppCTA.tsx, WhatsAppLink.tsx: - WhatsApp number sourced from NEXT_PUBLIC_WHATSAPP_NUMBER env var - WhatsAppLink builds wa.me URL with optional pre-filled message components/CopyMessageButton.tsx: - Copy-to-clipboard + open WhatsApp in new tab - Clipboard icon → check icon animation on copy components/AdminPagination.tsx: - Reusable pagination component for all admin list pages - Uses shadcn Pagination component; handles page=1 edge case components/WeeklyAvailabilityPicker.tsx: - Interactive day/time slot grid for tutor availability and student availability windows on request form - 7 days × time slot columns; toggleable cells with shift+click range app/error.tsx, app/not-found.tsx: - Global error boundary and 404 page with Bauhaus styling - 404 shows numeric big text block with return-home link Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
app/privacy/page.tsx: - Full Privacy Policy covering: data collected (name, email, WhatsApp, payment receipts), how data is used, storage via Supabase (EU/US), data retention, user rights (access, deletion, correction), contact information for data requests - Important for legal compliance when handling student/parent data and payment proof uploads app/terms/page.tsx: - Terms of Service covering: service description, user eligibility, payment terms (bank transfer, 48h verification), refund policy, no-show policy per docs/MVP.md, intellectual property, account termination conditions - Pakistan-jurisdiction clause; links to privacy policy app/help/page.tsx: - FAQ section with 10 common questions across: Getting Started, Sessions, Payments, Technical categories - WhatsApp contact button (pre-filled "I need help" message) - Email support address - Quick links to /policies, /privacy, /terms All three pages use Bauhaus styling and are accessible from the landing page footer and the student dashboard help icon. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tests vitest.config.ts (already committed) configures Vitest with node environment and @/ path alias. lib/services/__tests__/scheduling.test.ts (11 tests): - Generates correct session count for each tier (8/12/20) - Respects tier cap (no extra sessions beyond tierSessions) - Respects start/end date range (no sessions outside the month) - Handles 31-day months correctly - Converts session times to UTC correctly for Asia/Karachi (PKT, +5:00) - Handles DST boundary correctly for America/New_York (EDT/EST) - Returns empty array for unrecognised IANA timezone - Returns empty array for malformed time string (HH:MM expected) - Returns empty array when no days match in date range - Calculates duration_mins correctly in scheduled_end_utc - All 11 tests pass lib/__tests__/rate-limit.test.ts (4 tests): - Allows requests within the configured limit (remaining decrements) - Blocks requests once the limit is exceeded - Resets counter after the window expires (tested with vi.useFakeTimers) - Tracks different IP keys independently - All 4 tests pass playwright.config.ts: - Playwright E2E configuration targeting http://localhost:3000 - Single browser (Chromium) for CI speed; full browser matrix optional - webServer block to auto-start dev server during test runs e2e/: - Initial E2E test stubs for: sign-up flow, student request creation, admin payment approval, tutor session completion Run: npm test (unit) | npm run test:e2e (E2E) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pleted sprint CLAUDE.md: - Add unit test commands: npm test, npm run test:watch - Add typecheck command: npm run typecheck - Update key file locations table with new infrastructure: database.types.ts, rate-limit.ts, OnboardingChecklist, StatusBanner, unit tests, cron jobs, vercel.json/.env.example - Fix Atomic RPCs section: p_package_id → p_request_id (B2) docs/GAP_ANALYSIS.md: - Update summary table with status column (24/34 items done) - Mark all resolved items with ✅ DONE and resolution details: A1-A4, B1-B3, C1-C5, D3-D8, E1-E2, F1+F3-F5, G1-G2, H1 - Mark deferred items: B4 (reminders), D1 (parent accounts), D2 (email), F2 (Sentry), G3 (skeletons), H3 (tables) - Update Recommended Priority Order: all critical/important items struck through; only Sentry and nice-to-haves remain docs/plan-CorvEd.md: - Sprint 4 marked COMPLETE - Section 3 (MVP v0.1 blockers): mark addressed items done - Section 4 (MVP v0.2): mark completed items; testing section done - Section 6 (refinement plan): update step statuses - Section 9 (new): full inventory of files created and modified in this sprint with description of each change advisor_update.md, plan-corvEd.prompt.md: - Planning artifacts for this sprint Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
scripts/bauhaus-replace.ps1:
- PowerShell utility script used during the Bauhaus design sprint to
batch-remove dark mode Tailwind classes (dark:bg-*, dark:text-*,
dark:border-*, dark:hover:*) from all .tsx files in app/ and
components/; CorvEd uses a single light-mode Bauhaus palette so
dark mode variants are intentionally removed
- Also standardises specific Bauhaus button and badge class strings
across the codebase for consistency
scripts/fix3.ps1:
- Targeted follow-up script to correct residual class mismatches
after bauhaus-replace.ps1 (e.g. hover:border-indigo-400 →
hover:border-[#1040C0], animate-spin colour fix, button variant
string standardisation)
- Both scripts are idempotent: re-running them on already-updated
files produces no changes
supabase/.branches/_current_branch:
- Supabase CLI internal file tracking the active local branch ("main")
- Committed so the local Supabase environment matches the repo state
when a new developer clones and runs `npx supabase start`
.gitignore additions:
- .claude/ — Claude Code local settings (machine-specific)
- .playwright-mcp/ — Playwright MCP server runtime state
- .serena/ — Serena semantic code nav tool state
- .vscode/ — VS Code MCP config (machine-specific, not project config)
- playwright-report/ — generated Playwright HTML reports
- test-results/ — generated Playwright test artifacts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… copilot/fix-failing-workflow-again
…onent Co-authored-by: Taleef7 <89072337+Taleef7@users.noreply.github.com>
<a> with Next.js <Link> in error.tsx
- app/admin/requests/actions.ts: import Json & Database types; cast auditDetails as Json; use 'rejected' for payment status (not 'cancelled' which is not in the enum); cast newStatus to request_status_enum - app/admin/requests/page.tsx: import Database type; cast activeStatus and level to their respective enum types for Supabase .eq() calls - app/dashboard/requests/actions.ts: import Database type; use 'rejected' for payment status instead of 'cancelled' - lib/services/sessions.ts: import SchedulePattern type; fix schedule_pattern cast via unknown; pass empty string instead of null for p_notes RPC arg Co-authored-by: Taleef7 <89072337+Taleef7@users.noreply.github.com>
<a> with Next.js <Link> in error.tsxThere was a problem hiding this comment.
Pull request overview
Fixes CI-breaking TypeScript issues while expanding the app’s UI/system foundations (security headers, role enforcement, shared UI components, validators) and adding automated coverage for new utilities.
Changes:
- Added Supabase typing improvements, role-based middleware enforcement, and new admin/student/tutor server actions.
- Introduced shared UI primitives (shadcn-style components), new validators/config, and styling updates (Bauhaus theme).
- Added Vitest + Playwright tests for scheduling, formatting utilities, rate limiting, and key public/protected routes.
Reviewed changes
Copilot reviewed 143 out of 163 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| next.config.ts | Adds global security headers for all routes. |
| middleware.ts | Adds role-based and email-verification redirects for protected routes. |
| lib/validators/request.ts | Migrates availability_windows to structured array schema; adds tier validation. |
| lib/validators/payment.ts | Adds Zod schemas/types for payment-related actions. |
| lib/utils/session.ts | Updates session status labels/colors and refactors time formatting. |
| lib/utils/request.ts | Updates request status UI mapping and adds availability formatting utility. |
| lib/utils.ts | Adds cn() helper for Tailwind class merging. |
| lib/supabase/server.ts | Adds typed Supabase server client and safe cookie write handling. |
| lib/supabase/client.ts | Adds typed Supabase browser client. |
| lib/supabase/admin.ts | Adds typed Supabase admin client. |
| lib/services/whatsapp.ts | Adds WhatsApp helpers (templates/link builder re-exports + phone normalization). |
| lib/services/requests.ts | Adds request transition validation and admin request fetch helper. |
| lib/services/payments.ts | Adds package expiry + expiring-package queries and revalidation. |
| lib/services/tests/scheduling.test.ts | Adds Vitest coverage for session generation (tiers/timezones/DST). |
| lib/rate-limit.ts | Adds simple in-memory rate limiter utility. |
| lib/config/timezones.ts | Centralizes timezone options and type. |
| lib/config/pricing.ts | Reads payment instructions from env with safe fallbacks. |
| lib/auth/requireAdmin.ts | Adds shared admin guard for server actions. |
| lib/tests/rate-limit.test.ts | Adds Vitest coverage for in-memory rate limiter behavior. |
| e2e/utils.spec.ts | Adds Playwright checks for formatAvailabilityWindows and getLevelLabel. |
| e2e/responsive.spec.ts | Adds Playwright mobile viewport smoke tests. |
| e2e/request-form.spec.ts | Adds Playwright auth-redirect coverage for key pages. |
| e2e/protected-routes.spec.ts | Adds Playwright unauthenticated redirect assertions. |
| e2e/policies.spec.ts | Adds Playwright policies page rendering assertions. |
| e2e/not-found.spec.ts | Adds Playwright 404 page behavior assertions. |
| e2e/landing.spec.ts | Adds Playwright landing page smoke checks. |
| e2e/dashboard-features.spec.ts | Adds Playwright auth-redirect coverage across dashboard/admin/tutor routes. |
| e2e/auth.spec.ts | Adds Playwright auth UI smoke tests. |
| e2e/accessibility.spec.ts | Adds basic Playwright accessibility assertions. |
| components/ui/tooltip.tsx | Adds tooltip primitive wrapper component. |
| components/ui/tabs.tsx | Adds tabs primitive wrapper component. |
| components/ui/table.tsx | Adds table primitives for consistent styling. |
| components/ui/sonner.tsx | Adds global toast configuration wrapper. |
| components/ui/skeleton.tsx | Adds skeleton component for loading states. |
| components/ui/separator.tsx | Adds separator primitive wrapper component. |
| components/ui/pagination.tsx | Adds pagination primitives + link components. |
| components/ui/label.tsx | Adds label primitive wrapper component. |
| components/ui/input.tsx | Adds input component with themed focus/invalid styling. |
| components/ui/form.tsx | Adds RHF form primitives (FormField, FormControl, etc.). |
| components/ui/dialog.tsx | Adds dialog primitive wrapper component. |
| components/ui/card.tsx | Adds themed card primitives. |
| components/ui/button.tsx | Adds themed button component + variants. |
| components/ui/badge.tsx | Adds themed badge component + variants. |
| components/ui/alert-dialog.tsx | Adds alert-dialog primitive wrapper component. |
| components/dashboards/StatusBanner.tsx | Adds dashboard status banner helper + status-to-message mapping. |
| components/dashboards/SessionCompleteForm.tsx | Updates tutor session completion UX and adds toast feedback. |
| components/dashboards/RescheduleButton.tsx | Updates session reschedule CTA styling. |
| components/dashboards/OnboardingChecklist.tsx | Adds onboarding checklist component with progress bar. |
| components/dashboards/NextSessionCard.tsx | Restyles next-session card and improves Meet CTA accessibility. |
| components/WhatsAppLink.tsx | Restyles WhatsApp link component. |
| components/WhatsAppCTA.tsx | Restyles WhatsApp CTA component. |
| components/WeeklyAvailabilityPicker.tsx | Adds interactive weekly availability grid input. |
| components/CopyMessageButton.tsx | Restyles copy/open WhatsApp utility button(s). |
| components/AdminPagination.tsx | Adds admin list pagination component and shared page size. |
| components.json | Adds shadcn/ui config metadata for component generation. |
| app/tutor/sessions/page.tsx | Restyles tutor sessions listing and status UI. |
| app/tutor/profile/page.tsx | Switches reads to admin client for tutor data; restyles page. |
| app/tutor/profile/actions.ts | Switches tutor profile mutations to admin client (scoped to user). |
| app/tutor/loading.tsx | Adds tutor route loading skeleton. |
| app/tutor/layout.tsx | Adds tutor nav + sign-out server action integration. |
| app/tutor/error.tsx | Adds tutor route error boundary UI. |
| app/not-found.tsx | Adds global 404 page. |
| app/layout.tsx | Adds global TooltipProvider and Sonner Toaster. |
| app/globals.css | Adds base styles, focus ring, scrollbar, Bauhaus utilities, reduced-motion. |
| app/error.tsx | Adds global error boundary UI. |
| app/dashboard/tutor/[id]/page.tsx | Adds student-facing read-only tutor profile page. |
| app/dashboard/sessions/page.tsx | Restyles student sessions UI; improves Meet link accessibility. |
| app/dashboard/requests/actions.ts | Adds student cancel-request server action with audit logging. |
| app/dashboard/requests/[id]/CancelRequestButton.tsx | Adds client cancel-request button that calls server action. |
| app/dashboard/packages/new/page.tsx | Adds tier query param preselect + restyles package selection. |
| app/dashboard/packages/actions.ts | Adds signed proof URL generator + rejected payment resubmit action. |
| app/dashboard/loading.tsx | Adds dashboard loading skeleton. |
| app/dashboard/layout.tsx | Adds student dashboard layout nav + sign-out + role redirect. |
| app/dashboard/error.tsx | Adds dashboard route error boundary UI. |
| app/auth/verify/page.tsx | Restyles verification page and adds geometric panel. |
| app/auth/verify/ResendButton.tsx | Adds client-side resend verification flow. |
| app/auth/sign-out/route.ts | Supports GET+POST sign-out and centralizes logic. |
| app/auth/sign-in/page.tsx | Restyles sign-in page with geometric panel. |
| app/auth/reset-password/page.tsx | Adds reset-password page for Supabase recovery flow. |
| app/auth/profile-setup/page.tsx | Refactors profile setup form to Bauhaus UI components. |
| app/auth/forgot-password/page.tsx | Adds forgot-password page using Supabase reset flow. |
| app/auth/actions.ts | Adds shared signOut server action. |
| app/api/leads/route.ts | Adds IP-based rate limiting to lead submission endpoint. |
| app/api/cron/expire-packages/route.ts | Adds secured cron endpoint for package expiration. |
| app/admin/tutors/page.tsx | Restyles tutor directory and filter UI. |
| app/admin/tutors/actions.ts | Switches to shared requireAdmin guard. |
| app/admin/tutors/[id]/page.tsx | Restyles admin tutor detail page. |
| app/admin/tutors/TutorFilters.tsx | Restyles tutor filter selects. |
| app/admin/tutors/TutorActions.tsx | Restyles tutor approve/revoke actions UI. |
| app/admin/subjects/page.tsx | Adds admin subjects management page. |
| app/admin/subjects/actions.ts | Adds subject add/toggle server actions. |
| app/admin/subjects/AddSubjectForm.tsx | Adds client form for subject creation. |
| app/admin/sessions/page.tsx | Adds pagination and restyles sessions list; improves join-meet accessibility. |
| app/admin/requests/actions.ts | Switches to shared requireAdmin; adds request/match admin actions. |
| app/admin/requests/[id]/AdminRequestActions.tsx | Adds client admin controls for request status/cancel. |
| app/admin/requests/RequestFilters.tsx | Restyles request filter selects. |
| app/admin/payments/actions.ts | Adds Zod validation; switches to shared requireAdmin. |
| app/admin/payments/ViewProofButton.tsx | Adds modal preview + signed URL loading for payment proofs. |
| app/admin/payments/PaymentQuickActions.tsx | Adds WhatsApp/copy quick actions for payment workflows. |
| app/admin/payments/PaymentActions.tsx | Restyles payment action forms. |
| app/admin/page.tsx | Adds richer admin dashboard cards + counts. |
| app/admin/loading.tsx | Adds admin loading skeleton. |
| app/admin/layout.tsx | Adds admin nav + sign-out server action integration. |
| app/admin/error.tsx | Adds admin route error boundary UI. |
| app/admin/audit/page.tsx | Restyles audit log page. |
| app/admin/actions.ts | Switches to shared requireAdmin guard and removes duplicate signOut. |
| .env.example | Documents new env vars (bank info, cron secret, Sentry). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } from "lucide-react" | ||
|
|
||
| import { cn } from "@/lib/utils" | ||
| import { buttonVariants, type Button } from "@/components/ui/button" |
There was a problem hiding this comment.
"type Button" is imported as a type, but the file then uses Button in a value position via React.ComponentProps. This will cause a TypeScript error. Import Button as a value (or define a ButtonProps type to reference) and update the import accordingly.
| import { buttonVariants, type Button } from "@/components/ui/button" | |
| import { buttonVariants, Button } from "@/components/ui/button" |
| "use client" | ||
|
|
||
| import * as React from "react" | ||
| import { Tooltip as TooltipPrimitive } from "radix-ui" |
There was a problem hiding this comment.
The Radix import path/shape here does not match Radix UI’s standard packages (typically @radix-ui/react-tooltip with named exports such as Provider/Root/Trigger/Content/Portal/Arrow). As written, this is very likely to fail at build/runtime. Consider switching to the correct Radix package import and export usage; the same "radix-ui" import pattern appears in other newly added UI primitives (tabs/dialog/alert-dialog/label/separator/form/button/badge) and should be corrected consistently.
| import { Tooltip as TooltipPrimitive } from "radix-ui" | |
| import * as TooltipPrimitive from "@radix-ui/react-tooltip" |
| data-slot="tooltip-content" | ||
| sideOffset={sideOffset} | ||
| className={cn( | ||
| "bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance", |
There was a problem hiding this comment.
The Tailwind arbitrary value syntax for origin looks incorrect: "origin-(--radix-tooltip-content-transform-origin)" won’t be interpreted as an arbitrary value class. Use bracket syntax (e.g., origin-[--...]) so the transform-origin is applied correctly.
| const byDay = new Map<number, AvailWindow[]>(); | ||
| for (const w of windows as AvailWindow[]) { | ||
| if (w.day == null || !w.start || !w.end) continue; | ||
| const d = Number(w.day); |
There was a problem hiding this comment.
formatAvailabilityWindows can emit "undefined ..." if a window has a day outside 0–6 (or a non-numeric day producing NaN), because DAY_NAMES[d] will be undefined. Since the input is "unknown", it should defensively skip entries where "d" is not an integer in [0,6] before grouping/formatting.
| const d = Number(w.day); | |
| const d = Number(w.day); | |
| // Skip invalid day indices to avoid "undefined" DAY_NAMES entries | |
| if (!Number.isInteger(d) || d < 0 || d > 6) continue; |
| const ranges = slots.map((s) => `${shortTime(s.start)}–${shortTime(s.end)}`); | ||
| lines.push(`${DAY_NAMES[d]} ${ranges.join(", ")}`); |
There was a problem hiding this comment.
formatAvailabilityWindows can emit "undefined ..." if a window has a day outside 0–6 (or a non-numeric day producing NaN), because DAY_NAMES[d] will be undefined. Since the input is "unknown", it should defensively skip entries where "d" is not an integer in [0,6] before grouping/formatting.
| return { error: parsed.error.issues[0].message } | ||
| } | ||
|
|
||
| const admin = createAdminClient() |
There was a problem hiding this comment.
These admin server actions use the service-role admin client but do not enforce an admin guard (unlike other admin actions in this PR that call requireAdmin). To prevent direct invocation by non-admins (even if the UI is protected), add requireAdmin() checks at the start of addSubject and toggleSubjectActive.
| export async function toggleSubjectActive(id: number, active: boolean) { | ||
| const admin = createAdminClient() |
There was a problem hiding this comment.
These admin server actions use the service-role admin client but do not enforce an admin guard (unlike other admin actions in this PR that call requireAdmin). To prevent direct invocation by non-admins (even if the UI is protected), add requireAdmin() checks at the start of addSubject and toggleSubjectActive.
|
|
||
| return ( | ||
| <form action={formAction} className="mt-2 space-y-2 rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-800/50"> | ||
| <form action={formAction} className="mt-2 space-y-2 border border-[#D0D0D0] bg-[#F0F0F0] p-3 /50"> |
There was a problem hiding this comment.
The className contains a stray "/50" token, which looks like an incomplete Tailwind opacity utility (e.g., bg-black/50). This "class" won’t have any effect and is likely accidental—remove it or replace it with the intended utility.
| <form action={formAction} className="mt-2 space-y-2 border border-[#D0D0D0] bg-[#F0F0F0] p-3 /50"> | |
| <form action={formAction} className="mt-2 space-y-2 border border-[#D0D0D0] bg-[#F0F0F0] p-3"> |
| // Supabase will have set the session from the email link callback | ||
| useEffect(() => { | ||
| const supabase = createClient() | ||
| supabase.auth.onAuthStateChange((event) => { | ||
| if (event === 'PASSWORD_RECOVERY') { | ||
| // Session is set, user can now update password | ||
| } | ||
| }) | ||
| }, []) | ||
|
|
There was a problem hiding this comment.
onAuthStateChange returns a subscription that should be unsubscribed in the effect cleanup to avoid leaking listeners across client navigations. Also, since the handler currently doesn’t do anything, consider removing this effect entirely or using it to drive UI/state when the recovery session is present.
| // Supabase will have set the session from the email link callback | |
| useEffect(() => { | |
| const supabase = createClient() | |
| supabase.auth.onAuthStateChange((event) => { | |
| if (event === 'PASSWORD_RECOVERY') { | |
| // Session is set, user can now update password | |
| } | |
| }) | |
| }, []) |
| @@ -1,4 +1,4 @@ | |||
| // E8 S8.1 S8.2: Tutor sessions list — upcoming and past sessions with status update | |||
| // E8 S8.1 S8.2: Tutor sessions list — upcoming and past sessions with status update | |||
There was a problem hiding this comment.
This file appears to start with a UTF-8 BOM (the invisible character before the comment). BOMs can cause noisy diffs and occasional tooling issues; consider removing it. The same pattern appears in a few other changed files (e.g., some dashboard/auth components).
| // E8 S8.1 S8.2: Tutor sessions list — upcoming and past sessions with status update | |
| // E8 S8.1 S8.2: Tutor sessions list — upcoming and past sessions with status update |
|
@copilot apply changes based on the comments in this thread |
- pagination.tsx: import Button as value (not type-only) so typeof works - tooltip.tsx: fix Tailwind arbitrary value syntax origin-[--radix-...] - lib/utils/request.ts: skip day indices outside 0-6 in formatAvailabilityWindows - app/api/leads/route.ts: improve IP detection; skip rate limit when IP unavailable - app/admin/subjects/actions.ts: add requireAdmin() guard to addSubject and toggleSubjectActive - SessionCompleteForm.tsx: remove stray '/50' from className - app/auth/reset-password/page.tsx: remove no-op onAuthStateChange useEffect; drop unused import - Strip UTF-8 BOM from 33 affected .tsx/.ts files across app/ and components/ Co-authored-by: Taleef7 <89072337+Taleef7@users.noreply.github.com>
Applied all review comments in commit 9b1653d:
Note: The |
npx tsc --noEmitwas failing with 10 type errors across 4 files, blocking CI after lint passed. Additional correctness, security, and cleanup fixes were applied based on PR review feedback.Changes
app/admin/requests/actions.tsauditDetails(Record<string,unknown>) asJsonforaudit_logs.detailspayment_status_enumhas no'cancelled'— changed to'rejected'for voided pending paymentsnewStatus: string→request_status_enum(already runtime-validated in same function)app/admin/requests/page.tsxactiveStatusandlevelURL params torequest_status_enum/level_enumfor Supabase.eq()callsapp/dashboard/requests/actions.ts'cancelled'→'rejected'fix for student-side payment voidinglib/services/sessions.tsJson→SchedulePatterncast:as unknown as SchedulePatterntutor_update_sessionRPC typesp_notesasstring, notstring | null— changedtutorNotes ?? nulltotutorNotes ?? ""components/ui/pagination.tsxtype Buttonto value import soReact.ComponentProps<typeof Button>resolves correctlycomponents/ui/tooltip.tsxorigin-(--radix-tooltip-content-transform-origin)→origin-[--radix-tooltip-content-transform-origin]lib/utils/request.tsformatAvailabilityWindowsto skip day values outside[0, 6]or non-integer, preventingundefinedentries in outputapp/api/leads/route.tsx-real-ip, falls back tox-forwarded-for, skips rate limiting entirely when no IP is available (prevents accidental global throttling on'unknown'key)app/admin/subjects/actions.tsrequireAdmin()auth guard to bothaddSubjectandtoggleSubjectActiveto prevent direct invocation by non-adminscomponents/dashboards/SessionCompleteForm.tsx/50token from formclassNameapp/auth/reset-password/page.tsxonAuthStateChangeuseEffectthat subscribed without cleanup and did nothing; removed now-unuseduseEffectimport33 files across
app/andcomponents/.ts/.tsxfiles to prevent noisy diffs and tooling issuesTesting
Notes
'rejected'is the closest semantically validpayment_status_enumvalue for a voided/withdrawn payment. A proper'cancelled'enum variant would require a DB migration.p_notesreflects the RPC's generated type; passingnullwould require updating the DB function signature and regenerating types.radix-uiumbrella package (v1.4.3) correctly re-exports all Radix primitives; those imports were verified to work and were left unchanged.💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.