feat: v3.0 - canvas editor, JSON-only, no Excel, new UI
Some checks failed
Build & Push Docker / build (push) Has been cancelled
Some checks failed
Build & Push Docker / build (push) Has been cancelled
- Remove all Excel code (import, export, template, pandas, openpyxl) - New canvas-based schedule editor with drag & drop (interact.js) - Modern 3-panel UI: sidebar, canvas, documentation tab - New data model: Block with id/date/start/end, ProgramType with id/name/color - Clean API: GET /api/health, POST /api/validate, GET /api/sample, POST /api/generate-pdf - Rewritten PDF generator using ScenarioDocument directly (no DataFrame) - Professional PDF output: dark header, colored blocks, merged cells, legend, footer - Sample JSON: "Zimní výjezd oddílu" with 11 blocks, 3 program types - 30 tests passing (API, core models, PDF generation) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,131 +3,193 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Scenar Creator</title>
|
||||
<title>Scenár Creator</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/css/app.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Scenar Creator</h1>
|
||||
<p class="subtitle">Tvorba časových harmonogramů</p>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="tabs">
|
||||
<button class="tab active" onclick="switchTab(event, 'importTab')">Importovat Excel</button>
|
||||
<button class="tab" onclick="switchTab(event, 'builderTab')">Vytvořit inline</button>
|
||||
<header class="header">
|
||||
<div class="header-left">
|
||||
<h1 class="header-title">Scenár Creator</h1>
|
||||
<span class="header-version">v3.0</span>
|
||||
</div>
|
||||
|
||||
<!-- Import Excel Tab -->
|
||||
<div id="importTab" class="tab-content active">
|
||||
<form id="importForm" onsubmit="return handleImport(event)">
|
||||
<div class="form-group">
|
||||
<label for="importTitle">Název akce:</label>
|
||||
<input type="text" id="importTitle" name="title" maxlength="200" required placeholder="Název události">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="importDetail">Detail:</label>
|
||||
<input type="text" id="importDetail" name="detail" maxlength="500" required placeholder="Popis události">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="excelFile">Excel soubor:</label>
|
||||
<input type="file" id="excelFile" name="file" accept=".xlsx,.xls" required>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Importovat</button>
|
||||
<a href="/api/template" class="btn btn-secondary">Stáhnout šablonu</a>
|
||||
</div>
|
||||
</form>
|
||||
<div class="header-actions">
|
||||
<label class="btn btn-secondary btn-sm" id="importJsonBtn">
|
||||
<input type="file" accept=".json" id="importJsonInput" hidden>
|
||||
Import JSON
|
||||
</label>
|
||||
<button class="btn btn-secondary btn-sm" id="newScenarioBtn">Nový scénář</button>
|
||||
<button class="btn btn-secondary btn-sm" id="exportJsonBtn">Export JSON</button>
|
||||
<button class="btn btn-primary btn-sm" id="generatePdfBtn">Generovat PDF</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Builder Tab -->
|
||||
<div id="builderTab" class="tab-content">
|
||||
<form id="builderForm" onsubmit="return handleBuild(event)">
|
||||
<div class="form-group">
|
||||
<label for="builderTitle">Název akce:</label>
|
||||
<input type="text" id="builderTitle" name="title" maxlength="200" required placeholder="Název události">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="builderDetail">Detail:</label>
|
||||
<input type="text" id="builderDetail" name="detail" maxlength="500" required placeholder="Popis události">
|
||||
</div>
|
||||
|
||||
<h3>Typy programů</h3>
|
||||
<div id="typesContainer">
|
||||
<div class="type-row" data-index="0">
|
||||
<input type="text" name="type_name_0" placeholder="Kód typu (např. WORKSHOP)" class="type-code">
|
||||
<input type="text" name="type_desc_0" placeholder="Popis">
|
||||
<input type="color" name="type_color_0" value="#0070C0">
|
||||
<button type="button" class="btn btn-danger btn-sm" onclick="removeTypeRow(0)">X</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="addTypeRow()">+ Přidat typ</button>
|
||||
|
||||
<h3>Časový harmonogram</h3>
|
||||
<datalist id="availableTypes"></datalist>
|
||||
<div id="scheduleContainer">
|
||||
<table id="scheduleTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Datum</th>
|
||||
<th>Začátek</th>
|
||||
<th>Konec</th>
|
||||
<th>Program</th>
|
||||
<th>Typ</th>
|
||||
<th>Garant</th>
|
||||
<th>Poznámka</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="scheduleBody">
|
||||
<tr data-index="0">
|
||||
<td><input type="date" name="datum_0" required></td>
|
||||
<td><input type="time" name="zacatek_0" required></td>
|
||||
<td><input type="time" name="konec_0" required></td>
|
||||
<td><input type="text" name="program_0" required placeholder="Název bloku"></td>
|
||||
<td><input type="text" name="typ_0" list="availableTypes" required placeholder="Typ"></td>
|
||||
<td><input type="text" name="garant_0" placeholder="Garant"></td>
|
||||
<td><input type="text" name="poznamka_0" placeholder="Poznámka"></td>
|
||||
<td><button type="button" class="btn btn-danger btn-sm" onclick="removeScheduleRow(0)">X</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="addScheduleRow()">+ Přidat řádek</button>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary" name="format" value="excel">Stáhnout Excel</button>
|
||||
<button type="button" class="btn btn-primary" onclick="handleBuildPdf()">Stáhnout PDF</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Import results / editor area -->
|
||||
<div id="editorArea" class="editor-area" style="display:none;">
|
||||
<h2>Importovaná data</h2>
|
||||
<div id="importedInfo"></div>
|
||||
|
||||
<h3>Typy programů</h3>
|
||||
<div id="importedTypesContainer"></div>
|
||||
|
||||
<h3>Bloky</h3>
|
||||
<div id="importedBlocksContainer"></div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button class="btn btn-primary" onclick="generateExcelFromImport()">Stáhnout Excel</button>
|
||||
<button class="btn btn-primary" onclick="generatePdfFromImport()">Stáhnout PDF</button>
|
||||
<button class="btn btn-secondary" onclick="exportJson()">Exportovat JSON</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JSON import -->
|
||||
<div class="json-import">
|
||||
<label>Importovat JSON: <input type="file" id="jsonFile" accept=".json" onchange="handleJsonImport(event)"></label>
|
||||
</div>
|
||||
|
||||
<div id="statusMessage" class="status-message" style="display:none;"></div>
|
||||
<div class="tabs">
|
||||
<button class="tab active" data-tab="editor">Editor</button>
|
||||
<button class="tab" data-tab="docs">Dokumentace</button>
|
||||
</div>
|
||||
|
||||
<div class="tab-content" id="tab-editor">
|
||||
<div class="app-layout">
|
||||
<aside class="sidebar">
|
||||
<section class="sidebar-section">
|
||||
<h3 class="sidebar-heading">Informace o akci</h3>
|
||||
<div class="form-group">
|
||||
<label>Název</label>
|
||||
<input type="text" id="eventTitle" placeholder="Název akce">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Podtitul</label>
|
||||
<input type="text" id="eventSubtitle" placeholder="Podtitul">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Datum</label>
|
||||
<input type="date" id="eventDate">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Místo</label>
|
||||
<input type="text" id="eventLocation" placeholder="Místo konání">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="sidebar-section">
|
||||
<h3 class="sidebar-heading">Typy programů</h3>
|
||||
<div id="programTypesContainer"></div>
|
||||
<button class="btn btn-secondary btn-xs" id="addTypeBtn">+ Přidat typ</button>
|
||||
</section>
|
||||
|
||||
<section class="sidebar-section">
|
||||
<button class="btn btn-primary btn-block" id="addBlockBtn">+ Přidat blok</button>
|
||||
</section>
|
||||
</aside>
|
||||
|
||||
<main class="canvas-wrapper">
|
||||
<div class="canvas-header" id="canvasHeader"></div>
|
||||
<div class="canvas-scroll">
|
||||
<div class="canvas" id="canvas">
|
||||
<div class="time-axis" id="timeAxis"></div>
|
||||
<div class="day-columns" id="dayColumns"></div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-content hidden" id="tab-docs">
|
||||
<div class="docs-container">
|
||||
<h2>Dokumentace — Scenár Creator v3</h2>
|
||||
|
||||
<h3>Jak začít</h3>
|
||||
<p>Máte dvě možnosti:</p>
|
||||
<ol>
|
||||
<li><strong>Nový scénář</strong> — klikněte na tlačítko "Nový scénář" v záhlaví. Vytvoří se prázdný scénář s jedním dnem.</li>
|
||||
<li><strong>Import JSON</strong> — klikněte na "Import JSON" a vyberte dříve uložený .json soubor. Můžete také přetáhnout JSON soubor přímo na plochu editoru.</li>
|
||||
</ol>
|
||||
|
||||
<h3>Práce s bloky</h3>
|
||||
<ul>
|
||||
<li><strong>Přidání bloku:</strong> Klikněte na "+ Přidat blok" v postranním panelu, nebo klikněte na prázdné místo na časové ose.</li>
|
||||
<li><strong>Přesun bloku:</strong> Chytněte blok myší a přetáhněte ho na jiný čas. Bloky se přichytávají na 15minutovou mřížku.</li>
|
||||
<li><strong>Změna délky:</strong> Chytněte dolní okraj bloku a tažením změňte dobu trvání.</li>
|
||||
<li><strong>Úprava bloku:</strong> Klikněte na blok pro otevření editačního popup okna, kde můžete upravit název, typ, garanta a poznámku.</li>
|
||||
<li><strong>Smazání bloku:</strong> V editačním popup okně klikněte na tlačítko "Smazat blok".</li>
|
||||
</ul>
|
||||
|
||||
<h3>Typy programů a barvy</h3>
|
||||
<p>V postranním panelu v sekci "Typy programů" můžete:</p>
|
||||
<ul>
|
||||
<li>Přidat nový typ kliknutím na "+ Přidat typ"</li>
|
||||
<li>Pojmenovat typ a vybrat barvu pomocí barevného výběru</li>
|
||||
<li>Odebrat typ kliknutím na tlačítko ×</li>
|
||||
</ul>
|
||||
|
||||
<h3>Export JSON</h3>
|
||||
<p>Kliknutím na "Export JSON" stáhnete aktuální stav scénáře jako .json soubor. Tento soubor můžete později znovu importovat a pokračovat v úpravách.</p>
|
||||
|
||||
<h3>Generování PDF</h3>
|
||||
<p>Kliknutím na "Generovat PDF" se scénář odešle na server a vygeneruje se přehledný PDF dokument ve formátu A4 na šířku s barevnými bloky a legendou.</p>
|
||||
|
||||
<h3>Formát JSON</h3>
|
||||
<table class="docs-table">
|
||||
<thead>
|
||||
<tr><th>Pole</th><th>Typ</th><th>Popis</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>version</td><td>string</td><td>Verze formátu (1.0)</td></tr>
|
||||
<tr><td>event.title</td><td>string</td><td>Název akce</td></tr>
|
||||
<tr><td>event.subtitle</td><td>string?</td><td>Podtitul</td></tr>
|
||||
<tr><td>event.date</td><td>string?</td><td>Datum (YYYY-MM-DD)</td></tr>
|
||||
<tr><td>event.location</td><td>string?</td><td>Místo</td></tr>
|
||||
<tr><td>program_types[].id</td><td>string</td><td>Unikátní ID typu</td></tr>
|
||||
<tr><td>program_types[].name</td><td>string</td><td>Název typu</td></tr>
|
||||
<tr><td>program_types[].color</td><td>string</td><td>Barva (#RRGGBB)</td></tr>
|
||||
<tr><td>blocks[].id</td><td>string</td><td>Unikátní ID bloku</td></tr>
|
||||
<tr><td>blocks[].date</td><td>string</td><td>Datum (YYYY-MM-DD)</td></tr>
|
||||
<tr><td>blocks[].start</td><td>string</td><td>Začátek (HH:MM)</td></tr>
|
||||
<tr><td>blocks[].end</td><td>string</td><td>Konec (HH:MM)</td></tr>
|
||||
<tr><td>blocks[].title</td><td>string</td><td>Název bloku</td></tr>
|
||||
<tr><td>blocks[].type_id</td><td>string</td><td>ID typu programu</td></tr>
|
||||
<tr><td>blocks[].responsible</td><td>string?</td><td>Garant</td></tr>
|
||||
<tr><td>blocks[].notes</td><td>string?</td><td>Poznámka</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Vzorový JSON</h3>
|
||||
<p><a href="/api/sample" class="btn btn-secondary btn-sm">Stáhnout sample.json</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Block edit modal -->
|
||||
<div class="modal-overlay hidden" id="blockModal">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<h3 id="modalTitle">Upravit blok</h3>
|
||||
<button class="modal-close" id="modalClose">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="modalBlockId">
|
||||
<div class="form-group">
|
||||
<label>Název bloku</label>
|
||||
<input type="text" id="modalBlockTitle" placeholder="Název">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Typ programu</label>
|
||||
<select id="modalBlockType"></select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>Začátek</label>
|
||||
<input type="time" id="modalBlockStart">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Konec</label>
|
||||
<input type="time" id="modalBlockEnd">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Garant</label>
|
||||
<input type="text" id="modalBlockResponsible" placeholder="Garant">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Poznámka</label>
|
||||
<textarea id="modalBlockNotes" rows="3" placeholder="Poznámka"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-danger btn-sm" id="modalDeleteBtn">Smazat blok</button>
|
||||
<button class="btn btn-primary btn-sm" id="modalSaveBtn">Uložit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status toast -->
|
||||
<div class="toast hidden" id="toast"></div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/interactjs@1.10.27/dist/interact.min.js"></script>
|
||||
<script src="/static/js/api.js"></script>
|
||||
<script src="/static/js/canvas.js"></script>
|
||||
<script src="/static/js/export.js"></script>
|
||||
<script src="/static/js/app.js"></script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user