Add saved signature presets

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
odweta
2026-09-10 17:43:23 +02:00
co-authored by Copilot App
parent 3e5a0813b0
commit 1317c7297f
7 changed files with 217 additions and 7 deletions
+78 -2
View File
@@ -57,6 +57,10 @@ const signaturePreview = document.getElementById("signaturePreview");
const signatureDropzoneText = document.getElementById("signatureDropzoneText");
const removeSignatureButton = document.getElementById("removeSignatureButton");
const signatureStatus = document.getElementById("signatureStatus");
const signaturePresetSelect = document.getElementById("signaturePresetSelect");
const signaturePresetName = document.getElementById("signaturePresetName");
const saveSignaturePresetButton = document.getElementById("saveSignaturePresetButton");
const deleteSignaturePresetButton = document.getElementById("deleteSignaturePresetButton");
const loadPaymentPresetButton = document.getElementById("loadPaymentPresetButton");
const deletePaymentPresetButton = document.getElementById("deletePaymentPresetButton");
@@ -78,6 +82,7 @@ let itemIndex = 0;
let clients = [];
let invoices = [];
let paymentPresets = [];
let signaturePresets = [];
let currentInvoiceId = null;
let isRegistrationMode = false;
let signatureDataUrl = null;
@@ -99,12 +104,30 @@ const applySignaturePreview = (dataUrl) => {
removeSignatureButton.hidden = false;
} else {
signaturePreview.hidden = true;
signaturePreview.src = "";
signaturePreview.removeAttribute("src");
signatureDropzoneText.hidden = false;
removeSignatureButton.hidden = true;
}
};
const renderSignaturePresetOptions = (selectedId = signaturePresetSelect.value) => {
signaturePresetSelect.innerHTML = "<option value=\"\">Vyberte podpis</option>";
signaturePresets.forEach((preset) => {
const option = document.createElement("option");
option.value = preset.id;
option.textContent = preset.name;
signaturePresetSelect.appendChild(option);
});
signaturePresetSelect.value = selectedId;
deleteSignaturePresetButton.disabled = !signaturePresetSelect.value;
};
const loadSignaturePresets = async () => {
const response = await apiRequest("/api/signature-presets");
signaturePresets = response.presets;
renderSignaturePresetOptions();
};
const readImageDimensions = (dataUrl) => new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => resolve({ width: image.naturalWidth, height: image.naturalHeight });
@@ -162,6 +185,53 @@ signatureInput.addEventListener("change", () => {
handleSignatureFile(signatureInput.files[0]);
signatureInput.value = "";
});
signaturePresetSelect.addEventListener("change", () => {
const preset = signaturePresets.find(({ id }) => String(id) === signaturePresetSelect.value);
deleteSignaturePresetButton.disabled = !preset;
if (preset) {
signaturePresetName.value = preset.name;
applySignaturePreview(preset.dataUrl);
setSignatureStatus("Uložený podpis načten.");
}
});
saveSignaturePresetButton.addEventListener("click", async () => {
if (!signatureDataUrl) {
setSignatureStatus("Nejprve nahrajte nebo vyberte podpis.", true);
return;
}
try {
const response = await apiRequest("/api/signature-presets", {
method: "POST",
body: JSON.stringify({
name: signaturePresetName.value,
dataUrl: signatureDataUrl
})
});
const preset = response.preset;
signaturePresets = [
...signaturePresets.filter((entry) => entry.id !== preset.id && entry.name !== preset.name),
preset
].sort((left, right) => left.name.localeCompare(right.name));
renderSignaturePresetOptions(String(preset.id));
signaturePresetName.value = preset.name;
setSignatureStatus("Podpis byl uložen.");
} catch (error) {
setSignatureStatus(error.message, true);
}
});
deleteSignaturePresetButton.addEventListener("click", async () => {
if (!signaturePresetSelect.value) {
return;
}
try {
await apiRequest(`/api/signature-presets/${signaturePresetSelect.value}`, { method: "DELETE" });
signaturePresets = signaturePresets.filter(({ id }) => String(id) !== signaturePresetSelect.value);
signaturePresetName.value = "";
renderSignaturePresetOptions();
} catch (error) {
setSignatureStatus(error.message, true);
}
});
removeSignatureButton.addEventListener("click", (event) => {
event.stopPropagation();
applySignaturePreview(null);
@@ -374,6 +444,9 @@ const clearInvoiceEditor = () => {
paymentPresetSelect.value = "";
paymentPresetName.value = "";
deletePaymentPresetButton.disabled = true;
signaturePresetSelect.value = "";
signaturePresetName.value = "";
deleteSignaturePresetButton.disabled = true;
["cisloUctu", "iban", "swift"].forEach((id) => {
document.getElementById(id).value = "";
});
@@ -403,6 +476,9 @@ const loadInvoiceIntoEditor = (invoice) => {
document.getElementById(id).value = invoice.data.payment?.[id] || "";
});
applySignaturePreview(invoice.data.signature || null);
signaturePresetSelect.value = "";
signaturePresetName.value = "";
deleteSignaturePresetButton.disabled = true;
setSignatureStatus("");
itemsContainer.replaceChildren();
itemIndex = 0;
@@ -629,7 +705,7 @@ deleteClientButton.addEventListener("click", async () => {
const showInvoiceApp = async () => {
authScreen.hidden = true;
document.getElementById("main-container").hidden = false;
await Promise.all([loadSupplier(), loadClients(), loadInvoices(), loadPaymentPresets(), loadNumberingSettings()]);
await Promise.all([loadSupplier(), loadClients(), loadInvoices(), loadPaymentPresets(), loadSignaturePresets(), loadNumberingSettings()]);
showInvoiceScreen("list");
};