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>
5.9 KiB
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:
# 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)
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
- Clerk handles UI + JWT.
ConvexProviderWithClerk(from@repo/convex) passes the JWT to Convex. convex/auth.config.tstrusts the Clerk JWT issuer domain.- Convex functions access the user via
ctx.auth.getUserIdentity(). convex/model/users.tsexportsgetCurrentUser,requireAdmin,requireOwnership.- Clerk webhooks sync user changes to the
userstable viaconvex/http.ts.
Guest Cart & Session
- A guest
sessionIdis generated on first load and stored in a cookie (apps/storefront/src/lib/session/). - All cart/wishlist queries accept either
userId(signed-in) orsessionId(guest). - On sign-in,
SessionCartMergecomponent triggers a merge mutation to fold the guest cart into the user's cart.
Checkout Flow
- Address validation —
convex/model/checkout.ts+ Convex address functions - Shipping rates — Shippo API via
convex/model/shippo.ts - Payment intent — Stripe via
convex/stripeActions.ts - Order creation — snapshots of items, addresses, and pricing captured in
convex/orders.ts - Stripe webhook — handled in
convex/http.ts, updates order payment status
Convex Function Conventions
Always use the new function syntax with explicit validators:
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/internalActionfor private functions. - HTTP endpoints go in
convex/http.tsusinghttpAction. - Use
ctx.runQuery/ctx.runMutationfrom 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
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 dashboardNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY/CLERK_SECRET_KEYSTRIPE_SECRET_KEY/NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYSHIPPO_API_KEY
Convex backend env vars (set in Convex Dashboard): CLERK_JWT_ISSUER_DOMAIN, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, SHIPPO_API_KEY.