From 3d306e641cf6f04847161c22599e83e05315872b Mon Sep 17 00:00:00 2001 From: Docker Config Backup Date: Thu, 17 Sep 2026 13:02:52 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20add=20Krop=C3=A1=C4=8Dek=20Ond=C5=99ej?= =?UTF-8?q?=20+=20May=E2=80=93Jul=20feature=20work=20(half-cell=20marks,?= =?UTF-8?q?=20copy/paste,=20exports,=20roles)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New worker: Kropáček Ondřej (TKB, part-time student) inserted between Dvořák Václav and Vondrák Pavel in data.json, the full-month Excel template (new row 13, everything below shifted) and the docházka template (13 person rows). Not added to the harmonogram/CIS roster — he has no CIS personal number. Also checkpoints the uncommitted work deployed to production between May and July 2026: - half-cell day/night preference marks (color + text per half) - cell copy/paste (Ctrl+C/V, context menu), incl. from info rows - light/dark theme toggle, TKB people filter, 162 filter - full-month Excel export (template) and harmonogram export for CIS - docházka export: half-time workers, retired flag, contractor handling - third login role "cis" (viewer + export access) - retired flag hides a person from UI/FPD while keeping past data Co-Authored-By: Claude Fable 5.1 --- .../2026-04-22-half-cell-marks-design.md | 132 +++++ samples/Pohotovost_TKB+IT_směny_sample.xlsx | Bin 0 -> 33015 bytes samples/import_harmonogram (2).xlsx | Bin 0 -> 9057 bytes samples/import_harmonogram.xlsx | Bin 0 -> 8920 bytes samples/tkb_web_excel_export_01.png | Bin 0 -> 185462 bytes web/dochazka_template.xlsx | Bin 9282 -> 9247 bytes web/export_dochazka.py | 87 ++-- web/export_full_excel.py | 485 ++++++++++++++++++ web/export_harmonogram.py | 239 +++++++++ web/public/full_template.xlsx | Bin 0 -> 25485 bytes web/server.js | 100 +++- web/src/App.tsx | 235 ++++++++- web/src/ContextMenu.tsx | 270 ++++++++-- web/src/FpdCheck.tsx | 100 +++- web/src/Login.tsx | 12 +- web/src/PdfExport.ts | 137 ++++- web/src/ScheduleTable.tsx | 341 +++++++++--- web/src/Toolbar.tsx | 149 ++++++ web/src/data.json | 7 + web/src/index.css | 51 +- web/src/types.ts | 5 + web/src/useScheduleState.ts | 123 ++++- 22 files changed, 2231 insertions(+), 242 deletions(-) create mode 100644 docs/plans/2026-04-22-half-cell-marks-design.md create mode 100644 samples/Pohotovost_TKB+IT_směny_sample.xlsx create mode 100644 samples/import_harmonogram (2).xlsx create mode 100644 samples/import_harmonogram.xlsx create mode 100644 samples/tkb_web_excel_export_01.png create mode 100644 web/export_full_excel.py create mode 100644 web/export_harmonogram.py create mode 100644 web/public/full_template.xlsx diff --git a/docs/plans/2026-04-22-half-cell-marks-design.md b/docs/plans/2026-04-22-half-cell-marks-design.md new file mode 100644 index 0000000..d604983 --- /dev/null +++ b/docs/plans/2026-04-22-half-cell-marks-design.md @@ -0,0 +1,132 @@ +# Half-cell day/night preference marks — Design + +**Date:** 2026-04-22 +**Status:** approved, ready to implement + +## Problem + +In the original Excel schedule, each day cell was split horizontally +(left = night, right = day). Users marked preferences/obstacles by +filling the appropriate half with a color before shift planning began. + +In the current app, each day = one cell. Users can only express +day-vs-night preferences via cell comments (small blue triangle), which +is not visual at a glance and therefore "annoying." + +Multiple users have requested restoring the Excel behavior. + +## Decisions + +1. **Coexist with shifts** — preference marks stay visible after a shift + is assigned (so we can see whether we honored or violated the + request). +2. **Free color from palette** — same flexibility as the existing + whole-cell color picker; semantics are user-defined. +3. **Horizontal split: left = night, right = day** — matches the + original Excel. +4. **Visual when shift is assigned: corner triangles** — full shift + color fills the cell; small ~8×8 px triangles in the bottom corners + indicate the marks (bottom-left = night, bottom-right = day). +5. **Toggle in context menu** — segmented control `Celá | Noční | Denní` + above the color palette selects which field the next color click + updates. + +## Data model + +`CellValue` (in `types.ts`) gains two optional fields: + +```ts +interface CellValue { + v?: string + color?: string // existing — full-cell background + dayColor?: string // NEW — right half / day mark + nightColor?: string // NEW — left half / night mark +} +``` + +The change is purely additive. Existing JSON files load unchanged. + +## Rendering rules + +| State | Look | +|-------|------| +| empty + no marks | empty (current) | +| `dayColor` and/or `nightColor` set, no shift | left half = `nightColor`, right half = `dayColor` (each independent; missing half = transparent or `color` fallback) | +| shift assigned, no marks | full shift color (current) | +| shift assigned + marks | full shift color + 8 px triangles: bottom-left = night, bottom-right = day | + +Priority for picking renderer: + +1. If `dayColor || nightColor` → split-half mode +2. Else if `color` → full-cell color (current behavior) +3. Else → empty + +## Interaction + +Right-click cell → existing context menu, with new segmented toggle +above the color palette: + +``` +Barva: + [ Celá ] [ Noční ] [ Denní ] ← default: Celá + ● ● ● ● ● ● ● [Bez barvy] +``` + +- Toggle selects target field (`color`, `nightColor`, `dayColor`) +- Color click writes only to the selected target +- "Bez barvy" clears only the selected target +- Three fields are independent + +## Drag-and-drop + +Half marks are tied to the **day**, not the shift. When a user drags a +shift cell: + +- Only `v` (and `color` of the shift) moves to the destination +- `dayColor` / `nightColor` stay on both source and destination days + +## Compare / diff view + +Differences in `dayColor` / `nightColor` count as cell changes — render +the same orange "modified" border as for value changes. + +## Filters + +- "Color only" filter (rainbow dot) — also hides cells whose only + content is `dayColor` / `nightColor` (no `v`) +- Hidden value filter (e.g. hide "A") — hides the cell entirely + including marks (marks are auxiliary) + +## Scope + +Half-mark editing is available on: + +- ✅ Person cells (Pohotovost TKB / Pohotovost IT) +- ✅ Info rows (TKB / SAZLT / Metro / D8) +- ❌ PMS columns (month-end summary, doesn't make sense) + +## Read-only + +Existing `isReadOnly` guard handles viewer mode automatically. Viewers +see marks but cannot edit. + +## Out of scope (phase 1) + +- **Excel export/import** — original template has no clean + representation; round-trip would lose marks. Documented as a known + limitation; revisit if users request it. +- **PDF export** — will render half marks using same rules (no extra + work expected). + +## Files to change + +1. `web/src/types.ts` — extend `CellValue` +2. `web/src/useScheduleState.ts` — add `setCellHalfColor(personId, + dayIdx, target, color)` reducer + undo support +3. `web/src/ScheduleTable.tsx` — render split halves / corner triangles + in `PersonRow` and `renderInfoRowCells` +4. `web/src/ContextMenu.tsx` — add segmented toggle, route color clicks + to selected target +5. `web/src/cellColors.ts` — no change + +Estimate: ~150 LoC across 4 files plus minimal CSS. diff --git a/samples/Pohotovost_TKB+IT_směny_sample.xlsx b/samples/Pohotovost_TKB+IT_směny_sample.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..88b0c347254dac59400784ca48bce9f0a02c6eda GIT binary patch literal 33015 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 N!?(V8FSAK^pwPbMB|dd(ZpF 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 H<=g)NZL_>6 literal 0 HcmV?d00001 diff --git a/samples/import_harmonogram.xlsx b/samples/import_harmonogram.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..128a0e12d93c8cd67f3e5b3e61b30cc70bc2cbe3 GIT binary patch literal 8920 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 a|A#+Pl7)X%3@9kH#~0$Gj zAT{)0=q-^NTHycCbMDOCnLBs>x#zC+&-%v|^Ce$-+urZq&wlnMp%3rhIeVJp^r1tC z&fdGLs&nYj3H+f$M=+;O0B86wR^|YQ!|pnFZXQD6FU8l#{BMI za(k3|yg^bsWKS!?kD4itM|4S5~UsEm!VNQTL~6{*!*Y@gZR~(iIp2e3lKtP zrU!EZcw###4kFx=6?5Ge_BN(l8$ya$a5VVK3WkYze|maDlpgb= z;~#HbZCuTl(^0s`6HLh~a#U220Jzg7J&4GJ<*MsFA=9TZAaq=^w)RI|a657ynX z5QfQk%(@Yzg@m{o`0@Wnysyr2y;^jl(bwI2b;jEGar|GF@yCiiKY8BP_+rA(lX)Me z&T(9Sq4xOhfePfxR>DF~0?Tbq{^elh1T#&Qff41+PMx!Pqd0%|s(fZ>jR`zkA2N6e zJKu3b$#>1e%+nON-l5V`fyl3&#DZiX^*xnoHD+9$vbOLTH$b3@#5q@F=nQ6XVgOe^TcXWQoh3x zb?PA6b1gqlW!f1&eL|uxD?T}LZ&k)JZ3#P|Na66<*sSh#-

EMqw)o#HIV^wF+I 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 Y_;pO>OS6=}fqz=3^-txW_~pv~0qkgOV*mgE literal 0 HcmV?d00001 diff --git a/web/dochazka_template.xlsx b/web/dochazka_template.xlsx index f875237798d1855eade73f9356682059531be91f..166b1c0d30f81a33e3a891c7e1706aa0e46eb989 100644 GIT binary patch delta 4830 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 GApZlT?_cx) delta 4844 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 diff --git a/web/export_dochazka.py b/web/export_dochazka.py index 6b9fbc9..2ff5fcf 100644 --- a/web/export_dochazka.py +++ b/web/export_dochazka.py @@ -26,7 +26,10 @@ STRAVNE_8H = 155 # meal allowance for an 8h workday DAN = 0.15 # tax factor → 1.15 multiplier INTERNET = 500 # Janouš Petr monthly internet reimbursement -# Template name list — rows 8-19 of dochazka_template.xlsx. +# Part-time people: 4h/day instead of 8h → half FPD target, D/N/O deduct 4h. +HALF_TIME_NAMES = {'Dittrich Vladimír'} + +# Template name list — rows 8-20 of dochazka_template.xlsx. # Only these people are exported, in this order. TEMPLATE_NAMES = [ 'Dittrich Vladimír', @@ -35,6 +38,7 @@ TEMPLATE_NAMES = [ 'Vörös Pavel', 'Janouš Petr', 'Dvořák Václav', + 'Kropáček Ondřej', 'Toman Milan', 'Hanzlík Marek', 'Vondrák Pavel', @@ -48,6 +52,7 @@ PERSON_DATA = { 'Žemlička Miroslav': {'auto': 'AUS', 'km_tkb': 40, 'km_sat': 0}, 'Franek Lukáš': {'auto': 'AUS', 'km_tkb': 40, 'km_sat': 0}, 'Dvořák Václav': {'auto': 'AUS', 'km_tkb': 0, 'km_sat': 0}, + 'Kropáček Ondřej': {'auto': 'AUS', 'km_tkb': 0, 'km_sat': 0}, # TODO confirm auto/km 'Toman Milan': {'auto': 'AUS', 'km_tkb': 0, 'km_sat': 0}, 'Kohl David': {'auto': 'AUS', 'km_tkb': 0, 'km_sat': 0}, 'Milisavljevič Jovica':{'auto': 'AUS', 'km_tkb': 0, 'km_sat': 0}, @@ -75,12 +80,12 @@ PERSON_DATA = { # Template layout (1-based row numbers) TPL_FIRST = 8 # first data row -TPL_LAST = 19 # last data row (12 people) -TPL_CELKEM = 20 -TPL_HIDDEN1 = 22 -TPL_HIDDEN2 = 23 -TPL_IT_HDR = 24 -TPL_IT1 = 25 # first IT person +TPL_LAST = 20 # last data row (13 people) +TPL_CELKEM = 21 +TPL_HIDDEN1 = 23 +TPL_HIDDEN2 = 24 +TPL_IT_HDR = 25 +TPL_IT1 = 26 # first IT person # ────────────────────────────────────────────────────────────── def get_czech_holidays(year): @@ -132,37 +137,54 @@ def compute(schedule, month, year): fpd = work_days * 8 tkb_results = [] - # Only include people that are in the template — in template order + # Only include people that are in the template — in template order. + # Retired people are skipped for months where they have no data. tkb_by_name = {p['name']: p for p in people if p.get('group')=='TKB'} - tkb_people = [tkb_by_name[name] for name in TEMPLATE_NAMES if name in tkb_by_name] + def _has_month_data(p): + return any((p['data'].get(str(d['idx'])) or {}).get('v') for d in month_days) + tkb_people = [] + for name in TEMPLATE_NAMES: + p = tkb_by_name.get(name) + if not p: continue + if p.get('retired') and not _has_month_data(p): continue + tkb_people.append(p) for person in tkb_people: + daily_hours = 4 if person['name'] in HALF_TIME_NAMES else 8 r=o=n=0; dov=nem=otc=0; minus_fpd=0 sichet_12h = 0 # shifts with ≥12 h sichet_under_12h = 0 # shifts with <12 h uzavery = 0 # 'U' days (closures) for d in month_days: - v = (person['data'].get(str(d['idx'])) or {}).get('v', '') + cell = person['data'].get(str(d['idx'])) or {} is_w = (not d['weekend'] and Date(d['year'],d['month'],d['day']) not in holidays) - if v=='A': - dr,do=split_day(12); r+=dr; o+=do - sichet_12h += 1 - elif v=='B': - nr,no,nn=split_night(12); r+=nr; o+=no; n+=nn - sichet_12h += 1 - elif v=='D': dov+=1; minus_fpd += 8 if is_w else 0 - elif v=='N': nem+=1; minus_fpd += 8 if is_w else 0 - elif v=='O': otc+=1; minus_fpd += 8 if is_w else 0 - elif v=='U': uzavery += 1 - elif v not in ('x','U','') and v: - try: - h=float(v) - if h>0: - dr,do=split_day(h); r+=dr; o+=do - if h >= 12: sichet_12h += 1 - else: sichet_under_12h += 1 - except (ValueError,TypeError): pass + # Split cells carry two half-day values (dayValue + nightValue); + # process both halves, otherwise the single 'v'. + is_split = bool(cell.get('dayColor') or cell.get('nightColor') + or cell.get('dayValue') or cell.get('nightValue')) + values = ([cell.get('dayValue',''), cell.get('nightValue','')] + if is_split else [cell.get('v','')]) + for v in values: + if not v: continue + if v=='A': + dr,do=split_day(12); r+=dr; o+=do + sichet_12h += 1 + elif v=='B': + nr,no,nn=split_night(12); r+=nr; o+=no; n+=nn + sichet_12h += 1 + elif v=='D': dov+=1; minus_fpd += daily_hours if is_w else 0 + elif v=='N': nem+=1; minus_fpd += daily_hours if is_w else 0 + elif v=='O': otc+=1; minus_fpd += daily_hours if is_w else 0 + elif v=='U': uzavery += 1 + elif v not in ('x','U',''): + try: + h=float(v) + if h>0: + dr,do=split_day(h); r+=dr; o+=do + if h >= 12: sichet_12h += 1 + else: sichet_under_12h += 1 + except (ValueError,TypeError): pass # PMS stored at negative dayIdx = -month # PMS = práce mimo směnu: hours added to R/O but NOT counted as shifts @@ -176,7 +198,8 @@ def compute(schedule, month, year): except (ValueError,TypeError): pass total = r+o+n - o_navic = total - fpd + minus_fpd + person_fpd = work_days * daily_hours + o_navic = total - person_fpd + minus_fpd r_fpd, o_fpd, n_fpd = r, o, n if o_navic > 0: pom = o_navic @@ -192,7 +215,7 @@ def compute(schedule, month, year): # --- Financial calculations (sichtovnice.py) --- # Per-person FPD days (adjusted for D/N/O on working days) - person_fpd_dny = work_days - (minus_fpd / 8) + person_fpd_dny = work_days - (minus_fpd / daily_hours) # R (Jine1): meal allowance supplement # 1.15 × (sichet_12h × 236 + sichet_<12h × 155 − fpd_per_person × 155) @@ -394,7 +417,7 @@ def generate_excel(data): rows = {int(r.get('r')): r for r in sd.findall(f'{{{NS}}}row')} n_people = len(data['tkb']) - n_tmpl = TPL_LAST - TPL_FIRST + 1 # 12 + n_tmpl = TPL_LAST - TPL_FIRST + 1 # 13 # ── ROW 3: month metadata ── r3 = rows[3] @@ -403,7 +426,7 @@ def generate_excel(data): set_cell_num(r3, 7, 3, data['work_days']) # G3 set_cell_num(r3, 8, 3, data['fpd']) # H3 - # ── DATA ROWS 8–19 ── + # ── DATA ROWS 8–20 ── for i in range(n_tmpl): row_num = TPL_FIRST + i r = rows[row_num] diff --git a/web/export_full_excel.py b/web/export_full_excel.py new file mode 100644 index 0000000..a790f89 --- /dev/null +++ b/web/export_full_excel.py @@ -0,0 +1,485 @@ +"""Monthly Excel export matching the TKB sample format. + +Reads JSON {schedule, month, year} from stdin, writes xlsx bytes to stdout. + +Layout (preserved from samples/Pohotovost_TKB+IT_směny_2026_03_Březen.xlsx): +- 34 day-pair columns (cols 2-69, even=day half, odd=night half) +- Each export starts 3 days before the 1st of the requested month +- Persons matched by name to fixed template rows +- TKB closures → merged R3:R5 cell across day-pair +- SAZLT + D8 closures → row 1, comma-separated +- METRO closures → ignored +""" +import sys +import json +from copy import copy +from io import BytesIO +from datetime import date, timedelta +from openpyxl import load_workbook +from openpyxl.styles import PatternFill, Alignment, Border, Side, Font +from pathlib import Path + + +CELL_COLORS = { + '4': 'F1F8E9', '6': 'E8F5E9', '8': 'CCFFCC', + '12': 'A3D977', '16': '66BB6A', '24': '2E7D32', + 'A': 'FFE082', 'B': 'B0BEC5', + 'D': 'FFFF00', 'D/2': 'FFF9C4', + 'N': 'FF4444', 'U': '92D050', + 'O': 'FFC000', 'x': 'E0E0E0', +} + +# Czech weekday names indexed by Python's weekday() (Monday=0) +CZECH_DAYS = ['Pondělí', 'Úterý', 'Středa', 'Čtvrtek', 'Pátek', 'Sobota', 'Neděle'] + +# Person name → row in template (must match template content) +TKB_ROWS = { + 'Pauzer Libor': 7, 'Vörös Pavel': 8, 'Janouš Petr': 9, + 'Franek Lukáš': 10, 'Svoboda Daniel': 11, 'Dvořák Václav': 12, + 'Kropáček Ondřej': 13, + 'Vondrák Pavel': 14, 'Čeleda Olda': 15, 'Hanzlík Marek': 16, + 'Kohl David': 17, 'Dittrich Vladimír': 18, 'Toman Milan': 19, + 'Zábranský Petr': 20, 'Žemlička Miroslav': 21, 'Teslík Hynek': 22, +} +IT_ROWS = { + 'Vörös Pavel': 24, 'Janouš Petr': 25, 'Glaser Ondřej': 26, + 'Robert Štefan': 27, 'Franek Lukáš': 28, +} +# Template layout below the person rows +IT_HEADER_ROW = 23 # 'Pohotovost IT' label row +IT_FIRST_ROW, IT_LAST_ROW = 24, 28 +BELOW_IT_ROW = 29 # first row under the IT section + +# Day-pair start columns: 2, 4, 6, ..., 68 (34 day-pairs) +DAY_COLS = list(range(2, 70, 2)) + +# 162 group — excluded from FPD calculation (matches FpdCheck.tsx) +FPD_EXCLUDED = {'Pauzer Libor', 'Vörös Pavel', 'Janouš Petr', 'Franek Lukáš', 'Svoboda Daniel'} + + +def _easter_monday(year): + a = year % 19 + b = year // 100 + c = year % 100 + d = b // 4 + e = b % 4 + f = (b + 8) // 25 + g = (b - f + 1) // 3 + h = (19 * a + b - d - g + 15) % 30 + i = c // 4 + k = c % 4 + l = (32 + 2 * e + 2 * i - h - k) % 7 + m = (a + 11 * h + 22 * l) // 451 + month = (h + l - 7 * m + 114) // 31 + day = ((h + l - 7 * m + 114) % 31) + 1 + sunday = date(year, month, day) + return sunday + timedelta(days=1) + + +def czech_holidays(year): + em = _easter_monday(year) + gf = em - timedelta(days=3) + fixed = [(1, 1), (5, 1), (5, 8), (7, 5), (7, 6), (9, 28), + (10, 28), (11, 17), (12, 24), (12, 25), (12, 26)] + out = {(em.month, em.day), (gf.month, gf.day)} + for m, d in fixed: + out.add((m, d)) + return out + + +def hours_for(value): + """Match webapp's getHoursForValue (FpdCheck.tsx): + A and B count as 12 hours; numeric values as themselves; + everything else (U, D, N, x, O) counts as 0.""" + if value in ('A', 'B'): + return 12 + try: + n = float(value) + return n if n > 0 else 0 + except (ValueError, TypeError): + return 0 + + +def main(): + payload = json.loads(sys.stdin.read()) + schedule = payload['schedule'] + month = int(payload['month']) + year = int(payload['year']) + + # Show 3 days before the 1st of the month, then 34 days total + start = date(year, month, 1) - timedelta(days=3) + days = [start + timedelta(days=i) for i in range(34)] + + template_path = Path(__file__).parent / 'public' / 'full_template.xlsx' + wb = load_workbook(str(template_path)) + ws = wb['Plán Služeb'] + + # ---- A1: replace template "01.03.2026 v.1" with the export's month ---- + ws.cell(1, 1).value = f'01.{month:02d}.{year} v.1' + + # ---- Step 0: Strip ALL leftover cell comments from template ---- + for row in ws.iter_rows(): + for cell in row: + if cell.comment: + cell.comment = None + + # Snapshot original PMS column (BU) fills+borders BEFORE any modifications. + # These represent the row's "default" formatting for PMS, used later for FPD column. + pms_original_styles = {} + for r in range(7, BELOW_IT_ROW): + cell = ws.cell(r, 71) + pms_original_styles[r] = (copy(cell.fill), copy(cell.border)) + + # ---- Step 1: Unmerge ALL cells in R3:R5 across day-pair cols (clean slate) ---- + for m in list(ws.merged_cells.ranges): + if m.min_row >= 3 and m.max_row <= 5 and 2 <= m.min_col < 70: + ws.unmerge_cells(str(m)) + # Clear values + fills in R3:R5 cols 2-69. Restore consistent borders: + # - no border between day half and night half within a day-pair + # - month boundary marked with thicker (medium) left border on the day half + # We compute month-boundary based on the requested month dates set later. + # For now, just clear and apply baseline thin borders; medium override happens after dates are computed. + for r in (3, 4, 5): + for c in range(2, 70): + cell = ws.cell(r, c) + cell.value = None + cell.fill = PatternFill(fill_type=None) + + # ---- Step 2: Clear values in person rows. Replace RGB fills (sample shifts) with the + # row's "default" fill (taken from the col-1 label cell) so empty cells inherit the + # row's background instead of going white. ---- + person_rows = list(TKB_ROWS.values()) + list(IT_ROWS.values()) + for r in person_rows: + # Default row fill: from col-1 label cell (typically theme-colored) + try: + row_default_fill = copy(ws.cell(r, 1).fill) + except Exception: + row_default_fill = None + for c in range(2, 72): + # Cols 71 (PMS) and 72 (FPD) keep their template fills — handled separately + if c >= 71: + continue + cell = ws.cell(r, c) + if cell.__class__.__name__ == 'MergedCell': + continue + cell.value = None + # Col 70 (BR) is the separator column before "Práce mimo směnu" — keep white + if c == 70: + cell.fill = PatternFill(fill_type=None) + continue + try: + fg = cell.fill.fgColor + # If cell has an RGB fill (sample shift highlight), replace with row default + if fg and getattr(fg, 'type', None) == 'rgb': + if row_default_fill is not None: + cell.fill = copy(row_default_fill) + else: + cell.fill = PatternFill(fill_type=None) + except Exception: + pass + + # ---- Step 3: Clear row 1 closure labels in day-pair cols ---- + for m in list(ws.merged_cells.ranges): + if m.min_row == 1 and m.max_row == 1 and 2 <= m.min_col < 70: + ws.unmerge_cells(str(m)) + for c in range(2, 70): + cell = ws.cell(1, c) + cell.value = None + + # ---- Step 4: Update headers (R2 weekday, R3 year, R4 month, R5 day) + borders + weekend fills ---- + thin = Side(style='thin', color='000000') + medium = Side(style='medium', color='000000') + WEEKEND_FILL = PatternFill(start_color='FCD5B4', end_color='FCD5B4', fill_type='solid') # peach + NO_FILL = PatternFill(fill_type=None) + for i, day in enumerate(days): + col = DAY_COLS[i] + ws.cell(2, col).value = CZECH_DAYS[day.weekday()] + ws.cell(3, col).value = day.year + ws.cell(4, col).value = day.month + ws.cell(5, col).value = day.day + + is_month_start = (day.day == 1) + is_weekend = day.weekday() >= 5 # Sat=5, Sun=6 + for r in (3, 4, 5): + # Day half (left): left border (medium at month boundary, else thin), no right border + ws.cell(r, col).border = Border( + left=medium if is_month_start else thin, + right=None, + top=thin if r == 3 else None, + bottom=thin if r == 5 else None, + ) + # Night half (right): no left border, thin right border + ws.cell(r, col + 1).border = Border( + left=None, + right=thin, + top=thin if r == 3 else None, + bottom=thin if r == 5 else None, + ) + # Apply weekend fill (or clear for weekday) + ws.cell(r, col).fill = WEEKEND_FILL if is_weekend else NO_FILL + ws.cell(r, col + 1).fill = WEEKEND_FILL if is_weekend else NO_FILL + # R2 (weekday name) — same peach as R3-R5 for weekend + ws.cell(2, col).fill = WEEKEND_FILL if is_weekend else NO_FILL + ws.cell(2, col + 1).fill = WEEKEND_FILL if is_weekend else NO_FILL + + # Build dayIndex lookup by date + by_date = {} + for d in schedule['dayIndex']: + by_date[(d['year'], d['month'], d['day'])] = d + + # ---- Step 5: SAZLT + D8 closures into row 1 (comma-separated) ---- + info_rows = schedule.get('infoRows') or {} + sazlt_by_idx = {x['dayIdx']: x['text'] for x in (info_rows.get('sazlt') or [])} + d8_by_idx = {x['dayIdx']: x['text'] for x in (info_rows.get('d8') or [])} + + for i, day in enumerate(days): + di = by_date.get((day.year, day.month, day.day)) + if not di: + continue + parts = [] + s = sazlt_by_idx.get(di['idx']) + d = d8_by_idx.get(di['idx']) + if s: parts.append(s) + if d: parts.append(d) + if parts: + col = DAY_COLS[i] + ws.cell(1, col).value = ', '.join(parts) + # Merge across the day-pair so the label spans both day + night cols + ws.merge_cells(start_row=1, start_column=col, end_row=1, end_column=col + 1) + + # ---- Step 6: TKB closures: merged R3:R5 of NIGHT half (odd col), rotated text + bg color. Date in DAY half stays. ---- + tunnel_closures = {tc['dayIdx']: tc['text'] for tc in (schedule.get('tunnelClosures') or [])} + tunnel_colors = {tc['dayIdx']: tc.get('color') for tc in (schedule.get('tunnelClosures') or [])} + DEFAULT_TKB_BG = 'FFC000' # orange — matches predominant style in sample + for i, day in enumerate(days): + di = by_date.get((day.year, day.month, day.day)) + if not di: + continue + tkb = tunnel_closures.get(di['idx']) + if tkb: + night_col = DAY_COLS[i] + 1 + ws.merge_cells(start_row=3, start_column=night_col, end_row=5, end_column=night_col) + cell = ws.cell(3, night_col) + cell.value = tkb + cell.alignment = Alignment(text_rotation=90, horizontal='center', vertical='center') + color = tunnel_colors.get(di['idx']) or DEFAULT_TKB_BG + color = color.lstrip('#').upper() + cell.fill = PatternFill(start_color=color, end_color=color, fill_type='solid') + cell.border = Border(left=None, right=thin, top=thin, bottom=thin) + + # ---- Step 7: Fill person values ---- + # Translation rules per group: + # ALL groups: A → 12 (day half), B → 12 (night half) + # IT only: 16 → day=4, night=12 ; 24 → day=12, night=12 + def translate(value, is_it): + """Convert app shift code → (day_value, night_value) for excel cells. + Returns (day, night) where each is the value to write or None.""" + if value is None or value == '': + return (None, None) + if value == 'A': + return ('12', None) + if value == 'B': + return (None, '12') + if value == 'U': + return (None, 'U') # uzávěra is always at night + if is_it: + if value == '16': + return ('4', '12') + if value == '24': + return ('12', '12') + return (value, None) # default: in day half + + def write_cell(row, col, val, text_color=None, custom_bg=None): + if val in (None, '') and not custom_bg: + return + cell = ws.cell(row, col) + is_numeric = False + if val not in (None, ''): + try: + n = int(val) + cell.value = n + is_numeric = True + except (ValueError, TypeError): + cell.value = val + # Background priority: custom color from app > shift color (only for letter codes). + bg = None + if custom_bg: + bg = custom_bg.lstrip('#').upper() + elif val not in (None, '') and not is_numeric: + bg = CELL_COLORS.get(str(val)) + if bg: + cell.fill = PatternFill(start_color=bg, end_color=bg, fill_type='solid') + # Apply text color (red for VN persons, blue for NN persons) + if text_color: + base = cell.font + cell.font = Font( + name=base.name, size=base.size, bold=base.bold, italic=base.italic, + color=text_color, + ) + + for person in schedule['people']: + is_tkb = person.get('group') == 'TKB' + is_it = not is_tkb + rows_map = TKB_ROWS if is_tkb else IT_ROWS + row = rows_map.get(person['name']) + if not row: + continue + # Text color based on qualification: VN→red, NN→blue + note = (person.get('note') or '').strip() + text_color = None + if note == 'VN': + text_color = 'FFD32F2F' + elif note == 'NN': + text_color = 'FF1565C0' + + for i, day in enumerate(days): + di = by_date.get((day.year, day.month, day.day)) + if not di: + continue + cd = person['data'].get(str(di['idx'])) + if not cd: + continue + day_col = DAY_COLS[i] + night_col = day_col + 1 + + # Apply translations to whole-cell value, half values are explicit + day_v = cd.get('dayValue') + night_v = cd.get('nightValue') + + if day_v is None and night_v is None and cd.get('v'): + # Legacy whole-cell value — translate + day_v, night_v = translate(cd.get('v'), is_it) + else: + # Explicit halves — translate each independently if needed + if day_v in ('A',): day_v = '12' + if night_v in ('B',): night_v = '12' + if is_it: + if day_v == '16': day_v, _ = ('4', None); night_v = night_v or '12' + if night_v == '16': night_v, _ = ('12', None); day_v = day_v or '4' + if day_v == '24': day_v, _ = ('12', None); night_v = night_v or '12' + if night_v == '24': night_v = '12'; day_v = day_v or '12' + + # Custom background colors from webapp (per-half override > whole-cell) + day_bg = cd.get('dayColor') or cd.get('color') + night_bg = cd.get('nightColor') or cd.get('color') + + write_cell(row, day_col, day_v, text_color, custom_bg=day_bg) + write_cell(row, night_col, night_v, text_color, custom_bg=night_bg) + + # PMS: data is stored at person.data[String(-month)] for the requested month + pms = person['data'].get(str(-month)) + if pms and pms.get('v'): + write_cell(row, 71, pms['v'], text_color) + + # ---- Step 8: FPD column at C72 — header + per-person diff (workedHours - FPD) ---- + holidays = czech_holidays(year) + work_days = sum( + 1 for d in schedule['dayIndex'] + if d['year'] == year and d['month'] == month + and not d.get('weekend') + and (d['month'], d['day']) not in holidays + ) + month_fpd = work_days * 8 + + # Unmerge any leftover merges in col 72 + for m in list(ws.merged_cells.ranges): + if m.min_col <= 72 <= m.max_col and m.max_row >= 7: + ws.unmerge_cells(str(m)) + + # Match col 72 width to col 71 (PMS) + pms_col_dim = ws.column_dimensions.get('BS') + if pms_col_dim and pms_col_dim.width: + ws.column_dimensions['BT'].width = pms_col_dim.width + + # Strip formatting on the IT section of PMS/FPD cols and adjacent boundaries + NO_BORDER = Border() + for r in range(IT_FIRST_ROW, BELOW_IT_ROW): + for c in (70, 71, 72, 73): + cell = ws.cell(r, c) + if c in (71, 72): + cell.fill = PatternFill(fill_type=None) + cell.border = NO_BORDER + else: + # Adjacent cols: only clear the side facing PMS/FPD + b = cell.border + if c == 70: + cell.border = Border(left=b.left, right=Side(), top=b.top, bottom=b.bottom) + elif c == 73: + cell.border = Border(left=Side(), right=b.right, top=b.top, bottom=b.bottom) + # Clear bleed-in from rows 22 (above IT) and 28 (below IT) on BS/BT + for c in (71, 72): + b = ws.cell(IT_HEADER_ROW, c).border + ws.cell(IT_HEADER_ROW, c).border = Border(left=b.left, right=b.right, top=b.top, bottom=Side()) + ws.cell(BELOW_IT_ROW, c).border = NO_BORDER + + # Header at C72 (BT) — copy style from C73 (PMS), set text "FPD", merge R2:R6 + src_header = ws.cell(2, 71) + dst_header = ws.cell(2, 72) + dst_header.value = 'FPD' + dst_header.alignment = copy(src_header.alignment) + dst_header.font = copy(src_header.font) + dst_header.fill = copy(src_header.fill) + dst_header.border = copy(src_header.border) + try: + ws.merge_cells(start_row=2, start_column=72, end_row=6, end_column=72) + except Exception: + pass + + # Per-person FPD diff (calculate for ALL TKB persons, including 162 group) + for person in schedule['people']: + if person.get('group') != 'TKB': + continue + row = TKB_ROWS.get(person['name']) + if not row: + continue + worked = 0.0 + vacation = 0.0 # D = 8h, D/2 = 4h — reduces person's FPD target + def collect(v): + nonlocal worked, vacation + if not v: + return + if v == 'D': + vacation += 8 + elif v == 'D/2': + vacation += 4 + else: + worked += hours_for(v) + for d in schedule['dayIndex']: + if d['year'] != year or d['month'] != month: + continue + cd = person['data'].get(str(d['idx'])) + if not cd: + continue + is_split = bool(cd.get('dayColor') or cd.get('nightColor') + or cd.get('dayValue') or cd.get('nightValue')) + if is_split: + collect(cd.get('dayValue')) + collect(cd.get('nightValue')) + else: + collect(cd.get('v')) + person_fpd = month_fpd - vacation + diff = worked - person_fpd + dst = ws.cell(row, 72) + src_pms = ws.cell(row, 71) + dst.value = int(diff) if diff == int(diff) else round(diff, 1) + dst.alignment = Alignment(horizontal='center', vertical='center') + # Center PMS values too + src_pms.alignment = Alignment(horizontal='center', vertical='center') + # Copy ORIGINAL PMS fill + border (snapshot before U/PMS values were written) + orig_fill, orig_border = pms_original_styles.get(row, (None, None)) + if orig_fill is not None: + dst.fill = orig_fill + if orig_border is not None: + dst.border = orig_border + # Force black text (no VN/NN red/blue in FPD column) + base = dst.font + dst.font = Font(name=base.name, size=base.size, bold=base.bold, + italic=base.italic, color='FF000000') + + out = BytesIO() + wb.save(out) + sys.stdout.buffer.write(out.getvalue()) + + +if __name__ == '__main__': + main() diff --git a/web/export_harmonogram.py b/web/export_harmonogram.py new file mode 100644 index 0000000..0b6bc3e --- /dev/null +++ b/web/export_harmonogram.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python3 +""" +export_harmonogram.py +Reads JSON from stdin: {"schedule": {...}, "month": N, "year": N} +Writes an XLSX (to stdout) in the import format expected by the internal +attendance system. Layout matches samples/import_harmonogram.xlsx: + + - Sheet "Harmonogram": + Row 1: dates as 'YYYY-MM-DD' starting at column C + Col A: person name (with "(VN)"/"(NN)" suffix where applicable) + Col B: personal ID number + Cells: comma-separated shift codes from the Definice sheet + - Sheet "Definice": copy of the canonical key/time table. + +App → attendance code mapping: + A → R12 (12h day shift) + B → N12 (12h night shift) + 8 → R (8h day shift) + 12 → R12 + D → D12 (vacation, paid as 12h day shift in CIS) + U → skipped (closure work; handled elsewhere) + N/O / x → skipped (absences; handled elsewhere) + IT 16 → "POH_B1,R,POH_B2" (weekday on-call) + IT 24 → "POH_B3" (weekend full-day on-call) +""" +import sys +import json +import io +from datetime import date as Date, time as Time, timedelta +from openpyxl import Workbook +from openpyxl.utils import get_column_letter + +# ── Fixed roster — TKB shift workers only ── +# Output rows ALWAYS appear in this exact order, regardless of whether +# the person has any data this month. Format: (display_name, group, person_id, +# data_lookup_name) where data_lookup_name is how the person is named in the +# schedule data (may differ from display_name). +# Note: IT on-call people (Glaser, Janouš, Štefan, Vörös) are intentionally +# excluded — they're handled outside the CIS attendance import. +# Part-time people: half the monthly FPD (4h/day instead of 8h). +HALF_TIME_NAMES = {'Dittrich Vladimír'} + +ROSTER = [ + ('Dvořák Václav (VN)', 'TKB', 1247, 'Dvořák Václav'), + # Kropáček Ondřej — part-time student, not in CIS (no personal number); add here + # if he joins full time: ('Kropáček Ondřej', 'TKB', , 'Kropáček Ondřej'), + ('Vondrák Pavel (NN)', 'TKB', 454, 'Vondrák Pavel'), + ('Čeleda Olda (NN)', 'TKB', 463, 'Čeleda Olda'), + ('Hanzlík Marek (VN)', 'TKB', 1366, 'Hanzlík Marek'), + ('Kohl David (VN)', 'TKB', 1007, 'Kohl David'), + ('Dittrich Vladimír (VN)', 'TKB', 373, 'Dittrich Vladimír'), + ('Toman Milan (VN)', 'TKB', 157, 'Toman Milan'), + ('Žemlička Miroslav (NN)', 'TKB', 1827, 'Žemlička Miroslav'), + ('Teslík Hynek (NN)', 'TKB', 738, 'Teslík Hynek'), +] + +# ── Definice sheet (verbatim from sample) ── +# (key, key_k2, time_from, from_24plus, time_to, to_24plus, typ) +DEFINICE_ROWS = [ + ('R', 184730838379022, Time(7, 0), 0, Time(15, 30), 0, 0), + ('R12', 184997126340609, Time(7, 30), 0, Time(20, 0), 0, 0), + ('D12', 184997126340609, Time(7, 30), 0, Time(20, 0), 0, 0), + ('N12', 184997126340610, Time(20, 0), 0, Time(7, 30), 1, 0), + ('POH_B1', 184936996798976, Time(0, 0), 0, Time(7, 0), 0, 2), + ('POH_B2', 184936996798976, Time(15, 30), 0, Time(0, 0), 1, 2), + ('POH_B3', 184936996798976, Time(0, 0), 0, Time(0, 0), 1, 2), +] + + +def get_czech_holidays(year): + fixed = { + Date(year, 1, 1), Date(year, 5, 1), Date(year, 5, 8), + Date(year, 7, 5), Date(year, 7, 6), Date(year, 9, 28), + Date(year, 10, 28), Date(year, 11, 17), + Date(year, 12, 24), Date(year, 12, 25), Date(year, 12, 26), + } + a = year % 19; b = year // 100; c = year % 100 + d = b // 4; e = b % 4 + f = (b + 8) // 25; g = (b - f + 1) // 3 + h = (19 * a + b - d - g + 15) % 30 + i = c // 4; k = c % 4 + l = (32 + 2 * e + 2 * i - h - k) % 7 + m = (a + 11 * h + 22 * l) // 451 + mo = (h + l - 7 * m + 114) // 31 + dy = ((h + l - 7 * m + 114) % 31) + 1 + easter = Date(year, mo, dy) + fixed.add(easter - timedelta(days=2)) # Good Friday + fixed.add(easter + timedelta(days=1)) # Easter Monday + return fixed + + +def map_tkb_code(value): + """Return attendance code(s) for a TKB person's cell value, or None to skip.""" + v = (value or '').strip() + if not v: + return None + if v == 'A': + return 'R12' + if v == 'B': + return 'N12' + if v == '8': + return 'R' + if v == '12': + return 'R12' + if v == 'D': + return 'D12' # vacation → 12h day-shift code in CIS + # U (closure), N/O (absences), x (no-experience), other letters → skip. + if v in ('U', 'N', 'O', 'x'): + return None + # Unknown numeric or letter — skip silently to avoid breaking import. + return None + + +def map_it_code(value, is_weekend_or_holiday): + """Return attendance code(s) for an IT person's cell value, or None to skip. + Only the explicit on-call indicators map; everything else (U, D, N, O, x, + A, B, …) is skipped — those are absences or shift markers handled outside + the attendance import.""" + v = (value or '').strip() + if v == '24': + return 'POH_B3' + if v == '16': + return 'POH_B1,R,POH_B2' + return None + + +def compute_person_fpd(person, month_days, holidays, daily_hours): + """Per-person FPD target — mirrors the web Kontrola FPD check: + workDays × daily_hours, minus D/D2 vacation that falls on a working day. + (N sick is credited as a worked day in the check, so it does NOT reduce + the target here.)""" + work_days = sum(1 for d in month_days + if not d.get('weekend') + and Date(d['year'], d['month'], d['day']) not in holidays) + vacation = 0.0 + for d in month_days: + cell = person['data'].get(str(d['idx'])) or {} + is_work_day = (not d.get('weekend') + and Date(d['year'], d['month'], d['day']) not in holidays) + if not is_work_day: + continue + for v in (cell.get('dayValue'), cell.get('nightValue')) if ( + cell.get('dayValue') or cell.get('nightValue') + or cell.get('dayColor') or cell.get('nightColor')) else (cell.get('v'),): + if v == 'D': + vacation += daily_hours + elif v == 'D/2': + vacation += daily_hours / 2 + fpd = work_days * daily_hours - vacation + return int(fpd) if float(fpd).is_integer() else fpd + + +def build_workbook(schedule, month, year): + holidays = get_czech_holidays(year) + day_index = schedule['dayIndex'] + people = schedule['people'] + + month_days = [d for d in day_index if d['month'] == month and d['year'] == year] + month_days.sort(key=lambda d: d['day']) + + wb = Workbook() + ws = wb.active + ws.title = 'Harmonogram' + + # ── Header row: dates in YYYY-MM-DD as strings (matches sample) ── + for col_offset, d in enumerate(month_days): + col = 3 + col_offset # C, D, E, … + ws.cell(row=1, column=col, + value=f"{d['year']:04d}-{d['month']:02d}-{d['day']:02d}") + + # ── Trailing FPD column (right after the last day) ── + fpd_col = 3 + len(month_days) + ws.cell(row=1, column=fpd_col, value='FPD') + work_days = sum(1 for d in month_days + if not d.get('weekend') + and Date(d['year'], d['month'], d['day']) not in holidays) + month_fpd = work_days * 8 # flat monthly work-time fund, same for every row + + # ── Build name → person lookup from schedule data ── + by_name = {p['name']: p for p in people} + + def person_has_month_data(p): + if not p: return False + for d in month_days: + v = (p['data'].get(str(d['idx'])) or {}).get('v', '') + if v: return True + return False + + # ── Write roster rows in fixed sample order ── + # Retired people are skipped for months where they have no data (past-month + # exports still include them because their cells are preserved). + row = 2 + for display, group, pid, lookup_name in ROSTER: + person = by_name.get(lookup_name) + if person and person.get('retired') and not person_has_month_data(person): + continue # retired + no data this month → omit row entirely + ws.cell(row=row, column=1, value=display) + ws.cell(row=row, column=2, value=str(pid)) # MUST be string — attendance import does getString() + ws.cell(row=row, column=fpd_col, value=month_fpd) + if not person: + row += 1 + continue # roster name not found in this schedule — leave row empty + for col_offset, d in enumerate(month_days): + col = 3 + col_offset + raw = (person['data'].get(str(d['idx'])) or {}).get('v', '') + if group == 'TKB': + code = map_tkb_code(raw) + else: # IT + is_we = d.get('weekend') or Date(d['year'], d['month'], d['day']) in holidays + code = map_it_code(raw, is_we) + if code: + ws.cell(row=row, column=col, value=code) + row += 1 + + # Set column A wider for readability + ws.column_dimensions['A'].width = 28 + ws.column_dimensions['B'].width = 8 + + # ── Definice sheet ── + df = wb.create_sheet('Definice') + df.append(['Klíč', 'Klíč K2', 'Od', '24+', 'Do', '24+', 'Typ']) + for r in DEFINICE_ROWS: + df.append(list(r)) + df.column_dimensions['A'].width = 10 + df.column_dimensions['B'].width = 18 + + return wb + + +def main(): + inp = json.loads(sys.stdin.buffer.read()) + wb = build_workbook(inp['schedule'], inp['month'], inp['year']) + buf = io.BytesIO() + wb.save(buf) + sys.stdout.buffer.write(buf.getvalue()) + + +if __name__ == '__main__': + main() diff --git a/web/public/full_template.xlsx b/web/public/full_template.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ccea9d1ff986ae1633690452af5a21a1344c4ee6 GIT binary patch literal 25485 zcmd3Oby$?$x<1`qgEUAZLw6&sq#_N2A|c%!l1isENQpGk4FghA(%s$N^&9lt-_dWo zzwNoszRvmmG4BK~bFF8sC+_=R&n%@!uyA-#P*6xvNH5uRq}t_WVj%B&AP+3a!@%-| zl8vR6EsMUD70}7tOukPZwV4$SVXjfdp)x*@EE7XQC@`h24`}aK!C>xtvAYc?V&UxE zL7WbJOGR4|C%F$?GUs4#M;9Hre<9rUB`q**$+eJe`gQOpBFs#3GT4@M^HDE9HZiD5yvnC@Ab7O?Y8x^YYGyR+UEzEv#7eaxw0&1hV`zO$fX| z4<%`+Ue}avnV7y2DM2?0Ykpk?P)NvpT5QB;Ib)gGS)Db-tIqcV43F$lrWSJ&JtzZADF8 zL9h3*%Pmh_O&QBEAKSiRWHI4YSEqJAI9yt(sCyKM7G)wQ6G->cp8;$aI4VC{>XF_2 z1*tmXc8WJK%PA$Yi+!a2O_q@e(J6e8;EhD9o4P~OaavUvM^%3YfnMrfLdI;cZkde= zk0W_$38C5oQ{amcH=#8b71X3gy?ykb=e9Xu@}M0a3Tl)X3JMo8w@zj(cE&HwUw*%` z-OcW{hGG~o7hdE2ebW5u$|zYwhtfR5Jb7-Dv}uR(M1teBZe9)x!NrK}EpDD6vWRdj zcx$nRHfZP=&9{%vIZ-8*GCkmDu4ETvlg!@%VTM-1gSTfdcTcYbt8I?Oy1%kvM;>Ip zWpnBT}o8Zg5snF<{V?vo@g#&8C9Vnrd&{T7r@!Un3d4~NK8RkO5V(}e5LyW~_ z#gRTySC=3Saz1+Xn@`?pNZX=wxO2oDwYqucV{9MZ`r(kkPENK^9wTq8KG3(v)$u0J zHr##;Z-38?M$Qmd7+2Z@qd(u-GH~>a=iQ#xw!`FP?i{ggU9GR>2=~;q3;b^H{!Gcu_Oyag zhWF{pSqP1rrLAd8!Gf*QXJd9vaijetNkfEtHV)HBmN0dE(@?h21>0rb!pfUlwS=>) z>469D*aH=wIw%|P+>8317@x0wQ ze{!^O9yJ)yr$ByVcd^#L-a?M((RP9=y^RKrRQ-o zh4z$xuQgWD!}VmTRW0@AX8HxJo7&uKjNQRF8M~mQ_YcW@%n_gsD0-#)!&nQH=8(b> zKx&fsa|OtTwAzVJlO5GW*;t{ALUz^cgaSXSE5q(d;i-9DCY5pnq--q=O6+XgWQ_@c zZTve}i!Y+Kdc}=v7x5gpxIS%3WTvk``xq~VQ%j_og@hrog->UcEsVNvpaRd8(p>S% z*|=&l6%oHYwo7iqihV09hGgH$bLps@$bow)WTP!9E)Ft$F00-eGPF+JwpHD$1t_{<#Kdm%@pkRWp_B48&(XCvA2J32#X7 zDunP4QV)D0@4z-w4#ucrX9VZniwvQ};MKUdQycy9Fzc9-{*O$`;{h+=m^A4zJz9Y?MVcF!BFFk3g4R=F}HC) zH$4L>n_{4o#<#?PR4&>Exe~*aX7`Hd5%|WfH*tmsI%HTFGjQGC12g$kr*zs7JQq8{ zCW@(9Lp*CXR3Sspe)m8fN7i@(4`l#W?~E`n@v%6k8Bw=P_<22u6}`(ZKt_s};$<*N z2BpFa`AROmT`u{FXNx_V1-R5YNwwlhj?l`v%#xkD3exYMO0U6{9c-ldxg}NPQy%H< z5oR|j8oop=Wi`=gV3c<@XJ;WYn|Y^To(0Co&Ix~o+4b@Lm)H+QCXv-Q_I*89R%{4_ zx&1DhoC&>+AN}eHHQ7mU^a|qQF}t{gpGlBNf{}@eKk2F=qU=2GM*8Em!#%VDHNL!$aYLp;EQA8yS;P%X3s^Dl*BW0#nY z!aISzt6L`^V26+*%fc&3Jpq)8na6&%Waajyt@f7N8QgwMs9zf1TRoZQVBfU7yuwNk zGbE+)4spuS_%Lf`1I5q7waEVL){K}7Q_NPfj-}*$C}~=Keyg2OhSts0ER8V8*+Jr>y~1ZAq6-n&{lQ~h@0 zEa!#Mq=F%Ts>fi3%y4f3Q?7vL7QofZL`P3UByynUqd?i?>%F4H$=My@{AZ`Gw>M7q z?x%~1n5UudYSMRccbc7}(08a>=>_h@h1_6|ldPcdhm zAoryLJ#<2zokl5+r`|3fc(@JrHGDF(4C0#A^U&6r)tS-Woi6k^-w(m)jq2ZDZ%K8& zxg&RdoTm?aTQP;P-nVhF|Ax%*YF=GL=w{t4 z=a}4GbJ3(HGg|WYbTK0$x|b=7{=6(X;M&QYd#so^D}(ChjJ%cOcyH`#fAfk#mK-%& z2rQyERS=n&H@vW) z;2RTRN_BJf8*uSkJWzW?lDrk`IR19A#Vus9XoOdYpI=aTT3;ko@jy~R*Zo1f#3q-5 zo+qo(IgycG!KcGPo03Ld>dS?5ssPXD5m%i;4A*{=U(TsmQfGa9lF6f@u8?~hoIc5z ztkSK{hY2#<96gT+p`F!S;MH4r+3%o7RWuH^K0n$wo)JbqTXR@mxLL?1x3Ih^cf2HY z#yZYCnqFB$aV^DNsSOax43NJ%TcN*HJK65q3;+c6Uz_{0luGV;PKcaYG+ybLG}_gj zF4y{y&&(JRyHQxq76A4JVX`t7nVBBn9G*7D$Vtz7cHY=)-rSs=)+Yv(ZrPutE=TR2 z?uh6W6?%wZK2sQBN)_IoZ0XVjGdATmsergZ$5-^HjfUtQT$H2U+Qc@b9rmfK4IbQ5 zM2ML7nD4e)3ehNde5a6XUUbM)@LZ3iRkl1#1xFrQGFJ~d?RsuAxA!-rsykIrHV<0q zBR_lBSYFnaY^+=&kME=)(ZG;JeUDB>VJS(5Kf?Z%=O&m~M2MvyRz`CpQCFCmM3{p* zyF(!&g-S%yI}i<@A(w%sQ=*4KHg`R-Fhululac#U{CTciv<0EWnD0jFuSqM@qZ2n@>i%}_5$TP0z$KtJO>U=;?I5Vp&m(0#h>3-M7%y( ztG6KtKoD$KdkXh$!w-9&zykV{)uBX9%Z{WmBS)?WxI+29!0g< zGRS+#3k`vzh+eIWmA9M3?wPo;4bCeU&&b!8vi9{h?xGuIwFCdOgIlFy-&yVOOnX-tkErXz|!S|)0VL3eY?Usbz0YFD}&n`7WS7fk5{rM6K7A- z>)kbTpYk)GtamA5s`Yw0T`q@tmTKO98Z4c&d{)b>>_;=eB>-H+DL*C`K)q?x)hor- zO^;x~);ZM`$q%5>PLE;17Hrv`ez(?yGYz?dajvJ|5jNqpK(1h%#p#p?WWoD`)9>QI zIET|12GEElQ|8FXBjJwP)M=880zj24$pKP8PCu2v2JL}-4=KO|@{*CxMnOQ1u0%oL z72Sb?Kp-8v;vy*^{wtNhmu$d^`}M)NMsVq9*LBA3>CI@0rAT8z{>`m1ZOU#{eXFO5 ztH3Msfr-g7VV6%xCflUtC#VID)=R0LH--1~x~BKh<|b07b;~|ogc6%x@o-lx-fJxo z8BSv+Hr=iI)SEgzM(;7Za&H#??o&F(5(Ba#@5nM%9pv$zXl6}~$3 z*>`!@=z5XZRcj?9tgTpO-RJK*A5=o_`SH&a`DfxrWWH>h z#zKb47UU(N#vK>y*BksqX({4a2jrMrd3nK`B6bcW-AeN?bS(b#>J&Pqiz34HS%ZKl zPaB$KStSd`J3Gk@=u=|B=p@!$X&+ScW>+6#yL$^9I8Hlzf0p>Qb0a|Zz%sNJ(;$rZsb8q!2w10v-VB$ zogK^6g3wfl1LH1N*~Lfd*NK=sr#yNVJ3FUs%)9uvK`X_J$ArhLZI#qv=CxlO-4B6p zR7V1AtG3%O_D6D^x7HNRncb&HUTwY*m^umntd%Fyicwh~b$>>Ek>Pk1BmW$a@ivFK zJViWva%P*Vb*{O+Zh65@Ud3^)ZK1CHz;Iq~4H4w_3^j98$QRUal;x=-^nyIfI8D&Fv_u0@bL-iu{A_I|t z(6m&>-pF7SFe5k`6aq>H`GBH8L7+rX04Np|21=dtnhT$!_wOs*Ov?8(bnvyD5OKzF zN_=`9)OV{fy;IKO*-$-w+1@%lJhOZ}({sF$8u3ceGzP<)=aQNawGgB_l21Znf^WmH z^vcu$r>>|G<8s_<`;R)FoCGg2u_<1NIJsFg?qMZqRcx5G#?5h_0PeJkF1ja}@~xit z*g_|YSzxqRdM#>+D|o*@FpN!iq^66`|H_>g-94#;GEqPzD45%Fm{vEJwHG4FheniL zw{SU+S+K?{5Z$e%8)zXtkRkn1v9oS10GniBCe+xnJvvFrMy~T7P0p^SQY4Y3*uVz^ zaoJaOdC`v=(P}<`v(j%;|Gd z776GGzuA%I=utzbGw`jB1Q_jB<=-?EV-X zF9k0(Z{U0>@_AWYcvvN7N4DZKQp@x4RmH)^+X6eMtLyE-#+{Q3fZj7T*V{FR7VL98 zsih>z`v(lc9pR5C)wD`wE-wgDkunZVI+?t0XBPMtjIcxkpk6 zfUh%Tr#vjF1BlUiBBwGf=?=ix9ddfIDF1yfetfZN_v5<|$Nn?pI2|5glJG-H3a4?0 znbSA&UImjkR}?8dbVjw^bVjpriV1*Gp42f3tDpuuDGZ#L4H=Pi@q1Y=e-2 zyrj}G+zYz*=FO>P6*flkTeJw<4P+aW*i90&1>Ni9(eWIkkX zb{br{XY?9b26CN5G@H?x-a$mb+^Ua@>5)O2z})JOtLf=M3BcT%kDKWwrgE~mwI6rW zn}difU)AO8e`rhmyBE9mi!6< z33XWi`GdJBKW5mj1b@JjgC&!*!#20PKD}3|iVqN5bz(8*3gi(H1xtb(rmox$Z=M&~ zSi4u(pkznGCaBmDDli~&W{I+A888RzN81!%9~Ub(S;_H@-8`ctT{}?DI(vh%=>$Zo>T|I-V7=k(i=jN6*4ds`^ zt+53nMH(zFRwP%fObnpCw$3j^@6u20R2kw`O5U}o>%g|UZ`7fdE9o-3NmwHwVAc}#8!xjFPM(osq}=A62;GM7qYw2WswE=8n% zA~#|~*hlsLM@m2E)>L$xpqY_jav^cyk1`-XHCdH8G+G{1aG-Ppn-(P#+Pqq;1SnY@l zx<)q&xsvQ0y^CyV_xm#07P;PYd^vd)h;*yj7ly!n6UuHzXgd}AbLxsM7mR$1~HmM#YUo&jfO5dNySF0 zlaB`QD>pPGj%(dP?P4)&EWT;JL50U6qIfF0=A{Ia1}~28ONJzgl|b=S?B4{|A7-rr zKeu!wz_WY+N+5Z11b_;`ho9!o-`JMLzeuqtajV$|epXDqtgF$iZyejyd3ig=-b|ML@#G;-`ofHGZuGn}#U<+ld2WUp4AJ$>HbFX1#?@z05^E1W+M#4_kX8-^$rO zW-Ml9H`{U0=B>7nJH@)CYjV)!u;t`z;z8L{H=&icexs_4@Mu7a^L*u%G!N*;BR(OgE8DA~w#V$=Ltu~(zDm26~X-=?!0;&wk{)oL{h z949i|j57xj)UrHiEWU2OK)qza6E*fyf=+`KNA@K^;=*#Fz!Nj}R)SCa*V*s~UregS zx)nYkvZ$sm)jeg^<-&2T3z zL6T3@tQ%Jy&LP|)6B|}Oej?gxELU*R=cUd)xyKkSOl*8;s{k1X`tdA-ut;|~40^uI zr8ki;7HYGX0;n_3DhVH|A$tV)v+y*)Su3fH3Gh*#c-h zijE$XFJjg&56 z-s%55*qlmJ$QK-klKP!sSx2GT5(jR8i}LI1+>TnaW~FWyt=XxTEQPyd7CE)m?ziuF zFk=rUzaoT5MYh;LF-7OAKuUE%^y~`D**(pA8L3#CK=ZqcfgMA(SAYJ)|Km(DpC8pY zB$cYT)$D6EMj7WS+R_VFXz9IBsGBTYzPh$t9`xB&BQFA`T8JJ_w(71PoUH8ZpEypg zBwq7quyPm--$99-0s~*hQc@(U9FT!Q`DvS(_c)4SUYtCeR*M;FDSG$wQ@>?Eg`T>n z{;eMj5;_(mg`Ft<>#s0r2;!K&R7mnzxfK5vW_=4jTfl;~k&uD}xm`+7@yiM5I8)I< zg>{n&2GZvICz|9Vo$vIG>S~xgf^u&zTLNg!Nv%g7B_o6>LBcK)gZdjS>C!(!Sh*m2 zr_gfG2)zs3`rZ)Qr#~mAWo=vCT<-risk|{tI(}r%kPwQ3yFh7#o{__J>gAtwfbme_ z++;UYPRl>(rY9irGLePH@zgcMns-vLzzRp(SJq3_OZ9bD)2s7-k^qsM2{JP%Qy5cd zQ`k+Y%@sszBD-|yVnr)aD;X;ZtH)O2R&rKSRti>PRIY;RsRpnY~$_FK_pXFL)Ms2$w-w z8c{k|ZNBn=#J*y6B79S{5cP5#!sIG=yr!@!?xW^zgDv`Osjhc^J2?IW&_JCuZr%uZML{di~6y6ax1^*jQO&*aC zaSU^yx4e=qGn+Y!IzwmK9KBxi$vte7-o>(?Ib-JN>K~D%XAul}(0+6>Q-6sWyX$e- zb9lbIPUfNI`6?^p$->rA~0OaIdgUUVz423-S?-}6Z+Lo;`hWxz@yCP6sjtN=#U&)$bMbuk3ELU$ssTZQBO;~z=PxI28sWiYRM)c*tD$Y9?f6jTUNUgghy2Am^Bn%G=E3l z{8egqymn*p%XgeZ^n&152yTwiupfSOZQPNxbj>@liv5n@o~b}CLQ_m`T&y*B^j6Jk z#1S7OqZ&oakfbKhYCX{rADB!a;=rFqxoRi0L}GT&W?(WXK=0Wr&ztS_5l%%B3lzlU zW0m&#)yy(6$<4=|6b9 z@f(e3(HCy{O0e>ui0_CXGiwk~y-ZCCaJRe=mdj8G32r_3y9;74uqtFy^1pbZA1WO- zvo>$wEU5HDK>u-8v)>v96Ht3B?4;U?@@jvAWF2Nb(wHh!DuJSaqJT<1VkCSdf;*f$ zq9VK^!Xext;w1be0wWwFBDCV`>f!mgA%ck3s0jZ(Jl0|$r8=cLO&m^s|I7$7Hxf5; z1yTjF1Cj&s3DOBN1`-BxC{if00+IsqAF?7c3KPqH`S54q&mum^d^&4hLCs_N9hMlU zc>6cp%4t!Y( z0pMcc{9<48dc5K|jN z!_&X<6{kehKDxM`zLhRSPVf?I4N&F}c`>8D0M`u5495)H40j7_3ug;^3zr0o1cwBh z1UCsQ2`33V30E6S8wc;1?w^bPeBZAJ@J#PtOGsZ~)BYi|q~83EfV9`<=j5HX1yu@J z2xNcy??zQt@40z1nA67L$g}dH&5%c7)+-7!%@RSBNW0F_U|Q8Cg0bYbLfXhWk|UVVS#E>gIav#wa?XbM)l{^ z-sxafkU)s5A{!7MfF1}z?eBx;VL{r5d+5^ww~n`NhUzX1j^@FA98XK=_3t$CH*;}w zzH`xY!E;G--gA-fiw~N6Q6I6G)fb;M&!7ge{E*N7UP(AV3i~Z_wE z8R6fG;Z(e|yp+5&y!UzOswk?c>GSJ~51R*2wOI1&i%**uP!n158;XBNxDdYn8@2N% zKe!VK=gfhEsi06E+-^2`)^qfyKC_Z?3>yDRvH$BJw?6#gqk;7R0=+Re{bRr1A?b(C zM1NH;f2qy>FrnLQIQpF?5cg6Nxr#izy9)hd@QVWYtR~g6l z`~QEheGjuU=K78k9IiLMx2iV-m5jSRZ~J`@Bm7~OR$R1xf&uOUi~-;PBvY&WQh)uYtl1U|y}0SM z@7HuD2XbRzZ6F}jLkliK_-XUwTkag6iaZV936*C32c*Hy|J@oo=B)p{;E%cL|EH?x zA8DNj=u6HW2myF^K5`fJaHZ8WyOh(UVSkf8`}7&k=05&vVv%zqXl0mW@?@T5a%I|Q zqG!ryGH04+l4j~;3T6Hel+ikC$`sHW3+*?;HXv`iKs$d<$22=a^arS!kJ8_U6Pb9I zd6xxpN@_mTl%5cskeCpkkeU#ikeqlpA3ufFA1e zeSgbGj;ql8;XSqzyy=wf3V45sr{x% z5{FrPjGYTRnJO7u{$O#MoeTQUA(Ng^PVfCZWQHnd$Dg75C%i<1ALFF`0EtJL@v%(tC%Ajqiz`G@X=zalZ; zAsO6E{7ltMh5@1h{sDvm+5rNU|HOU{02qDRlJnO1ZrpFz@YaKkA;}tI!uJk|><>ND zpE1|lQhvySE*OhB$ln?&YQ5%%;Qpq-{7TSb_)iFrsDO&c@)F;!PCV9`rOZcnsU`7@ za(XQ4XGaD@eRE_0YZ3_R8?i$ryxMa7wfg3=7V}pQx%cVS{cVbEs%=WiA2+0bU=uZ< zKPjQ9%%%*nOyZxB_K)&+m;AKf&=ICT$>hkaMnweYeS&Z;C|qpLe_gU!Ej<_-V358! zkI~^_|4QMZv>mMaPu{ca1M;B#<~_aoXBHga^2_z}0Pk$iU)%R%9HABB~eNK-Y>jyy`OkPGNP4}hJO3PV zzvi6lhm}rgaq>X;Bj|ZWV80REK_Aw;4w}E>@xPFUywI}OWpB&;%0kL~%7V%Q@cxB% z|2y~$!pZykAR#0N4tpsi#xJP};pmVJP%x%pJ=8l_tg97=NG&Wq*O^`%5I#s1#86CB z%wLR9Oj}G)%w3F83@ol&N9>Op`qudqS9vw(Y?2bzl^Ev8mmcOw`NPuN5{7j_eG?ww z(b`{n-POQr7^%vq)W0y7l4}6Y1l|PSB(TiC?7OX!%{hM$RJLGoJe%QDV-*kooazkcqKp@nE)BaYb1Aiu<5PQSABz|OaD;b<6Ec)KiKMl5V z9`9Rf6A0KRR)GGNF@i`@W?0yMwLHHV@4sD}-+;^C8eAX}*Ma|&KT?L)j1`I%GAp(i z?!RUthbxxOEeuVRU}dz_zP?0ERlfAg#2@eHL$h=e{Z0!6hQ9Z0u{hAzeM>UGyA|=D zNEUZ+EA_2HL~r;C&@4U``rozd&0H}ba{Osm*eGAR!1Pac1+N-;-C?Y47=Or({~DP8 z5M_L z?yp(mS8j;+pXi792HUmYtQ!5p4Ofi3ezz6y@Z9+@LQDE+9q==BIFA>$34%0ZULpw( zWPWF-dQEdz?Ee?>MXY@mp(SIawRVVXcsE$1pVcS6U^>EYj%7ZP_MPx~e0n$!2m8MW zgufTp;+Ni({F3~W;;Hhi?TMH|{4)WqNVITw;_ee~E^mA95R!LYV7P~!|9xu875-bh z!M{^ceqg1O+D^h?Ghv67xSdCeKh?=4))WxMIMo>N9Xe!kzgGi<#$>MlT7G7b9lHOW z{#a2>P5&#`{$=$`ph||QrINqdll6_C`k`vk-eI!u=<$3m>_rU+?+T#vykC+LW+lElj=e0o)s8*f(zrd~nE7YGVKRg;_$|3E%6DyJL zf5j*MAy26M*o$Kf;lp;Jc)!(q`G>v-fTVTMUMXRCFJ#LFw|k|O@UM0D|F^-ufmEaK zW3@ONk7fOf2F({-1QsokEY^8I44KYvn<)h zRek@6Z5sRM7V-{#H*|zZNZEcq4vDC5edH{6(e&p|55XO2gEaITCqDAVQTZpD^=pj( z{tl<}sr+s`<6jf3B5R`S0oma8{!jV!yN0t((gvByJAC2sZ5G4lzO(c)ue+iE>EW2v z#)v5m!fK(v<>};*hb!5FvY_C8Tk7eVWuYKaC1m72m9MMrT49! z=zGZs0x*M0ee9gV6AUNgB{QE0@A_UQ2?0^a<1WC?_ot*B&QQW;5m)DRxiEXR=H;O6HvO(UhQF zK9nuH?ygm;eVjg)){WOXz;r)Q=622vt1w1YRaS=aUXH&(wE0UsbDAii9grcFZvMuh z)cmN~+(01TDl9n}g*4Sldi;H+3rJw1kr&mielRcmW^m3ljU6x%I%R-jIfyBu$!0Bo1XyeZG z_3p8$K6ii27uWKw+mNl>f`Jx;NcRYN+i?+o%v(OEvsXuZ!_WMt26GCt2Rn~7!)oO1$Iz(>In>?Ko56zG~rw&>G{cP@=N?ie~ z;gEVd!Va4))fKHKd+ZRX|3in-&gFrHdlrE=W;f+KAJB|8)xXh4k#j12qhym$nNX}hZ>Tm#GL5uj)3=Xhk}|aGSsgxA;0Y00R~;~ z)63B#vSm2<$u$84yHf zr)Fo}g2w}fTQ7&VTP$|3ZES6CkCrF1>+cophkA^*TC27FRnh%h_8wRYHy3WXnODv%3v$ zbuLf$^>`{9g>=cT)^DMHKKVdGg=?S~a^e93R_~0&?!bA1}V0d>|Pj zhSJQ6C3%`cWk6YD9rzw3Uu#8Ig(WCvoEX})A!md&@FJ?brMY{v%zA4d&_!1Tj^=|ijNp_UQ#0*6cokx^BoLq^c_tsjQ+IJR^s~y zY(NKdvzVvymXoY#*G&En=9-h&nlZdhU(+hho1TH%#XrTx6+9p!h~mFKj(R}z$Yzb7 z+$=uxY=0)o@S*7n$EM9X(eP_!Elt~%kP%tSg3mRT1HsgqB31wnG<(}NWbpB{=fO3sbe~|X+6D~n@<#>U!-8Pwj{pqF|c`c zVSD6jzcp@}UVbG2UnEgnDLQRekf9@HwRJ$omL!s2E82-2d)aKO7vQXYA%#yRrO5hRoFRzejW3Q80o8Vd4scQTf(owM0X+q*NdmUT=m z7CW#TP$-vngJlBynz+N`LwulQ9m3Pd0^^gGu@Vb6{)oALH_2AsE7_8K}z zN%8&Rmq{#(o8yf)dtkO{&s&<_1u8kC!^FJv*S$T%jD5Nt%v&0^S?NUPFBV;+b^C;+ zO>ESt7d~CeKi$d;9U9%1q$n&%H;)u11rGBpo9Gh09IjS}$}imVH1ViNuuPvtoS3BU z3L;V(+!(6Z#X!$#>mRJ>@kLaz?l4u=eMCBwz>)FT7sZ`bgkPT!A#^vcVR0hG;OX32 zEwir!eE%u){wm`9bE0O;e%e_nq+DzdyR45OdTI8+gzHnN58MBhKK6 z1uiW3*B?pSCOMbGc#H!HV$u;)ww+TfFHm_v&x2}&!4c1`BhBuKw5dAbC4IRcv!?Tv zwqi>IuOUXzW8(|G-#cCut|6BZrD2o_F)lVVQ4S0<@f@`&=X<3n(YrOTP5mmKT+;zm z*{SM0jT^S2%#zzm?6{S%r)3n@{l9#y@dcpkUx(4-QJY+-IDSNhAC5`P(|P*NI%4{n z-bTPi5S_@qfWFxj2mC5~Y$1&kN;u+!CSk>>EMuGNYGN(aRJFtTy_Wt(m-5%CmGlZ` zU7RBEm5~!Q*H=E5os<@v6!h~!`nF7b$N;gmaeYv{0tF%zmtr5iuf5d$HEFIgPgWiH zdewn(Ie&SZVZq~`Cz4e+*(DB+&YyyVGU`zYN>@#J)J*Ca^Hzfl>alO%ohxug#=dMQ z5PlG4oNT0Qj+UBgEMhVQKfkF_or79)YLxbtSb!PLCAVhxUdQ{(M6Fr>tEq7@iA{mw z7Y)HZwhC7n&>ZjB_|Y$tQhT)#{F*|Gv4oq@u`gKBsg;i>&f` zwjKj6oQ)-uG0L0b&rvB}s#E1&Ot;Q<@mX3qjq2)?v%8p^Z)teNqcB(m)2W!Zr~vh8 zf8BbR-@FyisKB5-Og4c`nA;$MPp=8@A5I#*4U*j~JXZ?s+1}g=6 zJF$dpS1v!Jx(qvnT&k@^Wf6aGb}{|&jk zS*i*5JnftL&psZM>umYCP6+2nSO-wOy z#%s;m2{Y2sDh{?9Jt$7#KHfUafx7qXLg;U#_^xSpvxOYaG_e(ew`9bNcaAIV`6v)Gj&k_Ia?yhFb6!b170(P9#Re+XaIaT3%{tI zo@sp97qYJkH*d_957rio=Fny8mzj=umDxD=!+;5t43^>pGE|u%)Z&^sf?|E0XX3p3 zF(R4A@7X)=zomFe*wRmqBsieLi_5Cr^k9_+#>3*IL?jYQjNvmJkT)5$Fge&$VC}*3 zsMc$zKGqy%HKvUCB86NCBR>3mLW{jMXW5LWatf{aEpCdpt62oMvDmQ$$4f-~ zbJ5|-t_k4s1X_n;=}(E6Nur{CG3rGoyORf<$aDsQ)h|UU6buW|lUnC1qTy#qn}ahd zI9->mo;`J{i-S*_V?$djEv94Y=luk&{p|DOdQkQA73e0uM@hv<4?e#qxA}^%b=0G% zbKu623sf)^XcJ3%P0M=@WO%`x3i;jxfhm0%^f$2C z0doe1$Sw+(^KiHinz6{oeM@o6Epw>ofIedErAw%vJ0F~GOiC8QD;P>fHkcps&n-P8 z_Ob$Ug4Y+861I~j=O5h8b$YsOBb$17w@MjGQd+(9yC-6k}PmQm|>Ij%Q$$ymJB?fcI_ZgR9q=1}|vU-d~R*(sT4-E*PxHHsz9YB$=x|H%}CO z&s{1`*R8DMpORROX#X}5+ppJp#ZW@*{gebA!WXkOO6+CsgJSFFZA$cb!-oD%OMbS{ ze%*|Y6HV{;R@AV>WZO6mZUcL>#V{s3GWShV%tbytqJ(=~6bC;J%JSBLf?Y6ZQ>;pF zMx)pM2wJh4A7y0?0yS*~K7pF|SE7-V8EKSy$Vh)c^t1zLi`>@L+gtf{b_GTWUM2fwt?YK%}I0($C0nsv8Jo z{P8gwR&dY42r&hX1}()NaSXND7s%hA@p6f&(f9YlDKc(_{lW}mz8=Ey9*3#kU>hsT z;LUry=Os{&F?iT~ynu08{H)UPSyj#KrurrGi>r8y934Uy9&m799)r!l@-rUYH(%ka zSwkWYK(q-<4rM*+rZ}uxXMUQsS}_NksG9ud5rHx_g=s!1PP@U4X<;09dS32zSRoLC)3eK86N)d}r&waPe zZ;gd!J^b{s=D}JmxOU09j;$(|^x97juMU#5_8KsS=-^=vpC-LWOj2!6$E<&V`Bs?i zT=(RomOU2EDUqf)lTQPGq2@bnb;**M=s_j@r_E~uLLS?A=CPL@c^mACG2y`|P7FPG z)@ek2$}C^jkG)A^9&c!Fuq7?QTrej=e?o*mWR#28ea2A$YA%CPb{^+_=;pV(68!kqsSb_k6Tu;UTVC{7 zbR5Sp*00!0Kbud9C3Uxve(qG8tzEfi6p2W!NGx#43ucx{;kFW65Q028NG-UNyC|Rx z@w~Z8S41np-9^r8lk&!D9P);DiY9eDJBNsrdnft!!yW17ZbFyRx@Ml2TsS&s%#bHIhryL-&=Hydjtk6xH(z=WoJ;}7~dsD=g7cU9#h`By@sqJljf5-fW^;SNGPMIFw#(W>n zUaB|x{+Y+r^{JQg)M%-S%TNE6J@CA^P$@!emg~vOi)5G9%O3wW;quG%$x2(QqF?+h z%n0G?yOXTiQ<${npJ~%c{x@}KJ?g}~RCY_CCyz2SFz`bQz3E1o)5ly_wziDgT@MH>0=pXZg-@-8%hg z(wyqG)qUq}cWSrZ$!)n2`Mam^=|T~O_qVro1x-pURV_MHsxD}Bc)`Lux3s3S9M@81 zF-qGmvphL{Mv=*m)#*Pk*|9Qtu!IzJcV>t;=qPEZe4Nm|=GTP@xvfhkq1QAv zcW=kq{(n5HOL`+0R;Ny0QrtLS?EKyJt6%qV6i1vmdw8hs(;og!48|4er`>(NUOW9! z(Qo?Mr?oHlw62SvCwop)zPqV;Uw^XPlJ|L1*6Pp{aX@{0LOSYQxhu-f|&|W+z z?TbSazvYRbl^W@{x|<&~L<)Ewj7XiDxu%mv>TfXL8NGQQ-9o3(zI|qS~MK3xz$Ml#K)fCkpZ%zv6kQ$6;O2EbWw! zT2^+yzcxoRYS;Z&kxuScn|0iMwL`bHVcfTsc_%7srbK3UdpcQm`U;)DXnpwfypL~A zO`mf3bijYE6HlVvFG(x9@Zgcp(WA?cZMgW#+->!-uW#qp8Wk3_narLi{PU90dasi6 zrBT~&{b|~`ah>g_)&qravt`#Flt;}@DsrN~`ptWWCDB{D^v*pt@Xr!o^C+^7ZO^5>Spt(Zb~)ZKPMP;^=At<^|C~!o?`GCm z{M}^nf!|04KIU9^pQoUrOcp(5=+xU|-nO;crk9Uk~@>ur?-?%am2xHfI=`J$(LyYn2!l4eGo zW!Zbq91?I0yTLHQ^k{Hw$(+7vnBJq2HzD|Knpb)xYX&5Ole8 zIod|(=C5ae9j(MfL&7)h+*>Wb{jW{Hm(y=;|JgoX{J+&|%68A*$EWwcmiW6q?FlYbh?9bQWP+fDgLQu#VND8;Cv%iZIa97i=J6OcY%!dIgTqx-$x; zI!D)tULhhhE{cYG0#-euYep~U5SlsSz?yNCdg$h$7bOUD-U7|RQm_Pgv$BB{@c^MR KuqqEp0Pz4^V-+X> literal 0 HcmV?d00001 diff --git a/web/server.js b/web/server.js index 105b00b..19825c1 100644 --- a/web/server.js +++ b/web/server.js @@ -194,9 +194,103 @@ app.get('/api/files/:id', (req, res) => { } }) -// Export file as Excel (not yet implemented for TKB format) -app.get('/api/files/:id/export-excel', (_req, res) => { - res.status(501).json({ error: 'Excel export not yet implemented for TKB format' }) +// Export full schedule Excel for a given month +app.get('/api/files/:id/export-excel', async (req, res) => { + const { id } = req.params + const month = parseInt(req.query.month) || (new Date().getMonth() + 1) + const year = parseInt(req.query.year) || new Date().getFullYear() + + const filePath = join(SCHEDULES_DIR, `${id}.json`) + if (!existsSync(filePath)) return res.status(404).json({ error: 'File not found' }) + + let fileData + try { fileData = JSON.parse(readFileSync(filePath, 'utf8')) } + catch { return res.status(500).json({ error: 'Failed to read file' }) } + + const input = JSON.stringify({ schedule: fileData.data, month, year }) + const scriptPath = join(__dirname, 'export_full_excel.py') + + try { + const { spawn } = await import('child_process') + await new Promise((resolve, reject) => { + const chunks = [] + const errChunks = [] + const proc = spawn('python3', [scriptPath], { cwd: __dirname }) + proc.stdout.on('data', d => chunks.push(d)) + proc.stderr.on('data', d => errChunks.push(d)) + proc.on('close', code => { + if (code !== 0) { + const errMsg = Buffer.concat(errChunks).toString() + console.error('export_full_excel error:', errMsg) + res.status(500).json({ error: errMsg }) + reject(new Error(errMsg)) + } else { + const xlsx = Buffer.concat(chunks) + const MONTH_NAMES = ['','Leden','Únor','Březen','Duben','Květen','Červen','Červenec','Srpen','Září','Říjen','Listopad','Prosinec'] + const mm = String(month).padStart(2, '0') + const fname = `Pohotovost_TKB+IT_smeny_${year}_${mm}_${MONTH_NAMES[month]}.xlsx` + res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + res.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(fname)}`) + res.send(xlsx) + resolve() + } + }) + proc.on('error', reject) + proc.stdin.write(input) + proc.stdin.end() + }) + } catch (err) { + if (!res.headersSent) res.status(500).json({ error: String(err) }) + } +}) + +// Export harmonogram Excel for a given month (import into attendance system) +app.get('/api/files/:id/export-harmonogram', async (req, res) => { + const { id } = req.params + const month = parseInt(req.query.month) || (new Date().getMonth() + 1) + const year = parseInt(req.query.year) || new Date().getFullYear() + + const filePath = join(SCHEDULES_DIR, `${id}.json`) + if (!existsSync(filePath)) return res.status(404).json({ error: 'File not found' }) + + let fileData + try { fileData = JSON.parse(readFileSync(filePath, 'utf8')) } + catch { return res.status(500).json({ error: 'Failed to read file' }) } + + const input = JSON.stringify({ schedule: fileData.data, month, year }) + const scriptPath = join(__dirname, 'export_harmonogram.py') + + try { + const { spawn } = await import('child_process') + await new Promise((resolve, reject) => { + const chunks = [] + const errChunks = [] + const proc = spawn('python3', [scriptPath], { cwd: __dirname }) + proc.stdout.on('data', d => chunks.push(d)) + proc.stderr.on('data', d => errChunks.push(d)) + proc.on('close', code => { + if (code !== 0) { + const errMsg = Buffer.concat(errChunks).toString() + console.error('export_harmonogram error:', errMsg) + if (!res.headersSent) res.status(500).json({ error: errMsg }) + reject(new Error(errMsg)) + } else { + const xlsx = Buffer.concat(chunks) + const mm = String(month).padStart(2, '0') + const fname = `import_harmonogram_${year}_${mm}.xlsx` + res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + res.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(fname)}`) + res.send(xlsx) + resolve() + } + }) + proc.on('error', reject) + proc.stdin.write(input) + proc.stdin.end() + }) + } catch (err) { + if (!res.headersSent) res.status(500).json({ error: 'Export failed', details: err.message }) + } }) // Export docházka Excel for a given month diff --git a/web/src/App.tsx b/web/src/App.tsx index f7ac110..c6cb063 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,4 +1,4 @@ -import { useState, useRef, useCallback, useEffect } from 'react' +import { useState, useRef, useCallback, useEffect, useMemo } from 'react' import { ScheduleTable } from './ScheduleTable' import type { SelectedCell } from './ScheduleTable' import { Toolbar } from './Toolbar' @@ -24,6 +24,14 @@ export function setFavouriteFileId(id: string | null): void { else localStorage.removeItem(FAVOURITE_FILE_KEY) } +// Characteristic colors for info rows — used when pasting from info row to person cell +const INFO_ROW_COLORS: Record = { + '__tunnel__': '#c2410c', + '__metro__': '#1d4ed8', + '__sazlt__': '#b45309', + '__d8__': '#15803d', +} + function normalizeDayIndex(data: ScheduleData): ScheduleData { const targetStart = new Date(2026, 0, 1) // January 1 const targetEnd = new Date(2026, 11, 31) // December 31 @@ -95,6 +103,12 @@ function getISOWeek(date: Date): number { type AppMode = 'login' | 'files' | 'editor' function App() { + // Apply persisted theme on initial mount (before first render of inner components) + useEffect(() => { + const t = localStorage.getItem('tkb-theme') + if (t === 'light') document.body.classList.add('light') + }, []) + // Auto-reload when server restarts with a new version (new deploy) useEffect(() => { let serverVersion: string | null = null @@ -115,12 +129,16 @@ function App() { const [authed, setAuthed] = useState(isAuthenticated()) const [role, setRole] = useState(() => isAuthenticated() ? getAuthRole() : 'editor') const isViewer = role === 'viewer' + const isCis = role === 'cis' + const isReadOnlyRole = isViewer || isCis // cis has same UI rights as viewer const hasFavOnLoad = authed && !!getFavouriteFileId() + // Viewer/cis with no favourite: show loading while we auto-fetch the active file + const viewerAutoLoad = authed && isReadOnlyRole && !hasFavOnLoad const [mode, setMode] = useState(authed ? (hasFavOnLoad ? 'editor' : 'files') : 'login') const [fileId, setFileId] = useState(null) const [fileName, setFileName] = useState('') const [fileData, setFileData] = useState(null) - const [loading, setLoading] = useState(hasFavOnLoad) + const [loading, setLoading] = useState(hasFavOnLoad || viewerAutoLoad) const [compareFileId, setCompareFileId] = useState(null) const autoOpenAttempted = useRef(false) @@ -143,18 +161,37 @@ function App() { } }, []) - // Auto-open favourite file when authenticated + // Auto-open favourite file when authenticated; for viewers with no favourite, auto-open most recent useEffect(() => { if (!authed || autoOpenAttempted.current) return autoOpenAttempted.current = true const fav = getFavouriteFileId() - if (fav) handleOpenFile(fav) - }, [authed, handleOpenFile]) + if (fav) { + handleOpenFile(fav) + } else if (isReadOnlyRole) { + ;(async () => { + try { + const res = await fetch('/api/files') + if (!res.ok) throw new Error('no files') + const files: { id: string; name: string }[] = await res.json() + if (files.length > 0) { + setFavouriteFileId(files[0].id) + handleOpenFile(files[0].id) + } else { + setLoading(false) + } + } catch { + setLoading(false) + } + })() + } + }, [authed, isReadOnlyRole, handleOpenFile]) const handleLogin = useCallback((newRole: UserRole) => { autoOpenAttempted.current = false setRole(newRole) setAuthed(true) + setMode('files') }, []) const handleLogout = useCallback(() => { @@ -214,7 +251,7 @@ function App() { } if (mode === 'files') { - if (isViewer) { + if (isReadOnlyRole) { return (
@@ -234,8 +271,9 @@ function App() { fileName={fileName} data={fileData} compareFileId={compareFileId} - onBack={isViewer ? undefined : handleBackToFiles} - isReadOnly={isViewer} + onBack={isReadOnlyRole ? undefined : handleBackToFiles} + isReadOnly={isReadOnlyRole} + canExportHarmonogram={!isViewer} onFileNameChange={setFileName} onClearCompare={() => setCompareFileId(null)} onLogout={handleLogout} @@ -291,15 +329,16 @@ interface ScheduleAppProps { onFileNameChange: (name: string) => void onClearCompare: () => void isReadOnly?: boolean + canExportHarmonogram?: boolean onLogout?: () => void } -function ScheduleApp({ fileId, fileName, data, compareFileId, onBack, onFileNameChange, onClearCompare, isReadOnly = false, onLogout }: ScheduleAppProps) { +function ScheduleApp({ fileId, fileName, data, compareFileId, onBack, onFileNameChange, onClearCompare, isReadOnly = false, canExportHarmonogram = true, onLogout }: ScheduleAppProps) { const { people, tunnelClosures, tunnelColors, metroClosures, metroColors, d8Closures, d8Colors, sazltClosures, sazltColors, dayComments, cellComments, - setCell, setCellColor, moveCell, setTunnelClosure, setTunnelClosureColor, + setCell, setCellColor, setCellHalfColor, setCellHalfValue, pasteCellTo, moveCell, setTunnelClosure, setTunnelClosureColor, setMetroClosure, setMetroClosureColor, setD8Closure, setD8ClosureColor, setSazltClosure, setSazltClosureColor, undo, canUndo, @@ -393,12 +432,14 @@ function ScheduleApp({ fileId, fileName, data, compareFileId, onBack, onFileName setSelectedCellsForMenu(selectedCells ?? []) }, []) - const handleTunnelContextMenu = useCallback((dayIdx: number, x: number, y: number) => { + const handleTunnelContextMenu = useCallback((dayIdx: number, x: number, y: number, selectedCells: SelectedCell[] = []) => { setContextMenu({ dayIdx, personId: null, x, y, isTunnelRow: true }) + setSelectedCellsForMenu(selectedCells) }, []) - const handleInfoRowContextMenu = useCallback((dayIdx: number, infoRowId: string, x: number, y: number) => { + const handleInfoRowContextMenu = useCallback((dayIdx: number, infoRowId: string, x: number, y: number, selectedCells: SelectedCell[] = []) => { setContextMenu({ dayIdx, personId: null, x, y, infoRowId }) + setSelectedCellsForMenu(selectedCells) }, []) const handleDenDaySelect = useCallback((dayIdx: number, shiftKey: boolean) => { @@ -476,14 +517,115 @@ function ScheduleApp({ fileId, fileName, data, compareFileId, onBack, onFileName const [showMetro, setShowMetro] = useState(true) const [showD8, setShowD8] = useState(true) const [showSazlt, setShowSazlt] = useState(true) + const [showIT, setShowIT] = useState(true) const [hide162, setHide162] = useState(false) + const [theme, setTheme] = useState<'dark' | 'light'>(() => + (localStorage.getItem('tkb-theme') as 'dark' | 'light') || 'dark' + ) + useEffect(() => { + document.body.classList.toggle('light', theme === 'light') + localStorage.setItem('tkb-theme', theme) + }, [theme]) + const [hiddenTkbPeople, setHiddenTkbPeople] = useState>(new Set()) const [hiddenValues, setHiddenValues] = useState>(new Set()) + // Clipboard for cell copy/paste + const [clipboard, setClipboard] = useState<(import('./types').CellValue & { comment?: string }) | null>(null) + const [clipboardSource, setClipboardSource] = useState<{ personId: string; dayIdx: number } | null>(null) + + // Info row helpers — get/set closure text + color by row id + const infoRowMaps = { + '__tunnel__': { closures: tunnelClosures, colors: tunnelColors, setText: setTunnelClosure, setColor: setTunnelClosureColor }, + '__sazlt__': { closures: sazltClosures, colors: sazltColors, setText: setSazltClosure, setColor: setSazltClosureColor }, + '__metro__': { closures: metroClosures, colors: metroColors, setText: setMetroClosure, setColor: setMetroClosureColor }, + '__d8__': { closures: d8Closures, colors: d8Colors, setText: setD8Closure, setColor: setD8ClosureColor }, + } as const + const isInfoRowKey = (k: string): k is keyof typeof infoRowMaps => + k === '__tunnel__' || k === '__sazlt__' || k === '__metro__' || k === '__d8__' + + const handleCopyCell = useCallback((key: string, dayIdx: number) => { + if (isInfoRowKey(key)) { + const m = infoRowMaps[key] + const text = m.closures.get(dayIdx) + const color = m.colors.get(dayIdx) + setClipboard({ + ...(text ? { v: text } : {}), + ...(color ? { color } : {}), + }) + setClipboardSource({ personId: key, dayIdx }) + return + } + const person = people.find(p => p.id === key) + if (!person) return + const cellData = person.data[String(dayIdx)] ?? {} + const comment = cellComments.get(`${key}-${dayIdx}`) + setClipboard({ ...cellData, ...(comment ? { comment } : {}) }) + setClipboardSource({ personId: key, dayIdx }) + }, [people, cellComments, infoRowMaps]) + + const handlePasteCells = useCallback((targets: { personId: string; dayIdx: number }[]) => { + if (!clipboard || targets.length === 0) return + for (const t of targets) { + if (clipboardSource && clipboardSource.personId === t.personId && clipboardSource.dayIdx === t.dayIdx) continue + // When pasting from an info row and the clipboard has no explicit color, + // inject the source row's characteristic color so the destination keeps the visual identity. + const extraColor = (!clipboard.color && clipboardSource && isInfoRowKey(clipboardSource.personId)) + ? INFO_ROW_COLORS[clipboardSource.personId] + : undefined + if (isInfoRowKey(t.personId)) { + const m = infoRowMaps[t.personId] + m.setText(t.dayIdx, clipboard.v ?? null) + m.setColor(t.dayIdx, clipboard.color ?? extraColor ?? null) + } else { + pasteCellTo(t.personId, t.dayIdx, { + ...clipboard, + ...(extraColor ? { color: extraColor } : {}), + comment: clipboard.comment ?? null, + }) + } + } + }, [clipboard, clipboardSource, pasteCellTo, infoRowMaps]) + + const handleClearClipboard = useCallback(() => { + setClipboard(null) + setClipboardSource(null) + }, []) + + // Retired people are hidden from all UI/FPD surfaces but their data stays in + // the schedule file (past-month exports keep working). + const visiblePeople = useMemo(() => people.filter(p => !p.retired), [people]) + + const tkbPeopleList = useMemo( + () => visiblePeople.filter(p => p.group === 'TKB'), + [visiblePeople] + ) + + const handleToggleTkbPerson = useCallback((id: string) => { + setHiddenTkbPeople(prev => { + const next = new Set(prev) + if (next.has(id)) next.delete(id); else next.add(id) + return next + }) + }, []) + + const handleSetAllTkbVisible = useCallback(() => { + setHiddenTkbPeople(new Set()) + }, []) + + const handleSetOnlyTkbPerson = useCallback((id: string) => { + setHiddenTkbPeople(new Set(tkbPeopleList.filter(p => p.id !== id).map(p => p.id))) + }, [tkbPeopleList]) + const handleExportPdf = useCallback(async (month: number) => { const { exportMonthPdf } = await import('./PdfExport') const year = data.dayIndex.find(d => d.month === month)?.year ?? new Date().getFullYear() - await exportMonthPdf(month, year, data.dayIndex, people, tunnelClosures, tunnelColors) - }, [data.dayIndex, people, tunnelClosures, tunnelColors]) + await exportMonthPdf(month, year, data.dayIndex, visiblePeople, tunnelClosures, tunnelColors, { + hide162, + showIT, + hiddenTkbPeople, + hiddenValues, + }) + }, [data.dayIndex, visiblePeople, tunnelClosures, tunnelColors, hide162, showIT, hiddenTkbPeople, hiddenValues]) const MONTH_NAMES_FULL: Record = { 1:'Leden',2:'Únor',3:'Březen',4:'Duben',5:'Květen',6:'Červen', @@ -501,6 +643,10 @@ function ScheduleApp({ fileId, fileName, data, compareFileId, onBack, onFileName : undefined const contextCellValue = contextCellData?.v const contextCellColor = contextCellData?.color + const contextCellDayColor = contextCellData?.dayColor + const contextCellNightColor = contextCellData?.nightColor + const contextCellDayValue = contextCellData?.dayValue + const contextCellNightValue = contextCellData?.nightValue // Determine which info row closures/colors/handlers to pass to context menu const contextInfoRowId = contextMenu?.infoRowId @@ -522,6 +668,24 @@ function ScheduleApp({ fileId, fileName, data, compareFileId, onBack, onFileName else if (contextInfoRowId === 'sazlt') setSazltClosureColor(dayIdx, color) }, [contextInfoRowId, setMetroClosureColor, setD8ClosureColor, setSazltClosureColor]) + // Unified color setter — routes to the correct setter based on row type (personId) + const handleSetAnyColor = useCallback((personId: string, dayIdx: number, color: string | null) => { + if (personId === '__tunnel__') setTunnelClosureColor(dayIdx, color) + else if (personId === '__metro__') setMetroClosureColor(dayIdx, color) + else if (personId === '__d8__') setD8ClosureColor(dayIdx, color) + else if (personId === '__sazlt__') setSazltClosureColor(dayIdx, color) + else setCellColor(personId, dayIdx, color) + }, [setTunnelClosureColor, setMetroClosureColor, setD8ClosureColor, setSazltClosureColor, setCellColor]) + + // Unified value setter — routes to the correct setter based on row type (personId) + const handleSetAnyValue = useCallback((personId: string, dayIdx: number, value: string | null) => { + if (personId === '__tunnel__') setTunnelClosure(dayIdx, value) + else if (personId === '__metro__') setMetroClosure(dayIdx, value) + else if (personId === '__d8__') setD8Closure(dayIdx, value) + else if (personId === '__sazlt__') setSazltClosure(dayIdx, value) + else setCell(personId, dayIdx, value) + }, [setTunnelClosure, setMetroClosure, setD8Closure, setSazltClosure, setCell]) + return (
@@ -579,6 +743,10 @@ function ScheduleApp({ fileId, fileName, data, compareFileId, onBack, onFileName onToggleD8={() => setShowD8(v => !v)} showSazlt={showSazlt} onToggleSazlt={() => setShowSazlt(v => !v)} + showIT={showIT} + onToggleIT={() => setShowIT(v => !v)} + theme={theme} + onToggleTheme={() => setTheme(t => t === 'dark' ? 'light' : 'dark')} hide162={hide162} onToggle162={() => setHide162(v => !v)} hiddenValues={hiddenValues} @@ -590,13 +758,22 @@ function ScheduleApp({ fileId, fileName, data, compareFileId, onBack, onFileName diffFileName={compareFileName} onCloseDiff={() => { setCompareData(null); setCompareFileName(null); onClearCompare() }} onExportPdf={handleExportPdf} + onExportExcel={currentFileId ? (month: number) => { + const year = data.dayIndex.find(d => d.month === month)?.year ?? new Date().getFullYear() + window.location.href = `/api/files/${currentFileId}/export-excel?month=${month}&year=${year}` + } : undefined} onShowProposals={isReadOnly ? undefined : () => setShowProposals(true)} onCheckFpd={() => setShowFpd(true)} isReadOnly={isReadOnly} + tkbPeopleList={tkbPeopleList} + hiddenTkbPeople={hiddenTkbPeople} + onToggleTkbPerson={handleToggleTkbPerson} + onSetAllTkbVisible={handleSetAllTkbVisible} + onSetOnlyTkbPerson={handleSetOnlyTkbPerson} /> {} : onCellPointerDown} onSetCell={isReadOnly ? () => {} : setCell} + onSetCellHalfValue={isReadOnly ? () => {} : setCellHalfValue} onSetTunnelClosure={isReadOnly ? () => {} : setTunnelClosure} onSetMetroClosure={isReadOnly ? () => {} : setMetroClosure} onSetD8Closure={isReadOnly ? () => {} : setD8Closure} @@ -617,13 +795,21 @@ function ScheduleApp({ fileId, fileName, data, compareFileId, onBack, onFileName showMetro={showMetro} showD8={showD8} showSazlt={showSazlt} + showIT={showIT} hide162={hide162} + hiddenTkbPeople={hiddenTkbPeople} hiddenValues={hiddenValues} scrollRef={scrollRef} onContextMenu={isReadOnly ? () => {} : handleContextMenu} onTunnelContextMenu={isReadOnly ? () => {} : handleTunnelContextMenu} onInfoRowContextMenu={isReadOnly ? () => {} : handleInfoRowContextMenu} compareData={compareData} + isReadOnly={isReadOnly} + clipboardSource={clipboardSource} + hasClipboard={!!clipboard} + onCopyCell={isReadOnly ? () => {} : handleCopyCell} + onPasteCells={isReadOnly ? () => {} : handlePasteCells} + onClearClipboard={handleClearClipboard} /> @@ -634,6 +820,10 @@ function ScheduleApp({ fileId, fileName, data, compareFileId, onBack, onFileName personName={contextPerson?.name || null} cellValue={contextCellValue} cellColor={contextCellColor} + cellDayColor={contextCellDayColor} + cellNightColor={contextCellNightColor} + cellDayValue={contextCellDayValue} + cellNightValue={contextCellNightValue} existingDayComment={dayComments.get(contextMenu.dayIdx)} existingCellComment={contextMenu.personId ? cellComments.get(`${contextMenu.personId}-${contextMenu.dayIdx}`) : undefined} existingTunnelClosure={tunnelClosures.get(contextMenu.dayIdx)} @@ -649,8 +839,15 @@ function ScheduleApp({ fileId, fileName, data, compareFileId, onBack, onFileName onRemoveCellComment={removeCellComment} onSetTunnelClosure={setTunnelClosure} onSetCellColor={setCellColor} + onSetCellHalfColor={setCellHalfColor} + onSetCellHalfValue={setCellHalfValue} onSetTunnelClosureColor={setTunnelClosureColor} onSetInfoRowColor={handleInfoRowSetColor} + onSetAnyColor={handleSetAnyColor} + onSetAnyValue={handleSetAnyValue} + hasClipboard={!!clipboard} + onCopyCell={handleCopyCell} + onPasteCells={handlePasteCells} onClose={() => { setContextMenu(null); setSelectedDenDays([]) }} /> )} @@ -664,12 +861,16 @@ function ScheduleApp({ fileId, fileName, data, compareFileId, onBack, onFileName month={activeMonth} year={data.dayIndex.find(d => d.month === activeMonth)?.year ?? 2026} dayIndex={data.dayIndex} - people={people} + people={visiblePeople} onClose={() => setShowFpd(false)} - onExportDochazka={currentFileId ? () => { + onExportDochazka={currentFileId && !isReadOnly ? () => { const year = data.dayIndex.find(d => d.month === activeMonth)?.year ?? 2026 window.location.href = `/api/files/${currentFileId}/export-dochazka?month=${activeMonth}&year=${year}` } : undefined} + onExportHarmonogram={currentFileId && canExportHarmonogram ? () => { + const year = data.dayIndex.find(d => d.month === activeMonth)?.year ?? 2026 + window.location.href = `/api/files/${currentFileId}/export-harmonogram?month=${activeMonth}&year=${year}` + } : undefined} /> )}
diff --git a/web/src/ContextMenu.tsx b/web/src/ContextMenu.tsx index 9921b6c..496482a 100644 --- a/web/src/ContextMenu.tsx +++ b/web/src/ContextMenu.tsx @@ -9,6 +9,10 @@ interface ContextMenuProps { personName: string | null cellValue: string | undefined cellColor: string | undefined + cellDayColor: string | undefined + cellNightColor: string | undefined + cellDayValue: string | undefined + cellNightValue: string | undefined existingDayComment: string | undefined existingCellComment: string | undefined existingTunnelClosure: string | undefined @@ -24,8 +28,15 @@ interface ContextMenuProps { onRemoveCellComment: (personId: string, dayIdx: number) => void onSetTunnelClosure: (dayIdx: number, text: string | null) => void onSetCellColor: (personId: string, dayIdx: number, color: string | null) => void + onSetCellHalfColor: (personId: string, dayIdx: number, target: 'day' | 'night', color: string | null) => void + onSetCellHalfValue: (personId: string, dayIdx: number, target: 'day' | 'night', value: string | null) => void onSetTunnelClosureColor: (dayIdx: number, color: string | null) => void onSetInfoRowColor: (dayIdx: number, color: string | null) => void + onSetAnyColor: (personId: string, dayIdx: number, color: string | null) => void + onSetAnyValue: (personId: string, dayIdx: number, value: string | null) => void + hasClipboard: boolean + onCopyCell: (personId: string, dayIdx: number) => void + onPasteCells: (targets: { personId: string; dayIdx: number }[]) => void onClose: () => void } @@ -35,6 +46,10 @@ export function ContextMenu({ personName, cellValue, cellColor, + cellDayColor, + cellNightColor, + cellDayValue, + cellNightValue, existingDayComment, existingCellComment, existingTunnelClosure, @@ -50,16 +65,40 @@ export function ContextMenu({ onRemoveCellComment, onSetTunnelClosure, onSetCellColor, + onSetCellHalfColor, + onSetCellHalfValue, onSetTunnelClosureColor, onSetInfoRowColor, + onSetAnyColor, + onSetAnyValue, + hasClipboard, + onCopyCell, + onPasteCells, onClose, }: ContextMenuProps) { + const [colorTarget, setColorTarget] = useState<'cell' | 'day' | 'night'>('cell') const isMultiSelect = selectedCells.length > 1 + const isTunnelRow = !!state.isTunnelRow + const isInfoRow = !!state.infoRowId + + // Canonical key for the currently right-clicked info row (null for person/DEN rows) + const infoRowKey: string | null = isTunnelRow ? '__tunnel__' + : isInfoRow ? (state.infoRowId === 'metro' ? '__metro__' : state.infoRowId === 'd8' ? '__d8__' : '__sazlt__') + : null + const [showCommentInput, setShowCommentInput] = useState(false) const [commentText, setCommentText] = useState('') const [showCellCommentInput, setShowCellCommentInput] = useState(false) const [cellCommentText, setCellCommentText] = useState('') const [cellValueInput, setCellValueInput] = useState(cellValue ?? '') + const [infoValueInput, setInfoValueInput] = useState(existingTunnelClosure ?? existingInfoRowClosure ?? '') + + // When toggle changes, refresh input to show the matching field's value + useEffect(() => { + if (colorTarget === 'cell') setCellValueInput(cellValue ?? '') + else if (colorTarget === 'day') setCellValueInput(cellDayValue ?? '') + else if (colorTarget === 'night') setCellValueInput(cellNightValue ?? '') + }, [colorTarget, cellValue, cellDayValue, cellNightValue]) const cellCommentInputRef = useRef(null) const commentInputRef = useRef(null) const cellValueInputRef = useRef(null) @@ -108,26 +147,36 @@ export function ContextMenu({ onClose() } - const handleSetCellValue = () => { - const val = cellValueInput.trim() - if (isMultiSelect) { - for (const cell of selectedCells) { - onSetCell(cell.personId, cell.dayIdx, val || null) + const INFO_KEYS = new Set(['__tunnel__', '__metro__', '__d8__', '__sazlt__']) + + const applyValueToCells = (val: string | null) => { + const cells = isMultiSelect ? selectedCells : (state.personId ? [{ personId: state.personId, dayIdx: state.dayIdx }] : []) + for (const cell of cells) { + if (colorTarget === 'cell') { + onSetAnyValue(cell.personId, cell.dayIdx, val) + } else if (!INFO_KEYS.has(cell.personId)) { + // half-value only for person cells + onSetCellHalfValue(cell.personId, cell.dayIdx, colorTarget, val) } - } else if (state.personId) { - onSetCell(state.personId, state.dayIdx, val || null) } + } + + const applyInfoValue = (val: string | null) => { + const cells = isMultiSelect + ? selectedCells + : infoRowKey ? [{ personId: infoRowKey, dayIdx: state.dayIdx }] : [] + for (const cell of cells) { + onSetAnyValue(cell.personId, cell.dayIdx, val) + } + } + + const handleSetCellValue = () => { + applyValueToCells(cellValueInput.trim() || null) onClose() } const handleClearCell = () => { - if (isMultiSelect) { - for (const cell of selectedCells) { - onSetCell(cell.personId, cell.dayIdx, null) - } - } else if (state.personId) { - onSetCell(state.personId, state.dayIdx, null) - } + applyValueToCells(null) onClose() } @@ -137,20 +186,78 @@ export function ContextMenu({ 7:'Červenec',8:'Srpen',9:'Září',10:'Říjen',11:'Listopad',12:'Prosinec', } const dateStr = isPms ? `Práce mimo směnu — ${MONTH_NAMES_CTX[dayInfo.month] ?? dayInfo.month}` : `${dayInfo.day}.${dayInfo.month}.${dayInfo.year}` - const isTunnelRow = !!state.isTunnelRow - const isInfoRow = !!state.infoRowId const infoRowId = state.infoRowId const infoRowLabel = infoRowId === 'metro' ? 'METRO' : infoRowId === 'd8' ? 'D8' : '' const isDenRow = !state.personId && !isTunnelRow && !isInfoRow const multiDayLabel = selectedDays.length > 1 ? ` (${selectedDays.length} dnu)` : '' + const isPersonCell = !isTunnelRow && !isInfoRow && !isDenRow + + const renderTargetToggle = () => { + if (!isPersonCell) return null + const toggleBtn = (target: 'cell' | 'day' | 'night', label: string) => ( + + ) + return ( +
+
Cíl
+
+ {toggleBtn('cell', 'Celá')} + {toggleBtn('day', 'Denní (←)')} + {toggleBtn('night', 'Noční (→)')} +
+
+ ) + } + const renderColorPalette = () => { const activeColor = isTunnelRow ? existingTunnelColor : isInfoRow ? existingInfoRowColor + : colorTarget === 'day' + ? cellDayColor + : colorTarget === 'night' + ? cellNightColor : cellColor + const INFO_KEYS = new Set(['__tunnel__', '__metro__', '__d8__', '__sazlt__']) + + const applyColor = (color: string | null) => { + if (isMultiSelect) { + // Apply to all selected cells — routes to correct setter per row type + for (const cell of selectedCells) { + if (colorTarget === 'cell') { + onSetAnyColor(cell.personId, cell.dayIdx, color) + } else if (!INFO_KEYS.has(cell.personId)) { + // half-color only makes sense for person cells + onSetCellHalfColor(cell.personId, cell.dayIdx, colorTarget, color) + } + } + } else { + // Single-cell mode + if (isTunnelRow) { + onSetTunnelClosureColor(state.dayIdx, color) + } else if (isInfoRow) { + onSetInfoRowColor(state.dayIdx, color) + } else if (colorTarget === 'cell') { + if (state.personId) onSetCellColor(state.personId, state.dayIdx, color) + } else { + if (state.personId) onSetCellHalfColor(state.personId, state.dayIdx, colorTarget, color) + } + } + onClose() + } + return (
Barva pozadi
@@ -160,20 +267,7 @@ export function ContextMenu({ return (
- {isInfoRow ? ( - <> - {existingInfoRowClosure && ( -
- ▶ - {existingInfoRowClosure} + {isInfoRow || isTunnelRow ? (() => { + const rowKey = isTunnelRow + ? '__tunnel__' + : infoRowId === 'metro' ? '__metro__' + : infoRowId === 'd8' ? '__d8__' + : '__sazlt__' + return ( + <> + {isTunnelRow && existingTunnelClosure && ( +
+ ▶ + {existingTunnelClosure} +
+ )} + {isInfoRow && existingInfoRowClosure && ( +
+ ▶ + {existingInfoRowClosure} +
+ )} + + {hasClipboard && ( + + )} + {/* Text input — same as person row */} +
+
+ setInfoValueInput(e.target.value)} + onKeyDown={e => { if (e.key === 'Enter') { applyInfoValue(infoValueInput.trim() || null); onClose() } }} + placeholder="Text..." + className="flex-1 bg-slate-900 border border-slate-600 rounded px-2 py-1.5 text-xs text-slate-200 + placeholder-slate-500 outline-none focus:border-blue-500 transition-colors" + /> + +
- )} - {renderColorPalette()} - - ) : isTunnelRow ? ( - <> - {existingTunnelClosure && ( -
- ▶ - {existingTunnelClosure} -
- )} - {renderColorPalette()} - - ) : isDenRow ? ( + {renderColorPalette()} + + ) + })() : isDenRow ? ( <> {existingDayComment && !showCommentInput ? ( <> @@ -310,6 +451,35 @@ export function ContextMenu({ ) : ( <> + {/* Copy / Paste */} + {!isMultiSelect && state.personId && ( + + )} + {hasClipboard && state.personId && ( + + )} + + {renderTargetToggle()} {/* Cell value editing */}
diff --git a/web/src/FpdCheck.tsx b/web/src/FpdCheck.tsx index d784077..8e4e032 100644 --- a/web/src/FpdCheck.tsx +++ b/web/src/FpdCheck.tsx @@ -6,6 +6,13 @@ const MONTH_NAMES: Record = { 7:'Červenec',8:'Srpen',9:'Září',10:'Říjen',11:'Listopad',12:'Prosinec', } +// Part-time people: half the daily hours (4h/day) and half the FPD target. +const HALF_TIME_PEOPLE = new Set(['Dittrich Vladimír']) + +// Info-only people (e.g. contractors): shown in the FPD check for reference but +// never flagged as a shortfall, even when below quota. +const INFO_ONLY_PEOPLE = new Set(['Zábranský Petr']) + interface FpdResult { personId: string name: string @@ -13,6 +20,7 @@ interface FpdResult { workedHours: number fpd: number diff: number // positive = over, negative = under + infoOnly: boolean // contractor — never warn on shortfall } function getHoursForValue(v: string): number { @@ -39,27 +47,52 @@ export function computeFpd( } const fpd = workDays * 8 - // Check TKB people only - const tkbPeople = people.filter(p => p.group === 'TKB') + // Check TKB people only — exclude the "162" group (handled separately) + const EXCLUDED = new Set(['Pauzer Libor', 'Vörös Pavel', 'Janouš Petr', 'Franek Lukáš', 'Svoboda Daniel']) + const tkbPeople = people.filter(p => p.group === 'TKB' && !EXCLUDED.has(p.name) && !p.retired) const results: FpdResult[] = [] for (const person of tkbPeople) { + // Half-time people work half the daily hours: their FPD target is halved + // and a vacation day ('D') deducts only their daily hours (4h, not 8h). + const dailyHours = HALF_TIME_PEOPLE.has(person.name) ? 4 : 8 let workedHours = 0 + let vacationHours = 0 // D vacation reduces the FPD target (full day = dailyHours) for (const d of monthDays) { const cellData = person.data[String(d.idx)] - const value = cellData?.v - if (value) { - workedHours += getHoursForValue(value) + if (!cellData) continue + const isWorkDay = !d.weekend && !holidaySet.has(d.day) + const isSplit = !!(cellData.dayColor || cellData.nightColor || cellData.dayValue || cellData.nightValue) + const collect = (v: string | undefined) => { + if (!v) return + // D vacation on a working day reduces the FPD target. + if (v === 'D') { if (isWorkDay) vacationHours += dailyHours } + else if (v === 'D/2') { if (isWorkDay) vacationHours += dailyHours / 2 } + // N sick on a working day is credited as a normal worked day (daily hours), + // so sick leave neither helps nor hurts the FPD balance. + else if (v === 'N') { if (isWorkDay) workedHours += dailyHours } + else workedHours += getHoursForValue(v) + } + if (isSplit) { + collect(cellData.dayValue) + collect(cellData.nightValue) + } else { + collect(cellData.v) } } - const diff = workedHours - fpd + // PMS (práce mimo směnu) is stored at day-index -month; counts as worked hours. + const pms = person.data[String(-month)]?.v + if (pms) workedHours += getHoursForValue(pms) + const personFpd = workDays * dailyHours - vacationHours + const diff = workedHours - personFpd results.push({ personId: person.id, name: person.name, note: person.note, workedHours, - fpd, + fpd: personFpd, diff, + infoOnly: INFO_ONLY_PEOPLE.has(person.name), }) } @@ -73,12 +106,14 @@ interface FpdModalProps { people: Person[] onClose: () => void onExportDochazka?: () => void + onExportHarmonogram?: () => void } -export function FpdModal({ month, year, dayIndex, people, onClose, onExportDochazka }: FpdModalProps) { +export function FpdModal({ month, year, dayIndex, people, onClose, onExportDochazka, onExportHarmonogram }: FpdModalProps) { const { fpd, workDays, results } = computeFpd(month, year, dayIndex, people) - const under = results.filter(r => r.diff < 0) - const okOrOver = results.filter(r => r.diff >= 0) + // Info-only people (contractors) never count as a shortfall + const under = results.filter(r => r.diff < 0 && !r.infoOnly) + const okOrOver = results.filter(r => r.diff >= 0 || r.infoOnly) return (
@@ -132,32 +167,51 @@ export function FpdModal({ month, year, dayIndex, people, onClose, onExportDocha
0 ? 'bg-slate-700/30 border border-slate-600/50' : 'bg-green-900/20 border border-green-700/30'}`} + ${r.infoOnly ? 'bg-slate-700/20 border border-slate-600/40' + : r.diff > 0 ? 'bg-slate-700/30 border border-slate-600/50' + : 'bg-green-900/20 border border-green-700/30'}`} > {r.name} {r.note && ({r.note})} + {r.infoOnly && (info)} {r.workedHours}h / {r.fpd}h - 0 ? 'text-slate-300' : 'text-green-400'}`}> - {r.diff > 0 ? `+${r.diff}h` : '✓'} - + {r.infoOnly ? ( + {r.diff > 0 ? `+${r.diff}` : r.diff}h + ) : ( + 0 ? 'text-slate-300' : 'text-green-400'}`}> + {r.diff > 0 ? `+${r.diff}h` : '✓'} + + )}
))}
- {onExportDochazka && ( -
- + {(onExportDochazka || onExportHarmonogram) && ( +
+ {onExportHarmonogram && ( + + )} + {onExportDochazka && ( + + )}
)}
diff --git a/web/src/Login.tsx b/web/src/Login.tsx index 0d48e26..e36273d 100644 --- a/web/src/Login.tsx +++ b/web/src/Login.tsx @@ -4,17 +4,22 @@ const VALID_USER = 'tkb' const VALID_PASS = 'sluzby' const VIEWER_USER = 'prohlizec' const VIEWER_PASS = 'pohled' +const CIS_USER = 'cis' +const CIS_PASS = 'import' const AUTH_KEY = 'tkb_auth' const ROLE_KEY = 'tkb_role' -export type UserRole = 'editor' | 'viewer' +export type UserRole = 'editor' | 'viewer' | 'cis' export function isAuthenticated(): boolean { return localStorage.getItem(AUTH_KEY) === 'true' } export function getAuthRole(): UserRole { - return localStorage.getItem(ROLE_KEY) === 'viewer' ? 'viewer' : 'editor' + const r = localStorage.getItem(ROLE_KEY) + if (r === 'viewer') return 'viewer' + if (r === 'cis') return 'cis' + return 'editor' } function setAuthenticated(role: UserRole): void { @@ -44,6 +49,9 @@ export function Login({ onLogin }: LoginProps) { } else if (username === VIEWER_USER && password === VIEWER_PASS) { setAuthenticated('viewer') onLogin('viewer') + } else if (username === CIS_USER && password === CIS_PASS) { + setAuthenticated('cis') + onLogin('cis') } else { setError(true) setTimeout(() => setError(false), 3000) diff --git a/web/src/PdfExport.ts b/web/src/PdfExport.ts index 98d7253..2abe70e 100644 --- a/web/src/PdfExport.ts +++ b/web/src/PdfExport.ts @@ -2,6 +2,15 @@ import jsPDF from 'jspdf' import type { DayInfo, Person } from './types' import { getCellStyle } from './cellColors' +const HIDE_162_NAMES = ['Pauzer Libor', 'Vörös Pavel', 'Janouš Petr', 'Franek Lukáš', 'Svoboda Daniel'] + +interface PdfFilters { + hide162: boolean + showIT: boolean + hiddenTkbPeople: Set + hiddenValues: Set +} + const MONTH_NAMES: Record = { 1: 'Leden', 2: 'Únor', 3: 'Březen', 4: 'Duben', 5: 'Květen', 6: 'Červen', 7: 'Červenec', 8: 'Srpen', 9: 'Září', 10: 'Říjen', 11: 'Listopad', 12: 'Prosinec', @@ -40,6 +49,7 @@ export async function exportMonthPdf( people: Person[], tunnelClosures: Map, tunnelColors: Map, + filters: PdfFilters = { hide162: false, showIT: true, hiddenTkbPeople: new Set(), hiddenValues: new Set() }, ) { const doc = new jsPDF({ orientation: 'landscape', unit: 'mm', format: 'a4' }) const hasFont = await loadFont(doc) @@ -52,8 +62,10 @@ export async function exportMonthPdf( const startIdx = Math.max(0, firstDayIdx - daysFromPrevMonth) const days = dayIndex.slice(startIdx, startIdx + 34) - const tkbPeople = people.filter(p => p.group === 'TKB') - const itPeople = people.filter(p => p.group === 'IT') + let tkbPeople = people.filter(p => p.group === 'TKB') + if (filters.hide162) tkbPeople = tkbPeople.filter(p => !HIDE_162_NAMES.includes(p.name)) + if (filters.hiddenTkbPeople.size > 0) tkbPeople = tkbPeople.filter(p => !filters.hiddenTkbPeople.has(p.id)) + const itPeople = filters.showIT ? people.filter(p => p.group === 'IT') : [] // Layout: fill entire A4 landscape (297 x 210 mm) const pageW = 297 @@ -63,11 +75,14 @@ export async function exportMonthPdf( const daysPerPage = 17 const cellW = (pageW - margin * 2 - nameColW) / daysPerPage - // Row height to fill full page + // Row height: fits unfiltered set; caps when filters reduce rows + // (so a single-person filter doesn't stretch the row to fill the page) const headerH = 24 - const totalDataRows = tkbPeople.length + itPeople.length + 2 + const sectionHeaderRows = (filters.showIT ? 2 : 1) + const totalDataRows = tkbPeople.length + itPeople.length + sectionHeaderRows const availableH = pageH - margin * 2 - headerH - const rowH = availableH / totalDataRows + const MAX_ROW_H = 7 // mm — keeps cells reasonable size when filtered + const rowH = Math.min(MAX_ROW_H, availableH / totalDataRows) // Font sizes — match day number size for readability const nameFontSize = 9 @@ -189,19 +204,21 @@ export async function exportMonthPdf( if (showNames) doc.text('POHOTOVOST TKB', margin + 2, curY + rowH * 0.7) curY += rowH - renderPeople(doc, tkbPeople, pageDays, curY, margin, showNames ? nameColW : 0, gridStartX, cellW, rowH, showNames, nameFontSize, cellFontSize, month) + renderPeople(doc, tkbPeople, pageDays, curY, margin, showNames ? nameColW : 0, gridStartX, cellW, rowH, showNames, nameFontSize, cellFontSize, month, filters.hiddenValues) curY += tkbPeople.length * rowH - // IT section header - doc.setFillColor(13, 148, 136) - doc.rect(margin, curY, totalW, rowH, 'F') - doc.setFontSize(sectionFontSize) - doc.setTextColor(255, 255, 255) - if (showNames) doc.text('POHOTOVOST IT', margin + 2, curY + rowH * 0.7) - curY += rowH + if (filters.showIT) { + // IT section header + doc.setFillColor(13, 148, 136) + doc.rect(margin, curY, totalW, rowH, 'F') + doc.setFontSize(sectionFontSize) + doc.setTextColor(255, 255, 255) + if (showNames) doc.text('POHOTOVOST IT', margin + 2, curY + rowH * 0.7) + curY += rowH - renderPeople(doc, itPeople, pageDays, curY, margin, showNames ? nameColW : 0, gridStartX, cellW, rowH, showNames, nameFontSize, cellFontSize, month) - curY += itPeople.length * rowH + renderPeople(doc, itPeople, pageDays, curY, margin, showNames ? nameColW : 0, gridStartX, cellW, rowH, showNames, nameFontSize, cellFontSize, month, filters.hiddenValues) + curY += itPeople.length * rowH + } // Outer border doc.setDrawColor(0, 0, 0) @@ -218,10 +235,12 @@ function renderPeople( startY: number, margin: number, nameColW: number, gridStartX: number, cellW: number, rowH: number, showNames: boolean, nameFontSize: number, cellFontSize: number, activeMonth: number, + hiddenValues: Set, ) { const totalW = nameColW + days.length * cellW const sectionH = people.length * rowH const textY = rowH * 0.65 + const halfW = cellW / 2 // Pass 1: Fill backgrounds people.forEach((person, pi) => { @@ -230,22 +249,62 @@ function renderPeople( const x = gridStartX + di * cellW const isOtherMonth = d.month !== activeMonth const cellData = person.data[String(d.idx)] - const value = cellData?.v ?? '' + const rawValue = cellData?.v ?? '' + const value = hiddenValues.has(rawValue) ? '' : rawValue const manualColor = cellData?.color + const dayColor = cellData?.dayColor + const nightColor = cellData?.nightColor + const dayValue = cellData?.dayValue && !hiddenValues.has(cellData.dayValue) ? cellData.dayValue : '' + const nightValue = cellData?.nightValue && !hiddenValues.has(cellData.nightValue) ? cellData.nightValue : '' + const renderAsSplit = !!(dayColor || nightColor || dayValue || nightValue) const style = getCellStyle(value || undefined) if (isOtherMonth) { - // Light gray wash for other month cells doc.setFillColor(245, 245, 245) doc.rect(x, y, cellW, rowH, 'F') - // If there's a value, draw it faded - if (manualColor || style) { + // For other-month cells, fade any colored content + if (renderAsSplit) { + doc.setGState(doc.GState({ opacity: 0.35 })) + const dayBg = dayColor ?? getCellStyle(dayValue || undefined)?.bg + const nightBg = nightColor ?? getCellStyle(nightValue || undefined)?.bg + if (dayBg) { + const rgb = hexToRgb(dayBg) + doc.setFillColor(rgb.r, rgb.g, rgb.b) + doc.rect(x, y, halfW, rowH, 'F') + } + if (nightBg) { + const rgb = hexToRgb(nightBg) + doc.setFillColor(rgb.r, rgb.g, rgb.b) + doc.rect(x + halfW, y, halfW, rowH, 'F') + } + doc.setGState(doc.GState({ opacity: 1 })) + } else if (manualColor || style) { doc.setGState(doc.GState({ opacity: 0.35 })) const rgb = hexToRgb(manualColor ?? style!.bg) doc.setFillColor(rgb.r, rgb.g, rgb.b) doc.rect(x, y, cellW, rowH, 'F') doc.setGState(doc.GState({ opacity: 1 })) } + } else if (renderAsSplit) { + // Split mode: Day = LEFT, Night = RIGHT + const dayBg = dayColor ?? getCellStyle(dayValue || undefined)?.bg + const nightBg = nightColor ?? getCellStyle(nightValue || undefined)?.bg + if (dayBg) { + const rgb = hexToRgb(dayBg) + doc.setFillColor(rgb.r, rgb.g, rgb.b) + doc.rect(x, y, halfW, rowH, 'F') + } else if (d.weekend) { + doc.setFillColor(240, 240, 240) + doc.rect(x, y, halfW, rowH, 'F') + } + if (nightBg) { + const rgb = hexToRgb(nightBg) + doc.setFillColor(rgb.r, rgb.g, rgb.b) + doc.rect(x + halfW, y, halfW, rowH, 'F') + } else if (d.weekend) { + doc.setFillColor(240, 240, 240) + doc.rect(x + halfW, y, halfW, rowH, 'F') + } } else if (manualColor) { const rgb = hexToRgb(manualColor) doc.setFillColor(rgb.r, rgb.g, rgb.b) @@ -301,9 +360,43 @@ function renderPeople( days.forEach((d, di) => { const x = gridStartX + di * cellW const cellData = person.data[String(d.idx)] - const value = cellData?.v ?? '' - if (value) { - const isOtherMonth = d.month !== activeMonth + const rawValue = cellData?.v ?? '' + const value = hiddenValues.has(rawValue) ? '' : rawValue + const dayValue = cellData?.dayValue && !hiddenValues.has(cellData.dayValue) ? cellData.dayValue : '' + const nightValue = cellData?.nightValue && !hiddenValues.has(cellData.nightValue) ? cellData.nightValue : '' + const dayColor = cellData?.dayColor + const nightColor = cellData?.nightColor + const renderAsSplit = !!(dayColor || nightColor || dayValue || nightValue) + const isOtherMonth = d.month !== activeMonth + + if (renderAsSplit) { + // Render text in each half: Day = LEFT, Night = RIGHT + const halfFontSize = Math.max(6, cellFontSize - 1) + if (dayValue) { + const dayStyle = dayColor + ? (isLightColor(dayColor) ? '#333333' : '#ffffff') + : (getCellStyle(dayValue)?.text ?? '#333333') + const rgb = hexToRgb(dayStyle) + if (isOtherMonth) doc.setTextColor(200, 200, 200) + else doc.setTextColor(rgb.r, rgb.g, rgb.b) + doc.setFontSize(halfFontSize) + doc.text(dayValue, x + halfW / 2, y + textY, { align: 'center' }) + } + if (nightValue) { + const nightStyle = nightColor + ? (isLightColor(nightColor) ? '#333333' : '#ffffff') + : (getCellStyle(nightValue)?.text ?? '#333333') + const rgb = hexToRgb(nightStyle) + if (isOtherMonth) doc.setTextColor(200, 200, 200) + else doc.setTextColor(rgb.r, rgb.g, rgb.b) + doc.setFontSize(halfFontSize) + doc.text(nightValue, x + halfW + halfW / 2, y + textY, { align: 'center' }) + } + // Thin divider between halves + doc.setDrawColor(160, 160, 160) + doc.setLineWidth(0.1) + doc.line(x + halfW, y, x + halfW, y + rowH) + } else if (value) { const manualColor = cellData?.color const style = getCellStyle(value || undefined) const textColor = manualColor diff --git a/web/src/ScheduleTable.tsx b/web/src/ScheduleTable.tsx index 48cacfa..e3a4386 100644 --- a/web/src/ScheduleTable.tsx +++ b/web/src/ScheduleTable.tsx @@ -39,6 +39,7 @@ interface ScheduleTableProps { dragState: DragState | null onCellPointerDown: (e: React.PointerEvent, personId: string, dayIdx: number, value: string) => void onSetCell: (personId: string, dayIdx: number, value: string | null) => void + onSetCellHalfValue: (personId: string, dayIdx: number, target: 'day' | 'night', value: string | null) => void onSetTunnelClosure: (dayIdx: number, text: string | null) => void onSetMetroClosure: (dayIdx: number, text: string | null) => void onSetD8Closure: (dayIdx: number, text: string | null) => void @@ -47,18 +48,27 @@ interface ScheduleTableProps { showMetro: boolean showD8: boolean showSazlt: boolean + showIT: boolean hide162: boolean + hiddenTkbPeople: Set hiddenValues: Set onContextMenu: (dayIdx: number, personId: string | null, x: number, y: number, selectedCells: SelectedCell[]) => void - onTunnelContextMenu: (dayIdx: number, x: number, y: number) => void - onInfoRowContextMenu: (dayIdx: number, infoRowId: string, x: number, y: number) => void + onTunnelContextMenu: (dayIdx: number, x: number, y: number, selectedCells: SelectedCell[]) => void + onInfoRowContextMenu: (dayIdx: number, infoRowId: string, x: number, y: number, selectedCells: SelectedCell[]) => void compareData?: ScheduleData | null + isReadOnly?: boolean + clipboardSource: { personId: string; dayIdx: number } | null + hasClipboard: boolean + onCopyCell: (personId: string, dayIdx: number) => void + onPasteCells: (targets: { personId: string; dayIdx: number }[]) => void + onClearClipboard: () => void } interface EditingCell { personId: string | '__tunnel__' | '__metro__' | '__d8__' | '__sazlt__' dayIdx: number value: string + target?: 'cell' | 'day' | 'night' // person-cell only; default 'cell' } // ---------- PersonRow (memoized) ---------- @@ -84,6 +94,9 @@ const PersonRow = memo(function PersonRow({ onCellDragSelectMove, pmsAfterSet, pmsMonthAtPos, + isReadOnly, + rowIndex, + clipboardSource, }: { person: Person dayIndex: DayInfo[] @@ -95,7 +108,7 @@ const PersonRow = memo(function PersonRow({ onCellPointerDown: (e: React.PointerEvent, personId: string, dayIdx: number, value: string) => void onContextMenu: (dayIdx: number, personId: string | null, x: number, y: number) => void editingCell: EditingCell | null - onStartEdit: (personId: string, dayIdx: number, currentValue: string) => void + onStartEdit: (personId: string, dayIdx: number, currentValue: string, target?: 'cell' | 'day' | 'night') => void onEditChange: (value: string) => void onEditConfirm: () => void onEditCancel: () => void @@ -105,17 +118,28 @@ const PersonRow = memo(function PersonRow({ onCellDragSelectMove: (personId: string, dayIdx: number) => void pmsAfterSet: Set pmsMonthAtPos: Map + isReadOnly: boolean + rowIndex: number + clipboardSource: { personId: string; dayIdx: number } | null }) { const isDragging = dragState?.personId === person.id + const zebra = rowIndex % 2 === 1 ? 'bg-slate-500/20' : '' return ( -
+
{dayIndex.map((d, i) => { const cellData = person.data[String(d.idx)] const value = cellData?.v ?? '' - const isColorOnly = !value && !!cellData?.color + const dayColor = cellData?.dayColor + const nightColor = cellData?.nightColor + const dayValue = cellData?.dayValue ?? '' + const nightValue = cellData?.nightValue ?? '' + const hasHalfMarks = !!(dayColor || nightColor || dayValue || nightValue) + const isColorOnly = !value && !dayValue && !nightValue && (!!cellData?.color || !!(dayColor || nightColor)) const isValueHidden = !!(value && hiddenValues.has(value)) || (isColorOnly && hiddenValues.has('__color_only__')) const style = getCellStyleForPerson(value || undefined, person.note) + const dayStyleObj = getCellStyleForPerson(dayValue || undefined, person.note) + const nightStyleObj = getCellStyleForPerson(nightValue || undefined, person.note) const isHoliday = holidays.has(`${d.year}-${d.month}-${d.day}`) const isWeekend = d.weekend || isHoliday const commentKey = `${person.id}-${d.idx}` @@ -129,7 +153,7 @@ const PersonRow = memo(function PersonRow({ const bgStyle: React.CSSProperties = {} let className = `flex items-center justify-center text-sm font-mono font-bold relative - border-r border-slate-700/20 + border-r border-slate-500/40 ${isMonthStart ? 'border-l-2 border-l-slate-500' : ''}` if (isDragOriginal) { @@ -140,8 +164,14 @@ const PersonRow = memo(function PersonRow({ className += ' border-2 border-dashed border-amber-400/70' } + // Split mode is active when any half data exists. In split mode, the + // legacy whole-cell value (v) and color are hidden; halves take over. + const renderAsSplit = hasHalfMarks && !isValueHidden + if (isValueHidden) { - // Hidden by filter — show as empty + if (isWeekend) className += ' bg-slate-800/50' + } else if (renderAsSplit) { + // Halves render their own bg via overlays; keep weekend tint as fallback if (isWeekend) className += ' bg-slate-800/50' } else { const manualColor = cellData?.color @@ -161,11 +191,24 @@ const PersonRow = memo(function PersonRow({ className += ' ring-2 ring-blue-500 ring-inset z-10' } - const hasCellData = !!(value || cellData?.color) + const isClipboardSource = !!clipboardSource && clipboardSource.personId === person.id && clipboardSource.dayIdx === d.idx + if (isClipboardSource && !isSelected) { + className += ' z-10' + bgStyle.outline = '2px dashed #60a5fa' + bgStyle.outlineOffset = '-2px' + } + + const hasCellData = !!(value || cellData?.color || hasHalfMarks) if (hasCellData && !isDragOriginal) { className += ' cursor-grab active:cursor-grabbing' } + // Compute half styles (bg + text color) for split mode + const nightBg = nightColor ?? nightStyleObj?.bg + const nightTextColor = nightColor ? getContrastColor(nightColor) : nightStyleObj?.text + const dayBg = dayColor ?? dayStyleObj?.bg + const dayTextColor = dayColor ? getContrastColor(dayColor) : dayStyleObj?.text + const dayCell = (
{ if (e.button === 2) return + if (isReadOnly) return // pan handled by scroll container if (e.ctrlKey || e.metaKey || e.shiftKey) return // let onClick handle modifier clicks if (hasCellData) { onCellPointerDown(e, person.id, d.idx, value || '●') @@ -182,18 +226,24 @@ const PersonRow = memo(function PersonRow({ } }} onPointerEnter={(e) => { - if (e.buttons === 1 && !hasCellData) { + if (!isReadOnly && e.buttons === 1 && !hasCellData) { onCellDragSelectMove(person.id, d.idx) } }} onClick={(e) => { if (e.button === 2) return - if (dragState) return + if (isReadOnly || dragState) return onCellClick(person.id, d.idx, e.ctrlKey || e.metaKey, e.shiftKey) }} - onDoubleClick={() => { - if (dragState) return - onStartEdit(person.id, d.idx, value) + onDoubleClick={(e) => { + if (isReadOnly || dragState) return + if (renderAsSplit) { + const rect = e.currentTarget.getBoundingClientRect() + const isLeft = e.clientX - rect.left < rect.width / 2 + onStartEdit(person.id, d.idx, isLeft ? dayValue : nightValue, isLeft ? 'day' : 'night') + } else { + onStartEdit(person.id, d.idx, value, 'cell') + } }} onContextMenu={(e) => { e.preventDefault() @@ -205,10 +255,31 @@ const PersonRow = memo(function PersonRow({ (hasCellComment ? `\n💬 ${cellComments.get(commentKey)}` : '') } > + {/* Split mode: each half renders its own bg + text. Day = LEFT, Night = RIGHT. */} + {renderAsSplit && ( + <> +
+ {dayValue && {dayValue}} +
+
+ {nightValue && {nightValue}} +
+ + )} {isEditing ? ( onEditChange(e.target.value)} @@ -223,9 +294,9 @@ const PersonRow = memo(function PersonRow({ ) : ( <> {isDragPreview ? ( - {dragState!.value} - ) : value && !isValueHidden ? ( - {value} + {dragState!.value} + ) : value && !isValueHidden && !renderAsSplit ? ( + {value} ) : null} )} @@ -259,12 +330,13 @@ const PersonRow = memo(function PersonRow({
onStartEdit(person.id, pmsDayIdx, pmsValue)} + onDoubleClick={() => { if (!isReadOnly) onStartEdit(person.id, pmsDayIdx, pmsValue) }} onClick={() => { - if (!isPmsEditing) onStartEdit(person.id, pmsDayIdx, pmsValue) + if (!isReadOnly && !isPmsEditing) onStartEdit(person.id, pmsDayIdx, pmsValue) }} onContextMenu={(e) => { e.preventDefault() @@ -275,7 +347,7 @@ const PersonRow = memo(function PersonRow({ {isPmsEditing ? ( onEditChange(e.target.value)} @@ -309,10 +381,12 @@ export function ScheduleTable(props: ScheduleTableProps) { dayIndex, people, tunnelClosures, tunnelColors, metroClosures, metroColors, d8Closures, d8Colors, sazltClosures, sazltColors, dayComments, cellComments, - dragState, onCellPointerDown, onSetCell, onSetTunnelClosure, + dragState, onCellPointerDown, onSetCell, onSetCellHalfValue, onSetTunnelClosure, onSetMetroClosure, onSetD8Closure, onSetSazltClosure, - showMetro, showD8, showSazlt, hide162, hiddenValues, + showMetro, showD8, showSazlt, showIT, hide162, hiddenTkbPeople, hiddenValues, scrollRef, onContextMenu, onTunnelContextMenu, onInfoRowContextMenu, compareData, + isReadOnly = false, + clipboardSource, hasClipboard, onCopyCell, onPasteCells, onClearClipboard, } = props const [editingCell, setEditingCell] = useState(null) @@ -372,9 +446,11 @@ export function ScheduleTable(props: ScheduleTableProps) { const HIDE_162_NAMES = ['Pauzer Libor', 'Vörös Pavel', 'Janouš Petr', 'Franek Lukáš', 'Svoboda Daniel'] const tkbPeople = useMemo(() => { - const all = people.filter(p => p.group === 'TKB') - return hide162 ? all.filter(p => !HIDE_162_NAMES.includes(p.name)) : all - }, [people, hide162]) + let all = people.filter(p => p.group === 'TKB') + if (hide162) all = all.filter(p => !HIDE_162_NAMES.includes(p.name)) + if (hiddenTkbPeople.size > 0) all = all.filter(p => !hiddenTkbPeople.has(p.id)) + return all + }, [people, hide162, hiddenTkbPeople]) const itPeople = useMemo(() => people.filter(p => p.group === 'IT'), [people]) // ---------- PMS (Práce mimo směnu) month-end positions ---------- @@ -425,8 +501,8 @@ export function ScheduleTable(props: ScheduleTableProps) { // ---------- Editing handlers ---------- - const onStartEdit = useCallback((personId: string, dayIdx: number, currentValue: string) => { - setEditingCell({ personId, dayIdx, value: currentValue }) + const onStartEdit = useCallback((personId: string, dayIdx: number, currentValue: string, target: 'cell' | 'day' | 'night' = 'cell') => { + setEditingCell({ personId, dayIdx, value: currentValue, target }) }, []) const onStartEditTunnel = useCallback((dayIdx: number, currentValue: string) => { @@ -461,10 +537,15 @@ export function ScheduleTable(props: ScheduleTableProps) { } else if (editingCell.personId === '__sazlt__') { onSetSazltClosure(editingCell.dayIdx, trimmed || null) } else { - onSetCell(editingCell.personId, editingCell.dayIdx, trimmed || null) + const target = editingCell.target ?? 'cell' + if (target === 'cell') { + onSetCell(editingCell.personId, editingCell.dayIdx, trimmed || null) + } else { + onSetCellHalfValue(editingCell.personId, editingCell.dayIdx, target, trimmed || null) + } } setEditingCell(null) - }, [editingCell, onSetCell, onSetTunnelClosure, onSetMetroClosure, onSetD8Closure, onSetSazltClosure]) + }, [editingCell, onSetCell, onSetCellHalfValue, onSetTunnelClosure, onSetMetroClosure, onSetD8Closure, onSetSazltClosure]) const onEditCancel = useCallback(() => { setEditingCell(null) @@ -479,31 +560,41 @@ export function ScheduleTable(props: ScheduleTableProps) { const cellKey = (personId: string, dayIdx: number) => `${personId}-${dayIdx}` + // Unified row-order that includes info rows + person rows (used for rectangle selection). + const allRowKeys = useMemo(() => { + const rows: string[] = ['__tunnel__'] + if (showSazlt) rows.push('__sazlt__') + if (showMetro) rows.push('__metro__') + if (showD8) rows.push('__d8__') + rows.push(...tkbPeople.map(p => p.id), ...itPeople.map(p => p.id)) + return rows + }, [showSazlt, showMetro, showD8, tkbPeople, itPeople]) + const computeRectSelection = useCallback(( anchor: { personId: string; dayIdx: number }, current: { personId: string; dayIdx: number }, ): Set => { - const anchorPersonIdx = people.findIndex(p => p.id === anchor.personId) - const currentPersonIdx = people.findIndex(p => p.id === current.personId) - if (anchorPersonIdx < 0 || currentPersonIdx < 0) return new Set() + const anchorIdx = allRowKeys.indexOf(anchor.personId) + const currentIdx = allRowKeys.indexOf(current.personId) + if (anchorIdx < 0 || currentIdx < 0) return new Set() const anchorDayPos = dayIndex.findIndex(d => d.idx === anchor.dayIdx) const currentDayPos = dayIndex.findIndex(d => d.idx === current.dayIdx) if (anchorDayPos < 0 || currentDayPos < 0) return new Set() - const minP = Math.min(anchorPersonIdx, currentPersonIdx) - const maxP = Math.max(anchorPersonIdx, currentPersonIdx) + const minP = Math.min(anchorIdx, currentIdx) + const maxP = Math.max(anchorIdx, currentIdx) const minD = Math.min(anchorDayPos, currentDayPos) const maxD = Math.max(anchorDayPos, currentDayPos) const sel = new Set() for (let pi = minP; pi <= maxP; pi++) { for (let di = minD; di <= maxD; di++) { - sel.add(cellKey(people[pi].id, dayIndex[di].idx)) + sel.add(cellKey(allRowKeys[pi], dayIndex[di].idx)) } } return sel - }, [people, dayIndex]) + }, [allRowKeys, dayIndex]) const onCellClick = useCallback((personId: string, dayIdx: number, ctrlKey: boolean, shiftKey: boolean) => { const key = cellKey(personId, dayIdx) @@ -571,17 +662,76 @@ export function ScheduleTable(props: ScheduleTableProps) { prevDragRef.current = dragState }, [dragState]) - // Clear selection on Escape + // Keyboard: Escape clears selection + clipboard, Ctrl+C/V copy/paste useEffect(() => { const handleKey = (e: KeyboardEvent) => { + // Skip if focus is in an input/textarea + const tag = (e.target as HTMLElement)?.tagName + if (tag === 'INPUT' || tag === 'TEXTAREA') return + if (e.key === 'Escape') { setSelectedCells(new Set()) selectionAnchorRef.current = null + onClearClipboard() + return + } + if (isReadOnly) return + const isCtrl = e.ctrlKey || e.metaKey + if (isCtrl && (e.key === 'c' || e.key === 'C')) { + if (selectedCells.size === 1) { + const key = Array.from(selectedCells)[0] + const lastDash = key.lastIndexOf('-') + const personId = key.substring(0, lastDash) + const dayIdx = Number(key.substring(lastDash + 1)) + onCopyCell(personId, dayIdx) + e.preventDefault() + } + } else if (isCtrl && (e.key === 'v' || e.key === 'V')) { + if (hasClipboard && selectedCells.size > 0) { + const targets: { personId: string; dayIdx: number }[] = [] + selectedCells.forEach(key => { + const lastDash = key.lastIndexOf('-') + targets.push({ personId: key.substring(0, lastDash), dayIdx: Number(key.substring(lastDash + 1)) }) + }) + onPasteCells(targets) + e.preventDefault() + } } } document.addEventListener('keydown', handleKey) return () => document.removeEventListener('keydown', handleKey) - }, []) + }, [selectedCells, hasClipboard, isReadOnly, onCopyCell, onPasteCells, onClearClipboard]) + + // Pan/grab behavior for read-only mode + useEffect(() => { + if (!isReadOnly) return + const el = scrollRef.current + if (!el) return + let startX = 0, startScrollLeft = 0, isPanning = false + + const onDown = (e: PointerEvent) => { + if (e.button !== 0) return + isPanning = true + startX = e.clientX + startScrollLeft = el.scrollLeft + } + const onMove = (e: PointerEvent) => { + if (!isPanning) return + el.scrollLeft = startScrollLeft - (e.clientX - startX) + } + const onUp = () => { isPanning = false } + + el.addEventListener('pointerdown', onDown, { capture: true }) + document.addEventListener('pointermove', onMove) + document.addEventListener('pointerup', onUp) + document.addEventListener('pointercancel', onUp) + return () => { + el.removeEventListener('pointerdown', onDown, { capture: true }) + document.removeEventListener('pointermove', onMove) + document.removeEventListener('pointerup', onUp) + document.removeEventListener('pointercancel', onUp) + } + }, [isReadOnly, scrollRef]) // Context menu handler wrapping the prop to include selected cells const handlePersonContextMenu = useCallback((dayIdx: number, personId: string | null, x: number, y: number) => { @@ -608,6 +758,22 @@ export function ScheduleTable(props: ScheduleTableProps) { } }, [selectedCells, onContextMenu]) + // Context menu handler for info rows — packages current selection like handlePersonContextMenu + const handleInfoRowContext = useCallback((rowId: string | null, dayIdx: number, x: number, y: number) => { + const key = `${rowId ?? '__tunnel__'}-${dayIdx}` + const cells: SelectedCell[] = [] + selectedCells.forEach(k => { + const lastDash = k.lastIndexOf('-') + cells.push({ personId: k.substring(0, lastDash), dayIdx: Number(k.substring(lastDash + 1)) }) + }) + const toPass = selectedCells.has(key) && selectedCells.size > 1 ? cells : [{ personId: rowId ?? '__tunnel__', dayIdx }] + if (rowId === null) { + onTunnelContextMenu(dayIdx, x, y, toPass) + } else { + onInfoRowContextMenu(dayIdx, rowId, x, y, toPass) + } + }, [selectedCells, onTunnelContextMenu, onInfoRowContextMenu]) + // ---------- Drag overlay ---------- const dragOverlay = useMemo(() => { @@ -727,7 +893,6 @@ export function ScheduleTable(props: ScheduleTableProps) { rowId: '__tunnel__' | '__metro__' | '__d8__' | '__sazlt__', closures: Map, colors: Map, - defaultBgClass: string, borderClass: string, editBorderColor: string, label: string, @@ -745,21 +910,48 @@ export function ScheduleTable(props: ScheduleTableProps) { if (closureColor) { rowBg.backgroundColor = closureColor rowBg.color = getContrastColor(closureColor) + } else if (closureVal) { + const valStyle = getCellStyle(closureVal) + if (valStyle) { + rowBg.backgroundColor = valStyle.bg + rowBg.color = valStyle.text + } } + const cellSelKey = `${rowId}-${d.idx}` + const isInfoSelected = selectedCells.has(cellSelKey) + const isInfoClipboardSource = !!clipboardSource && clipboardSource.personId === rowId && clipboardSource.dayIdx === d.idx + const infoExtraStyle: React.CSSProperties = {} + if (isInfoClipboardSource && !isInfoSelected) { + infoExtraStyle.outline = '2px dashed #60a5fa' + infoExtraStyle.outlineOffset = '-2px' + } const cells: React.ReactNode[] = [] cells.push(
{ - if (!isEditingThis) onStartEditFn(d.idx, closureVal) + style={{ width: CELL_W, height: 28, ...rowBg, ...infoExtraStyle }} + onPointerDown={(e) => { + if (e.button === 2 || isReadOnly) return + if (e.ctrlKey || e.metaKey || e.shiftKey) return + onCellDragSelectStart(rowId, d.idx) + }} + onPointerEnter={(e) => { + if (!isReadOnly && e.buttons === 1) onCellDragSelectMove(rowId, d.idx) + }} + onClick={(e) => { + if (e.button === 2 || isReadOnly) return + onCellClick(rowId, d.idx, e.ctrlKey || e.metaKey, e.shiftKey) + }} + onDoubleClick={() => { + if (!isReadOnly && !isEditingThis) onStartEditFn(d.idx, closureVal) }} onContextMenu={(e) => { e.preventDefault() @@ -770,7 +962,7 @@ export function ScheduleTable(props: ScheduleTableProps) { {isEditingThis ? ( onEditChange(e.target.value)} onKeyDown={(e) => { @@ -787,7 +979,7 @@ export function ScheduleTable(props: ScheduleTableProps) { ) if (pmsAfterSet.has(i)) { cells.push( -
+
) } return cells @@ -808,7 +1000,7 @@ export function ScheduleTable(props: ScheduleTableProps) { ) return ( -
+
{/* === Fixed left name column === */} @@ -843,8 +1035,8 @@ export function ScheduleTable(props: ScheduleTableProps) { {renderSectionHeaderLabel('Pohotovost TKB', 'bg-indigo-700/60', 'text-indigo-100')} {/* TKB person names */} - {tkbPeople.map((person) => ( -
+ {tkbPeople.map((person, idx) => ( +
{person.name} {person.note && ({person.note})} @@ -853,11 +1045,11 @@ export function ScheduleTable(props: ScheduleTableProps) { ))} {/* IT section header */} - {renderSectionHeaderLabel('Pohotovost IT', 'bg-teal-700/60', 'text-teal-100')} + {showIT && renderSectionHeaderLabel('Pohotovost IT', 'bg-teal-700/60', 'text-teal-100')} {/* IT person names */} - {itPeople.map((person) => ( -
+ {showIT && itPeople.map((person, idx) => ( +
{person.name} {person.note && ({person.note})} @@ -867,7 +1059,7 @@ export function ScheduleTable(props: ScheduleTableProps) {
{/* === Scrollable grid === */} -
+
{/* Month header row */} @@ -920,7 +1112,7 @@ export function ScheduleTable(props: ScheduleTableProps) { ) } fragments.push( -
+
) fragStart = pmsPos + 1 } @@ -974,18 +1166,17 @@ export function ScheduleTable(props: ScheduleTableProps) { > {/* Merged cell overlay covering DEN + DEN T. + all info rows */}
+
) } return cells @@ -1030,23 +1221,23 @@ export function ScheduleTable(props: ScheduleTableProps) { {/* TKB info row */} {renderInfoRowCells('__tunnel__', tunnelClosures, tunnelColors, - 'bg-orange-700/40 text-orange-200', 'border-slate-700', 'border-orange-400', 'TKB', - onTunnelContextMenu, onStartEditTunnel)} + 'border-slate-700', 'border-orange-400', 'TKB', + (dayIdx, x, y) => handleInfoRowContext(null, dayIdx, x, y), onStartEditTunnel)} {/* SAZLT info row */} {showSazlt && renderInfoRowCells('__sazlt__', sazltClosures, sazltColors, - 'bg-amber-700/40 text-amber-200', 'border-slate-700', 'border-amber-400', 'SAZLT', - (dayIdx, x, y) => onInfoRowContextMenu(dayIdx, 'sazlt', x, y), onStartEditSazlt)} + 'border-slate-700', 'border-amber-400', 'SAZLT', + (dayIdx, x, y) => handleInfoRowContext('sazlt', dayIdx, x, y), onStartEditSazlt)} {/* Metro info row */} {showMetro && renderInfoRowCells('__metro__', metroClosures, metroColors, - 'bg-blue-700/40 text-blue-200', 'border-slate-700', 'border-blue-400', 'Metro', - (dayIdx, x, y) => onInfoRowContextMenu(dayIdx, 'metro', x, y), onStartEditMetro)} + 'border-slate-700', 'border-blue-400', 'Metro', + (dayIdx, x, y) => handleInfoRowContext('metro', dayIdx, x, y), onStartEditMetro)} {/* D8 info row */} {showD8 && renderInfoRowCells('__d8__', d8Closures, d8Colors, - 'bg-green-700/40 text-green-200', 'border-slate-600', 'border-green-400', 'D8', - (dayIdx, x, y) => onInfoRowContextMenu(dayIdx, 'd8', x, y), onStartEditD8)} + 'border-slate-600', 'border-green-400', 'D8', + (dayIdx, x, y) => handleInfoRowContext('d8', dayIdx, x, y), onStartEditD8)} {/* Data rows container (with overlay) */}
@@ -1055,9 +1246,10 @@ export function ScheduleTable(props: ScheduleTableProps) { {renderSectionHeaderBar('bg-indigo-700/60')} {/* TKB person rows */} - {tkbPeople.map((person) => ( + {tkbPeople.map((person, idx) => ( ))} {/* IT section header bar */} - {renderSectionHeaderBar('bg-teal-700/60')} + {showIT && renderSectionHeaderBar('bg-teal-700/60')} {/* IT person rows */} - {itPeople.map((person) => ( + {showIT && itPeople.map((person, idx) => ( ))} diff --git a/web/src/Toolbar.tsx b/web/src/Toolbar.tsx index d6b8f95..8298700 100644 --- a/web/src/Toolbar.tsx +++ b/web/src/Toolbar.tsx @@ -1,3 +1,4 @@ +import { useState, useRef, useEffect } from 'react' import { getAllCellStyles } from './cellColors' const MONTH_BUTTONS = [ @@ -26,19 +27,30 @@ interface ToolbarProps { showMetro: boolean showD8: boolean showSazlt: boolean + showIT: boolean hide162: boolean + theme: 'dark' | 'light' onToggleMetro: () => void onToggleD8: () => void onToggleSazlt: () => void + onToggleIT: () => void onToggle162: () => void + onToggleTheme: () => void hiddenValues: Set onToggleValue: (code: string) => void diffFileName?: string | null onCloseDiff?: () => void onExportPdf?: (month: number) => void + onExportExcel?: (month: number) => void onShowProposals?: () => void onCheckFpd?: () => void isReadOnly?: boolean + // People filter + tkbPeopleList?: { id: string; name: string; note?: string }[] + hiddenTkbPeople?: Set + onToggleTkbPerson?: (id: string) => void + onSetAllTkbVisible?: () => void + onSetOnlyTkbPerson?: (id: string) => void } export function Toolbar({ @@ -52,21 +64,48 @@ export function Toolbar({ showMetro, showD8, showSazlt, + showIT, hide162, + theme, onToggleMetro, onToggleD8, onToggleSazlt, + onToggleIT, onToggle162, + onToggleTheme, hiddenValues, onToggleValue, diffFileName, onCloseDiff, onExportPdf, + onExportExcel, onShowProposals, onCheckFpd, isReadOnly = false, + tkbPeopleList = [], + hiddenTkbPeople = new Set(), + onToggleTkbPerson, + onSetAllTkbVisible, + onSetOnlyTkbPerson, }: ToolbarProps) { const cellStyles = getAllCellStyles() + const [showPeopleFilter, setShowPeopleFilter] = useState(false) + const peopleFilterRef = useRef(null) + + // Close dropdown on outside click + useEffect(() => { + if (!showPeopleFilter) return + const handleClick = (e: MouseEvent) => { + if (peopleFilterRef.current && !peopleFilterRef.current.contains(e.target as Node)) { + setShowPeopleFilter(false) + } + } + document.addEventListener('mousedown', handleClick) + return () => document.removeEventListener('mousedown', handleClick) + }, [showPeopleFilter]) + + const activeFilterCount = tkbPeopleList.length - hiddenTkbPeople.size + const isFiltered = hiddenTkbPeople.size > 0 return (
@@ -159,6 +198,16 @@ export function Toolbar({ )} + {onExportExcel && activeMonth && ( + + )} + {onShowProposals && ( +
{/* Row toggles */} @@ -213,6 +271,16 @@ export function Toolbar({ > SAZLT +
+ {/* TKB people filter */} + {tkbPeopleList.length > 0 && onToggleTkbPerson && ( +
+ + + {showPeopleFilter && ( +
+ {/* Header */} +
+ Pohotovost TKB +
+ + +
+
+ + {/* Person list */} +
+ {tkbPeopleList.map(person => { + const isVisible = !hiddenTkbPeople.has(person.id) + return ( +
+ onToggleTkbPerson(person.id)} + className="cursor-pointer accent-indigo-500" + id={`tkb-person-${person.id}`} + /> + + +
+ ) + })} +
+
+ )} +
+ )} +
{/* Color legend / filter */} diff --git a/web/src/data.json b/web/src/data.json index ddca30a..1d4198e 100644 --- a/web/src/data.json +++ b/web/src/data.json @@ -2963,6 +2963,12 @@ "group": "TKB", "data": {} }, + { + "id": "tkb-kropacek", + "name": "Kropáček Ondřej", + "group": "TKB", + "data": {} + }, { "id": "tkb-vondrak", "name": "Vondrák Pavel", @@ -2996,6 +3002,7 @@ "name": "Dittrich Vladimír", "note": "VN", "group": "TKB", + "retired": true, "data": {} }, { diff --git a/web/src/index.css b/web/src/index.css index 8bfc1de..8ab36f8 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -3,8 +3,8 @@ body { margin: 0; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; - background: #0f172a; - color: #e2e8f0; + background: var(--color-slate-900); + color: var(--color-slate-200); } #root { @@ -15,13 +15,44 @@ body { width: 8px; height: 8px; } -::-webkit-scrollbar-track { - background: #1e293b; +::-webkit-scrollbar-track { background: var(--color-slate-800); } +::-webkit-scrollbar-thumb { background: var(--color-slate-600); border-radius: 4px; } +::-webkit-scrollbar-thumb:hover { background: var(--color-slate-500); } + +/* PMS column (Práce mimo směnu) — month-boundary marker. + Dark mode: matches the original bg-slate-700/30 tint. */ +.pms-cell, +.pms-week { + background-color: color-mix(in oklab, var(--color-slate-700) 30%, transparent); } -::-webkit-scrollbar-thumb { - background: #475569; - border-radius: 4px; -} -::-webkit-scrollbar-thumb:hover { - background: #64748b; +.pms-label { background-color: #283040; } +.pms-label-text { color: var(--color-slate-300); } + +/* ============================================================ + LIGHT THEME + Implemented by inverting the slate color scale via CSS variables. + Browser only re-evaluates the variable, no rule matching, no !important. + ============================================================ */ +body.light { + --color-slate-900: #f8fafc; + --color-slate-800: #ffffff; + --color-slate-700: #e2e8f0; + --color-slate-600: #cbd5e1; + --color-slate-500: #94a3b8; + --color-slate-400: #64748b; + --color-slate-300: #334155; + --color-slate-200: #1e293b; + --color-slate-100: #0f172a; + --color-slate-50: #020617; } + +/* Light-mode specific tweaks where simple inversion isn't enough. + These selectors win on specificity (.light + .class > Tailwind's single class). */ +body.light .bg-slate-800\/50 { background-color: #dbeafe; } /* weekend cells in person rows */ +body.light .bg-slate-700\/30 { background-color: #dbeafe; } /* weekend cells in info rows */ +body.light .schedule-wrapper { background-color: #ffffff; } +body.light .pms-cell, +body.light .pms-week { background-color: #fde68a; } +body.light .pms-week { box-shadow: inset 1px 0 0 #94a3b8, inset -1px 0 0 #94a3b8; } +body.light .pms-label { background-color: #fde68a; } +body.light .pms-label-text { color: #78350f; } diff --git a/web/src/types.ts b/web/src/types.ts index bd6534e..cb5e8fe 100644 --- a/web/src/types.ts +++ b/web/src/types.ts @@ -13,11 +13,16 @@ export interface Person { group: 'TKB' | 'IT' note?: string data: Record + retired?: boolean // hidden from UI/FPD; exports keep past-month data but skip empty months } export interface CellValue { v?: string color?: string + dayColor?: string // right half — day shift preference mark + nightColor?: string // left half — night shift preference mark + dayValue?: string // right half — day shift text + nightValue?: string // left half — night shift text } export interface TunnelClosure { diff --git a/web/src/useScheduleState.ts b/web/src/useScheduleState.ts index a31276d..2383be5 100644 --- a/web/src/useScheduleState.ts +++ b/web/src/useScheduleState.ts @@ -138,14 +138,20 @@ export function useScheduleState( const next = JSON.parse(JSON.stringify(prev)) as Person[] const person = next.find(p => p.id === personId) if (!person) return prev + const existing = person.data[String(dayIdx)] ?? {} + // Half-cell colors and values belong to the day; always preserve them + const preserve = { + ...(existing.color ? { color: existing.color } : {}), + ...(existing.dayColor ? { dayColor: existing.dayColor } : {}), + ...(existing.nightColor ? { nightColor: existing.nightColor } : {}), + ...(existing.dayValue ? { dayValue: existing.dayValue } : {}), + ...(existing.nightValue ? { nightValue: existing.nightValue } : {}), + } if (value) { - const existing = person.data[String(dayIdx)] - person.data[String(dayIdx)] = { v: value, ...(existing?.color ? { color: existing.color } : {}) } + person.data[String(dayIdx)] = { v: value, ...preserve } } else { - const existing = person.data[String(dayIdx)] - if (existing?.color) { - const { v: _, ...rest } = existing - person.data[String(dayIdx)] = rest as any + if (Object.keys(preserve).length > 0) { + person.data[String(dayIdx)] = preserve } else { delete person.data[String(dayIdx)] } @@ -154,6 +160,9 @@ export function useScheduleState( }) }, [pushHistory]) + const isCellEmpty = (c: any) => + !c.v && !c.color && !c.dayColor && !c.nightColor && !c.dayValue && !c.nightValue + const setCellColor = useCallback((personId: string, dayIdx: number, color: string | null) => { pushHistory() setPeople(prev => { @@ -166,12 +175,81 @@ export function useScheduleState( } else { const { color: _, ...rest } = existing as any person.data[String(dayIdx)] = rest - if (!rest.v) delete person.data[String(dayIdx)] + if (isCellEmpty(rest)) delete person.data[String(dayIdx)] } return next }) }, [pushHistory]) + const setCellHalfColor = useCallback((personId: string, dayIdx: number, target: 'day' | 'night', color: string | null) => { + pushHistory() + setPeople(prev => { + const next = JSON.parse(JSON.stringify(prev)) as Person[] + const person = next.find(p => p.id === personId) + if (!person) return prev + const existing = person.data[String(dayIdx)] ?? {} + const field = target === 'day' ? 'dayColor' : 'nightColor' + if (color) { + person.data[String(dayIdx)] = { ...existing, [field]: color } + } else { + const { [field]: _, ...rest } = existing as any + person.data[String(dayIdx)] = rest + if (isCellEmpty(rest)) delete person.data[String(dayIdx)] + } + return next + }) + }, [pushHistory]) + + const setCellHalfValue = useCallback((personId: string, dayIdx: number, target: 'day' | 'night', value: string | null) => { + pushHistory() + setPeople(prev => { + const next = JSON.parse(JSON.stringify(prev)) as Person[] + const person = next.find(p => p.id === personId) + if (!person) return prev + const existing = person.data[String(dayIdx)] ?? {} + const field = target === 'day' ? 'dayValue' : 'nightValue' + if (value) { + person.data[String(dayIdx)] = { ...existing, [field]: value } + } else { + const { [field]: _, ...rest } = existing as any + person.data[String(dayIdx)] = rest + if (isCellEmpty(rest)) delete person.data[String(dayIdx)] + } + return next + }) + }, [pushHistory]) + + const pasteCellTo = useCallback((personId: string, dayIdx: number, payload: import('./types').CellValue & { comment?: string | null }) => { + pushHistory() + setPeople(prev => { + const next = JSON.parse(JSON.stringify(prev)) as Person[] + const person = next.find(p => p.id === personId) + if (!person) return prev + const cell: import('./types').CellValue = { + ...(payload.v ? { v: payload.v } : {}), + ...(payload.color ? { color: payload.color } : {}), + ...(payload.dayValue ? { dayValue: payload.dayValue } : {}), + ...(payload.dayColor ? { dayColor: payload.dayColor } : {}), + ...(payload.nightValue ? { nightValue: payload.nightValue } : {}), + ...(payload.nightColor ? { nightColor: payload.nightColor } : {}), + } + if (Object.keys(cell).length > 0) { + person.data[String(dayIdx)] = cell + } else { + delete person.data[String(dayIdx)] + } + return next + }) + // Comment: replace (set or remove) + setCellCommentsState(prev => { + const next = new Map(prev) + const key = `${personId}-${dayIdx}` + if (payload.comment) next.set(key, payload.comment) + else next.delete(key) + return next + }) + }, [pushHistory]) + const setTunnelClosureColor = useCallback((dayIdx: number, color: string | null) => { pushHistory() setTunnelColorsState(prev => { @@ -249,10 +327,30 @@ export function useScheduleState( const next = JSON.parse(JSON.stringify(prev)) as Person[] const person = next.find(p => p.id === personId) if (!person) return prev - const value = person.data[String(fromIdx)] - if (!value) return prev - delete person.data[String(fromIdx)] - person.data[String(toIdx)] = value + const source = person.data[String(fromIdx)] + if (!source?.v) return prev + + // Shift moves with v + color; half-marks (color & value) stay with the day + const moving = { v: source.v, ...(source.color ? { color: source.color } : {}) } + const halfMarks = (c: any) => ({ + ...(c.dayColor ? { dayColor: c.dayColor } : {}), + ...(c.nightColor ? { nightColor: c.nightColor } : {}), + ...(c.dayValue ? { dayValue: c.dayValue } : {}), + ...(c.nightValue ? { nightValue: c.nightValue } : {}), + }) + const stayingOnSource = halfMarks(source) + if (Object.keys(stayingOnSource).length > 0) { + person.data[String(fromIdx)] = stayingOnSource + } else { + delete person.data[String(fromIdx)] + } + + // Destination keeps its existing half-marks; v + color overwritten + const destExisting = person.data[String(toIdx)] ?? {} + person.data[String(toIdx)] = { + ...halfMarks(destExisting), + ...moving, + } return next }) }, [pushHistory]) @@ -357,6 +455,9 @@ export function useScheduleState( cellComments, setCell, setCellColor, + setCellHalfColor, + setCellHalfValue, + pasteCellTo, moveCell, setTunnelClosure, setTunnelClosureColor,