568 lines
22 KiB
HTML
568 lines
22 KiB
HTML
{% extends "layout.html" %}
|
||
{% block body %}
|
||
<div class="page-head">
|
||
<div>
|
||
<h1>Aufträge</h1>
|
||
<p class="tiny">Kunden, Angebote, Positionen und Fortschritt.</p>
|
||
</div>
|
||
<div class="actions">
|
||
<a class="ghost" id="lieferschein-link" href="#" hidden target="_blank" rel="noopener">Lieferschein</a>
|
||
<button type="button" class="btn-primary" id="order-new-head"><svg class="ico"><use href="#i-plus"/></svg>Neuer Auftrag</button>
|
||
</div>
|
||
</div>
|
||
<div class="split edit-split is-idle" id="order-layout">
|
||
<section class="card">
|
||
<div class="field inline">
|
||
<label for="filter-status">Status</label>
|
||
<select id="filter-status">
|
||
<option value="">alle</option>
|
||
<option value="idee">Idee</option>
|
||
<option value="angebot">Angebot</option>
|
||
<option value="bestaetigt">bestätigt</option>
|
||
<option value="druckt">druckt</option>
|
||
<option value="fertig">fertig</option>
|
||
<option value="geliefert">geliefert</option>
|
||
<option value="bezahlt">bezahlt</option>
|
||
</select>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead><tr><th>Titel</th><th>Status</th><th>Fortschritt</th></tr></thead>
|
||
<tbody id="order-rows"></tbody>
|
||
</table>
|
||
<div class="pager" id="order-pager" hidden></div>
|
||
</div>
|
||
</section>
|
||
<section class="card edit-panel" id="order-panel" hidden>
|
||
<div class="mat-form-head">
|
||
<h2 id="order-title">Neuer Auftrag</h2>
|
||
<button type="button" class="ghost tiny-btn" id="order-edit-btn" hidden>Bearbeiten</button>
|
||
<button type="button" class="icon-btn" id="order-close" title="Schließen" aria-label="Schließen"></button>
|
||
</div>
|
||
|
||
<form id="order-form" hidden>
|
||
<input id="order-id" type="hidden">
|
||
<div class="field"><label for="order-title-in">Titel</label><input id="order-title-in" required></div>
|
||
<div class="grid-2">
|
||
<div class="field"><label for="order-status">Status</label>
|
||
<select id="order-status">
|
||
<option value="idee">Idee</option>
|
||
<option value="angebot">Angebot</option>
|
||
<option value="bestaetigt">bestätigt</option>
|
||
<option value="druckt">druckt</option>
|
||
<option value="fertig">fertig</option>
|
||
<option value="geliefert">geliefert</option>
|
||
<option value="bezahlt">bezahlt</option>
|
||
</select>
|
||
</div>
|
||
<div class="field"><label for="order-due">Fällig</label><input id="order-due" type="date"></div>
|
||
</div>
|
||
<div class="field"><label for="order-customer">Kunde</label>
|
||
<select id="order-customer"><option value="">— keiner —</option></select>
|
||
</div>
|
||
<div class="field"><label for="order-notes">Notiz</label><textarea id="order-notes"></textarea></div>
|
||
<div class="actions">
|
||
<button type="submit" class="btn-primary">Speichern</button>
|
||
<button type="button" class="ghost" id="order-cancel-edit" hidden>Abbrechen</button>
|
||
<button type="button" class="btn-danger" id="order-delete" hidden>Löschen</button>
|
||
</div>
|
||
</form>
|
||
<p id="order-note" class="tiny"></p>
|
||
|
||
<div id="order-detail" hidden>
|
||
<p class="tiny" id="order-progress"></p>
|
||
<p class="tiny" id="order-actuals"></p>
|
||
<p class="tiny" id="order-notes-view"></p>
|
||
|
||
<h2 class="section-gap">Positionen</h2>
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead><tr><th>Datei</th><th class="num">Stk</th><th class="num">fertig</th><th></th></tr></thead>
|
||
<tbody id="item-rows"></tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<form id="item-edit-form" class="section-gap card" hidden style="padding:0.75rem;margin-top:0.75rem">
|
||
<input type="hidden" id="item-edit-id">
|
||
<h3 style="margin:0 0 0.5rem;font-size:0.95rem">Position ändern</h3>
|
||
<div class="grid-2">
|
||
<div class="field"><label for="item-edit-qty">Stück</label><input id="item-edit-qty" type="number" min="1" step="1" value="1"></div>
|
||
<div class="field"><label for="item-edit-price">Preis/Stk €</label><input id="item-edit-price" type="number" min="0" step="0.01" value="0"></div>
|
||
</div>
|
||
<div class="actions">
|
||
<button type="submit" class="btn-primary">Speichern</button>
|
||
<button type="button" class="ghost" id="item-edit-cancel">Abbrechen</button>
|
||
</div>
|
||
</form>
|
||
|
||
<form id="item-form" class="section-gap">
|
||
<div class="grid-2">
|
||
<div class="field"><label for="item-file">Datei</label><select id="item-file"><option value="">—</option></select></div>
|
||
<div class="field"><label for="item-qty">Stück</label><input id="item-qty" type="number" min="1" step="1" value="1"></div>
|
||
<div class="field"><label for="item-price">Preis/Stk €</label><input id="item-price" type="number" min="0" step="0.01" value="0"></div>
|
||
<div class="field"><label for="item-material">Material</label><input id="item-material"></div>
|
||
</div>
|
||
<div class="actions"><button type="submit">Position hinzufügen</button></div>
|
||
</form>
|
||
<details class="section-gap">
|
||
<summary>Kunde anlegen / bearbeiten</summary>
|
||
<form id="customer-form">
|
||
<input type="hidden" id="cust-id">
|
||
<div class="field"><label for="cust-name">Name</label><input id="cust-name" required></div>
|
||
<div class="field"><label for="cust-email">E-Mail</label><input id="cust-email" type="email"></div>
|
||
<div class="actions">
|
||
<button type="submit" class="btn-primary">Kunde speichern</button>
|
||
<button type="button" class="ghost" id="cust-edit-current">Aktuellen laden</button>
|
||
<button type="button" class="btn-danger" id="cust-delete" hidden>Kunde löschen</button>
|
||
</div>
|
||
</form>
|
||
</details>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
{% endblock %}
|
||
{% block script %}
|
||
<script>
|
||
const STATUS_LABEL = {
|
||
idee: "Idee", angebot: "Angebot", bestaetigt: "bestätigt", druckt: "druckt",
|
||
fertig: "fertig", geliefert: "geliefert", bezahlt: "bezahlt",
|
||
};
|
||
let orders = [];
|
||
let customers = [];
|
||
let files = [];
|
||
let current = null;
|
||
/** idle | create | view | edit */
|
||
let panelMode = "idle";
|
||
let orderPage = 1;
|
||
|
||
document.getElementById("order-close").innerHTML = icon("x");
|
||
|
||
function openPanel() {
|
||
document.getElementById("order-panel").hidden = false;
|
||
document.getElementById("order-layout").classList.remove("is-idle");
|
||
document.getElementById("order-panel").scrollIntoView({ block: "nearest", behavior: "smooth" });
|
||
}
|
||
|
||
function closePanel() {
|
||
panelMode = "idle";
|
||
current = null;
|
||
document.getElementById("order-panel").hidden = true;
|
||
document.getElementById("order-layout").classList.add("is-idle");
|
||
document.getElementById("order-form").hidden = true;
|
||
document.getElementById("order-detail").hidden = true;
|
||
document.getElementById("order-edit-btn").hidden = true;
|
||
document.getElementById("order-cancel-edit").hidden = true;
|
||
document.getElementById("order-delete").hidden = true;
|
||
document.getElementById("lieferschein-link").hidden = true;
|
||
document.getElementById("item-edit-form").hidden = true;
|
||
document.getElementById("order-note").textContent = "";
|
||
history.replaceState(null, "", location.pathname);
|
||
renderList();
|
||
}
|
||
|
||
function setFormValues(order) {
|
||
document.getElementById("order-id").value = order ? order.id : "";
|
||
document.getElementById("order-title-in").value = order ? order.title : "";
|
||
document.getElementById("order-status").value = order ? order.status : "idee";
|
||
document.getElementById("order-due").value = order && order.due_at ? order.due_at.slice(0, 10) : "";
|
||
document.getElementById("order-customer").value = order && order.customer_id ? order.customer_id : "";
|
||
document.getElementById("order-notes").value = order ? (order.notes || "") : "";
|
||
}
|
||
|
||
function fillCustomers() {
|
||
const sel = document.getElementById("order-customer");
|
||
const val = sel.value;
|
||
sel.innerHTML = '<option value="">— keiner —</option>';
|
||
customers.forEach((c) => {
|
||
const o = document.createElement("option");
|
||
o.value = c.id;
|
||
o.textContent = c.name;
|
||
sel.appendChild(o);
|
||
});
|
||
if (val) sel.value = val;
|
||
}
|
||
|
||
function fillFiles() {
|
||
const sel = document.getElementById("item-file");
|
||
sel.innerHTML = '<option value="">—</option>';
|
||
files.slice(0, 400).forEach((f) => {
|
||
const o = document.createElement("option");
|
||
o.value = f.id;
|
||
o.textContent = f.name;
|
||
sel.appendChild(o);
|
||
});
|
||
}
|
||
|
||
function paintActuals(order) {
|
||
const el = document.getElementById("order-actuals");
|
||
const a = order.actuals;
|
||
if (!a) { el.textContent = ""; return; }
|
||
const bits = [
|
||
"Ist: " + euro(a.cost_total),
|
||
hours(a.duration_s) || "0 min",
|
||
"Ausschuss " + a.pieces_bad + " Stk",
|
||
];
|
||
if (a.cost_per_part != null) bits.push(euro(a.cost_per_part) + "/Teil");
|
||
if (a.yield_percent != null) bits.push("Yield " + Math.round(a.yield_percent) + " %");
|
||
el.textContent = bits.join(" · ");
|
||
}
|
||
|
||
function showCreate() {
|
||
panelMode = "create";
|
||
current = null;
|
||
setFormValues(null);
|
||
document.getElementById("order-title").textContent = "Neuer Auftrag";
|
||
document.getElementById("order-form").hidden = false;
|
||
document.getElementById("order-detail").hidden = true;
|
||
document.getElementById("order-edit-btn").hidden = true;
|
||
document.getElementById("order-cancel-edit").hidden = true;
|
||
document.getElementById("order-delete").hidden = true;
|
||
document.getElementById("lieferschein-link").hidden = true;
|
||
document.getElementById("order-note").textContent = "";
|
||
document.getElementById("item-edit-form").hidden = true;
|
||
history.replaceState(null, "", location.pathname);
|
||
openPanel();
|
||
renderList();
|
||
document.getElementById("order-title-in").focus();
|
||
}
|
||
|
||
function showView(order) {
|
||
panelMode = "view";
|
||
current = order;
|
||
setFormValues(order);
|
||
document.getElementById("order-title").textContent = order.title || "Auftrag";
|
||
document.getElementById("order-form").hidden = true;
|
||
document.getElementById("order-detail").hidden = false;
|
||
document.getElementById("order-edit-btn").hidden = false;
|
||
document.getElementById("order-cancel-edit").hidden = true;
|
||
document.getElementById("order-delete").hidden = true;
|
||
document.getElementById("item-edit-form").hidden = true;
|
||
const link = document.getElementById("lieferschein-link");
|
||
link.href = "/auftraege/" + order.id + "/lieferschein";
|
||
link.hidden = false;
|
||
history.replaceState(null, "", "#" + order.id);
|
||
const prog = order.progress;
|
||
document.getElementById("order-progress").textContent = prog ? "Fortschritt: " + prog.label : "";
|
||
paintActuals(order);
|
||
const notes = (order.notes || "").trim();
|
||
document.getElementById("order-notes-view").textContent = notes ? "Notiz: " + notes : "";
|
||
renderItems(order.items || []);
|
||
openPanel();
|
||
renderList();
|
||
}
|
||
|
||
function showEdit(order) {
|
||
panelMode = "edit";
|
||
current = order;
|
||
setFormValues(order);
|
||
document.getElementById("order-title").textContent = "Bearbeiten";
|
||
document.getElementById("order-form").hidden = false;
|
||
document.getElementById("order-detail").hidden = true;
|
||
document.getElementById("order-edit-btn").hidden = true;
|
||
document.getElementById("order-cancel-edit").hidden = false;
|
||
document.getElementById("order-delete").hidden = false;
|
||
document.getElementById("item-edit-form").hidden = true;
|
||
openPanel();
|
||
renderList();
|
||
document.getElementById("order-title-in").focus();
|
||
}
|
||
|
||
function renderItems(items) {
|
||
const body = document.getElementById("item-rows");
|
||
body.innerHTML = "";
|
||
if (!items.length) {
|
||
body.innerHTML = '<tr><td colspan="4" class="empty">Keine Positionen.</td></tr>';
|
||
return;
|
||
}
|
||
items.forEach((item) => {
|
||
const tr = document.createElement("tr");
|
||
tr.innerHTML = "<td></td><td class=\"num\"></td><td class=\"num\"></td><td class=\"actions\"></td>";
|
||
if (item.file_id) {
|
||
tr.children[0].innerHTML = linkTo("datei", item.file_id, item.file_name || ("#" + item.file_id));
|
||
} else {
|
||
tr.children[0].textContent = item.file_name || "—";
|
||
}
|
||
tr.children[1].textContent = item.qty;
|
||
tr.children[2].textContent = item.done_ok + (item.done_bad ? " (−" + item.done_bad + ")" : "");
|
||
const tools = document.createElement("div");
|
||
tools.className = "queue-tools";
|
||
const btn = document.createElement("button");
|
||
btn.type = "button";
|
||
btn.className = "ghost tiny-btn";
|
||
btn.textContent = "Einreihen";
|
||
btn.disabled = !item.file_id || !current;
|
||
btn.addEventListener("click", () => einreihen(item.id));
|
||
const edit = document.createElement("button");
|
||
edit.type = "button";
|
||
edit.className = "ghost tiny-btn";
|
||
edit.textContent = "Ändern";
|
||
edit.addEventListener("click", () => beginEditItem(item));
|
||
const del = document.createElement("button");
|
||
del.type = "button";
|
||
del.className = "btn-danger tiny-btn";
|
||
del.textContent = "Löschen";
|
||
del.addEventListener("click", (event) => {
|
||
armButton(event.currentTarget, "Position löschen?", async () => {
|
||
await api("/auftraege/positionen/" + item.id, { method: "DELETE" });
|
||
toast("Position gelöscht.");
|
||
const full = await api("/auftraege/" + current.id);
|
||
showView(full);
|
||
});
|
||
});
|
||
tools.append(btn, edit, del);
|
||
tr.children[3].appendChild(tools);
|
||
body.appendChild(tr);
|
||
});
|
||
}
|
||
|
||
function beginEditItem(item) {
|
||
document.getElementById("item-edit-id").value = item.id;
|
||
document.getElementById("item-edit-qty").value = item.qty || 1;
|
||
document.getElementById("item-edit-price").value = item.unit_price || 0;
|
||
document.getElementById("item-edit-form").hidden = false;
|
||
document.getElementById("item-edit-qty").focus();
|
||
}
|
||
|
||
async function einreihen(itemId) {
|
||
if (!current) return;
|
||
try {
|
||
await api("/auftraege/" + current.id + "/einreihen", {
|
||
method: "POST",
|
||
json: { item_id: itemId, when_free: true, confirm: true },
|
||
});
|
||
toast("In Warteschlange.");
|
||
const full = await api("/auftraege/" + current.id);
|
||
showView(full);
|
||
await loadList();
|
||
} catch (error) {
|
||
showNote("order-note", error);
|
||
}
|
||
}
|
||
|
||
function renderList() {
|
||
const body = document.getElementById("order-rows");
|
||
body.innerHTML = "";
|
||
const pager = document.getElementById("order-pager");
|
||
if (!orders.length) {
|
||
body.innerHTML = '<tr><td colspan="3" class="empty"><div class="empty-state"><p>Noch kein Auftrag.</p><p><button type="button" class="btn-primary" id="order-empty-new">Ersten Auftrag anlegen</button></p></div></td></tr>';
|
||
document.getElementById("order-empty-new")?.addEventListener("click", showCreate);
|
||
renderPager(pager, null, () => {});
|
||
return;
|
||
}
|
||
const selected = current ? String(current.id) : "";
|
||
const paged = pageSlice(orders, orderPage, DEFAULT_PAGE_SIZE);
|
||
orderPage = paged.page;
|
||
paged.items.forEach((order) => {
|
||
const tr = document.createElement("tr");
|
||
tr.className = "pick" + (selected && String(order.id) === selected ? " on" : "");
|
||
tr.innerHTML = "<td></td><td></td><td class=\"cell-path\"></td>";
|
||
tr.children[0].textContent = order.title;
|
||
const st = order.status || "";
|
||
const chipState = st === "druckt" ? "printing" : (st === "fertig" || st === "geliefert" || st === "bezahlt") ? "done" : st === "bestaetigt" ? "waiting" : "idle";
|
||
tr.children[1].innerHTML = chipHtml(chipState, STATUS_LABEL[st] || st);
|
||
tr.children[2].textContent = (order.progress && order.progress.label) || "";
|
||
tr.children[2].title = (order.progress && order.progress.label) || "";
|
||
tr.addEventListener("click", async () => {
|
||
try {
|
||
const full = await api("/auftraege/" + order.id);
|
||
showView(full);
|
||
} catch (error) {
|
||
showNote("order-note", error);
|
||
}
|
||
});
|
||
body.appendChild(tr);
|
||
});
|
||
renderPager(pager, paged, (p) => {
|
||
orderPage = p;
|
||
renderList();
|
||
});
|
||
}
|
||
|
||
async function loadList() {
|
||
const st = document.getElementById("filter-status").value;
|
||
const q = st ? "?status=" + encodeURIComponent(st) : "";
|
||
orders = await api("/auftraege" + q);
|
||
orderPage = 1;
|
||
renderList();
|
||
}
|
||
|
||
async function loadBase() {
|
||
[customers, files] = await Promise.all([
|
||
api("/kunden").catch(() => []),
|
||
api("/dateien").catch(() => []),
|
||
]);
|
||
fillCustomers();
|
||
fillFiles();
|
||
await loadList();
|
||
}
|
||
|
||
document.getElementById("filter-status").addEventListener("change", () => loadList().catch((e) => showNote("order-note", e)));
|
||
|
||
document.getElementById("order-new-head").addEventListener("click", showCreate);
|
||
document.getElementById("order-close").addEventListener("click", closePanel);
|
||
document.getElementById("order-edit-btn").addEventListener("click", () => {
|
||
if (current) showEdit(current);
|
||
});
|
||
document.getElementById("order-cancel-edit").addEventListener("click", async () => {
|
||
if (!current) {
|
||
closePanel();
|
||
return;
|
||
}
|
||
try {
|
||
const full = await api("/auftraege/" + current.id);
|
||
showView(full);
|
||
} catch (error) {
|
||
showNote("order-note", error);
|
||
}
|
||
});
|
||
|
||
document.getElementById("order-form").addEventListener("submit", async (event) => {
|
||
event.preventDefault();
|
||
const id = document.getElementById("order-id").value;
|
||
const payload = {
|
||
title: document.getElementById("order-title-in").value,
|
||
status: document.getElementById("order-status").value,
|
||
notes: document.getElementById("order-notes").value,
|
||
due_at: document.getElementById("order-due").value || null,
|
||
customer_id: document.getElementById("order-customer").value || null,
|
||
};
|
||
try {
|
||
const saved = id
|
||
? await api("/auftraege/" + id, { method: "PATCH", json: payload })
|
||
: await api("/auftraege", { method: "POST", json: payload });
|
||
showNote("order-note", null);
|
||
toast("Gespeichert.", "good");
|
||
await loadList();
|
||
const full = await api("/auftraege/" + saved.id);
|
||
showView(full);
|
||
} catch (error) {
|
||
showNote("order-note", error);
|
||
}
|
||
});
|
||
|
||
document.getElementById("order-delete").addEventListener("click", (event) => {
|
||
const id = document.getElementById("order-id").value;
|
||
if (!id) return;
|
||
armButton(event.currentTarget, "Auftrag wirklich löschen?", async () => {
|
||
await api("/auftraege/" + id, { method: "DELETE" });
|
||
toast("Gelöscht.");
|
||
closePanel();
|
||
loadList();
|
||
});
|
||
});
|
||
|
||
document.getElementById("item-form").addEventListener("submit", async (event) => {
|
||
event.preventDefault();
|
||
if (!current) {
|
||
showNote("order-note", new Error("Zuerst Auftrag speichern."));
|
||
return;
|
||
}
|
||
const payload = {
|
||
file_id: document.getElementById("item-file").value || null,
|
||
qty: Number(document.getElementById("item-qty").value),
|
||
unit_price: Number(document.getElementById("item-price").value),
|
||
material_type: document.getElementById("item-material").value,
|
||
};
|
||
try {
|
||
await api("/auftraege/" + current.id + "/positionen", { method: "POST", json: payload });
|
||
const full = await api("/auftraege/" + current.id);
|
||
showView(full);
|
||
} catch (error) {
|
||
showNote("order-note", error);
|
||
}
|
||
});
|
||
|
||
document.getElementById("item-edit-form").addEventListener("submit", async (event) => {
|
||
event.preventDefault();
|
||
if (!current) return;
|
||
const itemId = document.getElementById("item-edit-id").value;
|
||
try {
|
||
await api("/auftraege/positionen/" + itemId, {
|
||
method: "PATCH",
|
||
json: {
|
||
qty: Number(document.getElementById("item-edit-qty").value) || 1,
|
||
unit_price: Number(document.getElementById("item-edit-price").value) || 0,
|
||
},
|
||
});
|
||
document.getElementById("item-edit-form").hidden = true;
|
||
const full = await api("/auftraege/" + current.id);
|
||
showView(full);
|
||
toast("Position gespeichert.");
|
||
} catch (error) {
|
||
showNote("order-note", error);
|
||
}
|
||
});
|
||
document.getElementById("item-edit-cancel").addEventListener("click", () => {
|
||
document.getElementById("item-edit-form").hidden = true;
|
||
});
|
||
|
||
document.getElementById("customer-form").addEventListener("submit", async (event) => {
|
||
event.preventDefault();
|
||
const id = document.getElementById("cust-id").value;
|
||
const payload = {
|
||
name: document.getElementById("cust-name").value,
|
||
email: document.getElementById("cust-email").value,
|
||
};
|
||
try {
|
||
const row = id
|
||
? await api("/kunden/" + id, { method: "PATCH", json: payload })
|
||
: await api("/kunden", { method: "POST", json: payload });
|
||
if (id) {
|
||
customers = customers.map((c) => (c.id === row.id ? row : c));
|
||
} else {
|
||
customers.push(row);
|
||
}
|
||
fillCustomers();
|
||
document.getElementById("order-customer").value = row.id;
|
||
document.getElementById("cust-id").value = "";
|
||
document.getElementById("cust-name").value = "";
|
||
document.getElementById("cust-email").value = "";
|
||
document.getElementById("cust-delete").hidden = true;
|
||
toast(id ? "Kunde gespeichert." : "Kunde angelegt.");
|
||
} catch (error) {
|
||
showNote("order-note", error);
|
||
}
|
||
});
|
||
|
||
document.getElementById("cust-edit-current").addEventListener("click", () => {
|
||
const id = document.getElementById("order-customer").value;
|
||
const row = customers.find((c) => String(c.id) === String(id));
|
||
if (!row) {
|
||
toast("Zuerst einen Kunden wählen.", "bad");
|
||
return;
|
||
}
|
||
document.getElementById("cust-id").value = row.id;
|
||
document.getElementById("cust-name").value = row.name || "";
|
||
document.getElementById("cust-email").value = row.email || "";
|
||
document.getElementById("cust-delete").hidden = false;
|
||
});
|
||
|
||
document.getElementById("cust-delete").addEventListener("click", (event) => {
|
||
const id = document.getElementById("cust-id").value;
|
||
if (!id) return;
|
||
armButton(event.currentTarget, "Kunde löschen?", async () => {
|
||
await api("/kunden/" + id, { method: "DELETE" });
|
||
customers = customers.filter((c) => String(c.id) !== String(id));
|
||
fillCustomers();
|
||
document.getElementById("cust-id").value = "";
|
||
document.getElementById("cust-name").value = "";
|
||
document.getElementById("cust-email").value = "";
|
||
document.getElementById("cust-delete").hidden = true;
|
||
toast("Kunde gelöscht.");
|
||
});
|
||
});
|
||
|
||
loadBase().then(async () => {
|
||
const hash = (location.hash || "").replace(/^#/, "");
|
||
if (hash && /^\d+$/.test(hash)) {
|
||
try {
|
||
const full = await api("/auftraege/" + hash);
|
||
showView(full);
|
||
} catch (error) {
|
||
showNote("order-note", error);
|
||
}
|
||
}
|
||
}).catch((error) => showNote("order-note", error));
|
||
</script>
|
||
{% endblock %}
|