8.0 KiB
8.0 KiB
Gunbuilder Prototype — AI Coding Instructions
Project Overview
Gunbuilder is a Next.js 14 (App Router) admin UI + consumer builder for a firearms parts marketplace. The system manages merchant feeds, normalizes product catalogs, powers a rifle builder interface, and backs a Spring Boot API.
Key Architecture
- Frontend: Next.js 14 (App Router), TypeScript, TailwindCSS, React 18
- Backend: Spring Boot (proxied at
/api/*vianext.config.mjs) - Auth: JWT tokens stored in cookies (
bb_access_token,bb_role), validated in middleware - State: React Context (AuthContext) for user/auth, client-side React hooks for UI state
- Data: Spring Boot REST API—responses paginated via Spring's
PageResponse<T>structure withcontent,totalElements,totalPages,number(0-based),size
File Structure & Naming Conventions
App Router Layout
app/
├── layout.tsx # Root (AuthProvider, metadata, theme script)
├── (admin)/layout.tsx # Admin area: auth gate + left nav sidebar
├── (app)/layout.tsx # Consumer builder: TopNav + BuilderNav
├── (account)/ # User account routes (protected)
├── login/page.tsx, register/page.tsx
├── api/ # Server-side API route handlers
└── actions/ # Server Actions (e.g., sendEmail.ts)
Naming & Organization
- Layout grouping: Use
(admin),(app),(account)route groups to scope nav & auth logic - Client vs Server: Mark pages/components with
"use client"if they use hooks, state, or browser APIs. Default is Server Component - Components: Store in
/componentswith category subdirectories:builder/,parts/,ui/ - Utilities: Place reusable logic in
/lib(API calls, parsers, helpers) - Types: Centralize in
/types(e.g.,uiPart.ts,builderSlots.ts,user.ts) - Data: Non-fetched lookup tables in
/data(e.g.,gunbuilderParts.ts— builder slot definitions)
Critical Patterns
1. Authentication & Authorization
- Token storage: Stored in HTTP-only cookies (
bb_access_token,bb_role) - Client-side context:
AuthContextprovidestoken,user,login(),register(),logout(),refreshMeAndSession() - Middleware gate (middleware.ts): Admin routes at
/admin/*requirebb_access_token+ role checkif (pathname.startsWith("/admin")) { const token = req.cookies.get("bb_access_token")?.value; const role = req.cookies.get("bb_role")?.value; const ok = !!token && (!role || role === "ADMIN"); if (!ok) redirect to /login } - Wrap pages: Use
protectedPage()HOC for client-side auth walls
2. API Integration (lib/api.ts)
- useApi() hook: Returns
{ get(), post(), put() }methods with auto-injected Bearer token - Response unwrapping (components/parts/PartsBrowseClient.tsx):
function unwrapContent<T>(json: any) { if (Array.isArray(json)) return { items: json }; if (Array.isArray(json.content)) return { items: json.content, page: json }; // Spring PageResponse return { items: [] }; } - Pagination: API returns
{ content: T[], totalElements, totalPages, number, size }—unwrap before rendering
3. Builder State & Overlaps (lib/buildOverlaps.ts, data/gunbuilderParts.ts)
- Category slots: Defined in
/data/gunbuilderParts.ts(CATEGORIES array with id, name, group) - BuildState type:
Partial<Record<CategoryId, string>>(maps slot ID to selected product ID) - Overlap logic: getOverlapChipsForCategory() educates users when incompatible parts selected
- E.g., selecting "complete-upper" + "barrel" → show warning chip
- Used in OverlapChip, PartCard
4. Parts Browsing & Filtering (components/parts/PartsBrowseClient.tsx)
- Page size: Hardcoded to 24 items
- Query params:
platform,partRole,search,sort(relevance | price-asc | price-desc | brand-asc),page(0-based) - View modes: Card (grid) or list view via state hook
- Product type:
GunbuilderProductFromApiwithid,name,brand,platform,partRole,price,imageUrl,buyUrl,inStock
5. Styling & Dark Mode
- TailwindCSS: Configured for dark mode via
"class"strategy (tailwind.config.ts) - Root script (app/layout.tsx): Inline JS checks localStorage for theme, applies
darkclass to<html> - Class pattern: Use
dark:prefix for dark-mode overrides (e.g.,dark:bg-black dark:text-zinc-50) - Colors: Zinc scale primary (white/950 light, black/50 dark); semantic grays for UI
6. Form & Rich Text (components/ui/RichTextEditor.tsx)
- RichTextEditor: Wraps Quill with image resize module for email/admin content
- Email form: send_email_form.tsx uses form context + RichTextEditor combo
Development Workflows
Local Setup
npm install
npm run dev # Starts Next.js at http://localhost:3000
Environment Variables (.env.local)
NEXT_PUBLIC_API_BASE_URL=http://localhost:8080
NEXT_PUBLIC_ADMIN_BASE_PATH=/builder/admin
NEXT_PUBLIC_LAUNCH_ONLY_ROOT=false # Set true to restrict non-launch content
Building
npm run build
npm run start # Production mode
npm run lint # ESLint check
Key Backend Endpoints (Spring Boot)
POST /api/auth/login→ returns{ token, user }GET /api/gunbuilder/products?platform=...&partRole=...→ returns Spring PageResponsePOST /api/admin/...→ admin-only mutations (import, mapping, etc.)
Common Tasks & Patterns
Add a New Admin Page
- Create
app/admin/[feature]/page.tsxwith"use client" - Add to AdminLeftNavigation navGroups
- Use
useAuth()for permission checks; fallback<protectedPage />if needed - Fetch via
useApi()hook
Modify Parts Filtering
- Edit sort options in SortBar or query logic in PartsBrowseClient
- Update
PAGE_SIZEconstant if needed - Ensure query params flow through useRouter + useSearchParams
Update Builder Slot Definitions
- Edit data/gunbuilderParts.ts CATEGORIES array
- Run lib/buildOverlaps.ts overlap checks if adding conflicting slots
- Update type in types/builderSlots.ts to reflect new CategoryId union
Add Server Action
- Create file in
app/actions/(e.g.,sendEmail.ts) - Mark top with
"use server" - Import & call from client components; errors bubble naturally
Known Constraints & Workarounds
- No test suite: Manual testing required; dev server hot-reload via
npm run dev - Spring PageResponse unwrapping: Always check for
.contentarray when parsing API responses - Image URLs: May be null; provide fallback in UI (e.g., placeholder image)
- Mobile responsive: TailwindCSS handles breakpoints; test with dark mode class toggle
- Type safety: Use strict TS config; leverage builder slot types to avoid string mismatches
Integration Points & Gotchas
- CORS: Spring Boot backend must allow origin
http://localhost:3000(or production domain) - API rewrites: Next.js rewrites
/api/*to backend at build time; ensureNEXT_PUBLIC_API_BASE_URLmatches - Auth token refresh: Handled in AuthContext
refreshMeAndSession()—called on mount or nav - Dark mode class toggle: Update
<html>class, not just CSS var—required for TailwindCSSdark:selectors - Hydration: Theme script runs inline before React hydration to avoid FOUC (flash of unstyled content)