feat(nav): Breadcrumb-Navigation für Hierarchie-Aufrufkette

Neue BreadcrumbBar-Widget-Komponente (breadcrumb.ts) zeigt die
Aufrufkette als anklickbare Leiste. Der Route-Param bc (JSON-String)
transportiert die Vorgänger-Ansichten durch alle Listen und Masken.
Jede Ansicht hängt beim Weiternavigieren ihren eigenen Crumb an die
Kette an; Masken reichen bc beim Speichern/Abbrechen zurück.
Erfasst: kunde, projekt, kontaktadresse, call_log, kontakt.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
gg committed 2026-09-19 16:16:49 +02:00
1 parent f1e5938ae1
commit fd30049a32
6 files changed
+316 -27

No files matched your search

+33
View File
@@ -3,6 +3,7 @@ import {Form, iForm} from "./widgets/form"
import { Input, iInput, Textarea, iTextarea} from "./widgets/inputs"
import {Select, iSelect, MultiSelect, iMultiSelect, iSelectOption} from "./widgets/select"
import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2"
import {BreadcrumbBar, iBreadcrumb, iBreadcrumbBar, parseBc, serializeBc} from "./widgets/breadcrumb"
import {iCallLog, fetchCallLogs, fetchCallLogById, createCallLogREST, updateCallLogREST} from "../models/call_log"
import {iKunde, fetchKunden} from "../models/kunde"
import {iProjekt, fetchProjekte, fetchProjektById} from "../models/projekt"
@@ -22,11 +23,20 @@ export const call_log_liste: m.ClosureComponent = (vnode) => {
// da es nicht in den Route-Params enthalten ist.
let projektName: string = ""
let kundenName: string = ""
// Breadcrumb-Kette der Vorgänger-Ansichten (aus Route-Param bc).
let breadcrumbs: iBreadcrumb[] = []
const handleRowMarked = (node: iTreenode) => {
selectedNode = node
}
// Eigener Listen-Crumb für die Breadcrumb-Kette.
const ownCrumb = (): iBreadcrumb => ({
route: "/calllogliste",
params: filterProjektId !== null ? {projekt_id: filterProjektId} : {},
label: filterProjektId !== null ? "Notizen: " + projektName : "Gesprächsnotizen"
})
const addCallLogRoute = () => {
const now = new Date()
const datum = now.toISOString().split('T')[0]
@@ -51,6 +61,8 @@ export const call_log_liste: m.ClosureComponent = (vnode) => {
params.return_projekt_id = filterProjektId
params.return_projekt_name = projektName
}
// Breadcrumb-Kette um eigene Position erweitern.
params.bc = serializeBc([...breadcrumbs, ownCrumb()])
m.route.set("/call_log", params)
}
@@ -61,6 +73,8 @@ export const call_log_liste: m.ClosureComponent = (vnode) => {
params.return_projekt_id = filterProjektId
params.return_projekt_name = projektName
}
// Breadcrumb-Kette um eigene Position erweitern.
params.bc = serializeBc([...breadcrumbs, ownCrumb()])
m.route.set("/call_log", params)
}
@@ -118,6 +132,8 @@ export const call_log_liste: m.ClosureComponent = (vnode) => {
filterProjektId = null
projektName = ""
}
// Breadcrumb-Kette aus Route-Param parsen.
breadcrumbs = parseBc(routeParams && routeParams.bc)
// Gesprächsnotizen asynchron vom Server laden. Die Filterung
// nach projekt_id erfolgt serverseitig, sofern ein Filter
// gesetzt ist.
@@ -143,6 +159,10 @@ export const call_log_liste: m.ClosureComponent = (vnode) => {
height: 'inherit',
'overflow-y': 'inherit'}},
m("h1", projektName ? "Gesprächsnotizen für " + projektName : "Gesprächsnotizen"),
m(BreadcrumbBar, {
breadcrumbs: breadcrumbs,
current: filterProjektId !== null ? "Notizen: " + projektName : "Gesprächsnotizen"
} as iBreadcrumbBar),
m("div", {style: {flex: '1', 'overflow-y': 'inherit'}},
m(Treetable,
{
@@ -219,6 +239,8 @@ export const Call_Log: m.ClosureComponent = (vnode) => {
// null bedeutet ungefiltert; ein Wert bedeutet projektgefiltert.
let returnProjektId: number | null = null
let returnProjektName: string = ""
// Breadcrumb-Kette der Vorgänger-Ansichten (aus Route-Param bc).
let breadcrumbs: iBreadcrumb[] = []
/**
* Formatiert verstrichene Sekunden als mm:ss.
@@ -240,6 +262,8 @@ export const Call_Log: m.ClosureComponent = (vnode) => {
returnProjektId = parseInt(routeParams.return_projekt_id)
returnProjektName = routeParams.return_projekt_name || ""
}
// Breadcrumb-Kette aus Route-Param parsen.
breadcrumbs = parseBc(routeParams && routeParams.bc)
if (id !== -1) {
// Gesprächsnotiz frisch vom Server holen.
fetchCallLogById(id).then((result) => {
@@ -334,6 +358,11 @@ export const Call_Log: m.ClosureComponent = (vnode) => {
params.projekt_id = currentLog.projekt_id
params.projekt_name = currentLog.projekt_name
}
// Breadcrumb-Kette ohne das letzte Glied (die Notizliste
// selbst) an die Liste durchreichen, damit diese die
// Vorgänger-Ansichten wieder als Leiste anzeigen kann.
const listBc = serializeBc(breadcrumbs.slice(0, -1))
if (listBc) params.bc = listBc
m.route.set("/calllogliste", params)
}
@@ -367,6 +396,10 @@ export const Call_Log: m.ClosureComponent = (vnode) => {
}
return m("div.w3-container.w3-auto.form-view-container",
m("h1", currentLog.id === -1 ? "Neue Gesprächsnotiz anlegen" : "Gesprächsnotiz"),
m(BreadcrumbBar, {
breadcrumbs: breadcrumbs,
current: currentLog.id === -1 ? "Neue Gesprächsnotiz" : "Gesprächsnotiz"
} as iBreadcrumbBar),
m("div", {style: {flex: '1', 'min-height': '0'}},
m(Form, {showButtons: true, onsubmit: submit, oncancel: cancel} as iForm, [
m("input", {type: "hidden", name: "projekt_id", value: currentLog.projekt_id}),