Serverseitige Filterung für Listen-Endpunkte und Einzelabrufe in Listen-Komponenten
Server: Die list-Endpunkte für Kontakt, Projekt, Kontaktadresse und CallLog akzeptieren nun einen optionalen Query-Parameter (kunden_id bzw. projekt_id) und filtern im ORM-Query. Neue Migration V01.05 legt Indizes auf den FK-Spalten an. Client: Die fetch-Funktionen erhalten einen optionalen Parent-ID-Parameter. Die Listen-Komponenten übergeben die Parent-ID aus den Route-Params an den Server und laden nur noch benötigte Daten. Statt kompletter Eltern-Listen wird per fetchXxxById ein einzelner Datensatz für die Namensauflösung geladen. Die CallLog-Bearbeitungsmaske behält ihre ungesfilterten Dropdown-Abrufe. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
1 parent
e9a2f48ede
commit
93827dde0f
15 files changed
+179
-131
No files matched your search
@@ -12,7 +12,7 @@ erfolgt ausschließlich über __bind_key__.
|
|||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
|
|
||||||
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String
|
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Index, Integer, String
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
from db import dbapp
|
from db import dbapp
|
||||||
@@ -103,6 +103,9 @@ class Kontakt(dbapp.Model):
|
|||||||
"""
|
"""
|
||||||
__tablename__ = 'kontakte'
|
__tablename__ = 'kontakte'
|
||||||
__bind_key__ = BINDKEY
|
__bind_key__ = BINDKEY
|
||||||
|
__table_args__ = (
|
||||||
|
Index('ix_kontakte_kunden_id', 'kunden_id'),
|
||||||
|
)
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
kunden_id = Column(Integer, ForeignKey('kunden.id'), nullable=False)
|
kunden_id = Column(Integer, ForeignKey('kunden.id'), nullable=False)
|
||||||
anrede = Column(String(30), nullable=True)
|
anrede = Column(String(30), nullable=True)
|
||||||
@@ -134,6 +137,9 @@ class Projekt(dbapp.Model):
|
|||||||
"""
|
"""
|
||||||
__tablename__ = 'projekte'
|
__tablename__ = 'projekte'
|
||||||
__bind_key__ = BINDKEY
|
__bind_key__ = BINDKEY
|
||||||
|
__table_args__ = (
|
||||||
|
Index('ix_projekte_kunden_id', 'kunden_id'),
|
||||||
|
)
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
kunden_id = Column(Integer, ForeignKey('kunden.id'), nullable=False)
|
kunden_id = Column(Integer, ForeignKey('kunden.id'), nullable=False)
|
||||||
projektname = Column(String(200), nullable=False)
|
projektname = Column(String(200), nullable=False)
|
||||||
@@ -163,6 +169,9 @@ class Kontaktadresse(dbapp.Model):
|
|||||||
"""
|
"""
|
||||||
__tablename__ = 'kontaktadressen'
|
__tablename__ = 'kontaktadressen'
|
||||||
__bind_key__ = BINDKEY
|
__bind_key__ = BINDKEY
|
||||||
|
__table_args__ = (
|
||||||
|
Index('ix_kontaktadressen_projekt_id', 'projekt_id'),
|
||||||
|
)
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
projekt_id = Column(Integer, ForeignKey('projekte.id'), nullable=False)
|
projekt_id = Column(Integer, ForeignKey('projekte.id'), nullable=False)
|
||||||
anrede = Column(String(30), nullable=True)
|
anrede = Column(String(30), nullable=True)
|
||||||
@@ -200,6 +209,9 @@ class CallLog(dbapp.Model):
|
|||||||
"""
|
"""
|
||||||
__tablename__ = 'call_logs'
|
__tablename__ = 'call_logs'
|
||||||
__bind_key__ = BINDKEY
|
__bind_key__ = BINDKEY
|
||||||
|
__table_args__ = (
|
||||||
|
Index('ix_call_logs_projekt_id', 'projekt_id'),
|
||||||
|
)
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
projekt_id = Column(Integer, ForeignKey('projekte.id'), nullable=False)
|
projekt_id = Column(Integer, ForeignKey('projekte.id'), nullable=False)
|
||||||
datum = Column(String(20), nullable=True)
|
datum = Column(String(20), nullable=True)
|
||||||
|
|||||||
@@ -17,11 +17,18 @@ from auth.model.errors import ApplicationError, HtmlStatusCode
|
|||||||
session = dbapp.session
|
session = dbapp.session
|
||||||
|
|
||||||
|
|
||||||
def get_call_logs():
|
def get_call_logs(projekt_id: int | None = None):
|
||||||
"""
|
"""
|
||||||
Liefert alle Gesprächsnotizen, sortiert nach Datum absteigend.
|
Liefert Gesprächsnotizen, sortiert nach Datum absteigend.
|
||||||
|
|
||||||
|
Ist projekt_id gesetzt, werden nur die Notizen dieses Projekts
|
||||||
|
zurückgegeben (serverseitige Filterung). Ohne projekt_id werden
|
||||||
|
alle Notizen geliefert.
|
||||||
"""
|
"""
|
||||||
return CallLog.query.order_by(CallLog.datum.desc()).all()
|
query = CallLog.query
|
||||||
|
if projekt_id is not None:
|
||||||
|
query = query.filter(CallLog.projekt_id == projekt_id)
|
||||||
|
return query.order_by(CallLog.datum.desc()).all()
|
||||||
|
|
||||||
|
|
||||||
def get_call_log(call_log_id: int) -> CallLog:
|
def get_call_log(call_log_id: int) -> CallLog:
|
||||||
|
|||||||
@@ -91,6 +91,23 @@ def _db_V01_04():
|
|||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def _db_V01_05():
|
||||||
|
"""
|
||||||
|
Legt Indizes auf den Fremdschlüsselspalten an, die für die
|
||||||
|
serverseitige Filterung der Listen-Endpunkte genutzt werden.
|
||||||
|
CREATE INDEX IF NOT EXISTS ist idempotent und überspringet bereits
|
||||||
|
vorhandene Indizes.
|
||||||
|
"""
|
||||||
|
from sqlalchemy import text
|
||||||
|
|
||||||
|
session.execute(text("CREATE INDEX IF NOT EXISTS ix_kontakte_kunden_id ON kontakte (kunden_id)"))
|
||||||
|
session.execute(text("CREATE INDEX IF NOT EXISTS ix_projekte_kunden_id ON projekte (kunden_id)"))
|
||||||
|
session.execute(text("CREATE INDEX IF NOT EXISTS ix_kontaktadressen_projekt_id ON kontaktadressen (projekt_id)"))
|
||||||
|
session.execute(text("CREATE INDEX IF NOT EXISTS ix_call_logs_projekt_id ON call_logs (projekt_id)"))
|
||||||
|
_updatesysteminfo('01.05')
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
def checkupdates():
|
def checkupdates():
|
||||||
"""
|
"""
|
||||||
Ermittelt die aktuelle DB-Version aus der Systeminfo-Tabelle und führt alle
|
Ermittelt die aktuelle DB-Version aus der Systeminfo-Tabelle und führt alle
|
||||||
|
|||||||
@@ -17,11 +17,18 @@ from auth.model.errors import ApplicationError, HtmlStatusCode
|
|||||||
session = dbapp.session
|
session = dbapp.session
|
||||||
|
|
||||||
|
|
||||||
def get_kontakte():
|
def get_kontakte(kunden_id: int | None = None):
|
||||||
"""
|
"""
|
||||||
Liefert alle Kontakte, sortiert nach Name.
|
Liefert Kontakte, sortiert nach Name.
|
||||||
|
|
||||||
|
Ist kunden_id gesetzt, werden nur die Kontakte dieses Kunden
|
||||||
|
zurückgegeben (serverseitige Filterung). Ohne kunden_id werden
|
||||||
|
alle Kontakte geliefert.
|
||||||
"""
|
"""
|
||||||
return Kontakt.query.order_by(Kontakt.name).all()
|
query = Kontakt.query
|
||||||
|
if kunden_id is not None:
|
||||||
|
query = query.filter(Kontakt.kunden_id == kunden_id)
|
||||||
|
return query.order_by(Kontakt.name).all()
|
||||||
|
|
||||||
|
|
||||||
def get_kontakt(kontakt_id: int) -> Kontakt:
|
def get_kontakt(kontakt_id: int) -> Kontakt:
|
||||||
|
|||||||
@@ -17,11 +17,18 @@ from auth.model.errors import ApplicationError, HtmlStatusCode
|
|||||||
session = dbapp.session
|
session = dbapp.session
|
||||||
|
|
||||||
|
|
||||||
def get_kontaktadressen():
|
def get_kontaktadressen(projekt_id: int | None = None):
|
||||||
"""
|
"""
|
||||||
Liefert alle Kontaktadressen, sortiert nach Name.
|
Liefert Kontaktadressen, sortiert nach Name.
|
||||||
|
|
||||||
|
Ist projekt_id gesetzt, werden nur die Kontaktadressen dieses
|
||||||
|
Projekts zurückgegeben (serverseitige Filterung). Ohne projekt_id
|
||||||
|
werden alle Kontaktadressen geliefert.
|
||||||
"""
|
"""
|
||||||
return Kontaktadresse.query.order_by(Kontaktadresse.name).all()
|
query = Kontaktadresse.query
|
||||||
|
if projekt_id is not None:
|
||||||
|
query = query.filter(Kontaktadresse.projekt_id == projekt_id)
|
||||||
|
return query.order_by(Kontaktadresse.name).all()
|
||||||
|
|
||||||
|
|
||||||
def get_kontaktadresse(kontaktadresse_id: int) -> Kontaktadresse:
|
def get_kontaktadresse(kontaktadresse_id: int) -> Kontaktadresse:
|
||||||
|
|||||||
@@ -17,11 +17,18 @@ from auth.model.errors import ApplicationError, HtmlStatusCode
|
|||||||
session = dbapp.session
|
session = dbapp.session
|
||||||
|
|
||||||
|
|
||||||
def get_projekte():
|
def get_projekte(kunden_id: int | None = None):
|
||||||
"""
|
"""
|
||||||
Liefert alle Projekte, sortiert nach projektname.
|
Liefert Projekte, sortiert nach projektname.
|
||||||
|
|
||||||
|
Ist kunden_id gesetzt, werden nur die Projekte dieses Kunden
|
||||||
|
zurückgegeben (serverseitige Filterung). Ohne kunden_id werden
|
||||||
|
alle Projekte geliefert.
|
||||||
"""
|
"""
|
||||||
return Projekt.query.order_by(Projekt.projektname).all()
|
query = Projekt.query
|
||||||
|
if kunden_id is not None:
|
||||||
|
query = query.filter(Projekt.kunden_id == kunden_id)
|
||||||
|
return query.order_by(Projekt.projektname).all()
|
||||||
|
|
||||||
|
|
||||||
def get_projekt(projekt_id: int) -> Projekt:
|
def get_projekt(projekt_id: int) -> Projekt:
|
||||||
|
|||||||
@@ -80,8 +80,9 @@ def kunde_delete():
|
|||||||
@app.get("/xhr/kontakt/list")
|
@app.get("/xhr/kontakt/list")
|
||||||
@authorisation_required([MITARBEITER, KUNDEN_MANAGER])
|
@authorisation_required([MITARBEITER, KUNDEN_MANAGER])
|
||||||
def kontakt_list():
|
def kontakt_list():
|
||||||
"""Liefert alle Kontakte als Liste."""
|
"""Liefert Kontakte als Liste. Optional nach kunden_id gefiltert."""
|
||||||
result = [k.to_dict() for k in kontakte.get_kontakte()]
|
kunden_id = request.args.get("kunden_id", type=int)
|
||||||
|
result = [k.to_dict() for k in kontakte.get_kontakte(kunden_id)]
|
||||||
return jsonify(result), 200
|
return jsonify(result), 200
|
||||||
|
|
||||||
|
|
||||||
@@ -125,8 +126,9 @@ def kontakt_delete():
|
|||||||
@app.get("/xhr/projekt/list")
|
@app.get("/xhr/projekt/list")
|
||||||
@authorisation_required([MITARBEITER, KUNDEN_MANAGER])
|
@authorisation_required([MITARBEITER, KUNDEN_MANAGER])
|
||||||
def projekt_list():
|
def projekt_list():
|
||||||
"""Liefert alle Projekte als Liste."""
|
"""Liefert Projekte als Liste. Optional nach kunden_id gefiltert."""
|
||||||
result = [p.to_dict() for p in projekte.get_projekte()]
|
kunden_id = request.args.get("kunden_id", type=int)
|
||||||
|
result = [p.to_dict() for p in projekte.get_projekte(kunden_id)]
|
||||||
return jsonify(result), 200
|
return jsonify(result), 200
|
||||||
|
|
||||||
|
|
||||||
@@ -170,8 +172,9 @@ def projekt_delete():
|
|||||||
@app.get("/xhr/kontaktadresse/list")
|
@app.get("/xhr/kontaktadresse/list")
|
||||||
@authorisation_required([MITARBEITER, KUNDEN_MANAGER])
|
@authorisation_required([MITARBEITER, KUNDEN_MANAGER])
|
||||||
def kontaktadresse_list():
|
def kontaktadresse_list():
|
||||||
"""Liefert alle Kontaktadressen als Liste."""
|
"""Liefert Kontaktadressen als Liste. Optional nach projekt_id gefiltert."""
|
||||||
result = [k.to_dict() for k in kontaktadressen.get_kontaktadressen()]
|
projekt_id = request.args.get("projekt_id", type=int)
|
||||||
|
result = [k.to_dict() for k in kontaktadressen.get_kontaktadressen(projekt_id)]
|
||||||
return jsonify(result), 200
|
return jsonify(result), 200
|
||||||
|
|
||||||
|
|
||||||
@@ -215,8 +218,9 @@ def kontaktadresse_delete():
|
|||||||
@app.get("/xhr/calllog/list")
|
@app.get("/xhr/calllog/list")
|
||||||
@authorisation_required([MITARBEITER, KUNDEN_MANAGER])
|
@authorisation_required([MITARBEITER, KUNDEN_MANAGER])
|
||||||
def calllog_list():
|
def calllog_list():
|
||||||
"""Liefert alle Gesprächsnotizen als Liste."""
|
"""Liefert Gesprächsnotizen als Liste. Optional nach projekt_id gefiltert."""
|
||||||
result = [l.to_dict() for l in call_logs.get_call_logs()]
|
projekt_id = request.args.get("projekt_id", type=int)
|
||||||
|
result = [l.to_dict() for l in call_logs.get_call_logs(projekt_id)]
|
||||||
return jsonify(result), 200
|
return jsonify(result), 200
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ 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, fetchCallLogs, fetchCallLogById, createCallLogREST, updateCallLogREST} from "../models/call_log"
|
import {iCallLog, fetchCallLogs, fetchCallLogById, createCallLogREST, updateCallLogREST} from "../models/call_log"
|
||||||
import {iKunde, fetchKunden} from "../models/kunde"
|
import {iKunde, fetchKunden} from "../models/kunde"
|
||||||
import {iProjekt, fetchProjekte} from "../models/projekt"
|
import {iProjekt, fetchProjekte, fetchProjektById} from "../models/projekt"
|
||||||
import {iKontaktadresse, fetchKontaktadressen} from "../models/kontaktadresse"
|
import {iKontaktadresse, fetchKontaktadressen} from "../models/kontaktadresse"
|
||||||
|
|
||||||
// Aktualisierungsintervall für die Dauer-Anzeige in Millisekunden.
|
// Aktualisierungsintervall für die Dauer-Anzeige in Millisekunden.
|
||||||
@@ -15,10 +15,11 @@ export const call_log_liste: m.ClosureComponent = (vnode) => {
|
|||||||
let treenodes: iTreenode[] = []
|
let treenodes: iTreenode[] = []
|
||||||
let selectedNode: iTreenode | null = null
|
let selectedNode: iTreenode | null = null
|
||||||
let filterProjektId: number | null = null
|
let filterProjektId: number | null = null
|
||||||
// Projektname für die Überschrift, asynchron aufgelöst.
|
// Projektname für die Überschrift, asynchron per fetchProjektById
|
||||||
|
// aufgelöst. kunden_name wird ebenfalls aus dem Projekt bezogen,
|
||||||
|
// da es nicht in den Route-Params enthalten ist.
|
||||||
let projektName: string = ""
|
let projektName: string = ""
|
||||||
// Projektliste asynchron laden, für Projektauflösung beim Neuanlegen.
|
let kundenName: string = ""
|
||||||
let projektListe: iProjekt[] = []
|
|
||||||
|
|
||||||
const handleRowMarked = (node: iTreenode) => {
|
const handleRowMarked = (node: iTreenode) => {
|
||||||
selectedNode = node
|
selectedNode = node
|
||||||
@@ -41,12 +42,9 @@ export const call_log_liste: m.ClosureComponent = (vnode) => {
|
|||||||
erledigt: false
|
erledigt: false
|
||||||
}
|
}
|
||||||
if (filterProjektId !== null) {
|
if (filterProjektId !== null) {
|
||||||
const projekt = projektListe.find((p: iProjekt) => p.id === filterProjektId)
|
params.projekt_id = filterProjektId
|
||||||
if (projekt) {
|
params.projekt_name = projektName
|
||||||
params.projekt_id = projekt.id
|
params.kunden_name = kundenName
|
||||||
params.projekt_name = projekt.projektname
|
|
||||||
params.kunden_name = projekt.kunden_name
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
m.route.set("/call_log", params)
|
m.route.set("/call_log", params)
|
||||||
}
|
}
|
||||||
@@ -94,24 +92,22 @@ export const call_log_liste: m.ClosureComponent = (vnode) => {
|
|||||||
filterProjektId = null
|
filterProjektId = null
|
||||||
projektName = ""
|
projektName = ""
|
||||||
}
|
}
|
||||||
// Gesprächsnotizen asynchron vom Server laden.
|
// Gesprächsnotizen asynchron vom Server laden. Die Filterung
|
||||||
fetchCallLogs().then((result) => {
|
// nach projekt_id erfolgt serverseitig, sofern ein Filter
|
||||||
let filtered = result
|
// gesetzt ist.
|
||||||
if (filterProjektId !== null) {
|
fetchCallLogs(filterProjektId !== null ? filterProjektId : undefined).then((result) => {
|
||||||
filtered = result.filter((l: iCallLog) => l.projekt_id === filterProjektId)
|
treenodes = result.map((log: iCallLog) => new Treenode(log, true, []))
|
||||||
}
|
|
||||||
treenodes = filtered.map((log: iCallLog) => new Treenode(log, true, []))
|
|
||||||
})
|
})
|
||||||
// Projektliste asynchron laden, für Projektnamen-Auflösung.
|
// Projektnamen und Kundennamen asynchron auflösen, falls
|
||||||
fetchProjekte().then((result) => {
|
// nicht in den Route-Params enthalten.
|
||||||
projektListe = result
|
if (filterProjektId !== null) {
|
||||||
if (filterProjektId !== null && projektName === "") {
|
fetchProjektById(filterProjektId).then((projekt: iProjekt) => {
|
||||||
const projekt = projektListe.find((p: iProjekt) => p.id === filterProjektId)
|
if (projektName === "") {
|
||||||
if (projekt) {
|
|
||||||
projektName = projekt.projektname
|
projektName = projekt.projektname
|
||||||
}
|
}
|
||||||
}
|
kundenName = projekt.kunden_name
|
||||||
})
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
view: (vnode) => {
|
view: (vnode) => {
|
||||||
return m("div.w3-container.w3-auto", {
|
return m("div.w3-container.w3-auto", {
|
||||||
|
|||||||
@@ -3,18 +3,15 @@ import {Form, iForm} from "./widgets/form"
|
|||||||
import { Input, iInput, Textarea, iTextarea} from "./widgets/inputs"
|
import { Input, iInput, Textarea, iTextarea} from "./widgets/inputs"
|
||||||
import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2"
|
import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2"
|
||||||
import {iKontakt, fetchKontakte, fetchKontaktById, createKontaktREST, updateKontaktREST} from "../models/kontakt"
|
import {iKontakt, fetchKontakte, fetchKontaktById, createKontaktREST, updateKontaktREST} from "../models/kontakt"
|
||||||
import {iKunde, fetchKunden} from "../models/kunde"
|
import {iKunde, fetchKundeById} from "../models/kunde"
|
||||||
|
|
||||||
export const kontaktliste: m.ClosureComponent = (vnode) => {
|
export const kontaktliste: m.ClosureComponent = (vnode) => {
|
||||||
let treenodes: iTreenode[] = []
|
let treenodes: iTreenode[] = []
|
||||||
let selectedNode: iTreenode | null = null
|
let selectedNode: iTreenode | null = null
|
||||||
let filterKundenId: number | null = null
|
let filterKundenId: number | null = null
|
||||||
// Kundenname für die Überschrift, asynchron aus der Kundenliste
|
// Kundenname für die Überschrift, asynchron per fetchKundeById
|
||||||
// aufgelöst.
|
// aufgelöst, falls nicht in den Route-Params enthalten.
|
||||||
let kundenName: string = ""
|
let kundenName: string = ""
|
||||||
// Kundenliste asynchron vom Server laden, für die Kundenauflösung
|
|
||||||
// beim Anlegen eines neuen Kontakts.
|
|
||||||
let kundenListe: iKunde[] = []
|
|
||||||
|
|
||||||
const handleRowMarked = (node: iTreenode) => {
|
const handleRowMarked = (node: iTreenode) => {
|
||||||
selectedNode = node
|
selectedNode = node
|
||||||
@@ -24,11 +21,8 @@ export const kontaktliste: m.ClosureComponent = (vnode) => {
|
|||||||
const params: any = {id: -1}
|
const params: any = {id: -1}
|
||||||
// Nur bei kundengefilterter Liste den Kunden vorbelegen.
|
// Nur bei kundengefilterter Liste den Kunden vorbelegen.
|
||||||
if (filterKundenId !== null) {
|
if (filterKundenId !== null) {
|
||||||
const kunde = kundenListe.find((k: iKunde) => k.id === filterKundenId)
|
params.kunden_id = filterKundenId
|
||||||
if (kunde) {
|
params.kunden_name = kundenName
|
||||||
params.kunden_id = kunde.id
|
|
||||||
params.kunden_name = kunde.name
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
m.route.set("/kontakt", params)
|
m.route.set("/kontakt", params)
|
||||||
}
|
}
|
||||||
@@ -71,27 +65,17 @@ export const kontaktliste: m.ClosureComponent = (vnode) => {
|
|||||||
filterKundenId = null
|
filterKundenId = null
|
||||||
kundenName = ""
|
kundenName = ""
|
||||||
}
|
}
|
||||||
// Kontakte asynchron vom Server laden. m.request löst nach
|
// Kontakte asynchron vom Server laden. Die Filterung nach
|
||||||
// Promise-Auflösung automatisch ein Redraw aus.
|
// kunden_id erfolgt serverseitig, sofern ein Filter gesetzt ist.
|
||||||
fetchKontakte().then((result) => {
|
fetchKontakte(filterKundenId !== null ? filterKundenId : undefined).then((result) => {
|
||||||
let filtered = result
|
treenodes = result.map((kontakt: iKontakt) => new Treenode(kontakt, true, []))
|
||||||
if (filterKundenId !== null) {
|
|
||||||
filtered = result.filter((k: iKontakt) => k.kunden_id === filterKundenId)
|
|
||||||
}
|
|
||||||
treenodes = filtered.map((kontakt: iKontakt) => new Treenode(kontakt, true, []))
|
|
||||||
})
|
})
|
||||||
// Kundenliste asynchron vom Server laden.
|
// Kundennamen asynchron auflösen, falls nicht in Route-Params.
|
||||||
fetchKunden().then((result) => {
|
|
||||||
kundenListe = result
|
|
||||||
// Fallback: Falls kunden_name nicht in den Route-Params
|
|
||||||
// enthalten war, aus der Kundenliste auflösen.
|
|
||||||
if (filterKundenId !== null && kundenName === "") {
|
if (filterKundenId !== null && kundenName === "") {
|
||||||
const kunde = kundenListe.find((k: iKunde) => k.id === filterKundenId)
|
fetchKundeById(filterKundenId).then((kunde: iKunde) => {
|
||||||
if (kunde) {
|
|
||||||
kundenName = kunde.name
|
kundenName = kunde.name
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
view: (vnode) => {
|
view: (vnode) => {
|
||||||
return m("div.w3-container.w3-auto", {
|
return m("div.w3-container.w3-auto", {
|
||||||
|
|||||||
@@ -3,18 +3,15 @@ import {Form, iForm} from "./widgets/form"
|
|||||||
import { Input, iInput, Textarea, iTextarea} from "./widgets/inputs"
|
import { Input, iInput, Textarea, iTextarea} from "./widgets/inputs"
|
||||||
import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2"
|
import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2"
|
||||||
import {iKontaktadresse, fetchKontaktadressen, fetchKontaktadresseById, createKontaktadresseREST, updateKontaktadresseREST} from "../models/kontaktadresse"
|
import {iKontaktadresse, fetchKontaktadressen, fetchKontaktadresseById, createKontaktadresseREST, updateKontaktadresseREST} from "../models/kontaktadresse"
|
||||||
import {iProjekt, fetchProjekte} from "../models/projekt"
|
import {iProjekt, fetchProjektById} from "../models/projekt"
|
||||||
|
|
||||||
export const kontaktadresseliste: m.ClosureComponent = (vnode) => {
|
export const kontaktadresseliste: m.ClosureComponent = (vnode) => {
|
||||||
let treenodes: iTreenode[] = []
|
let treenodes: iTreenode[] = []
|
||||||
let selectedNode: iTreenode | null = null
|
let selectedNode: iTreenode | null = null
|
||||||
let filterProjektId: number | null = null
|
let filterProjektId: number | null = null
|
||||||
// Projektname für die Überschrift, asynchron aus der Projektliste
|
// Projektname für die Überschrift, asynchron per fetchProjektById
|
||||||
// aufgelöst.
|
// aufgelöst, falls nicht in den Route-Params enthalten.
|
||||||
let projektName: string = ""
|
let projektName: string = ""
|
||||||
// Projektliste asynchron vom Server laden, für die Projektauflösung
|
|
||||||
// beim Anlegen einer neuen Kontaktadresse.
|
|
||||||
let projektListe: iProjekt[] = []
|
|
||||||
|
|
||||||
const handleRowMarked = (node: iTreenode) => {
|
const handleRowMarked = (node: iTreenode) => {
|
||||||
selectedNode = node
|
selectedNode = node
|
||||||
@@ -23,11 +20,8 @@ export const kontaktadresseliste: m.ClosureComponent = (vnode) => {
|
|||||||
const addKontaktadresseRoute = () => {
|
const addKontaktadresseRoute = () => {
|
||||||
const params: any = {id: -1}
|
const params: any = {id: -1}
|
||||||
if (filterProjektId !== null) {
|
if (filterProjektId !== null) {
|
||||||
const projekt = projektListe.find((p: iProjekt) => p.id === filterProjektId)
|
params.projekt_id = filterProjektId
|
||||||
if (projekt) {
|
params.projekt_name = projektName
|
||||||
params.projekt_id = projekt.id
|
|
||||||
params.projekt_name = projekt.projektname
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
m.route.set("/kontaktadresse", params)
|
m.route.set("/kontaktadresse", params)
|
||||||
}
|
}
|
||||||
@@ -97,24 +91,18 @@ export const kontaktadresseliste: m.ClosureComponent = (vnode) => {
|
|||||||
filterProjektId = null
|
filterProjektId = null
|
||||||
projektName = ""
|
projektName = ""
|
||||||
}
|
}
|
||||||
// Kontaktadressen asynchron vom Server laden.
|
// Kontaktadressen asynchron vom Server laden. Die Filterung
|
||||||
fetchKontaktadressen().then((result) => {
|
// nach projekt_id erfolgt serverseitig, sofern ein Filter
|
||||||
let filtered = result
|
// gesetzt ist.
|
||||||
if (filterProjektId !== null) {
|
fetchKontaktadressen(filterProjektId !== null ? filterProjektId : undefined).then((result) => {
|
||||||
filtered = result.filter((k: iKontaktadresse) => k.projekt_id === filterProjektId)
|
treenodes = result.map((kontaktadresse: iKontaktadresse) => new Treenode(kontaktadresse, true, []))
|
||||||
}
|
|
||||||
treenodes = filtered.map((kontaktadresse: iKontaktadresse) => new Treenode(kontaktadresse, true, []))
|
|
||||||
})
|
})
|
||||||
// Projektliste asynchron vom Server laden.
|
// Projektnamen asynchron auflösen, falls nicht in Route-Params.
|
||||||
fetchProjekte().then((result) => {
|
|
||||||
projektListe = result
|
|
||||||
if (filterProjektId !== null && projektName === "") {
|
if (filterProjektId !== null && projektName === "") {
|
||||||
const projekt = projektListe.find((p: iProjekt) => p.id === filterProjektId)
|
fetchProjektById(filterProjektId).then((projekt: iProjekt) => {
|
||||||
if (projekt) {
|
|
||||||
projektName = projekt.projektname
|
projektName = projekt.projektname
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
view: (vnode) => {
|
view: (vnode) => {
|
||||||
return m("div.w3-container.w3-auto", {
|
return m("div.w3-container.w3-auto", {
|
||||||
|
|||||||
@@ -3,18 +3,15 @@ import {Form, iForm} from "./widgets/form"
|
|||||||
import { Input, iInput} from "./widgets/inputs"
|
import { Input, iInput} from "./widgets/inputs"
|
||||||
import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2"
|
import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "./widgets/Treetable_V2"
|
||||||
import {iProjekt, fetchProjekte, fetchProjektById, createProjektREST, updateProjektREST} from "../models/projekt"
|
import {iProjekt, fetchProjekte, fetchProjektById, createProjektREST, updateProjektREST} from "../models/projekt"
|
||||||
import {iKunde, fetchKunden} from "../models/kunde"
|
import {iKunde, fetchKundeById} from "../models/kunde"
|
||||||
|
|
||||||
export const projektliste: m.ClosureComponent = (vnode) => {
|
export const projektliste: m.ClosureComponent = (vnode) => {
|
||||||
let treenodes: iTreenode[] = []
|
let treenodes: iTreenode[] = []
|
||||||
let selectedNode: iTreenode | null = null
|
let selectedNode: iTreenode | null = null
|
||||||
let filterKundenId: number | null = null
|
let filterKundenId: number | null = null
|
||||||
// Kundenname für die Überschrift, asynchron aus der Kundenliste
|
// Kundenname für die Überschrift, asynchron per fetchKundeById
|
||||||
// aufgelöst.
|
// aufgelöst, falls nicht in den Route-Params enthalten.
|
||||||
let kundenName: string = ""
|
let kundenName: string = ""
|
||||||
// Kundenliste asynchron vom Server laden, für die Kundenauflösung
|
|
||||||
// beim Anlegen eines neuen Projekts.
|
|
||||||
let kundenListe: iKunde[] = []
|
|
||||||
|
|
||||||
const handleRowMarked = (node: iTreenode) => {
|
const handleRowMarked = (node: iTreenode) => {
|
||||||
selectedNode = node
|
selectedNode = node
|
||||||
@@ -24,11 +21,8 @@ export const projektliste: m.ClosureComponent = (vnode) => {
|
|||||||
const params: any = {id: -1}
|
const params: any = {id: -1}
|
||||||
// Nur bei kundengefilterter Liste den Kunden vorbelegen.
|
// Nur bei kundengefilterter Liste den Kunden vorbelegen.
|
||||||
if (filterKundenId !== null) {
|
if (filterKundenId !== null) {
|
||||||
const kunde = kundenListe.find((k: iKunde) => k.id === filterKundenId)
|
params.kunden_id = filterKundenId
|
||||||
if (kunde) {
|
params.kunden_name = kundenName
|
||||||
params.kunden_id = kunde.id
|
|
||||||
params.kunden_name = kunde.name
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
m.route.set("/projekt", params)
|
m.route.set("/projekt", params)
|
||||||
}
|
}
|
||||||
@@ -108,24 +102,17 @@ export const projektliste: m.ClosureComponent = (vnode) => {
|
|||||||
filterKundenId = null
|
filterKundenId = null
|
||||||
kundenName = ""
|
kundenName = ""
|
||||||
}
|
}
|
||||||
// Projekte asynchron vom Server laden.
|
// Projekte asynchron vom Server laden. Die Filterung nach
|
||||||
fetchProjekte().then((result) => {
|
// kunden_id erfolgt serverseitig, sofern ein Filter gesetzt ist.
|
||||||
let filtered = result
|
fetchProjekte(filterKundenId !== null ? filterKundenId : undefined).then((result) => {
|
||||||
if (filterKundenId !== null) {
|
treenodes = result.map((projekt: iProjekt) => new Treenode(projekt, true, []))
|
||||||
filtered = result.filter((p: iProjekt) => p.kunden_id === filterKundenId)
|
|
||||||
}
|
|
||||||
treenodes = filtered.map((projekt: iProjekt) => new Treenode(projekt, true, []))
|
|
||||||
})
|
})
|
||||||
// Kundenliste asynchron vom Server laden.
|
// Kundennamen asynchron auflösen, falls nicht in Route-Params.
|
||||||
fetchKunden().then((result) => {
|
|
||||||
kundenListe = result
|
|
||||||
if (filterKundenId !== null && kundenName === "") {
|
if (filterKundenId !== null && kundenName === "") {
|
||||||
const kunde = kundenListe.find((k: iKunde) => k.id === filterKundenId)
|
fetchKundeById(filterKundenId).then((kunde: iKunde) => {
|
||||||
if (kunde) {
|
|
||||||
kundenName = kunde.name
|
kundenName = kunde.name
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
view: (vnode) => {
|
view: (vnode) => {
|
||||||
return m("div.w3-container.w3-auto", {
|
return m("div.w3-container.w3-auto", {
|
||||||
|
|||||||
@@ -36,11 +36,19 @@ const CALLLOG_REST_BASE = "/xhr/calllog"
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt die Liste der Gesprächsnotizen vom Server.
|
* Lädt die Liste der Gesprächsnotizen vom Server.
|
||||||
|
* Ist projektId gesetzt (>= 0), werden nur die Notizen dieses Projekts
|
||||||
|
* geliefert (serverseitige Filterung). Ohne Parameter werden alle
|
||||||
|
* Notizen geliefert.
|
||||||
*/
|
*/
|
||||||
export function fetchCallLogs(): Promise<iCallLog[]> {
|
export function fetchCallLogs(projektId?: number): Promise<iCallLog[]> {
|
||||||
|
const params: Record<string, any> = {}
|
||||||
|
if (projektId !== undefined && projektId >= 0) {
|
||||||
|
params.projekt_id = projektId
|
||||||
|
}
|
||||||
return m.request({
|
return m.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: CALLLOG_REST_BASE + "/list"
|
url: CALLLOG_REST_BASE + "/list",
|
||||||
|
params: params
|
||||||
}).then((result) => result as iCallLog[])
|
}).then((result) => result as iCallLog[])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,11 +33,19 @@ const KONTAKT_REST_BASE = "/xhr/kontakt"
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt die Kontaktliste vom Server.
|
* Lädt die Kontaktliste vom Server.
|
||||||
|
* Ist kundenId gesetzt (>= 0), werden nur die Kontakte dieses Kunden
|
||||||
|
* geliefert (serverseitige Filterung). Ohne Parameter werden alle
|
||||||
|
* Kontakte geliefert.
|
||||||
*/
|
*/
|
||||||
export function fetchKontakte(): Promise<iKontakt[]> {
|
export function fetchKontakte(kundenId?: number): Promise<iKontakt[]> {
|
||||||
|
const params: Record<string, any> = {}
|
||||||
|
if (kundenId !== undefined && kundenId >= 0) {
|
||||||
|
params.kunden_id = kundenId
|
||||||
|
}
|
||||||
return m.request({
|
return m.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: KONTAKT_REST_BASE + "/list"
|
url: KONTAKT_REST_BASE + "/list",
|
||||||
|
params: params
|
||||||
}).then((result) => result as iKontakt[])
|
}).then((result) => result as iKontakt[])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,11 +38,19 @@ const KONTAKTADRESSE_REST_BASE = "/xhr/kontaktadresse"
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt die Kontaktadressenliste vom Server.
|
* Lädt die Kontaktadressenliste vom Server.
|
||||||
|
* Ist projektId gesetzt (>= 0), werden nur die Kontaktadressen dieses
|
||||||
|
* Projekts geliefert (serverseitige Filterung). Ohne Parameter werden
|
||||||
|
* alle Kontaktadressen geliefert.
|
||||||
*/
|
*/
|
||||||
export function fetchKontaktadressen(): Promise<iKontaktadresse[]> {
|
export function fetchKontaktadressen(projektId?: number): Promise<iKontaktadresse[]> {
|
||||||
|
const params: Record<string, any> = {}
|
||||||
|
if (projektId !== undefined && projektId >= 0) {
|
||||||
|
params.projekt_id = projektId
|
||||||
|
}
|
||||||
return m.request({
|
return m.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: KONTAKTADRESSE_REST_BASE + "/list"
|
url: KONTAKTADRESSE_REST_BASE + "/list",
|
||||||
|
params: params
|
||||||
}).then((result) => result as iKontaktadresse[])
|
}).then((result) => result as iKontaktadresse[])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,11 +30,19 @@ const PROJEKT_REST_BASE = "/xhr/projekt"
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt die Projektliste vom Server.
|
* Lädt die Projektliste vom Server.
|
||||||
|
* Ist kundenId gesetzt (>= 0), werden nur die Projekte dieses Kunden
|
||||||
|
* geliefert (serverseitige Filterung). Ohne Parameter werden alle
|
||||||
|
* Projekte geliefert.
|
||||||
*/
|
*/
|
||||||
export function fetchProjekte(): Promise<iProjekt[]> {
|
export function fetchProjekte(kundenId?: number): Promise<iProjekt[]> {
|
||||||
|
const params: Record<string, any> = {}
|
||||||
|
if (kundenId !== undefined && kundenId >= 0) {
|
||||||
|
params.kunden_id = kundenId
|
||||||
|
}
|
||||||
return m.request({
|
return m.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: PROJEKT_REST_BASE + "/list"
|
url: PROJEKT_REST_BASE + "/list",
|
||||||
|
params: params
|
||||||
}).then((result) => result as iProjekt[])
|
}).then((result) => result as iProjekt[])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user