import m from "mithril" import {Form, iForm} from "./widgets/form" 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, getProjektById} from "../models/projekt" function createKontaktadresseTreenodes(projektId: number | null = null): iTreenode[] { let filteredKontaktadressen = getKontaktadressenByProjektId(projektId) return filteredKontaktadressen.map((kontaktadresse: iKontaktadresse) => { return new Treenode(kontaktadresse, true, []) }) } export const kontaktadresseliste: m.ClosureComponent = (vnode) => { let treenodes: iTreenode[] = createKontaktadresseTreenodes() let selectedNode: iTreenode | null = null let filterProjektId: number | null = null const handleRowMarked = (node: iTreenode) => { selectedNode = node } const addKontaktadresseRoute = () => { const params: any = { id: -1, anrede: "", name: "", firma: "", telefon: "", email: "", strasse: "", plz: "", ort: "", land: "", notiz: "" } // Nur bei projektgefilterter Liste das Projekt vorbelegen. if (filterProjektId !== null) { const projekt = getProjekte().find((p: iProjekt) => p.id === filterProjektId) if (projekt) { params.projektId = projekt.id params.projektname = projekt.projektname } } m.route.set("/kontaktadresse", params) } const editKontaktadresse = (kontaktadresse: iKontaktadresse, e: Event) => { e.stopPropagation() m.route.set("/kontaktadresse", { id: kontaktadresse.id, projektId: kontaktadresse.projektId, projektname: kontaktadresse.projektName, anrede: kontaktadresse.anrede, name: kontaktadresse.name, firma: kontaktadresse.firma, telefon: kontaktadresse.telefon, email: kontaktadresse.email, strasse: kontaktadresse.strasse, plz: kontaktadresse.plz, ort: kontaktadresse.ort, land: kontaktadresse.land, notiz: kontaktadresse.notiz }) } 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 return [ m("div", m("strong", kontaktadresse.name)), m("span", kontaktadresse.firma), m("span", kontaktadresse.telefon), m("span", kontaktadresse.email), m("span", kontaktadresse.ort), m("span", kontaktadresse.projektName), 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")) ) ] } const filterFunction = (node: iTreenode, searchvalue: string): boolean => { let kontaktadresse: iKontaktadresse = node.nodevalue let searchLower = searchvalue.toLowerCase() return kontaktadresse.name.toLowerCase().includes(searchLower) || kontaktadresse.firma.toLowerCase().includes(searchLower) || kontaktadresse.telefon.toLowerCase().includes(searchLower) || kontaktadresse.email.toLowerCase().includes(searchLower) || kontaktadresse.ort.toLowerCase().includes(searchLower) || kontaktadresse.projektName.toLowerCase().includes(searchLower) } return { oninit: (vnode) => { const routeParams = m.route.param() if (routeParams && routeParams.projektId) { filterProjektId = parseInt(routeParams.projektId) } else { filterProjektId = null } treenodes = createKontaktadresseTreenodes(filterProjektId) }, view: (vnode) => { return m("div.w3-container.w3-auto", { style: { display: 'flex', 'flex-direction': 'column', height: 'inherit', 'overflow-y': 'inherit'}}, m("h1", "Kontaktadressen"), m("div", {style: {flex: '1', 'overflow-y': 'inherit'}}, m(Treetable, { treenodes: treenodes, columnmapper: columnmapper, filter: filterFunction, onRowMarked: handleRowMarked, isSelected: (node: iTreenode) => node === selectedNode } as iTreetable, m(TopbarButton, {onclick: addKontaktadresseRoute, tooltip: "Neue Kontaktadresse anlegen"} as iTopbarButton, m("i.material-icons", "person_add") ) ) ) ) } } } export const Kontaktadresse: m.ClosureComponent = (vnode) => { // currentKontaktadresse hält den Formularzustand. projektId referenziert das // Projekt eindeutig, projektname dient der Anzeige. let currentKontaktadresse: { id: number projektId: number projektname: string anrede: string name: string firma: string telefon: string email: string strasse: string plz: string ort: string land: string notiz: string } = { id: -1, projektId: -1, projektname: "", anrede: "", name: "", firma: "", telefon: "", email: "", strasse: "", plz: "", ort: "", land: "", notiz: "" } // Ist das Projekt voreingestellt (gefilterte Kontaktadressenliste)? let projektVoreingestellt: boolean = false return { oninit: (vnode) => { const routeParams = m.route.param() if (routeParams) { const projektId = routeParams.projektId ? parseInt(routeParams.projektId) : -1 projektVoreingestellt = projektId !== -1 currentKontaktadresse = { id: routeParams.id ? parseInt(routeParams.id) : -1, projektId: projektId, projektname: routeParams.projektname || "", anrede: routeParams.anrede || "", name: routeParams.name || "", firma: routeParams.firma || "", telefon: routeParams.telefon || "", email: routeParams.email || "", strasse: routeParams.strasse || "", plz: routeParams.plz || "", ort: routeParams.ort || "", land: routeParams.land || "", notiz: routeParams.notiz || "" } } }, view: (vnode) => { const submit = (form: HTMLFormElement): boolean => { const formData = new FormData(form) const projektId = parseInt(formData.get("projektId") as string) // Projektnamen zur gewählten projektId auflösen. const projekt = getProjekte().find((p: iProjekt) => p.id === projektId) const gespeicherteKontaktadresse: iKontaktadresse = { id: currentKontaktadresse.id, projektId: projektId, projektName: projekt ? projekt.projektname : "", anrede: (formData.get("anrede") as string) || "", name: (formData.get("name") as string) || "", firma: (formData.get("firma") as string) || "", telefon: (formData.get("telefon") as string) || "", email: (formData.get("email") as string) || "", strasse: (formData.get("strasse") as string) || "", plz: (formData.get("plz") as string) || "", ort: (formData.get("ort") as string) || "", land: (formData.get("land") as string) || "", notiz: (formData.get("notiz") as string) || "" } if (currentKontaktadresse.id === -1) { gespeicherteKontaktadresse.id = getNewKontaktadresseId() addKontaktadresse(gespeicherteKontaktadresse) } else { updateKontaktadresse(gespeicherteKontaktadresse) } m.route.set("/kontaktadresseliste") return true } const cancel = (e: Event) => { m.route.set("/kontaktadresseliste") } // Select-Optionen aus der Projektliste aufbauen. const projektOptionen: iSelectOption[] = getProjekte().map((p: iProjekt) => { return {value: p.id, label: p.projektname} }) return m("div.w3-container.w3-auto.form-view-container", m("h1", currentKontaktadresse.id === -1 ? "Neue Kontaktadresse anlegen" : "Kontaktadresse-Stammblatt"), m("div", {style: {flex: '1', 'min-height': '0'}}, m(Form, {showButtons: true, onsubmit: submit, oncancel: cancel} as iForm, [ m("label", "Projekt", m(Select, { name: "projektId", value: currentKontaktadresse.projektId, options: projektOptionen, readonly: projektVoreingestellt, required: true } as iSelect)), m("label", "Anrede", m(Input, {name: "anrede", value: currentKontaktadresse.anrede, type: "text"} as iInput)), m("label", "Name", m(Input, {name: "name", value: currentKontaktadresse.name, type: "text", required: true} as iInput)), m("label", "Firma", m(Input, {name: "firma", value: currentKontaktadresse.firma, type: "text"} as iInput)), m("label", "Telefon", m(Input, {name: "telefon", value: currentKontaktadresse.telefon, type: "tel"} as iInput)), m("label", "E-Mail", m(Input, {name: "email", value: currentKontaktadresse.email, type: "email"} as iInput)), m("label", "Straße", m(Input, {name: "strasse", value: currentKontaktadresse.strasse, type: "text"} as iInput)), // PLZ und Ort in einer Zeile. m("div", {style: {display: 'flex', gap: '8px', 'margin-bottom': '5px'}}, m("label", {style: {flex: '0 0 120px'}}, "PLZ", m(Input, {name: "plz", value: currentKontaktadresse.plz, type: "text"} as iInput)), m("label", {style: {flex: '1'}}, "Ort", m(Input, {name: "ort", value: currentKontaktadresse.ort, type: "text"} as iInput)) ), m("label", "Land", m(Input, {name: "land", value: currentKontaktadresse.land, type: "text"} as iInput)), m("label", "Notiz", m(Textarea, {name: "notiz", value: currentKontaktadresse.notiz, rows: 4} as iTextarea)) ]) ) ) } } }