Server: neuer Endpunkt /xhr/workflow/list aggregiert pro Kontaktadresse die letzte Gesprächsnotiz, das effektiv gültige Wiedervorlage-Datum (jüngste Notiz mit Wert), den Erledigt-Status und eine priorisierte Sortierung (überfällig → ohne Notizen → Rest). Filter: kunden_id, status (offen/erledigt), wiedervorlage (heute/3tage/ueberfaellig/woche). Sichtbarkeit wie kontaktadressen über projekt_users. CallLog erfasst den Ersteller serverseitig (user_id, Migration _db_V01_14, kein FK wegen separater Auth-DB); to_dict ergänzt mitarbeiter_name. calllog_list unterstützt kontaktadresse_id als Filter. Client: TagesWorkflowListe (Route /tagesworkflow, Filterleiste) und TagesWorkflowMaske (Route /tagesworkflowkontakt, lesende Historie, Notiz-Formular mit "Speichern und weiter"), Routes + Menüpunkt erster Eintrag. berechneWiedervorlageDatum aus der Call_Log-Maske nach models/tag.ts ausgelagert (gemeinsame Nutzung). Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
144 lines
4.8 KiB
TypeScript
144 lines
4.8 KiB
TypeScript
import m from "mithril"
|
|
|
|
/**
|
|
* Datenmodell für einen Tag (hierarchische Taxonomie).
|
|
* Die Feldnamen entsprechen den serverseitigen ORM-Spalten (snake_case).
|
|
* Tags koennen beliebig verschachtelt werden (parent_id -> tags.id).
|
|
*/
|
|
export interface iTag {
|
|
id: number
|
|
name: string
|
|
description: string
|
|
color: string
|
|
parent_id: number | null
|
|
is_parent: boolean
|
|
/** True = Kontakt gilt als erledigt, wenn dieser Tag zugewiesen wird. */
|
|
is_erledigend: boolean
|
|
/** True = Tag löst eine Wiedervorlage aus. */
|
|
loest_wiedervorlage_aus: boolean
|
|
/** Tage bis zur Wiedervorlage (nur relevant, wenn loest_wiedervorlage_aus True). */
|
|
wiedervorlage_abstand_tage: number | null
|
|
}
|
|
|
|
/**
|
|
* Tag mit verschachtelten Kindern für die Hierarchie-Darstellung.
|
|
* Das children-Array wird serverseitig im Hierarchie-Endpunkt befüllt.
|
|
*/
|
|
export interface iTagWithChildren extends iTag {
|
|
children: iTagWithChildren[]
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* REST-API-Aufrufe (Client-Seite) */
|
|
/* ------------------------------------------------------------------ */
|
|
/* Die folgenden Funktionen kapseln die Kommunikation mit den */
|
|
/* serverseitigen REST-Endpunkten (/xhr/tag/*). */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/**
|
|
* Basis-Pfad der Tag-REST-Endpunkte.
|
|
*/
|
|
const TAG_REST_BASE = "/xhr/tag"
|
|
|
|
/**
|
|
* Lädt alle Tags als flache Liste vom Server.
|
|
* Ist parentId gesetzt, werden nur die direkten Kinder dieses Tags
|
|
* geliefert (serverseitige Filterung).
|
|
*/
|
|
export function fetchTags(parentId?: number): Promise<iTag[]> {
|
|
const params: Record<string, any> = {}
|
|
if (parentId !== undefined) {
|
|
params.parent_id = parentId
|
|
}
|
|
return m.request({
|
|
method: "GET",
|
|
url: TAG_REST_BASE + "/list",
|
|
params: params
|
|
}).then((result) => result as iTag[])
|
|
}
|
|
|
|
/**
|
|
* Lädt die vollständige Tag-Hierarchie als verschachtelte Struktur vom Server.
|
|
* Jeder Tag enthält ein children-Array mit seinen rekursiv eingebetteten Kindern.
|
|
*/
|
|
export function fetchTagHierarchy(): Promise<iTagWithChildren[]> {
|
|
return m.request({
|
|
method: "GET",
|
|
url: TAG_REST_BASE + "/hierarchy"
|
|
}).then((result) => result as iTagWithChildren[])
|
|
}
|
|
|
|
/**
|
|
* Lädt einen einzelnen Tag anhand seiner ID vom Server.
|
|
*/
|
|
export function fetchTagById(id: number): Promise<iTag> {
|
|
return m.request({
|
|
method: "GET",
|
|
url: TAG_REST_BASE + "/get",
|
|
params: {id: id}
|
|
}).then((result) => result as iTag)
|
|
}
|
|
|
|
/**
|
|
* Legt einen neuen Tag serverseitig an. Die ID wird vom Server
|
|
* vergeben und im zurückgegebenen Tag-Objekt gesetzt.
|
|
*/
|
|
export function createTagREST(tag: iTag): Promise<iTag> {
|
|
return m.request({
|
|
method: "POST",
|
|
url: TAG_REST_BASE + "/create",
|
|
body: tag
|
|
}).then((result) => result as iTag)
|
|
}
|
|
|
|
/**
|
|
* Aktualisiert einen bestehenden Tag serverseitig.
|
|
*/
|
|
export function updateTagREST(tag: iTag): Promise<iTag> {
|
|
return m.request({
|
|
method: "PUT",
|
|
url: TAG_REST_BASE + "/update",
|
|
body: tag
|
|
}).then((result) => result as iTag)
|
|
}
|
|
|
|
/**
|
|
* Löscht einen Tag inkl. aller Nachfahren serverseitig anhand seiner ID.
|
|
*/
|
|
export function deleteTagREST(id: number): Promise<void> {
|
|
return m.request({
|
|
method: "DELETE",
|
|
url: TAG_REST_BASE + "/delete",
|
|
params: {id: id}
|
|
}).then(() => undefined)
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Fachliche Hilfsfunktionen */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
/**
|
|
* Berechnet das Wiedervorlage-Datum automatisch aus den ausgewählten
|
|
* Tags. Basis ist das übergebene Basisdatum (z.B. das Datum der Notiz
|
|
* oder heute). Aus allen ausgewählten Tags mit
|
|
* loest_wiedervorlage_aus=True wird das Minimum von
|
|
* wiedervorlage_abstand_tage ermittelt und zum Basisdatum addiert.
|
|
* Liefert einen leeren String, wenn kein Tag mit
|
|
* loest_wiedervorlage_aus=True ausgewählt ist.
|
|
*
|
|
* Wird von der Call_Log-Maske und der Tages-Workflow-Maske genutzt,
|
|
* damit die Berechnungslogik nur an einer Stelle gepflegt wird.
|
|
*/
|
|
export function berechneWiedervorlageDatum(tagIds: number[],
|
|
tagListe: iTag[],
|
|
basisdatum: string): string {
|
|
const wvTags = tagListe.filter((t: iTag) =>
|
|
tagIds.includes(t.id) && t.loest_wiedervorlage_aus && t.wiedervorlage_abstand_tage != null
|
|
)
|
|
if (wvTags.length === 0) return ""
|
|
const minAbstand = Math.min(...wvTags.map(t => t.wiedervorlage_abstand_tage as number))
|
|
const basisDate = new Date(basisdatum)
|
|
basisDate.setDate(basisDate.getDate() + minAbstand)
|
|
return basisDate.toISOString().split('T')[0]
|
|
}
|