|
1 | | -# 🦅 Admin Guidance · Obsidian Operations |
2 | | -**Access:** `https://dinacosmetic.store/admin` |
3 | | - |
4 | | -## 🎭 Admin Role Activation |
5 | | -1. Sign up on the storefront with your email. |
6 | | -2. Go to **Supabase Dashboard > Table Editor > profiles**. |
7 | | -3. Find your user ID and change `role` from `user` to `admin`. |
8 | | -4. Refresh the admin portal. |
9 | | - |
10 | | -## 📦 Merchandise Management |
11 | | -- **Dashboard:** Track total luxury sales, recent volume, and low stock alerts. |
12 | | -- **Stock:** Inventory is **Atomic**. If you sell 1 unit, the system locks the database row to prevent overselling. |
13 | | -- **Pricing:** Validated server-side during checkout. |
14 | | - |
15 | | -## 🎨 Visual Curation (Frontend) |
16 | | -- Navigate to **Admin > Customization**. |
17 | | -- Change the **Hero Masterpiece** title and subtitle. |
18 | | -- Updates apply instantly to the homepage via Next.js server actions. |
19 | | - |
20 | | -## 📜 Order Ledger |
21 | | -- View all paid sessions synced from Stripe. |
22 | | -- Click a Reference Number to enter the fulfillment workflow. |
23 | | - |
24 | | -## ⚠️ Security Protocols |
25 | | -- Never share your `SUPABASE_SERVICE_ROLE_KEY`. It allows full DB bypass. |
26 | | -- The `/admin` route is protected by both Next.js middleware and Supabase RLS policies (Double-lock). |
| 1 | +# DINA COSMETIC — Admin & Deployment Guide |
| 2 | + |
| 3 | +> **The Obsidian Palace** · LMXEngine · Built on Next.js 15 + Supabase + Stripe |
| 4 | +
|
| 5 | +--- |
| 6 | + |
| 7 | +## Table of Contents |
| 8 | + |
| 9 | +1. [What Was Built](#1-what-was-built) |
| 10 | +2. [Admin Portal — Features & How to Use](#2-admin-portal--features--how-to-use) |
| 11 | +3. [Database Setup](#3-database-setup) |
| 12 | +4. [Environment Variables](#4-environment-variables) |
| 13 | +5. [Deploy to Vercel](#5-deploy-to-vercel) |
| 14 | +6. [Post-Deploy Checklist](#6-post-deploy-checklist) |
| 15 | +7. [Storefront Routes](#7-storefront-routes) |
| 16 | +8. [Architecture Notes](#8-architecture-notes) |
| 17 | + |
| 18 | +--- |
| 19 | + |
| 20 | +## 1. What Was Built |
| 21 | + |
| 22 | +### Storefront (`/`) |
| 23 | +| Route | Description | |
| 24 | +|---|---| |
| 25 | +| `/` | Homepage with hero, featured products, collections teaser | |
| 26 | +| `/shop` | Full product catalogue with real-time Supabase updates | |
| 27 | +| `/collections` | Curated vault index — links dynamically to DB categories | |
| 28 | +| `/collections/[slug]` | Individual collection page — filtered by category slug | |
| 29 | +| `/product/[slug]` | Single product detail with variants & add-to-cart | |
| 30 | +| `/account` | Customer order history and profile | |
| 31 | +| `/login` | Supabase Auth email/password + magic link | |
| 32 | +| `/cart` | Shopping bag drawer (client component) | |
| 33 | +| `/checkout` | Stripe Checkout session redirect | |
| 34 | +| `/checkout/success` | Post-payment confirmation page | |
| 35 | +| `/about` | Brand story page | |
| 36 | +| `/contact` | Contact form (Resend) | |
| 37 | + |
| 38 | +### Admin Portal (`/admin`) |
| 39 | +| Route | Description | |
| 40 | +|---|---| |
| 41 | +| `/admin` | **Command Center** — live stats dashboard | |
| 42 | +| `/admin/products` | Product list with inline actions | |
| 43 | +| `/admin/products/new` | Create product with image upload | |
| 44 | +| `/admin/products/[id]` | Edit product details, images, stock | |
| 45 | +| `/admin/orders` | Order fulfilment table with status controls | |
| 46 | +| `/admin/categories` | Category management | |
| 47 | +| `/admin/users` | Customer directory | |
| 48 | +| `/admin/settings` | Site settings and frontend content editor | |
| 49 | + |
| 50 | +### API Routes (`/app/api`) |
| 51 | +| Route | Description | |
| 52 | +|---|---| |
| 53 | +| `/api/checkout` | Creates Stripe Checkout session (server-side) | |
| 54 | +| `/api/webhooks/stripe` | Receives and verifies Stripe events | |
| 55 | + |
| 56 | +--- |
| 57 | + |
| 58 | +## 2. Admin Portal — Features & How to Use |
| 59 | + |
| 60 | +### Accessing Admin |
| 61 | + |
| 62 | +1. Log in at `/login` with your admin email |
| 63 | +2. Your `profiles.role` must be `'admin'` in Supabase |
| 64 | +3. Navigate to `/admin` — non-admin users are redirected to `/` |
| 65 | + |
| 66 | +> **To make an account admin:** In Supabase → Table Editor → `profiles` → find your row → set `role` to `admin` |
| 67 | +
|
| 68 | +--- |
| 69 | + |
| 70 | +### Command Center Dashboard (`/admin`) |
| 71 | + |
| 72 | +**Live stats refreshed on every page load:** |
| 73 | +- **Total Revenue** — sum of all `paid` orders |
| 74 | +- **Orders** — total order count |
| 75 | +- **Low Stock** — products with `inventory < 5` |
| 76 | +- **Customers** — total registered users |
| 77 | + |
| 78 | +**Quick Actions grid:** |
| 79 | +- → New Product |
| 80 | +- → View Orders |
| 81 | +- → Manage Categories |
| 82 | +- → Site Settings |
| 83 | + |
| 84 | +**Low Stock Alerts panel** — top 5 products by lowest inventory, click to edit |
| 85 | + |
| 86 | +**Recent Transactions table** — last 8 orders with status badges and amounts |
| 87 | + |
| 88 | +--- |
| 89 | + |
| 90 | +### Product Management (`/admin/products`) |
| 91 | + |
| 92 | +#### Creating a Product |
| 93 | +1. Click **"New Product"** or go to `/admin/products/new` |
| 94 | +2. Fill in: |
| 95 | + - **Name** (required) |
| 96 | + - **Description** |
| 97 | + - **Price** (in USD, e.g. `49.99`) |
| 98 | + - **Stock / Inventory** quantity |
| 99 | + - **Category** — dropdown populated from DB |
| 100 | + - **Images** — drag & drop upload to Supabase Storage |
| 101 | + - **Active** toggle — only active products appear in the store |
| 102 | +3. Click **Save** — page revalidates instantly |
| 103 | + |
| 104 | +#### Editing a Product |
| 105 | +1. Click any product row → `/admin/products/[id]` |
| 106 | +2. Same form, pre-filled with existing data |
| 107 | +3. Upload new images or remove old ones |
| 108 | +4. Save — changes reflect on storefront immediately (ISR revalidation) |
| 109 | + |
| 110 | +#### Deleting a Product |
| 111 | +- Delete button on the product list row |
| 112 | +- Product is removed from DB and storefront |
| 113 | + |
| 114 | +--- |
| 115 | + |
| 116 | +### Order Fulfilment (`/admin/orders`) |
| 117 | + |
| 118 | +**Status values:** `pending` → `paid` → `shipped` → `delivered` | `cancelled` | `refunded` |
| 119 | + |
| 120 | +**Workflow:** |
| 121 | +1. Order arrives as `pending` when checkout starts |
| 122 | +2. Stripe webhook (`/api/webhooks/stripe`) marks it `paid` automatically |
| 123 | +3. Admin manually marks `shipped` after generating a label |
| 124 | +4. Shippo label generation available via the order detail action button |
| 125 | +5. Tracking number is stored and visible to the customer in `/account` |
| 126 | + |
| 127 | +**Important:** Orders are **immutable after payment** per security rules. Status can advance but items cannot be changed. |
| 128 | + |
| 129 | +--- |
| 130 | + |
| 131 | +### Category Management (`/admin/categories`) |
| 132 | + |
| 133 | +- Add categories with `name`, `slug`, `description`, `image_url` |
| 134 | +- Slug must be URL-safe (e.g. `skincare`, `lip-collection`) |
| 135 | +- Categories appear in shop filter and `/collections/[slug]` pages |
| 136 | +- Deleting a category does **not** delete products (FK set to NULL) |
| 137 | + |
| 138 | +--- |
| 139 | + |
| 140 | +### Site Settings (`/admin/settings`) |
| 141 | + |
| 142 | +Edits content stored in `site_settings` and `frontend_content` tables: |
| 143 | +- Hero headline and subtitle |
| 144 | +- Store name and tagline |
| 145 | +- **Kill Switch** — set `store_enabled` to `false` to show a maintenance page |
| 146 | + |
| 147 | +--- |
| 148 | + |
| 149 | +## 3. Database Setup |
| 150 | + |
| 151 | +### Run in Supabase SQL Editor |
| 152 | + |
| 153 | +> **Supabase → SQL Editor → New Query → Paste → Run** |
| 154 | +
|
| 155 | +**Use `DATABASE_FINAL.sql`** — this is the single source of truth. It: |
| 156 | +- Adds all missing columns to existing tables |
| 157 | +- Creates `variants`, `site_settings`, `frontend_content` tables |
| 158 | +- Sets exactly **4 RLS policies per table** (SELECT / INSERT / UPDATE / DELETE) |
| 159 | +- Uses `(select auth.uid())` pattern — zero performance warnings |
| 160 | +- Creates all indexes, triggers, and the `admin_sales_stats` view |
| 161 | +- Seeds default settings |
| 162 | + |
| 163 | +### Make Yourself Admin |
| 164 | + |
| 165 | +After running the SQL and creating your account: |
| 166 | +```sql |
| 167 | +UPDATE public.profiles |
| 168 | +SET role = 'admin' |
| 169 | +WHERE email = 'your@email.com'; |
| 170 | +``` |
| 171 | + |
| 172 | +### Storage Bucket |
| 173 | + |
| 174 | +The SQL creates the `product-images` bucket automatically. Configure storage policies via: |
| 175 | +> **Supabase → Storage → product-images → Policies** |
| 176 | +
|
| 177 | +Add: |
| 178 | +- `SELECT`: `true` (public read) |
| 179 | +- `INSERT/UPDATE/DELETE`: `(select role from profiles where id = auth.uid()) = 'admin'` |
| 180 | + |
| 181 | +--- |
| 182 | + |
| 183 | +## 4. Environment Variables |
| 184 | + |
| 185 | +Create `.env.local` (never commit this file): |
| 186 | + |
| 187 | +```env |
| 188 | +# Supabase |
| 189 | +NEXT_PUBLIC_SUPABASE_URL=https://zsahskxejgbrvfhobfyp.supabase.co |
| 190 | +NEXT_PUBLIC_SUPABASE_ANON_KEY=sb_publishable_... |
| 191 | +SUPABASE_SERVICE_ROLE_KEY=eyJ... |
| 192 | +
|
| 193 | +# Stripe |
| 194 | +STRIPE_SECRET_KEY=sk_live_... |
| 195 | +NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_... |
| 196 | +STRIPE_WEBHOOK_SECRET=whsec_... |
| 197 | +
|
| 198 | +# Email (Resend) |
| 199 | +RESEND_API_KEY=re_... |
| 200 | +
|
| 201 | +# Shipping (Shippo) |
| 202 | +SHIPPO_API_KEY=shippo_live_... |
| 203 | +
|
| 204 | +# App |
| 205 | +NEXT_PUBLIC_SITE_URL=https://dinacosmetic.store |
| 206 | +``` |
| 207 | + |
| 208 | +### Vercel Environment Variables |
| 209 | + |
| 210 | +Add all of the above to: |
| 211 | +> **Vercel → Project → Settings → Environment Variables** |
| 212 | +
|
| 213 | +Set for **Production**, **Preview**, and **Development**. |
| 214 | + |
| 215 | +--- |
| 216 | + |
| 217 | +## 5. Deploy to Vercel |
| 218 | + |
| 219 | +### First Deployment |
| 220 | + |
| 221 | +```bash |
| 222 | +# Install Vercel CLI (if not installed) |
| 223 | +npm i -g vercel |
| 224 | + |
| 225 | +# From project root |
| 226 | +vercel --prod |
| 227 | +``` |
| 228 | + |
| 229 | +Or connect via GitHub: |
| 230 | +1. Go to [vercel.com/new](https://vercel.com/new) |
| 231 | +2. Import `lead-matrix/LMXEngine` |
| 232 | +3. Framework: **Next.js** (auto-detected) |
| 233 | +4. Add all environment variables |
| 234 | +5. Deploy |
| 235 | + |
| 236 | +### Subsequent Deploys |
| 237 | + |
| 238 | +```bash |
| 239 | +git push origin main |
| 240 | +``` |
| 241 | +Vercel auto-deploys on every push to `main`. |
| 242 | + |
| 243 | +### Stripe Webhook (Production) |
| 244 | + |
| 245 | +After deploying, register your webhook: |
| 246 | +1. [Stripe Dashboard → Webhooks](https://dashboard.stripe.com/webhooks) |
| 247 | +2. Add endpoint: `https://dinacosmetic.store/api/webhooks/stripe` |
| 248 | +3. Events to listen for: |
| 249 | + - `checkout.session.completed` |
| 250 | + - `payment_intent.succeeded` |
| 251 | + - `payment_intent.payment_failed` |
| 252 | +4. Copy the **Signing secret** → set as `STRIPE_WEBHOOK_SECRET` in Vercel |
| 253 | + |
| 254 | +### Supabase Auth Redirect URLs |
| 255 | + |
| 256 | +In **Supabase → Authentication → URL Configuration**: |
| 257 | +- **Site URL:** `https://dinacosmetic.store` |
| 258 | +- **Redirect URLs:** |
| 259 | + ``` |
| 260 | + https://dinacosmetic.store/** |
| 261 | + http://localhost:3000/** |
| 262 | + ``` |
| 263 | + |
| 264 | +--- |
| 265 | + |
| 266 | +## 6. Post-Deploy Checklist |
| 267 | + |
| 268 | +``` |
| 269 | +□ DATABASE_FINAL.sql run successfully in Supabase SQL Editor |
| 270 | +□ Admin account role set to 'admin' in profiles table |
| 271 | +□ All environment variables added to Vercel |
| 272 | +□ Stripe webhook registered with correct endpoint |
| 273 | +□ Supabase Auth redirect URLs configured |
| 274 | +□ product-images storage bucket is public |
| 275 | +□ Test: Create a product via /admin/products/new |
| 276 | +□ Test: Place a test order with Stripe test card 4242 4242 4242 4242 |
| 277 | +□ Test: Verify webhook fires and order status changes to 'paid' |
| 278 | +□ Test: /admin Command Center shows correct stats |
| 279 | +□ Test: /collections loads categories from DB |
| 280 | +□ Test: /collections/[slug] filters products correctly |
| 281 | +``` |
| 282 | + |
| 283 | +--- |
| 284 | + |
| 285 | +## 7. Storefront Routes |
| 286 | + |
| 287 | +### Customer Flow |
| 288 | +``` |
| 289 | +/ → /shop → /product/[slug] → (add to cart) → /checkout → /checkout/success |
| 290 | + → (Stripe webhook fires) |
| 291 | + → Order marked 'paid' |
| 292 | + → Confirmation email sent |
| 293 | +``` |
| 294 | + |
| 295 | +### Auth Flow |
| 296 | +``` |
| 297 | +/login → Supabase Auth → redirect to /account (or previous page) |
| 298 | +/account → view orders, update profile |
| 299 | +``` |
| 300 | + |
| 301 | +--- |
| 302 | + |
| 303 | +## 8. Architecture Notes |
| 304 | + |
| 305 | +### Security |
| 306 | +- All pricing calculated **server-side** — client never submits prices |
| 307 | +- Stripe webhook verified with `STRIPE_WEBHOOK_SECRET` signature |
| 308 | +- Admin routes protected by middleware + server-side role check |
| 309 | +- `SUPABASE_SERVICE_ROLE_KEY` only used in server components and API routes |
| 310 | +- RLS enabled on all tables with zero policy conflicts |
| 311 | + |
| 312 | +### Performance |
| 313 | +- Product lists use `revalidate = 60` (ISR — fresh every 60s) |
| 314 | +- Admin dashboard is fully dynamic (no cache) |
| 315 | +- All `auth.uid()` calls use `(select auth.uid())` pattern (single eval per query) |
| 316 | +- Exactly 4 RLS policies per table (no redundant evaluation) |
| 317 | +- Indexed columns: `is_active`, `category_id`, `inventory`, `slug`, `status`, `created_at` |
| 318 | + |
| 319 | +### Real-time |
| 320 | +- `ProductGrid` subscribes to Supabase Realtime — product updates appear instantly |
| 321 | +- Admin orders page refreshes on status change |
| 322 | + |
| 323 | +### Image Uploads |
| 324 | +- Stored in Supabase Storage `product-images` bucket (public CDN) |
| 325 | +- Uploaded via drag-and-drop in `ImageUpload` component |
| 326 | +- `next/image` used throughout with `sizes` attribute for responsive loading |
| 327 | + |
| 328 | +--- |
| 329 | + |
| 330 | +*Last updated: 2026-02-26 · LMXEngine / DINA COSMETIC* |
0 commit comments