diff --git a/AGENTS.md b/AGENTS.md index ca27e5f..ca70c54 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,6 +6,7 @@ In den Programmen sollen aussagekräftige Kommentare in deutsch erstellt werden. Verwende bei der GUI primar die widgets, die es in client/Components/widgets gibt. Fehler widgets, gib eines Hinweis und erstelle einen Vorschlag für ein entsprechendes neues Widget. Es soll immer ein responsive Design implmentiert werden, da die App sowohl auf PCs als auch auf mobilen Geräten laufen soll. Verwende für Attribute der GUI-Oberfläche nach Möglichkeit Klassen aus NGINX_Server/html/static/w3.css, insbesondere wenn es darum geht, eine Respnosive Design zu implentieren. +Für Farben (foreground und background) verwende priorisiert die Klassen aus NGINX_Server/html/static/app-theme.css View-Container werden mit `w3-container.w3-auto` gewrappt, damit sie auf kleinen Bildschirmen 100% und auf großen bis max. 1140px (bzw. 95%) zentriert nutzen. Bei Mithril `m(...)`-Aufrufen mit längeren Attributlisten (z.B. mehrere `style`-Eigenschaften oder mehrere Attribute) schreibe die Attribute untereinander, sobald sie in einer Zeile nicht mehr gut lesbar sind (Faustregel: mehr als 2–3 Attribute oder ein mehrzeiliges `style`-Objekt). Kurze Attributlisten (1–2 Attribute, einzeiliges `style`) dürfen in einer Zeile bleiben. Formatbeispiel: diff --git a/Client/Components/call_log.ts b/Client/Components/call_log.ts index 6f9a377..79a027f 100644 --- a/Client/Components/call_log.ts +++ b/Client/Components/call_log.ts @@ -6,6 +6,7 @@ import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} import {iCallLog, getCallLogsByProjektId, getNewCallLogId, addCallLogData, updateCallLogData} from "../models/call_log" import {iKunde, getKunden} from "../models/kunde" import {getProjekteByKundenId, getProjektById} from "../models/projekt" +import {getKontaktadressenByProjektId, getKontaktadresseById} from "../models/kontaktadresse" // Aktualisierungsintervall für die Dauer-Anzeige in Millisekunden. // 1000 = jede Sekunde, 60000 = jede Minute. @@ -177,6 +178,10 @@ export const Call_Log: m.ClosureComponent = (vnode) => { // Sind Kunde und Projekt voreingestellt (Liste wurde projektgefiltert // oder aus der Projektliste aufgerufen)? let kundeVoreingestellt: boolean = false + // Ist der Ansprechpartner voreingestellt (aus Kontaktadressenliste)? + // Wenn ja, wird ein readonly Input gezeigt; sonst ein Select, das die + // Kontaktadressen des gewählten Projekts anbietet. + let ansprechpartnerVoreingestellt: boolean = false // Startzeit für die Dauer-Messung (nur bei Neuanlage gesetzt). let startZeit: Date | null = null // Timer-Handle für die Dauer-Aktualisierung. @@ -224,6 +229,7 @@ export const Call_Log: m.ClosureComponent = (vnode) => { erledigt: routeParams.erledigt === true || routeParams.erledigt === "true" } // Bei Neuanlage Startzeit merken und Dauer-Timer starten. + ansprechpartnerVoreingestellt = currentLog.nameAnsprechpartner !== "" if (currentLog.id === -1) { startZeit = new Date() timerId = window.setInterval(() => { @@ -254,6 +260,13 @@ export const Call_Log: m.ClosureComponent = (vnode) => { const projektOptionen: iSelectOption[] = getProjekteByKundenId(currentLog.kundenId).map((p) => { return {value: p.id, label: p.projektname} }) + // Kontaktadressen-Optionen sind vom gewählten Projekt abhängig. + // Werden nur benötigt, wenn der Ansprechpartner nicht voreingestellt ist. + const kontaktadressenOptionen: iSelectOption[] = !ansprechpartnerVoreingestellt + ? getKontaktadressenByProjektId(currentLog.projektId).map((ka) => { + return {value: ka.id, label: ka.name + (ka.firma ? " (" + ka.firma + ")" : "")} + }) + : [] // Einheit der Dauer-Anzeige im Format mm:ss. return m("div.w3-container.w3-auto.form-view-container", m("h1", currentLog.id === -1 ? "Neue Gesprächsnotiz anlegen" : "Gesprächsnotiz"), @@ -283,7 +296,16 @@ export const Call_Log: m.ClosureComponent = (vnode) => { value: currentLog.projektId, options: projektOptionen, readonly: kundeVoreingestellt, - required: true + required: true, + onchanged: (projektId: number) => { + currentLog.projektId = projektId + // Bei Projektwechsel Ansprechpartner und Telefon + // zurücksetzen, falls nicht voreingestellt. + if (!ansprechpartnerVoreingestellt) { + currentLog.nameAnsprechpartner = "" + currentLog.telefon = "" + } + } } as iSelect)), // Datum, Startzeit und Dauer in einer Zeile. m("div", {style: {display: 'flex', gap: '8px', 'margin-bottom': '5px'}}, @@ -306,9 +328,26 @@ export const Call_Log: m.ClosureComponent = (vnode) => { ), m("label", "Betreff", m(Input, {name: "betreff", value: currentLog.betreff, type: "text"} as iInput)), // Ansprechpartner und Telefon in einer Zeile. + // Wenn der Ansprechpartner voreingestellt ist (aus Kontaktadressenliste), + // wird ein readonly Input gezeigt; sonst ein Select, das die + // Kontaktadressen des gewählten Projekts anbietet. Bei Auswahl + // werden Name und Telefon automatisch übernommen. m("div", {style: {display: 'flex', gap: '8px', 'margin-bottom': '5px'}}, m("label", {style: {flex: '1'}}, "Name Ansprechpartner", - m(Input, {name: "nameAnsprechpartner", value: currentLog.nameAnsprechpartner, type: "text"} as iInput)), + ansprechpartnerVoreingestellt + ? m(Input, {name: "nameAnsprechpartner", value: currentLog.nameAnsprechpartner, type: "text", readonly: true} as iInput) + : m(Select, { + name: "kontaktadresseId", + value: -1, + options: kontaktadressenOptionen, + onchanged: (kontaktadresseId: number) => { + const ka = getKontaktadresseById(kontaktadresseId) + if (ka) { + currentLog.nameAnsprechpartner = ka.name + currentLog.telefon = ka.telefon + } + } + } as iSelect)), m("label", {style: {flex: '1'}}, "Telefon", m(Input, {name: "telefon", value: currentLog.telefon, type: "tel"} as iInput)) ), diff --git a/Client/Components/kontaktadresse.ts b/Client/Components/kontaktadresse.ts index e03d3f9..3d974ba 100644 --- a/Client/Components/kontaktadresse.ts +++ b/Client/Components/kontaktadresse.ts @@ -4,7 +4,7 @@ import { Input, iInput, Textarea, iTextarea} from "./widgets/inputs" import {Select, iSelect, iSelectOption} from "./widgets/select" import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2" import {iKontaktadresse, getKontaktadressenByProjektId, getNewKontaktadresseId, addKontaktadresse, updateKontaktadresse} from "../models/kontaktadresse" -import {iProjekt, getProjekte} from "../models/projekt" +import {iProjekt, getProjekte, getProjektById} from "../models/projekt" function createKontaktadresseTreenodes(projektId: number | null = null): iTreenode[] { let filteredKontaktadressen = getKontaktadressenByProjektId(projektId) @@ -67,6 +67,30 @@ export const kontaktadresseliste: m.ClosureComponent = (vnode) => { }) } + const addCallLog = (kontaktadresse: iKontaktadresse, e: Event) => { + e.stopPropagation() + const now = new Date() + const datum = now.toISOString().split('T')[0] + const uhrzeit = now.toTimeString().split(':')[0] + ':' + now.toTimeString().split(':')[1] + // Kundennamen über das Projekt auflösen. + const projekt = getProjektById(kontaktadresse.projektId) + m.route.set("/call_log", { + id: -1, + kundenName: projekt ? projekt.kundenName : "", + projektId: kontaktadresse.projektId, + projektName: kontaktadresse.projektName, + datum: datum, + uhrzeitBeginn: uhrzeit, + dauer: "", + betreff: "", + nameAnsprechpartner: kontaktadresse.name, + telefon: kontaktadresse.telefon, + notiz: "", + wiedervorlageDatum: "", + erledigt: false + }) + } + const columnmapper: (node: iTreenode) => any[] = (node: iTreenode) => { let kontaktadresse: iKontaktadresse = node.nodevalue @@ -77,7 +101,10 @@ export const kontaktadresseliste: m.ClosureComponent = (vnode) => { m("span", kontaktadresse.email), m("span", kontaktadresse.ort), m("span", kontaktadresse.projektName), - m("button.w3-button.w3-small", {style: {padding: '0 2px'}, onclick: (e: Event) => editKontaktadresse(kontaktadresse, e)}, m("i.material-icons", "edit")) + m("div", {style: {display: 'flex', gap: '0', 'white-space': 'nowrap'}}, + m("button.w3-button.w3-small", {style: {padding: '0 2px'}, onclick: (e: Event) => editKontaktadresse(kontaktadresse, e)}, m("i.material-icons", "edit")), + m("button.w3-button.w3-small", {title: "Gesprächsnotiz hinzufügen", style: {padding: '0 2px'}, onclick: (e: Event) => addCallLog(kontaktadresse, e)}, m("i.material-icons", "add_call")) + ) ] }