Files
projektuebersicht/AGENTS.md
T
2026-08-13 14:39:07 +02:00

132 lines
6.1 KiB
Markdown

# AGENTS.md
## Overview
Single-page app for tracking engineering projects across weekly sessions
("Sitzungen"). Vanilla JS/HTML/CSS frontend + Node.js/Express server. No build
step, no bundler, no tests. Data is persisted as semicolon-separated CSVs
under `sessions/`.
Two HTML entrypoints, both served statically by Express:
- `Test.html` — the main app (tabs: Projektboard, Auslastung, Projekte,
Projektbeteiligte). Served at `/`.
- `project-plan.html` — per-project Gantt/TODO planning view, opened in a new
tab via `openProjectPlan()` in `main.v2.js:2358`. Loads `main.v2.js` first,
then `project-plan.js`.
All UI strings/comments/CSV content are German. Preserve German in
user-facing text.
## File structure
```
.
├── server.js # Express server + CSV API
├── package.json # deps: express, cors only
├── docker-compose.yml # at repo ROOT; build context ., dockerfile Docker/Dockerfile
├── Docker/Dockerfile # node:20-alpine
├── .dockerignore
├── .vscode/extensions.json
├── public/
│ ├── Test.html # main app (served at /)
│ ├── main.v2.js # entire main client app (~3200 lines)
│ ├── project-plan.html # Gantt/TODO planning view
│ ├── project-plan.js # planning logic (~1500 lines)
│ ├── styles.css
│ └── dein_bild.ico
└── sessions/ # data; bind-mounted into Docker container
├── ELT/ HLS/ ARCH/ # one folder per department
```
## Running
```bash
npm install
npm start # http://localhost:3003, or PORT=3004 if occupied
docker compose up --build # Docker variant (compose file is at repo root)
```
- PORT env var overrides default 3003 in `server.js`.
- Frontend must be served over HTTP: `baseUrl()` in `main.v2.js` uses
`window.location.origin` or falls back to `http://localhost:3003` when opened
via `file://` (then only seed/demo data loads).
- Check `/health` returns `{ok:true}` after starting.
## Server API (`server.js`)
- `GET /` → `Test.html`; `GET /health`.
- `GET /load-all?department=` → `{sessions:[{filename,content}], people}` with
`_people.csv` content in `people`; sessions sorted by filename.
- `POST /save-csv {filename, csv, department}` → writes UTF-8 BOM CSV.
- `POST /delete-csv {filename, department}`.
- Valid departments hard-coded as `ELT|HLS|ARCH` in `server.js`
(`VALID_DEPARTMENTS`); anything else silently falls back to `ELT`. Same set in
`DEPARTMENTS` in `main.v2.js`.
- `safeName()` strictly validates filenames (path traversal protection);
static assets get `Cache-Control: no-store`.
- JSON body limit is 2 MB.
## CSV format
- Delimiter `;`, UTF-8 with BOM. Hand-editing is possible but risky; the parser
reconstructs objects from the fixed column layout.
- Week files `<Label>.csv` (e.g. `KW_30___2026.csv` — `filenameForWeek()`
replaces non-word chars with `_`). Row layout is positional:
`WOCHE` (label, date, locked flag) → `PERSONEN` (`name|role|fullName` per
column) → header row → `ANWESENHEIT` → one row per project.
- `_people.csv` per department: `NAME;Rollen;Voller Name;E-Mail;Abwesenheiten`
(absences as JSON). People are global per department; new people are copied
into the current week. Historical weeks keep their own person copies, so
deleting a global person leaves old sessions unaffected no matter what.
- Don't change the column layout without updating both `weekToCsv()` and
`parseSessionCsv()` (`main.v2.js`), which must stay inverse of each other.
## Frontend architecture
- `main.v2.js` is loaded as a classic non-module `<script>`; **all functions are
globals on `window`** and shared across both pages. Edit functions in place;
the file is one large script with German section-header comments.
- State: `let db = { weeks, currentWeek, globalPeople }` (in-memory only).
- Project IDs are regenerated on every CSV load, so projects are addressed
cross-page by `department` + week `label` + `projNr` + `projName` via URL
params (`openProjectPlan`, `findProjectForPlan`).
- Person IDs are stable across reloads via `personIdForName(name)` →
`p_<lowercased alnum>`.
- `save()` debounces 500 ms then `syncAllToServer()` writes **all** weeks plus
`_people.csv` — not just the changed week. Exception: `project-plan.js`
`savePlan()` bypasses `save()` and writes only the current week's CSV directly
via `sendCsvToServer()`.
- Weeks are sorted by calendar week extracted from the label (`KW XX / YYYY`)
via `weekSortKey()`, not alphabetically/by filename.
- `applyAutoLock()` locks every week except the chronologically last one after
load/clone/new-week; `localStorage` UI state is not restored for locked tabs.
- `localStorage` keys: `projuebersicht_v1` (department only — tab is never
restored), `projuebersicht-theme`, header-collapse key. Do **not** store data
in `localStorage`; it is UI state only.
## Auth / security quirk
- The `projects` and `people` tabs, week deletion, and week unlock are gated by
a password modal comparing `CryptoJS.SHA256(input)` to
`PROTECTED_PASSWORD_HASH` (`main.v2.js`). Client-side only — not real
security. CryptoJS and pdfmake (PDF export) are loaded from
`cdnjs.cloudflare.com` in `Test.html`, so those features need internet access
and won't work offline. `project-plan.html` loads neither.
- User-provided status HTML is sanitized on render via a small whitelist
(`sanitizeStatusHtml` in `main.v2.js`) to mitigate XSS.
## Docker
- `docker compose up --build` from the repo root. Compose maps `3003:3003`,
bind-mounts `./sessions:/app/sessions`, `restart: unless-stopped`.
- Dockerfile: `node:20-alpine`, `npm ci --production`, runs as non-root `node`,
HEALTHCHECK on `/health`. `.dockerignore` excludes `Docker/*` (except
`Docker/Dockerfile`), `node_modules/`, `.git`, `npm-debug.log`.
## Conventions & verification
- Git repo; commits are German (e.g. "PDF Export von A4 auf A3 …"). Remote:
`http://192.168.3.187:3000/erik/projektuebersicht.git`.
- No linter, formatter, typecheck, or test suite. Manual verification only:
start server, open `http://localhost:3003`, exercises the relevant tab,
confirm CSV changes in `sessions/`.