π― Status: Ready for testing | β±οΈ Setup Time: 3-5 minutes | π§ Dependencies: Node.js + Docker
A progressive, A/B tested subscription cancellation flow built with Next.js, TypeScript, and Supabase. Features pixel-perfect responsive design, deterministic A/B testing, and robust security implementation.
π Demo Link : Short Demo
Prerequisites: Node.js, Docker Desktop
Prerequisites:
- Node.js (v18+)
- Docker Desktop (Install here)
# 1. Clone and install
git clone <repo-url>
cd cancel-flow-task-main
npm install
# 2. Setup database and start server (one command does everything)
npm run setup
# Note: If prompted during setup, press ENTER to accept defaults
# 3. Setup automatically starts the dev server
# Visit: http://localhost:3000/cancel
π― Test URL: http://localhost:3000/cancel
- Complete any cancellation flow normally
- Try to cancel again (
/cancel
page) - Should see enhanced "Cancellation Request Received" message
Command | Purpose |
---|---|
npm run setup |
One-click setup: Install deps + database + start dev server |
npm run dev |
Start development server (only if not already running) |
npm run db:fresh |
Clean reset - Use when testing from scratch |
npm run db:cleanup |
Fresh state - Reset to 3 active subscriptions, 0 cancellations |
npm run db:start |
Start Supabase only |
npm run db:stop |
Stop Supabase only |
Note:
npm run setup
automatically starts the development server. No need to runnpm run dev
separately.
- Next.js 15 + App Router: Server-side rendering, built-in API routes, excellent TypeScript support
- Supabase (PostgreSQL): Instant PostgreSQL with built-in RLS, real-time capabilities, Docker-based local development
- Tailwind CSS: Rapid, consistent styling for pixel-perfect Figma implementation
- TypeScript: End-to-end type safety, reducing runtime errors
- Step-based Modal System: Each cancellation step is isolated component
- Centralized State Management: React Context for global flow state
- Local UI State: Components handle their own UI interactions
- Clear Separation: Business logic separated from presentation
CancellationContext (Global State)
βββ CancellationModal (Orchestrator)
βββ ProgressBar (Visual feedback)
βββ Steps/
βββ JobCheckStep
βββ DownsellStep
βββ SurveyStep
βββ ReasonStep
βββ SuccessStep
-- Users can only access their own data
CREATE POLICY user_cancellations ON cancellations
FOR ALL USING (auth.uid() = user_id);
-- Subscription access restricted to owners
CREATE POLICY user_subscriptions ON subscriptions
FOR ALL USING (auth.uid() = user_id);
- Frontend: React controlled components with real-time validation
- API Layer: Request body validation and sanitization
- Database: Check constraints and foreign key relationships
- XSS Protection: All user inputs sanitized, no dangerouslySetInnerHTML
- Next.js built-in CSRF protection via SameSite cookies
- Origin header verification on API routes
// Cryptographically secure randomization
const variant = crypto.randomInt(2) === 0 ? 'A' : 'B';
// Persistent storage - same user = same variant
await supabase
.from('cancellations')
.insert({ downsell_variant: variant })
Key Features:
- β Persistence: Variant stored in database on first assignment
- β Consistency: Same user always receives same variant
- β No Re-randomization: Database check prevents variant switching
- Variant A: Skips downsell β Direct to survey
- Variant B: Shows $10 discount offer ($25β$15, $29β$19)
How to Test Both Variants:
# Reset for new test session
npm run db:fresh
# Visit /cancel - you'll be randomly assigned A or B
# To test the other variant, reset database and visit again
- Select "I found a job" β Should skip downsell entirely
- Continue to feedback step
- Verify no downsell tracking in database
- Select "I'm still job searching"
- Should go directly to survey (no downsell)
- Continue through reason selection
- Select "I'm still job searching"
- Should see downsell offer ($25β$15 or $29β$19)
- Test both "Accept" and "Decline" paths
- Verify
downsell_shown=true
andaccepted_downsell
tracking
- Complete any cancellation flow normally
- Try to cancel again (
/cancel
page) - Should see enhanced "Cancellation Request Received" message
- Desktop: Icon + title in one line, responsive image, full-width button
- Mobile: Compact layout, single action button
- Navigate to cancellation reasons step (via flow or direct)
- Select "Other" as cancellation reason
- Should see: Textarea appears with "Please tell us about your reason for cancelling*"
- Test validation: Enter <25 characters, try to continue β see error
- Test success: Enter β₯25 characters β character count turns green, form submits
- Verify database: Check
cancellations.feedback
contains "Other" text
-- Check user's cancellation record
SELECT * FROM cancellations WHERE user_id = 'test-user-1';
-- Verify subscription status
SELECT * FROM subscriptions WHERE user_id = 'test-user-1';
Downsell shown only when:
- User assigned Variant B
- User indicates "still job searching"
- No previous downsell acceptance recorded
npm run db:cleanup
: Remove duplicates, clean orphaned recordsnpm run db:fresh
: Complete reset for clean testing- Conflict Resolution:
ON CONFLICT (user_id, status) DO NOTHING
User Action β Context State β API Route β Database
β β β β
UI Update β Component β Response β Supabase
Critical Data Points Captured:
downsell_variant
(A/B assignment)job_status
(found job vs still searching)downsell_shown
(actual display tracking)accepted_downsell
(conversion tracking)reason
(cancellation reason)feedback
(user comments)
- β Pixel-perfect responsive design matching Figma
- β Deterministic A/B testing with persistence
- β Complete data capture (survey, feedback, job status)
- β Secure database operations with RLS
- β Input validation and sanitization
- β Comprehensive error handling
- β Mock user system for testing
- Image Optimization: Next.js Image component with automatic WebP conversion
- Lazy Loading: Step components load on-demand
- Turbopack: Faster development builds (
--turbopack
flag) - Database Indexing: Optimized queries on frequently accessed fields
# Error: "Docker is not installed or not running!"
# Solution: Install Docker Desktop and make sure it's running
- Download Docker Desktop from docker.com
- Install and start Docker Desktop
- Wait for Docker to fully start (green icon in system tray)
- Run
npm run setup
again
# Error: "cannot read config in ... open supabase/config.toml: no such file or directory"
# OR: "unknown flag: --no-prompt"
# Solution: Supabase initialization failed
- Manual initialization:
npx supabase init
(press ENTER for defaults) - Verify creation: Check if
supabase/config.toml
exists - Check permissions - Make sure you can write to the project directory
- Run setup again:
npm run setup
# Error: "ERROR: relation 'subscriptions' does not exist"
# Solution: Seeding attempted before schema creation
- Reset database:
npx supabase db reset
- Run setup again:
npm run setup
- If persists:
npx supabase stop && npm run setup
Note: The setup script now prevents auto-seeding conflicts, but existing setups may need reset.
# Error: "supabaseUrl is required" or "Unexpected token '<'"
# Solution: Environment variables not loaded properly
- Check .env.local exists:
ls -la .env.local
- Manually create if missing:
echo "NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321 NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0 SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU" > .env.local
- Restart dev server: Stop server (Ctrl+C) and run
npm run dev
# Error: Node version issues
# Solution: Use Node.js v18 or higher
- Check version:
node --version
- If < v18, download from nodejs.org
- Run
npm run setup
again
# Error: "Port 3000 is already in use"
# Solution: Kill existing processes or use different port
- Kill existing:
npx kill-port 3000
- Or restart:
npm run db:fresh
# Check API health
curl http://localhost:3000/api/cancel
# Should return: {"price":2500,"subscription_id":"uuid","status":"active"}
# Clean reset everything
npm run db:fresh
# Or clean just duplicates
npm run db:cleanup
- Check Docker is running: Look for Docker whale icon in system tray
- Check ports: Make sure 3000, 54321, 54322 are available
- Check Node version: Must be v18+
- Try fresh setup:
npm run db:fresh
API includes detailed logging:
βΉοΈ [API]
- Informationβ οΈ [API]
- Warnings (like multiple subscriptions)β [API]
- Errors
π Ready to test! The application provides a robust, secure cancellation flow with comprehensive A/B testing and data persistence capabilities.