@flowselections/floriday-afhandeling-module 1.0.63 → 1.0.64
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/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/DagDetailDialog.js +2 -1
- package/dist-lib/components/afhandeling/SignupMailMoments.d.ts +8 -0
- package/dist-lib/components/afhandeling/SignupMailMoments.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/SignupMailMoments.js +72 -0
- package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/WeekAgendaView.js +2 -1
- package/dist-lib/components/settings/AfhandelingSettingsCard.d.ts.map +1 -1
- package/dist-lib/components/settings/AfhandelingSettingsCard.js +47 -2
- package/dist-lib/integrations/supabase/types.d.ts +110 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/carrierSignup.functions.d.ts +100 -0
- package/dist-lib/lib/carrierSignup.functions.d.ts.map +1 -0
- package/dist-lib/lib/carrierSignup.functions.js +117 -0
- package/dist-lib/lib/serverRoutes/dispatchCarrierSignups.handler.d.ts +1 -1
- package/dist-lib/lib/serverRoutes/dispatchCarrierSignups.handler.d.ts.map +1 -1
- package/dist-lib/lib/serverRoutes/dispatchCarrierSignups.handler.js +2 -325
- package/dist-lib/lib/signupMailText.d.ts +4 -0
- package/dist-lib/lib/signupMailText.d.ts.map +1 -0
- package/dist-lib/lib/signupMailText.js +35 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AA6JjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAQD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,sCAG1D"}
|
|
@@ -13,6 +13,7 @@ import { getOrderLines, getDayCartContents, getOrderCartIds, getPakbonCartInfo,
|
|
|
13
13
|
import { printLayoutDocument, printPdfDocument, printFoutMelding } from "../../lib/printing";
|
|
14
14
|
import { NuVersturenButton } from "./NuVersturenButton";
|
|
15
15
|
import { AutoSendStatus } from "./AutoSendStatus";
|
|
16
|
+
import { SignupMailMoments } from "./SignupMailMoments";
|
|
16
17
|
import { CapacityWarning } from "./CapacityWarning";
|
|
17
18
|
import { useCapacityCheck } from "../../hooks/useCapacityCheck";
|
|
18
19
|
import { toDayKey } from "../../lib/logisticsEngine/capacity";
|
|
@@ -180,7 +181,7 @@ function DagDetailContent({ open, date, planned, onOpenChange, }) {
|
|
|
180
181
|
}
|
|
181
182
|
// Tel karren op unieke kar-id (gedeelde bufferkar één keer); rijen zonder id tellen los.
|
|
182
183
|
const uniekeKarren = new Set(cartRows.map((c) => c.cartId ?? `zonder-id:${c.key}`)).size;
|
|
183
|
-
return (_jsx(ShipStateCtx.Provider, { value: shipState, children: _jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { size: "xl", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Dagdetails \u2014 ", NL_DATE.format(date)] }), _jsxs(DialogDescription, { children: [ordersOnDay.length, " bestellingen \u00B7 ", uniekeKarren, " ", uniekeKarren === 1 ? "kar" : "karren", " \u00B7", " ", ordersOnDay.filter((o) => voortgang(o.status).done).length, " van ", ordersOnDay.length, " ", "bestellingen afgehandeld \u00B7 ", new Set(cartRows.filter((c) => voortgang(c.status).done).map((c) => c.cartId ?? `zonder-id:${c.key}`)).size, " van", " ", uniekeKarren, " karren afgehandeld"] })] }), _jsxs(ResponsiveDialogBody, { children: [_jsx(DayCapacityWarning, { date: date }), _jsx(BezettingCard, { date: date, cartsToday: uniekeKarren }), _jsxs(Tabs, { defaultValue: "bestellingen", children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "bestellingen", children: ["Bestellingen (", ordersOnDay.length, ")"] }), _jsxs(TabsTrigger, { value: "karren", children: ["Karren (", uniekeKarren, ")"] })] }), _jsx(TabsContent, { value: "bestellingen", className: "mt-4", children: _jsx(BestellingenTab, { orders: ordersOnDay, isLoading: agenda.isLoading }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows }) })] })] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Sluiten" }) })] }) }) }));
|
|
184
|
+
return (_jsx(ShipStateCtx.Provider, { value: shipState, children: _jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { size: "xl", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Dagdetails \u2014 ", NL_DATE.format(date)] }), _jsxs(DialogDescription, { children: [ordersOnDay.length, " bestellingen \u00B7 ", uniekeKarren, " ", uniekeKarren === 1 ? "kar" : "karren", " \u00B7", " ", ordersOnDay.filter((o) => voortgang(o.status).done).length, " van ", ordersOnDay.length, " ", "bestellingen afgehandeld \u00B7 ", new Set(cartRows.filter((c) => voortgang(c.status).done).map((c) => c.cartId ?? `zonder-id:${c.key}`)).size, " van", " ", uniekeKarren, " karren afgehandeld"] })] }), _jsxs(ResponsiveDialogBody, { children: [_jsx(DayCapacityWarning, { date: date }), _jsx(BezettingCard, { date: date, cartsToday: uniekeKarren }), _jsx(SignupMailMoments, { from: dayKey, to: dayKey }), _jsxs(Tabs, { defaultValue: "bestellingen", children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "bestellingen", children: ["Bestellingen (", ordersOnDay.length, ")"] }), _jsxs(TabsTrigger, { value: "karren", children: ["Karren (", uniekeKarren, ")"] })] }), _jsx(TabsContent, { value: "bestellingen", className: "mt-4", children: _jsx(BestellingenTab, { orders: ordersOnDay, isLoading: agenda.isLoading }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows }) })] })] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Sluiten" }) })] }) }) }));
|
|
184
185
|
}
|
|
185
186
|
function BezettingCard({ date, cartsToday }) {
|
|
186
187
|
const queryClient = useQueryClient();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SignupMailMoments.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/SignupMailMoments.tsx"],"names":[],"mappings":"AAqBA,UAAU,KAAK;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,KAAK,sCAyC7D"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
5
|
+
import { Button, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, toast, } from "@flowselections/core";
|
|
6
|
+
import { Mail } from "lucide-react";
|
|
7
|
+
import { getSignupOverview, previewSignupMail, sendSignupMailNow, } from "../../lib/carrierSignup.functions";
|
|
8
|
+
import { fmtDayTime, fmtTime, signupReasonText } from "../../lib/signupMailText";
|
|
9
|
+
export function SignupMailMoments({ from, to, compact }) {
|
|
10
|
+
const fetchOverview = useServerFn(getSignupOverview);
|
|
11
|
+
const q = useQuery({
|
|
12
|
+
queryKey: ["afhandeling", "signup-overview", from, to],
|
|
13
|
+
queryFn: () => fetchOverview({ data: { from, to } }),
|
|
14
|
+
refetchInterval: 60000,
|
|
15
|
+
});
|
|
16
|
+
const [preview, setPreview] = useState(null);
|
|
17
|
+
if (q.isLoading)
|
|
18
|
+
return null;
|
|
19
|
+
if (q.isError) {
|
|
20
|
+
return _jsx("div", { className: "rounded-md border px-3 py-2 text-xs text-muted-foreground", children: "Aanmeldmail: stand onbekend." });
|
|
21
|
+
}
|
|
22
|
+
const ov = q.data;
|
|
23
|
+
if (!ov)
|
|
24
|
+
return null;
|
|
25
|
+
const tz = ov.timezone;
|
|
26
|
+
const reason = ov.ok ? (ov.enabled ? signupReasonText(ov.reason) : null) : signupReasonText(ov.reason);
|
|
27
|
+
const unassigned = ov.unassigned.length;
|
|
28
|
+
if (ov.groups.length === 0 && unassigned === 0 && !reason)
|
|
29
|
+
return null;
|
|
30
|
+
return (_jsxs("div", { className: "space-y-2 rounded-md border p-3", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm font-medium", children: [_jsx(Mail, { className: "h-4 w-4 text-primary" }), " Aanmeldmail vervoerders"] }), !ov.enabled ? _jsx("p", { className: "text-xs text-muted-foreground", children: "De aanmeldmail staat uit." }) : null, reason && ov.enabled ? _jsx("p", { className: "text-xs text-destructive", children: reason }) : null, unassigned > 0 ? (_jsxs("p", { className: "text-xs text-destructive", children: [unassigned, " ", unassigned === 1 ? "order heeft" : "orders hebben", " geen vervoerder. Stel een standaardvervoerder in bij Vervoerders."] })) : null, _jsx("ul", { className: "space-y-1", children: ov.groups.map((g) => (_jsx(MomentRow, { g: g, tz: tz, compact: compact, enabled: ov.enabled, onPreview: () => setPreview(g) }, `${g.carrier_id}-${g.pickup_date}-${g.pickup_moment_key}`))) }), preview ? _jsx(PreviewDialog, { g: preview, onClose: () => setPreview(null) }) : null] }));
|
|
31
|
+
}
|
|
32
|
+
function statusLine(g, tz, enabled) {
|
|
33
|
+
const moment = compactMoment(g, tz);
|
|
34
|
+
if (g.last) {
|
|
35
|
+
const base = `Aanmeldmail verstuurd om ${fmtTime(g.last.sent_at, tz)} naar ${g.carrier_name} · ${g.last.cart_count} karren`;
|
|
36
|
+
return g.last.fingerprint_match ? `${moment} — ${base}` : `${moment} — ${base} · wijziging volgt: nu ${g.cart_count} karren`;
|
|
37
|
+
}
|
|
38
|
+
if (!enabled)
|
|
39
|
+
return `${moment} — ${g.carrier_name} · ${g.cart_count} karren`;
|
|
40
|
+
return `${moment} — Aanmeldmail volgt om ${fmtTime(g.send_at, tz)} naar ${g.carrier_name} · ${g.cart_count} karren`;
|
|
41
|
+
}
|
|
42
|
+
function compactMoment(g, tz) {
|
|
43
|
+
return `Ophalen ${fmtDayTime(g.pickup_at, tz)}`;
|
|
44
|
+
}
|
|
45
|
+
function MomentRow({ g, tz, compact, enabled, onPreview }) {
|
|
46
|
+
const qc = useQueryClient();
|
|
47
|
+
const send = useServerFn(sendSignupMailNow);
|
|
48
|
+
const m = useMutation({
|
|
49
|
+
mutationFn: () => send({ data: { carrierId: g.carrier_id, pickupDate: g.pickup_date, momentKey: g.pickup_moment_key } }),
|
|
50
|
+
onSuccess: (r) => {
|
|
51
|
+
if (r.code === "sent")
|
|
52
|
+
toast.success("Aanmeldmail verstuurd");
|
|
53
|
+
else if (r.code === "already_sent")
|
|
54
|
+
toast.info("Deze stand is al verstuurd");
|
|
55
|
+
else
|
|
56
|
+
toast.error(signupReasonText(r.code) ?? "Versturen lukte niet");
|
|
57
|
+
void qc.invalidateQueries({ queryKey: ["afhandeling", "signup-overview"] });
|
|
58
|
+
void qc.invalidateQueries({ queryKey: ["afhandeling", "signup-status"] });
|
|
59
|
+
},
|
|
60
|
+
onError: (e) => toast.error(e instanceof Error ? e.message : "Versturen lukte niet"),
|
|
61
|
+
});
|
|
62
|
+
const upToDate = g.last?.fingerprint_match === true;
|
|
63
|
+
return (_jsxs("li", { className: "flex flex-wrap items-center justify-between gap-2 text-xs", children: [_jsx("span", { children: statusLine(g, tz, enabled) }), compact ? null : (_jsxs("span", { className: "flex gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: onPreview, children: "Voorbeeld bekijken" }), _jsx(Button, { size: "sm", disabled: !enabled || upToDate || m.isPending, onClick: () => m.mutate(), children: m.isPending ? "Versturen…" : "Nu versturen" })] }))] }));
|
|
64
|
+
}
|
|
65
|
+
function PreviewDialog({ g, onClose }) {
|
|
66
|
+
const fetchPreview = useServerFn(previewSignupMail);
|
|
67
|
+
const q = useQuery({
|
|
68
|
+
queryKey: ["afhandeling", "signup-preview", g.carrier_id, g.pickup_date, g.pickup_moment_key],
|
|
69
|
+
queryFn: () => fetchPreview({ data: { carrierId: g.carrier_id, pickupDate: g.pickup_date, momentKey: g.pickup_moment_key } }),
|
|
70
|
+
});
|
|
71
|
+
return (_jsx(Dialog, { open: true, onOpenChange: (o) => (!o ? onClose() : undefined), children: _jsxs(DialogContent, { className: "max-w-2xl", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Voorbeeld aanmeldmail" }), _jsxs(DialogDescription, { children: [q.data?.to ? _jsxs(_Fragment, { children: ["Gaat naar ", _jsx("strong", { children: q.data.to }), "."] }) : "Ontvanger wordt geladen…", q.data?.already_sent ? " Deze stand is al verstuurd." : null] })] }), q.isLoading ? _jsx("p", { className: "text-sm text-muted-foreground", children: "Laden\u2026" }) : null, q.data && !q.data.ok ? _jsx("p", { className: "text-sm text-destructive", children: signupReasonText(q.data.reason) }) : null, q.data?.ok ? (_jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-sm font-medium", children: q.data.subject }), _jsx("pre", { className: "max-h-[50vh] overflow-auto whitespace-pre-wrap rounded-md bg-muted p-3 text-xs", children: q.data.text })] })) : null] }) }));
|
|
72
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAQjE,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,GAAG,oBAAoB,GAAG,MAAM,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC9C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;IACxD,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAiGD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,UAAU,EACV,KAAK,EACL,YAAY,EACZ,UAAU,GACX,EAAE,mBAAmB,+BAsMrB"}
|
|
@@ -10,6 +10,7 @@ import { useCapacityCheck } from "../../hooks/useCapacityCheck";
|
|
|
10
10
|
import { usePickAgenda } from "../../hooks/usePickAgenda";
|
|
11
11
|
import { useConsistencyCheck } from "../../hooks/useConsistencyCheck";
|
|
12
12
|
import { CapacityWarning } from "./CapacityWarning";
|
|
13
|
+
import { SignupMailMoments } from "./SignupMailMoments";
|
|
13
14
|
import { toDayKey } from "../../lib/logisticsEngine/capacity";
|
|
14
15
|
const SHIPPED = new Set(["verzonden", "gereed voor facturatie", "gefactureerd", "geleverd", "afgeleverd", "afgerond", "afgehandeld", "afgemeld", "shipped", "delivered", "completed", "invoiced"]);
|
|
15
16
|
/** Eén indeling per order. Floriday herkend op Floriday-id; afmeldstand uit de order. */
|
|
@@ -205,7 +206,7 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
|
|
|
205
206
|
d.setHours(0, 0, 0, 0);
|
|
206
207
|
setCursor(startOfWeek(d));
|
|
207
208
|
};
|
|
208
|
-
return (_jsxs("div", { className: "flex h-full flex-col gap-4 overflow-hidden", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("h2", { className: "text-xl font-semibold tracking-tight", children: title ?? headerLabel }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: goPrev, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), " Vorige week"] }), _jsx(Button, { variant: "outline", size: "sm", onClick: goToday, children: "Deze week" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: goNext, children: ["Volgende week ", _jsx(ChevronRight, { className: "ml-1 h-4 w-4" })] })] })] }), misaligned && (_jsx("div", { className: "rounded-md border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: "De aantallen worden bijgewerkt." })), _jsxs("div", { className: "grid shrink-0 grid-cols-1 gap-3 sm:grid-cols-3 lg:grid-cols-6", children: [_jsx(SummaryCard, { label: MODE_LABELS[mode].summary, value: totals.leveringen, tone: "default" }), _jsx(SummaryCard, { label: "Verzendklaar", value: totals.verzendklaar, tone: "success" }), _jsx(SummaryCard, { label: "In voorbereiding", value: totals.voorbereiding, tone: "muted" }), _jsx(SummaryCard, { label: "Afgehandeld", value: totals.afgehandeld, tone: "success" }), _jsx(SummaryCard, { label: "Afgehandeld, nog niet gemeld bij Floriday", value: totals.nietAfgemeld, tone: "warning" }), _jsx(SummaryCard, { label: "Totaal stuks", value: totals.totaalStuks, tone: "default" })] }), _jsx("div", { className: "min-h-0 flex-1 space-y-2 overflow-y-auto rounded-lg border bg-muted/20 p-2", children: isLoading || (mode === "pick" && pickAgenda.isLoading) ? (_jsx("div", { className: "rounded-lg border bg-card p-10 text-center text-sm text-muted-foreground", children: "Laden\u2026" })) : (days.map((day) => (_jsx(DayRow, { day: day, onOrderClick: onOrderClick, onDayClick: onDayClick, capacity: mode === "delivery" ? undefined : capacity.byDate.get(toDayKey(day.date)) }, day.date.toISOString())))) })] }));
|
|
209
|
+
return (_jsxs("div", { className: "flex h-full flex-col gap-4 overflow-hidden", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("h2", { className: "text-xl font-semibold tracking-tight", children: title ?? headerLabel }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: goPrev, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), " Vorige week"] }), _jsx(Button, { variant: "outline", size: "sm", onClick: goToday, children: "Deze week" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: goNext, children: ["Volgende week ", _jsx(ChevronRight, { className: "ml-1 h-4 w-4" })] })] })] }), misaligned && (_jsx("div", { className: "rounded-md border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: "De aantallen worden bijgewerkt." })), _jsxs("div", { className: "grid shrink-0 grid-cols-1 gap-3 sm:grid-cols-3 lg:grid-cols-6", children: [_jsx(SummaryCard, { label: MODE_LABELS[mode].summary, value: totals.leveringen, tone: "default" }), _jsx(SummaryCard, { label: "Verzendklaar", value: totals.verzendklaar, tone: "success" }), _jsx(SummaryCard, { label: "In voorbereiding", value: totals.voorbereiding, tone: "muted" }), _jsx(SummaryCard, { label: "Afgehandeld", value: totals.afgehandeld, tone: "success" }), _jsx(SummaryCard, { label: "Afgehandeld, nog niet gemeld bij Floriday", value: totals.nietAfgemeld, tone: "warning" }), _jsx(SummaryCard, { label: "Totaal stuks", value: totals.totaalStuks, tone: "default" })] }), mode !== "pick" ? _jsx(SignupMailMoments, { from: toDayKey(weekStart), to: toDayKey(weekEnd), compact: true }) : null, _jsx("div", { className: "min-h-0 flex-1 space-y-2 overflow-y-auto rounded-lg border bg-muted/20 p-2", children: isLoading || (mode === "pick" && pickAgenda.isLoading) ? (_jsx("div", { className: "rounded-lg border bg-card p-10 text-center text-sm text-muted-foreground", children: "Laden\u2026" })) : (days.map((day) => (_jsx(DayRow, { day: day, onOrderClick: onOrderClick, onDayClick: onDayClick, capacity: mode === "delivery" ? undefined : capacity.byDate.get(toDayKey(day.date)) }, day.date.toISOString())))) })] }));
|
|
209
210
|
}
|
|
210
211
|
function SummaryCard({ label, value, tone, }) {
|
|
211
212
|
const valueClass = tone === "success" ? "text-green-600" : tone === "warning" ? "text-amber-600" : "text-foreground";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AfhandelingSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/AfhandelingSettingsCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AfhandelingSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/AfhandelingSettingsCard.tsx"],"names":[],"mappings":"AAuBA,wBAAgB,uBAAuB,gCAyJtC"}
|
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
4
|
import { useServerFn } from "@tanstack/react-start";
|
|
5
5
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label, Switch, toast, } from "@flowselections/core";
|
|
6
6
|
import { CalendarClock, Mail } from "lucide-react";
|
|
7
7
|
import { getAfhandelingSettings, updateAfhandelingSettings, } from "../../lib/afhandelingSettings.functions";
|
|
8
|
+
import { getSignupCronStatus } from "../../lib/carrierSignup.functions";
|
|
9
|
+
import { fmtTime, signupReasonText } from "../../lib/signupMailText";
|
|
8
10
|
export function AfhandelingSettingsCard() {
|
|
9
11
|
const qc = useQueryClient();
|
|
10
12
|
const fetchSettings = useServerFn(getAfhandelingSettings);
|
|
11
13
|
const saveSettings = useServerFn(updateAfhandelingSettings);
|
|
14
|
+
const fetchCronStatus = useServerFn(getSignupCronStatus);
|
|
15
|
+
// Openen van de kaart leest de status en herstelt de taak als hij ontbreekt.
|
|
16
|
+
const cron = useQuery({
|
|
17
|
+
queryKey: ["afhandeling", "signup-status"],
|
|
18
|
+
queryFn: () => fetchCronStatus({ data: { sync: false } }),
|
|
19
|
+
refetchInterval: 60000,
|
|
20
|
+
});
|
|
21
|
+
const healedShown = useRef(false);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (cron.data?.healed && !healedShown.current) {
|
|
24
|
+
healedShown.current = true;
|
|
25
|
+
toast.info("De automatische controle ontbrak en is opnieuw aangezet.");
|
|
26
|
+
}
|
|
27
|
+
}, [cron.data?.healed]);
|
|
12
28
|
const { data } = useQuery({
|
|
13
29
|
queryKey: ["afhandeling", "settings"],
|
|
14
30
|
queryFn: async () => {
|
|
@@ -41,11 +57,40 @@ export function AfhandelingSettingsCard() {
|
|
|
41
57
|
onSuccess: async () => {
|
|
42
58
|
const { invalidatePlanning } = await import("../../lib/invalidatePlanning");
|
|
43
59
|
await invalidatePlanning(qc);
|
|
60
|
+
try {
|
|
61
|
+
const st = await fetchCronStatus({ data: { sync: true } });
|
|
62
|
+
qc.setQueryData(["afhandeling", "signup-status"], st);
|
|
63
|
+
if (!st.cronAvailable) {
|
|
64
|
+
toast.error("Automatisch versturen is in deze omgeving niet beschikbaar. Vraag je beheerder om dit aan te zetten.");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (st.syncCode === null && st.enabled !== st.active) {
|
|
68
|
+
toast.error("Alleen een beheerder kan de automatische controle aan- of uitzetten.");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
toast.error(e instanceof Error ? e.message : "Automatische controle kon niet worden ingesteld");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
44
76
|
toast.success("Instellingen opgeslagen");
|
|
45
77
|
},
|
|
46
78
|
onError: (e) => {
|
|
47
79
|
toast.error(e instanceof Error ? e.message : "Opslaan mislukt");
|
|
48
80
|
},
|
|
49
81
|
});
|
|
50
|
-
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Afhandeling \u2014 Planning" })] }), _jsx(CardDescription, { children: "De pickplanning (pickdagen, picktijd en verdeling over de week) wordt volledig bepaald door de planning. Hieronder staan alleen de instellingen van deze module zelf." })] }), _jsxs(CardContent, { className: "space-y-5", children: [_jsxs("div", { className: "rounded-md border p-4 space-y-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Mail, { className: "h-4 w-4 text-primary" }), _jsx(Label, { htmlFor: "mailEnabled", className: "font-semibold", children: "Aanmeldmail vervoerders" })] }), _jsx("p", { className: "text-xs text-muted-foreground max-w-prose", children: "Verstuurt automatisch een mail met de te verwachten karren naar de vervoerder v\u00F3\u00F3r de aanmelddeadline van het ophaalmoment. E\u00E9n mail per ophaaldag/-moment per vervoerder." })] }), _jsx(Switch, { id: "mailEnabled", checked: mailEnabled, onCheckedChange: setMailEnabled })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "mailLead", children: "Minuten v\u00F3\u00F3r aanmelddeadline versturen" }), _jsx(Input, { id: "mailLead", type: "number", min: 0, max: 1440, value: mailLead, disabled: !mailEnabled, onChange: (e) => setMailLead(Math.max(0, Math.min(1440, Math.floor(Number(e.target.value)) || 0))) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "mailPurpose", children: "E-mail-doeleinde (purpose)" }), _jsx(Input, { id: "mailPurpose", value: mailPurpose, disabled: !mailEnabled, onChange: (e) => setMailPurpose(e.target.value) }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Koppel dit doeleinde in de module ", _jsx("strong", { children: "E-mailkoppeling" }), " aan een actief send-adres. Standaard: ", _jsx("code", { children: "afhandeling_carrier_signup" }), "."] })] })] }), _jsxs("div", { className: "rounded-md bg-muted/50 p-3 text-xs text-muted-foreground", children: [_jsx("strong", { children: "Ophaaltijd" }), " en ", _jsx("strong", { children: "Aanmeldtijd" }), " stel je per vervoerder in (werkdagen v\u00F3\u00F3r bezorgdag). De vervoerder die als ", _jsx("strong", { children: "standaard" }), " is gemarkeerd, wordt gebruikt voor orders zonder eigen vervoerder."] }), _jsx(Button, { onClick: () => mutation.mutate(), disabled: mutation.isPending, children: mutation.isPending ? "Opslaan…" : "Opslaan" })] })] }));
|
|
82
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Afhandeling \u2014 Planning" })] }), _jsx(CardDescription, { children: "De pickplanning (pickdagen, picktijd en verdeling over de week) wordt volledig bepaald door de planning. Hieronder staan alleen de instellingen van deze module zelf." })] }), _jsxs(CardContent, { className: "space-y-5", children: [_jsxs("div", { className: "rounded-md border p-4 space-y-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Mail, { className: "h-4 w-4 text-primary" }), _jsx(Label, { htmlFor: "mailEnabled", className: "font-semibold", children: "Aanmeldmail vervoerders" })] }), _jsx("p", { className: "text-xs text-muted-foreground max-w-prose", children: "Verstuurt automatisch een mail met de te verwachten karren naar de vervoerder v\u00F3\u00F3r de aanmelddeadline van het ophaalmoment. E\u00E9n mail per ophaaldag/-moment per vervoerder." })] }), _jsx(Switch, { id: "mailEnabled", checked: mailEnabled, onCheckedChange: setMailEnabled })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "mailLead", children: "Minuten v\u00F3\u00F3r aanmelddeadline versturen" }), _jsx(Input, { id: "mailLead", type: "number", min: 0, max: 1440, value: mailLead, disabled: !mailEnabled, onChange: (e) => setMailLead(Math.max(0, Math.min(1440, Math.floor(Number(e.target.value)) || 0))) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "mailPurpose", children: "E-mail-doeleinde (purpose)" }), _jsx(Input, { id: "mailPurpose", value: mailPurpose, disabled: !mailEnabled, onChange: (e) => setMailPurpose(e.target.value) }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Koppel dit doeleinde in de module ", _jsx("strong", { children: "E-mailkoppeling" }), " aan een actief send-adres. Standaard: ", _jsx("code", { children: "afhandeling_carrier_signup" }), "."] })] })] }), _jsx(SignupStatusBlock, { status: cron.data ?? null, loading: cron.isLoading, failed: cron.isError }), _jsxs("div", { className: "rounded-md bg-muted/50 p-3 text-xs text-muted-foreground", children: [_jsx("strong", { children: "Ophaaltijd" }), " en ", _jsx("strong", { children: "Aanmeldtijd" }), " stel je per vervoerder in (werkdagen v\u00F3\u00F3r bezorgdag). De vervoerder die als ", _jsx("strong", { children: "standaard" }), " is gemarkeerd, wordt gebruikt voor orders zonder eigen vervoerder."] }), _jsx(Button, { onClick: () => mutation.mutate(), disabled: mutation.isPending, children: mutation.isPending ? "Opslaan…" : "Opslaan" })] })] }));
|
|
83
|
+
}
|
|
84
|
+
function SignupStatusBlock({ status, loading, failed, }) {
|
|
85
|
+
if (loading)
|
|
86
|
+
return null;
|
|
87
|
+
if (failed || !status) {
|
|
88
|
+
return _jsx("div", { className: "rounded-md border p-3 text-xs text-muted-foreground", children: "Automatische controle: stand onbekend." });
|
|
89
|
+
}
|
|
90
|
+
const tz = status.timezone;
|
|
91
|
+
const run = status.lastRun;
|
|
92
|
+
const reason = status.enabled
|
|
93
|
+
? signupReasonText(status.reason) ?? (run && !run.ok ? signupReasonText(run.reason) : null)
|
|
94
|
+
: null;
|
|
95
|
+
return (_jsxs("div", { className: "space-y-1 rounded-md border p-3 text-xs", children: [_jsxs("div", { children: [_jsx("strong", { children: "Automatische controle:" }), " ", !status.cronAvailable ? (_jsx("span", { className: "text-destructive", children: "niet beschikbaar in deze omgeving. Vraag je beheerder om automatische taken aan te zetten." })) : status.active ? (_jsx("span", { className: "text-primary", children: "actief" })) : (_jsx("span", { className: "text-muted-foreground", children: "niet actief" }))] }), run ? (_jsxs("div", { className: "text-muted-foreground", children: ["Laatst gecontroleerd om ", fmtTime(run.started_at, tz), " \u00B7 ", status.sentToday, " ", status.sentToday === 1 ? "mail" : "mails", " verstuurd vandaag"] })) : (_jsx("div", { className: "text-muted-foreground", children: "Nog niet gecontroleerd." })), reason ? _jsx("div", { className: "text-destructive", children: reason }) : null] }));
|
|
51
96
|
}
|
|
@@ -1231,38 +1231,59 @@ export type Database = {
|
|
|
1231
1231
|
Row: {
|
|
1232
1232
|
carrier_id: string;
|
|
1233
1233
|
cart_count: number;
|
|
1234
|
+
cart_ids: string[] | null;
|
|
1234
1235
|
created_at: string;
|
|
1236
|
+
fingerprint: string | null;
|
|
1235
1237
|
id: string;
|
|
1238
|
+
kind: string;
|
|
1236
1239
|
order_count: number;
|
|
1237
1240
|
outbox_id: string | null;
|
|
1238
1241
|
pickup_date: string;
|
|
1239
1242
|
pickup_moment_key: string;
|
|
1243
|
+
previous_cart_count: number | null;
|
|
1244
|
+
recipient: string | null;
|
|
1240
1245
|
sent_at: string;
|
|
1241
1246
|
signup_deadline: string;
|
|
1247
|
+
source: string;
|
|
1248
|
+
version: number;
|
|
1242
1249
|
};
|
|
1243
1250
|
Insert: {
|
|
1244
1251
|
carrier_id: string;
|
|
1245
1252
|
cart_count?: number;
|
|
1253
|
+
cart_ids?: string[] | null;
|
|
1246
1254
|
created_at?: string;
|
|
1255
|
+
fingerprint?: string | null;
|
|
1247
1256
|
id?: string;
|
|
1257
|
+
kind?: string;
|
|
1248
1258
|
order_count?: number;
|
|
1249
1259
|
outbox_id?: string | null;
|
|
1250
1260
|
pickup_date: string;
|
|
1251
1261
|
pickup_moment_key: string;
|
|
1262
|
+
previous_cart_count?: number | null;
|
|
1263
|
+
recipient?: string | null;
|
|
1252
1264
|
sent_at?: string;
|
|
1253
1265
|
signup_deadline: string;
|
|
1266
|
+
source?: string;
|
|
1267
|
+
version?: number;
|
|
1254
1268
|
};
|
|
1255
1269
|
Update: {
|
|
1256
1270
|
carrier_id?: string;
|
|
1257
1271
|
cart_count?: number;
|
|
1272
|
+
cart_ids?: string[] | null;
|
|
1258
1273
|
created_at?: string;
|
|
1274
|
+
fingerprint?: string | null;
|
|
1259
1275
|
id?: string;
|
|
1276
|
+
kind?: string;
|
|
1260
1277
|
order_count?: number;
|
|
1261
1278
|
outbox_id?: string | null;
|
|
1262
1279
|
pickup_date?: string;
|
|
1263
1280
|
pickup_moment_key?: string;
|
|
1281
|
+
previous_cart_count?: number | null;
|
|
1282
|
+
recipient?: string | null;
|
|
1264
1283
|
sent_at?: string;
|
|
1265
1284
|
signup_deadline?: string;
|
|
1285
|
+
source?: string;
|
|
1286
|
+
version?: number;
|
|
1266
1287
|
};
|
|
1267
1288
|
Relationships: [
|
|
1268
1289
|
{
|
|
@@ -28564,6 +28585,18 @@ export type Database = {
|
|
|
28564
28585
|
};
|
|
28565
28586
|
Returns: string;
|
|
28566
28587
|
};
|
|
28588
|
+
afh_html: {
|
|
28589
|
+
Args: {
|
|
28590
|
+
_s: string;
|
|
28591
|
+
};
|
|
28592
|
+
Returns: string;
|
|
28593
|
+
};
|
|
28594
|
+
afh_nl_date: {
|
|
28595
|
+
Args: {
|
|
28596
|
+
_d: string;
|
|
28597
|
+
};
|
|
28598
|
+
Returns: string;
|
|
28599
|
+
};
|
|
28567
28600
|
afh_normalize_country: {
|
|
28568
28601
|
Args: {
|
|
28569
28602
|
_raw: string;
|
|
@@ -28576,6 +28609,46 @@ export type Database = {
|
|
|
28576
28609
|
};
|
|
28577
28610
|
Returns: string;
|
|
28578
28611
|
};
|
|
28612
|
+
afh_signup_config: {
|
|
28613
|
+
Args: never;
|
|
28614
|
+
Returns: Json;
|
|
28615
|
+
};
|
|
28616
|
+
afh_signup_find_group: {
|
|
28617
|
+
Args: {
|
|
28618
|
+
p_carrier_id: string;
|
|
28619
|
+
p_moment_key: string;
|
|
28620
|
+
p_pickup_date: string;
|
|
28621
|
+
p_tz: string;
|
|
28622
|
+
};
|
|
28623
|
+
Returns: Json;
|
|
28624
|
+
};
|
|
28625
|
+
afh_signup_groups: {
|
|
28626
|
+
Args: {
|
|
28627
|
+
p_from: string;
|
|
28628
|
+
p_to: string;
|
|
28629
|
+
p_tz: string;
|
|
28630
|
+
};
|
|
28631
|
+
Returns: Json;
|
|
28632
|
+
};
|
|
28633
|
+
afh_signup_mail: {
|
|
28634
|
+
Args: {
|
|
28635
|
+
g: Json;
|
|
28636
|
+
p_prev: number;
|
|
28637
|
+
p_tz: string;
|
|
28638
|
+
};
|
|
28639
|
+
Returns: Json;
|
|
28640
|
+
};
|
|
28641
|
+
afh_signup_send_group: {
|
|
28642
|
+
Args: {
|
|
28643
|
+
g: Json;
|
|
28644
|
+
p_connection: string;
|
|
28645
|
+
p_purpose: string;
|
|
28646
|
+
p_source: string;
|
|
28647
|
+
p_tz: string;
|
|
28648
|
+
p_user: string;
|
|
28649
|
+
};
|
|
28650
|
+
Returns: Json;
|
|
28651
|
+
};
|
|
28579
28652
|
afh_subtract_workdays: {
|
|
28580
28653
|
Args: {
|
|
28581
28654
|
_date: string;
|
|
@@ -28583,6 +28656,14 @@ export type Database = {
|
|
|
28583
28656
|
};
|
|
28584
28657
|
Returns: string;
|
|
28585
28658
|
};
|
|
28659
|
+
afhandeling_signup_cron_status: {
|
|
28660
|
+
Args: never;
|
|
28661
|
+
Returns: Json;
|
|
28662
|
+
};
|
|
28663
|
+
afhandeling_sync_signup_cron: {
|
|
28664
|
+
Args: never;
|
|
28665
|
+
Returns: Json;
|
|
28666
|
+
};
|
|
28586
28667
|
ai_usage_by_module: {
|
|
28587
28668
|
Args: {
|
|
28588
28669
|
_from: string;
|
|
@@ -28724,6 +28805,29 @@ export type Database = {
|
|
|
28724
28805
|
id: string;
|
|
28725
28806
|
}[];
|
|
28726
28807
|
};
|
|
28808
|
+
carrier_signup_overview: {
|
|
28809
|
+
Args: {
|
|
28810
|
+
p_from: string;
|
|
28811
|
+
p_to: string;
|
|
28812
|
+
};
|
|
28813
|
+
Returns: Json;
|
|
28814
|
+
};
|
|
28815
|
+
carrier_signup_preview: {
|
|
28816
|
+
Args: {
|
|
28817
|
+
p_carrier_id: string;
|
|
28818
|
+
p_moment_key: string;
|
|
28819
|
+
p_pickup_date: string;
|
|
28820
|
+
};
|
|
28821
|
+
Returns: Json;
|
|
28822
|
+
};
|
|
28823
|
+
carrier_signup_send_now: {
|
|
28824
|
+
Args: {
|
|
28825
|
+
p_carrier_id: string;
|
|
28826
|
+
p_moment_key: string;
|
|
28827
|
+
p_pickup_date: string;
|
|
28828
|
+
};
|
|
28829
|
+
Returns: Json;
|
|
28830
|
+
};
|
|
28727
28831
|
check_offer_product_available_stock: {
|
|
28728
28832
|
Args: {
|
|
28729
28833
|
p_campaign_product_id: string;
|
|
@@ -33736,6 +33840,12 @@ export type Database = {
|
|
|
33736
33840
|
Args: never;
|
|
33737
33841
|
Returns: Json;
|
|
33738
33842
|
};
|
|
33843
|
+
pick_assert_printable: {
|
|
33844
|
+
Args: {
|
|
33845
|
+
_item: Database["public"]["Tables"]["pick_queue"]["Row"];
|
|
33846
|
+
};
|
|
33847
|
+
Returns: undefined;
|
|
33848
|
+
};
|
|
33739
33849
|
pick_available_pick_days: {
|
|
33740
33850
|
Args: {
|
|
33741
33851
|
_max_days?: number;
|