@flowselections/floriday-verkoop-module 1.0.6 → 1.0.8

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 (57) hide show
  1. package/dist-lib/components/verkoop/AnalyticsCards.js +2 -2
  2. package/dist-lib/components/verkoop/CreateOrderPage.d.ts +2 -0
  3. package/dist-lib/components/verkoop/CreateOrderPage.d.ts.map +1 -0
  4. package/dist-lib/components/verkoop/CreateOrderPage.js +221 -0
  5. package/dist-lib/components/verkoop/DiagnoseConnectionDialog.d.ts +5 -0
  6. package/dist-lib/components/verkoop/DiagnoseConnectionDialog.d.ts.map +1 -0
  7. package/dist-lib/components/verkoop/DiagnoseConnectionDialog.js +40 -0
  8. package/dist-lib/components/verkoop/SalesOrderDetailPage.d.ts +2 -0
  9. package/dist-lib/components/verkoop/SalesOrderDetailPage.d.ts.map +1 -0
  10. package/dist-lib/components/verkoop/SalesOrderDetailPage.js +14 -0
  11. package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts +6 -0
  12. package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -0
  13. package/dist-lib/components/verkoop/SalesOrderDetailView.js +213 -0
  14. package/dist-lib/components/verkoop/SalesOrdersFilterPanel.d.ts +9 -0
  15. package/dist-lib/components/verkoop/SalesOrdersFilterPanel.d.ts.map +1 -0
  16. package/dist-lib/components/verkoop/SalesOrdersFilterPanel.js +62 -0
  17. package/dist-lib/components/verkoop/SalesOrdersFilters.d.ts +13 -0
  18. package/dist-lib/components/verkoop/SalesOrdersFilters.d.ts.map +1 -1
  19. package/dist-lib/components/verkoop/SalesOrdersFilters.js +123 -23
  20. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts +3 -2
  21. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
  22. package/dist-lib/components/verkoop/SalesOrdersTable.js +3 -3
  23. package/dist-lib/components/verkoop/VerkoopOrdersPage.d.ts.map +1 -1
  24. package/dist-lib/components/verkoop/VerkoopOrdersPage.js +124 -33
  25. package/dist-lib/components/verkoop/WeekCalendar.d.ts +6 -0
  26. package/dist-lib/components/verkoop/WeekCalendar.d.ts.map +1 -0
  27. package/dist-lib/components/verkoop/WeekCalendar.js +101 -0
  28. package/dist-lib/index.d.ts +3 -1
  29. package/dist-lib/index.d.ts.map +1 -1
  30. package/dist-lib/index.js +3 -1
  31. package/dist-lib/integrations/supabase/auth-middleware.d.ts +5727 -653
  32. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  33. package/dist-lib/integrations/supabase/client.d.ts +5727 -653
  34. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  35. package/dist-lib/integrations/supabase/client.js +1 -1
  36. package/dist-lib/integrations/supabase/client.server.d.ts +5727 -653
  37. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  38. package/dist-lib/integrations/supabase/client.server.js +1 -1
  39. package/dist-lib/integrations/supabase/types.d.ts +5871 -653
  40. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  41. package/dist-lib/integrations/supabase/types.js +18 -1
  42. package/dist-lib/lib/floriday-sync.d.ts +32 -0
  43. package/dist-lib/lib/floriday-sync.d.ts.map +1 -0
  44. package/dist-lib/lib/floriday-sync.js +177 -0
  45. package/dist-lib/lib/salesorders-mapping.d.ts +85 -0
  46. package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -0
  47. package/dist-lib/lib/salesorders-mapping.js +198 -0
  48. package/dist-lib/lib/salesorders.functions.d.ts +19941 -0
  49. package/dist-lib/lib/salesorders.functions.d.ts.map +1 -0
  50. package/dist-lib/lib/salesorders.functions.js +216 -0
  51. package/dist-lib/lib/tradeitems.functions.d.ts +6641 -0
  52. package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -0
  53. package/dist-lib/lib/tradeitems.functions.js +60 -0
  54. package/dist-lib/lib/useSalesOrdersQuery.d.ts +6 -0
  55. package/dist-lib/lib/useSalesOrdersQuery.d.ts.map +1 -0
  56. package/dist-lib/lib/useSalesOrdersQuery.js +143 -0
  57. package/package.json +18 -4
@@ -0,0 +1,62 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect } from "react";
3
+ import { Button, Checkbox, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
4
+ import { X } from "lucide-react";
5
+ export function countActiveFilters(f) {
6
+ let n = 0;
7
+ if (f.handelsvorm)
8
+ n++;
9
+ if (f.klant)
10
+ n++;
11
+ if (f.voorraadlocatie)
12
+ n++;
13
+ if (f.kanaal)
14
+ n++;
15
+ if (f.financieleAfhandeling)
16
+ n++;
17
+ if (f.onvolledigVerrekend)
18
+ n++;
19
+ if (f.artikel)
20
+ n++;
21
+ if (f.daytrade)
22
+ n++;
23
+ if (f.orderReferentie)
24
+ n++;
25
+ return n;
26
+ }
27
+ export function SalesOrdersFilterPanel({ open, onOpenChange, value, onChange, }) {
28
+ useEffect(() => {
29
+ if (!open)
30
+ return;
31
+ const onKey = (e) => {
32
+ if (e.key === "Escape")
33
+ onOpenChange(false);
34
+ };
35
+ window.addEventListener("keydown", onKey);
36
+ return () => window.removeEventListener("keydown", onKey);
37
+ }, [open, onOpenChange]);
38
+ const clearAll = () => {
39
+ onChange({
40
+ ...value,
41
+ handelsvorm: undefined,
42
+ klant: undefined,
43
+ voorraadlocatie: undefined,
44
+ kanaal: undefined,
45
+ financieleAfhandeling: undefined,
46
+ onvolledigVerrekend: undefined,
47
+ artikel: undefined,
48
+ daytrade: undefined,
49
+ orderReferentie: undefined,
50
+ });
51
+ };
52
+ const activeCount = countActiveFilters(value);
53
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/30 transition-opacity " +
54
+ (open ? "opacity-100" : "pointer-events-none opacity-0"), onClick: () => onOpenChange(false), "aria-hidden": true }), _jsxs("aside", { className: "fixed right-0 top-0 z-50 flex h-full w-full max-w-sm flex-col border-l border-border bg-card shadow-xl transition-transform " +
55
+ (open ? "translate-x-0" : "translate-x-full"), role: "dialog", "aria-label": "Filters", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-border px-5 py-4", children: [_jsx("h2", { className: "text-base font-semibold text-foreground", children: "Filters" }), _jsx("button", { type: "button", onClick: () => onOpenChange(false), className: "inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", "aria-label": "Sluiten", children: _jsx(X, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-5 py-4", children: [_jsx("button", { type: "button", onClick: clearAll, disabled: activeCount === 0, className: "mb-4 text-sm " +
56
+ (activeCount === 0
57
+ ? "cursor-not-allowed text-muted-foreground/60"
58
+ : "text-primary hover:underline"), children: "Wis alle filters" }), _jsxs("div", { className: "space-y-5", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { className: "text-sm font-semibold", children: "Klant" }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx("span", { className: "inline-flex h-7 items-center rounded-full border border-border bg-background px-3 text-xs text-muted-foreground", children: "Connecties" }), _jsx("span", { className: "inline-flex h-7 items-center rounded-full border border-primary/40 bg-primary/10 px-3 text-xs font-medium text-primary", children: "Alle klanten" })] }), _jsxs(Select, { value: value.klant ?? "all", onValueChange: (v) => onChange({ ...value, klant: v === "all" ? undefined : v }), children: [_jsx(SelectTrigger, { className: "h-10 w-full", children: _jsx(SelectValue, { placeholder: "Kies een klant" }) }), _jsx(SelectContent, { children: _jsx(SelectItem, { value: "all", children: "Kies een klant" }) })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { className: "text-sm font-semibold", children: "Handelsvorm" }), _jsxs(Select, { value: value.handelsvorm ?? "all", onValueChange: (v) => onChange({ ...value, handelsvorm: v === "all" ? undefined : v }), children: [_jsx(SelectTrigger, { className: "h-10 w-full", children: _jsx(SelectValue, { placeholder: "- Alles -" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "- Alles -" }), _jsx(SelectItem, { value: "klok", children: "Klok" }), _jsx(SelectItem, { value: "direct", children: "Direct" })] })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { className: "text-sm font-semibold", children: "Voorraadlocatie" }), _jsxs(Select, { value: value.voorraadlocatie ?? "all", onValueChange: (v) => onChange({ ...value, voorraadlocatie: v === "all" ? undefined : v }), children: [_jsx(SelectTrigger, { className: "h-10 w-full", children: _jsx(SelectValue, { placeholder: "- Alle voorraadlocaties -" }) }), _jsx(SelectContent, { children: _jsx(SelectItem, { value: "all", children: "- Alle voorraadlocaties -" }) })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { className: "text-sm font-semibold", children: "Kanaal" }), _jsxs(Select, { value: value.kanaal ?? "all", onValueChange: (v) => onChange({ ...value, kanaal: v === "all" ? undefined : v }), children: [_jsx(SelectTrigger, { className: "h-10 w-full", children: _jsx(SelectValue, { placeholder: "- Alle verkoopkanalen -" }) }), _jsx(SelectContent, { children: _jsx(SelectItem, { value: "all", children: "- Alle verkoopkanalen -" }) })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { className: "text-sm font-semibold", children: "Financi\u00EBle afhandeling" }), _jsxs(Select, { value: value.financieleAfhandeling ?? "all", onValueChange: (v) => onChange({ ...value, financieleAfhandeling: v === "all" ? undefined : v }), children: [_jsx(SelectTrigger, { className: "h-10 w-full", children: _jsx(SelectValue, { placeholder: "- Financi\u00EBle afhandeling -" }) }), _jsx(SelectContent, { children: _jsx(SelectItem, { value: "all", children: "- Financi\u00EBle afhandeling -" }) })] }), _jsxs("label", { className: "flex items-center gap-2 pt-1 text-sm text-foreground", children: [_jsx(Checkbox, { checked: !!value.onvolledigVerrekend, onCheckedChange: (c) => onChange({ ...value, onvolledigVerrekend: c === true ? true : undefined }) }), _jsx("span", { children: "Onvolledig verrekend" })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { className: "text-sm font-semibold", children: "Artikel" }), _jsxs(Select, { value: value.artikel ?? "all", onValueChange: (v) => onChange({ ...value, artikel: v === "all" ? undefined : v }), children: [_jsx(SelectTrigger, { className: "h-10 w-full", children: _jsx(SelectValue, { placeholder: "- Alle artikelen -" }) }), _jsx(SelectContent, { children: _jsx(SelectItem, { value: "all", children: "- Alle artikelen -" }) })] }), _jsxs("label", { className: "flex items-center gap-2 pt-1 text-sm text-foreground", children: [_jsx(Checkbox, { checked: !!value.daytrade, onCheckedChange: (c) => onChange({ ...value, daytrade: c === true ? true : undefined }) }), _jsx("span", { children: "Daytrade transacties" })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { className: "text-sm font-semibold", children: "Order-referentie" }), _jsx(Input, { placeholder: "Order-referentie", value: value.orderReferentie ?? "", onChange: (e) => onChange({
59
+ ...value,
60
+ orderReferentie: e.target.value ? e.target.value : undefined,
61
+ }), className: "h-10" })] })] })] }), _jsx("div", { className: "border-t border-border px-5 py-3", children: _jsx(Button, { className: "w-full", onClick: () => onOpenChange(false), children: "Toepassen" }) })] })] }));
62
+ }
@@ -6,8 +6,21 @@ export interface Filters {
6
6
  handelsvorm?: string;
7
7
  klant?: string;
8
8
  voorraadlocatie?: string;
9
+ kanaal?: string;
10
+ financieleAfhandeling?: string;
11
+ onvolledigVerrekend?: boolean;
12
+ artikel?: string;
13
+ daytrade?: boolean;
14
+ orderReferentie?: string;
9
15
  week?: number;
10
16
  year?: number;
17
+ month?: number;
18
+ monthYear?: number;
19
+ day?: string;
20
+ customFrom?: string;
21
+ customTo?: string;
22
+ dateField?: "ordered" | "delivery";
23
+ period?: "week" | "month" | "day" | "last7" | "last30" | "custom";
11
24
  }
12
25
  export declare function SalesOrdersFilters({ value, onChange, }: {
13
26
  value: Filters;
@@ -1 +1 @@
1
- {"version":3,"file":"SalesOrdersFilters.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrdersFilters.tsx"],"names":[],"mappings":"AAoBA,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAcD,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACnC,2CAkLA"}
1
+ {"version":3,"file":"SalesOrdersFilters.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrdersFilters.tsx"],"names":[],"mappings":"AA8BA,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;CACnE;AAoCD,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACnC,2CA4PA"}
@@ -1,7 +1,8 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
- import { Button, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
4
- import { ChevronLeft, ChevronRight, MoreVertical, ShoppingCart, Truck, Filter, Search, } from "lucide-react";
3
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, Input, Popover, PopoverContent, PopoverTrigger, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
4
+ import { ChevronDown, ChevronLeft, ChevronRight, MoreVertical, ShoppingCart, Truck, Search, } from "lucide-react";
5
+ import { WeekCalendar } from "./WeekCalendar";
5
6
  function getCurrentWeek() {
6
7
  const now = new Date();
7
8
  const target = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()));
@@ -12,29 +13,128 @@ function getCurrentWeek() {
12
13
  Math.round(((target.getTime() - firstThursday.getTime()) / 86400000 - 3 + ((firstThursday.getUTCDay() + 6) % 7)) / 7);
13
14
  return { week, year: target.getUTCFullYear() };
14
15
  }
16
+ function getCurrentMonth() {
17
+ const now = new Date();
18
+ return { month: now.getMonth(), year: now.getFullYear() };
19
+ }
20
+ const MONTHS_NL = [
21
+ "januari", "februari", "maart", "april", "mei", "juni",
22
+ "juli", "augustus", "september", "oktober", "november", "december",
23
+ ];
24
+ function generateMonthOptions() {
25
+ const now = new Date();
26
+ const currentYear = now.getFullYear();
27
+ const options = [];
28
+ for (let y = currentYear - 2; y <= currentYear + 2; y++) {
29
+ for (let m = 0; m < 12; m++) {
30
+ options.push({ label: `${MONTHS_NL[m]} ${y}`, value: `${y}-${m}` });
31
+ }
32
+ }
33
+ return options;
34
+ }
15
35
  export function SalesOrdersFilters({ value, onChange, }) {
16
- const current = getCurrentWeek();
17
- const week = value.week ?? current.week;
18
- const year = value.year ?? current.year;
36
+ const currentWeek = getCurrentWeek();
37
+ const week = value.week ?? currentWeek.week;
38
+ const year = value.year ?? currentWeek.year;
39
+ const currentMonth = getCurrentMonth();
40
+ const month = value.month ?? currentMonth.month;
41
+ const monthYear = value.monthYear ?? currentMonth.year;
42
+ const period = value.period ?? "week";
43
+ const todayYmd = (() => {
44
+ const n = new Date();
45
+ return `${n.getFullYear()}-${String(n.getMonth() + 1).padStart(2, "0")}-${String(n.getDate()).padStart(2, "0")}`;
46
+ })();
47
+ const day = value.day ?? todayYmd;
19
48
  const [search, setSearch] = useState(value.search ?? "");
20
- const shiftWeek = (delta) => {
21
- let w = week + delta;
22
- let y = year;
23
- if (w < 1) {
24
- w = 52;
25
- y -= 1;
26
- }
27
- else if (w > 53) {
28
- w = 1;
29
- y += 1;
30
- }
31
- onChange({ ...value, week: w, year: y });
49
+ const monthOptions = generateMonthOptions();
50
+ const formatDayNL = (ymd) => {
51
+ const [y, m, d] = ymd.split("-").map(Number);
52
+ const dt = new Date(Date.UTC(y, (m ?? 1) - 1, d ?? 1));
53
+ return `${String(dt.getUTCDate()).padStart(2, "0")} ${MONTHS_NL[dt.getUTCMonth()]} ${dt.getUTCFullYear()}`;
54
+ };
55
+ const shiftPeriod = (delta) => {
56
+ if (period === "month") {
57
+ let m = month + delta;
58
+ let y = monthYear;
59
+ if (m < 0) {
60
+ m = 11;
61
+ y -= 1;
62
+ }
63
+ else if (m > 11) {
64
+ m = 0;
65
+ y += 1;
66
+ }
67
+ onChange({ ...value, month: m, monthYear: y });
68
+ }
69
+ else if (period === "day") {
70
+ const [y, m, d] = day.split("-").map(Number);
71
+ const dt = new Date(Date.UTC(y, m - 1, d));
72
+ dt.setUTCDate(dt.getUTCDate() + delta);
73
+ const ymd = `${dt.getUTCFullYear()}-${String(dt.getUTCMonth() + 1).padStart(2, "0")}-${String(dt.getUTCDate()).padStart(2, "0")}`;
74
+ onChange({ ...value, day: ymd });
75
+ }
76
+ else if (period === "week") {
77
+ let w = week + delta;
78
+ let y = year;
79
+ if (w < 1) {
80
+ w = 52;
81
+ y -= 1;
82
+ }
83
+ else if (w > 53) {
84
+ w = 1;
85
+ y += 1;
86
+ }
87
+ onChange({ ...value, week: w, year: y });
88
+ }
89
+ };
90
+ const handlePeriodChange = (nextPeriod) => {
91
+ const base = { ...value, period: nextPeriod };
92
+ if (nextPeriod === "month" && (base.month === undefined || base.monthYear === undefined)) {
93
+ const cm = getCurrentMonth();
94
+ base.month = cm.month;
95
+ base.monthYear = cm.year;
96
+ }
97
+ if (nextPeriod === "week" && (base.week === undefined || base.year === undefined)) {
98
+ const cw = getCurrentWeek();
99
+ base.week = cw.week;
100
+ base.year = cw.year;
101
+ }
102
+ if (nextPeriod === "day" && !base.day) {
103
+ base.day = todayYmd;
104
+ }
105
+ if (nextPeriod === "custom") {
106
+ if (!base.customFrom)
107
+ base.customFrom = todayYmd;
108
+ if (!base.customTo)
109
+ base.customTo = todayYmd;
110
+ }
111
+ onChange(base);
112
+ };
113
+ const periodLabel = () => {
114
+ if (period === "month")
115
+ return `${MONTHS_NL[month]} ${monthYear}`;
116
+ if (period === "day")
117
+ return formatDayNL(day);
118
+ if (period === "last7")
119
+ return "Afgelopen 7 dagen";
120
+ if (period === "last30")
121
+ return "Afgelopen 30 dagen";
122
+ if (period === "custom")
123
+ return "Aangepast tijdsbestek";
124
+ return `Week ${week}, ${year}`;
32
125
  };
33
- return (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("div", { className: "flex h-9 items-center gap-1 rounded-md border border-border bg-card px-1", children: [_jsx("button", { type: "button", onClick: () => shiftWeek(-1), className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground", "aria-label": "Vorige week", children: _jsx(ChevronLeft, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: () => shiftWeek(1), className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground", "aria-label": "Volgende week", children: _jsx(ChevronRight, { className: "h-4 w-4" }) }), _jsxs("div", { className: "px-2 text-sm font-medium text-foreground whitespace-nowrap", children: ["Week ", week, " - ", year] }), _jsx("button", { type: "button", className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground", "aria-label": "Meer", children: _jsx(MoreVertical, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "flex h-9 items-center rounded-md border border-border bg-card", children: [_jsx("button", { type: "button", className: "inline-flex h-9 w-9 items-center justify-center rounded-l-md border-r border-border bg-accent text-foreground", "aria-label": "Bestellingen", children: _jsx(ShoppingCart, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", className: "inline-flex h-9 w-9 items-center justify-center border-r border-border text-muted-foreground hover:bg-accent hover:text-foreground", "aria-label": "Leveringen", children: _jsx(Truck, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", className: "inline-flex h-9 w-9 items-center justify-center rounded-r-md text-muted-foreground hover:bg-accent hover:text-foreground", "aria-label": "Filter", children: _jsx(Filter, { className: "h-4 w-4" }) })] }), _jsxs(Select, { value: value.handelsvorm ?? "all", onValueChange: (v) => onChange({ ...value, handelsvorm: v === "all" ? undefined : v }), children: [_jsx(SelectTrigger, { className: "h-9 w-40", children: _jsx(SelectValue, { placeholder: "- Handelsvorm -" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "- Handelsvorm -" }), _jsx(SelectItem, { value: "klok", children: "Klok" }), _jsx(SelectItem, { value: "direct", children: "Direct" })] })] }), _jsxs(Select, { value: value.klant ?? "all", onValueChange: (v) => onChange({ ...value, klant: v === "all" ? undefined : v }), children: [_jsx(SelectTrigger, { className: "h-9 w-44", children: _jsx(SelectValue, { placeholder: "- Alle klanten -" }) }), _jsx(SelectContent, { children: _jsx(SelectItem, { value: "all", children: "- Alle klanten -" }) })] }), _jsxs(Select, { value: value.voorraadlocatie ?? "all", onValueChange: (v) => onChange({ ...value, voorraadlocatie: v === "all" ? undefined : v }), children: [_jsx(SelectTrigger, { className: "h-9 w-52", children: _jsx(SelectValue, { placeholder: "- Alle voorraadlocaties -" }) }), _jsx(SelectContent, { children: _jsx(SelectItem, { value: "all", children: "- Alle voorraadlocaties -" }) })] }), _jsxs(Select, { value: value.status ?? "all", onValueChange: (v) => onChange({ ...value, status: v === "all" ? undefined : v }), children: [_jsx(SelectTrigger, { className: "h-9 w-36", children: _jsx(SelectValue, { placeholder: "Status" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle statussen" }), _jsx(SelectItem, { value: "open", children: "Open" }), _jsx(SelectItem, { value: "shipped", children: "Verzonden" }), _jsx(SelectItem, { value: "invoiced", children: "Gefactureerd" }), _jsx(SelectItem, { value: "cancelled", children: "Geannuleerd" })] })] }), _jsxs("div", { className: "flex h-9 items-center overflow-hidden rounded-md border border-border bg-card", children: [_jsx(Input, { placeholder: "Op briefnummer zoeken", value: search, onChange: (e) => setSearch(e.target.value), onKeyDown: (e) => {
126
+ const showArrows = period === "week" || period === "month" || period === "day";
127
+ return (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("div", { className: "flex h-9 items-center gap-1 rounded-md border border-border bg-card px-1", children: [showArrows && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => shiftPeriod(-1), className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground", "aria-label": "Vorige", children: _jsx(ChevronLeft, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: () => shiftPeriod(1), className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground", "aria-label": "Volgende", children: _jsx(ChevronRight, { className: "h-4 w-4" }) })] })), period === "month" ? (_jsxs(Select, { value: `${monthYear}-${month}`, onValueChange: (v) => {
128
+ const [y, m] = v.split("-").map(Number);
129
+ onChange({ ...value, month: m, monthYear: y });
130
+ }, children: [_jsx(SelectTrigger, { className: "h-7 w-36 border-0 bg-transparent px-2 text-sm font-medium text-foreground shadow-none focus:ring-0", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: monthOptions.map((opt) => (_jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))) })] })) : period === "day" ? (_jsx(Input, { type: "date", value: day, onChange: (e) => onChange({ ...value, day: e.target.value }), className: "h-7 w-40 border-0 bg-transparent px-2 text-sm font-medium text-foreground shadow-none focus-visible:ring-0" })) : period === "custom" ? (_jsxs("div", { className: "flex items-center gap-1 px-2", children: [_jsx(Input, { type: "date", value: value.customFrom ?? todayYmd, onChange: (e) => onChange({ ...value, customFrom: e.target.value }), className: "h-7 w-36 border-0 bg-transparent px-1 text-sm font-medium text-foreground shadow-none focus-visible:ring-0" }), _jsx("span", { className: "text-muted-foreground", children: "\u2013" }), _jsx(Input, { type: "date", value: value.customTo ?? todayYmd, onChange: (e) => onChange({ ...value, customTo: e.target.value }), className: "h-7 w-36 border-0 bg-transparent px-1 text-sm font-medium text-foreground shadow-none focus-visible:ring-0" })] })) : period === "last7" || period === "last30" ? (_jsx("span", { className: "px-2 text-sm font-medium text-foreground whitespace-nowrap", children: period === "last7" ? "Afgelopen 7 dagen" : "Afgelopen 30 dagen" })) : (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "px-2 text-sm font-medium text-foreground whitespace-nowrap rounded hover:bg-accent h-7", "aria-label": "Kies week", children: ["Week ", week, " - ", year] }) }), _jsx(PopoverContent, { className: "w-auto p-0 pointer-events-auto", align: "start", children: _jsx(WeekCalendar, { week: week, year: year, onSelect: (w, y) => onChange({ ...value, week: w, year: y }) }) })] })), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground", "aria-label": "Periode-opties", children: _jsx(MoreVertical, { className: "h-4 w-4" }) }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [_jsxs(DropdownMenuRadioGroup, { value: period, onValueChange: (v) => handlePeriodChange(v), children: [_jsx(DropdownMenuRadioItem, { value: "day", children: "Dag" }), _jsx(DropdownMenuRadioItem, { value: "week", children: "Week" }), _jsx(DropdownMenuRadioItem, { value: "month", children: "Maand" }), _jsx(DropdownMenuRadioItem, { value: "last7", children: "Afgelopen 7 dagen" }), _jsx(DropdownMenuRadioItem, { value: "last30", children: "Afgelopen 30 dagen" }), _jsx(DropdownMenuRadioItem, { value: "custom", children: "Aangepast tijdsbestek" })] }), _jsx(DropdownMenuSeparator, {}), _jsxs("div", { className: "px-2 py-1.5 text-sm text-muted-foreground inline-flex items-center gap-1", children: [periodLabel(), _jsx(ChevronDown, { className: "h-3.5 w-3.5" })] })] })] })] }), _jsxs("div", { className: "flex h-9 items-center rounded-md border border-border bg-card", children: [_jsx("button", { type: "button", onClick: () => onChange({ ...value, dateField: "ordered" }), className: "inline-flex h-9 w-9 items-center justify-center rounded-l-md border-r border-border " +
131
+ ((value.dateField ?? "ordered") === "ordered"
132
+ ? "bg-accent text-foreground"
133
+ : "text-muted-foreground hover:bg-accent hover:text-foreground"), "aria-label": "Filter op besteldatum", title: "Filter op besteldatum", "aria-pressed": (value.dateField ?? "ordered") === "ordered", children: _jsx(ShoppingCart, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: () => onChange({ ...value, dateField: "delivery" }), className: "inline-flex h-9 w-9 items-center justify-center rounded-r-md " +
134
+ (value.dateField === "delivery"
135
+ ? "bg-accent text-foreground"
136
+ : "text-muted-foreground hover:bg-accent hover:text-foreground"), "aria-label": "Filter op transportdatum", title: "Filter op transportdatum", "aria-pressed": value.dateField === "delivery", children: _jsx(Truck, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "flex h-9 items-center overflow-hidden rounded-md border border-border bg-card", children: [_jsx(Input, { placeholder: "Op briefnummer zoeken", value: search, onChange: (e) => setSearch(e.target.value), onKeyDown: (e) => {
34
137
  if (e.key === "Enter")
35
138
  onChange({ ...value, search });
36
- }, onBlur: () => onChange({ ...value, search }), className: "h-9 w-56 border-0 shadow-none focus-visible:ring-0" }), _jsx("button", { type: "button", onClick: () => onChange({ ...value, search }), className: "inline-flex h-9 w-10 items-center justify-center bg-primary text-primary-foreground hover:opacity-90", "aria-label": "Zoeken", children: _jsx(Search, { className: "h-4 w-4" }) })] }), (value.search || value.status || value.handelsvorm || value.klant || value.voorraadlocatie) && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
37
- setSearch("");
38
- onChange({ week, year });
39
- }, children: "Wissen" }))] }));
139
+ }, onBlur: () => onChange({ ...value, search }), className: "h-9 w-56 border-0 shadow-none focus-visible:ring-0" }), _jsx("button", { type: "button", onClick: () => onChange({ ...value, search }), className: "inline-flex h-9 w-10 items-center justify-center bg-primary text-primary-foreground hover:opacity-90", "aria-label": "Zoeken", children: _jsx(Search, { className: "h-4 w-4" }) })] })] }));
40
140
  }
@@ -1,6 +1,7 @@
1
- import type { SalesOrderDTO } from "../../lib/floriday/sales-orders.functions";
2
- export declare function SalesOrdersTable({ orders, loading, }: {
1
+ import type { SalesOrderDTO } from "../../lib/salesorders-mapping";
2
+ export declare function SalesOrdersTable({ orders, loading, onSelectOrder, }: {
3
3
  orders: SalesOrderDTO[];
4
4
  loading?: boolean;
5
+ onSelectOrder?: (order: SalesOrderDTO) => void;
5
6
  }): import("react/jsx-runtime").JSX.Element;
6
7
  //# sourceMappingURL=SalesOrdersTable.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SalesOrdersTable.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrdersTable.tsx"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AA6E/E,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,OAAO,GACR,EAAE;IACD,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,2CAkJA"}
1
+ {"version":3,"file":"SalesOrdersTable.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrdersTable.tsx"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AA6EnE,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,OAAO,EACP,aAAa,GACd,EAAE;IACD,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAChD,2CA6JA"}
@@ -64,7 +64,7 @@ function StatusBadges({ status }) {
64
64
  return (_jsx("div", { className: "flex items-center gap-1", children: items.map((it) => (_jsx("span", { title: it.label, className: "inline-flex h-7 w-7 items-center justify-center rounded-md " +
65
65
  (it.active ? it.classes : "bg-muted text-muted-foreground/40"), children: _jsx(it.icon, { className: "h-4 w-4" }) }, it.key))) }));
66
66
  }
67
- export function SalesOrdersTable({ orders, loading, }) {
67
+ export function SalesOrdersTable({ orders, loading, onSelectOrder, }) {
68
68
  const [selected, setSelected] = useState(new Set());
69
69
  const allChecked = orders.length > 0 && selected.size === orders.length;
70
70
  const toggle = (id) => setSelected((s) => {
@@ -76,8 +76,8 @@ export function SalesOrdersTable({ orders, loading, }) {
76
76
  Array.from({ length: 6 }).map((_, i) => (_jsx(TableRow, { children: Array.from({ length: 8 }).map((__, j) => (_jsx(TableCell, { children: _jsx(Skeleton, { className: "h-6 w-full" }) }, j))) }, `s-${i}`))), !loading && orders.length === 0 && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 8, className: "py-12 text-center text-muted-foreground", children: "Geen verkooporders gevonden." }) })), !loading &&
77
77
  orders.map((o) => {
78
78
  const ordered = formatDateShort(o.orderedAt);
79
- return (_jsxs(TableRow, { className: "align-top", children: [_jsx(TableCell, { children: _jsx(Checkbox, { checked: selected.has(o.id), onCheckedChange: () => toggle(o.id) }) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-md bg-muted", children: o.imageUrl ? (
79
+ return (_jsxs(TableRow, { className: "align-top cursor-pointer", onClick: () => onSelectOrder?.(o), children: [_jsx(TableCell, { onClick: (e) => e.stopPropagation(), children: _jsx(Checkbox, { checked: selected.has(o.id), onCheckedChange: () => toggle(o.id) }) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-md bg-muted", children: o.imageUrl ? (
80
80
  // eslint-disable-next-line @next/next/no-img-element
81
- _jsx("img", { src: o.imageUrl, alt: "", className: "h-full w-full object-cover" })) : (_jsx(ImageIcon, { className: "h-5 w-5 text-muted-foreground" })) }), _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate font-medium text-foreground", children: o.productName }), _jsx("div", { className: "truncate text-xs text-muted-foreground", children: o.sku || o.orderNumber })] })] }) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Package, { className: "h-4 w-4 text-primary" }), _jsxs("div", { children: [_jsx("div", { className: "font-medium text-foreground", children: ordered.date }), _jsx("div", { className: "text-xs text-muted-foreground", children: ordered.time })] })] }) }), _jsxs(TableCell, { children: [_jsxs("div", { className: "font-medium text-foreground", children: [o.quantity.multiplier, " \u00D7 ", o.quantity.count, " \u00D7 ", eur.format(o.quantity.unitPrice)] }), _jsx("div", { className: "text-xs text-muted-foreground", children: eur.format(o.revenue) })] }), _jsxs(TableCell, { children: [_jsx("div", { className: "font-medium text-foreground", children: o.customer.name }), _jsx("div", { className: "text-xs text-muted-foreground", children: [o.customer.postalCode, o.customer.city].filter(Boolean).join(" ") })] }), _jsx(TableCell, { className: "text-sm text-foreground", children: formatDateLong(o.deliveryAt) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(StatusBadges, { status: o.status }), o.orderNumber && (_jsx(Badge, { variant: "outline", className: "font-mono text-xs", children: o.orderNumber }))] }) }), _jsx(TableCell, { children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { className: "inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", children: _jsx(MoreVertical, { className: "h-4 w-4" }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuItem, { children: "Bewerken" }), _jsx(DropdownMenuItem, { children: "Details bekijken" }), _jsx(DropdownMenuItem, { children: "Bevestigen" }), _jsx(DropdownMenuItem, { className: "text-destructive focus:text-destructive", children: "Annuleren" })] })] }) })] }, o.id));
81
+ _jsx("img", { src: o.imageUrl, alt: "", className: "h-full w-full object-cover" })) : (_jsx(ImageIcon, { className: "h-5 w-5 text-muted-foreground" })) }), _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate font-medium text-foreground", children: o.productName }), _jsx("div", { className: "truncate text-xs text-muted-foreground", children: o.sku || o.orderNumber })] })] }) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Package, { className: "h-4 w-4 text-primary" }), _jsxs("div", { children: [_jsx("div", { className: "font-medium text-foreground", children: ordered.date }), _jsx("div", { className: "text-xs text-muted-foreground", children: ordered.time })] })] }) }), _jsxs(TableCell, { children: [_jsxs("div", { className: "font-medium text-foreground", children: [o.quantity.multiplier, " \u00D7 ", o.quantity.count, " \u00D7 ", eur.format(o.quantity.unitPrice)] }), _jsx("div", { className: "text-xs text-muted-foreground", children: eur.format(o.revenue) })] }), _jsxs(TableCell, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "font-medium text-foreground", children: o.customer.name }), o.isLocal && (_jsx(Badge, { variant: "outline", className: "border-primary/30 text-primary", children: "Lokaal" }))] }), _jsx("div", { className: "text-xs text-muted-foreground", children: [o.customer.postalCode, o.customer.city].filter(Boolean).join(" ") })] }), _jsx(TableCell, { className: "text-sm text-foreground", children: formatDateLong(o.deliveryAt) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(StatusBadges, { status: o.status }), o.orderNumber && (_jsx(Badge, { variant: "outline", className: "font-mono text-xs", children: o.orderNumber }))] }) }), _jsx(TableCell, { onClick: (e) => e.stopPropagation(), children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { className: "inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", children: _jsx(MoreVertical, { className: "h-4 w-4" }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuItem, { children: "Bewerken" }), _jsx(DropdownMenuItem, { children: "Details bekijken" }), _jsx(DropdownMenuItem, { children: "Bevestigen" }), _jsx(DropdownMenuItem, { className: "text-destructive focus:text-destructive", children: "Annuleren" })] })] }) })] }, o.id));
82
82
  })] })] }) }));
83
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"VerkoopOrdersPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/VerkoopOrdersPage.tsx"],"names":[],"mappings":"AAyBA,wBAAgB,iBAAiB,4CAwGhC"}
1
+ {"version":3,"file":"VerkoopOrdersPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/VerkoopOrdersPage.tsx"],"names":[],"mappings":"AA4DA,wBAAgB,iBAAiB,4CAwRhC"}
@@ -1,40 +1,101 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo, useState } from "react";
3
- import { useQuery } from "@tanstack/react-query";
2
+ import { useEffect, useMemo, useState } from "react";
3
+ import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
4
+ import { Download, Plus, AlertTriangle, RefreshCw, ChevronLeft, ChevronRight, Filter, } from "lucide-react";
4
5
  import { useServerFn } from "@tanstack/react-start";
5
- import { Button, toast } from "@flowselections/core";
6
- import { Download, Plus, AlertTriangle } from "lucide-react";
7
- import { listSalesOrders, exportSalesOrders, } from "../../lib/floriday/sales-orders.functions";
6
+ import { useNavigate } from "@tanstack/react-router";
7
+ import { runSalesOrdersSync } from "../../lib/salesorders.functions";
8
+ import { runTradeItemsSync } from "../../lib/tradeitems.functions";
9
+ import { applyFilters, computeTotals, ordersToCsv, } from "../../lib/salesorders-mapping";
10
+ import { useSalesOrdersQuery } from "../../lib/useSalesOrdersQuery";
8
11
  import { AnalyticsCards } from "./AnalyticsCards";
9
- import { SalesOrdersFilters, } from "./SalesOrdersFilters";
12
+ import { SalesOrdersFilters } from "./SalesOrdersFilters";
13
+ import { SalesOrdersFilterPanel, countActiveFilters, } from "./SalesOrdersFilterPanel";
10
14
  import { SalesOrdersTable } from "./SalesOrdersTable";
11
- import { CreateOrderDialog } from "./CreateOrderDialog";
12
- const TABS = [
13
- { id: "overzicht", label: "Overzicht" },
14
- { id: "correcties", label: "Correcties" },
15
- { id: "geplande", label: "Geplande orders", badge: 2 },
15
+ const STATUS_TABS = [
16
+ { id: "all", label: "Alle" },
17
+ { id: "open", label: "Open" },
18
+ { id: "shipped", label: "Verzonden" },
19
+ { id: "invoiced", label: "Gefactureerd" },
20
+ { id: "cancelled", label: "Geannuleerd" },
16
21
  ];
22
+ function matchesStatus(o, tab) {
23
+ if (tab === "all")
24
+ return true;
25
+ if (tab === "cancelled")
26
+ return o.status.isCancelled;
27
+ if (tab === "shipped")
28
+ return o.status.isShipped;
29
+ if (tab === "invoiced")
30
+ return o.status.isInvoiced;
31
+ if (tab === "open")
32
+ return !o.status.isCancelled && !o.status.isShipped;
33
+ return true;
34
+ }
17
35
  export function VerkoopOrdersPage() {
18
- const [tab, setTab] = useState("overzicht");
19
- const [filters, setFilters] = useState({});
20
- const [createOpen, setCreateOpen] = useState(false);
21
- const fetchOrders = useServerFn(listSalesOrders);
22
- const exportFn = useServerFn(exportSalesOrders);
23
- const apiFilters = useMemo(() => ({
24
- search: filters.search,
25
- status: filters.status,
26
- from: filters.from,
27
- to: filters.to,
28
- }), [filters]);
29
- const { data, isLoading, error, refetch, isFetching } = useQuery({
30
- queryKey: ["floriday", "sales-orders", apiFilters],
31
- queryFn: () => fetchOrders({ data: apiFilters }),
32
- staleTime: 30000,
36
+ const [tab, setTab] = useState("all");
37
+ const [filterPanelOpen, setFilterPanelOpen] = useState(false);
38
+ const [filters, setFilters] = useState(() => {
39
+ const now = new Date();
40
+ const target = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()));
41
+ const dayNr = (target.getUTCDay() + 6) % 7;
42
+ target.setUTCDate(target.getUTCDate() - dayNr + 3);
43
+ const firstThursday = new Date(Date.UTC(target.getUTCFullYear(), 0, 4));
44
+ const week = 1 +
45
+ Math.round(((target.getTime() - firstThursday.getTime()) / 86400000 -
46
+ 3 +
47
+ ((firstThursday.getUTCDay() + 6) % 7)) /
48
+ 7);
49
+ return { week, year: target.getUTCFullYear(), dateField: "ordered", period: "week" };
33
50
  });
34
- const handleExport = async () => {
51
+ const [syncing, setSyncing] = useState(false);
52
+ const [page, setPage] = useState(1);
53
+ const [pageSize, setPageSize] = useState(20);
54
+ const navigate = useNavigate();
55
+ const triggerSync = useServerFn(runSalesOrdersSync);
56
+ const triggerTradeItemsSync = useServerFn(runTradeItemsSync);
57
+ const { data, isLoading, error, refetch, isFetching } = useSalesOrdersQuery();
58
+ const orders = data?.orders ?? [];
59
+ const statusFiltered = useMemo(() => orders.filter((o) => matchesStatus(o, tab)), [orders, tab]);
60
+ const filtered = useMemo(() => applyFilters(statusFiltered, filters), [statusFiltered, filters]);
61
+ const totals = useMemo(() => computeTotals(filtered), [filtered]);
62
+ const statusCounts = useMemo(() => {
63
+ const base = applyFilters(orders, filters);
64
+ const counts = {
65
+ all: base.length,
66
+ open: 0,
67
+ shipped: 0,
68
+ invoiced: 0,
69
+ cancelled: 0,
70
+ };
71
+ for (const o of base) {
72
+ if (o.status.isCancelled)
73
+ counts.cancelled++;
74
+ else if (o.status.isShipped)
75
+ counts.shipped++;
76
+ else
77
+ counts.open++;
78
+ if (o.status.isInvoiced)
79
+ counts.invoiced++;
80
+ }
81
+ return counts;
82
+ }, [orders, filters]);
83
+ const activeFilterCount = countActiveFilters(filters);
84
+ const totalCount = filtered.length;
85
+ const totalPages = Math.max(1, Math.ceil(totalCount / pageSize));
86
+ useEffect(() => {
87
+ if (page > totalPages)
88
+ setPage(totalPages);
89
+ }, [page, totalPages]);
90
+ // Reset naar pagina 1 als filters wijzigen
91
+ useEffect(() => {
92
+ setPage(1);
93
+ }, [filters, pageSize]);
94
+ const paged = useMemo(() => filtered.slice((page - 1) * pageSize, page * pageSize), [filtered, page, pageSize]);
95
+ const handleExport = () => {
35
96
  try {
36
- const { csv, filename } = await exportFn({ data: apiFilters });
37
- const blob = new Blob(["" + csv], { type: "text/csv;charset=utf-8" });
97
+ const { csv, filename } = ordersToCsv(filtered);
98
+ const blob = new Blob(["\ufeff" + csv], { type: "text/csv;charset=utf-8" });
38
99
  const url = URL.createObjectURL(blob);
39
100
  const a = document.createElement("a");
40
101
  a.href = url;
@@ -47,11 +108,41 @@ export function VerkoopOrdersPage() {
47
108
  toast.error(e?.message ?? "Export mislukt");
48
109
  }
49
110
  };
50
- return (_jsxs("main", { className: "space-y-5 p-6", children: [_jsx("div", { className: "flex items-center justify-between border-b border-border", children: _jsx("div", { className: "flex items-center gap-6", children: TABS.map((t) => {
111
+ const handleSync = async () => {
112
+ try {
113
+ setSyncing(true);
114
+ const [orders, items] = await Promise.all([
115
+ triggerSync(),
116
+ triggerTradeItemsSync(),
117
+ ]);
118
+ const syncedOrders = orders.results.reduce((s, x) => s + x.synced, 0);
119
+ const syncedItems = items.results.reduce((s, x) => s + x.synced, 0);
120
+ toast.success(`Sync klaar — ${syncedOrders} order(s), ${syncedItems} artikel(en) bijgewerkt`);
121
+ await refetch();
122
+ }
123
+ catch (e) {
124
+ toast.error(e?.message ?? "Sync mislukt");
125
+ }
126
+ finally {
127
+ setSyncing(false);
128
+ }
129
+ };
130
+ return (_jsxs("main", { className: "space-y-5 p-6", children: [_jsx("div", { className: "flex items-center justify-between border-b border-border", children: _jsx("div", { className: "flex items-center gap-6 overflow-x-auto", children: STATUS_TABS.map((t) => {
51
131
  const active = tab === t.id;
52
- return (_jsxs("button", { type: "button", onClick: () => setTab(t.id), className: "relative -mb-px flex items-center gap-2 border-b-2 px-1 pb-3 pt-1 text-sm transition-colors " +
132
+ const count = statusCounts[t.id];
133
+ return (_jsxs("button", { type: "button", onClick: () => setTab(t.id), className: "relative -mb-px flex items-center gap-2 whitespace-nowrap border-b-2 px-1 pb-3 pt-1 text-sm transition-colors " +
53
134
  (active
54
135
  ? "border-primary font-semibold text-foreground"
55
- : "border-transparent text-muted-foreground hover:text-foreground"), children: [_jsx("span", { children: t.label }), t.badge ? (_jsx("span", { className: "inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1.5 text-[11px] font-semibold text-primary-foreground", children: t.badge })) : null] }, t.id));
56
- }) }) }), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx(SalesOrdersFilters, { value: filters, onChange: setFilters }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: handleExport, children: [_jsx(Download, { className: "mr-2 h-4 w-4" }), " Exporteren"] }), _jsxs(Button, { size: "sm", onClick: () => setCreateOpen(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Bestelling aanmaken"] })] })] }), _jsx(AnalyticsCards, { totals: data?.totals, loading: isLoading }), error && (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0 text-destructive" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-medium text-destructive", children: "Floriday gegevens konden niet geladen worden" }), _jsx("div", { className: "mt-1 text-muted-foreground", children: error.message })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => refetch(), disabled: isFetching, children: "Opnieuw proberen" })] })), _jsx(SalesOrdersTable, { orders: data?.orders ?? [], loading: isLoading }), _jsx(CreateOrderDialog, { open: createOpen, onOpenChange: setCreateOpen })] }));
136
+ : "border-transparent text-muted-foreground hover:text-foreground"), children: [_jsx("span", { children: t.label }), _jsx("span", { className: "inline-flex h-5 min-w-5 items-center justify-center rounded-full px-1.5 text-[11px] font-semibold " +
137
+ (active
138
+ ? "bg-primary text-primary-foreground"
139
+ : "bg-muted text-muted-foreground"), children: count })] }, t.id));
140
+ }) }) }), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx(SalesOrdersFilters, { value: filters, onChange: setFilters }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setFilterPanelOpen(true), "aria-label": "Filters", children: [_jsx(Filter, { className: "mr-2 h-4 w-4" }), "Filters", activeFilterCount > 0 && (_jsx("span", { className: "ml-2 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1.5 text-[11px] font-semibold text-primary-foreground", children: activeFilterCount }))] }), _jsx(Button, { variant: "outline", size: "icon", onClick: handleSync, disabled: syncing, "aria-label": "Synchroniseren", title: "Synchroniseren", children: _jsx(RefreshCw, { className: "h-4 w-4 " + (syncing ? "animate-spin" : "") }) }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleExport, children: [_jsx(Download, { className: "mr-2 h-4 w-4" }), " Exporteren"] }), _jsxs(Button, { size: "sm", onClick: () => navigate({ to: "/floriday-verkoop/verkooporders/nieuw" }), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Bestelling aanmaken"] })] })] }), _jsx(AnalyticsCards, { totals: totals, loading: isLoading }), error && (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0 text-destructive" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-medium text-destructive", children: "Floriday gegevens konden niet geladen worden" }), _jsx("div", { className: "mt-1 text-muted-foreground", children: error.message })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => refetch(), disabled: isFetching, children: "Opnieuw proberen" })] })), _jsx(SalesOrdersTable, { orders: paged, loading: isLoading, onSelectOrder: (o) => navigate({
141
+ to: "/floriday-verkoop/verkooporders/$salesOrderId",
142
+ params: { salesOrderId: o.id },
143
+ }) }), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 text-sm text-muted-foreground", children: [_jsx("div", { children: totalCount === 0
144
+ ? "Geen resultaten"
145
+ : totalCount === 1
146
+ ? "1 resultaat"
147
+ : `${totalCount} resultaten` }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { children: "Resultaten per pagina" }), _jsxs(Select, { value: String(pageSize), onValueChange: (v) => setPageSize(Number(v)), children: [_jsx(SelectTrigger, { className: "h-8 w-20", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: [10, 20, 50, 100].map((n) => (_jsx(SelectItem, { value: String(n), children: n }, n))) })] })] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Button, { variant: "outline", size: "icon", className: "h-8 w-8", onClick: () => setPage((p) => Math.max(1, p - 1)), disabled: page <= 1, "aria-label": "Vorige pagina", children: _jsx(ChevronLeft, { className: "h-4 w-4" }) }), _jsxs("div", { className: "px-2 text-foreground", children: [page, " / ", totalPages] }), _jsx(Button, { variant: "outline", size: "icon", className: "h-8 w-8", onClick: () => setPage((p) => Math.min(totalPages, p + 1)), disabled: page >= totalPages, "aria-label": "Volgende pagina", children: _jsx(ChevronRight, { className: "h-4 w-4" }) })] })] })] }), _jsx(SalesOrdersFilterPanel, { open: filterPanelOpen, onOpenChange: setFilterPanelOpen, value: filters, onChange: setFilters })] }));
57
148
  }
@@ -0,0 +1,6 @@
1
+ export declare function WeekCalendar({ week, year, onSelect, }: {
2
+ week: number;
3
+ year: number;
4
+ onSelect: (week: number, year: number) => void;
5
+ }): import("react/jsx-runtime").JSX.Element;
6
+ //# sourceMappingURL=WeekCalendar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WeekCalendar.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/WeekCalendar.tsx"],"names":[],"mappings":"AAiDA,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChD,2CAqJA"}