forked from hazem/yslootahrobotics
- Implemented Prisma schema with models for AdminUser, AppSettings, and Snapshot. - Created seed script to initialize the database with an admin user and JWT secret. - Developed admin login page with form handling and error management. - Added API routes for admin login, logout, change password, and JWT verification. - Integrated Stripe for payment intent management in admin orders. - Established middleware for protecting admin routes with JWT authentication. - Created Zustand stores for managing persona and snapshot states.
117 lines
6.0 KiB
Markdown
117 lines
6.0 KiB
Markdown
# Plan: Admin Dashboard Auth + CRUD + Enhancements
|
|
|
|
## TL;DR
|
|
Add password authentication to the admin dashboard, enable full CRUD for personas/pricing, and add useful admin features like order analytics and configurator settings management.
|
|
|
|
## Phase 1: Admin Authentication (Database-backed with Prisma + SQLite)
|
|
|
|
### Steps
|
|
1. **Setup Prisma + SQLite**
|
|
- Install `prisma` and `@prisma/client`
|
|
- Create `prisma/schema.prisma` with SQLite provider
|
|
- Models: `AdminUser` (id, username, passwordHash, createdAt), `AppSettings` (key, value)
|
|
- Run `npx prisma db push` to create the database
|
|
- Create `src/lib/prisma.ts` — singleton Prisma client
|
|
|
|
2. **Create seed script** `prisma/seed.ts`
|
|
- Creates default admin user with bcrypt-hashed password
|
|
- Generates and stores JWT secret in `AppSettings` table
|
|
- Run with `npx prisma db seed`
|
|
|
|
3. **Create admin auth API routes**
|
|
- `src/app/api/admin/login/route.ts` — POST, accepts `{ username, password }`, verifies bcrypt hash from DB, returns JWT in httpOnly cookie (JWT secret from DB)
|
|
- `src/app/api/admin/verify/route.ts` — GET, checks JWT cookie validity
|
|
- `src/app/api/admin/logout/route.ts` — POST, clears auth cookie
|
|
- `src/app/api/admin/change-password/route.ts` — POST, accepts `{ currentPassword, newPassword }`, updates hash in DB
|
|
|
|
4. **Create admin middleware** `src/middleware.ts`
|
|
- Protects all `/admin/*` routes (except `/admin/login/`)
|
|
- Checks for valid JWT cookie, redirects to `/admin/login/` if missing/invalid
|
|
- Note: middleware can't use Prisma directly (edge runtime), so JWT secret needs to be in env OR verify via API call
|
|
|
|
5. **Create admin login page** `src/app/admin/login/page.tsx`
|
|
- Username + password form (styled to match existing admin design)
|
|
- Calls login API, redirects to `/admin/` on success
|
|
- Shows error on wrong credentials
|
|
|
|
6. **Add JWT_SECRET to .env.local** (only this one — password is in DB)
|
|
- Alternative: store JWT secret in DB and load at startup into a module-level cache
|
|
|
|
### Dependencies to install
|
|
- `prisma` (dev), `@prisma/client`, `bcryptjs`, `@types/bcryptjs` (dev), `jose`
|
|
|
|
## Phase 2: Full CRUD for Pricing Items + Personas
|
|
|
|
### Steps (depends on Phase 1)
|
|
6. **Add/Remove pricing items in admin** — Update `src/app/admin/page.tsx`
|
|
- "Add Item" button with fields: id (auto-slug from label), label, price
|
|
- Delete button per row (with confirmation)
|
|
- Update `usePricingStore` to support `addItem()` and `removeItem()` actions
|
|
|
|
7. **Persona management section** — New section in admin page
|
|
- List current personas with edit capability (label, description, colors)
|
|
- Add new persona form
|
|
- Delete persona button
|
|
- Create `usePersonaStore` or extend `usePricingStore` to manage persona definitions
|
|
- Sync persona list with ConfigPanel's `PERSONA_OPTIONS` (currently hardcoded)
|
|
|
|
## Phase 3: Additional Admin Features
|
|
|
|
### Steps (parallel with Phase 2)
|
|
8. **Orders dashboard section** — New section in admin page
|
|
- Show orders from Stripe API (list recent payments)
|
|
- Display: order ID, customer email, amount, status, date
|
|
- New API route `src/app/api/admin/orders/route.ts` to fetch from Stripe
|
|
|
|
9. **Analytics overview cards** at top of admin page
|
|
- Total revenue (from Stripe)
|
|
- Number of orders
|
|
- Most popular persona
|
|
- Most popular color
|
|
|
|
10. **Configurator settings** section
|
|
- Edit default color (`#96a2b6`)
|
|
- Toggle available color options
|
|
- Set min/max price boundaries
|
|
|
|
11. **Logout button** in admin header
|
|
|
|
## Relevant Files
|
|
- `src/app/admin/page.tsx` — Main admin dashboard (add CRUD UI, analytics)
|
|
- `src/app/admin/login/page.tsx` — New login page
|
|
- `src/app/api/admin/login/route.ts` — New auth API
|
|
- `src/app/api/admin/verify/route.ts` — New verify API
|
|
- `src/app/api/admin/orders/route.ts` — New orders API
|
|
- `src/middleware.ts` — New, protects admin routes
|
|
- `src/lib/prisma.ts` — New, Prisma client singleton
|
|
- `prisma/schema.prisma` — New, database schema (AdminUser, AppSettings)
|
|
- `prisma/seed.ts` — New, seeds default admin user
|
|
- `src/store/usePricingStore.ts` — Add `addItem()`, `removeItem()` actions
|
|
- `src/components/ConfigPanel.tsx` — Persona options currently hardcoded (PERSONA_OPTIONS const)
|
|
- `.env.local` — Add ADMIN_PASSWORD, ADMIN_JWT_SECRET
|
|
|
|
## Verification
|
|
1. Visit `/admin/` without login → redirected to `/admin/login/`
|
|
2. Enter wrong password → error message shown
|
|
3. Enter correct password → redirected to `/admin/`
|
|
4. Add a pricing item → appears in configurator pricing breakdown
|
|
5. Remove a pricing item → disappears from pricing
|
|
6. Edit persona → reflected in ConfigPanel
|
|
7. Orders section shows real Stripe payment data
|
|
8. Refresh admin page → still logged in (cookie persists)
|
|
9. Run `npx vitest run` → all existing tests pass
|
|
|
|
## Decisions
|
|
- **Database: Prisma + SQLite** — file-based, no external server needed, real ORM
|
|
- Admin password stored as bcrypt hash in DB (not in env vars)
|
|
- JWT secret stored in AppSettings table in DB
|
|
- JWT in httpOnly cookie — secure, no localStorage tokens
|
|
- jose library for JWT — lightweight, edge-compatible (works in Next.js middleware)
|
|
- Personas need to move from hardcoded array to store-driven — breaking change in ConfigPanel
|
|
- Stripe orders fetched via Stripe API directly — no local DB needed for orders
|
|
|
|
## Further Considerations
|
|
1. **Persona storage**: Currently hardcoded in ConfigPanel. Moving to a store means ConfigPanel reads from store dynamically. This is required for admin CRUD to work. **Recommended: create persona store with localStorage persistence (same pattern as pricing store)**
|
|
2. **Stripe orders vs local orders**: Currently orders only exist client-side. To show in admin, we fetch from Stripe's payment intents list. **Recommended: use Stripe API directly, no local DB needed**
|
|
3. **Multi-admin support**: Currently single password. If needed later, can upgrade to NextAuth with credentials provider. **Recommended: start simple, upgrade if needed**
|