init
This commit is contained in:
488
AGENTS.md
Normal file
488
AGENTS.md
Normal file
@@ -0,0 +1,488 @@
|
||||
# AGENTS.md - AI Coding Agent Reference
|
||||
|
||||
This file provides project-specific guidance for AI coding agents working in this repository. It reflects the current architecture and migration status of the codebase.
|
||||
|
||||
---
|
||||
|
||||
## Project Overview
|
||||
|
||||
This project is a Next.js admin dashboard starter that is being migrated from template scaffolding to an app-owned architecture.
|
||||
|
||||
Current core stack:
|
||||
|
||||
- **Framework**: Next.js 16 (App Router)
|
||||
- **Language**: TypeScript 5.7
|
||||
- **UI**: shadcn/ui + Tailwind CSS v4
|
||||
- **Authentication**: Auth.js
|
||||
- **Persistence**: PostgreSQL + Drizzle ORM
|
||||
- **Data Fetching**: TanStack React Query
|
||||
- **URL State**: nuqs
|
||||
- **Forms**: TanStack Form + Zod
|
||||
- **Charts**: Recharts
|
||||
- **Monitoring**: Sentry
|
||||
- **Package Manager**: Bun preferred, npm acceptable
|
||||
|
||||
Current migration status:
|
||||
|
||||
- Auth has been moved to **Auth.js**
|
||||
- Multi-tenant data now uses app-owned **`users`**, **`organizations`**, and **`memberships`**
|
||||
- **`products`** has been migrated to Route Handlers + Drizzle
|
||||
- **`users`** has been migrated to Route Handlers + Drizzle
|
||||
- RBAC now uses a global `users.system_role` plus organization-scoped `memberships.role`
|
||||
- self-service sign-up is disabled; accounts must be created by an admin
|
||||
- some legacy pages still exist as placeholders or transitional seams
|
||||
|
||||
---
|
||||
|
||||
## Technology Stack Details
|
||||
|
||||
### Core Runtime
|
||||
|
||||
- Next.js 16.x with App Router
|
||||
- React 19.x
|
||||
- TypeScript strict mode
|
||||
|
||||
### Styling & UI
|
||||
|
||||
- Tailwind CSS v4
|
||||
- shadcn/ui components
|
||||
- CSS custom properties and theme registry
|
||||
|
||||
### State Management
|
||||
|
||||
- Zustand for local UI state
|
||||
- nuqs for URL search params
|
||||
- TanStack Form + Zod for forms
|
||||
|
||||
### Data Fetching
|
||||
|
||||
- TanStack React Query for server state
|
||||
- server prefetch with `HydrationBoundary` + `dehydrate`
|
||||
- `useSuspenseQuery()` for client consumption of prefetched data
|
||||
|
||||
### Authentication & Authorization
|
||||
|
||||
- Auth.js for authentication
|
||||
- app-owned `organizations` and `memberships` for multi-tenant access
|
||||
- global system role stored on `users.system_role`
|
||||
- organization role and permissions stored on `memberships`
|
||||
- client-side nav filtering is UX-only; real protection must be server-side
|
||||
|
||||
### Persistence & APIs
|
||||
|
||||
- PostgreSQL + Drizzle ORM
|
||||
- Route Handlers under `src/app/api/**` are the main server boundary
|
||||
- feature services call those route handlers through `src/lib/api-client.ts`
|
||||
- mock APIs remain only as **legacy migration seams**
|
||||
|
||||
---
|
||||
|
||||
## Current Auth Model
|
||||
|
||||
The current auth model is app-owned and centered around:
|
||||
|
||||
- `users`
|
||||
- `organizations`
|
||||
- `memberships`
|
||||
|
||||
Important files:
|
||||
|
||||
- `src/auth.ts` - Auth.js config and exported helpers
|
||||
- `src/lib/auth/session.ts` - shared server-side auth helpers
|
||||
- `src/proxy.ts` - route protection
|
||||
- `src/db/schema.ts` - current database schema
|
||||
|
||||
Current behavior:
|
||||
|
||||
- authentication uses an Auth.js **Credentials** provider
|
||||
- passwords are verified against `users.password_hash`
|
||||
- global RBAC uses `users.system_role` with `super_admin | user`
|
||||
- organization RBAC uses `memberships.role` with `admin | user`
|
||||
- the user row stores `active_organization_id`
|
||||
- session enrichment loads organization and membership data on each request
|
||||
- self-service sign-up is disabled
|
||||
- initial super admin bootstrap is done with the seed script, not through the UI
|
||||
|
||||
Preferred server-side helpers:
|
||||
|
||||
- `requireSession()`
|
||||
- `requireSystemRole()`
|
||||
- `requireOrganizationAccess(options?)`
|
||||
|
||||
Do not introduce new Clerk usage.
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```text
|
||||
/src
|
||||
├── app/ # Next.js App Router
|
||||
│ ├── auth/ # Auth pages
|
||||
│ ├── dashboard/ # Dashboard routes
|
||||
│ ├── api/ # Route handlers
|
||||
│ ├── layout.tsx # Root layout
|
||||
│ └── page.tsx # Landing / redirect entry
|
||||
│
|
||||
├── components/
|
||||
│ ├── ui/ # shadcn/ui and app UI primitives
|
||||
│ ├── layout/ # Sidebar, header, providers
|
||||
│ ├── forms/ # Form field wrappers
|
||||
│ ├── themes/ # Theme system
|
||||
│ └── icons.tsx # Single icon registry
|
||||
│
|
||||
├── db/
|
||||
│ └── schema.ts # Drizzle schema
|
||||
│
|
||||
├── features/
|
||||
│ ├── auth/
|
||||
│ ├── overview/
|
||||
│ ├── products/ # Migrated: React Query + Route Handlers + Drizzle
|
||||
│ ├── users/ # Migrated: React Query + Route Handlers + Drizzle + RBAC
|
||||
│ ├── react-query-demo/
|
||||
│ ├── kanban/
|
||||
│ ├── chat/
|
||||
│ ├── notifications/
|
||||
│ └── profile/
|
||||
│
|
||||
├── config/
|
||||
│ └── nav-config.ts
|
||||
│
|
||||
├── hooks/
|
||||
│ ├── use-nav.ts
|
||||
│ └── use-data-table.ts
|
||||
│
|
||||
├── lib/
|
||||
│ ├── api-client.ts
|
||||
│ ├── auth/
|
||||
│ ├── db.ts
|
||||
│ ├── query-client.ts
|
||||
│ └── searchparams.ts
|
||||
│
|
||||
├── styles/
|
||||
└── types/
|
||||
```
|
||||
|
||||
Relevant docs/plans:
|
||||
|
||||
- `plans/req.md`
|
||||
- `plans/implementation-plan.md`
|
||||
- `docs/themes.md`
|
||||
- `docs/nav-rbac.md`
|
||||
- `docs/clerk_setup.md` is legacy historical reference only
|
||||
|
||||
---
|
||||
|
||||
## Build & Development Commands
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
bun install
|
||||
# or
|
||||
npm install
|
||||
|
||||
# Development server
|
||||
bun run dev
|
||||
# or
|
||||
npm run dev
|
||||
|
||||
# Build
|
||||
bun run build
|
||||
# or
|
||||
npm run build
|
||||
|
||||
# Start
|
||||
bun run start
|
||||
# or
|
||||
npm run start
|
||||
|
||||
# Lint
|
||||
bun run lint
|
||||
# or
|
||||
npm run lint
|
||||
|
||||
# Format
|
||||
bun run format
|
||||
# or
|
||||
npm run format
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
Copy `env.example.txt` to `.env.local` and configure:
|
||||
|
||||
### Required
|
||||
|
||||
```env
|
||||
AUTH_SECRET=your-long-random-secret
|
||||
DATABASE_URL=postgres://postgres:postgres@localhost:5432/training_system
|
||||
SUPER_ADMIN_EMAIL=admin@example.com
|
||||
SUPER_ADMIN_PASSWORD=change-this-password
|
||||
```
|
||||
|
||||
### Optional for Sentry
|
||||
|
||||
```env
|
||||
NEXT_PUBLIC_SENTRY_DSN=https://...@....ingest.sentry.io/...
|
||||
NEXT_PUBLIC_SENTRY_ORG=your-org
|
||||
NEXT_PUBLIC_SENTRY_PROJECT=your-project
|
||||
SENTRY_AUTH_TOKEN=sntrys_...
|
||||
NEXT_PUBLIC_SENTRY_DISABLED="false"
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- keep auth and database secrets server-only
|
||||
- OAuth providers can be added later through Auth.js
|
||||
- billing is currently modeled as app-owned organization plan data, not a live provider integration
|
||||
- run `npm run setup:db` on first setup to apply migrations and seed the initial super admin
|
||||
|
||||
---
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
### TypeScript
|
||||
|
||||
- strict mode enabled
|
||||
- use explicit return types for public functions when useful
|
||||
- prefer `interface` for object contracts
|
||||
- use `@/*` imports from `src`
|
||||
|
||||
### Component Conventions
|
||||
|
||||
- use function declarations for components
|
||||
- server components by default
|
||||
- add `'use client'` only when needed
|
||||
- use `cn()` for className merging
|
||||
|
||||
### Forms
|
||||
|
||||
- use TanStack Form via `useAppForm` from `@/components/ui/tanstack-form`
|
||||
- use Zod for submit validation
|
||||
- avoid `useState` inside `AppField` render props
|
||||
|
||||
### Buttons
|
||||
|
||||
- use `<Button isLoading={isPending}>` for loading states
|
||||
|
||||
---
|
||||
|
||||
## Navigation & RBAC
|
||||
|
||||
Navigation is configured in `src/config/nav-config.ts`.
|
||||
|
||||
Current access properties:
|
||||
|
||||
- `systemRole`
|
||||
- `requireOrg`
|
||||
- `permission`
|
||||
- `role`
|
||||
- `plan`
|
||||
- `feature`
|
||||
|
||||
Notes:
|
||||
|
||||
- `systemRole` is for global access such as `super_admin`
|
||||
- `requireOrg` remains a compatibility seam and means the user must have an active organization
|
||||
- nav filtering in `src/hooks/use-nav.ts` uses Auth.js session data
|
||||
- server routes and server pages must enforce real access independently
|
||||
|
||||
---
|
||||
|
||||
## Authentication Patterns
|
||||
|
||||
### Protected Routes
|
||||
|
||||
Dashboard routes are protected through `src/proxy.ts`.
|
||||
|
||||
Example protected page:
|
||||
|
||||
```tsx
|
||||
import { auth } from '@/auth';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
export default async function Page() {
|
||||
const session = await auth();
|
||||
|
||||
if (!session?.user) {
|
||||
redirect('/auth/sign-in');
|
||||
}
|
||||
|
||||
if (!session.user.activeOrganizationId) {
|
||||
redirect('/dashboard/workspaces');
|
||||
}
|
||||
|
||||
return <div>Protected</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### Shared Server Helpers
|
||||
|
||||
Prefer:
|
||||
|
||||
- `requireSession()`
|
||||
- `requireSystemRole()`
|
||||
- `requireOrganizationAccess(options?)`
|
||||
|
||||
### Plan / Feature Gating
|
||||
|
||||
Do not use vendor-specific gating components.
|
||||
|
||||
Read plan or feature state from app-owned organization data and enforce it server-side.
|
||||
|
||||
---
|
||||
|
||||
## Data Fetching Patterns
|
||||
|
||||
### Service Layer Architecture
|
||||
|
||||
Each feature should use:
|
||||
|
||||
```text
|
||||
src/features/<name>/api/
|
||||
types.ts
|
||||
service.ts
|
||||
queries.ts
|
||||
mutations.ts # when mutations exist
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- components import types from `types.ts`
|
||||
- components call query options from `queries.ts`
|
||||
- services call local route handlers through `apiClient`
|
||||
- route handlers talk to Drizzle / database
|
||||
|
||||
### Backend Patterns
|
||||
|
||||
Preferred patterns in this repo:
|
||||
|
||||
| Pattern | Guidance |
|
||||
| --- | --- |
|
||||
| Route Handlers + ORM | Preferred for app-owned features |
|
||||
| Server Actions + ORM | Acceptable when it fits the feature well |
|
||||
| BFF | Acceptable when proxying to an external backend |
|
||||
| Mock | Legacy only, not the preferred path |
|
||||
|
||||
### React Query
|
||||
|
||||
Preferred pattern for new pages:
|
||||
|
||||
1. define query options in `queries.ts`
|
||||
2. prefetch in the server page
|
||||
3. hydrate with `HydrationBoundary`
|
||||
4. read with `useSuspenseQuery()`
|
||||
|
||||
### URL State
|
||||
|
||||
Use `nuqs` for table search params and list filters.
|
||||
|
||||
### Tables
|
||||
|
||||
Use:
|
||||
|
||||
- TanStack Table
|
||||
- React Query
|
||||
- `useDataTable`
|
||||
- column definitions under the feature component tree
|
||||
|
||||
---
|
||||
|
||||
## Current Migration Guidance
|
||||
|
||||
Use the migrated `products` feature as the main reference for new CRUD work.
|
||||
|
||||
Important current status:
|
||||
|
||||
- `products` is migrated to Auth.js + Route Handlers + Drizzle
|
||||
- `users` is migrated to Auth.js + Route Handlers + Drizzle
|
||||
- new features should follow the `products` and `users` pattern, not the old mock pattern
|
||||
|
||||
Legacy seams still present:
|
||||
|
||||
- `src/constants/mock-api.ts`
|
||||
- `src/constants/mock-api-users.ts`
|
||||
- some placeholder pages under workspaces, billing, profile, and exclusive sections
|
||||
|
||||
Treat those as migration seams, not target architecture.
|
||||
|
||||
---
|
||||
|
||||
## Deployment Notes
|
||||
|
||||
### Production Requirements
|
||||
|
||||
Ensure these are set:
|
||||
|
||||
- `AUTH_SECRET`
|
||||
- `DATABASE_URL`
|
||||
- `SUPER_ADMIN_EMAIL`
|
||||
- `SUPER_ADMIN_PASSWORD`
|
||||
- relevant `SENTRY_*` variables if using Sentry
|
||||
|
||||
### Build Considerations
|
||||
|
||||
- app builds as standalone
|
||||
- database migrations must be applied before runtime
|
||||
- production secrets must not be exposed as `NEXT_PUBLIC_*`
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Auth fails in development
|
||||
|
||||
- verify `AUTH_SECRET`
|
||||
- verify `DATABASE_URL`
|
||||
- verify migrations are applied
|
||||
- verify the super admin seed has run and seeded users have valid `password_hash`
|
||||
|
||||
### Theme not applying
|
||||
|
||||
- verify theme CSS import in `src/styles/theme.css`
|
||||
- verify theme registration in `theme.config.ts`
|
||||
|
||||
### Navigation items not showing
|
||||
|
||||
- check `access` in `nav-config.ts`
|
||||
- verify session has active organization, role, or permissions as needed
|
||||
|
||||
### Feature data looks stale
|
||||
|
||||
- verify mutation invalidates the feature query key factory
|
||||
- verify filters are included in the query key
|
||||
|
||||
---
|
||||
|
||||
## External Documentation
|
||||
|
||||
- [Next.js App Router](https://nextjs.org/docs/app)
|
||||
- [Auth.js](https://authjs.dev)
|
||||
- [Drizzle ORM](https://orm.drizzle.team)
|
||||
- [shadcn/ui](https://ui.shadcn.com/docs)
|
||||
- [Tailwind CSS v4](https://tailwindcss.com/docs)
|
||||
- [TanStack Query](https://tanstack.com/query)
|
||||
- [TanStack Table](https://tanstack.com/table/latest)
|
||||
- [TanStack Form](https://tanstack.com/form)
|
||||
- [Sentry Next.js](https://docs.sentry.io/platforms/javascript/guides/nextjs/)
|
||||
|
||||
---
|
||||
|
||||
## Notes for AI Agents
|
||||
|
||||
1. Always use `cn()` for className merging.
|
||||
2. Respect the feature-based structure and keep new work inside `src/features/`.
|
||||
3. Server components are the default.
|
||||
4. Avoid `any`; keep contracts explicit.
|
||||
5. Use `PageContainer` for page headers instead of importing `Heading` directly in pages.
|
||||
6. Use TanStack Form + Zod via the existing project wrappers.
|
||||
7. Use icons only through `@/components/icons`.
|
||||
8. For new app-owned features, prefer Route Handlers + Drizzle + React Query.
|
||||
9. Never import from `@/constants/mock-api*` directly in components.
|
||||
10. Use `@/auth` and `src/lib/auth/session.ts` for auth and organization checks.
|
||||
11. Do not introduce new Clerk usage.
|
||||
12. Treat legacy mock-backed modules as migration seams.
|
||||
13. Follow the migrated `products` and `users` feature patterns when building new CRUD modules.
|
||||
14. Do not re-enable self-service sign-up unless the product requirement changes explicitly.
|
||||
Reference in New Issue
Block a user