Call_Log: Notiz als Textarea, Dauer-Timer mit mm:ss und DOM-Direktaktualisierung
Notiz-Feld auf Textarea-Widget umgestellt (mehrzeilige Eingabe). Dauer-Feld bei Neuanlage mit Timer automatisch aktualisiert: verstrichene Sekunden als mm:ss formatiert, direkt im DOM gesetzt (ohne redraw, damit andere Felder nicht überschrieben werden). Timer in onremove gestoppt. Konstante DAUER_UPDATE_INTERVALL steuert die Update-Frequenz (Sekunde/Minute). Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
1 parent
16eaab8b64
commit
3accb8f690
1 file changed
+57
-3
@@ -1,12 +1,16 @@
|
|||||||
import m from "mithril"
|
import m from "mithril"
|
||||||
import {Form, iForm} from "./widgets/form"
|
import {Form, iForm} from "./widgets/form"
|
||||||
import { Input, iInput} from "./widgets/inputs"
|
import { Input, iInput, Textarea, iTextarea} from "./widgets/inputs"
|
||||||
import {Select, iSelect, iSelectOption} from "./widgets/select"
|
import {Select, iSelect, iSelectOption} from "./widgets/select"
|
||||||
import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2"
|
import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2"
|
||||||
import {iCallLog, getCallLogsByProjektId, getNewCallLogId, addCallLogData, updateCallLogData} from "../models/call_log"
|
import {iCallLog, getCallLogsByProjektId, getNewCallLogId, addCallLogData, updateCallLogData} from "../models/call_log"
|
||||||
import {iKunde, getKunden} from "../models/kunde"
|
import {iKunde, getKunden} from "../models/kunde"
|
||||||
import {getProjekteByKundenId, getProjektById} from "../models/projekt"
|
import {getProjekteByKundenId, getProjektById} from "../models/projekt"
|
||||||
|
|
||||||
|
// Aktualisierungsintervall für die Dauer-Anzeige in Millisekunden.
|
||||||
|
// 1000 = jede Sekunde, 60000 = jede Minute.
|
||||||
|
const DAUER_UPDATE_INTERVALL = 1000
|
||||||
|
|
||||||
function createCallLogTreenodes(projektId: number | null = null): iTreenode[] {
|
function createCallLogTreenodes(projektId: number | null = null): iTreenode[] {
|
||||||
let filteredLogs = getCallLogsByProjektId(projektId)
|
let filteredLogs = getCallLogsByProjektId(projektId)
|
||||||
|
|
||||||
@@ -174,6 +178,23 @@ export const Call_Log: m.ClosureComponent = (vnode) => {
|
|||||||
// Sind Kunde und Projekt voreingestellt (Liste wurde projektgefiltert
|
// Sind Kunde und Projekt voreingestellt (Liste wurde projektgefiltert
|
||||||
// oder aus der Projektliste aufgerufen)?
|
// oder aus der Projektliste aufgerufen)?
|
||||||
let kundeVoreingestellt: boolean = false
|
let kundeVoreingestellt: boolean = false
|
||||||
|
// Startzeit für die Dauer-Messung (nur bei Neuanlage gesetzt).
|
||||||
|
let startZeit: Date | null = null
|
||||||
|
// Timer-Handle für die Dauer-Aktualisierung.
|
||||||
|
let timerId: number | null = null
|
||||||
|
// Direkte DOM-Referenz auf das Dauer-Feld, damit der Timer den Wert
|
||||||
|
// ohne Redraw aktualisieren kann (ein Redraw würde die übrigen Felder
|
||||||
|
// mit den veralteten currentLog-Werten überschreiben).
|
||||||
|
let dauerInput: HTMLInputElement | null = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatiert verstrichene Sekunden als mm:ss (Minuten:Sekunden).
|
||||||
|
*/
|
||||||
|
const formatDauer = (sekunden: number): string => {
|
||||||
|
const m = Math.floor(sekunden / 60)
|
||||||
|
const s = sekunden % 60
|
||||||
|
return m.toString().padStart(2, '0') + ':' + s.toString().padStart(2, '0')
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
oninit: (vnode) => {
|
oninit: (vnode) => {
|
||||||
@@ -203,8 +224,28 @@ export const Call_Log: m.ClosureComponent = (vnode) => {
|
|||||||
wiedervorlageDatum: routeParams.wiedervorlageDatum || "",
|
wiedervorlageDatum: routeParams.wiedervorlageDatum || "",
|
||||||
erledigt: routeParams.erledigt === true || routeParams.erledigt === "true"
|
erledigt: routeParams.erledigt === true || routeParams.erledigt === "true"
|
||||||
}
|
}
|
||||||
|
// Bei Neuanlage Startzeit merken und Dauer-Timer starten.
|
||||||
|
if (currentLog.id === -1) {
|
||||||
|
startZeit = new Date()
|
||||||
|
timerId = window.setInterval(() => {
|
||||||
|
if (startZeit) {
|
||||||
|
// Verstrichene Sekunden berechnen; das Intervall
|
||||||
|
// steuert nur die Update-Frequenz.
|
||||||
|
const sekunden = Math.floor((Date.now() - startZeit.getTime()) / 1000)
|
||||||
|
const dauer = formatDauer(sekunden)
|
||||||
|
currentLog.dauer = dauer
|
||||||
|
// Nur das Dauer-Feld direkt im DOM aktualisieren,
|
||||||
|
// ohne redraw (sonst werden andere Felder zurückgesetzt).
|
||||||
|
if (dauerInput) { dauerInput.value = dauer }
|
||||||
|
}
|
||||||
|
}, DAUER_UPDATE_INTERVALL)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onremove: (vnode) => {
|
||||||
|
// Timer stoppen, damit er nach Verlassen des Views nicht weiterläuft.
|
||||||
|
if (timerId != null) { window.clearInterval(timerId) }
|
||||||
|
},
|
||||||
view: (vnode) => {
|
view: (vnode) => {
|
||||||
// Select-Optionen aus Kunden- und Projektliste aufbauen.
|
// Select-Optionen aus Kunden- und Projektliste aufbauen.
|
||||||
const kundenOptionen: iSelectOption[] = getKunden().map((k: iKunde) => {
|
const kundenOptionen: iSelectOption[] = getKunden().map((k: iKunde) => {
|
||||||
@@ -214,6 +255,7 @@ export const Call_Log: m.ClosureComponent = (vnode) => {
|
|||||||
const projektOptionen: iSelectOption[] = getProjekteByKundenId(currentLog.kundenId).map((p) => {
|
const projektOptionen: iSelectOption[] = getProjekteByKundenId(currentLog.kundenId).map((p) => {
|
||||||
return {value: p.id, label: p.projektname}
|
return {value: p.id, label: p.projektname}
|
||||||
})
|
})
|
||||||
|
// Einheit der Dauer-Anzeige im Format mm:ss.
|
||||||
return m("div.w3-container.w3-auto",
|
return m("div.w3-container.w3-auto",
|
||||||
m("h1", currentLog.id === -1 ? "Neue Gesprächsnotiz anlegen" : "Gesprächsnotiz"),
|
m("h1", currentLog.id === -1 ? "Neue Gesprächsnotiz anlegen" : "Gesprächsnotiz"),
|
||||||
m(Form, {showButtons: true, onsubmit: (null), oncancel: (null)} as iForm, [
|
m(Form, {showButtons: true, onsubmit: (null), oncancel: (null)} as iForm, [
|
||||||
@@ -242,11 +284,23 @@ export const Call_Log: m.ClosureComponent = (vnode) => {
|
|||||||
} as iSelect)),
|
} as iSelect)),
|
||||||
m("label", "Datum", m(Input, {name: "datum", value: currentLog.datum, type: "date"} as iInput)),
|
m("label", "Datum", m(Input, {name: "datum", value: currentLog.datum, type: "date"} as iInput)),
|
||||||
m("label", "Uhrzeit Beginn", m(Input, {name: "uhrzeitBeginn", value: currentLog.uhrzeitBeginn, type: "time"} as iInput)),
|
m("label", "Uhrzeit Beginn", m(Input, {name: "uhrzeitBeginn", value: currentLog.uhrzeitBeginn, type: "time"} as iInput)),
|
||||||
m("label", "Dauer (Minuten)", m(Input, {name: "dauer", value: currentLog.dauer, type: "number"} as iInput)),
|
m("label", "Dauer (mm:ss)",
|
||||||
|
// Raw input statt Input-Widget, damit oncreate die
|
||||||
|
// DOM-Referenz für den Timer greifen kann. Text-Typ,
|
||||||
|
// da das Format mm:ss keine Zahl ist.
|
||||||
|
m("input.w3-input.w3-border", {
|
||||||
|
name: "dauer",
|
||||||
|
type: "text",
|
||||||
|
value: currentLog.dauer,
|
||||||
|
style: {'margin-bottom': '5px', width: '100%'},
|
||||||
|
oncreate: (vnode: m.VnodeDOM) => {
|
||||||
|
dauerInput = vnode.dom as HTMLInputElement
|
||||||
|
}
|
||||||
|
})),
|
||||||
m("label", "Betreff", m(Input, {name: "betreff", value: currentLog.betreff, type: "text"} as iInput)),
|
m("label", "Betreff", m(Input, {name: "betreff", value: currentLog.betreff, type: "text"} as iInput)),
|
||||||
m("label", "Name Ansprechpartner", m(Input, {name: "nameAnsprechpartner", value: currentLog.nameAnsprechpartner, type: "text"} as iInput)),
|
m("label", "Name Ansprechpartner", m(Input, {name: "nameAnsprechpartner", value: currentLog.nameAnsprechpartner, type: "text"} as iInput)),
|
||||||
m("label", "Telefon", m(Input, {name: "telefon", value: currentLog.telefon, type: "tel"} as iInput)),
|
m("label", "Telefon", m(Input, {name: "telefon", value: currentLog.telefon, type: "tel"} as iInput)),
|
||||||
m("label", "Notiz", m(Input, {name: "notiz", value: currentLog.notiz, type: "text"} as iInput)),
|
m("label", "Notiz", m(Textarea, {name: "notiz", value: currentLog.notiz, rows: 4} as iTextarea)),
|
||||||
m("label", "Wiedervorlage Datum", m(Input, {name: "wiedervorlageDatum", value: currentLog.wiedervorlageDatum, type: "date"} as iInput)),
|
m("label", "Wiedervorlage Datum", m(Input, {name: "wiedervorlageDatum", value: currentLog.wiedervorlageDatum, type: "date"} as iInput)),
|
||||||
m("label", m(Input, {name: "erledigt", type: "checkbox", value: currentLog.erledigt} as iInput), "Erledigt")
|
m("label", m(Input, {name: "erledigt", type: "checkbox", value: currentLog.erledigt} as iInput), "Erledigt")
|
||||||
])
|
])
|
||||||
|
|||||||
Reference in new issue
Block a user