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

6.1 KiB

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

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/.