feat: TKB shift scheduler — personnel shift planning web app

Full rewrite of METRO HMG for TKB tunnel department:
- People-based grid (18 TKB + 5 IT), year-long calendar
- Color-coded shift values (4/6/8/12/A/B/D/N/U/O)
- Drag-and-drop cells, multi-cell selection (click/ctrl/shift/drag)
- Right-click context menu with color palette
- Tunnel closure + Metro + D8 info rows (toggleable)
- Czech holidays highlighted with names
- PDF export (2-page A4 landscape, DejaVu font for Czech chars)
- Improvement proposals system
- Sticky headers (vertical + horizontal scroll)
- Cell value filter toggles in legend

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Docker Config Backup
2026-04-02 09:48:38 +02:00
commit b4158d687f
47 changed files with 14185 additions and 0 deletions

View File

@@ -0,0 +1,171 @@
# File Manager + Diff Comparison Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Transform the app from single-schedule editor into a multi-file schedule manager with Excel import/export and visual diff comparison.
**Architecture:** Server stores multiple JSON schedule files in a `schedules/` directory. Each file has metadata (name, created, modified). The UI gets a file list sidebar/header, open/save/save-as/delete operations, Excel import creates new files, Excel export from any file. Diff mode loads two files and renders an overlay showing changed cells.
**Tech Stack:** Express (file-based storage), React (existing), openpyxl (Excel), xlsx (client-side Excel read)
---
### Task 1: Server — File Storage API
**Files:**
- Modify: `server.js`
**Step 1: Create schedules directory and migrate existing data**
Add `schedules/` directory. Each file is `{id}.json` with structure:
```json
{
"id": "abc123",
"name": "HMG_Profylaxe_20260326_D6",
"createdAt": "2026-03-26T10:00:00Z",
"modifiedAt": "2026-03-27T14:00:00Z",
"data": { "dayIndex": [...], "stations": [...], "obstacles": [...], "dayComments": [...] }
}
```
**Step 2: New API endpoints**
Replace single GET/POST `/api/schedule` with:
- `GET /api/files` — List all files (id, name, createdAt, modifiedAt — no data)
- `GET /api/files/:id` — Load single file with full data
- `POST /api/files` — Create new file (from JSON body or Excel import)
- `PUT /api/files/:id` — Update existing file
- `DELETE /api/files/:id` — Delete file
- `POST /api/files/import-excel` — Upload Excel, parse to JSON, create file
- `GET /api/files/:id/export-excel` — Download file as Excel using template
- `GET /api/files/diff/:id1/:id2` — Compute diff between two files, return changed cells
Keep old endpoints as aliases for backward compat (GET/POST `/api/schedule` → operates on a "current" file or the first file).
**Step 3: Migration on first start**
If `saved_schedule.json` exists but `schedules/` is empty, migrate it as the first file.
**Step 4: Diff computation**
Diff endpoint compares two files cell by cell:
```json
{
"added": [{"stationCode": "DE", "dayIdx": 100, "newValue": "k"}],
"removed": [{"stationCode": "DE", "dayIdx": 96, "oldValue": "k"}],
"changed": [{"stationCode": "HR", "dayIdx": 105, "oldValue": 14, "newValue": "Z"}]
}
```
**Step 5: Commit**
---
### Task 2: UI — File List & Management
**Files:**
- Create: `src/FileManager.tsx`
- Modify: `src/App.tsx`
- Modify: `src/types.ts`
**Step 1: Add types**
```typescript
interface ScheduleFile {
id: string
name: string
createdAt: string
modifiedAt: string
}
interface ScheduleFileWithData extends ScheduleFile {
data: ScheduleData
}
```
**Step 2: FileManager component**
A top bar or panel showing:
- List of saved files (name, date modified)
- "Otevřít" (open) button per file
- "Smazat" (delete) button per file
- "Nahrát Excel" (upload Excel) button
- "Nový soubor" (new file) button
- Currently open file name shown prominently
- "Uložit" saves to current file
- "Uložit jako" (save as) creates new file
- Checkbox to select two files for comparison
**Step 3: App.tsx changes**
- Start with file list view (no schedule loaded)
- Opening a file loads data into the editor
- Save writes back to the same file ID
- "Save as" prompts for name, creates new file
- Back button returns to file list
**Step 4: Commit**
---
### Task 3: Excel Import → New File
**Files:**
- Modify: `server.js` (import endpoint)
- Modify: `src/FileManager.tsx`
- Modify: `src/excelIO.ts`
**Step 1: Upload flow**
FileManager has "Nahrát Excel" button → file picker → POST multipart to `/api/files/import-excel` → server parses with Python → creates new schedule file → returns file metadata → UI refreshes list.
Server-side parsing reuses the column mapping logic (col = idx + 7, rows 13-39 for stations). Also extract comments from DEN row.
**Step 2: Commit**
---
### Task 4: Diff Comparison View
**Files:**
- Create: `src/DiffOverlay.tsx`
- Modify: `src/ScheduleTable.tsx`
- Modify: `src/App.tsx`
**Step 1: DiffOverlay component**
When two files are selected for comparison, load both, compute diff, and render overlay on the table:
- **Green background** on cells that exist in current but not in comparison (added)
- **Red background** on cells that exist in comparison but not in current (removed)
- **Yellow background** on cells with different values (changed)
- Legend showing what colors mean
- Toggle to show/hide diff overlay
**Step 2: Diff in ScheduleTable**
Pass optional `diffData` prop to ScheduleTable. When present, each cell checks if it has a diff entry and renders the appropriate overlay color.
**Step 3: Commit**
---
### Task 5: Excel Export per File
**Files:**
- Modify: `server.js`
- Modify: `export_excel.py`
**Step 1: Per-file export**
`GET /api/files/:id/export-excel` loads the specific file's data and runs through the Python template exporter. File downloads with the schedule name in filename.
**Step 2: Commit**
---
### Task 6: Deploy & Test
**Step 1: Build and test locally**
**Step 2: Deploy to copelk**
**Step 3: Migrate existing data on copelk**