181 lines
8.0 KiB
Markdown
181 lines
8.0 KiB
Markdown
# 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/*` via `next.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 with `content`, `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 `/components` with 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**: `AuthContext` provides `token`, `user`, `login()`, `register()`, `logout()`, `refreshMeAndSession()`
|
|
- **Middleware gate** ([middleware.ts](middleware.ts#L24-L34)): Admin routes at `/admin/*` require `bb_access_token` + role check
|
|
```typescript
|
|
if (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](lib/api.ts))
|
|
|
|
- **useApi() hook**: Returns `{ get(), post(), put() }` methods with auto-injected Bearer token
|
|
- **Response unwrapping** ([components/parts/PartsBrowseClient.tsx](components/parts/PartsBrowseClient.tsx#L36-L43)):
|
|
```typescript
|
|
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](lib/buildOverlaps.ts), [data/gunbuilderParts.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()](lib/buildOverlaps.ts#L53) educates users when incompatible parts selected
|
|
- E.g., selecting "complete-upper" + "barrel" → show warning chip
|
|
- Used in [OverlapChip](components/builder/OverlapChip.tsx), [PartCard](components/PartCard.tsx)
|
|
|
|
### 4. **Parts Browsing & Filtering** ([components/parts/PartsBrowseClient.tsx](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**: `GunbuilderProductFromApi` with `id`, `name`, `brand`, `platform`, `partRole`, `price`, `imageUrl`, `buyUrl`, `inStock`
|
|
|
|
### 5. **Styling & Dark Mode**
|
|
|
|
- **TailwindCSS**: Configured for dark mode via `"class"` strategy ([tailwind.config.ts](tailwind.config.ts#L8))
|
|
- **Root script** ([app/layout.tsx](app/layout.tsx#L21-L31)): Inline JS checks localStorage for theme, applies `dark` class 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](components/ui/RichTextEditor.tsx))
|
|
|
|
- **RichTextEditor**: Wraps Quill with image resize module for email/admin content
|
|
- **Email form**: [send_email_form.tsx](components/ui/send_email_form.tsx) uses form context + RichTextEditor combo
|
|
|
|
---
|
|
|
|
## Development Workflows
|
|
|
|
### Local Setup
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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 PageResponse
|
|
- `POST /api/admin/...` → admin-only mutations (import, mapping, etc.)
|
|
|
|
---
|
|
|
|
## Common Tasks & Patterns
|
|
|
|
### Add a New Admin Page
|
|
|
|
1. Create `app/admin/[feature]/page.tsx` with `"use client"`
|
|
2. Add to [AdminLeftNavigation](components/AdminLeftNavigation.tsx) navGroups
|
|
3. Use `useAuth()` for permission checks; fallback `<protectedPage />` if needed
|
|
4. Fetch via `useApi()` hook
|
|
|
|
### Modify Parts Filtering
|
|
|
|
- Edit sort options in [SortBar](components/parts/SortBar.tsx) or query logic in [PartsBrowseClient](components/parts/PartsBrowseClient.tsx)
|
|
- Update `PAGE_SIZE` constant if needed
|
|
- Ensure query params flow through useRouter + useSearchParams
|
|
|
|
### Update Builder Slot Definitions
|
|
|
|
- Edit [data/gunbuilderParts.ts](data/gunbuilderParts.ts) CATEGORIES array
|
|
- Run [lib/buildOverlaps.ts](lib/buildOverlaps.ts) overlap checks if adding conflicting slots
|
|
- Update type in [types/builderSlots.ts](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 `.content` array 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; ensure `NEXT_PUBLIC_API_BASE_URL` matches
|
|
- **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 TailwindCSS `dark:` selectors
|
|
- **Hydration**: Theme script runs inline before React hydration to avoid FOUC (flash of unstyled content)
|