feat: initial commit — storefront, convex backend, and shared packages
Completes the first milestone of The Pet Loft ecommerce platform: - apps/storefront: full customer-facing Next.js app with HeroUI (cart, checkout, orders, wishlist, product detail, shop, search, auth) - convex/: serverless backend with schema, queries, mutations, actions, HTTP routes, Stripe/Shippo integrations, and co-located tests - packages/types, packages/utils, packages/convex: shared workspace packages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
153
CLAUDE.md
Normal file
153
CLAUDE.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
**The Pet Loft** — a full-stack ecommerce platform for pet supplies. Turborepo monorepo with two Next.js apps, a shared Convex backend, and shared packages.
|
||||
|
||||
## Development Commands
|
||||
|
||||
Two processes must run simultaneously for full-stack development:
|
||||
|
||||
```bash
|
||||
# Terminal 1 — Convex backend (must run first)
|
||||
npx convex dev
|
||||
|
||||
# Terminal 2 — Next.js apps
|
||||
npm run dev # Both apps in parallel
|
||||
npm run dev:storefront # Storefront only (port 3000)
|
||||
npm run dev:admin # Admin only (port 3001)
|
||||
```
|
||||
|
||||
```bash
|
||||
npm run type-check # TypeScript check across all workspaces
|
||||
npm run lint # ESLint via Turbo
|
||||
npm run test # Vitest watch mode (edge-runtime)
|
||||
npm run test:once # Run all tests once
|
||||
npm run test:coverage # Coverage report
|
||||
npm run build # Build both apps
|
||||
```
|
||||
|
||||
To run a single test file: `npx vitest run convex/carts.test.ts`
|
||||
|
||||
## Architecture
|
||||
|
||||
### Monorepo Layout
|
||||
|
||||
```
|
||||
apps/storefront/ # Customer store — Next.js + HeroUI (port 3000)
|
||||
apps/admin/ # Staff dashboard — Next.js + ShadCN (port 3001)
|
||||
convex/ # Serverless backend: schema, functions, HTTP routes, tests
|
||||
packages/types/ # Shared TypeScript interfaces (Product, Order, User…)
|
||||
packages/utils/ # Shared helpers: formatPrice, slugify, formatDate
|
||||
packages/convex/ # ConvexClientProvider (Clerk + Convex integration)
|
||||
```
|
||||
|
||||
### Backend: Convex
|
||||
|
||||
All backend logic lives in `convex/`. The schema defines 11 tables:
|
||||
|
||||
- **users** — Clerk-synced; roles: `customer`, `admin`, `super_admin`
|
||||
- **products** / **productImages** / **productVariants** — catalog with SKUs
|
||||
- **categories** — hierarchical (parent + topCategory slugs)
|
||||
- **carts** — guest (`sessionId`) or authenticated (`userId`), 30-day expiry
|
||||
- **orders** / **orderItems** — price/address snapshots at order time
|
||||
- **addresses** — shipping/billing with validation flag
|
||||
- **reviews** / **wishlists** — community features
|
||||
|
||||
Business logic is extracted into `convex/model/*.ts` helpers and reused across public functions.
|
||||
|
||||
### Auth Flow
|
||||
|
||||
1. Clerk handles UI + JWT. `ConvexProviderWithClerk` (from `@repo/convex`) passes the JWT to Convex.
|
||||
2. `convex/auth.config.ts` trusts the Clerk JWT issuer domain.
|
||||
3. Convex functions access the user via `ctx.auth.getUserIdentity()`.
|
||||
4. `convex/model/users.ts` exports `getCurrentUser`, `requireAdmin`, `requireOwnership`.
|
||||
5. Clerk webhooks sync user changes to the `users` table via `convex/http.ts`.
|
||||
|
||||
### Guest Cart & Session
|
||||
|
||||
- A guest `sessionId` is generated on first load and stored in a cookie (`apps/storefront/src/lib/session/`).
|
||||
- All cart/wishlist queries accept either `userId` (signed-in) or `sessionId` (guest).
|
||||
- On sign-in, `SessionCartMerge` component triggers a merge mutation to fold the guest cart into the user's cart.
|
||||
|
||||
### Checkout Flow
|
||||
|
||||
1. Address validation — `convex/model/checkout.ts` + Convex address functions
|
||||
2. Shipping rates — Shippo API via `convex/model/shippo.ts`
|
||||
3. Payment intent — Stripe via `convex/stripeActions.ts`
|
||||
4. Order creation — snapshots of items, addresses, and pricing captured in `convex/orders.ts`
|
||||
5. Stripe webhook — handled in `convex/http.ts`, updates order payment status
|
||||
|
||||
## Convex Function Conventions
|
||||
|
||||
Always use the new function syntax with explicit validators:
|
||||
|
||||
```typescript
|
||||
import { query, mutation } from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
|
||||
export const myFunction = query({
|
||||
args: { id: v.id("products") },
|
||||
handler: async (ctx, args) => { ... },
|
||||
});
|
||||
```
|
||||
|
||||
- Use `internalQuery` / `internalMutation` / `internalAction` for private functions.
|
||||
- HTTP endpoints go in `convex/http.ts` using `httpAction`.
|
||||
- Use `ctx.runQuery` / `ctx.runMutation` from actions; avoid chaining many calls (race conditions).
|
||||
- Leverage index-based queries over full table scans.
|
||||
|
||||
## Storefront UI Rules
|
||||
|
||||
**HeroUI** is the component library for the storefront. Two mandatory overrides:
|
||||
|
||||
| Situation | Use |
|
||||
|-----------|-----|
|
||||
| Internal navigation | `next/link` `<Link>` (never `@heroui/react` `Link`) |
|
||||
| Content images | `next/image` `<Image>` (never raw `<img>`) |
|
||||
|
||||
Preserve semantic HTML (`<main>`, `<header>`, `<footer>`, `<article>` per product card, `<ol>` for breadcrumbs) even when composing Hero UI components around them.
|
||||
|
||||
## PetPaws Branding (Storefront)
|
||||
|
||||
**Typography:**
|
||||
- Headings (h1–h3): Fraunces serif — `font-[family-name:var(--font-fraunces)]`
|
||||
- Body / UI: DM Sans — `font-sans` (default)
|
||||
|
||||
**Key colors:**
|
||||
| Token | Hex | Usage |
|
||||
|-------|-----|-------|
|
||||
| Deep Teal | `#236f6b` | Header/footer bg, price text |
|
||||
| Brand Teal | `#38a99f` | Primary buttons, links, focus rings |
|
||||
| Sunny Amber | `#f4a13a` | High-priority CTAs ("Add to Cart") |
|
||||
| Playful Coral | `#f2705a` | Discount badges, sale text, alerts |
|
||||
| Forest Black | `#1a2e2d` | Body text (never pure `#000000`) |
|
||||
| Ice White | `#f0f8f7` | Page background |
|
||||
|
||||
Prefer CSS variables or Tailwind arbitrary values over raw hex. Do not mix coral and amber in the same component.
|
||||
|
||||
## Shared Packages
|
||||
|
||||
```typescript
|
||||
import type { Product, Order, User } from "@repo/types";
|
||||
import { formatPrice, slugify, formatDate } from "@repo/utils";
|
||||
import { ConvexClientProvider } from "@repo/convex"; // wraps app layouts
|
||||
```
|
||||
|
||||
Both apps' `next.config.js` transpile `@repo/*` packages.
|
||||
|
||||
## Testing
|
||||
|
||||
Tests are co-located with source files (`convex/*.test.ts`). Vitest runs in edge-runtime using `convex-test`. Environment variables for tests are not required — `convex-test` mocks the Convex runtime.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Each app has its own `.env.local` (copy from `.env.example`). Required vars:
|
||||
- `NEXT_PUBLIC_CONVEX_URL` — from Convex dashboard
|
||||
- `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` / `CLERK_SECRET_KEY`
|
||||
- `STRIPE_SECRET_KEY` / `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY`
|
||||
- `SHIPPO_API_KEY`
|
||||
|
||||
Convex backend env vars (set in Convex Dashboard): `CLERK_JWT_ISSUER_DOMAIN`, `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`, `SHIPPO_API_KEY`.
|
||||
Reference in New Issue
Block a user