feat: navigation and UX improvements complete
This commit is contained in:
@@ -22,18 +22,15 @@ You are a senior software engineer specializsing in NodeJS, Typescript, front en
|
||||
- Make no assumptions.
|
||||
- Ask clarifying questions.
|
||||
- Ultrathink
|
||||
- You will be extending the "Documents" feature to include manuals.
|
||||
- You will be auditing the Dark vs Light theme implementation
|
||||
|
||||
*** CONTEXT ***
|
||||
- This is a modern web app for managing a vehicle fleet. It has both a desktop and mobile versions of the site that both need to maintain feature parity. It's currently deployed via docker compose but in the future will be deployed via k8s.
|
||||
- Read README.md CLAUDE.md and AI-INDEX.md and follow relevant instructions to understand this code repository in the context of this change.
|
||||
- You need to extend the Documents feature to include a third "Document Type"
|
||||
- Right now the document has two types. Insurance and Registration
|
||||
- The third type will be called "Manual"
|
||||
- This document will just have the uploaded file and a notes field and Title field
|
||||
- When implementing this we need to play for the future feature of scanning the document for maintenance schedules
|
||||
- Add a toggle for this scanning. Label it "Scan for Maintenance Schedule"
|
||||
- Do not implement this feature at this time but put the toggle in the interface and the backend changes to facility this workflow.
|
||||
- You need to audit the Dark vs Light theme.
|
||||
- The colors were not all changed so some of the dark theme
|
||||
- The dark versus light theme does not save between logins.
|
||||
- Think hard about the color choices and if any better colors are available form the MVP-COLOR-SCHEME.md
|
||||
|
||||
*** CHANGES TO IMPLEMENT ***
|
||||
- Research this code base and ask iterative questions to compile a complete plan.
|
||||
@@ -58,30 +55,7 @@ You are a senior software engineer specializsing in NodeJS, Typescript, front en
|
||||
- Debug what could be causing this issue. No changes were made to the Gitlab server besides adding the RESEND variable so there shouldn't be anything on the server causing this issue.
|
||||
$ chmod +x scripts/inject-secrets.sh
|
||||
$ ./scripts/inject-secrets.sh
|
||||
Injecting secrets...
|
||||
Deploy path: /opt/gitlab-runner/builds/motovaultpro
|
||||
Secrets dir: /opt/gitlab-runner/builds/motovaultpro/secrets/app
|
||||
Cleaning up any corrupted secret paths...
|
||||
ERROR: Variable POSTGRES_PASSWORD is not set
|
||||
Ensure it exists in GitLab CI/CD Variables
|
||||
ERROR: Variable AUTH0_CLIENT_SECRET is not set
|
||||
Ensure it exists in GitLab CI/CD Variables
|
||||
ERROR: Variable AUTH0_MANAGEMENT_CLIENT_ID is not set
|
||||
Ensure it exists in GitLab CI/CD Variables
|
||||
ERROR: Variable AUTH0_MANAGEMENT_CLIENT_SECRET is not set
|
||||
Ensure it exists in GitLab CI/CD Variables
|
||||
ERROR: Variable GOOGLE_MAPS_API_KEY is not set
|
||||
Ensure it exists in GitLab CI/CD Variables
|
||||
ERROR: Variable GOOGLE_MAPS_MAP_ID is not set
|
||||
Ensure it exists in GitLab CI/CD Variables
|
||||
ERROR: Variable CF_DNS_API_TOKEN is not set
|
||||
Ensure it exists in GitLab CI/CD Variables
|
||||
ERROR: Variable RESEND_API_KEY is not set
|
||||
Ensure it exists in GitLab CI/CD Variables
|
||||
ERROR: One or more secrets failed to inject
|
||||
Ensure all required CI/CD variables are configured as File type in GitLab
|
||||
Running after_script
|
||||
00:00
|
||||
|
||||
Running after script...
|
||||
|
||||
|
||||
@@ -89,6 +63,35 @@ Running after script...
|
||||
|
||||
|
||||
|
||||
*** ROLE ***
|
||||
- You are a senior DBA with expert knowledge in Postgres SQL.
|
||||
|
||||
*** ACTION ***
|
||||
- Make no assumptions.
|
||||
- Ask clarifying questions.
|
||||
- Ultrathink
|
||||
- You will be implementing an ETL process that takes a export of the NHTSA vPIC database in Postgres and transforming it for use in this application.
|
||||
|
||||
*** CONTEXT ***
|
||||
- This is a modern web app for managing a vehicle fleet. It has both a desktop and mobile versions of the site that both need to maintain feature parity. It's currently deployed via docker compose but in the future will be deployed via k8s.
|
||||
- Read README.md CLAUDE.md and AI-INDEX.md and follow relevant instructions to understand this code repository in the context of this change.
|
||||
- There is an existing database import process in this directory. This process works and should not be changed.
|
||||
- The source database from the NHTSA vPIC dataset is located in the @vpic-source directory
|
||||
- Deep research needs to be conducted on how to execute this ETL process.
|
||||
- The source database is designed for VIN decoding only.
|
||||
- Example VIN: 2025 Honda Civic Hybrid - 2HGFE4F88SH315466
|
||||
- Example VIN: 2023 GMC Sierra 1500 AT4x - 3GTUUFEL6PG140748
|
||||
- Example VIN: 2017 Chevrolet Corvette Z06 - 1G1YU3D64H5602799
|
||||
|
||||
*** CHANGES TO IMPLEMENT ***
|
||||
- Research this code base and ask iterative questions to compile a complete plan.
|
||||
- generate a project plan
|
||||
- break into bite-sized tasks and milestones
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*** ROLE ***
|
||||
- You are a senior DBA with expert knowledge in Postgres SQL.
|
||||
|
||||
|
||||
448
docs/changes/DARK-THEME-PLAN.md
Normal file
448
docs/changes/DARK-THEME-PLAN.md
Normal file
@@ -0,0 +1,448 @@
|
||||
from pathlib import Path
|
||||
|
||||
md = """# MotoVaultPro Homepage Dark Theme Update (Logo-Compatible)
|
||||
|
||||
## Goal
|
||||
Update the **public marketing homepage** to a **dark theme** so the **new logo (with a black background)** feels native and intentional.
|
||||
|
||||
## Constraints (DO NOT VIOLATE)
|
||||
- **Do not modify the logo image files** (no transparency edits, no recolors).
|
||||
- The logo background **must remain dark/black**.
|
||||
- Keep the existing page structure (nav → hero → welcome → about → features grid → CTA strip → footer).
|
||||
- Only adjust styling/theme + minor hero layout to place the logo cleanly.
|
||||
|
||||
## Source of Truth (what this doc is based on)
|
||||
The current homepage markup is Tailwind-based and includes these notable class patterns:
|
||||
- Root container: `min-h-screen bg-white`
|
||||
- Nav: `bg-white shadow-md sticky top-0`
|
||||
- Section backgrounds: `bg-white`, `bg-gray-100`, `bg-gray-50`
|
||||
- Feature cards: `bg-white p-6` + `text-gray-900` + `text-gray-600`
|
||||
- Footer: `bg-gray-900`
|
||||
- Primary color already matches **Rosso Mugello** (primary-500 = `#7A212A`)
|
||||
|
||||
This doc provides **exact class changes and snippets** to implement in the **React/Tailwind** source (not the built HTML).
|
||||
|
||||
---
|
||||
|
||||
## Assets
|
||||
|
||||
### Logo files (must remain unchanged)
|
||||
Place these in the frontend **public** folder so they can be used without bundler imports:
|
||||
|
||||
- `frontend/public/branding/motovaultpro-title.png`
|
||||
- `frontend/public/branding/motovaultpro-logo-only.png`
|
||||
|
||||
(If your repo structure differs, keep the same `/branding/...` URL path equivalent.)
|
||||
|
||||
Usage:
|
||||
- Hero: `/branding/motovaultpro-title.png`
|
||||
- Nav: `/branding/motovaultpro-logo-only.png`
|
||||
|
||||
---
|
||||
|
||||
## Design Tokens (from MVP color scheme)
|
||||
Use these colors (hex values) directly in Tailwind arbitrary values or via Tailwind config.
|
||||
- **Nero Daytona**: `#231F1C` (page base)
|
||||
- **Bianco Avus**: `#F2F3F6` (primary text on dark)
|
||||
- **Grigio Titanio**: `#A8B8C0` (secondary text on dark)
|
||||
- **Canna Di Fucile**: `#7E8792` (muted borders/icons)
|
||||
- **Rosso Mugello (Primary)**: `#7A212A` (primary CTA; already `primary-500`)
|
||||
|
||||
### Quick token aliases (recommended)
|
||||
If you can edit `tailwind.config.*`, add:
|
||||
- `nero: "#231F1C"`
|
||||
- `avus: "#F2F3F6"`
|
||||
- `titanio: "#A8B8C0"`
|
||||
- `canna: "#7E8792"`
|
||||
|
||||
If you prefer no config changes, use arbitrary values like `bg-[#231F1C]`.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Plan (Step-by-step)
|
||||
|
||||
### 0) Locate the homepage components
|
||||
In the repo, find the marketing homepage by searching for one of these:
|
||||
- `hero-carousel`
|
||||
- `slick-dots`
|
||||
- `MOTOVAULTPRO`
|
||||
- `Thank you for your interest in MotoVaultPro`
|
||||
- `What We Offer`
|
||||
- `min-h-screen bg-white`
|
||||
- `bg-gray-50` + `Our Features`
|
||||
|
||||
Common file names in React projects:
|
||||
- `LandingPage.tsx`, `HomePage.tsx`, `MarketingHome.tsx`, `PublicHome.tsx`
|
||||
- Components: `HeroCarousel.tsx`, `Navbar.tsx`, `FeaturesSection.tsx`
|
||||
|
||||
---
|
||||
|
||||
## 1) Root container: switch to dark base
|
||||
|
||||
**Before**
|
||||
```tsx
|
||||
<div className="min-h-screen bg-white">
|
||||
After
|
||||
|
||||
Always show details
|
||||
<div className="min-h-screen bg-[#231F1C] text-[#F2F3F6]">
|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
This makes all default text inherit Bianco Avus.
|
||||
|
||||
Any sections that used gray text must be updated (see below).
|
||||
|
||||
2) Nav: dark surface + logo-only
|
||||
Nav container
|
||||
|
||||
Before
|
||||
|
||||
Always show details
|
||||
<nav className="bg-white shadow-md sticky top-0 z-50">
|
||||
|
||||
|
||||
After
|
||||
|
||||
Always show details
|
||||
<nav className="sticky top-0 z-50 bg-[#231F1C]/90 backdrop-blur border-b border-white/10">
|
||||
|
||||
Brand area
|
||||
|
||||
Replace the text-only brand with logo-only + text.
|
||||
|
||||
Before
|
||||
|
||||
Always show details
|
||||
<h1 className="text-2xl font-bold text-primary-500">MotoVaultPro</h1>
|
||||
|
||||
|
||||
After
|
||||
|
||||
Always show details
|
||||
<a href="#home" className="flex items-center gap-3">
|
||||
<img
|
||||
src="/branding/motovaultpro-logo-only.png"
|
||||
alt="MotoVaultPro"
|
||||
className="h-8 w-auto"
|
||||
/>
|
||||
<span className="text-lg md:text-xl font-bold tracking-wide text-[#F2F3F6]">
|
||||
MotoVaultPro
|
||||
</span>
|
||||
</a>
|
||||
|
||||
Nav links (desktop)
|
||||
|
||||
Before
|
||||
|
||||
Always show details
|
||||
<a className="text-gray-700 hover:text-primary-500 transition-colors">Home</a>
|
||||
|
||||
|
||||
After
|
||||
|
||||
Always show details
|
||||
<a className="text-white/75 hover:text-white transition-colors">Home</a>
|
||||
|
||||
|
||||
Apply same to Features/About links.
|
||||
|
||||
Mobile menu button
|
||||
|
||||
Before
|
||||
|
||||
Always show details
|
||||
<button className="text-gray-700 hover:text-primary-500 ...">
|
||||
|
||||
|
||||
After
|
||||
|
||||
Always show details
|
||||
<button className="text-white/80 hover:text-white focus:outline-none">
|
||||
|
||||
Nav CTA buttons
|
||||
|
||||
Remove light hover backgrounds (e.g., hover:bg-primary-50) since that becomes bright on dark.
|
||||
|
||||
Sign Up (outline)
|
||||
Before
|
||||
|
||||
Always show details
|
||||
<button className="border-2 border-primary-500 text-primary-500 hover:bg-primary-50 ...">
|
||||
|
||||
|
||||
After
|
||||
|
||||
Always show details
|
||||
<button className="border border-primary-500/90 text-primary-500 hover:bg-primary-500/10 hover:border-primary-500 transition-colors duration-300 font-semibold py-2 px-6 rounded-lg">
|
||||
Sign Up
|
||||
</button>
|
||||
|
||||
|
||||
Login (filled)
|
||||
Keep as primary, optionally add shadow.
|
||||
|
||||
Always show details
|
||||
<button className="bg-primary-500 hover:bg-primary-700 text-white font-semibold py-2 px-6 rounded-lg transition-colors duration-300 shadow-lg shadow-black/30">
|
||||
Login
|
||||
</button>
|
||||
|
||||
3) Hero: place the full logo on a dark “brand plate”
|
||||
|
||||
Goal: the logo has a black rectangle background; we want it to feel embedded, not pasted.
|
||||
We do that by placing it on a slightly-transparent dark plate with a subtle border and blur.
|
||||
|
||||
Replace hero overlay content
|
||||
|
||||
Find the hero overlay container (currently shows “Welcome to” + big text + Learn More button).
|
||||
|
||||
Before (conceptually)
|
||||
|
||||
Always show details
|
||||
<p className="text-white ...">Welcome to</p>
|
||||
<h1 className="text-white ...">MOTOVAULTPRO</h1>
|
||||
<button className="bg-primary-500 ...">Learn More</button>
|
||||
|
||||
|
||||
After
|
||||
|
||||
Always show details
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center text-center px-4">
|
||||
<div className="rounded-2xl border border-white/10 bg-black/40 backdrop-blur-sm p-5 md:p-8 shadow-[0_20px_60px_rgba(0,0,0,0.65)]">
|
||||
<img
|
||||
src="/branding/motovaultpro-title.png"
|
||||
alt="MotoVaultPro — Precision Vehicle Management"
|
||||
className="w-[280px] md:w-[520px] lg:w-[620px] h-auto"
|
||||
loading="eager"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="mt-6 max-w-2xl text-white/85 text-base md:text-lg leading-relaxed">
|
||||
Maintenance, fuel/energy, documents, and reminders — organized per vehicle.
|
||||
</p>
|
||||
|
||||
<div className="mt-6 flex flex-col sm:flex-row gap-3">
|
||||
<button className="bg-primary-500 hover:bg-primary-700 text-white font-semibold py-3 px-8 rounded-lg transition-colors duration-300 shadow-lg shadow-black/30">
|
||||
Get Started
|
||||
</button>
|
||||
<a href="#features" className="inline-flex items-center justify-center rounded-lg border border-white/15 bg-white/5 hover:bg-white/10 text-white font-semibold py-3 px-8 transition-colors duration-300">
|
||||
View Features
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Keep existing hero gradient overlay
|
||||
|
||||
Your hero images already have:
|
||||
bg-gradient-to-b from-black/60 via-black/40 to-black/60
|
||||
Keep it; it improves readability.
|
||||
|
||||
Optional: reduce slick arrow intensity
|
||||
|
||||
If the slick arrows feel too “bright” on dark:
|
||||
|
||||
Reduce opacity and add hover.
|
||||
|
||||
Example CSS:
|
||||
|
||||
Always show details
|
||||
.hero-carousel .slick-prev:before,
|
||||
.hero-carousel .slick-next:before { opacity: 0.45; }
|
||||
.hero-carousel .slick-prev:hover:before,
|
||||
.hero-carousel .slick-next:hover:before { opacity: 0.8; }
|
||||
|
||||
4) Welcome section: dark panel
|
||||
|
||||
Before
|
||||
|
||||
Always show details
|
||||
<section className="py-16 px-4 md:px-8 bg-white">
|
||||
|
||||
|
||||
After
|
||||
|
||||
Always show details
|
||||
<section className="py-16 px-4 md:px-8 bg-[#1D1A18] border-t border-white/5">
|
||||
|
||||
|
||||
Text updates (do as class swaps; keep content if you want):
|
||||
|
||||
text-gray-900 → text-[#F2F3F6]
|
||||
|
||||
text-gray-600 → text-[#A8B8C0]
|
||||
|
||||
Example:
|
||||
|
||||
Always show details
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-[#F2F3F6] mb-6">...</h2>
|
||||
<p className="text-lg text-[#A8B8C0] leading-relaxed mb-8">...</p>
|
||||
|
||||
5) About section: dark base + keep the maroon feature block
|
||||
|
||||
Before
|
||||
|
||||
Always show details
|
||||
<section id="about" className="py-16 px-4 md:px-8 bg-gray-100">
|
||||
|
||||
|
||||
After
|
||||
|
||||
Always show details
|
||||
<section id="about" className="py-16 px-4 md:px-8 bg-[#231F1C] border-t border-white/5">
|
||||
|
||||
|
||||
Text swaps:
|
||||
|
||||
text-gray-900 → text-[#F2F3F6]
|
||||
|
||||
text-gray-600 → text-[#A8B8C0]
|
||||
|
||||
Trusted Platform block:
|
||||
|
||||
Keep bg-primary-500
|
||||
|
||||
Add subtle border to sharpen against dark background:
|
||||
|
||||
Always show details
|
||||
<div className="w-64 h-64 bg-primary-500 rounded-lg border border-white/10 ...">
|
||||
|
||||
6) Features section: dark background + convert white cards to “dark cards”
|
||||
Section background
|
||||
|
||||
Before
|
||||
|
||||
Always show details
|
||||
<section className="py-16 px-4 md:px-8 bg-gray-50">
|
||||
|
||||
|
||||
After
|
||||
|
||||
Always show details
|
||||
<section className="py-16 px-4 md:px-8 bg-[#1D1A18] border-t border-white/5">
|
||||
|
||||
Card container (most important change)
|
||||
|
||||
Find the card body container:
|
||||
Before
|
||||
|
||||
Always show details
|
||||
<div className="bg-white p-6">
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-2">...</h3>
|
||||
<p className="text-gray-600 leading-relaxed">...</p>
|
||||
</div>
|
||||
|
||||
|
||||
After
|
||||
|
||||
Always show details
|
||||
<div className="bg-white/5 border border-white/10 p-6">
|
||||
<h3 className="text-xl font-bold text-[#F2F3F6] mb-2">...</h3>
|
||||
<p className="text-[#A8B8C0] leading-relaxed">...</p>
|
||||
</div>
|
||||
|
||||
Card outer wrapper (shadow + hover)
|
||||
|
||||
Before
|
||||
|
||||
Always show details
|
||||
<div className="overflow-hidden rounded-lg shadow-lg hover:shadow-xl transition-shadow duration-300">
|
||||
|
||||
|
||||
After
|
||||
|
||||
Always show details
|
||||
<div className="overflow-hidden rounded-lg border border-white/10 bg-white/5 shadow-lg shadow-black/30 hover:border-white/20 hover:shadow-xl hover:shadow-black/40 transition-all duration-300">
|
||||
|
||||
|
||||
Note: With the new card body background already set, you may remove redundant bg-white/5 either on wrapper or body.
|
||||
Prefer wrapper bg-white/5 + body bg-transparent, OR wrapper bg-transparent + body bg-white/5. Don’t double-darken unless it looks better.
|
||||
|
||||
7) CTA strip: keep Rosso Mugello but make it richer on dark pages
|
||||
|
||||
Current
|
||||
|
||||
Always show details
|
||||
<section className="py-16 px-4 md:px-8 bg-primary-500 text-white">
|
||||
|
||||
|
||||
Recommended
|
||||
|
||||
Always show details
|
||||
<section className="py-16 px-4 md:px-8 text-white bg-[linear-gradient(90deg,#6A1C24_0%,#7A212A_50%,#6A1C24_100%)] border-t border-white/10">
|
||||
|
||||
|
||||
Button:
|
||||
Before
|
||||
|
||||
Always show details
|
||||
<button className="bg-white text-primary-500 hover:bg-gray-100 ...">Get Started</button>
|
||||
|
||||
|
||||
After
|
||||
|
||||
Always show details
|
||||
<button className="bg-white/95 text-primary-500 hover:bg-white font-semibold py-3 px-8 rounded-lg transition-colors duration-300 shadow-lg shadow-black/30">
|
||||
Get Started
|
||||
</button>
|
||||
|
||||
8) Footer: align to the new dark base
|
||||
|
||||
Before
|
||||
|
||||
Always show details
|
||||
<footer className="bg-gray-900 text-white py-8 px-4 md:px-8">
|
||||
<p className="text-gray-400">...</p>
|
||||
</footer>
|
||||
|
||||
|
||||
After
|
||||
|
||||
Always show details
|
||||
<footer className="bg-black text-white py-8 px-4 md:px-8 border-t border-white/10">
|
||||
<p className="text-white/50">© 2025 MotoVaultPro. All rights reserved.</p>
|
||||
</footer>
|
||||
|
||||
9) Remove or replace light-only Tailwind classes on homepage
|
||||
|
||||
On the homepage only, avoid these patterns because they create “light UI” artifacts on dark pages:
|
||||
|
||||
bg-white, bg-gray-50, bg-gray-100
|
||||
|
||||
text-gray-900, text-gray-700, text-gray-600
|
||||
|
||||
hover:bg-primary-50 (currently a very light blue in your built CSS)
|
||||
|
||||
Use instead:
|
||||
|
||||
dark surfaces: bg-[#231F1C], bg-[#1D1A18], bg-white/5
|
||||
|
||||
text: text-[#F2F3F6], text-[#A8B8C0], text-white/80
|
||||
|
||||
hover: hover:bg-white/10 or hover:bg-primary-500/10
|
||||
|
||||
Acceptance Criteria (QA checklist)
|
||||
|
||||
Homepage background is dark everywhere (no white sections).
|
||||
|
||||
Full logo in hero feels native (sits on a dark “brand plate” with border/blur).
|
||||
|
||||
Nav uses logo-only and remains readable on scroll.
|
||||
|
||||
Feature cards are dark, readable, and keep hover affordances.
|
||||
|
||||
No bright hover states (no primary-50 / light blue flashes).
|
||||
|
||||
Mobile: spacing remains consistent; CTAs are reachable; text contrast is good.
|
||||
|
||||
Suggested PR structure
|
||||
|
||||
feat(ui): dark theme for marketing homepage to match logo background
|
||||
|
||||
Include before/after screenshots at desktop + mobile widths.
|
||||
|
||||
"""
|
||||
|
||||
path = Path("/mnt/data/MVP-HOMEPAGE-DARK-THEME-SPEC.md")
|
||||
path.write_text(md, encoding="utf-8")
|
||||
str(path)
|
||||
193
docs/changes/ONBOARDING-FIX.md
Normal file
193
docs/changes/ONBOARDING-FIX.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# Fix New User Signup Wizard Flow
|
||||
|
||||
## Problem Summary
|
||||
The new user signup wizard is broken:
|
||||
1. After Auth0 callback, users go to `/garage` instead of `/verify-email`
|
||||
2. Users can access `/garage/*` without verified email
|
||||
3. Onboarding flow is bypassed entirely
|
||||
4. **New Requirement**: Block login completely at Auth0 for unverified users
|
||||
|
||||
## Root Causes
|
||||
1. **Auth0Provider.tsx:29** - `onRedirectCallback` defaults to `/garage` without checking verification
|
||||
2. **App.tsx:472-481** - Callback route just shows "Processing login..." with no routing logic
|
||||
3. **App.tsx:549+** - Protected routes have no email verification check
|
||||
4. **Auth0** - No rule/action blocking unverified users from logging in
|
||||
|
||||
---
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Auth0 Configuration (Manual Step)
|
||||
|
||||
**Auth0 Dashboard -> Actions -> Flows -> Login**
|
||||
|
||||
Create a Post Login Action to block unverified users:
|
||||
|
||||
```javascript
|
||||
exports.onExecutePostLogin = async (event, api) => {
|
||||
if (!event.user.email_verified) {
|
||||
api.access.deny('Please verify your email address before logging in. Check your inbox for a verification link.');
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
This ensures:
|
||||
- Unverified users cannot get a JWT
|
||||
- They see a clear error on the Auth0 login screen
|
||||
- They must click the verification link before logging in
|
||||
|
||||
### Phase 2: Update Signup Flow
|
||||
|
||||
**After signup, redirect to a "Check Your Email" page (not /verify-email)**
|
||||
|
||||
The new flow:
|
||||
1. User submits signup form
|
||||
2. Backend creates Auth0 user (unverified)
|
||||
3. Auth0 automatically sends verification email
|
||||
4. Frontend shows "Check Your Email" page with:
|
||||
- Message: "We've sent a verification link to your email"
|
||||
- Resend button (calls public resend endpoint)
|
||||
- "Back to Login" button
|
||||
5. User clicks email link -> Auth0 marks as verified
|
||||
6. User can now login -> goes to /onboarding
|
||||
|
||||
### Phase 3: Backend Changes
|
||||
|
||||
**File: `backend/src/features/auth/api/auth.routes.ts`**
|
||||
- Add `POST /api/auth/resend-verification-public` (no JWT required)
|
||||
- Takes email address, looks up user, resends verification
|
||||
|
||||
**File: `backend/src/features/auth/api/auth.controller.ts`**
|
||||
- Add `resendVerificationPublic` handler
|
||||
|
||||
**File: `backend/src/features/auth/domain/auth.service.ts`**
|
||||
- Add `resendVerificationByEmail` method
|
||||
|
||||
**File: `backend/src/features/auth/api/auth.routes.ts`**
|
||||
- Add `GET /api/auth/user-status` (JWT required)
|
||||
- Returns: `{ emailVerified, onboardingCompleted, email }`
|
||||
|
||||
**File: `backend/src/core/plugins/auth.plugin.ts`**
|
||||
- Add `/api/auth/user-status` to `VERIFICATION_EXEMPT_ROUTES`
|
||||
|
||||
### Phase 4: Create Callback Handler
|
||||
|
||||
**File: `frontend/src/features/auth/pages/CallbackPage.tsx`** (NEW)
|
||||
- Fetches user status after Auth0 callback
|
||||
- Routes based on status:
|
||||
- Not onboarded -> `/onboarding`
|
||||
- Onboarded -> `/garage` (or returnTo)
|
||||
- Note: Unverified users never reach this (blocked by Auth0)
|
||||
|
||||
**File: `frontend/src/features/auth/mobile/CallbackMobileScreen.tsx`** (NEW)
|
||||
- Mobile version
|
||||
|
||||
### Phase 5: Update Auth0Provider
|
||||
|
||||
**File: `frontend/src/core/auth/Auth0Provider.tsx`**
|
||||
|
||||
Update `onRedirectCallback` (line 27-31):
|
||||
|
||||
```typescript
|
||||
const onRedirectCallback = (appState?: { returnTo?: string }) => {
|
||||
navigate('/callback', {
|
||||
replace: true,
|
||||
state: { returnTo: appState?.returnTo || '/garage' }
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### Phase 6: Rename/Update Verify Email Page
|
||||
|
||||
**File: `frontend/src/features/auth/pages/VerifyEmailPage.tsx`**
|
||||
- Rename concept to "Check Your Email" page
|
||||
- Remove polling (user can't be authenticated)
|
||||
- Show static message + resend button (calls public endpoint)
|
||||
- Add "Back to Login" button
|
||||
|
||||
**File: `frontend/src/features/auth/mobile/VerifyEmailMobileScreen.tsx`**
|
||||
- Same changes for mobile
|
||||
|
||||
### Phase 7: Update App.tsx Routing
|
||||
|
||||
**File: `frontend/src/App.tsx`**
|
||||
|
||||
1. Replace callback handling (lines 472-481) with CallbackPage
|
||||
2. Add onboarding guard after authentication check
|
||||
3. Remove email verification check from frontend (Auth0 handles it)
|
||||
|
||||
```typescript
|
||||
// After isAuthenticated check:
|
||||
// Fetch onboarding status
|
||||
// If not onboarded -> /onboarding
|
||||
// Otherwise -> proceed to /garage
|
||||
```
|
||||
|
||||
### Phase 8: Create Supporting Files
|
||||
|
||||
**File: `frontend/src/core/auth/useUserStatus.ts`** (NEW)
|
||||
- Hook for fetching user status
|
||||
|
||||
**File: `frontend/src/features/auth/api/auth.api.ts`**
|
||||
- Add `getUserStatus()`
|
||||
- Add `resendVerificationPublic(email)` (no auth)
|
||||
|
||||
---
|
||||
|
||||
## Files to Modify
|
||||
|
||||
### Auth0 (Manual Configuration)
|
||||
- Create Post Login Action to block unverified users
|
||||
|
||||
### Backend
|
||||
- `backend/src/features/auth/api/auth.routes.ts` - Add endpoints
|
||||
- `backend/src/features/auth/api/auth.controller.ts` - Add handlers
|
||||
- `backend/src/features/auth/domain/auth.service.ts` - Add methods
|
||||
- `backend/src/core/plugins/auth.plugin.ts` - Update exempt routes
|
||||
|
||||
### Frontend
|
||||
- `frontend/src/core/auth/Auth0Provider.tsx` - Fix onRedirectCallback
|
||||
- `frontend/src/App.tsx` - Add route guards and callback handler
|
||||
- `frontend/src/features/auth/pages/CallbackPage.tsx` - NEW
|
||||
- `frontend/src/features/auth/mobile/CallbackMobileScreen.tsx` - NEW
|
||||
- `frontend/src/features/auth/pages/VerifyEmailPage.tsx` - Update to static page
|
||||
- `frontend/src/features/auth/mobile/VerifyEmailMobileScreen.tsx` - Update
|
||||
- `frontend/src/core/auth/useUserStatus.ts` - NEW
|
||||
- `frontend/src/features/auth/api/auth.api.ts` - Add functions
|
||||
|
||||
---
|
||||
|
||||
## New User Flow (After Fix)
|
||||
|
||||
```
|
||||
1. Signup form submission
|
||||
2. -> POST /api/auth/signup (creates unverified Auth0 user)
|
||||
3. -> Navigate to /verify-email (static "Check Your Email" page)
|
||||
4. User clicks verification link in email
|
||||
5. -> Auth0 marks user as verified
|
||||
6. User clicks "Login" on /verify-email page
|
||||
7. -> Auth0 login succeeds (user is now verified)
|
||||
8. -> /callback page fetches status
|
||||
9. -> Not onboarded? -> /onboarding
|
||||
10. -> Complete onboarding -> /garage
|
||||
```
|
||||
|
||||
## Returning User Flow
|
||||
|
||||
```
|
||||
1. Login attempt (unverified) -> Auth0 blocks with error message
|
||||
2. Login attempt (verified, not onboarded) -> /callback -> /onboarding
|
||||
3. Login attempt (verified, onboarded) -> /callback -> /garage
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
- [ ] Auth0 Action blocks unverified login with clear error
|
||||
- [ ] Signup -> check-email page -> verify via email -> login works
|
||||
- [ ] Resend verification from check-email page works
|
||||
- [ ] Verified user (no onboarding) -> onboarding wizard
|
||||
- [ ] Verified + onboarded user -> direct to garage
|
||||
- [ ] Direct URL access to /garage -> requires login
|
||||
- [ ] All flows work on mobile
|
||||
- [ ] All flows work on desktop
|
||||
Reference in New Issue
Block a user