Call_Log: Ansprechpartner als Select aus Kontaktadressen des Projekts

Im Call-Log-Formular wird "Name Ansprechpartner" durch ein Select ersetzt,
das die Kontaktadressen des gewählten Projekts anbietet, sofern der Name nicht
bereits voreingestellt ist (z. B. aus der Kontaktadressenliste). Bei Auswahl
einer Kontaktadresse werden Name und Telefon automatisch übernommen. Bei
Projektwechsel werden Ansprechpartner und Telefon zurückgesetzt.

Kontaktadressenliste: add_call-Button pro Zeile, der eine Gesprächsnotiz mit
den ForeignKeys aus der Kontaktadresse (projektId, kundenName, name, telefon)
anlegt.

AGENTS.md: Vorgabe ergänzt — Farben priorisiert aus app-theme.css.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
gg committed 2026-09-01 18:21:28 +02:00
1 parent ad24981a0b
commit a8838a1a38
3 files changed
+71 -4

No files matched your search

+41 -2
View File
@@ -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))
),