Compare commits
2 Commits
ccbdc387eb
...
84bd962744
| Author | SHA1 | Date | |
|---|---|---|---|
| 84bd962744 | |||
| 8505d4fd57 |
135
AGENTS.md
Normal file
135
AGENTS.md
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This file defines project-specific guidance for future contributors and coding agents working in this repository. Follow it alongside the main project docs.
|
||||||
|
|
||||||
|
## Project overview
|
||||||
|
|
||||||
|
MRP Codex is a modular Manufacturing Resource Planning platform intended to be a lighter, sleeker alternative to Odoo. The current repository contains the foundation release:
|
||||||
|
|
||||||
|
- React + Vite + Tailwind frontend
|
||||||
|
- Express + TypeScript backend
|
||||||
|
- Prisma + SQLite persistence
|
||||||
|
- local JWT auth and RBAC
|
||||||
|
- Company Settings and runtime branding
|
||||||
|
- filesystem-backed attachments
|
||||||
|
- Puppeteer PDF foundation
|
||||||
|
- single-container Docker deployment
|
||||||
|
|
||||||
|
## Source of truth documents
|
||||||
|
|
||||||
|
Read these before major work:
|
||||||
|
|
||||||
|
- [README.md](D:/CODING/mrp-codex/README.md)
|
||||||
|
- [INSTRUCTIONS.md](D:/CODING/mrp-codex/INSTRUCTIONS.md)
|
||||||
|
- [STRUCTURE.md](D:/CODING/mrp-codex/STRUCTURE.md)
|
||||||
|
- [ROADMAP.md](D:/CODING/mrp-codex/ROADMAP.md)
|
||||||
|
- [UNRAID.md](D:/CODING/mrp-codex/UNRAID.md)
|
||||||
|
|
||||||
|
If implementation changes invalidate those docs, update them in the same change set.
|
||||||
|
|
||||||
|
## Architecture rules
|
||||||
|
|
||||||
|
### Keep the app modular by domain
|
||||||
|
|
||||||
|
- Backend modules belong under `server/src/modules/<domain>`
|
||||||
|
- Frontend modules belong under `client/src/modules/<domain>`
|
||||||
|
- Shared contracts belong under `shared/src`
|
||||||
|
- Do not collapse unrelated business logic into the app shell or generic utility folders
|
||||||
|
|
||||||
|
### Backend boundaries
|
||||||
|
|
||||||
|
- Keep routers thin
|
||||||
|
- Put business logic in services
|
||||||
|
- Put persistence access behind Prisma usage in module services or focused helpers
|
||||||
|
- Keep auth, RBAC, storage, and Prisma setup in `server/src/lib`
|
||||||
|
- Keep environment and path configuration in `server/src/config`
|
||||||
|
|
||||||
|
### Frontend boundaries
|
||||||
|
|
||||||
|
- Shared UI primitives go in `client/src/components`
|
||||||
|
- Theme logic goes in `client/src/theme`
|
||||||
|
- Authentication state goes in `client/src/auth`
|
||||||
|
- Route-level business pages go in `client/src/modules`
|
||||||
|
- Do not mix PDF template concerns into normal UI pages
|
||||||
|
|
||||||
|
### Shared package constraints
|
||||||
|
|
||||||
|
- `shared` must stay framework-agnostic
|
||||||
|
- Use explicit `.js` relative exports/imports in `shared/src` because it ships as Node ESM
|
||||||
|
- Keep DTOs, permission keys, and cross-app types there
|
||||||
|
|
||||||
|
## Data and persistence rules
|
||||||
|
|
||||||
|
- SQLite database must live under `/app/data/prisma/app.db`
|
||||||
|
- Uploaded files must live under `/app/data/uploads`
|
||||||
|
- Never store file blobs in SQLite
|
||||||
|
- Store metadata and relative paths only
|
||||||
|
- Any persisted schema change must include a Prisma migration in `server/prisma/migrations`
|
||||||
|
|
||||||
|
## Prisma rules
|
||||||
|
|
||||||
|
- Run `npm run prisma:generate` after Prisma schema changes
|
||||||
|
- Use committed migrations as the source of truth
|
||||||
|
- Prefer Node 22 or Docker for Prisma migration execution
|
||||||
|
- Prisma client generation for Docker must continue to support the runtime binary target:
|
||||||
|
- `debian-openssl-3.0.x`
|
||||||
|
- Do not remove the current `binaryTargets` setting from `server/prisma/schema.prisma` unless the base image changes and the runtime target is updated intentionally
|
||||||
|
|
||||||
|
## Docker rules
|
||||||
|
|
||||||
|
- The Dockerfile is designed for command-line builds from the repo root
|
||||||
|
- Do not reintroduce Puppeteer browser downloads during image build
|
||||||
|
- The runtime image uses system Chromium at `/usr/bin/chromium`
|
||||||
|
- Container startup must continue to apply Prisma migrations before launching the app
|
||||||
|
- If Docker/runtime dependency handling changes, verify:
|
||||||
|
- Prisma binary is present
|
||||||
|
- Prisma client is generated in the runtime image
|
||||||
|
- shared ESM output resolves correctly in Node
|
||||||
|
|
||||||
|
## UI and product rules
|
||||||
|
|
||||||
|
- The application must remain brandable through centralized theme tokens and Company Settings
|
||||||
|
- Light and dark mode must remain first-class, not bolted on later
|
||||||
|
- New UI should respect the theme system and avoid hardcoded one-off colors where possible
|
||||||
|
- Keep the interface intentional and operational, not generic admin-template filler
|
||||||
|
|
||||||
|
## Feature expectations
|
||||||
|
|
||||||
|
Near-term priorities are:
|
||||||
|
|
||||||
|
1. CRM detail and edit workflows
|
||||||
|
2. Inventory and BOM data model
|
||||||
|
3. Sales and quote foundation
|
||||||
|
4. Shipping tied to sales orders
|
||||||
|
5. Live manufacturing gantt scheduling
|
||||||
|
|
||||||
|
When adding new modules, preserve the ability to extend the system without refactoring the existing app shell.
|
||||||
|
|
||||||
|
## Testing and verification
|
||||||
|
|
||||||
|
Before closing substantial work:
|
||||||
|
|
||||||
|
- run `npm run build`
|
||||||
|
- run `npm run test`
|
||||||
|
- if Docker-related code changed, rebuild the image if the environment allows it
|
||||||
|
- if Prisma schema changed, regenerate the client and confirm migrations are present
|
||||||
|
|
||||||
|
If you cannot run one of those checks, say so explicitly.
|
||||||
|
|
||||||
|
## Git and workflow expectations
|
||||||
|
|
||||||
|
- Keep commits focused and source-only; do not commit generated local build artifacts
|
||||||
|
- Update roadmap/docs when major work shifts priorities or architecture
|
||||||
|
- Do not remove or overwrite user changes without explicit instruction
|
||||||
|
- If a task reveals a persistent operational issue, document it rather than leaving it tribal knowledge
|
||||||
|
|
||||||
|
## Known pitfalls already encountered
|
||||||
|
|
||||||
|
- `npx prisma` from `/app` did not resolve correctly in the container; the entrypoint uses the server workspace binary directly
|
||||||
|
- Prisma client must be generated in the production dependency stage of the Docker build
|
||||||
|
- Prisma runtime on Debian bookworm requires `debian-openssl-3.0.x`
|
||||||
|
- `shared` package exports must use Node ESM-compatible `.js` specifiers
|
||||||
|
- Local Docker validation may fail if the Docker daemon is unavailable; distinguish daemon issues from image issues
|
||||||
|
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
color-scheme: light;
|
||||||
--font-family: "Manrope";
|
--font-family: "Manrope";
|
||||||
--color-brand: 24 90 219;
|
--color-brand: 24 90 219;
|
||||||
--color-accent: 0 166 166;
|
--color-accent: 0 166 166;
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
|
color-scheme: dark;
|
||||||
--color-brand: 63 140 255;
|
--color-brand: 63 140 255;
|
||||||
--color-accent: 34 211 238;
|
--color-accent: 34 211 238;
|
||||||
--color-surface: 30 41 59;
|
--color-surface: 30 41 59;
|
||||||
@@ -40,8 +42,60 @@ body {
|
|||||||
font-family: var(--font-family), sans-serif;
|
font-family: var(--font-family), sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
select,
|
||||||
|
button {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:not([type="color"]),
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
color: rgb(var(--color-text));
|
||||||
|
}
|
||||||
|
|
||||||
|
input::placeholder,
|
||||||
|
textarea::placeholder {
|
||||||
|
color: rgb(var(--color-muted));
|
||||||
|
}
|
||||||
|
|
||||||
.gantt-theme .wx-bar,
|
.gantt-theme .wx-bar,
|
||||||
.gantt-theme .wx-task {
|
.gantt-theme .wx-task {
|
||||||
fill: rgb(var(--color-brand));
|
fill: rgb(var(--color-brand));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gantt-theme {
|
||||||
|
--wx-font-family: var(--font-family), sans-serif;
|
||||||
|
--wx-background: rgb(var(--color-page));
|
||||||
|
--wx-background-alt: rgb(var(--color-surface));
|
||||||
|
--wx-color-font: rgb(var(--color-text));
|
||||||
|
--wx-color-secondary-font: rgb(var(--color-muted));
|
||||||
|
--wx-color-font-disabled: rgb(var(--color-muted));
|
||||||
|
--wx-color-link: rgb(var(--color-brand));
|
||||||
|
--wx-color-primary: rgb(var(--color-brand));
|
||||||
|
--wx-icon-color: rgb(var(--color-muted));
|
||||||
|
--wx-border: 1px solid rgb(var(--color-line));
|
||||||
|
--wx-box-shadow: 0 24px 60px rgba(15, 23, 42, 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gantt-theme .wx-layout,
|
||||||
|
.gantt-theme .wx-scale,
|
||||||
|
.gantt-theme .wx-gantt,
|
||||||
|
.gantt-theme .wx-table-container {
|
||||||
|
background-color: rgb(var(--color-page));
|
||||||
|
color: rgb(var(--color-text));
|
||||||
|
}
|
||||||
|
|
||||||
|
.gantt-theme .wx-grid,
|
||||||
|
.gantt-theme .wx-cell,
|
||||||
|
.gantt-theme .wx-row,
|
||||||
|
.gantt-theme .wx-text,
|
||||||
|
.gantt-theme .wx-task-name {
|
||||||
|
color: rgb(var(--color-text));
|
||||||
|
}
|
||||||
|
|
||||||
|
.gantt-theme .wx-cell,
|
||||||
|
.gantt-theme .wx-row {
|
||||||
|
border-color: rgb(var(--color-line));
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import type { GanttLinkDto, GanttTaskDto } from "@mrp/shared";
|
|||||||
|
|
||||||
import { useAuth } from "../../auth/AuthProvider";
|
import { useAuth } from "../../auth/AuthProvider";
|
||||||
import { api } from "../../lib/api";
|
import { api } from "../../lib/api";
|
||||||
|
import { useTheme } from "../../theme/ThemeProvider";
|
||||||
|
|
||||||
export function GanttPage() {
|
export function GanttPage() {
|
||||||
const { token } = useAuth();
|
const { token } = useAuth();
|
||||||
|
const { mode } = useTheme();
|
||||||
const [tasks, setTasks] = useState<GanttTaskDto[]>([]);
|
const [tasks, setTasks] = useState<GanttTaskDto[]>([]);
|
||||||
const [links, setLinks] = useState<GanttLinkDto[]>([]);
|
const [links, setLinks] = useState<GanttLinkDto[]>([]);
|
||||||
|
|
||||||
@@ -28,7 +30,11 @@ export function GanttPage() {
|
|||||||
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Planning</p>
|
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-muted">Planning</p>
|
||||||
<h3 className="mt-3 text-2xl font-bold text-text">SVAR Gantt Preview</h3>
|
<h3 className="mt-3 text-2xl font-bold text-text">SVAR Gantt Preview</h3>
|
||||||
<p className="mt-2 text-sm text-muted">Theme-aware integration wrapper prepared for future manufacturing schedules and task dependencies.</p>
|
<p className="mt-2 text-sm text-muted">Theme-aware integration wrapper prepared for future manufacturing schedules and task dependencies.</p>
|
||||||
<div className="gantt-theme mt-6 overflow-hidden rounded-2xl border border-line/70 bg-page/70 p-4">
|
<div
|
||||||
|
className={`gantt-theme mt-6 overflow-hidden rounded-2xl border border-line/70 bg-page/70 p-4 ${
|
||||||
|
mode === "dark" ? "wx-willow-dark-theme" : "wx-willow-theme"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
<Gantt
|
<Gantt
|
||||||
tasks={tasks.map((task) => ({
|
tasks={tasks.map((task) => ({
|
||||||
...task,
|
...task,
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export function LoginPage() {
|
|||||||
className="w-full rounded-2xl border border-line/70 bg-page px-4 py-3 text-text outline-none transition focus:border-brand"
|
className="w-full rounded-2xl border border-line/70 bg-page px-4 py-3 text-text outline-none transition focus:border-brand"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
{error ? <div className="rounded-2xl border border-red-300 bg-red-50 px-4 py-3 text-sm text-red-700">{error}</div> : null}
|
{error ? <div className="rounded-2xl border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-200 dark:text-red-200">{error}</div> : null}
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
@@ -73,4 +73,3 @@ export function LoginPage() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.documentElement.classList.toggle("dark", mode === "dark");
|
document.documentElement.classList.toggle("dark", mode === "dark");
|
||||||
|
document.documentElement.style.colorScheme = mode;
|
||||||
window.localStorage.setItem(storageKey, mode);
|
window.localStorage.setItem(storageKey, mode);
|
||||||
}, [mode]);
|
}, [mode]);
|
||||||
|
|
||||||
@@ -55,4 +56,3 @@ export function useTheme() {
|
|||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user