@flowselections/actie-mail-kwekers-erp-mathot 1.0.0
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.
- package/dist-lib/_core-safelist.d.ts +2 -0
- package/dist-lib/_core-safelist.d.ts.map +1 -0
- package/dist-lib/_core-safelist.js +15 -0
- package/dist-lib/components/AanbiedingsmailtjePage.d.ts +2 -0
- package/dist-lib/components/AanbiedingsmailtjePage.d.ts.map +1 -0
- package/dist-lib/components/AanbiedingsmailtjePage.js +239 -0
- package/dist-lib/components/ActiePage.d.ts +2 -0
- package/dist-lib/components/ActiePage.d.ts.map +1 -0
- package/dist-lib/components/ActiePage.js +173 -0
- package/dist-lib/components/CampagneDetail.d.ts +2 -0
- package/dist-lib/components/CampagneDetail.d.ts.map +1 -0
- package/dist-lib/components/CampagneDetail.js +61 -0
- package/dist-lib/components/CampagneOverzicht.d.ts +2 -0
- package/dist-lib/components/CampagneOverzicht.d.ts.map +1 -0
- package/dist-lib/components/CampagneOverzicht.js +16 -0
- package/dist-lib/components/DateRangePicker.d.ts +20 -0
- package/dist-lib/components/DateRangePicker.d.ts.map +1 -0
- package/dist-lib/components/DateRangePicker.js +140 -0
- package/dist-lib/components/RapportagePage.d.ts +2 -0
- package/dist-lib/components/RapportagePage.d.ts.map +1 -0
- package/dist-lib/components/RapportagePage.js +60 -0
- package/dist-lib/components/settings/AfzenderSettingsCard.d.ts +2 -0
- package/dist-lib/components/settings/AfzenderSettingsCard.d.ts.map +1 -0
- package/dist-lib/components/settings/AfzenderSettingsCard.js +30 -0
- package/dist-lib/components/settings/HuisstijlSettingsCard.d.ts +2 -0
- package/dist-lib/components/settings/HuisstijlSettingsCard.d.ts.map +1 -0
- package/dist-lib/components/settings/HuisstijlSettingsCard.js +30 -0
- package/dist-lib/components/settings/StickerSettingsCard.d.ts +2 -0
- package/dist-lib/components/settings/StickerSettingsCard.d.ts.map +1 -0
- package/dist-lib/components/settings/StickerSettingsCard.js +41 -0
- package/dist-lib/components/settings/TestmailCard.d.ts +2 -0
- package/dist-lib/components/settings/TestmailCard.d.ts.map +1 -0
- package/dist-lib/components/settings/TestmailCard.js +21 -0
- package/dist-lib/components/settings/TrackingSettingsCard.d.ts +2 -0
- package/dist-lib/components/settings/TrackingSettingsCard.d.ts.map +1 -0
- package/dist-lib/components/settings/TrackingSettingsCard.js +30 -0
- package/dist-lib/components/settings/UitsluitingenSettingsCard.d.ts +2 -0
- package/dist-lib/components/settings/UitsluitingenSettingsCard.d.ts.map +1 -0
- package/dist-lib/components/settings/UitsluitingenSettingsCard.js +41 -0
- package/dist-lib/index.d.ts +17 -0
- package/dist-lib/index.d.ts.map +1 -0
- package/dist-lib/index.js +34 -0
- package/dist-lib/integrations/supabase/auth-attacher.d.ts +2 -0
- package/dist-lib/integrations/supabase/auth-attacher.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/auth-attacher.js +12 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +2555 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/auth-middleware.js +52 -0
- package/dist-lib/integrations/supabase/client.d.ts +2551 -0
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/client.js +13 -0
- package/dist-lib/integrations/supabase/client.server.d.ts +2551 -0
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/client.server.js +30 -0
- package/dist-lib/integrations/supabase/types.d.ts +2685 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/types.js +8 -0
- package/dist-lib/lib/campaigns.functions.d.ts +12881 -0
- package/dist-lib/lib/campaigns.functions.d.ts.map +1 -0
- package/dist-lib/lib/campaigns.functions.js +358 -0
- package/dist-lib/lib/public-offer.functions.d.ts +68 -0
- package/dist-lib/lib/public-offer.functions.d.ts.map +1 -0
- package/dist-lib/lib/public-offer.functions.js +116 -0
- package/dist-lib/lib/stats.functions.d.ts +5180 -0
- package/dist-lib/lib/stats.functions.d.ts.map +1 -0
- package/dist-lib/lib/stats.functions.js +63 -0
- package/dist-lib/lib/utils.d.ts +3 -0
- package/dist-lib/lib/utils.d.ts.map +1 -0
- package/dist-lib/lib/utils.js +5 -0
- package/dist-lib/lib/validationSchemas.d.ts +15 -0
- package/dist-lib/lib/validationSchemas.d.ts.map +1 -0
- package/dist-lib/lib/validationSchemas.js +25 -0
- package/dist-lib/styles.css +1 -0
- package/package.json +76 -0
- package/public/flowselections-assets/template-module/README.md +15 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface DateRange {
|
|
2
|
+
from: Date | null;
|
|
3
|
+
to: Date | null;
|
|
4
|
+
presetId: string;
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
type PresetId = "today" | "yesterday" | "this_week" | "last_week" | "this_month" | "last_month" | "last_30" | "last_60" | "last_90" | "this_quarter" | "last_quarter" | "this_year" | "last_year" | "all" | "custom";
|
|
8
|
+
export declare function computePreset(id: PresetId): {
|
|
9
|
+
from: Date | null;
|
|
10
|
+
to: Date | null;
|
|
11
|
+
label: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function DateRangePicker({ value, onChange }: {
|
|
14
|
+
value: DateRange;
|
|
15
|
+
onChange: (v: DateRange) => void;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
17
|
+
export declare function loadStoredRange(): DateRange;
|
|
18
|
+
export declare function storeRange(r: DateRange): void;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=DateRangePicker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateRangePicker.d.ts","sourceRoot":"","sources":["../../src/components/DateRangePicker.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,KAAK,QAAQ,GACT,OAAO,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GACjD,YAAY,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAC/D,cAAc,GAAG,cAAc,GAAG,WAAW,GAAG,WAAW,GAC3D,KAAK,GAAG,QAAQ,CAAC;AA4BrB,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,GAAG;IAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAAC,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAqBjG;AAeD,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,CAAA;CAAE,+BA+E1G;AAID,wBAAgB,eAAe,IAAI,SAAS,CAuB3C;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,SAAS,QAQtC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Button, Popover, PopoverContent, PopoverTrigger, Label } from "@flowselections/core";
|
|
4
|
+
import { Calendar as CalendarIcon } from "lucide-react";
|
|
5
|
+
const PRESETS = [
|
|
6
|
+
{ id: "today", label: "Vandaag" },
|
|
7
|
+
{ id: "yesterday", label: "Gisteren" },
|
|
8
|
+
{ id: "this_week", label: "Deze week" },
|
|
9
|
+
{ id: "last_week", label: "Vorige week" },
|
|
10
|
+
{ id: "this_month", label: "Deze maand" },
|
|
11
|
+
{ id: "last_month", label: "Vorige maand" },
|
|
12
|
+
{ id: "last_30", label: "Laatste 30 dagen" },
|
|
13
|
+
{ id: "last_60", label: "Laatste 60 dagen" },
|
|
14
|
+
{ id: "last_90", label: "Laatste 90 dagen" },
|
|
15
|
+
{ id: "this_quarter", label: "Dit kwartaal" },
|
|
16
|
+
{ id: "last_quarter", label: "Vorig kwartaal" },
|
|
17
|
+
{ id: "this_year", label: "Dit jaar" },
|
|
18
|
+
{ id: "last_year", label: "Vorig jaar" },
|
|
19
|
+
{ id: "all", label: "Alles" },
|
|
20
|
+
];
|
|
21
|
+
function startOfDay(d) { const x = new Date(d); x.setHours(0, 0, 0, 0); return x; }
|
|
22
|
+
function endOfDay(d) { const x = new Date(d); x.setHours(23, 59, 59, 999); return x; }
|
|
23
|
+
function addDays(d, n) { const x = new Date(d); x.setDate(x.getDate() + n); return x; }
|
|
24
|
+
function startOfWeek(d) { const x = startOfDay(d); const day = (x.getDay() + 6) % 7; return addDays(x, -day); } // ma
|
|
25
|
+
function startOfMonth(d) { return new Date(d.getFullYear(), d.getMonth(), 1); }
|
|
26
|
+
function endOfMonth(d) { return endOfDay(new Date(d.getFullYear(), d.getMonth() + 1, 0)); }
|
|
27
|
+
function startOfQuarter(d) { const q = Math.floor(d.getMonth() / 3); return new Date(d.getFullYear(), q * 3, 1); }
|
|
28
|
+
function endOfQuarter(d) { const q = Math.floor(d.getMonth() / 3); return endOfDay(new Date(d.getFullYear(), q * 3 + 3, 0)); }
|
|
29
|
+
export function computePreset(id) {
|
|
30
|
+
const now = new Date();
|
|
31
|
+
const preset = PRESETS.find((p) => p.id === id);
|
|
32
|
+
const label = preset?.label ?? "Aangepast";
|
|
33
|
+
switch (id) {
|
|
34
|
+
case "today": return { from: startOfDay(now), to: endOfDay(now), label };
|
|
35
|
+
case "yesterday": {
|
|
36
|
+
const y = addDays(now, -1);
|
|
37
|
+
return { from: startOfDay(y), to: endOfDay(y), label };
|
|
38
|
+
}
|
|
39
|
+
case "this_week": return { from: startOfWeek(now), to: endOfDay(now), label };
|
|
40
|
+
case "last_week": {
|
|
41
|
+
const s = addDays(startOfWeek(now), -7);
|
|
42
|
+
return { from: s, to: endOfDay(addDays(s, 6)), label };
|
|
43
|
+
}
|
|
44
|
+
case "this_month": return { from: startOfMonth(now), to: endOfDay(now), label };
|
|
45
|
+
case "last_month": {
|
|
46
|
+
const s = startOfMonth(addDays(startOfMonth(now), -1));
|
|
47
|
+
return { from: s, to: endOfMonth(s), label };
|
|
48
|
+
}
|
|
49
|
+
case "last_30": return { from: startOfDay(addDays(now, -29)), to: endOfDay(now), label };
|
|
50
|
+
case "last_60": return { from: startOfDay(addDays(now, -59)), to: endOfDay(now), label };
|
|
51
|
+
case "last_90": return { from: startOfDay(addDays(now, -89)), to: endOfDay(now), label };
|
|
52
|
+
case "this_quarter": return { from: startOfQuarter(now), to: endOfDay(now), label };
|
|
53
|
+
case "last_quarter": {
|
|
54
|
+
const s = startOfQuarter(addDays(startOfQuarter(now), -1));
|
|
55
|
+
return { from: s, to: endOfQuarter(s), label };
|
|
56
|
+
}
|
|
57
|
+
case "this_year": return { from: new Date(now.getFullYear(), 0, 1), to: endOfDay(now), label };
|
|
58
|
+
case "last_year": return { from: new Date(now.getFullYear() - 1, 0, 1), to: endOfDay(new Date(now.getFullYear() - 1, 11, 31)), label };
|
|
59
|
+
case "all": return { from: null, to: null, label };
|
|
60
|
+
default: return { from: null, to: null, label };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function fmt(d) {
|
|
64
|
+
if (!d)
|
|
65
|
+
return "";
|
|
66
|
+
return d.toLocaleDateString("nl-NL", { day: "2-digit", month: "short", year: "numeric" });
|
|
67
|
+
}
|
|
68
|
+
function toInputDate(d) {
|
|
69
|
+
if (!d)
|
|
70
|
+
return "";
|
|
71
|
+
const y = d.getFullYear();
|
|
72
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
73
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
74
|
+
return `${y}-${m}-${day}`;
|
|
75
|
+
}
|
|
76
|
+
export function DateRangePicker({ value, onChange }) {
|
|
77
|
+
const [open, setOpen] = useState(false);
|
|
78
|
+
const [customFrom, setCustomFrom] = useState(toInputDate(value.from));
|
|
79
|
+
const [customTo, setCustomTo] = useState(toInputDate(value.to));
|
|
80
|
+
const pick = (id) => {
|
|
81
|
+
const r = computePreset(id);
|
|
82
|
+
onChange({ from: r.from, to: r.to, presetId: id, label: r.label });
|
|
83
|
+
setOpen(false);
|
|
84
|
+
};
|
|
85
|
+
const applyCustom = () => {
|
|
86
|
+
if (!customFrom || !customTo)
|
|
87
|
+
return;
|
|
88
|
+
const f = startOfDay(new Date(customFrom));
|
|
89
|
+
const t = endOfDay(new Date(customTo));
|
|
90
|
+
onChange({
|
|
91
|
+
from: f,
|
|
92
|
+
to: t,
|
|
93
|
+
presetId: "custom",
|
|
94
|
+
label: `${fmt(f)} – ${fmt(t)}`,
|
|
95
|
+
});
|
|
96
|
+
setOpen(false);
|
|
97
|
+
};
|
|
98
|
+
const labelText = value.presetId === "all"
|
|
99
|
+
? "Periode: Alles"
|
|
100
|
+
: value.from && value.to
|
|
101
|
+
? `${fmt(value.from)} – ${fmt(value.to)}`
|
|
102
|
+
: value.label || "Periode selecteren";
|
|
103
|
+
return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: "gap-2", children: [_jsx(CalendarIcon, { className: "h-4 w-4" }), labelText] }) }), _jsx(PopoverContent, { align: "end", className: "w-[420px] p-0", children: _jsxs("div", { className: "grid grid-cols-2 gap-0", children: [_jsx("div", { className: "border-r p-2 max-h-[360px] overflow-y-auto", children: PRESETS.map((p) => (_jsx("button", { onClick: () => pick(p.id), className: `w-full text-left px-3 py-1.5 rounded-md text-sm hover:bg-muted ${value.presetId === p.id ? "bg-muted font-medium" : ""}`, children: p.label }, p.id))) }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "text-xs font-semibold text-muted-foreground", children: "Aangepaste periode" }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Van" }), _jsx("input", { type: "date", value: customFrom, onChange: (e) => setCustomFrom(e.target.value), className: "w-full border rounded-md px-2 py-1.5 text-sm" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Tot" }), _jsx("input", { type: "date", value: customTo, onChange: (e) => setCustomTo(e.target.value), className: "w-full border rounded-md px-2 py-1.5 text-sm" })] }), _jsx(Button, { size: "sm", className: "w-full", onClick: applyCustom, disabled: !customFrom || !customTo, children: "Toepassen" })] })] }) })] }));
|
|
104
|
+
}
|
|
105
|
+
const STORAGE_KEY = "aanbiedingsmailtje:reportPeriod";
|
|
106
|
+
export function loadStoredRange() {
|
|
107
|
+
if (typeof window === "undefined") {
|
|
108
|
+
const r = computePreset("last_30");
|
|
109
|
+
return { from: r.from, to: r.to, presetId: "last_30", label: r.label };
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const raw = window.localStorage.getItem(STORAGE_KEY);
|
|
113
|
+
if (raw) {
|
|
114
|
+
const obj = JSON.parse(raw);
|
|
115
|
+
if (obj.presetId && obj.presetId !== "custom") {
|
|
116
|
+
const r = computePreset(obj.presetId);
|
|
117
|
+
return { from: r.from, to: r.to, presetId: obj.presetId, label: r.label };
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
from: obj.from ? new Date(obj.from) : null,
|
|
121
|
+
to: obj.to ? new Date(obj.to) : null,
|
|
122
|
+
presetId: obj.presetId,
|
|
123
|
+
label: obj.label,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch { /* noop */ }
|
|
128
|
+
const r = computePreset("last_30");
|
|
129
|
+
return { from: r.from, to: r.to, presetId: "last_30", label: r.label };
|
|
130
|
+
}
|
|
131
|
+
export function storeRange(r) {
|
|
132
|
+
if (typeof window === "undefined")
|
|
133
|
+
return;
|
|
134
|
+
window.localStorage.setItem(STORAGE_KEY, JSON.stringify({
|
|
135
|
+
presetId: r.presetId,
|
|
136
|
+
from: r.from ? r.from.toISOString() : null,
|
|
137
|
+
to: r.to ? r.to.toISOString() : null,
|
|
138
|
+
label: r.label,
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RapportagePage.d.ts","sourceRoot":"","sources":["../../src/components/RapportagePage.tsx"],"names":[],"mappings":"AAQA,wBAAgB,cAAc,gCAiG7B"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
4
|
+
import { Card, CardContent, CardHeader, CardTitle, Skeleton, Badge, Button } from "@flowselections/core";
|
|
5
|
+
import { getOverallStats } from "../lib/stats.functions";
|
|
6
|
+
import { Mail, Eye, MousePointerClick, ShoppingCart, Euro, BarChart3, ArrowLeft } from "lucide-react";
|
|
7
|
+
import { Link } from "@tanstack/react-router";
|
|
8
|
+
import { DateRangePicker, loadStoredRange, storeRange } from "./DateRangePicker";
|
|
9
|
+
export function RapportagePage() {
|
|
10
|
+
const fn = useServerFn(getOverallStats);
|
|
11
|
+
const [range, setRange] = useState(null);
|
|
12
|
+
const [data, setData] = useState(null);
|
|
13
|
+
const [loading, setLoading] = useState(false);
|
|
14
|
+
useEffect(() => { setRange(loadStoredRange()); }, []);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!range)
|
|
17
|
+
return;
|
|
18
|
+
setLoading(true);
|
|
19
|
+
const payload = {};
|
|
20
|
+
if (range.from)
|
|
21
|
+
payload.from = range.from.toISOString();
|
|
22
|
+
if (range.to)
|
|
23
|
+
payload.to = range.to.toISOString();
|
|
24
|
+
fn({ data: payload }).then((r) => { setData(r); setLoading(false); });
|
|
25
|
+
}, [range]);
|
|
26
|
+
const handleChange = (r) => { storeRange(r); setRange(r); };
|
|
27
|
+
const { topCustomers, topProducts } = useMemo(() => {
|
|
28
|
+
if (!data)
|
|
29
|
+
return { topCustomers: [], topProducts: [] };
|
|
30
|
+
const byCustomer = new Map();
|
|
31
|
+
for (const o of data.orders) {
|
|
32
|
+
const key = o.customer_id ?? o.customer_name;
|
|
33
|
+
const cur = byCustomer.get(key) ?? { name: o.customer_name, orders: 0, revenue: 0 };
|
|
34
|
+
cur.orders += 1;
|
|
35
|
+
cur.revenue += Number(o.total_amount ?? 0);
|
|
36
|
+
byCustomer.set(key, cur);
|
|
37
|
+
}
|
|
38
|
+
const byProduct = new Map();
|
|
39
|
+
for (const o of data.orders) {
|
|
40
|
+
for (const it of (Array.isArray(o.items) ? o.items : [])) {
|
|
41
|
+
const cur = byProduct.get(it.product_name) ?? { name: it.product_name, qty: 0, revenue: 0 };
|
|
42
|
+
cur.qty += Number(it.quantity ?? 0);
|
|
43
|
+
cur.revenue += Number(it.quantity ?? 0) * Number(it.unit_price ?? 0);
|
|
44
|
+
byProduct.set(it.product_name, cur);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
topCustomers: [...byCustomer.values()].sort((a, b) => b.revenue - a.revenue).slice(0, 10),
|
|
49
|
+
topProducts: [...byProduct.values()].sort((a, b) => b.qty - a.qty).slice(0, 10),
|
|
50
|
+
};
|
|
51
|
+
}, [data]);
|
|
52
|
+
if (!range || !data)
|
|
53
|
+
return _jsx("div", { className: "p-6", children: _jsx(Skeleton, { className: "h-64" }) });
|
|
54
|
+
const t = data.totals;
|
|
55
|
+
const totalRecords = t.sent + t.orders;
|
|
56
|
+
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsx(Link, { to: "/aanbiedingsmailtje", children: _jsxs(Button, { variant: "ghost", size: "sm", className: "-ml-2", children: [_jsx(ArrowLeft, { className: "h-4 w-4 mr-1" }), "Terug"] }) }), _jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(BarChart3, { className: "h-6 w-6 text-primary" }), _jsx("h2", { className: "text-2xl font-bold", children: "Rapportage" })] }), range && _jsx(DateRangePicker, { value: range, onChange: handleChange })] }), _jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx("span", { children: "Actieve periode:" }), _jsx(Badge, { variant: "secondary", className: "font-medium", children: range.label }), _jsxs("span", { className: "text-xs", children: ["\u00B7 ", totalRecords, " records ", loading && "(bijwerken…)"] })] }), _jsxs("div", { className: "grid grid-cols-2 md:grid-cols-6 gap-3", children: [_jsx(Stat, { icon: _jsx(Mail, {}), label: "Verzonden", value: t.sent }), _jsx(Stat, { icon: _jsx(Eye, {}), label: "Geopend", value: t.opened }), _jsx(Stat, { icon: _jsx(MousePointerClick, {}), label: "Klikken", value: t.clicked }), _jsx(Stat, { icon: _jsx(ShoppingCart, {}), label: "Bestellingen", value: t.orders }), _jsx(Stat, { icon: _jsx(Euro, {}), label: "Omzet", value: `€ ${t.revenue.toFixed(2)}` }), _jsx(Stat, { icon: _jsx(BarChart3, {}), label: "Campagnes", value: t.campaigns })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: "Top klanten op omzet" }) }), _jsx(CardContent, { children: topCustomers.length === 0 ? _jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen bestellingen in deze periode" }) : (_jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "text-left text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "py-1", children: "Klant" }), _jsx("th", { children: "Bestellingen" }), _jsx("th", { className: "text-right", children: "Omzet" })] }) }), _jsx("tbody", { children: topCustomers.map((c, i) => (_jsxs("tr", { className: "border-t", children: [_jsx("td", { className: "py-2", children: c.name }), _jsx("td", { children: c.orders }), _jsxs("td", { className: "text-right", children: ["\u20AC ", c.revenue.toFixed(2)] })] }, i))) })] })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: "Best verkochte actieproducten" }) }), _jsx(CardContent, { children: topProducts.length === 0 ? _jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen bestellingen in deze periode" }) : (_jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "text-left text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "py-1", children: "Product" }), _jsx("th", { children: "Aantal" }), _jsx("th", { className: "text-right", children: "Omzet" })] }) }), _jsx("tbody", { children: topProducts.map((p, i) => (_jsxs("tr", { className: "border-t", children: [_jsx("td", { className: "py-2", children: p.name }), _jsx("td", { children: p.qty }), _jsxs("td", { className: "text-right", children: ["\u20AC ", p.revenue.toFixed(2)] })] }, i))) })] })) })] })] }));
|
|
57
|
+
}
|
|
58
|
+
function Stat({ icon, label, value }) {
|
|
59
|
+
return (_jsx(Card, { children: _jsxs(CardContent, { className: "p-4", children: [_jsxs("div", { className: "flex items-center gap-2 text-muted-foreground text-xs mb-1", children: [icon, _jsx("span", { children: label })] }), _jsx("div", { className: "text-2xl font-bold", children: value })] }) }));
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AfzenderSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/AfzenderSettingsCard.tsx"],"names":[],"mappings":"AAIA,wBAAgB,oBAAoB,gCAsCnC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label, Textarea, supabase, toast, useAuth } from "@flowselections/core";
|
|
4
|
+
import { Mail } from "lucide-react";
|
|
5
|
+
export function AfzenderSettingsCard() {
|
|
6
|
+
const { isAdmin } = useAuth();
|
|
7
|
+
const [state, setState] = useState({ from_name: "", from_email: "", default_subject: "", default_intro: "" });
|
|
8
|
+
const [saving, setSaving] = useState(false);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
supabase.from("offer_settings").select("from_name, from_email, default_subject, default_intro").maybeSingle()
|
|
11
|
+
.then(({ data }) => { if (data)
|
|
12
|
+
setState(data); });
|
|
13
|
+
}, []);
|
|
14
|
+
const save = async () => {
|
|
15
|
+
setSaving(true);
|
|
16
|
+
try {
|
|
17
|
+
const { error } = await supabase.from("offer_settings").update(state).eq("singleton", true);
|
|
18
|
+
if (error)
|
|
19
|
+
throw error;
|
|
20
|
+
toast.success("Opgeslagen");
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
toast.error(e?.message ?? "Fout");
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
setSaving(false);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Mail, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Afzender" })] }), _jsx(CardDescription, { children: "Standaard afzendergegevens voor aanbiedingsmails" })] }), _jsxs(CardContent, { className: "space-y-3", children: [_jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Afzendernaam" }), _jsx(Input, { value: state.from_name, onChange: (e) => setState({ ...state, from_name: e.target.value }) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Afzender e-mail" }), _jsx(Input, { type: "email", value: state.from_email, onChange: (e) => setState({ ...state, from_email: e.target.value }) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Standaard onderwerp" }), _jsx(Input, { value: state.default_subject, onChange: (e) => setState({ ...state, default_subject: e.target.value }) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Standaard intro" }), _jsx(Textarea, { rows: 3, value: state.default_intro, onChange: (e) => setState({ ...state, default_intro: e.target.value }) })] }), _jsx(Button, { onClick: save, disabled: saving || !isAdmin, children: saving ? "Opslaan..." : "Opslaan" }), !isAdmin && _jsx("p", { className: "text-xs text-muted-foreground", children: "Alleen admins kunnen deze instellingen wijzigen." })] })] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HuisstijlSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/HuisstijlSettingsCard.tsx"],"names":[],"mappings":"AAIA,wBAAgB,qBAAqB,gCAoCpC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label, supabase, toast, useAuth } from "@flowselections/core";
|
|
4
|
+
import { Palette } from "lucide-react";
|
|
5
|
+
export function HuisstijlSettingsCard() {
|
|
6
|
+
const { isAdmin } = useAuth();
|
|
7
|
+
const [state, setState] = useState({ logo_url: "", primary_color: "#16a34a", button_color: "#16a34a" });
|
|
8
|
+
const [saving, setSaving] = useState(false);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
supabase.from("offer_settings").select("logo_url, primary_color, button_color").maybeSingle()
|
|
11
|
+
.then(({ data }) => { if (data)
|
|
12
|
+
setState({ logo_url: data.logo_url ?? "", primary_color: data.primary_color, button_color: data.button_color }); });
|
|
13
|
+
}, []);
|
|
14
|
+
const save = async () => {
|
|
15
|
+
setSaving(true);
|
|
16
|
+
try {
|
|
17
|
+
const { error } = await supabase.from("offer_settings").update(state).eq("singleton", true);
|
|
18
|
+
if (error)
|
|
19
|
+
throw error;
|
|
20
|
+
toast.success("Opgeslagen");
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
toast.error(e?.message ?? "Fout");
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
setSaving(false);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Palette, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Huisstijl" })] }), _jsx(CardDescription, { children: "Logo en kleuren voor de e-mail en actiepagina" })] }), _jsxs(CardContent, { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Logo URL" }), _jsx(Input, { value: state.logo_url, onChange: (e) => setState({ ...state, logo_url: e.target.value }), placeholder: "https://..." })] }), _jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Primaire kleur" }), _jsx(Input, { type: "color", value: state.primary_color, onChange: (e) => setState({ ...state, primary_color: e.target.value }) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Knop kleur" }), _jsx(Input, { type: "color", value: state.button_color, onChange: (e) => setState({ ...state, button_color: e.target.value }) })] })] }), _jsx(Button, { onClick: save, disabled: saving || !isAdmin, children: saving ? "Opslaan..." : "Opslaan" })] })] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StickerSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/StickerSettingsCard.tsx"],"names":[],"mappings":"AAIA,wBAAgB,mBAAmB,gCA8DlC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Label, Input, supabase, toast, useAuth } from "@flowselections/core";
|
|
4
|
+
import { Tag } from "lucide-react";
|
|
5
|
+
export function StickerSettingsCard() {
|
|
6
|
+
const { isAdmin } = useAuth();
|
|
7
|
+
const [value, setValue] = useState("0");
|
|
8
|
+
const [saving, setSaving] = useState(false);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
supabase.from("offer_settings").select("default_sticker_cost").maybeSingle()
|
|
11
|
+
.then(({ data }) => {
|
|
12
|
+
if (data && data.default_sticker_cost != null) {
|
|
13
|
+
setValue(String(data.default_sticker_cost));
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}, []);
|
|
17
|
+
const save = async () => {
|
|
18
|
+
const v = Number(value.replace(",", "."));
|
|
19
|
+
if (!Number.isFinite(v) || v < 0) {
|
|
20
|
+
toast.error("Vul een geldige prijs in");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
setSaving(true);
|
|
24
|
+
try {
|
|
25
|
+
const { error } = await supabase
|
|
26
|
+
.from("offer_settings")
|
|
27
|
+
.update({ default_sticker_cost: v })
|
|
28
|
+
.eq("singleton", true);
|
|
29
|
+
if (error)
|
|
30
|
+
throw error;
|
|
31
|
+
toast.success("Opgeslagen");
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
toast.error(e?.message ?? "Fout");
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
setSaving(false);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Tag, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Stickers" })] }), _jsx(CardDescription, { children: "Basisprijs voor een sticker (per plant). Wordt automatisch ingevuld bij nieuwe aanbiedingen." })] }), _jsxs(CardContent, { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "default-sticker-cost", children: "Basisprijs sticker (\u20AC per plant)" }), _jsx(Input, { id: "default-sticker-cost", type: "number", step: "0.01", min: "0", inputMode: "decimal", value: value, onChange: (e) => setValue(e.target.value), className: "w-40" })] }), _jsx(Button, { onClick: save, disabled: saving || !isAdmin, children: saving ? "Opslaan..." : "Opslaan" })] })] }));
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestmailCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/TestmailCard.tsx"],"names":[],"mappings":"AAIA,wBAAgB,YAAY,gCA0B3B"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label, toast } from "@flowselections/core";
|
|
4
|
+
import { TestTube2 } from "lucide-react";
|
|
5
|
+
export function TestmailCard() {
|
|
6
|
+
const [email, setEmail] = useState("");
|
|
7
|
+
const [sending, setSending] = useState(false);
|
|
8
|
+
const send = async () => {
|
|
9
|
+
if (!email) {
|
|
10
|
+
toast.error("Vul een e-mailadres in");
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
setSending(true);
|
|
14
|
+
// Note: actual delivery requires Lovable Emails infrastructure to be configured.
|
|
15
|
+
setTimeout(() => {
|
|
16
|
+
setSending(false);
|
|
17
|
+
toast.success(`Testmail wordt verstuurd naar ${email} (vereist e-mailinfrastructuur)`);
|
|
18
|
+
}, 500);
|
|
19
|
+
};
|
|
20
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(TestTube2, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Testmail" })] }), _jsx(CardDescription, { children: "Verstuur een voorbeeldcampagne naar uzelf" })] }), _jsxs(CardContent, { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Test e-mailadres" }), _jsx(Input, { type: "email", value: email, onChange: (e) => setEmail(e.target.value) })] }), _jsx(Button, { onClick: send, disabled: sending, children: sending ? "Versturen..." : "Verstuur testmail" })] })] }));
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TrackingSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/TrackingSettingsCard.tsx"],"names":[],"mappings":"AAIA,wBAAgB,oBAAoB,gCAuDnC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Label, Switch, supabase, toast, useAuth, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@flowselections/core";
|
|
4
|
+
import { Eye } from "lucide-react";
|
|
5
|
+
export function TrackingSettingsCard() {
|
|
6
|
+
const { isAdmin } = useAuth();
|
|
7
|
+
const [state, setState] = useState({ tracking_enabled: true, discount_display: "percent", show_customer_classes: true });
|
|
8
|
+
const [saving, setSaving] = useState(false);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
supabase.from("offer_settings").select("tracking_enabled, discount_display, show_customer_classes").maybeSingle()
|
|
11
|
+
.then(({ data }) => { if (data)
|
|
12
|
+
setState({ tracking_enabled: data.tracking_enabled ?? true, discount_display: data.discount_display ?? "percent", show_customer_classes: data.show_customer_classes ?? true }); });
|
|
13
|
+
}, []);
|
|
14
|
+
const save = async () => {
|
|
15
|
+
setSaving(true);
|
|
16
|
+
try {
|
|
17
|
+
const { error } = await supabase.from("offer_settings").update(state).eq("singleton", true);
|
|
18
|
+
if (error)
|
|
19
|
+
throw error;
|
|
20
|
+
toast.success("Opgeslagen");
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
toast.error(e?.message ?? "Fout");
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
setSaving(false);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Eye, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Tracking & weergave" })] }), _jsx(CardDescription, { children: "Automatische tracking en kortingsweergave" })] }), _jsxs(CardContent, { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { children: "Automatische tracking" }), _jsx(Switch, { checked: state.tracking_enabled, onCheckedChange: (v) => setState({ ...state, tracking_enabled: v }) })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(Label, { children: "Klantgroepen tonen" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Toon de \"Selecteer op klantgroep\" knoppen bij het aanmaken van een aanbieding" })] }), _jsx(Switch, { checked: state.show_customer_classes, onCheckedChange: (v) => setState({ ...state, show_customer_classes: v }) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Kortingsweergave" }), _jsxs(Select, { value: state.discount_display, onValueChange: (v) => setState({ ...state, discount_display: v }), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "percent", children: "Percentage (-20%)" }), _jsx(SelectItem, { value: "amount", children: "Bedrag (\u20AC5 korting)" }), _jsx(SelectItem, { value: "both", children: "Beide" })] })] })] }), _jsx(Button, { onClick: save, disabled: saving || !isAdmin, children: saving ? "Opslaan..." : "Opslaan" })] })] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UitsluitingenSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/UitsluitingenSettingsCard.tsx"],"names":[],"mappings":"AAMA,wBAAgB,yBAAyB,gCA+DxC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Label, supabase, toast, useAuth, Badge } from "@flowselections/core";
|
|
4
|
+
import { UserX, X } from "lucide-react";
|
|
5
|
+
export function UitsluitingenSettingsCard() {
|
|
6
|
+
const { isAdmin } = useAuth();
|
|
7
|
+
const [customers, setCustomers] = useState([]);
|
|
8
|
+
const [excluded, setExcluded] = useState([]);
|
|
9
|
+
const [search, setSearch] = useState("");
|
|
10
|
+
const [saving, setSaving] = useState(false);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
(async () => {
|
|
13
|
+
const [{ data: cs }, { data: s }] = await Promise.all([
|
|
14
|
+
supabase.from("customers").select("id, company_name, email").order("company_name").limit(1000),
|
|
15
|
+
supabase.from("offer_settings").select("excluded_customer_ids").maybeSingle(),
|
|
16
|
+
]);
|
|
17
|
+
setCustomers((cs ?? []));
|
|
18
|
+
if (s?.excluded_customer_ids)
|
|
19
|
+
setExcluded(s.excluded_customer_ids);
|
|
20
|
+
})();
|
|
21
|
+
}, []);
|
|
22
|
+
const toggle = (id) => setExcluded((p) => p.includes(id) ? p.filter((x) => x !== id) : [...p, id]);
|
|
23
|
+
const save = async () => {
|
|
24
|
+
setSaving(true);
|
|
25
|
+
try {
|
|
26
|
+
const { error } = await supabase.from("offer_settings").update({ excluded_customer_ids: excluded }).eq("singleton", true);
|
|
27
|
+
if (error)
|
|
28
|
+
throw error;
|
|
29
|
+
toast.success("Opgeslagen");
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
toast.error(e?.message ?? "Fout");
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
setSaving(false);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const excludedCustomers = customers.filter((c) => excluded.includes(c.id));
|
|
39
|
+
const filtered = customers.filter((c) => c.company_name?.toLowerCase().includes(search.toLowerCase()) && !excluded.includes(c.id));
|
|
40
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(UserX, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Uitsluitingen" })] }), _jsx(CardDescription, { children: "Klanten die nooit aanbiedingsmails moeten ontvangen" })] }), _jsxs(CardContent, { className: "space-y-3", children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [excludedCustomers.map((c) => (_jsxs(Badge, { variant: "secondary", className: "cursor-pointer", onClick: () => toggle(c.id), children: [c.company_name, " ", _jsx(X, { className: "h-3 w-3 ml-1" })] }, c.id))), excludedCustomers.length === 0 && _jsx("p", { className: "text-sm text-muted-foreground", children: "Geen uitsluitingen" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Klant toevoegen" }), _jsx("input", { className: "w-full border rounded-md px-3 py-2 text-sm", placeholder: "Zoek...", value: search, onChange: (e) => setSearch(e.target.value) }), search && (_jsx("div", { className: "max-h-40 overflow-y-auto border rounded-md mt-2", children: filtered.slice(0, 20).map((c) => (_jsx("button", { onClick: () => { toggle(c.id); setSearch(""); }, className: "w-full text-left px-3 py-1.5 hover:bg-muted text-sm", children: c.company_name }, c.id))) }))] }), _jsx(Button, { onClick: save, disabled: saving || !isAdmin, children: saving ? "Opslaan..." : "Opslaan" })] })] }));
|
|
41
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FlowModule } from "@flowselections/core";
|
|
2
|
+
export * from "./_core-safelist";
|
|
3
|
+
export { AanbiedingsmailtjePage } from "./components/AanbiedingsmailtjePage";
|
|
4
|
+
export { CampagneOverzicht } from "./components/CampagneOverzicht";
|
|
5
|
+
export { CampagneDetail } from "./components/CampagneDetail";
|
|
6
|
+
export { RapportagePage } from "./components/RapportagePage";
|
|
7
|
+
export { ActiePage } from "./components/ActiePage";
|
|
8
|
+
import { AfzenderSettingsCard } from "./components/settings/AfzenderSettingsCard";
|
|
9
|
+
import { HuisstijlSettingsCard } from "./components/settings/HuisstijlSettingsCard";
|
|
10
|
+
import { TrackingSettingsCard } from "./components/settings/TrackingSettingsCard";
|
|
11
|
+
import { UitsluitingenSettingsCard } from "./components/settings/UitsluitingenSettingsCard";
|
|
12
|
+
import { StickerSettingsCard } from "./components/settings/StickerSettingsCard";
|
|
13
|
+
import { TestmailCard } from "./components/settings/TestmailCard";
|
|
14
|
+
export { AfzenderSettingsCard, HuisstijlSettingsCard, TrackingSettingsCard, UitsluitingenSettingsCard, StickerSettingsCard, TestmailCard };
|
|
15
|
+
export declare const aanbiedingsmailtjeModule: FlowModule;
|
|
16
|
+
export declare const myModule: FlowModule;
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAClF,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAClF,OAAO,EAAE,yBAAyB,EAAE,MAAM,iDAAiD,CAAC;AAC5F,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAElE,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,YAAY,EAAE,CAAC;AAE3I,eAAO,MAAM,wBAAwB,EAAE,UAiBtC,CAAC;AAGF,eAAO,MAAM,QAAQ,YAA2B,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Mail } from "lucide-react";
|
|
2
|
+
export * from "./_core-safelist";
|
|
3
|
+
export { AanbiedingsmailtjePage } from "./components/AanbiedingsmailtjePage";
|
|
4
|
+
export { CampagneOverzicht } from "./components/CampagneOverzicht";
|
|
5
|
+
export { CampagneDetail } from "./components/CampagneDetail";
|
|
6
|
+
export { RapportagePage } from "./components/RapportagePage";
|
|
7
|
+
export { ActiePage } from "./components/ActiePage";
|
|
8
|
+
import { AfzenderSettingsCard } from "./components/settings/AfzenderSettingsCard";
|
|
9
|
+
import { HuisstijlSettingsCard } from "./components/settings/HuisstijlSettingsCard";
|
|
10
|
+
import { TrackingSettingsCard } from "./components/settings/TrackingSettingsCard";
|
|
11
|
+
import { UitsluitingenSettingsCard } from "./components/settings/UitsluitingenSettingsCard";
|
|
12
|
+
import { StickerSettingsCard } from "./components/settings/StickerSettingsCard";
|
|
13
|
+
import { TestmailCard } from "./components/settings/TestmailCard";
|
|
14
|
+
export { AfzenderSettingsCard, HuisstijlSettingsCard, TrackingSettingsCard, UitsluitingenSettingsCard, StickerSettingsCard, TestmailCard };
|
|
15
|
+
export const aanbiedingsmailtjeModule = {
|
|
16
|
+
id: "aanbiedingsmailtje",
|
|
17
|
+
name: "Aanbiedingsmailtje",
|
|
18
|
+
version: "1.0.0",
|
|
19
|
+
nav: {
|
|
20
|
+
label: "Aanbiedingsmailtje",
|
|
21
|
+
href: "/aanbiedingsmailtje",
|
|
22
|
+
icon: Mail,
|
|
23
|
+
},
|
|
24
|
+
settingsCards: [
|
|
25
|
+
{ component: AfzenderSettingsCard, order: 10 },
|
|
26
|
+
{ component: HuisstijlSettingsCard, order: 20 },
|
|
27
|
+
{ component: TrackingSettingsCard, order: 30 },
|
|
28
|
+
{ component: StickerSettingsCard, order: 35 },
|
|
29
|
+
{ component: UitsluitingenSettingsCard, order: 40 },
|
|
30
|
+
{ component: TestmailCard, order: 50 },
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
// Backwards-compatibility alias used by _authenticated.tsx
|
|
34
|
+
export const myModule = aanbiedingsmailtjeModule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-attacher.d.ts","sourceRoot":"","sources":["../../../src/integrations/supabase/auth-attacher.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,kBAAkB,mHAQ9B,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// This file is automatically generated. Do not edit it directly.
|
|
2
|
+
import { createMiddleware } from '@tanstack/react-start';
|
|
3
|
+
import { supabase } from './client';
|
|
4
|
+
// Must be registered as a global `functionMiddleware` in `src/start.ts`; otherwise
|
|
5
|
+
// the browser never attaches the bearer token to serverFn RPCs.
|
|
6
|
+
export const attachSupabaseAuth = createMiddleware({ type: 'function' }).client(async ({ next }) => {
|
|
7
|
+
const { data } = await supabase.auth.getSession();
|
|
8
|
+
const token = data.session?.access_token;
|
|
9
|
+
return next({
|
|
10
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
|
11
|
+
});
|
|
12
|
+
});
|