6.1 KiB
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 viaopenProjectPlan()inmain.v2.js:2358. Loadsmain.v2.jsfirst, thenproject-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()inmain.v2.jsuseswindow.location.originor falls back tohttp://localhost:3003when opened viafile://(then only seed/demo data loads). - Check
/healthreturns{ok:true}after starting.
Server API (server.js)
GET /→Test.html;GET /health.GET /load-all?department=→{sessions:[{filename,content}], people}with_people.csvcontent inpeople; 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|ARCHinserver.js(VALID_DEPARTMENTS); anything else silently falls back toELT. Same set inDEPARTMENTSinmain.v2.js. safeName()strictly validates filenames (path traversal protection); static assets getCache-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|fullNameper column) → header row →ANWESENHEIT→ one row per project. _people.csvper 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()andparseSessionCsv()(main.v2.js), which must stay inverse of each other.
Frontend architecture
main.v2.jsis loaded as a classic non-module<script>; all functions are globals onwindowand 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+ weeklabel+projNr+projNamevia URL params (openProjectPlan,findProjectForPlan). - Person IDs are stable across reloads via
personIdForName(name)→p_<lowercased alnum>. save()debounces 500 ms thensyncAllToServer()writes all weeks plus_people.csv— not just the changed week. Exception:project-plan.jssavePlan()bypassessave()and writes only the current week's CSV directly viasendCsvToServer().- Weeks are sorted by calendar week extracted from the label (
KW XX / YYYY) viaweekSortKey(), not alphabetically/by filename. applyAutoLock()locks every week except the chronologically last one after load/clone/new-week;localStorageUI state is not restored for locked tabs.localStoragekeys:projuebersicht_v1(department only — tab is never restored),projuebersicht-theme, header-collapse key. Do not store data inlocalStorage; it is UI state only.
Auth / security quirk
- The
projectsandpeopletabs, week deletion, and week unlock are gated by a password modal comparingCryptoJS.SHA256(input)toPROTECTED_PASSWORD_HASH(main.v2.js). Client-side only — not real security. CryptoJS and pdfmake (PDF export) are loaded fromcdnjs.cloudflare.cominTest.html, so those features need internet access and won't work offline.project-plan.htmlloads neither. - User-provided status HTML is sanitized on render via a small whitelist
(
sanitizeStatusHtmlinmain.v2.js) to mitigate XSS.
Docker
docker compose up --buildfrom the repo root. Compose maps3003:3003, bind-mounts./sessions:/app/sessions,restart: unless-stopped.- Dockerfile:
node:20-alpine,npm ci --production, runs as non-rootnode, HEALTHCHECK on/health..dockerignoreexcludesDocker/*(exceptDocker/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 insessions/.