This guide documents the implementation approach, setup instructions, and testing procedures for each feature.
- Tech Stack
- TODO 1: Infrastructure Setup
- TODO 2: Prisma Schema
- TODO 3: Supabase Clients
- TODO 4: Google Authentication
- TODO 5: Auth Middleware
- TODO 6: Onboarding Modal
- TODO 7: Onboarding Steps
- TODO 8: Goals API
- TODO 9: Dashboard Layout
- TODO 10: Task Management
| Layer | Technology | Purpose |
|---|---|---|
| Framework | Next.js 16 | App Router, Server Components, API Routes |
| Language | TypeScript | Type safety across the stack |
| Database | PostgreSQL (Supabase) | Managed database with realtime capabilities |
| ORM | Prisma | Type-safe database queries and migrations |
| Auth | Supabase Auth | Google OAuth, session management |
| Styling | Tailwind CSS 4 | Utility-first CSS |
| Animations | Framer Motion | Smooth, declarative animations |
Set up the foundational dependencies and environment configuration for database (Prisma) and authentication (Supabase). This establishes the core infrastructure that all other features will depend on.
- Install Supabase client libraries for both browser and server-side usage
- Install Prisma ORM (v7) for type-safe database operations
- Initialize Prisma with PostgreSQL configuration using the new Prisma 7 config format
- Create environment variable templates for easy onboarding
- Add npm scripts for common database operations
{
"dependencies": {
"@prisma/client": "^7.2.0",
"@supabase/ssr": "^0.8.0",
"@supabase/supabase-js": "^2.89.0",
"dotenv": "^17.x"
},
"devDependencies": {
"prisma": "^7.2.0"
}
}| File | Purpose |
|---|---|
prisma/schema.prisma |
Database schema with User, Goal, MeetingSchedule models and enums |
prisma.config.ts |
Prisma 7 configuration file for database connections |
env.example |
Environment variable template |
package.json |
Added database scripts |
Prisma 7 introduced a breaking change - database URLs are no longer specified in schema.prisma. Instead, they are configured in prisma.config.ts:
// prisma.config.ts
export default defineConfig({
earlyAccess: true,
schema: path.join(__dirname, "prisma", "schema.prisma"),
migrate: { url: process.env.DIRECT_URL! },
studio: { url: process.env.DIRECT_URL! },
});The runtime DATABASE_URL is passed when instantiating the Prisma client (handled in TODO 3).
| Script | Command | Purpose |
|---|---|---|
db:generate |
prisma generate |
Generate Prisma Client |
db:push |
prisma db push |
Push schema to database (dev) |
db:migrate |
prisma migrate dev |
Create and run migrations |
db:studio |
prisma studio |
Open Prisma Studio GUI |
db:reset |
prisma migrate reset |
Reset database |
postinstall |
prisma generate |
Auto-generate client on install |
- Go to supabase.com and create a new project
- Wait for the project to be provisioned (~2 minutes)
- Navigate to Settings > API in Supabase Dashboard
- Copy the Project URL →
NEXT_PUBLIC_SUPABASE_URL - Copy the anon/public key →
NEXT_PUBLIC_SUPABASE_ANON_KEY
- Navigate to Settings > Database in Supabase Dashboard
- Scroll to Connection string section
- Select Transaction pooler → Copy for
DATABASE_URL(used at runtime with connection pooling) - Select Session pooler or Direct → Copy for
DIRECT_URL(used for migrations)
# Copy the example environment file
cp env.example .env.local
# Edit .env.local with your Supabase values
# Replace placeholders with actual credentialsnpm run db:generatenpm run db:push# Check Prisma is installed correctly
npx prisma --version
# Expected: prisma : 7.2.0
# Validate schema syntax
npx prisma validate
# Expected: The schema at prisma\schema.prisma is valid 🚀
# Generate Prisma client
npm run db:generate
# Expected: ✔ Generated Prisma Client (v7.2.0)# Open Prisma Studio to verify connection
npm run db:studio
# This opens a browser at localhost:5555 if connection is successful
# Push schema to database
npm run db:push
# This creates the tables in your Supabase database| Issue | Solution |
|---|---|
prisma generate fails |
Run npm install first, ensure node_modules exists |
| Schema validation error | Check prisma.config.ts exists and has valid syntax |
| Connection refused | Verify Supabase project is active, check credentials in .env.local |
| SSL/TLS error | Connection strings from Supabase should include SSL by default |
DIRECT_URL not found |
Ensure .env.local has DIRECT_URL set for migrations |
| Pooler timeout | Use DIRECT_URL for migrations, DATABASE_URL (pooler) for runtime queries |
Define the complete database schema for RunBook with models for users, goals, and meeting schedules. The schema is designed to support the AI accountability meeting system with proper relationships and future extensibility.
- Create User model linked to Supabase Auth via
supabaseId - Create Goal model with type (daily/weekly/monthly) and priority levels
- Create MeetingSchedule model for storing user's preferred meeting times
- Define enums for type-safe goal categorization
- Add proper indexes and cascading deletes for data integrity
┌─────────────────────────────────────────────────────────────┐
│ USER │
├─────────────────────────────────────────────────────────────┤
│ id String @id (cuid) │
│ supabaseId String @unique (links to Supabase Auth) │
│ email String @unique │
│ name String? (set during onboarding) │
│ avatarUrl String? (from Google profile) │
│ onboardingDone Boolean (tracks onboarding completion) │
│ createdAt DateTime │
│ updatedAt DateTime │
├─────────────────────────────────────────────────────────────┤
│ Relations: goals[], meetingSchedule? │
└─────────────────────────────────────────────────────────────┘
│
│ 1:N
▼
┌─────────────────────────────────────────────────────────────┐
│ GOAL │
├─────────────────────────────────────────────────────────────┤
│ id String @id (cuid) │
│ title String (goal description) │
│ description String? (optional details) │
│ type GoalType (DAILY/WEEKLY/MONTHLY) │
│ priority Priority (DEFAULT/LOW/HIGH/URGENT) │
│ completed Boolean (completion status) │
│ order Int (for drag-drop ordering) │
│ userId String (foreign key) │
│ createdAt DateTime │
│ updatedAt DateTime │
├─────────────────────────────────────────────────────────────┤
│ Index: userId (for efficient user queries) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ MEETING_SCHEDULE │
├─────────────────────────────────────────────────────────────┤
│ id String @id (cuid) │
│ userId String @unique (1:1 with User) │
│ time String (e.g., "09:00" in 24h format) │
│ timezone String (e.g., "America/New_York") │
│ frequency String (daily/weekly) │
│ isActive Boolean (pause/resume meetings) │
│ createdAt DateTime │
│ updatedAt DateTime │
└─────────────────────────────────────────────────────────────┘
| Enum | Values | Purpose |
|---|---|---|
GoalType |
DAILY, WEEKLY, MONTHLY | Categorize goals by timeframe |
Priority |
DEFAULT, LOW, HIGH, URGENT | Task prioritization for AI context |
| Decision | Rationale |
|---|---|
supabaseId separate from id |
Allows internal ID management while linking to Supabase Auth |
onboardingDone flag |
Controls whether to show onboarding modal on login |
order field on Goal |
Enables drag-and-drop reordering in the UI |
isActive on MeetingSchedule |
Users can pause meetings without deleting schedule |
| Cascade deletes | Deleting a user removes all their goals and schedules |
@@map() annotations |
Snake_case table names for PostgreSQL convention |
| File | Changes |
|---|---|
prisma/schema.prisma |
Added User, Goal, MeetingSchedule models and enums |
# Validate schema syntax
npx prisma validate
# View schema in Prisma Studio
npm run db:studio
# Check tables in Supabase Dashboard
# Navigate to Table Editor - you should see:
# - users
# - goals
# - meeting_schedulesThe schema is designed to easily accommodate future features:
- Meeting model: For storing meeting transcripts and summaries
- Pattern model: For AI-detected behavioral patterns
- Notification preferences: Can be added to User model
- Team features: Can add Organization and TeamMember models
Create properly configured Supabase clients for different execution contexts (browser, server, middleware) and a Prisma singleton for database operations. This separation ensures proper cookie handling and prevents multiple client instances.
- Create browser client for client-side auth operations
- Create server client with cookie management for SSR
- Create middleware client for session refresh during navigation
- Create Prisma singleton to prevent hot-reload issues in development
- Provide helper functions for common auth operations
┌─────────────────────────────────────────────────────────────────┐
│ CLIENT LAYER │
├─────────────────────────────────────────────────────────────────┤
│ Browser Client (lib/supabase/client.ts) │
│ └─ Used in: Client Components ("use client") │
│ └─ Purpose: Auth operations (signIn, signOut, onAuthChange) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ SERVER LAYER │
├─────────────────────────────────────────────────────────────────┤
│ Server Client (lib/supabase/server.ts) │
│ └─ Used in: Server Components, Route Handlers, Server Actions │
│ └─ Purpose: Get user/session with cookie management │
├─────────────────────────────────────────────────────────────────┤
│ Middleware Client (lib/supabase/middleware.ts) │
│ └─ Used in: middleware.ts │
│ └─ Purpose: Refresh sessions, protect routes │
├─────────────────────────────────────────────────────────────────┤
│ Prisma Client (lib/prisma.ts) │
│ └─ Used in: API Routes, Server Actions │
│ └─ Purpose: All database queries (CRUD operations) │
└─────────────────────────────────────────────────────────────────┘
| File | Purpose |
|---|---|
lib/supabase/client.ts |
Browser client for client-side auth |
lib/supabase/server.ts |
Server client with getUser() and getSession() helpers |
lib/supabase/middleware.ts |
Middleware client for route protection |
lib/supabase/index.ts |
Re-exports for convenient imports |
lib/prisma.ts |
Prisma singleton with connection pooling |
"use client";
import { createClient } from "@/lib/supabase/client";
function LoginButton() {
const supabase = createClient();
const handleLogin = async () => {
await supabase.auth.signInWithOAuth({
provider: "google",
options: { redirectTo: `${location.origin}/auth/callback` },
});
};
return <button onClick={handleLogin}>Sign in with Google</button>;
}import { getUser } from "@/lib/supabase/server";
async function Dashboard() {
const user = await getUser();
if (!user) redirect("/login");
return <div>Welcome, {user.email}</div>;
}import { prisma } from "@/lib/prisma";
import { getUser } from "@/lib/supabase/server";
export async function GET() {
const user = await getUser();
if (!user) return Response.json({ error: "Unauthorized" }, { status: 401 });
const goals = await prisma.goal.findMany({
where: { user: { supabaseId: user.id } },
});
return Response.json(goals);
}Prisma 7 requires passing the database URL at runtime:
// lib/prisma.ts
new PrismaClient({
datasourceUrl: process.env.DATABASE_URL,
log:
process.env.NODE_ENV === "development"
? ["query", "error", "warn"]
: ["error"],
});# Verify TypeScript compilation
npx tsc --noEmit
# Test Prisma client generation
npm run db:generate
# Test in development (check console for Prisma logs)
npm run dev- Start dev server:
npm run dev - Check browser console for Supabase initialization
- Check terminal for Prisma query logs (in development mode)
| Variable | Used By | Purpose |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
All Supabase clients | Supabase project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
All Supabase clients | Public anonymous key |
DATABASE_URL |
Prisma | PostgreSQL connection (pooled) |
Implement Google OAuth authentication using Supabase Auth. Users can only sign in with Google - no email/password option. On first sign-in, a user record is automatically created in our database.
- Create a beautiful login page matching the existing design system
- Build a reusable Google Sign-In button with loading states
- Create OAuth callback route to handle Google's redirect
- Sync Supabase Auth user with our Prisma User model
- Add sign-out functionality
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Login Page │────▶│ Supabase │────▶│ Google │────▶│ Callback │
│ │ │ Auth │ │ OAuth │ │ Route │
└─────────────┘ └─────────────┘ └─────────────┘ └──────┬──────┘
│
┌─────────────┐ ┌─────────────┐ │
│ Dashboard │◀────│ Create/Get │◀────────────┘
│ │ │ DB User │
└─────────────┘ └─────────────┘
| File | Purpose |
|---|---|
app/login/page.tsx |
Beautiful login page with gradient background |
app/auth/callback/route.ts |
OAuth callback handler, creates DB user |
components/auth/GoogleSignInButton.tsx |
Animated Google sign-in button |
components/auth/SignOutButton.tsx |
Sign-out button with variants |
components/auth/index.ts |
Component exports |
lib/auth.ts |
Auth utilities: getDbUser(), requireAuth(), needsOnboarding() |
import { GoogleSignInButton } from "@/components/auth";
// Basic usage
<GoogleSignInButton />
// With custom redirect
<GoogleSignInButton redirectTo="/auth/callback?next=/onboarding" />import { SignOutButton } from "@/components/auth";
// Full button with icon and text
<SignOutButton />
// Icon only
<SignOutButton variant="icon" />
// Text only
<SignOutButton variant="text" />import { getDbUser, requireAuth, needsOnboarding } from "@/lib/auth";
// Get user (returns null if not authenticated)
const user = await getDbUser();
// Require auth (redirects to /login if not authenticated)
const user = await requireAuth();
// Check if onboarding needed
const needsSetup = await needsOnboarding();- Go to Supabase Dashboard > Authentication > Providers
- Find Google and enable it
- You'll need Google OAuth credentials
- Go to Google Cloud Console
- Create a new project or select existing
- Navigate to APIs & Services > Credentials
- Click Create Credentials > OAuth client ID
- Select Web application
- Add authorized redirect URI:
https://<your-project-ref>.supabase.co/auth/v1/callback - Copy Client ID and Client Secret
- Back in Supabase Dashboard > Authentication > Providers > Google
- Paste the Client ID and Client Secret
- Save
- Go to Authentication > URL Configuration
- Add your site URL to Site URL:
http://localhost:3000 - Add to Redirect URLs:
http://localhost:3000/auth/callbackhttps://your-domain.com/auth/callback(for production)
# Start the dev server
npm run dev
# Visit login page
open http://localhost:3000/login
# Click "Continue with Google"
# You should be redirected to Google
# After auth, you'll land on /dashboard# Open Prisma Studio
npm run db:studio
# Check the "users" table for your new user| Issue | Solution |
|---|---|
| "OAuth error" | Check Google credentials in Supabase |
| "Redirect URI mismatch" | Add the exact callback URL to Google Console |
| User not created in DB | Check server logs, verify Prisma connection |
| "Invalid login" | Clear cookies, try incognito mode |
Create Next.js middleware to protect routes and manage authentication flow. The middleware intercepts requests before they reach the page, checking auth status and redirecting as needed.
- Create middleware that checks Supabase session on every request
- Define protected routes (require auth) and auth routes (redirect if logged in)
- Handle redirects based on authentication state
- Create protected layout wrapper for additional server-side auth check
┌─────────────────────────────────────────────────────────────────┐
│ INCOMING REQUEST │
└─────────────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────┐
│ Check Supabase User │
└─────────────┬───────────┘
│
┌─────────────┴───────────┐
│ │
▼ ▼
┌───────────────┐ ┌───────────────┐
│ Logged In │ │ Not Logged │
└───────┬───────┘ └───────┬───────┘
│ │
┌───────────┴───────────┐ ┌─────────┴─────────┐
│ │ │ │
▼ ▼ ▼ ▼
Protected Route? Auth Route? Protected Route? Auth Route?
│ │ │ │
▼ ▼ ▼ ▼
Allow Redirect to Redirect to Allow
/dashboard /login
| File | Purpose |
|---|---|
middleware.ts |
Next.js middleware for route protection |
app/(protected)/layout.tsx |
Server-side auth check wrapper |
app/(protected)/dashboard/page.tsx |
Dashboard placeholder page |
app/(protected)/meeting/page.tsx |
Meeting "Coming Soon" page |
| Route Type | Routes | Behavior |
|---|---|---|
| Protected | /dashboard, /meeting, /settings, /profile |
Redirect to /login if not authenticated |
| Auth | /login |
Redirect to /dashboard if authenticated |
| Public | /, /auth/callback, /terms, /privacy |
Accessible to everyone |
// middleware.ts
export const config = {
matcher: [
// Match all routes except static files and images
"/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
],
};// app/(protected)/layout.tsx
import { requireAuth } from "@/lib/auth";
export default async function ProtectedLayout({ children }) {
// Redirects to /login if not authenticated
await requireAuth();
return <>{children}</>;
}"use client";
import { createClient } from "@/lib/supabase/client";
import { useEffect, useState } from "react";
function useUser() {
const [user, setUser] = useState(null);
const supabase = createClient();
useEffect(() => {
supabase.auth.getUser().then(({ data }) => setUser(data.user));
}, []);
return user;
}# Start dev server
npm run dev
# Test protected route (should redirect to /login)
open http://localhost:3000/dashboard
# Login, then try /login (should redirect to /dashboard)
open http://localhost:3000/login- Middleware runs on Edge - Fast, but can't access database directly
- Double protection - Middleware + Layout auth check for defense in depth
- Session refresh - Middleware client automatically refreshes expired sessions
- Redirect preservation - Original URL saved in query param for post-login redirect
Build a beautiful, animated modal system for onboarding new users. The modal features a blurred backdrop, step-by-step navigation, and smooth Framer Motion animations that create a pleasing, app-like experience.
- Create reusable modal wrapper with backdrop blur
- Build progress indicator (dots) for step visualization
- Implement slide animations between steps
- Create state management for onboarding data
- Design render props pattern for flexible step content
┌────────────────────────────────────────────────────────────┐
│ OnboardingModal │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ OnboardingProgress (dots) │ │
│ └──────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Step Content │ │
│ │ (slides left/right) │ │
│ │ │ │
│ │ ┌────────────┐ ┌────────────┐ │ │
│ │ │ Back │ │ Next │ │ │
│ │ └────────────┘ └────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘
| File | Purpose |
|---|---|
components/onboarding/OnboardingModal.tsx |
Main modal with state and navigation |
components/onboarding/OnboardingProgress.tsx |
Step indicator dots |
components/onboarding/animations.ts |
Framer Motion variants |
components/onboarding/index.ts |
Component exports |
interface OnboardingData {
name: string; // User's preferred name
goals: { title: string; id: string }[]; // Up to 3 daily goals
meetingChoice: "try" | "schedule" | null; // Meeting preference
scheduledTime: string | null; // Scheduled meeting time
}| Animation | Description |
|---|---|
backdropVariants |
Fade in/out with blur |
modalVariants |
Scale + fade with spring easing |
stepVariants |
Horizontal slide between steps |
staggerContainer |
Staggered reveal for lists |
staggerItem |
Individual item animation |
import { OnboardingModal, OnboardingData } from "@/components/onboarding";
function Dashboard() {
const [showOnboarding, setShowOnboarding] = useState(true);
const handleComplete = async (data: OnboardingData) => {
// Save data to database
await saveOnboarding(data);
setShowOnboarding(false);
};
return (
<>
<OnboardingModal isOpen={showOnboarding} onComplete={handleComplete}>
{(props) => <WelcomeStep {...props} />}
{(props) => <GoalsStep {...props} />}
{(props) => <MeetingChoiceStep {...props} />}
</OnboardingModal>
{/* Dashboard content */}
</>
);
}Each step receives these props:
type OnboardingStepProps = {
data: OnboardingData; // Current form data
updateData: (updates) => void; // Update data
nextStep: () => void; // Go to next step
prevStep: () => void; // Go to previous step
onComplete: () => void; // Complete onboarding
isFirstStep: boolean; // Hide back button
isLastStep: boolean; // Show complete button
};- Backdrop blur: Semi-transparent overlay with
backdrop-blur-md - Rounded modal:
rounded-3xlfor modern feel - Progress dots: Animated color and scale changes
- Slide transitions: Content slides horizontally between steps
- Custom easing:
[0.16, 1, 0.3, 1]for smooth, natural motion
Build a three-step onboarding flow with smooth animations that captures user name, daily goals, and meeting preferences. The implementation includes a beautiful scrollable time picker inspired by mobile alarm apps, full dark/light mode support, and uses only the website's theme colors.
- Create WelcomeStep with name input and auto-focus
- Create GoalsStep allowing users to add up to 3 daily goals with animated list
- Create MeetingChoiceStep with two options: Try Meeting Now or Schedule Daily
- Build TimePickerModal with scroll-wheel interface for time selection
- Integrate with API routes for persisting onboarding data
- Check
onboardingDoneflag to conditionally show modal
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Step 1 │ │ Step 2 │ │ Step 3 │
│ Welcome │────▶│ Set Goals │────▶│ Meeting Choice │
│ (Name Input) │ │ (Up to 3) │ │ │
└──────────────────┘ └──────────────────┘ └────────┬─────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ │
┌────────────┐ ┌────────────┐ │
│ Try Meeting│ │ Schedule │ │
│ Now │ │ Daily │ │
└─────┬──────┘ └─────┬──────┘ │
│ │ │
│ ▼ │
│ ┌────────────┐ │
│ │Time Picker │ │
│ │ Modal │ │
│ └─────┬──────┘ │
│ │ │
▼ ▼ │
┌──────────────────────────────────────┘
│ Dashboard
└──────────────────────────────────────
| File | Purpose |
|---|---|
components/onboarding/steps/WelcomeStep.tsx |
Name input with greeting |
components/onboarding/steps/GoalsStep.tsx |
Add/remove up to 3 goals |
components/onboarding/steps/MeetingChoiceStep.tsx |
Try now or schedule option |
components/onboarding/TimePickerModal.tsx |
Scrollable time picker like alarm app |
components/onboarding/steps/index.ts |
Step exports |
app/api/profile/route.ts |
GET/PATCH user profile |
app/api/goals/route.ts |
GET/POST goals (batch create supported) |
app/api/goals/[id]/route.ts |
GET/PATCH/DELETE individual goal |
app/api/meeting-schedule/route.ts |
CRUD for meeting schedule |
- Auto-focuses input after animation
- Enter key submits
- Skip option sets name to "Friend"
- Animated underline on focus
- Maximum 3 goals with visual indicator dots
- Animated list with stagger effects
- Goals can be removed with animated exit
- Numbered badges for each goal
- Two card options with selection state
- Checkmark animation on selection
- Opens TimePickerModal for scheduling
- Scroll-wheel interface (Hours, Minutes, AM/PM)
- Snap scrolling for precise selection
- Gradient fade at top/bottom edges
- Hidden scrollbars for clean UI
- 5-minute intervals for minutes
- Real-time display of selected time
| Animation | Description |
|---|---|
staggerContainer |
Parent container for staggered children |
staggerItem |
Individual items with fade + slide up |
inputFocusVariants |
Focus ring animation for inputs |
cardSelectVariants |
Scale + lift on card selection |
fadeInUp |
Standard fade in with upward motion |
scaleIn |
Scale from 0.8 to 1 with fade |
scrollSnapVariants |
Active/inactive states for time picker |
| Method | Purpose |
|---|---|
| GET | Fetch current user with goals and schedule |
| PATCH | Update name, avatarUrl, or onboardingDone |
| Method | Purpose |
|---|---|
| GET | Fetch all goals (optional type filter) |
| POST | Create single goal or batch create |
Batch Create Example (for onboarding):
POST /api/goals
{
"goals": [
{ "title": "Exercise for 30 minutes" },
{ "title": "Read 20 pages" },
{ "title": "Meditate" }
]
}| Method | Purpose |
|---|---|
| GET | Fetch user's meeting schedule |
| POST | Create or update schedule |
| PATCH | Update schedule properties |
| DELETE | Remove schedule |
All components use the website's theme colors:
- Primary accent:
#156d95(teal/blue) - Gradient:
from-[#156d95] to-[#1a7faa] - Background: Uses CSS variables (
bg-zinc-*,dark:bg-zinc-*) - Font: Figtree for all text
Added to globals.css:
/* Hide scrollbar utility for time picker */
.hide-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
.hide-scrollbar::-webkit-scrollbar {
display: none;
}The dashboard page now:
- Fetches user profile on mount
- Checks
onboardingDoneflag - Shows OnboardingModal if
onboardingDone === false - Saves all onboarding data via API calls on completion
- Redirects to
/meetingif user chose "Try Meeting Now"
npm run dev- Sign in with Google (creates new user with
onboardingDone: false) - Onboarding modal should appear automatically
- Step 1: Enter name or skip
- Step 2: Add 1-3 goals, test adding/removing
- Step 3: Choose either option:
- "Try Meeting Now" → Completes and redirects to
/meeting - "Schedule Daily" → Opens time picker
- "Try Meeting Now" → Completes and redirects to
- Scroll through hours, minutes, and AM/PM
- Click values to snap to them
- Verify display updates in real-time
- Confirm time selection
# Open Prisma Studio
npm run db:studioCheck:
userstable:nameupdated,onboardingDone: truegoalstable: Goals created with correct user IDmeeting_schedulestable: Schedule created if scheduled
- Toggle system dark mode or use browser dev tools
- Verify all components render correctly
- Check contrast and readability
# Get profile
curl http://localhost:3000/api/profile
# Update profile
curl -X PATCH http://localhost:3000/api/profile \
-H "Content-Type: application/json" \
-d '{"name": "Test User"}'
# Get goals
curl http://localhost:3000/api/goals
# Create meeting schedule
curl -X POST http://localhost:3000/api/meeting-schedule \
-H "Content-Type: application/json" \
-d '{"time": "09:00", "timezone": "America/New_York"}'| Issue | Solution |
|---|---|
| Onboarding not showing | Check onboardingDone in database, should be false |
| API returns 401 | Ensure user is authenticated, check cookies |
| Time picker not scrolling | Verify hide-scrollbar CSS is loaded |
| Goals not saving | Check API response, verify Prisma connection |
| Modal not animating | Ensure Framer Motion is installed |
Create comprehensive REST API routes for goals CRUD operations with proper authentication, authorization, and error handling. The API supports both individual goal operations and batch creation (for onboarding).
- Create
/api/goalsroute for listing all goals and creating new ones - Create
/api/goals/[id]route for individual goal operations (GET, PATCH, DELETE) - Implement authentication checks using Supabase session
- Add ownership verification to prevent unauthorized access
- Support filtering by goal type (DAILY, WEEKLY, MONTHLY)
- Handle ordering for drag-and-drop reordering support
| File | Purpose |
|---|---|
app/api/goals/route.ts |
List goals (GET) and create goals (POST) |
app/api/goals/[id]/route.ts |
Get, update, delete individual goals |
Fetch all goals for the authenticated user.
Query Parameters:
type(optional): Filter by goal type (DAILY,WEEKLY,MONTHLY)
Response:
[
{
"id": "clx...",
"title": "Exercise for 30 minutes",
"description": null,
"type": "DAILY",
"priority": "DEFAULT",
"completed": false,
"order": 0,
"userId": "clx...",
"createdAt": "2026-01-06T...",
"updatedAt": "2026-01-06T..."
}
]Create a new goal or batch create goals.
Single Goal Request:
{
"title": "Read for 20 minutes",
"description": "Optional description",
"type": "DAILY",
"priority": "HIGH"
}Batch Create Request (for onboarding):
{
"goals": [{ "title": "Goal 1" }, { "title": "Goal 2" }, { "title": "Goal 3" }]
}Response: Created goal(s) with status 201
Fetch a specific goal by ID.
Response: Single goal object or 404 if not found
Update a specific goal.
Request Body (all fields optional):
{
"title": "Updated title",
"description": "Updated description",
"type": "WEEKLY",
"priority": "URGENT",
"completed": true,
"order": 2
}Response: Updated goal object
Delete a specific goal.
Response:
{ "success": true }All endpoints:
- Verify Supabase session exists
- Look up user by
supabaseId - For individual goal operations, verify the goal belongs to the user
| Status | Error | Description |
|---|---|---|
| 401 | Unauthorized | No valid session |
| 404 | User not found | User doesn't exist in DB |
| 404 | Goal not found | Goal doesn't exist |
| 400 | Title is required | Missing required field |
| 500 | Internal server error | Unexpected error |
# Get all goals
curl http://localhost:3000/api/goals
# Get daily goals only
curl "http://localhost:3000/api/goals?type=DAILY"
# Create a goal
curl -X POST http://localhost:3000/api/goals \
-H "Content-Type: application/json" \
-d '{"title": "New Goal", "priority": "HIGH"}'
# Update a goal
curl -X PATCH http://localhost:3000/api/goals/[id] \
-H "Content-Type: application/json" \
-d '{"completed": true}'
# Delete a goal
curl -X DELETE http://localhost:3000/api/goals/[id]- Login to the app
- Open DevTools → Network tab
- Navigate to dashboard (triggers goals fetch)
- Verify GET /api/goals returns 200
- Complete onboarding with goals to test POST
- Goals are ordered by the
orderfield for drag-and-drop support - Batch creation automatically assigns incrementing order values
- All mutations update the
updatedAttimestamp automatically (Prisma) - Cascading deletes ensure goals are removed when user is deleted
Coming soon
Coming soon