diff --git a/Client/Components/call_log.ts b/Client/Components/call_log.ts index d8a46f6..dfb4c52 100644 --- a/Client/Components/call_log.ts +++ b/Client/Components/call_log.ts @@ -229,6 +229,9 @@ export const Call_Log: m.ClosureComponent = (vnode) => { let kundenListe: iKunde[] = [] let projektListe: iProjekt[] = [] let kontaktadressenListe: iKontaktadresse[] = [] + // Im Auswahl-Modus (nicht vorbelegt): gewählte Kunden-ID für die + // abhängige Projekt-Filterung. + let selectedKundenId: number = -1 // Asynchron geladene Tag-Liste (nur zuordenbare, nicht is_parent). let tagListe: iTag[] = [] // Aktuell ausgewählte Tag-IDs im Formular. @@ -272,6 +275,27 @@ export const Call_Log: m.ClosureComponent = (vnode) => { return min.toString().padStart(2, '0') + ':' + sek.toString().padStart(2, '0') } + /** + * Berechnet das Wiedervorlage-Datum automatisch aus den ausgewählten + * Tags. Basis ist das Datum der Notiz (currentLog.datum) oder heute, + * falls dieses leer ist. Aus allen ausgewählten Tags mit + * loest_wiedervorlage_aus=True wird das Minimum von + * wiedervorlage_abstand_tage ermittelt und zum Basisdatum addiert. + * Liefert einen leeren String, wenn kein Tag mit + * loest_wiedervorlage_aus=True ausgewählt ist. + */ + const berechneWiedervorlageDatum = (tagIds: number[]): string => { + const basis = currentLog.datum || new Date().toISOString().split('T')[0] + const wvTags = tagListe.filter((t: iTag) => + tagIds.includes(t.id) && t.loest_wiedervorlage_aus && t.wiedervorlage_abstand_tage != null + ) + if (wvTags.length === 0) return "" + const minAbstand = Math.min(...wvTags.map(t => t.wiedervorlage_abstand_tage as number)) + const basisDate = new Date(basis) + basisDate.setDate(basisDate.getDate() + minAbstand) + return basisDate.toISOString().split('T')[0] + } + return { oninit: (vnode) => { const routeParams = m.route.param() @@ -332,23 +356,12 @@ export const Call_Log: m.ClosureComponent = (vnode) => { guard.cleanup() }, view: (vnode) => { - // kunden_id aus dem geladenen Projekt ableiten (fuer abhaengiges - // Projekt-Select). Wird nur bei nicht voreingestelltem Projekt - // benoetigt. - const internKundenId = (() => { - if (currentLog.projekt_id !== -1) { - const p = projektListe.find((p: iProjekt) => p.id === currentLog.projekt_id) - return p ? p.kunden_id : -1 - } - return -1 - })() - const kundenOptionen: iSelectOption[] = kundenListe.map((k: iKunde) => { return {value: k.id, label: k.name} }) // Projekt-Optionen sind vom gewählten Kunden abhängig. const projektOptionen: iSelectOption[] = projektListe - .filter((p: iProjekt) => internKundenId === -1 || p.kunden_id === internKundenId) + .filter((p: iProjekt) => selectedKundenId === -1 || p.kunden_id === selectedKundenId) .map((p: iProjekt) => { return {value: p.id, label: p.projektname} }) @@ -370,17 +383,19 @@ export const Call_Log: m.ClosureComponent = (vnode) => { const formData = new FormData(form) const gespeichertesLog: iCallLog = { id: currentLog.id, - projekt_id: currentLog.projekt_id, + projekt_id: projektVoreingestellt + ? currentLog.projekt_id + : parseInt((formData.get("projekt_id_select") as string) || "-1"), projekt_name: "", kunden_name: "", - datum: (formData.get("datum") as string) || "", + datum: currentLog.datum || "", uhrzeit_beginn: (formData.get("uhrzeit_beginn") as string) || "", dauer: (formData.get("dauer") as string) || "", betreff: (formData.get("betreff") as string) || "", name_ansprechpartner: (formData.get("name_ansprechpartner") as string) || "", telefon: (formData.get("telefon") as string) || "", notiz: (formData.get("notiz") as string) || "", - wiedervorlage_datum: (formData.get("wiedervorlage_datum") as string) || "", + wiedervorlage_datum: currentLog.wiedervorlage_datum || "", erledigt: formData.get("erledigt") === "on", tag_ids: formData.getAll("tag_ids").map((v) => parseInt(v as string)).filter((n) => !isNaN(n)) } @@ -400,26 +415,70 @@ export const Call_Log: m.ClosureComponent = (vnode) => { } as iBreadcrumbBar), m("div", {style: {flex: '1', 'min-height': '0'}}, m(Form, guard.formAttrs(submit) as iForm, [ - m("input", {type: "hidden", name: "projekt_id", value: currentLog.projekt_id}), - // Kunde und Projekt als Read-Only-Text, da voreingestellt. - m("label", "Kunde", - m("input.w3-input.w3-border", { - type: "text", - value: currentLog.kunden_name, - readonly: true, - style: {'margin-bottom': '5px'} - })), - m("label", "Projekt", - m("input.w3-input.w3-border", { - type: "text", - value: currentLog.projekt_name, - readonly: true, - style: {'margin-bottom': '5px'} - })), + projektVoreingestellt + ? m("input", {type: "hidden", name: "projekt_id", value: currentLog.projekt_id}) + : null, + // Kunde und Projekt: vorbelegt als Read-Only-Text, + // nicht vorbelegt als auswählbare Select-Widgets. + projektVoreingestellt + ? [ + m("label", "Kunde", + m("input.w3-input.w3-border", { + type: "text", + value: currentLog.kunden_name, + readonly: true, + style: {'margin-bottom': '5px'} + })), + m("label", "Projekt", + m("input.w3-input.w3-border", { + type: "text", + value: currentLog.projekt_name, + readonly: true, + style: {'margin-bottom': '5px'} + })) + ] + : [ + m("label", "Kunde", m(Select, { + name: "kunden_id", + value: selectedKundenId, + options: kundenOptionen, + required: true, + onchanged: (kundenId: number) => { + selectedKundenId = kundenId + // Projekt zurücksetzen, da es zum + // alten Kunden gehörte. + currentLog.projekt_id = -1 + currentLog.projekt_name = "" + currentLog.kunden_name = kundenListe.find(k => k.id === kundenId)?.name || "" + guard.markDirty() + } + } as iSelect)), + m("label", "Projekt", m(Select, { + name: "projekt_id_select", + value: currentLog.projekt_id, + options: projektOptionen, + required: true, + onchanged: (projektId: number) => { + const p = projektListe.find(pr => pr.id === projektId) + currentLog.projekt_id = projektId + currentLog.projekt_name = p?.projektname || "" + currentLog.kunden_name = p?.kunden_name || "" + guard.markDirty() + } + } as iSelect)) + ], // Datum, Startzeit und Dauer in einer Zeile. m("div", {style: {display: 'flex', gap: '8px', 'margin-bottom': '5px'}}, m("label", {style: {flex: '1'}}, "Datum", - m(Input, {name: "datum", value: currentLog.datum, type: "date"} as iInput)), + m(Input, { + name: "datum", + value: currentLog.datum, + type: "date", + oninput: (e: Event) => { + currentLog.datum = (e.target as HTMLInputElement).value + guard.markDirty() + } + } as iInput)), m("label", {style: {flex: '1'}}, "Uhrzeit Beginn", m(Input, {name: "uhrzeit_beginn", value: currentLog.uhrzeit_beginn, type: "time"} as iInput)), m("label", {style: {flex: '1'}}, "Dauer (mm:ss)", @@ -463,9 +522,25 @@ export const Call_Log: m.ClosureComponent = (vnode) => { placeholder: "kein Tag", modalTitle: "Tags auswählen", editTooltip: "Tags bearbeiten", - onchanged: () => { guard.markDirty() } + onchanged: (newSelection: number[]) => { + selectedTagIds.splice(0, selectedTagIds.length) + Array.prototype.push.apply(selectedTagIds, newSelection) + const vorschlag = berechneWiedervorlageDatum(newSelection) + if (vorschlag) { + currentLog.wiedervorlage_datum = vorschlag + } + guard.markDirty() + } } as iMultiSelect)), - m("label", "Wiedervorlage Datum", m(Input, {name: "wiedervorlage_datum", value: currentLog.wiedervorlage_datum, type: "date"} as iInput)), + m("label", "Wiedervorlage Datum", m(Input, { + name: "wiedervorlage_datum", + value: currentLog.wiedervorlage_datum, + type: "date", + oninput: (e: Event) => { + currentLog.wiedervorlage_datum = (e.target as HTMLInputElement).value + guard.markDirty() + } + } as iInput)), m("label", m(Input, {name: "erledigt", type: "checkbox", value: currentLog.erledigt} as iInput), "Erledigt") ]) )