@flowselections/offertes-installatie-techniek 1.0.2 → 1.21.1

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 (105) hide show
  1. package/README.md +16 -1
  2. package/dist-lib/components/offertes/ArtikelKiezer.d.ts +16 -0
  3. package/dist-lib/components/offertes/ArtikelKiezer.d.ts.map +1 -0
  4. package/dist-lib/components/offertes/ArtikelKiezer.js +56 -0
  5. package/dist-lib/components/offertes/ExtraWerkMeldingenLijst.d.ts +4 -0
  6. package/dist-lib/components/offertes/ExtraWerkMeldingenLijst.d.ts.map +1 -0
  7. package/dist-lib/components/offertes/ExtraWerkMeldingenLijst.js +42 -0
  8. package/dist-lib/components/offertes/ExtraWerkOffertePaneel.d.ts +14 -0
  9. package/dist-lib/components/offertes/ExtraWerkOffertePaneel.d.ts.map +1 -0
  10. package/dist-lib/components/offertes/ExtraWerkOffertePaneel.js +91 -0
  11. package/dist-lib/components/offertes/MoederWerkbonPaneel.d.ts +5 -0
  12. package/dist-lib/components/offertes/MoederWerkbonPaneel.d.ts.map +1 -0
  13. package/dist-lib/components/offertes/MoederWerkbonPaneel.js +66 -0
  14. package/dist-lib/components/offertes/NieuweOfferteFormulier.d.ts +33 -0
  15. package/dist-lib/components/offertes/NieuweOfferteFormulier.d.ts.map +1 -0
  16. package/dist-lib/components/offertes/NieuweOfferteFormulier.js +523 -0
  17. package/dist-lib/components/offertes/OfferteA4.d.ts +28 -0
  18. package/dist-lib/components/offertes/OfferteA4.d.ts.map +1 -0
  19. package/dist-lib/components/offertes/OfferteA4.js +47 -0
  20. package/dist-lib/components/offertes/OfferteAkkoordPage.d.ts +4 -0
  21. package/dist-lib/components/offertes/OfferteAkkoordPage.d.ts.map +1 -0
  22. package/dist-lib/components/offertes/OfferteAkkoordPage.js +66 -0
  23. package/dist-lib/components/offertes/OfferteDetailPage.d.ts.map +1 -1
  24. package/dist-lib/components/offertes/OfferteDetailPage.js +247 -23
  25. package/dist-lib/components/offertes/OfferteRegelsEditor.js +1 -1
  26. package/dist-lib/components/offertes/OfferteStatusBadge.d.ts.map +1 -1
  27. package/dist-lib/components/offertes/OfferteStatusBadge.js +1 -0
  28. package/dist-lib/components/offertes/OffertesOverzichtPage.d.ts.map +1 -1
  29. package/dist-lib/components/offertes/OffertesOverzichtPage.js +25 -3
  30. package/dist-lib/components/offertes/VoorstelOffertePaneel.d.ts +14 -0
  31. package/dist-lib/components/offertes/VoorstelOffertePaneel.d.ts.map +1 -0
  32. package/dist-lib/components/offertes/VoorstelOffertePaneel.js +144 -0
  33. package/dist-lib/components/offertes/WerkbonAanmakenPaneel.d.ts +32 -0
  34. package/dist-lib/components/offertes/WerkbonAanmakenPaneel.d.ts.map +1 -0
  35. package/dist-lib/components/offertes/WerkbonAanmakenPaneel.js +26 -0
  36. package/dist-lib/components/offertes/WerkbonVoorstellenLijst.d.ts +4 -0
  37. package/dist-lib/components/offertes/WerkbonVoorstellenLijst.d.ts.map +1 -0
  38. package/dist-lib/components/offertes/WerkbonVoorstellenLijst.js +47 -0
  39. package/dist-lib/components/settings/OfferteEmailSettingsCard.d.ts +2 -0
  40. package/dist-lib/components/settings/OfferteEmailSettingsCard.d.ts.map +1 -0
  41. package/dist-lib/components/settings/OfferteEmailSettingsCard.js +38 -0
  42. package/dist-lib/components/settings/OfferteHuisstijlSettingsCard.d.ts +2 -0
  43. package/dist-lib/components/settings/OfferteHuisstijlSettingsCard.d.ts.map +1 -0
  44. package/dist-lib/components/settings/OfferteHuisstijlSettingsCard.js +66 -0
  45. package/dist-lib/components/settings/OfferteVoorwaardenSettingsCard.d.ts +2 -0
  46. package/dist-lib/components/settings/OfferteVoorwaardenSettingsCard.d.ts.map +1 -0
  47. package/dist-lib/components/settings/OfferteVoorwaardenSettingsCard.js +30 -0
  48. package/dist-lib/hooks/useExtraWerkMeldingen.d.ts +32 -0
  49. package/dist-lib/hooks/useExtraWerkMeldingen.d.ts.map +1 -0
  50. package/dist-lib/hooks/useExtraWerkMeldingen.js +103 -0
  51. package/dist-lib/hooks/useHuisstijl.d.ts +32 -0
  52. package/dist-lib/hooks/useHuisstijl.d.ts.map +1 -0
  53. package/dist-lib/hooks/useHuisstijl.js +60 -0
  54. package/dist-lib/hooks/useOfferte.d.ts +21 -0
  55. package/dist-lib/hooks/useOfferte.d.ts.map +1 -1
  56. package/dist-lib/hooks/useOfferte.js +105 -4
  57. package/dist-lib/hooks/useOffertes.d.ts.map +1 -1
  58. package/dist-lib/hooks/useOffertes.js +17 -7
  59. package/dist-lib/hooks/useWerkbonVoorstellen.d.ts +45 -0
  60. package/dist-lib/hooks/useWerkbonVoorstellen.d.ts.map +1 -0
  61. package/dist-lib/hooks/useWerkbonVoorstellen.js +74 -0
  62. package/dist-lib/index.d.ts +19 -2
  63. package/dist-lib/index.d.ts.map +1 -1
  64. package/dist-lib/index.js +26 -4
  65. package/dist-lib/integrations/supabase/auth-middleware.d.ts +4 -23403
  66. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  67. package/dist-lib/integrations/supabase/auth-middleware.js +1 -1
  68. package/dist-lib/integrations/supabase/client.d.ts +2 -23403
  69. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  70. package/dist-lib/integrations/supabase/client.server.d.ts +2 -23403
  71. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  72. package/dist-lib/integrations/supabase/previewAuthStorage.d.ts +1 -1
  73. package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -1
  74. package/dist-lib/integrations/supabase/previewAuthStorage.js +8 -1
  75. package/dist-lib/integrations/supabase/types.d.ts +23504 -11091
  76. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  77. package/dist-lib/integrations/supabase/types.js +2 -0
  78. package/dist-lib/lib/artikelenZoek.d.ts +17 -0
  79. package/dist-lib/lib/artikelenZoek.d.ts.map +1 -0
  80. package/dist-lib/lib/artikelenZoek.js +37 -0
  81. package/dist-lib/lib/offerteAcceptatie.functions.d.ts +7 -0
  82. package/dist-lib/lib/offerteAcceptatie.functions.d.ts.map +1 -0
  83. package/dist-lib/lib/offerteAcceptatie.functions.js +28 -0
  84. package/dist-lib/lib/offerteAdres.d.ts +7 -0
  85. package/dist-lib/lib/offerteAdres.d.ts.map +1 -0
  86. package/dist-lib/lib/offerteAdres.js +19 -0
  87. package/dist-lib/lib/offerteAi.functions.d.ts +34 -0
  88. package/dist-lib/lib/offerteAi.functions.d.ts.map +1 -0
  89. package/dist-lib/lib/offerteAi.functions.js +252 -0
  90. package/dist-lib/lib/offerteMail.d.ts +27 -0
  91. package/dist-lib/lib/offerteMail.d.ts.map +1 -0
  92. package/dist-lib/lib/offerteMail.js +141 -0
  93. package/dist-lib/lib/offerteRegels.d.ts +9 -0
  94. package/dist-lib/lib/offerteRegels.d.ts.map +1 -0
  95. package/dist-lib/lib/offerteRegels.js +20 -0
  96. package/dist-lib/lib/offerteRegels.test.d.ts +2 -0
  97. package/dist-lib/lib/offerteRegels.test.d.ts.map +1 -0
  98. package/dist-lib/lib/offerteRegels.test.js +17 -0
  99. package/dist-lib/lib/offerteReistijd.functions.d.ts +22 -0
  100. package/dist-lib/lib/offerteReistijd.functions.d.ts.map +1 -0
  101. package/dist-lib/lib/offerteReistijd.functions.js +173 -0
  102. package/dist-lib/lib/offertes.d.ts +8 -1
  103. package/dist-lib/lib/offertes.d.ts.map +1 -1
  104. package/dist-lib/lib/offertes.js +6 -4
  105. package/package.json +96 -5
@@ -0,0 +1,32 @@
1
+ export interface Huisstijl {
2
+ company_name: string;
3
+ address: string;
4
+ postal_code: string;
5
+ city: string;
6
+ chamber_of_commerce_number: string;
7
+ vat_identification_number: string;
8
+ email: string;
9
+ phone: string;
10
+ iban: string;
11
+ website: string;
12
+ primary_color: string;
13
+ secondary_color: string;
14
+ logo_path: string | null;
15
+ payment_term_days: number;
16
+ show_company_name: boolean;
17
+ }
18
+ export declare const legeHuisstijl: () => Huisstijl;
19
+ export declare function haalHuisstijl(): Promise<{
20
+ huisstijl: Huisstijl;
21
+ ingesteld: boolean;
22
+ }>;
23
+ export declare function bewaarHuisstijl(value: Huisstijl): Promise<Huisstijl>;
24
+ export declare function huisstijlLogoUrl(path: string | null | undefined): Promise<string | null>;
25
+ export declare function useHuisstijl(): {
26
+ huisstijl: Huisstijl | null;
27
+ ingesteld: boolean;
28
+ logoUrl: string | null;
29
+ loading: boolean;
30
+ fout: string | null;
31
+ };
32
+ //# sourceMappingURL=useHuisstijl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useHuisstijl.d.ts","sourceRoot":"","sources":["../../src/hooks/useHuisstijl.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IACzE,0BAA0B,EAAE,MAAM,CAAC;IAAC,yBAAyB,EAAE,MAAM,CAAC;IACtE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAC5D,aAAa,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,eAAO,MAAM,aAAa,QAAO,SAO/B,CAAC;AAEH,wBAAsB,aAAa,IAAI,OAAO,CAAC;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAU3F;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAI1E;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAK9F;AAED,wBAAgB,YAAY;eAEb,SAAS,GAAG,IAAI;eAChB,OAAO;aACT,MAAM,GAAG,IAAI;aACb,OAAO;UACV,MAAM,GAAG,IAAI;EAoBtB"}
@@ -0,0 +1,60 @@
1
+ // Huisstijl wordt gedeeld met de Facturen-module (eigenaar van invoice_branding_settings).
2
+ // Lezen: directe select. Schrijven: uitsluitend via invoice_save_branding_settings.
3
+ import { useEffect, useState } from "react";
4
+ import { supabase } from "@flowselections/core";
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ const db = supabase;
7
+ export const legeHuisstijl = () => ({
8
+ company_name: "", address: "", postal_code: "", city: "",
9
+ chamber_of_commerce_number: "", vat_identification_number: "",
10
+ email: "", phone: "", iban: "", website: "",
11
+ primary_color: "#17365D", secondary_color: "#6B7280", logo_path: null,
12
+ payment_term_days: 30,
13
+ show_company_name: true,
14
+ });
15
+ export async function haalHuisstijl() {
16
+ const { data, error } = await db
17
+ .from("invoice_branding_settings")
18
+ .select("*")
19
+ .eq("singleton_key", true)
20
+ .maybeSingle();
21
+ if (error)
22
+ throw error;
23
+ return data
24
+ ? { huisstijl: { ...legeHuisstijl(), ...data }, ingesteld: true }
25
+ : { huisstijl: legeHuisstijl(), ingesteld: false };
26
+ }
27
+ export async function bewaarHuisstijl(value) {
28
+ const { data, error } = await db.rpc("invoice_save_branding_settings", { _payload: value });
29
+ if (error)
30
+ throw error;
31
+ return { ...legeHuisstijl(), ...data };
32
+ }
33
+ export async function huisstijlLogoUrl(path) {
34
+ if (!path)
35
+ return null;
36
+ const { data, error } = await supabase.storage.from("layout-logos").createSignedUrl(path, 3600);
37
+ if (error)
38
+ return null;
39
+ return data?.signedUrl ?? null;
40
+ }
41
+ export function useHuisstijl() {
42
+ const [state, setState] = useState({ huisstijl: null, ingesteld: false, logoUrl: null, loading: true, fout: null });
43
+ useEffect(() => {
44
+ let actief = true;
45
+ haalHuisstijl()
46
+ .then(async ({ huisstijl, ingesteld }) => {
47
+ const logoUrl = await huisstijlLogoUrl(huisstijl.logo_path);
48
+ if (actief)
49
+ setState({ huisstijl, ingesteld, logoUrl, loading: false, fout: null });
50
+ })
51
+ .catch(() => {
52
+ if (actief)
53
+ setState({ huisstijl: null, ingesteld: false, logoUrl: null, loading: false, fout: "Huisstijl kon niet worden geladen" });
54
+ });
55
+ return () => {
56
+ actief = false;
57
+ };
58
+ }, []);
59
+ return state;
60
+ }
@@ -1,17 +1,38 @@
1
1
  import type { Offerte, OfferteEvent, OfferteRegel } from "../lib/offertes";
2
+ import type { WerkbonConcept } from "../components/offertes/WerkbonAanmakenPaneel";
2
3
  export declare function useOfferte(offerteId: string): {
3
4
  offerte: Offerte | null;
4
5
  klantNaam: string | null;
6
+ klantEmail: string;
5
7
  werkbonNummer: string | null;
8
+ bronWerkbon: Record<string, unknown> | null;
6
9
  regels: OfferteRegel[];
7
10
  events: OfferteEvent[];
8
11
  restantMandaat: number;
12
+ werkbonAanvraag: {
13
+ id: string;
14
+ status: string;
15
+ werkbon_nummer: string | null;
16
+ foutmelding: string | null;
17
+ } | null;
9
18
  loading: boolean;
10
19
  reload: () => Promise<void>;
11
20
  updateOfferte: (patch: Record<string, unknown>, eventType?: string, eventTekst?: string) => Promise<boolean>;
12
21
  addRegel: (regel: Partial<OfferteRegel>) => Promise<void>;
13
22
  updateRegel: (id: string, patch: Partial<OfferteRegel>) => Promise<void>;
14
23
  deleteRegel: (id: string) => Promise<void>;
24
+ saveDocument: (patch: Record<string, unknown>, nieuweRegels: Array<{
25
+ description: string;
26
+ unit: string;
27
+ quantity: number;
28
+ unit_price_cents: number;
29
+ package_id?: string | null;
30
+ }>) => Promise<boolean>;
15
31
  naarWerkbon: () => Promise<boolean>;
32
+ maakWerkbon: (concept: WerkbonConcept) => Promise<{
33
+ werkbon_id: string;
34
+ werkbon_nummer: string | null;
35
+ changed: boolean;
36
+ }>;
16
37
  };
17
38
  //# sourceMappingURL=useOfferte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useOfferte.d.ts","sourceRoot":"","sources":["../../src/hooks/useOfferte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAI3E,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM;;;;;;;;;2BAuE1B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,cAAc,MAAM,eAAe,MAAM;sBAchE,OAAO,CAAC,YAAY,CAAC;sBAgBxB,MAAM,SAAS,OAAO,CAAC,YAAY,CAAC;sBAYpC,MAAM;;EAqCpB"}
1
+ {"version":3,"file":"useOfferte.d.ts","sourceRoot":"","sources":["../../src/hooks/useOfferte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8CAA8C,CAAC;AAInF,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM;;;;;;;;;;YAUpC,MAAM;gBACF,MAAM;wBACE,MAAM,GAAG,IAAI;qBAChB,MAAM,GAAG,IAAI;;;;2BAsFZ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,cAAc,MAAM,eAAe,MAAM;sBAchE,OAAO,CAAC,YAAY,CAAC;sBAgBxB,MAAM,SAAS,OAAO,CAAC,YAAY,CAAC;sBAYpC,MAAM;0BAaR,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,gBAChB,KAAK,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;;2BAoDtF,cAAc;oBAqC/B,MAAM;wBAAkB,MAAM,GAAG,IAAI;iBAAW,OAAO;;EAuBvF"}
@@ -7,17 +7,20 @@ const db = supabase;
7
7
  export function useOfferte(offerteId) {
8
8
  const [offerte, setOfferte] = useState(null);
9
9
  const [klantNaam, setKlantNaam] = useState(null);
10
+ const [klantEmail, setKlantEmail] = useState("");
10
11
  const [werkbonNummer, setWerkbonNummer] = useState(null);
12
+ const [bronWerkbon, setBronWerkbon] = useState(null);
11
13
  const [regels, setRegels] = useState([]);
12
14
  const [events, setEvents] = useState([]);
13
15
  const [restantMandaat, setRestantMandaat] = useState(0);
16
+ const [werkbonAanvraag, setWerkbonAanvraag] = useState(null);
14
17
  const [loading, setLoading] = useState(true);
15
18
  const load = useCallback(async () => {
16
19
  setLoading(true);
17
20
  try {
18
21
  const { data, error } = await db
19
22
  .from("itk_offertes")
20
- .select("*, itk_customers(name)")
23
+ .select("*, itk_customers(name, general_email)")
21
24
  .eq("id", offerteId)
22
25
  .maybeSingle();
23
26
  if (error)
@@ -28,6 +31,7 @@ export function useOfferte(offerteId) {
28
31
  }
29
32
  setOfferte(data);
30
33
  setKlantNaam(data.itk_customers?.name ?? null);
34
+ setKlantEmail(data.itk_customers?.general_email ?? "");
31
35
  setWerkbonNummer(data.source_number ?? null);
32
36
  const [regelRes, eventRes] = await Promise.all([
33
37
  db.from("itk_offerte_regels").select("*").eq("offerte_id", offerteId).order("sort_order"),
@@ -40,6 +44,24 @@ export function useOfferte(offerteId) {
40
44
  setRegels(Array.isArray(regelRes.data) ? regelRes.data : []);
41
45
  setEvents(Array.isArray(eventRes.data) ? eventRes.data : []);
42
46
  const bronRef = data.source_ref ?? data.werkbon_id;
47
+ if (bronRef && data.source_type === "work_order") {
48
+ const { data: werkbon } = await db
49
+ .from("werkbonnen")
50
+ .select("id, project_number, customer_id, work_address, room_number, complaint_description, technician_task_description, technician_notes, budget_mandate_cents")
51
+ .eq("id", bronRef)
52
+ .maybeSingle();
53
+ setBronWerkbon(werkbon ?? null);
54
+ }
55
+ else {
56
+ setBronWerkbon(null);
57
+ }
58
+ const { data: queueRows } = await db
59
+ .from("itk_offerte_werkbon_queue")
60
+ .select("id, status, werkbon_nummer, foutmelding")
61
+ .eq("offerte_id", offerteId)
62
+ .order("created_at", { ascending: false })
63
+ .limit(1);
64
+ setWerkbonAanvraag(Array.isArray(queueRows) && queueRows[0] ? queueRows[0] : null);
43
65
  if (bronRef) {
44
66
  const { data: restant } = await db.rpc("itk_offerte_restant_mandaat", {
45
67
  _werkbon_id: bronRef,
@@ -107,29 +129,108 @@ export function useOfferte(offerteId) {
107
129
  }
108
130
  await load();
109
131
  }, [load]);
132
+ const saveDocument = useCallback(async (patch, nieuweRegels) => {
133
+ const veiligeRegels = Array.isArray(nieuweRegels) ? nieuweRegels : [];
134
+ const { error: offerteError } = await db.from("itk_offertes").update(patch).eq("id", offerteId);
135
+ if (offerteError) {
136
+ toast.error("Opslaan mislukt");
137
+ return false;
138
+ }
139
+ const { error: deleteError } = await db.from("itk_offerte_regels").delete().eq("offerte_id", offerteId);
140
+ if (deleteError) {
141
+ toast.error("Offerteregels opslaan mislukt");
142
+ return false;
143
+ }
144
+ if (veiligeRegels.length > 0) {
145
+ const { error: insertError } = await db.from("itk_offerte_regels").insert(veiligeRegels.map((regel, index) => ({
146
+ offerte_id: offerteId,
147
+ source: "vrij",
148
+ package_id: regel.package_id ??
149
+ regels.find((r) => r.omschrijving === regel.description)?.package_id ??
150
+ null,
151
+ omschrijving: regel.description,
152
+ unit: regel.unit || null,
153
+ quantity: regel.quantity,
154
+ unit_price_cents: Math.round(regel.unit_price_cents),
155
+ sort_order: index,
156
+ })));
157
+ if (insertError) {
158
+ toast.error("Offerteregels opslaan mislukt");
159
+ return false;
160
+ }
161
+ }
162
+ await db.rpc("quote_sync_work_order", { _offerte_id: offerteId, _monteur_instructies: null });
163
+ await logEvent("concept_bijgewerkt", "Offerte op het A4 bijgewerkt");
164
+ await load();
165
+ return true;
166
+ }, [offerteId, load, logEvent, regels]);
110
167
  const naarWerkbon = useCallback(async () => {
111
- const { error } = await db.rpc("itk_offerte_naar_werkbon", { _offerte_id: offerteId });
168
+ const { error } = await db.rpc("quote_queue_follow_up", { _offerte_id: offerteId });
112
169
  if (error) {
113
- toast.error("Omzetten naar werkbon mislukt");
170
+ toast.error("Vervolgwerkbon aanvragen mislukt");
114
171
  return false;
115
172
  }
116
- toast.success("Vervolgwerkbon aangemaakt");
117
173
  await load();
118
174
  return true;
119
175
  }, [offerteId, load]);
176
+ const maakWerkbon = useCallback(async (concept) => {
177
+ const veiligeRegels = Array.isArray(concept.regels) ? concept.regels : [];
178
+ const packages = veiligeRegels
179
+ .filter((regel) => Boolean(regel.package_id))
180
+ .map((regel) => ({
181
+ package_id: regel.package_id,
182
+ quantity: Number(regel.quantity || 0),
183
+ unit_price_cents: Math.round(Number(regel.unit_price_cents || 0)),
184
+ }));
185
+ const materials = veiligeRegels
186
+ .filter((regel) => !regel.package_id)
187
+ .map((regel) => ({
188
+ description: regel.description.trim() || "Werkzaamheden",
189
+ quantity: Number(regel.quantity || 0),
190
+ unit: regel.unit.trim() || "st",
191
+ unit_price_cents: Math.round(Number(regel.unit_price_cents || 0)),
192
+ }));
193
+ const { data, error } = await db.rpc("quote_create_follow_up_now", {
194
+ _offerte_id: offerteId,
195
+ _payload: {
196
+ parent_work_order_id: concept.is_follow_up ? concept.parent_work_order_id : null,
197
+ is_follow_up: concept.is_follow_up,
198
+ estimated_duration_minutes: concept.estimated_duration_minutes,
199
+ deadline_date: concept.deadline_date || null,
200
+ customer_id: concept.customer_id,
201
+ work_address: concept.work_address.trim(),
202
+ room_number: concept.room_number.trim(),
203
+ reference: concept.reference.trim(),
204
+ description: concept.complaint_description.trim(),
205
+ technician_task_description: concept.technician_task_description.trim(),
206
+ budget_mandate_cents: Math.max(0, Math.round(concept.budget_mandate_cents)),
207
+ packages,
208
+ materials,
209
+ },
210
+ });
211
+ if (error)
212
+ throw error;
213
+ await load();
214
+ return data;
215
+ }, [offerteId, load]);
120
216
  return {
121
217
  offerte,
122
218
  klantNaam,
219
+ klantEmail,
123
220
  werkbonNummer,
221
+ bronWerkbon,
124
222
  regels,
125
223
  events,
126
224
  restantMandaat,
225
+ werkbonAanvraag,
127
226
  loading,
128
227
  reload: load,
129
228
  updateOfferte,
130
229
  addRegel,
131
230
  updateRegel,
132
231
  deleteRegel,
232
+ saveDocument,
133
233
  naarWerkbon,
234
+ maakWerkbon,
134
235
  };
135
236
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useOffertes.d.ts","sourceRoot":"","sources":["../../src/hooks/useOffertes.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAIvD,wBAAgB,WAAW;;;;EAmC1B"}
1
+ {"version":3,"file":"useOffertes.d.ts","sourceRoot":"","sources":["../../src/hooks/useOffertes.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAIvD,wBAAgB,WAAW;;;;EA0C1B"}
@@ -10,13 +10,23 @@ export function useOffertes() {
10
10
  const load = useCallback(async () => {
11
11
  setLoading(true);
12
12
  try {
13
- const { data, error } = await db
14
- .from("itk_offertes")
15
- .select("*, itk_customers(name)")
16
- .order("created_at", { ascending: false });
17
- if (error)
18
- throw error;
19
- const rows = Array.isArray(data) ? data : [];
13
+ // In blokken ophalen: Supabase geeft max. 1000 rijen per verzoek.
14
+ const BLOK = 1000;
15
+ const rows = [];
16
+ for (let van = 0; van < 100000; van += BLOK) {
17
+ const { data, error } = await db
18
+ .from("itk_offertes")
19
+ .select("*, itk_customers(name)")
20
+ .order("created_at", { ascending: false })
21
+ .order("id", { ascending: false })
22
+ .range(van, van + BLOK - 1);
23
+ if (error)
24
+ throw error;
25
+ const blok = Array.isArray(data) ? data : [];
26
+ rows.push(...blok);
27
+ if (blok.length < BLOK)
28
+ break;
29
+ }
20
30
  setOffertes(rows.map((row) => ({
21
31
  ...row,
22
32
  klant_naam: row.itk_customers?.name ?? null,
@@ -0,0 +1,45 @@
1
+ export interface WerkbonVoorstel {
2
+ id: string;
3
+ from_email: string | null;
4
+ from_name: string | null;
5
+ subject: string | null;
6
+ received_at: string | null;
7
+ customer_id: string | null;
8
+ work_address: string | null;
9
+ room_number: string | null;
10
+ complaint_description: string | null;
11
+ technician_task_description: string | null;
12
+ scheduled_start_time: string | null;
13
+ scheduled_end_time: string | null;
14
+ estimated_duration_minutes: number | null;
15
+ externe_referentie: string | null;
16
+ budget_mandate_cents: number | null;
17
+ internal_notes: string | null;
18
+ deadline_date: string | null;
19
+ reviewed_at: string | null;
20
+ created_at: string;
21
+ klant_naam: string | null;
22
+ seen_at: string | null;
23
+ offerte_id: string | null;
24
+ ai_concept: unknown;
25
+ }
26
+ export interface WerkbonVoorstelRegel {
27
+ id: string;
28
+ proposal_id: string;
29
+ line_type: string | null;
30
+ package_id: string | null;
31
+ description: string | null;
32
+ article_number: string | null;
33
+ quantity: number | null;
34
+ unit: string | null;
35
+ note: string | null;
36
+ sort_order: number | null;
37
+ }
38
+ export declare function useWerkbonVoorstellen(): {
39
+ voorstellen: WerkbonVoorstel[];
40
+ loading: boolean;
41
+ reload: () => Promise<void>;
42
+ upsertStatus: (proposalId: string, patch: Record<string, unknown>) => Promise<void>;
43
+ };
44
+ export declare function laadVoorstelRegels(proposalId: string): Promise<WerkbonVoorstelRegel[]>;
45
+ //# sourceMappingURL=useWerkbonVoorstellen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useWerkbonVoorstellen.d.ts","sourceRoot":"","sources":["../../src/hooks/useWerkbonVoorstellen.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wBAAgB,qBAAqB;;;;+BAoDiB,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EAQ3F;AAED,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAO5F"}
@@ -0,0 +1,74 @@
1
+ // ============================================================================
2
+ // src/hooks/useWerkbonVoorstellen.ts — goedgekeurde werkbonvoorstellen (uit
3
+ // de E-mailmodule, alleen lezen) plus eigen status (gezien / omgezet).
4
+ // ============================================================================
5
+ import { useCallback, useEffect, useState } from "react";
6
+ import { supabase, toast } from "@flowselections/core";
7
+ const db = supabase;
8
+ export function useWerkbonVoorstellen() {
9
+ const [voorstellen, setVoorstellen] = useState([]);
10
+ const [loading, setLoading] = useState(true);
11
+ const load = useCallback(async () => {
12
+ setLoading(true);
13
+ try {
14
+ const { data, error } = await db
15
+ .from("werkbon_proposals")
16
+ .select("*")
17
+ .eq("status", "approved")
18
+ .order("reviewed_at", { ascending: false, nullsFirst: false });
19
+ if (error)
20
+ throw error;
21
+ const rows = Array.isArray(data) ? data : [];
22
+ const ids = rows.map((r) => r.id);
23
+ const custIds = [...new Set(rows.map((r) => r.customer_id).filter(Boolean))];
24
+ const [statusRes, klantRes] = await Promise.all([
25
+ ids.length
26
+ ? db.from("itk_offerte_voorstel_status").select("*").in("proposal_id", ids)
27
+ : Promise.resolve({ data: [] }),
28
+ custIds.length
29
+ ? db.from("itk_customers").select("id, name").in("id", custIds)
30
+ : Promise.resolve({ data: [] }),
31
+ ]);
32
+ const statussen = Array.isArray(statusRes.data) ? statusRes.data : [];
33
+ const klanten = Array.isArray(klantRes.data) ? klantRes.data : [];
34
+ const lijst = rows.map((r) => {
35
+ const s = statussen.find((x) => x.proposal_id === r.id);
36
+ return {
37
+ ...r,
38
+ klant_naam: klanten.find((k) => k.id === r.customer_id)?.name ?? null,
39
+ seen_at: s?.seen_at ?? null,
40
+ offerte_id: s?.offerte_id ?? null,
41
+ ai_concept: s?.ai_concept ?? null,
42
+ };
43
+ });
44
+ lijst.sort((a, b) => Number(!!a.seen_at) - Number(!!b.seen_at));
45
+ setVoorstellen(lijst);
46
+ }
47
+ catch {
48
+ toast.error("Werkbonvoorstellen konden niet worden geladen");
49
+ setVoorstellen([]);
50
+ }
51
+ finally {
52
+ setLoading(false);
53
+ }
54
+ }, []);
55
+ useEffect(() => {
56
+ load();
57
+ }, [load]);
58
+ const upsertStatus = useCallback(async (proposalId, patch) => {
59
+ const { error } = await db
60
+ .from("itk_offerte_voorstel_status")
61
+ .upsert({ proposal_id: proposalId, ...patch }, { onConflict: "proposal_id" });
62
+ if (error)
63
+ console.error("voorstelstatus opslaan mislukt", error);
64
+ }, []);
65
+ return { voorstellen, loading, reload: load, upsertStatus };
66
+ }
67
+ export async function laadVoorstelRegels(proposalId) {
68
+ const { data } = await db
69
+ .from("werkbon_proposal_lines")
70
+ .select("*")
71
+ .eq("proposal_id", proposalId)
72
+ .order("sort_order");
73
+ return Array.isArray(data) ? data : [];
74
+ }
@@ -1,15 +1,32 @@
1
1
  import type { FlowModule } from "@flowselections/core";
2
+ import { OfferteHuisstijlSettingsCard } from "./components/settings/OfferteHuisstijlSettingsCard";
3
+ import { OfferteEmailSettingsCard } from "./components/settings/OfferteEmailSettingsCard";
4
+ import { OfferteVoorwaardenSettingsCard } from "./components/settings/OfferteVoorwaardenSettingsCard";
5
+ export { OfferteEmailSettingsCard };
6
+ export { OfferteHuisstijlSettingsCard };
7
+ export { OfferteVoorwaardenSettingsCard };
8
+ export { useHuisstijl } from "./hooks/useHuisstijl";
2
9
  export * from "./_core-safelist";
3
10
  export * from "./lib/offertes";
4
11
  export { OffertesOverzichtPage } from "./components/offertes/OffertesOverzichtPage";
5
12
  export { OfferteDetailPage } from "./components/offertes/OfferteDetailPage";
6
13
  export { OfferteDetailRoute } from "./components/offertes/OfferteDetailRoute";
14
+ export { OfferteAkkoordPage } from "./components/offertes/OfferteAkkoordPage";
7
15
  export { OfferteStatusBadge } from "./components/offertes/OfferteStatusBadge";
8
- export { OfferteKpiCards } from "./components/offertes/OfferteKpiCards";
9
16
  export { OfferteRegelsEditor } from "./components/offertes/OfferteRegelsEditor";
10
- export { NieuweOfferteDialog } from "./components/offertes/NieuweOfferteDialog";
17
+ export { NieuweOfferteFormulier } from "./components/offertes/NieuweOfferteFormulier";
11
18
  export { useOffertes } from "./hooks/useOffertes";
12
19
  export { useOfferte } from "./hooks/useOfferte";
13
20
  export { useWerkbonPakketten } from "./hooks/useWerkbonPakketten";
21
+ export { WerkbonVoorstellenLijst } from "./components/offertes/WerkbonVoorstellenLijst";
22
+ export { VoorstelOffertePaneel } from "./components/offertes/VoorstelOffertePaneel";
23
+ export { useWerkbonVoorstellen } from "./hooks/useWerkbonVoorstellen";
24
+ export { ExtraWerkMeldingenLijst } from "./components/offertes/ExtraWerkMeldingenLijst";
25
+ export { ExtraWerkOffertePaneel } from "./components/offertes/ExtraWerkOffertePaneel";
26
+ export { OfferteA4 } from "./components/offertes/OfferteA4";
27
+ export { WerkbonAanmakenPaneel } from "./components/offertes/WerkbonAanmakenPaneel";
28
+ export { MoederWerkbonPaneel } from "./components/offertes/MoederWerkbonPaneel";
29
+ export type { WerkbonConcept, WerkbonConceptRegel } from "./components/offertes/WerkbonAanmakenPaneel";
30
+ export { useExtraWerkMeldingen } from "./hooks/useExtraWerkMeldingen";
14
31
  export declare const offertesInstallatieTechniekModule: FlowModule;
15
32
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAE/B,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAEhF,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,eAAO,MAAM,iCAAiC,EAAE,UAa/C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,4BAA4B,EAAE,MAAM,oDAAoD,CAAC;AAClG,OAAO,EAAE,wBAAwB,EAAE,MAAM,gDAAgD,CAAC;AAC1F,OAAO,EAAE,8BAA8B,EAAE,MAAM,sDAAsD,CAAC;AACtG,OAAO,EAAE,wBAAwB,EAAE,CAAC;AACpC,OAAO,EAAE,4BAA4B,EAAE,CAAC;AACxC,OAAO,EAAE,8BAA8B,EAAE,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAE/B,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,8CAA8C,CAAC;AAEtF,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AACxF,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACpF,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AACxF,OAAO,EAAE,sBAAsB,EAAE,MAAM,8CAA8C,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAChF,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AACvG,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,eAAO,MAAM,iCAAiC,EAAE,UAmB/C,CAAC"}
package/dist-lib/index.js CHANGED
@@ -6,27 +6,49 @@
6
6
  // geëxporteerd zodat de shell-configurator ze kan detecteren.
7
7
  // ============================================================================
8
8
  import { FileText, List } from "lucide-react";
9
+ import { OfferteHuisstijlSettingsCard } from "./components/settings/OfferteHuisstijlSettingsCard";
10
+ import { OfferteEmailSettingsCard } from "./components/settings/OfferteEmailSettingsCard";
11
+ import { OfferteVoorwaardenSettingsCard } from "./components/settings/OfferteVoorwaardenSettingsCard";
12
+ export { OfferteEmailSettingsCard };
13
+ export { OfferteHuisstijlSettingsCard };
14
+ export { OfferteVoorwaardenSettingsCard };
15
+ export { useHuisstijl } from "./hooks/useHuisstijl";
9
16
  export * from "./_core-safelist";
10
17
  export * from "./lib/offertes";
11
18
  export { OffertesOverzichtPage } from "./components/offertes/OffertesOverzichtPage";
12
19
  export { OfferteDetailPage } from "./components/offertes/OfferteDetailPage";
13
20
  export { OfferteDetailRoute } from "./components/offertes/OfferteDetailRoute";
21
+ export { OfferteAkkoordPage } from "./components/offertes/OfferteAkkoordPage";
14
22
  export { OfferteStatusBadge } from "./components/offertes/OfferteStatusBadge";
15
- export { OfferteKpiCards } from "./components/offertes/OfferteKpiCards";
16
23
  export { OfferteRegelsEditor } from "./components/offertes/OfferteRegelsEditor";
17
- export { NieuweOfferteDialog } from "./components/offertes/NieuweOfferteDialog";
24
+ export { NieuweOfferteFormulier } from "./components/offertes/NieuweOfferteFormulier";
18
25
  export { useOffertes } from "./hooks/useOffertes";
19
26
  export { useOfferte } from "./hooks/useOfferte";
20
27
  export { useWerkbonPakketten } from "./hooks/useWerkbonPakketten";
28
+ export { WerkbonVoorstellenLijst } from "./components/offertes/WerkbonVoorstellenLijst";
29
+ export { VoorstelOffertePaneel } from "./components/offertes/VoorstelOffertePaneel";
30
+ export { useWerkbonVoorstellen } from "./hooks/useWerkbonVoorstellen";
31
+ export { ExtraWerkMeldingenLijst } from "./components/offertes/ExtraWerkMeldingenLijst";
32
+ export { ExtraWerkOffertePaneel } from "./components/offertes/ExtraWerkOffertePaneel";
33
+ export { OfferteA4 } from "./components/offertes/OfferteA4";
34
+ export { WerkbonAanmakenPaneel } from "./components/offertes/WerkbonAanmakenPaneel";
35
+ export { MoederWerkbonPaneel } from "./components/offertes/MoederWerkbonPaneel";
36
+ export { useExtraWerkMeldingen } from "./hooks/useExtraWerkMeldingen";
21
37
  export const offertesInstallatieTechniekModule = {
22
38
  id: "offertes-installatietechniek",
23
39
  name: "Offertes Installatie Techniek",
24
- version: "1.0.0",
40
+ version: "1.21.0",
25
41
  nav: {
26
42
  label: "Offertes",
27
43
  href: "/offertes",
28
44
  icon: FileText,
29
45
  children: [{ label: "Overzicht", href: "/offertes", icon: List }],
30
46
  },
31
- settingsCards: [],
47
+ settingsCards: [
48
+ { component: OfferteHuisstijlSettingsCard, order: 20 },
49
+ // order 21: direct naast/onder Offertehuisstijl
50
+ { component: OfferteVoorwaardenSettingsCard, order: 21 },
51
+ // order 1.5: staat op de plek van het notificatieblok (dat order 2 heeft)
52
+ { component: OfferteEmailSettingsCard, order: 1.5 },
53
+ ],
32
54
  };