Skip to content

Fix TypeScript type errors and apply review feedback for CI on mvp-overhaul PR#109

Merged
Taleef7 merged 21 commits intomainfrom
copilot/fix-failing-workflow-again
Mar 4, 2026
Merged

Fix TypeScript type errors and apply review feedback for CI on mvp-overhaul PR#109
Taleef7 merged 21 commits intomainfrom
copilot/fix-failing-workflow-again

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 3, 2026

npx tsc --noEmit was 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.ts

  • Cast auditDetails (Record<string,unknown>) as Json for audit_logs.details
  • payment_status_enum has no 'cancelled' — changed to 'rejected' for voided pending payments
  • Cast newStatus: stringrequest_status_enum (already runtime-validated in same function)

app/admin/requests/page.tsx

  • Cast activeStatus and level URL params to request_status_enum / level_enum for Supabase .eq() calls

app/dashboard/requests/actions.ts

  • Same 'cancelled''rejected' fix for student-side payment voiding

lib/services/sessions.ts

  • Fix overly-narrow JsonSchedulePattern cast: as unknown as SchedulePattern
  • tutor_update_session RPC types p_notes as string, not string | null — changed tutorNotes ?? null to tutorNotes ?? ""

components/ui/pagination.tsx

  • Changed type Button to value import so React.ComponentProps<typeof Button> resolves correctly

components/ui/tooltip.tsx

  • Fixed Tailwind arbitrary value syntax: origin-(--radix-tooltip-content-transform-origin)origin-[--radix-tooltip-content-transform-origin]

lib/utils/request.ts

  • Added defensive guard in formatAvailabilityWindows to skip day values outside [0, 6] or non-integer, preventing undefined entries in output

app/api/leads/route.ts

  • Improved IP detection: uses x-real-ip, falls back to x-forwarded-for, skips rate limiting entirely when no IP is available (prevents accidental global throttling on 'unknown' key)

app/admin/subjects/actions.ts

  • Added requireAdmin() auth guard to both addSubject and toggleSubjectActive to prevent direct invocation by non-admins

components/dashboards/SessionCompleteForm.tsx

  • Removed stray /50 token from form className

app/auth/reset-password/page.tsx

  • Removed no-op onAuthStateChange useEffect that subscribed without cleanup and did nothing; removed now-unused useEffect import

33 files across app/ and components/

  • Stripped UTF-8 BOM characters from all affected .ts/.tsx files to prevent noisy diffs and tooling issues

Testing

  • Verified locally
  • Checked key flows manually

Notes

  • 'rejected' is the closest semantically valid payment_status_enum value for a voided/withdrawn payment. A proper 'cancelled' enum variant would require a DB migration.
  • The empty-string fallback for p_notes reflects the RPC's generated type; passing null would require updating the DB function signature and regenerating types.
  • The radix-ui umbrella 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.

Taleef7 and others added 17 commits March 3, 2026 16:33
- 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 AI and others added 2 commits March 3, 2026 22:04
…onent

Co-authored-by: Taleef7 <89072337+Taleef7@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix failing workflow in MVP overhaul Fix failing CI: replace <a> with Next.js <Link> in error.tsx Mar 3, 2026
- 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>
Copilot AI changed the title Fix failing CI: replace <a> with Next.js <Link> in error.tsx Fix TypeScript type errors breaking CI on mvp-overhaul PR Mar 3, 2026
@Taleef7 Taleef7 marked this pull request as ready for review March 4, 2026 03:11
Copilot AI review requested due to automatic review settings March 4, 2026 03:11
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread components/ui/pagination.tsx Outdated
} from "lucide-react"

import { cn } from "@/lib/utils"
import { buttonVariants, type Button } from "@/components/ui/button"
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"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.

Suggested change
import { buttonVariants, type Button } from "@/components/ui/button"
import { buttonVariants, Button } from "@/components/ui/button"

Copilot uses AI. Check for mistakes.
Comment thread components/ui/tooltip.tsx
"use client"

import * as React from "react"
import { Tooltip as TooltipPrimitive } from "radix-ui"
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
import { Tooltip as TooltipPrimitive } from "radix-ui"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"

Copilot uses AI. Check for mistakes.
Comment thread components/ui/tooltip.tsx Outdated
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",
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread lib/utils/request.ts
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);
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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;

Copilot uses AI. Check for mistakes.
Comment thread lib/utils/request.ts
Comment on lines +94 to +95
const ranges = slots.map((s) => `${shortTime(s.start)}–${shortTime(s.end)}`);
lines.push(`${DAY_NAMES[d]} ${ranges.join(", ")}`);
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
return { error: parsed.error.issues[0].message }
}

const admin = createAdminClient()
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +54 to +55
export async function toggleSubjectActive(id: number, active: boolean) {
const admin = createAdminClient()
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.

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">
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
<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">

Copilot uses AI. Check for mistakes.
Comment thread app/auth/reset-password/page.tsx Outdated
Comment on lines +17 to +26
// 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
}
})
}, [])

Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
// 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
}
})
}, [])

Copilot uses AI. Check for mistakes.
Comment thread app/tutor/sessions/page.tsx Outdated
@@ -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
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
// 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 uses AI. Check for mistakes.
@Taleef7
Copy link
Copy Markdown
Owner

Taleef7 commented Mar 4, 2026

@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>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 4, 2026

@copilot apply changes based on the comments in this thread

Applied all review comments in commit 9b1653d:

  • pagination.tsx: Changed type Button to value import so React.ComponentProps<typeof Button> works correctly
  • tooltip.tsx: Fixed Tailwind CSS arbitrary value syntax from origin-(--radix-...) to origin-[--radix-...]
  • lib/utils/request.ts: Added defensive check in formatAvailabilityWindows to skip day values outside 0–6 (or non-integer)
  • app/api/leads/route.ts: Improved IP detection — uses x-real-ip, falls back to x-forwarded-for, and skips rate limiting entirely if no IP is available (prevents accidental global throttling)
  • app/admin/subjects/actions.ts: Added requireAdmin() guard to both addSubject and toggleSubjectActive
  • SessionCompleteForm.tsx: Removed stray /50 token from the form className
  • app/auth/reset-password/page.tsx: Removed the no-op onAuthStateChange useEffect (it subscribed but never unsubscribed and did nothing); also removed now-unused useEffect import
  • UTF-8 BOM: Stripped BOM from all 33 affected .ts/.tsx files across app/ and components/

Note: The radix-ui umbrella package imports (tooltip.tsx etc.) are correct — radix-ui@1.4.3 is installed and does re-export all primitives. TypeScript and lint both pass with 0 errors.

Copilot AI changed the title Fix TypeScript type errors breaking CI on mvp-overhaul PR Fix TypeScript type errors and apply review feedback for CI on mvp-overhaul PR Mar 4, 2026
@Taleef7 Taleef7 merged commit defd0bb into main Mar 4, 2026
1 check passed
@Taleef7 Taleef7 deleted the copilot/fix-failing-workflow-again branch March 4, 2026 04:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants