Auslastungsberechung angepasst
This commit is contained in:
1 parent
eb71a9160f
commit
eeb2e63f89
12 files changed
+646
-297
No files matched your search
@@ -1,133 +1,132 @@
|
||||
# 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 with API endpoints
|
||||
├── package.json # Dependencies (express, cors)
|
||||
├── 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 # Root HTML file (served at /)
|
||||
│ ├── main.v2.js # Complete frontend client app
|
||||
│ └── styles.css # UI styles
|
||||
├── Docker/
|
||||
│ └── Dockerfile # Node.js Alpine image
|
||||
├── docker-compose.yml # Docker service definition
|
||||
└── sessions/ # Data directory (bind-mounted in Docker)
|
||||
├── ELT/ # Elektro department
|
||||
├── HLS/ # HLS department
|
||||
└── ARCH/ # Architektur department
|
||||
│ ├── 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
|
||||
```
|
||||
|
||||
## Overview
|
||||
|
||||
Single-page app for tracking engineering projects across weekly sessions
|
||||
("Sitzungen"). Vanilla JS/HTML/CSS frontend (`public/`) + Node.js/Express server
|
||||
(`server.js`). No build step, no bundler. Data is persisted as CSV files under
|
||||
`sessions/`.
|
||||
|
||||
## Running
|
||||
|
||||
- `npm install` then `npm start` (executes `node server.js`).
|
||||
- Or with Docker: `docker compose -f Docker/docker-compose.yml up --build`.
|
||||
- Server runs on `PORT` env var or defaults to `3003`; serves `public/Test.html`
|
||||
as root and API under `/load-all`, `/save-csv`, `/delete-csv`.
|
||||
- Frontend assumes the API lives at `window.location.origin`, falling back to
|
||||
`http://localhost:3003` when opened as a local file. Opening `Test.html`
|
||||
directly without a server will load seed/demo data.
|
||||
|
||||
## Operations
|
||||
|
||||
### Local
|
||||
```bash
|
||||
npm install
|
||||
npm start
|
||||
npm start # http://localhost:3003, or PORT=3004 if occupied
|
||||
docker compose up --build # Docker variant (compose file is at repo root)
|
||||
```
|
||||
Access at `http://localhost:3003` (or `PORT=3004` if occupied).
|
||||
|
||||
### Docker
|
||||
```bash
|
||||
docker compose -f Docker/docker-compose.yml up --build
|
||||
```
|
||||
Access at `http://localhost:3003` (or override with `PORT=3004`).
|
||||
- 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: semicolon (`;`)
|
||||
- Encoding: UTF-8 with BOM (`\ufeff`)
|
||||
- Structure:
|
||||
- `sessions/<DEPT>/` contains department data
|
||||
- Week files: `<Label>.csv` (e.g., `KW_30___2026.csv`)
|
||||
- Global people: `_people.csv` with columns `NAME;Rollen;Voller Name`
|
||||
- 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.
|
||||
|
||||
## Data layout
|
||||
- Data is split by department: `sessions/ELT/`, `sessions/HLS/`, `sessions/ARCH/`.
|
||||
Valid departments are hard-coded in `server.js` (`VALID_DEPARTMENTS`) and
|
||||
`main.v2.js` (`DEPARTMENTS`). Unknown department values fall back to `ELT`.
|
||||
- Each week is one CSV file named `<Label>.csv` with `;` delimiter and UTF-8 BOM.
|
||||
- Each department has a global people list in `_people.csv` with columns
|
||||
`NAME;Rollen;Voller Name`. New people are added globally and copied into the
|
||||
current week. Historical weeks keep their own person copies so deleted global
|
||||
people remain visible in old sessions.
|
||||
- The app reads CSVs on startup via `/load-all`; `localStorage` is used only
|
||||
for the last-selected department/tab UI state. Do not store data in
|
||||
`localStorage`.
|
||||
- Editing CSVs by hand is possible but risky; prefer the UI.
|
||||
## Frontend architecture
|
||||
|
||||
## Architecture notes
|
||||
|
||||
- Frontend is in `public/` (`Test.html`, `main.v2.js`, `styles.css`).
|
||||
- `main.v2.js` is the entire client app loaded as a classic non-module `<script>`;
|
||||
all functions are globals on `window`.
|
||||
- State lives in `let db = { weeks: [], currentWeek, globalPeople: {} }`.
|
||||
- Each week object has `projects` (with `assign`, `lph`, `group`, `status`,
|
||||
`order`) and `people` with session-local copies.
|
||||
- `save()` schedules `syncAllToServer()` debounced by 500 ms; this writes **all**
|
||||
weeks plus the global `_people.csv` to the server, not just the changed one.
|
||||
- Weeks are sorted by calendar week extracted from the label (`KW XX / YYYY`),
|
||||
not alphabetically.
|
||||
- `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, so historical weeks are read-only unless explicitly
|
||||
unlocked.
|
||||
- Person IDs are derived from the person's abbreviation/name to stay stable
|
||||
across reloads (`personIdForName` in `main.v2.js`).
|
||||
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
|
||||
|
||||
- Dockerfile uses `node:20-alpine` as base image.
|
||||
- Copies only `package.json/package-lock.json` first (`npm ci --production`), then server files.
|
||||
- Excludes `Docker/`, `node_modules/`, `.git`, `.gitignore` via `.dockerignore`.
|
||||
- Runs as non-root `node` user; `/app/sessions` bind-mounted from host `./sessions`.
|
||||
- HEALTHCHECK on `/health` endpoint; service restarts on failure (`unless-stopped`).
|
||||
- Frontend must be served via HTTP (not `file://`) for CORS to work.
|
||||
- `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`.
|
||||
|
||||
## Security / auth quirk
|
||||
## Conventions & verification
|
||||
|
||||
- `projects` and `people` tabs plus week deletion are gated by a password modal.
|
||||
The check compares `CryptoJS.SHA256(input)` to `PROTECTED_PASSWORD_HASH` in
|
||||
`main.v2.js`. This is client-side only and **not real security**. CryptoJS is
|
||||
loaded from a CDN (`cdnjs.cloudflare.com`) in `Test.html`, so the password
|
||||
gate needs internet access.
|
||||
- User-provided status HTML is sanitized on render (small tag/attribute
|
||||
whitelist) to mitigate XSS while keeping formatting options.
|
||||
- Filenames sent to `/save-csv` and `/delete-csv` are strictly validated server
|
||||
side to prevent path traversal.
|
||||
|
||||
## Conventions
|
||||
|
||||
- UI strings, comments, and CSV content are German. Preserve German in
|
||||
user-facing text.
|
||||
- No linter, formatter, typecheck, or test suite is configured. Verify changes
|
||||
by running the server and exercising the relevant tab in the browser.
|
||||
- This is **not** a git repo. Don't assume git workflows.
|
||||
- Edit `main.v2.js` functions in place; the file is intentionally one large script
|
||||
with German header comments per section. Match that style.
|
||||
|
||||
## Testing & verification
|
||||
|
||||
- No automated test suite exists. Manual verification only:
|
||||
- Start server (`npm start` or Docker)
|
||||
- Open `http://localhost:3003`
|
||||
- Check `/health` endpoint returns `{ok:true}`
|
||||
- Exercise relevant tab in browser (Board, Projects, People)
|
||||
- Verify CSV persistence in `sessions/` directory
|
||||
- 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/`.
|
||||
Reference in new issue
Block a user