@flowselections/dashboard-planner-installatie-techniek 1.0.3 → 1.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +0 -1
  2. package/dist-lib/components/DashboardPlannerInstallatieTechniekPage.d.ts +11 -0
  3. package/dist-lib/components/DashboardPlannerInstallatieTechniekPage.d.ts.map +1 -0
  4. package/dist-lib/components/DashboardPlannerInstallatieTechniekPage.js +173 -0
  5. package/dist-lib/components/DashboardSnelkoppelingen.d.ts +16 -0
  6. package/dist-lib/components/DashboardSnelkoppelingen.d.ts.map +1 -0
  7. package/dist-lib/components/DashboardSnelkoppelingen.js +123 -0
  8. package/dist-lib/components/DashboardStatistieken.d.ts +9 -0
  9. package/dist-lib/components/DashboardStatistieken.d.ts.map +1 -0
  10. package/dist-lib/components/DashboardStatistieken.js +80 -0
  11. package/dist-lib/components/ModulePlaceholderPage.d.ts +4 -0
  12. package/dist-lib/components/ModulePlaceholderPage.d.ts.map +1 -0
  13. package/dist-lib/components/ModulePlaceholderPage.js +11 -0
  14. package/dist-lib/components/PlannerInstallatieTechniekLogo.d.ts +6 -0
  15. package/dist-lib/components/PlannerInstallatieTechniekLogo.d.ts.map +1 -0
  16. package/dist-lib/components/PlannerInstallatieTechniekLogo.js +4 -0
  17. package/dist-lib/components/PlannerPaginaKiezer.d.ts +10 -0
  18. package/dist-lib/components/PlannerPaginaKiezer.d.ts.map +1 -0
  19. package/dist-lib/components/PlannerPaginaKiezer.js +15 -0
  20. package/dist-lib/components/WerkbonnenFunnel.d.ts +8 -0
  21. package/dist-lib/components/WerkbonnenFunnel.d.ts.map +1 -0
  22. package/dist-lib/components/WerkbonnenFunnel.js +68 -0
  23. package/dist-lib/components/WerkurenGrafiek.d.ts +26 -0
  24. package/dist-lib/components/WerkurenGrafiek.d.ts.map +1 -0
  25. package/dist-lib/components/WerkurenGrafiek.js +71 -0
  26. package/dist-lib/components/dashboard-viewport.d.ts +2 -0
  27. package/dist-lib/components/dashboard-viewport.d.ts.map +1 -0
  28. package/dist-lib/components/dashboard-viewport.js +97 -0
  29. package/dist-lib/components/ui/sankey-chart.d.ts +30 -0
  30. package/dist-lib/components/ui/sankey-chart.d.ts.map +1 -0
  31. package/dist-lib/components/ui/sankey-chart.js +292 -0
  32. package/dist-lib/index.d.ts +9 -3
  33. package/dist-lib/index.d.ts.map +1 -1
  34. package/dist-lib/index.js +9 -5
  35. package/dist-lib/integrations/supabase/auth-middleware.d.ts +10 -22015
  36. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  37. package/dist-lib/integrations/supabase/auth-middleware.js +1 -1
  38. package/dist-lib/integrations/supabase/client.d.ts +2 -22012
  39. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  40. package/dist-lib/integrations/supabase/client.js +2 -1
  41. package/dist-lib/integrations/supabase/client.server.d.ts +2 -22012
  42. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  43. package/dist-lib/integrations/supabase/previewAuthStorage.d.ts +6 -0
  44. package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -0
  45. package/dist-lib/integrations/supabase/previewAuthStorage.js +99 -0
  46. package/dist-lib/integrations/supabase/types.d.ts +23780 -10104
  47. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  48. package/dist-lib/integrations/supabase/types.js +1 -0
  49. package/dist-lib/lib/attach-core-auth.d.ts +2 -0
  50. package/dist-lib/lib/attach-core-auth.d.ts.map +1 -0
  51. package/dist-lib/lib/attach-core-auth.js +15 -0
  52. package/dist-lib/lib/dashboard-constanten.d.ts +4 -0
  53. package/dist-lib/lib/dashboard-constanten.d.ts.map +1 -0
  54. package/dist-lib/lib/dashboard-constanten.js +8 -0
  55. package/dist-lib/lib/dashboard-planner-installatie-techniek.functions.d.ts +13 -0
  56. package/dist-lib/lib/dashboard-planner-installatie-techniek.functions.d.ts.map +1 -0
  57. package/dist-lib/lib/dashboard-planner-installatie-techniek.functions.js +59 -0
  58. package/dist-lib/lib/dashboard-planner-installatie-techniek.modules.d.ts +11 -0
  59. package/dist-lib/lib/dashboard-planner-installatie-techniek.modules.d.ts.map +1 -0
  60. package/dist-lib/lib/dashboard-planner-installatie-techniek.modules.js +11 -0
  61. package/dist-lib/lib/dashboard-planner-installatie-techniek.server.d.ts +17 -0
  62. package/dist-lib/lib/dashboard-planner-installatie-techniek.server.d.ts.map +1 -0
  63. package/dist-lib/lib/dashboard-planner-installatie-techniek.server.js +239 -0
  64. package/dist-lib/lib/dashboard-planner-installatie-techniek.stats.server.d.ts +19 -0
  65. package/dist-lib/lib/dashboard-planner-installatie-techniek.stats.server.d.ts.map +1 -0
  66. package/dist-lib/lib/dashboard-planner-installatie-techniek.stats.server.js +214 -0
  67. package/dist-lib/lib/dashboard-planner-installatie-techniek.types.d.ts +68 -0
  68. package/dist-lib/lib/dashboard-planner-installatie-techniek.types.d.ts.map +1 -0
  69. package/dist-lib/lib/dashboard-planner-installatie-techniek.types.js +7 -0
  70. package/dist-lib/lib/dashboard-snelkoppelingen.catalogus.d.ts +27 -0
  71. package/dist-lib/lib/dashboard-snelkoppelingen.catalogus.d.ts.map +1 -0
  72. package/dist-lib/lib/dashboard-snelkoppelingen.catalogus.js +102 -0
  73. package/dist-lib/lib/dashboard-snelkoppelingen.d.ts +49 -0
  74. package/dist-lib/lib/dashboard-snelkoppelingen.d.ts.map +1 -0
  75. package/dist-lib/lib/dashboard-snelkoppelingen.js +55 -0
  76. package/dist-lib/lib/dashboard-tellingen.d.ts +12 -0
  77. package/dist-lib/lib/dashboard-tellingen.d.ts.map +1 -0
  78. package/dist-lib/lib/dashboard-tellingen.js +22 -0
  79. package/dist-lib/lib/dashboard-werkbon-funnel.d.ts +60 -0
  80. package/dist-lib/lib/dashboard-werkbon-funnel.d.ts.map +1 -0
  81. package/dist-lib/lib/dashboard-werkbon-funnel.js +100 -0
  82. package/dist-lib/lib/dashboard-werkbon-funnel.server.d.ts +13 -0
  83. package/dist-lib/lib/dashboard-werkbon-funnel.server.d.ts.map +1 -0
  84. package/dist-lib/lib/dashboard-werkbon-funnel.server.js +53 -0
  85. package/dist-lib/lib/funnel-indeling.d.ts +13 -0
  86. package/dist-lib/lib/funnel-indeling.d.ts.map +1 -0
  87. package/dist-lib/lib/funnel-indeling.js +74 -0
  88. package/dist-lib/lib/sankey-doek.d.ts +25 -0
  89. package/dist-lib/lib/sankey-doek.d.ts.map +1 -0
  90. package/dist-lib/lib/sankey-doek.js +38 -0
  91. package/package.json +71 -8
  92. package/public/favicon.png +0 -0
  93. /package/public/{logo.png → flowselections-assets/dashboard-planner-installatie-techniek/logo.png} +0 -0
@@ -0,0 +1,214 @@
1
+ import { DASHBOARD_TIJDZONE } from "./dashboard-constanten";
2
+ const TZ = DASHBOARD_TIJDZONE;
3
+ /** YYYY-MM-DD in Nederlandse tijd */
4
+ function nlDatum(d) {
5
+ return new Intl.DateTimeFormat("en-CA", { timeZone: TZ, year: "numeric", month: "2-digit", day: "2-digit" }).format(d);
6
+ }
7
+ /** Maandag van de huidige week (NL) als YYYY-MM-DD */
8
+ function maandagDezeWeek(nu = new Date()) {
9
+ const vandaag = nlDatum(nu);
10
+ const d = new Date(`${vandaag}T12:00:00Z`);
11
+ const wd = (d.getUTCDay() + 6) % 7;
12
+ return nlDatum(new Date(d.getTime() - wd * 86400000));
13
+ }
14
+ /** Dagen (ma → zo) van de week die op deze maandag begint */
15
+ function dagenVanWeek(maandag) {
16
+ const start = new Date(`${maandag}T12:00:00Z`).getTime();
17
+ return Array.from({ length: 7 }, (_, i) => nlDatum(new Date(start + i * 86400000)));
18
+ }
19
+ const afr = (v) => Math.round(v * 10) / 10;
20
+ async function telOmzetOpen(supabase) {
21
+ const leeg = { bedragCents: null, aantal: null, vervallenCents: null, vervallenAantal: null };
22
+ try {
23
+ const { data, error } = await supabase.from("invoices").select("total_cents, due_date").eq("status", "verzonden");
24
+ if (error)
25
+ return leeg;
26
+ const rows = Array.isArray(data) ? data : [];
27
+ const vandaag = nlDatum(new Date());
28
+ let bedrag = 0, verv = 0, vervN = 0;
29
+ for (const r of rows) {
30
+ const c = Number(r.total_cents ?? 0);
31
+ bedrag += c;
32
+ if (r.due_date && r.due_date.slice(0, 10) < vandaag) {
33
+ verv += c;
34
+ vervN++;
35
+ }
36
+ }
37
+ return { bedragCents: bedrag, aantal: rows.length, vervallenCents: verv, vervallenAantal: vervN };
38
+ }
39
+ catch {
40
+ return leeg;
41
+ }
42
+ }
43
+ async function telOffertePijplijn(supabase) {
44
+ const leeg = { verzondenCents: null, verzondenAantal: null, conceptCents: null, acceptatiePct: null, trechter: [] };
45
+ try {
46
+ const { data, error } = await supabase.from("itk_offertes").select("status, total_cents");
47
+ if (error)
48
+ return leeg;
49
+ const per = {};
50
+ for (const r of (Array.isArray(data) ? data : [])) {
51
+ const s = String(r.status ?? "");
52
+ per[s] ?? (per[s] = { n: 0, c: 0 });
53
+ per[s].n++;
54
+ per[s].c += Number(r.total_cents ?? 0);
55
+ }
56
+ const n = (s) => per[s]?.n ?? 0;
57
+ const geaccepteerd = n("goedgekeurd") + n("geaccepteerd") + n("omgezet");
58
+ const besloten = geaccepteerd + n("afgewezen") + n("verlopen");
59
+ return {
60
+ verzondenCents: (per["verzonden"]?.c ?? 0) + (per["bekeken"]?.c ?? 0),
61
+ verzondenAantal: n("verzonden") + n("bekeken"),
62
+ conceptCents: per["concept"]?.c ?? 0,
63
+ acceptatiePct: besloten > 0 ? Math.round((geaccepteerd / besloten) * 100) : null,
64
+ trechter: [
65
+ { stap: "Concept", aantal: n("concept") },
66
+ { stap: "Verzonden", aantal: n("verzonden") + n("bekeken") },
67
+ { stap: "Goedgekeurd", aantal: n("goedgekeurd") + n("geaccepteerd") },
68
+ { stap: "Omgezet", aantal: n("omgezet") },
69
+ ],
70
+ };
71
+ }
72
+ catch {
73
+ return leeg;
74
+ }
75
+ }
76
+ /** Gewerkte uren van één werkbonregistratie; een terugdraai-correctie telt niet mee. */
77
+ function duur(r) {
78
+ if (!r.started_at || !r.ended_at)
79
+ return 0;
80
+ const ms = new Date(r.ended_at).getTime() - new Date(r.started_at).getTime();
81
+ return ms > 0 ? ms / 3600000 : 0;
82
+ }
83
+ export async function telWerkUren(supabase, nu = new Date(), wekenTerug = 0) {
84
+ // Weekkeuze: 0 = huidige week, max 6 weken terug. Alleen de uren verschuiven;
85
+ // "afgerond deze maand" blijft op de echte maand gebaseerd.
86
+ const doel = new Date(nu.getTime() - Math.min(Math.max(wekenTerug, 0), 6) * 7 * 86400000);
87
+ const dagen = dagenVanWeek(maandagDezeWeek(doel));
88
+ const maandStart = `${nlDatum(nu).slice(0, 7)}-01`;
89
+ const res = {
90
+ afgerondDezeMaand: null, urenDezeWeek: null, overurenDezeWeek: null,
91
+ firstTimeFixPct: null, firstTimeFixBasis: null, urenPerDag: [],
92
+ };
93
+ try {
94
+ // Alleen de gekozen week (ma → zo): vaste, korte periode die meeschaalt met de database.
95
+ const { vanaf, tot } = weekPeriode(nu, wekenTerug);
96
+ const [afgerond, ftf, wtl, ot] = await Promise.all([
97
+ supabase.from("werkbonnen").select("id", { count: "exact", head: true }).gte("afgerond_op", `${maandStart}T00:00:00`),
98
+ supabase.from("werkbonnen").select("first_time_fix").not("first_time_fix", "is", null),
99
+ // De werkbon-klok is waar de monteur zijn uren bijwerkt: activity "werken".
100
+ supabase
101
+ .from("werkbon_time_logs")
102
+ .select("started_at, ended_at")
103
+ .eq("activity", "werken")
104
+ .gte("started_at", vanaf)
105
+ .lte("started_at", tot),
106
+ // Overuren staan alleen in het oude kloksysteem; ontbreken = 0.
107
+ supabase
108
+ .from("time_logs")
109
+ .select("overtime_hours")
110
+ .eq("type", "werken")
111
+ .gte("start_time", vanaf)
112
+ .lte("start_time", tot),
113
+ ]);
114
+ if (!afgerond.error)
115
+ res.afgerondDezeMaand = afgerond.count ?? 0;
116
+ if (!ftf.error) {
117
+ const rows = (Array.isArray(ftf.data) ? ftf.data : []);
118
+ res.firstTimeFixBasis = rows.length;
119
+ res.firstTimeFixPct = rows.length ? Math.round((rows.filter((r) => r.first_time_fix).length / rows.length) * 100) : null;
120
+ }
121
+ if (!wtl.error) {
122
+ const perDag = {};
123
+ for (const r of (Array.isArray(wtl.data) ? wtl.data : [])) {
124
+ if (!r.started_at)
125
+ continue;
126
+ const dag = nlDatum(new Date(r.started_at));
127
+ perDag[dag] = (perDag[dag] ?? 0) + duur(r);
128
+ }
129
+ res.urenPerDag = dagen.map((d) => ({ dag: d, uren: afr(perDag[d] ?? 0) }));
130
+ res.urenDezeWeek = afr(dagen.reduce((s, d) => s + (perDag[d] ?? 0), 0));
131
+ }
132
+ if (!ot.error) {
133
+ const rows = (Array.isArray(ot.data) ? ot.data : []);
134
+ res.overurenDezeWeek = afr(rows.reduce((s, r) => s + Number(r.overtime_hours ?? 0), 0));
135
+ }
136
+ }
137
+ catch {
138
+ /* null-waarden blijven staan */
139
+ }
140
+ return res;
141
+ }
142
+ async function telWerkbonJaarStats(supabase) {
143
+ const leeg = { werkbonSoorten: { totaal: null, soorten: [] }, werkbonDuur: { basis: null, onder1: 0, tussen1en3: 0, boven3: 0 } };
144
+ try {
145
+ const { data, error } = await supabase.rpc("dashboard_werkbon_jaarstats");
146
+ if (error || !data || typeof data !== "object")
147
+ return leeg;
148
+ const s = data.werkbonSoorten;
149
+ const d = data.werkbonDuur;
150
+ const number = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
151
+ return {
152
+ werkbonSoorten: { totaal: number(s?.totaal), soorten: Array.isArray(s?.soorten) ? s.soorten.map((r) => ({ soort: String(r?.soort ?? ""), aantal: number(r?.aantal) ?? 0 })) : [] },
153
+ werkbonDuur: { basis: number(d?.basis), onder1: number(d?.onder1) ?? 0, tussen1en3: number(d?.tussen1en3) ?? 0, boven3: number(d?.boven3) ?? 0 },
154
+ };
155
+ }
156
+ catch {
157
+ return leeg;
158
+ }
159
+ }
160
+ export async function fetchDashboardStats(supabase, wekenTerug = 0, omzetUitDb) {
161
+ const [omzetOpen, offertePijplijn, werkUren, werkbonJaar, facturenBetaald] = await Promise.all([
162
+ omzetUitDb ? Promise.resolve(omzetUitDb) : telOmzetOpen(supabase),
163
+ telOffertePijplijn(supabase),
164
+ telWerkUren(supabase, new Date(), wekenTerug),
165
+ telWerkbonJaarStats(supabase),
166
+ telFacturenBetaald(supabase),
167
+ ]);
168
+ return { omzetOpen, offertePijplijn, werkUren, ...werkbonJaar, facturenBetaald };
169
+ }
170
+ /** Zet een Nederlandse kalenderdag (UTC-middernacht) om naar het werkelijke UTC-moment. */
171
+ function nlMidernachtUTC(doel) {
172
+ let instant = doel;
173
+ const formatter = new Intl.DateTimeFormat("en-GB", {
174
+ timeZone: TZ, year: "numeric", month: "2-digit", day: "2-digit",
175
+ hour: "2-digit", minute: "2-digit", second: "2-digit", hourCycle: "h23",
176
+ });
177
+ for (let i = 0; i < 3; i++) {
178
+ const p = Object.fromEntries(formatter.formatToParts(new Date(instant)).map((x) => [x.type, x.value]));
179
+ const lokaal = Date.UTC(Number(p.year), Number(p.month) - 1, Number(p.day), Number(p.hour), Number(p.minute), Number(p.second));
180
+ instant += doel - lokaal;
181
+ }
182
+ return new Date(instant).toISOString();
183
+ }
184
+ /** Vandaag en de 27 voorgaande Nederlandse kalenderdagen. */
185
+ export function betaalPeriode(nu = new Date()) {
186
+ const datum = new Date(`${nlDatum(nu)}T00:00:00Z`);
187
+ datum.setUTCDate(datum.getUTCDate() - 27);
188
+ return { vanaf: nlMidernachtUTC(datum.getTime()), tot: nu.toISOString() };
189
+ }
190
+ /** Maandag 00:00 Nederlandse tijd tot en met nu — de gekozen week (ma → zo).
191
+ * Bij een eerdere week loopt de periode tot de maandag erna (de week is al voorbij). */
192
+ export function weekPeriode(nu = new Date(), wekenTerug = 0) {
193
+ const doel = new Date(nu.getTime() - Math.min(Math.max(wekenTerug, 0), 6) * 7 * 86400000);
194
+ const maandag = new Date(`${maandagDezeWeek(doel)}T00:00:00Z`).getTime();
195
+ const einde = maandag + 7 * 86400000;
196
+ return {
197
+ vanaf: nlMidernachtUTC(maandag),
198
+ tot: (wekenTerug === 0 ? nu : new Date(einde)).toISOString(),
199
+ };
200
+ }
201
+ export async function telFacturenBetaald(supabase, nu = new Date()) {
202
+ const leeg = { verzondenAantal: null, betaaldAantal: null };
203
+ try {
204
+ const { vanaf, tot } = betaalPeriode(nu);
205
+ const query = () => supabase.from("invoices").select("id", { count: "exact", head: true }).gte("sent_at", vanaf).lte("sent_at", tot);
206
+ const [alle, betaald] = await Promise.all([query(), query().eq("status", "betaald")]);
207
+ if (alle.error || betaald.error || typeof alle.count !== "number" || typeof betaald.count !== "number" || betaald.count > alle.count)
208
+ return leeg;
209
+ return { verzondenAantal: alle.count, betaaldAantal: betaald.count };
210
+ }
211
+ catch {
212
+ return leeg;
213
+ }
214
+ }
@@ -0,0 +1,68 @@
1
+ export interface DashboardKpis {
2
+ planningNietIngepland: number | null;
3
+ werkbonnenOpen: number | null;
4
+ urenTeBeoordelen: number | null;
5
+ mailsTeControleren: number | null;
6
+ offertesMeerwerk: number | null;
7
+ facturenTeFactureren: number | null;
8
+ }
9
+ export interface OmzetOpenStat {
10
+ bedragCents: number | null;
11
+ aantal: number | null;
12
+ vervallenCents: number | null;
13
+ vervallenAantal: number | null;
14
+ }
15
+ export interface OffertePijplijnStat {
16
+ verzondenCents: number | null;
17
+ verzondenAantal: number | null;
18
+ conceptCents: number | null;
19
+ acceptatiePct: number | null;
20
+ /** aantallen per stap: concept, verzonden, goedgekeurd, omgezet */
21
+ trechter: {
22
+ stap: string;
23
+ aantal: number;
24
+ }[];
25
+ }
26
+ export interface WerkUrenStat {
27
+ afgerondDezeMaand: number | null;
28
+ /** totaal van deze week (ma → zo) */
29
+ urenDezeWeek: number | null;
30
+ overurenDezeWeek: number | null;
31
+ firstTimeFixPct: number | null;
32
+ firstTimeFixBasis: number | null;
33
+ /** gewerkte uren per dag van deze week (ma → zo) */
34
+ urenPerDag: {
35
+ dag: string;
36
+ uren: number;
37
+ }[];
38
+ }
39
+ export interface WerkbonSoortenStat {
40
+ totaal: number | null;
41
+ /** soort: standby | mail | extra_werk | handmatig */
42
+ soorten: {
43
+ soort: string;
44
+ aantal: number;
45
+ }[];
46
+ }
47
+ export interface WerkbonDuurStat {
48
+ basis: number | null;
49
+ onder1: number;
50
+ tussen1en3: number;
51
+ boven3: number;
52
+ }
53
+ export interface DashboardStats {
54
+ omzetOpen: OmzetOpenStat;
55
+ facturenBetaald: {
56
+ verzondenAantal: number | null;
57
+ betaaldAantal: number | null;
58
+ };
59
+ offertePijplijn: OffertePijplijnStat;
60
+ werkUren: WerkUrenStat;
61
+ werkbonSoorten: WerkbonSoortenStat;
62
+ werkbonDuur: WerkbonDuurStat;
63
+ }
64
+ export interface DashboardData {
65
+ kpis: DashboardKpis;
66
+ stats?: DashboardStats;
67
+ }
68
+ //# sourceMappingURL=dashboard-planner-installatie-techniek.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-planner-installatie-techniek.types.d.ts","sourceRoot":"","sources":["../../src/lib/dashboard-planner-installatie-techniek.types.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,aAAa;IAC5B,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mEAAmE;IACnE,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,YAAY;IAC3B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,qCAAqC;IACrC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,oDAAoD;IACpD,UAAU,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,qDAAqD;IACrD,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,aAAa,CAAC;IACzB,eAAe,EAAE;QAAE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAClF,eAAe,EAAE,mBAAmB,CAAC;IACrC,QAAQ,EAAE,YAAY,CAAC;IACvB,cAAc,EAAE,kBAAkB,CAAC;IACnC,WAAW,EAAE,eAAe,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB"}
@@ -0,0 +1,7 @@
1
+ // ============================================================================
2
+ // src/lib/dashboard-planner-installatie-techniek.types.ts
3
+ // ============================================================================
4
+ // Client-veilige typen voor het dashboard van de installatietechniek.
5
+ // `null` = telling onbekend (bron ontbreekt of query faalde) → tegel toont "–".
6
+ // ============================================================================
7
+ export {};
@@ -0,0 +1,27 @@
1
+ import type { FlowModule } from "@flowselections/core";
2
+ import type { KiesbareModule, RechtRegel } from "./dashboard-snelkoppelingen";
3
+ interface CatalogusPagina {
4
+ titel: string;
5
+ paden: string[];
6
+ tabblad?: boolean;
7
+ urenToegang?: boolean;
8
+ }
9
+ interface CatalogusModule {
10
+ titel: string;
11
+ paden: string[];
12
+ paginas: CatalogusPagina[];
13
+ }
14
+ export declare const PLANNER_SNELKOPPELINGEN: CatalogusModule[];
15
+ export declare function sleutelVanPad(pad: string): string;
16
+ /** Verkeerde of ontbrekende rechtengegevens zijn geen geldige lege lijst. */
17
+ export declare function leesSnelkoppelingRechten(data: unknown): RechtRegel[];
18
+ export interface PlannerCatalogusContext {
19
+ modules: FlowModule[];
20
+ geregistreerdePaden: string[];
21
+ rechten: RechtRegel[];
22
+ rechtenBekend: boolean;
23
+ urenToegang?: boolean;
24
+ }
25
+ export declare function maakPlannerCatalogus(context: PlannerCatalogusContext): KiesbareModule[];
26
+ export {};
27
+ //# sourceMappingURL=dashboard-snelkoppelingen.catalogus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-snelkoppelingen.catalogus.d.ts","sourceRoot":"","sources":["../../src/lib/dashboard-snelkoppelingen.catalogus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9E,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B;AAGD,eAAO,MAAM,uBAAuB,EAAE,eAAe,EAsCpD,CAAC;AAEF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,6EAA6E;AAC7E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,EAAE,CAcpE;AASD,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,uBAAuB,GAAG,cAAc,EAAE,CA+BvF"}
@@ -0,0 +1,102 @@
1
+ // Alleen in de bronmodules geverifieerde paden; geen omgevingsadressen.
2
+ export const PLANNER_SNELKOPPELINGEN = [
3
+ { titel: "E-mail (planner)", paden: ["/werkbon-mail"], paginas: [
4
+ { titel: "Voorstellen", paden: ["/werkbon-mail"] },
5
+ { titel: "Verwerkt", paden: ["/werkbon-verwerkt"] },
6
+ { titel: "Postvak", paden: ["/postvak"] },
7
+ ] },
8
+ { titel: "Werkbonnen (planner)", paden: ["/installatietechniek-werkbonnen-planner"], paginas: [
9
+ { titel: "Overzicht", paden: ["/installatietechniek-werkbonnen-planner"] },
10
+ { titel: "Nieuwe werkbon", paden: ["/installatietechniek-werkbonnen-planner/nieuw"] },
11
+ { titel: "Aanvragen", paden: ["/installatietechniek-werkbonnen-planner/aanvragen"] },
12
+ ] },
13
+ { titel: "Planning (planner)", paden: ["/installatietechniek-werkbonnen/planning"], paginas: [
14
+ { titel: "Planning", paden: ["/installatietechniek-werkbonnen/planning"] },
15
+ { titel: "Reisadressen", paden: ["/installatietechniek-werkbonnen/reisadressen"] },
16
+ ] },
17
+ { titel: "Urenregistratie (planner)", paden: ["/urenregistratie-installatietechniek-planner", "/urenregistratie-installatietechniek"], paginas: [
18
+ { titel: "Te beoordelen", paden: [], tabblad: true },
19
+ { titel: "Week", paden: [], tabblad: true },
20
+ { titel: "Loonexport", paden: [], tabblad: true, urenToegang: true },
21
+ { titel: "Urenregistratie", paden: [], tabblad: true, urenToegang: true },
22
+ ] },
23
+ { titel: "Offertes (planner)", paden: ["/offertes"], paginas: [
24
+ { titel: "Overzicht", paden: ["/offertes"] },
25
+ ...["Alle", "Concept", "Verzonden", "Goedgekeurd"].map((titel) => ({ titel, paden: [], tabblad: true })),
26
+ ] },
27
+ { titel: "Facturen (planner)", paden: ["/facturen-installatietechniek"], paginas: [
28
+ { titel: "Facturenoverzicht", paden: ["/facturen-installatietechniek"] },
29
+ ] },
30
+ { titel: "Artikelen (planner)", paden: ["/artikelen-installatietechniek-planner", "/artikelen-installatietechniek"], paginas: [
31
+ { titel: "Artikelen", paden: ["/artikelen-installatietechniek-planner", "/artikelen-installatietechniek"] },
32
+ { titel: "Artikelvelden", paden: ["/artikelen-installatietechniek/velden-planner", "/artikelen-installatietechniek/velden"] },
33
+ ] },
34
+ { titel: "Pakketten (planner)", paden: ["/installatietechniek-pakketten"], paginas: [
35
+ { titel: "Pakkettenoverzicht", paden: ["/installatietechniek-pakketten"] },
36
+ ] },
37
+ { titel: "Klanten (planner)", paden: ["/installatietechniek-klanten-planner"], paginas: [
38
+ { titel: "Klantenoverzicht", paden: ["/installatietechniek-klanten-planner"] },
39
+ ] },
40
+ ];
41
+ export function sleutelVanPad(pad) {
42
+ return pad.split("/").filter(Boolean)[0]?.replace(/_+$/, "") ?? "";
43
+ }
44
+ /** Verkeerde of ontbrekende rechtengegevens zijn geen geldige lege lijst. */
45
+ export function leesSnelkoppelingRechten(data) {
46
+ if (!Array.isArray(data))
47
+ throw new Error("Toegangsgegevens ontbreken");
48
+ return data.map((raw) => {
49
+ if (!raw || typeof raw !== "object")
50
+ throw new Error("Ongeldige toegangsgegevens");
51
+ const row = raw;
52
+ const moduleId = row.module_id ?? row.moduleId;
53
+ const featureId = row.feature_id ?? row.featureId ?? null;
54
+ const permission = row.permission ?? row.level ?? row.permission_level;
55
+ if (typeof moduleId !== "string" || (featureId !== null && typeof featureId !== "string") ||
56
+ (permission !== "none" && permission !== "view" && permission !== "edit")) {
57
+ throw new Error("Ongeldige toegangsgegevens");
58
+ }
59
+ return { moduleId: sleutelVanPad(moduleId), featureId, permission };
60
+ });
61
+ }
62
+ function toegestaan(rechten, key) {
63
+ const regels = rechten.filter((r) => r.moduleId === key);
64
+ const moduleRegel = regels.find((r) => r.featureId === null);
65
+ if (moduleRegel)
66
+ return moduleRegel.permission !== "none";
67
+ return regels.length === 0 || regels.some((r) => r.permission !== "none");
68
+ }
69
+ export function maakPlannerCatalogus(context) {
70
+ const { modules, rechten, rechtenBekend, urenToegang } = context;
71
+ const paden = new Set(context.geregistreerdePaden.map((pad) => pad.replace(/\/$/, "") || "/"));
72
+ const geregistreerd = (alternatieven) => alternatieven.find((pad) => paden.has(pad));
73
+ const sleutel = (pad) => {
74
+ const module = modules.find((m) => m.nav.href.replace(/\/$/, "") === pad);
75
+ return module?.registryKey ?? sleutelVanPad(module?.nav.href ?? pad);
76
+ };
77
+ return PLANNER_SNELKOPPELINGEN.flatMap((m) => {
78
+ const hoofdPad = geregistreerd(m.paden);
79
+ const key = sleutel(hoofdPad ?? m.paden[0]);
80
+ if (rechtenBekend && !toegestaan(rechten, key))
81
+ return [];
82
+ const paginas = m.paginas.flatMap((pagina) => {
83
+ const pad = geregistreerd(pagina.paden);
84
+ const pageKey = pad ? sleutel(pad) : key;
85
+ // Een afzonderlijk shell-navigatiekind kan een eigen rechtenkey hebben.
86
+ if (rechtenBekend && !toegestaan(rechten, pageKey))
87
+ return [];
88
+ // Feature-id niet raden uit een URL. Zonder expliciete paginametadata
89
+ // kunnen featurebeperkingen niet betrouwbaar worden gekoppeld.
90
+ const beperkteFeatures = rechten.some((r) => r.moduleId === pageKey && r.featureId !== null && r.permission === "none");
91
+ const beschikbaar = Boolean(pad && hoofdPad && rechtenBekend && !beperkteFeatures && !pagina.tabblad);
92
+ const reden = pagina.urenToegang && urenToegang === false
93
+ ? "Geen toegang"
94
+ : !rechtenBekend ? "Toegang niet bevestigd"
95
+ : pagina.tabblad ? "Tabbladlink nog niet beschikbaar"
96
+ : beperkteFeatures ? "Paginatoegang niet bevestigd"
97
+ : !pad || !hoofdPad ? "Niet geregistreerd in dit ERP" : undefined;
98
+ return [{ titel: pagina.titel, pad: pad ?? "", beschikbaar, reden }];
99
+ });
100
+ return paginas.length ? [{ key, titel: m.titel, pad: hoofdPad ?? "", paginas }] : [];
101
+ });
102
+ }
@@ -0,0 +1,49 @@
1
+ export interface Snelkoppeling {
2
+ id: string;
3
+ titel: string;
4
+ pad: string;
5
+ volgorde: number;
6
+ }
7
+ export declare const MAX_SNELKOPPELINGEN = 12;
8
+ /** True zolang er nog een snelkoppeling bij mag. */
9
+ export declare function kanToevoegen(aantal: number): boolean;
10
+ export interface KiesbarePagina {
11
+ titel: string;
12
+ pad: string;
13
+ beschikbaar?: boolean;
14
+ reden?: string;
15
+ }
16
+ export interface KiesbareModule {
17
+ key: string;
18
+ titel: string;
19
+ pad: string;
20
+ paginas: KiesbarePagina[];
21
+ }
22
+ export interface RechtRegel {
23
+ moduleId: string;
24
+ featureId: string | null;
25
+ permission: "none" | "view" | "edit";
26
+ }
27
+ export interface ModuleBron {
28
+ key: string;
29
+ nav: {
30
+ label: string;
31
+ href: string;
32
+ children?: {
33
+ label: string;
34
+ href: string;
35
+ }[];
36
+ };
37
+ }
38
+ /** Alleen interne paden: begint met één "/", geen schema, geen backslash. */
39
+ export declare function isInternPad(pad: string): boolean;
40
+ /** Geldige snelkoppeling: intern pad óf een http(s)-link naar buiten. */
41
+ export declare function isGeldigeLink(pad: string): boolean;
42
+ /** True als de link naar buiten verwijst (http/https). */
43
+ export declare function isExterneLink(pad: string): boolean;
44
+ /**
45
+ * Filtert modules en pagina's op rechten. Een expliciete "none" verbergt;
46
+ * zonder regel volgt de core-regel (toestaan).
47
+ */
48
+ export declare function filterOpRechten(modules: ModuleBron[], rechten: RechtRegel[]): KiesbareModule[];
49
+ //# sourceMappingURL=dashboard-snelkoppelingen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-snelkoppelingen.d.ts","sourceRoot":"","sources":["../../src/lib/dashboard-snelkoppelingen.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAGD,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,oDAAoD;AACpD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEpD;AAGD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,CAAC;CACpF;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMhD;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAIlD;AAED,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAElD;AAOD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE,CAmB9F"}
@@ -0,0 +1,55 @@
1
+ // Client-veilige helpers voor snelkoppelingen op het dashboard.
2
+ // Zelfde grens als de database-controle dashboard_snelkoppeling_valideer().
3
+ export const MAX_SNELKOPPELINGEN = 12;
4
+ /** True zolang er nog een snelkoppeling bij mag. */
5
+ export function kanToevoegen(aantal) {
6
+ return aantal < MAX_SNELKOPPELINGEN;
7
+ }
8
+ /** Alleen interne paden: begint met één "/", geen schema, geen backslash. */
9
+ export function isInternPad(pad) {
10
+ const p = pad.trim();
11
+ if (!p.startsWith("/") || p.startsWith("//"))
12
+ return false;
13
+ if (p.includes("\\"))
14
+ return false;
15
+ if (/^[a-z][a-z0-9+.-]*:/i.test(p))
16
+ return false;
17
+ return p.length <= 500;
18
+ }
19
+ /** Geldige snelkoppeling: intern pad óf een http(s)-link naar buiten. */
20
+ export function isGeldigeLink(pad) {
21
+ const p = pad.trim();
22
+ if (isInternPad(p))
23
+ return true;
24
+ return p.length <= 500 && /^https?:\/\/[^\s\\]+$/i.test(p);
25
+ }
26
+ /** True als de link naar buiten verwijst (http/https). */
27
+ export function isExterneLink(pad) {
28
+ return /^https?:\/\//i.test(pad.trim());
29
+ }
30
+ function laatsteSegment(href) {
31
+ const delen = href.split("?")[0].split("/").filter(Boolean);
32
+ return (delen[delen.length - 1] ?? "").replace(/_+$/, "");
33
+ }
34
+ /**
35
+ * Filtert modules en pagina's op rechten. Een expliciete "none" verbergt;
36
+ * zonder regel volgt de core-regel (toestaan).
37
+ */
38
+ export function filterOpRechten(modules, rechten) {
39
+ const veilig = Array.isArray(rechten) ? rechten : [];
40
+ const geweigerd = (moduleId, featureId) => veilig.some((r) => r.moduleId === moduleId && r.featureId === featureId && r.permission === "none");
41
+ return (Array.isArray(modules) ? modules : [])
42
+ .filter((m) => !geweigerd(m.key, null))
43
+ .map((m) => {
44
+ const kinderen = Array.isArray(m.nav.children) ? m.nav.children : [];
45
+ const paginas = kinderen
46
+ .filter((c) => !geweigerd(m.key, laatsteSegment(c.href)))
47
+ .map((c) => ({ titel: c.label, pad: c.href }));
48
+ return {
49
+ key: m.key,
50
+ titel: m.nav.label,
51
+ pad: m.nav.href,
52
+ paginas: paginas.length ? paginas : [{ titel: m.nav.label, pad: m.nav.href }],
53
+ };
54
+ });
55
+ }
@@ -0,0 +1,12 @@
1
+ import type { OmzetOpenStat } from "./dashboard-planner-installatie-techniek.types";
2
+ export type DbTellingen = {
3
+ werkbonnenOpen: number | null;
4
+ facturenTeFactureren: number | null;
5
+ planningNietIngepland: number | null;
6
+ urenTeBeoordelen: number | null;
7
+ mailsTeControleren: number | null;
8
+ omzetOpen: OmzetOpenStat | null;
9
+ };
10
+ /** Ongeldige of ontbrekende uitkomst → null (de oude telwijze neemt het over). */
11
+ export declare function leesDbTellingen(data: unknown): DbTellingen | null;
12
+ //# sourceMappingURL=dashboard-tellingen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-tellingen.d.ts","sourceRoot":"","sources":["../../src/lib/dashboard-tellingen.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AAEpF,MAAM,MAAM,WAAW,GAAG;IACxB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,aAAa,GAAG,IAAI,CAAC;CACjC,CAAC;AAOF,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAejE"}
@@ -0,0 +1,22 @@
1
+ const getal = (v) => {
2
+ const n = typeof v === "string" ? Number(v) : v;
3
+ return typeof n === "number" && Number.isFinite(n) && n >= 0 ? n : null;
4
+ };
5
+ /** Ongeldige of ontbrekende uitkomst → null (de oude telwijze neemt het over). */
6
+ export function leesDbTellingen(data) {
7
+ if (!data || typeof data !== "object" || Array.isArray(data))
8
+ return null;
9
+ const d = data;
10
+ const o = d.omzetOpen && typeof d.omzetOpen === "object" ? d.omzetOpen : null;
11
+ const omzet = o && getal(o.aantal) !== null
12
+ ? { bedragCents: getal(o.bedragCents), aantal: getal(o.aantal), vervallenCents: getal(o.vervallenCents), vervallenAantal: getal(o.vervallenAantal) }
13
+ : null;
14
+ return {
15
+ werkbonnenOpen: getal(d.werkbonnenOpen),
16
+ facturenTeFactureren: getal(d.facturenTeFactureren),
17
+ planningNietIngepland: getal(d.planningNietIngepland),
18
+ urenTeBeoordelen: getal(d.urenTeBeoordelen),
19
+ mailsTeControleren: getal(d.mailsTeControleren),
20
+ omzetOpen: omzet,
21
+ };
22
+ }
@@ -0,0 +1,60 @@
1
+ export type FunnelPeriode = 'week' | 'maand' | 'jaar';
2
+ export interface WerkbonFunnelData {
3
+ periode: FunnelPeriode;
4
+ vanaf: string;
5
+ tot: string;
6
+ totaal: number;
7
+ afgerond: number;
8
+ betaald: number;
9
+ extraWerk: number;
10
+ extraWerkBetaald: number;
11
+ onbekend: number;
12
+ extraMeldingen: number;
13
+ extraAfgerond: number;
14
+ extraNietGeaccepteerd: number;
15
+ soorten: {
16
+ soort: string;
17
+ aantal: number;
18
+ }[];
19
+ }
20
+ export declare function valideerFunnelPeriode(value: unknown): FunnelPeriode;
21
+ export declare function valideerFunnelInvoer(input: unknown): FunnelPeriode;
22
+ export declare function normaliseerFunnel(raw: unknown): WerkbonFunnelData;
23
+ export declare const FUNNEL_PAARS = "var(--funnel-extra)";
24
+ export declare const FUNNEL_NAVY = "var(--funnel-navy)";
25
+ export declare const FUNNEL_HERKOMST: Record<string, {
26
+ label: string;
27
+ kleur: string;
28
+ }>;
29
+ export declare const FUNNEL_ONDERAAN: string[];
30
+ export declare const FUNNEL_EXTRA = "Extra werk meldingen";
31
+ export declare function rangFunnelNode(name: string): number;
32
+ export interface FunnelNode {
33
+ name: string;
34
+ value: number;
35
+ kleur: string;
36
+ paars?: boolean;
37
+ }
38
+ export declare function maakFunnelStromen(data: WerkbonFunnelData): {
39
+ nodes: FunnelNode[];
40
+ links: {
41
+ source: number;
42
+ target: number;
43
+ value: number;
44
+ }[];
45
+ };
46
+ export type FunnelStatus = {
47
+ status: 'ok';
48
+ funnel: WerkbonFunnelData;
49
+ } | {
50
+ status: 'sessie';
51
+ } | {
52
+ status: 'data';
53
+ };
54
+ export interface FunnelFoutMelding {
55
+ tekst: string;
56
+ actie: 'inloggen' | 'opnieuw';
57
+ }
58
+ /** Tekst en knop per foutstatus; bij 'ok' is er niets te melden. */
59
+ export declare function funnelFoutMelding(status: 'sessie' | 'data'): FunnelFoutMelding;
60
+ //# sourceMappingURL=dashboard-werkbon-funnel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-werkbon-funnel.d.ts","sourceRoot":"","sources":["../../src/lib/dashboard-werkbon-funnel.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AACtD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IACnD,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAClD,SAAS,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAC9D,cAAc,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,qBAAqB,EAAE,MAAM,CAAC;IAC7E,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC9C;AACD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAGnE;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAGlE;AACD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,iBAAiB,CAwBjE;AACD,eAAO,MAAM,YAAY,wBAAwB,CAAC;AAClD,eAAO,MAAM,WAAW,uBAAuB,CAAC;AAChD,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAK5E,CAAC;AAEF,eAAO,MAAM,eAAe,UAAyG,CAAC;AAGtI,eAAO,MAAM,YAAY,yBAAyB,CAAC;AACnD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAInD;AACD,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE;AAC3F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,iBAAiB;;;gBAMhC,MAAM;gBAAU,MAAM;eAAS,MAAM;;EAoB7D;AAMD,MAAM,MAAM,YAAY,GACpB;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GAC3C;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpB;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvB,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,GAAG,SAAS,CAAC;CAC/B;AAED,oEAAoE;AACpE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,iBAAiB,CAI9E"}