feat: přidáno URL pole pro bloky – klikatelný odkaz v PDF
Some checks failed
Build & Push Docker / build (push) Has been cancelled

- Block model: nové volitelné pole 'url' (Optional[str])
- Frontend: URL input v modálu pro přidání/editaci bloku
- PDF generátor: c.linkURL() – celý blok je klikatelný odkaz
- sample.json: ukázkový blok s URL
- index.html: dokumentace URL pole
- .github/copilot-instructions.md: přidány Copilot instrukce
This commit is contained in:
Martin Sukany
2026-03-14 19:10:13 +01:00
parent 5d712494a5
commit 0a694ce63a
7 changed files with 315 additions and 68 deletions

View File

@@ -120,6 +120,7 @@
<li><strong>Program přes půlnoc</strong> — Konec &lt; Začátek je validní (blok přechází přes půlnoc). V editoru označen „→", v PDF správně vykreslí.</li>
<li><strong>Garant</strong> — zobrazí se v bloku v editoru i v PDF (pod názvem bloku).</li>
<li><strong>Poznámka</strong> — nezobrazuje se v editoru, pouze v PDF jako horní index (¹ ²...) u názvu bloku. Všechny poznámky jsou vypsány na 2. stránce PDF.</li>
<li><strong>URL odkaz</strong> — volitelný. V PDF bude celý blok klikatelný a odkazuje na zadanou adresu.</li>
</ul>
<h3>Export / Import</h3>
@@ -154,6 +155,7 @@
<tr><td>blocks[].type_id</td><td>string</td><td>ID typu programu (musí existovat v program_types)</td></tr>
<tr><td>blocks[].responsible</td><td>string?</td><td>Garant — zobrazí se v editoru i PDF</td></tr>
<tr><td>blocks[].notes</td><td>string?</td><td>Poznámka — jen v PDF, jako horní index + stránka 2</td></tr>
<tr><td>blocks[].url</td><td>string?</td><td>URL odkaz — v PDF je celý blok klikatelný</td></tr>
<tr><td>blocks[].series_id</td><td>string?</td><td>Sdílené ID série — bloky přidané přes „Přidat do všech dnů" sdílejí toto ID</td></tr>
</tbody>
</table>
@@ -220,6 +222,10 @@
<label>Poznámka</label>
<textarea id="modalBlockNotes" rows="3" placeholder="Poznámka"></textarea>
</div>
<div class="form-group">
<label>URL odkaz</label>
<input type="url" id="modalBlockUrl" placeholder="https://…">
</div>
<!-- Shown only when creating a new block -->
<div class="form-group series-row hidden" id="seriesRow">
<label class="series-label">

View File

@@ -193,6 +193,7 @@ const App = {
document.getElementById('modalBlockEnd').value = block.end || '';
document.getElementById('modalBlockResponsible').value = block.responsible || '';
document.getElementById('modalBlockNotes').value = block.notes || '';
document.getElementById('modalBlockUrl').value = block.url || '';
this._populateTypeSelect(block.type_id);
this._populateDaySelect(block.date);
@@ -222,6 +223,7 @@ const App = {
document.getElementById('modalBlockEnd').value = end || '10:00';
document.getElementById('modalBlockResponsible').value = '';
document.getElementById('modalBlockNotes').value = '';
document.getElementById('modalBlockUrl').value = '';
this._populateTypeSelect(null);
this._populateDaySelect(date);
@@ -312,6 +314,7 @@ const App = {
const end = document.getElementById('modalBlockEnd').value;
const responsible = document.getElementById('modalBlockResponsible').value.trim() || null;
const notes = document.getElementById('modalBlockNotes').value.trim() || null;
const url = document.getElementById('modalBlockUrl').value.trim() || null;
const timeRe = /^\d{2}:\d{2}$/;
if (!title) { this.toast('Zadejte název bloku', 'error'); return; }
@@ -324,7 +327,7 @@ const App = {
if (idx !== -1) {
const existing = this.state.blocks[idx];
Object.assign(this.state.blocks[idx], {
date, title, type_id, start, end, responsible, notes,
date, title, type_id, start, end, responsible, notes, url,
series_id: existing.series_id || null
});
}
@@ -336,14 +339,14 @@ const App = {
const series_id = this.uid();
const dates = this.getDates();
for (const d of dates) {
this.state.blocks.push({ id: this.uid(), date: d, title, type_id, start, end, responsible, notes, series_id });
this.state.blocks.push({ id: this.uid(), date: d, title, type_id, start, end, responsible, notes, url, series_id });
}
document.getElementById('blockModal').classList.add('hidden');
this.renderCanvas();
this.toast(`Blok přidán do ${dates.length} dnů`, 'success');
return;
} else {
this.state.blocks.push({ id: this.uid(), date, title, type_id, start, end, responsible, notes, series_id: null });
this.state.blocks.push({ id: this.uid(), date, title, type_id, start, end, responsible, notes, url, series_id: null });
}
}

View File

@@ -31,7 +31,8 @@
"id": "d1_b3", "date": "2026-03-01",
"start": "09:00", "end": "11:00",
"title": "Stopovací hra v lese", "type_id": "main",
"responsible": "Lucka", "notes": "4 skupiny"
"responsible": "Lucka", "notes": "4 skupiny",
"url": "https://example.com/stopovaci-hra"
},
{
"id": "d1_b4", "date": "2026-03-01",