From 762ae81345407bb3f5c56fd55e29027b5b0f2940 Mon Sep 17 00:00:00 2001 From: caoimhinr Date: Wed, 3 Jun 2026 21:03:49 +0200 Subject: [PATCH] feat: add export/import buttons to tickets UI --- corvid/templates/tickets_base.html | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/corvid/templates/tickets_base.html b/corvid/templates/tickets_base.html index 7f1b5b6..653027d 100644 --- a/corvid/templates/tickets_base.html +++ b/corvid/templates/tickets_base.html @@ -282,6 +282,11 @@ textarea.desc-input {
+ + @@ -1186,6 +1191,43 @@ document.getElementById('env-create-overlay').addEventListener('click', e => { document.addEventListener('click', () => closeEnvDropdown()); document.getElementById('env-selector').addEventListener('click', e => e.stopPropagation()); +// ── Export / Import ────────────────────────────────────────────────────────── + +async function exportTickets() { + try { + const data = await api('/api/tickets/export'); + const blob = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json'}); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + const ts = new Date().toISOString().slice(0, 10); + a.href = url; + a.download = `tickets-export-${ts}.json`; + a.click(); + URL.revokeObjectURL(url); + } catch(e) { + toast(e.message, true); + } +} + +async function importTickets(event) { + const file = event.target.files?.[0]; + if (!file) return; + event.target.value = ''; + try { + const text = await file.text(); + const body = JSON.parse(text); + const result = await api('/api/tickets/import', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(body), + }); + toast(`Imported ${result.imported} ticket(s)`); + await loadEnvironments(); + } catch(e) { + toast(e.message || 'Import failed', true); + } +} + loadEnvironments(); {% endblock %}