@flowselections/core 1.0.8 → 1.0.9
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/auth/useAuth.d.ts +27 -27
- package/dist/auth/useAuth.js +72 -72
- package/dist/components/instellingen/NotificationSettingsCard.d.ts +1 -1
- package/dist/components/instellingen/NotificationSettingsCard.js +63 -63
- package/dist/components/layout/BugsDialog.d.ts +6 -6
- package/dist/components/layout/BugsDialog.js +148 -148
- package/dist/components/layout/Header.d.ts +5 -5
- package/dist/components/layout/Header.js +30 -30
- package/dist/components/layout/InstellingenPage.d.ts +4 -4
- package/dist/components/layout/InstellingenPage.js +78 -78
- package/dist/components/layout/LoginPage.d.ts +5 -5
- package/dist/components/layout/LoginPage.js +57 -57
- package/dist/components/layout/NotificationBell.d.ts +1 -1
- package/dist/components/layout/NotificationBell.js +45 -45
- package/dist/components/layout/ResetWachtwoordPage.d.ts +5 -5
- package/dist/components/layout/ResetWachtwoordPage.js +51 -51
- package/dist/components/layout/Sidebar.d.ts +8 -8
- package/dist/components/layout/Sidebar.d.ts.map +1 -1
- package/dist/components/layout/Sidebar.js +29 -31
- package/dist/components/ui/accordion.d.ts +7 -7
- package/dist/components/ui/accordion.js +13 -13
- package/dist/components/ui/alert-dialog.d.ts +20 -20
- package/dist/components/ui/alert-dialog.js +26 -26
- package/dist/components/ui/alert.d.ts +8 -8
- package/dist/components/ui/alert.js +22 -22
- package/dist/components/ui/aspect-ratio.d.ts +3 -3
- package/dist/components/ui/aspect-ratio.js +3 -3
- package/dist/components/ui/avatar.d.ts +6 -6
- package/dist/components/ui/avatar.js +12 -12
- package/dist/components/ui/badge.d.ts +9 -9
- package/dist/components/ui/badge.js +20 -20
- package/dist/components/ui/breadcrumb.d.ts +19 -19
- package/dist/components/ui/breadcrumb.js +23 -23
- package/dist/components/ui/button.d.ts +11 -11
- package/dist/components/ui/button.js +33 -33
- package/dist/components/ui/calendar.d.ts +8 -8
- package/dist/components/ui/calendar.js +74 -74
- package/dist/components/ui/card.d.ts +8 -8
- package/dist/components/ui/card.js +16 -16
- package/dist/components/ui/carousel.d.ts +18 -18
- package/dist/components/ui/carousel.js +98 -98
- package/dist/components/ui/chart.d.ts +62 -62
- package/dist/components/ui/chart.js +130 -130
- package/dist/components/ui/checkbox.d.ts +4 -4
- package/dist/components/ui/checkbox.js +8 -8
- package/dist/components/ui/collapsible.d.ts +5 -5
- package/dist/components/ui/collapsible.js +6 -6
- package/dist/components/ui/command.d.ts +80 -80
- package/dist/components/ui/command.js +29 -29
- package/dist/components/ui/context-menu.d.ts +27 -27
- package/dist/components/ui/context-menu.js +33 -33
- package/dist/components/ui/dialog.d.ts +19 -19
- package/dist/components/ui/dialog.js +23 -23
- package/dist/components/ui/drawer.d.ts +22 -22
- package/dist/components/ui/drawer.js +22 -22
- package/dist/components/ui/dropdown-menu.d.ts +27 -27
- package/dist/components/ui/dropdown-menu.js +36 -36
- package/dist/components/ui/form.d.ts +23 -23
- package/dist/components/ui/form.js +65 -65
- package/dist/components/ui/hover-card.d.ts +6 -6
- package/dist/components/ui/hover-card.js +9 -9
- package/dist/components/ui/icon-picker.d.ts +14 -14
- package/dist/components/ui/icon-picker.js +73 -73
- package/dist/components/ui/input-otp.d.ts +34 -34
- package/dist/components/ui/input-otp.js +18 -18
- package/dist/components/ui/input.d.ts +3 -3
- package/dist/components/ui/input.js +8 -8
- package/dist/components/ui/label.d.ts +5 -5
- package/dist/components/ui/label.js +10 -10
- package/dist/components/ui/menubar.d.ts +28 -28
- package/dist/components/ui/menubar.js +45 -45
- package/dist/components/ui/navigation-menu.d.ts +12 -12
- package/dist/components/ui/navigation-menu.js +24 -24
- package/dist/components/ui/pagination.d.ts +28 -28
- package/dist/components/ui/pagination.js +23 -23
- package/dist/components/ui/popover.d.ts +7 -7
- package/dist/components/ui/popover.js +10 -10
- package/dist/components/ui/progress.d.ts +4 -4
- package/dist/components/ui/progress.js +8 -8
- package/dist/components/ui/radio-group.d.ts +5 -5
- package/dist/components/ui/radio-group.js +14 -14
- package/dist/components/ui/resizable.d.ts +7 -7
- package/dist/components/ui/resizable.js +8 -8
- package/dist/components/ui/scroll-area.d.ts +5 -5
- package/dist/components/ui/scroll-area.js +11 -11
- package/dist/components/ui/select.d.ts +13 -13
- package/dist/components/ui/select.js +27 -27
- package/dist/components/ui/separator.d.ts +4 -4
- package/dist/components/ui/separator.js +7 -7
- package/dist/components/ui/sheet.d.ts +25 -25
- package/dist/components/ui/sheet.js +37 -37
- package/dist/components/ui/sidebar.d.ts +65 -65
- package/dist/components/ui/sidebar.js +224 -224
- package/dist/components/ui/skeleton.d.ts +2 -2
- package/dist/components/ui/skeleton.js +6 -6
- package/dist/components/ui/slider.d.ts +4 -4
- package/dist/components/ui/slider.js +7 -7
- package/dist/components/ui/sonner.d.ts +4 -4
- package/dist/components/ui/sonner.js +13 -13
- package/dist/components/ui/switch.d.ts +4 -4
- package/dist/components/ui/switch.js +7 -7
- package/dist/components/ui/table.d.ts +10 -10
- package/dist/components/ui/table.js +20 -20
- package/dist/components/ui/tabs.d.ts +7 -7
- package/dist/components/ui/tabs.js +12 -12
- package/dist/components/ui/textarea.d.ts +3 -3
- package/dist/components/ui/textarea.js +8 -8
- package/dist/components/ui/toggle-group.d.ts +12 -12
- package/dist/components/ui/toggle-group.js +21 -21
- package/dist/components/ui/toggle.d.ts +12 -12
- package/dist/components/ui/toggle.js +25 -25
- package/dist/components/ui/tooltip.d.ts +7 -7
- package/dist/components/ui/tooltip.js +11 -11
- package/dist/hooks/use-mobile.d.ts +1 -1
- package/dist/hooks/use-mobile.js +15 -15
- package/dist/index.d.ts +31 -31
- package/dist/index.js +33 -33
- package/dist/lib/utils.d.ts +2 -2
- package/dist/lib/utils.js +5 -5
- package/dist/lib/validationSchemas.d.ts +14 -14
- package/dist/lib/validationSchemas.js +25 -25
- package/dist/supabase/auth-middleware.d.ts +638 -638
- package/dist/supabase/auth-middleware.js +52 -52
- package/dist/supabase/client.d.ts +634 -634
- package/dist/supabase/client.js +13 -13
- package/dist/supabase/client.server.d.ts +634 -634
- package/dist/supabase/client.server.js +30 -30
- package/dist/supabase/types.d.ts +716 -716
- package/dist/supabase/types.js +7 -7
- package/dist/types/module.d.ts +19 -19
- package/dist/types/module.js +1 -1
- package/package.json +1 -1
|
@@ -1,148 +1,148 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect, useRef } from "react";
|
|
3
|
-
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog";
|
|
4
|
-
import { Button } from "../ui/button";
|
|
5
|
-
import { Input } from "../ui/input";
|
|
6
|
-
import { Textarea } from "../ui/textarea";
|
|
7
|
-
import { Label } from "../ui/label";
|
|
8
|
-
import { Badge } from "../ui/badge";
|
|
9
|
-
import { Select, SelectContent, SelectItem, SelectTrigger } from "../ui/select";
|
|
10
|
-
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
|
|
11
|
-
import { Plus, Loader2, CheckCircle, Trash2, Paperclip, X, FileText } from "lucide-react";
|
|
12
|
-
import { supabase } from "../../supabase/client";
|
|
13
|
-
import { toast } from "sonner";
|
|
14
|
-
import { bugReportSchema, validateFileUpload, ALLOWED_FILE_TYPES } from "../../lib/validationSchemas";
|
|
15
|
-
const statusConfig = {
|
|
16
|
-
open: { label: "Open", variant: "destructive" },
|
|
17
|
-
in_progress: { label: "In behandeling", variant: "default" },
|
|
18
|
-
resolved: { label: "Opgelost", variant: "secondary" },
|
|
19
|
-
};
|
|
20
|
-
export function BugsDialog({ open, onOpenChange }) {
|
|
21
|
-
const [bugs, setBugs] = useState([]);
|
|
22
|
-
const [loading, setLoading] = useState(true);
|
|
23
|
-
const [submitting, setSubmitting] = useState(false);
|
|
24
|
-
const [activeTab, setActiveTab] = useState("list");
|
|
25
|
-
const fileInputRef = useRef(null);
|
|
26
|
-
const [title, setTitle] = useState("");
|
|
27
|
-
const [description, setDescription] = useState("");
|
|
28
|
-
const [attachment, setAttachment] = useState(null);
|
|
29
|
-
const fetchBugs = async () => {
|
|
30
|
-
try {
|
|
31
|
-
const { data, error } = await supabase
|
|
32
|
-
.from("bugs")
|
|
33
|
-
.select("*")
|
|
34
|
-
.order("created_at", { ascending: false });
|
|
35
|
-
if (error)
|
|
36
|
-
throw error;
|
|
37
|
-
setBugs(data || []);
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
toast.error("Fout bij ophalen van bugs");
|
|
41
|
-
}
|
|
42
|
-
finally {
|
|
43
|
-
setLoading(false);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
if (open)
|
|
48
|
-
fetchBugs();
|
|
49
|
-
}, [open]);
|
|
50
|
-
const handleSubmit = async () => {
|
|
51
|
-
const validation = bugReportSchema.safeParse({ title: title.trim(), description: description.trim() });
|
|
52
|
-
if (!validation.success) {
|
|
53
|
-
toast.error(validation.error.issues[0].message);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
setSubmitting(true);
|
|
57
|
-
try {
|
|
58
|
-
const { data: { user } } = await supabase.auth.getUser();
|
|
59
|
-
if (!user)
|
|
60
|
-
throw new Error("Niet ingelogd");
|
|
61
|
-
let attachmentUrl = null;
|
|
62
|
-
if (attachment) {
|
|
63
|
-
const fileExt = attachment.name.split('.').pop();
|
|
64
|
-
const fileName = `${Date.now()}-${Math.random().toString(36).substring(7)}.${fileExt}`;
|
|
65
|
-
const { error: uploadError } = await supabase.storage.from('bug-attachments').upload(fileName, attachment);
|
|
66
|
-
if (uploadError)
|
|
67
|
-
throw uploadError;
|
|
68
|
-
const { data: signedData, error: signedError } = await supabase.storage
|
|
69
|
-
.from('bug-attachments').createSignedUrl(fileName, 3600 * 24 * 7);
|
|
70
|
-
if (signedError)
|
|
71
|
-
throw signedError;
|
|
72
|
-
attachmentUrl = signedData?.signedUrl || null;
|
|
73
|
-
}
|
|
74
|
-
const { error } = await supabase.from("bugs").insert({
|
|
75
|
-
title: title.trim(),
|
|
76
|
-
description: description.trim(),
|
|
77
|
-
priority: "normaal",
|
|
78
|
-
status: "open",
|
|
79
|
-
attachment_url: attachmentUrl,
|
|
80
|
-
user_id: user.id,
|
|
81
|
-
});
|
|
82
|
-
if (error)
|
|
83
|
-
throw error;
|
|
84
|
-
toast.success("Bug gemeld!");
|
|
85
|
-
setTitle("");
|
|
86
|
-
setDescription("");
|
|
87
|
-
setAttachment(null);
|
|
88
|
-
setActiveTab("list");
|
|
89
|
-
await fetchBugs();
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
toast.error("Fout bij melden van bug");
|
|
93
|
-
}
|
|
94
|
-
finally {
|
|
95
|
-
setSubmitting(false);
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
const handleFileChange = (e) => {
|
|
99
|
-
const file = e.target.files?.[0];
|
|
100
|
-
if (file) {
|
|
101
|
-
const validation = validateFileUpload(file);
|
|
102
|
-
if (!validation.valid) {
|
|
103
|
-
toast.error(validation.error);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
setAttachment(file);
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
const removeAttachment = () => {
|
|
110
|
-
setAttachment(null);
|
|
111
|
-
if (fileInputRef.current)
|
|
112
|
-
fileInputRef.current.value = "";
|
|
113
|
-
};
|
|
114
|
-
const handleUpdateStatus = async (id, newStatus) => {
|
|
115
|
-
try {
|
|
116
|
-
const { error } = await supabase.from("bugs").update({ status: newStatus }).eq("id", id);
|
|
117
|
-
if (error)
|
|
118
|
-
throw error;
|
|
119
|
-
await fetchBugs();
|
|
120
|
-
toast.success("Status bijgewerkt");
|
|
121
|
-
}
|
|
122
|
-
catch {
|
|
123
|
-
toast.error("Fout bij bijwerken");
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
const handleDelete = async (id) => {
|
|
127
|
-
try {
|
|
128
|
-
const { error } = await supabase.from("bugs").delete().eq("id", id);
|
|
129
|
-
if (error)
|
|
130
|
-
throw error;
|
|
131
|
-
await fetchBugs();
|
|
132
|
-
toast.success("Bug verwijderd");
|
|
133
|
-
}
|
|
134
|
-
catch {
|
|
135
|
-
toast.error("Fout bij verwijderen");
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
const formatDate = (dateStr) => {
|
|
139
|
-
return new Date(dateStr).toLocaleDateString("nl-NL", {
|
|
140
|
-
day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit",
|
|
141
|
-
});
|
|
142
|
-
};
|
|
143
|
-
const acceptString = ALLOWED_FILE_TYPES.join(",");
|
|
144
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "max-w-2xl max-h-[85vh] overflow-hidden flex flex-col", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Bugs" }) }), _jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, className: "flex-1 flex flex-col overflow-hidden", children: [_jsxs(TabsList, { className: "grid w-full grid-cols-2", children: [_jsxs(TabsTrigger, { value: "list", children: ["Overzicht (", bugs.filter(b => b.status !== "resolved").length, ")"] }), _jsxs(TabsTrigger, { value: "new", children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Nieuwe Bug"] })] }), _jsx(TabsContent, { value: "list", className: "flex-1 overflow-y-auto mt-4", children: loading ? (_jsx("div", { className: "flex items-center justify-center py-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }) })) : bugs.length === 0 ? (_jsx("div", { className: "text-center py-8 text-muted-foreground", children: "Geen bugs gemeld \uD83C\uDF89" })) : (_jsx("div", { className: "space-y-3", children: bugs.map((bug) => {
|
|
145
|
-
const status = statusConfig[bug.status] || statusConfig.open;
|
|
146
|
-
return (_jsxs("div", { className: "rounded-lg border border-border p-4 space-y-2", children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { className: "flex-1", children: [_jsx("h4", { className: "font-medium", children: bug.title }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: bug.description }), bug.attachment_url && (_jsxs("a", { href: bug.attachment_url, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 text-xs text-primary hover:underline mt-2", children: [_jsx(FileText, { className: "h-3 w-3" }), "Bijlage bekijken"] }))] }), _jsx(Button, { variant: "ghost", size: "icon", className: "shrink-0", onClick: () => handleDelete(bug.id), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Select, { value: bug.status, onValueChange: (value) => handleUpdateStatus(bug.id, value), children: [_jsx(SelectTrigger, { className: "h-7 w-auto", children: _jsx(Badge, { variant: status.variant, children: status.label }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "open", children: _jsx(Badge, { variant: "destructive", children: "Open" }) }), _jsx(SelectItem, { value: "in_progress", children: _jsx(Badge, { variant: "default", children: "In behandeling" }) }), _jsx(SelectItem, { value: "resolved", children: _jsx(Badge, { variant: "secondary", children: "Opgelost" }) })] })] }), _jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(bug.created_at) })] })] }, bug.id));
|
|
147
|
-
}) })) }), _jsx(TabsContent, { value: "new", className: "flex-1 overflow-y-auto mt-4", children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "bug-title", children: "Titel" }), _jsx(Input, { id: "bug-title", placeholder: "Korte beschrijving van de bug...", value: title, onChange: (e) => setTitle(e.target.value), maxLength: 200 })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "bug-description", children: "Beschrijving" }), _jsx(Textarea, { id: "bug-description", placeholder: "Beschrijf de bug in detail. Wat ging er mis? Wat verwachtte je?", value: description, onChange: (e) => setDescription(e.target.value), rows: 4, maxLength: 5000 })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Bijlage (optioneel)" }), _jsx("input", { type: "file", ref: fileInputRef, onChange: handleFileChange, className: "hidden", accept: acceptString }), attachment ? (_jsxs("div", { className: "flex items-center gap-2 p-2 border rounded-md bg-muted/50", children: [_jsx(FileText, { className: "h-4 w-4 text-muted-foreground" }), _jsx("span", { className: "text-sm flex-1 truncate", children: attachment.name }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6", onClick: removeAttachment, children: _jsx(X, { className: "h-4 w-4" }) })] })) : (_jsxs(Button, { type: "button", variant: "outline", className: "w-full", onClick: () => fileInputRef.current?.click(), children: [_jsx(Paperclip, { className: "mr-2 h-4 w-4" }), "Document toevoegen (max 10MB)"] })), _jsx("p", { className: "text-xs text-muted-foreground", children: "Toegestane bestandstypen: PNG, JPEG, GIF, PDF" })] }), _jsx(Button, { onClick: handleSubmit, disabled: submitting || !title.trim() || !description.trim(), className: "w-full", children: submitting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Melden..."] })) : (_jsxs(_Fragment, { children: [_jsx(CheckCircle, { className: "mr-2 h-4 w-4" }), "Bug Melden"] })) })] }) })] })] }) }));
|
|
148
|
-
}
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useRef } from "react";
|
|
3
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog";
|
|
4
|
+
import { Button } from "../ui/button";
|
|
5
|
+
import { Input } from "../ui/input";
|
|
6
|
+
import { Textarea } from "../ui/textarea";
|
|
7
|
+
import { Label } from "../ui/label";
|
|
8
|
+
import { Badge } from "../ui/badge";
|
|
9
|
+
import { Select, SelectContent, SelectItem, SelectTrigger } from "../ui/select";
|
|
10
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
|
|
11
|
+
import { Plus, Loader2, CheckCircle, Trash2, Paperclip, X, FileText } from "lucide-react";
|
|
12
|
+
import { supabase } from "../../supabase/client";
|
|
13
|
+
import { toast } from "sonner";
|
|
14
|
+
import { bugReportSchema, validateFileUpload, ALLOWED_FILE_TYPES } from "../../lib/validationSchemas";
|
|
15
|
+
const statusConfig = {
|
|
16
|
+
open: { label: "Open", variant: "destructive" },
|
|
17
|
+
in_progress: { label: "In behandeling", variant: "default" },
|
|
18
|
+
resolved: { label: "Opgelost", variant: "secondary" },
|
|
19
|
+
};
|
|
20
|
+
export function BugsDialog({ open, onOpenChange }) {
|
|
21
|
+
const [bugs, setBugs] = useState([]);
|
|
22
|
+
const [loading, setLoading] = useState(true);
|
|
23
|
+
const [submitting, setSubmitting] = useState(false);
|
|
24
|
+
const [activeTab, setActiveTab] = useState("list");
|
|
25
|
+
const fileInputRef = useRef(null);
|
|
26
|
+
const [title, setTitle] = useState("");
|
|
27
|
+
const [description, setDescription] = useState("");
|
|
28
|
+
const [attachment, setAttachment] = useState(null);
|
|
29
|
+
const fetchBugs = async () => {
|
|
30
|
+
try {
|
|
31
|
+
const { data, error } = await supabase
|
|
32
|
+
.from("bugs")
|
|
33
|
+
.select("*")
|
|
34
|
+
.order("created_at", { ascending: false });
|
|
35
|
+
if (error)
|
|
36
|
+
throw error;
|
|
37
|
+
setBugs(data || []);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
toast.error("Fout bij ophalen van bugs");
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
setLoading(false);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (open)
|
|
48
|
+
fetchBugs();
|
|
49
|
+
}, [open]);
|
|
50
|
+
const handleSubmit = async () => {
|
|
51
|
+
const validation = bugReportSchema.safeParse({ title: title.trim(), description: description.trim() });
|
|
52
|
+
if (!validation.success) {
|
|
53
|
+
toast.error(validation.error.issues[0].message);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
setSubmitting(true);
|
|
57
|
+
try {
|
|
58
|
+
const { data: { user } } = await supabase.auth.getUser();
|
|
59
|
+
if (!user)
|
|
60
|
+
throw new Error("Niet ingelogd");
|
|
61
|
+
let attachmentUrl = null;
|
|
62
|
+
if (attachment) {
|
|
63
|
+
const fileExt = attachment.name.split('.').pop();
|
|
64
|
+
const fileName = `${Date.now()}-${Math.random().toString(36).substring(7)}.${fileExt}`;
|
|
65
|
+
const { error: uploadError } = await supabase.storage.from('bug-attachments').upload(fileName, attachment);
|
|
66
|
+
if (uploadError)
|
|
67
|
+
throw uploadError;
|
|
68
|
+
const { data: signedData, error: signedError } = await supabase.storage
|
|
69
|
+
.from('bug-attachments').createSignedUrl(fileName, 3600 * 24 * 7);
|
|
70
|
+
if (signedError)
|
|
71
|
+
throw signedError;
|
|
72
|
+
attachmentUrl = signedData?.signedUrl || null;
|
|
73
|
+
}
|
|
74
|
+
const { error } = await supabase.from("bugs").insert({
|
|
75
|
+
title: title.trim(),
|
|
76
|
+
description: description.trim(),
|
|
77
|
+
priority: "normaal",
|
|
78
|
+
status: "open",
|
|
79
|
+
attachment_url: attachmentUrl,
|
|
80
|
+
user_id: user.id,
|
|
81
|
+
});
|
|
82
|
+
if (error)
|
|
83
|
+
throw error;
|
|
84
|
+
toast.success("Bug gemeld!");
|
|
85
|
+
setTitle("");
|
|
86
|
+
setDescription("");
|
|
87
|
+
setAttachment(null);
|
|
88
|
+
setActiveTab("list");
|
|
89
|
+
await fetchBugs();
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
toast.error("Fout bij melden van bug");
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
setSubmitting(false);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const handleFileChange = (e) => {
|
|
99
|
+
const file = e.target.files?.[0];
|
|
100
|
+
if (file) {
|
|
101
|
+
const validation = validateFileUpload(file);
|
|
102
|
+
if (!validation.valid) {
|
|
103
|
+
toast.error(validation.error);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
setAttachment(file);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const removeAttachment = () => {
|
|
110
|
+
setAttachment(null);
|
|
111
|
+
if (fileInputRef.current)
|
|
112
|
+
fileInputRef.current.value = "";
|
|
113
|
+
};
|
|
114
|
+
const handleUpdateStatus = async (id, newStatus) => {
|
|
115
|
+
try {
|
|
116
|
+
const { error } = await supabase.from("bugs").update({ status: newStatus }).eq("id", id);
|
|
117
|
+
if (error)
|
|
118
|
+
throw error;
|
|
119
|
+
await fetchBugs();
|
|
120
|
+
toast.success("Status bijgewerkt");
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
toast.error("Fout bij bijwerken");
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const handleDelete = async (id) => {
|
|
127
|
+
try {
|
|
128
|
+
const { error } = await supabase.from("bugs").delete().eq("id", id);
|
|
129
|
+
if (error)
|
|
130
|
+
throw error;
|
|
131
|
+
await fetchBugs();
|
|
132
|
+
toast.success("Bug verwijderd");
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
toast.error("Fout bij verwijderen");
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
const formatDate = (dateStr) => {
|
|
139
|
+
return new Date(dateStr).toLocaleDateString("nl-NL", {
|
|
140
|
+
day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit",
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
const acceptString = ALLOWED_FILE_TYPES.join(",");
|
|
144
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "max-w-2xl max-h-[85vh] overflow-hidden flex flex-col", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Bugs" }) }), _jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, className: "flex-1 flex flex-col overflow-hidden", children: [_jsxs(TabsList, { className: "grid w-full grid-cols-2", children: [_jsxs(TabsTrigger, { value: "list", children: ["Overzicht (", bugs.filter(b => b.status !== "resolved").length, ")"] }), _jsxs(TabsTrigger, { value: "new", children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Nieuwe Bug"] })] }), _jsx(TabsContent, { value: "list", className: "flex-1 overflow-y-auto mt-4", children: loading ? (_jsx("div", { className: "flex items-center justify-center py-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }) })) : bugs.length === 0 ? (_jsx("div", { className: "text-center py-8 text-muted-foreground", children: "Geen bugs gemeld \uD83C\uDF89" })) : (_jsx("div", { className: "space-y-3", children: bugs.map((bug) => {
|
|
145
|
+
const status = statusConfig[bug.status] || statusConfig.open;
|
|
146
|
+
return (_jsxs("div", { className: "rounded-lg border border-border p-4 space-y-2", children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { className: "flex-1", children: [_jsx("h4", { className: "font-medium", children: bug.title }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: bug.description }), bug.attachment_url && (_jsxs("a", { href: bug.attachment_url, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 text-xs text-primary hover:underline mt-2", children: [_jsx(FileText, { className: "h-3 w-3" }), "Bijlage bekijken"] }))] }), _jsx(Button, { variant: "ghost", size: "icon", className: "shrink-0", onClick: () => handleDelete(bug.id), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Select, { value: bug.status, onValueChange: (value) => handleUpdateStatus(bug.id, value), children: [_jsx(SelectTrigger, { className: "h-7 w-auto", children: _jsx(Badge, { variant: status.variant, children: status.label }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "open", children: _jsx(Badge, { variant: "destructive", children: "Open" }) }), _jsx(SelectItem, { value: "in_progress", children: _jsx(Badge, { variant: "default", children: "In behandeling" }) }), _jsx(SelectItem, { value: "resolved", children: _jsx(Badge, { variant: "secondary", children: "Opgelost" }) })] })] }), _jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(bug.created_at) })] })] }, bug.id));
|
|
147
|
+
}) })) }), _jsx(TabsContent, { value: "new", className: "flex-1 overflow-y-auto mt-4", children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "bug-title", children: "Titel" }), _jsx(Input, { id: "bug-title", placeholder: "Korte beschrijving van de bug...", value: title, onChange: (e) => setTitle(e.target.value), maxLength: 200 })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "bug-description", children: "Beschrijving" }), _jsx(Textarea, { id: "bug-description", placeholder: "Beschrijf de bug in detail. Wat ging er mis? Wat verwachtte je?", value: description, onChange: (e) => setDescription(e.target.value), rows: 4, maxLength: 5000 })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Bijlage (optioneel)" }), _jsx("input", { type: "file", ref: fileInputRef, onChange: handleFileChange, className: "hidden", accept: acceptString }), attachment ? (_jsxs("div", { className: "flex items-center gap-2 p-2 border rounded-md bg-muted/50", children: [_jsx(FileText, { className: "h-4 w-4 text-muted-foreground" }), _jsx("span", { className: "text-sm flex-1 truncate", children: attachment.name }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6", onClick: removeAttachment, children: _jsx(X, { className: "h-4 w-4" }) })] })) : (_jsxs(Button, { type: "button", variant: "outline", className: "w-full", onClick: () => fileInputRef.current?.click(), children: [_jsx(Paperclip, { className: "mr-2 h-4 w-4" }), "Document toevoegen (max 10MB)"] })), _jsx("p", { className: "text-xs text-muted-foreground", children: "Toegestane bestandstypen: PNG, JPEG, GIF, PDF" })] }), _jsx(Button, { onClick: handleSubmit, disabled: submitting || !title.trim() || !description.trim(), className: "w-full", children: submitting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Melden..."] })) : (_jsxs(_Fragment, { children: [_jsx(CheckCircle, { className: "mr-2 h-4 w-4" }), "Bug Melden"] })) })] }) })] })] }) }));
|
|
148
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
interface HeaderProps {
|
|
2
|
-
title: string;
|
|
3
|
-
}
|
|
4
|
-
export declare function Header({ title }: HeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
export {};
|
|
1
|
+
interface HeaderProps {
|
|
2
|
+
title: string;
|
|
3
|
+
}
|
|
4
|
+
export declare function Header({ title }: HeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export {};
|
|
6
6
|
//# sourceMappingURL=Header.d.ts.map
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect } from "react";
|
|
3
|
-
import { Bug, Search } from "lucide-react";
|
|
4
|
-
import { Input } from "../ui/input";
|
|
5
|
-
import { Button } from "../ui/button";
|
|
6
|
-
import { BugsDialog } from "./BugsDialog";
|
|
7
|
-
import { NotificationBell } from "./NotificationBell";
|
|
8
|
-
export function Header({ title }) {
|
|
9
|
-
const [bugsDialogOpen, setBugsDialogOpen] = useState(false);
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
fetch("/huisstijl.json")
|
|
12
|
-
.then((res) => {
|
|
13
|
-
if (!res.ok)
|
|
14
|
-
return;
|
|
15
|
-
return res.json();
|
|
16
|
-
})
|
|
17
|
-
.then((stijl) => {
|
|
18
|
-
if (!stijl)
|
|
19
|
-
return;
|
|
20
|
-
const root = document.documentElement;
|
|
21
|
-
Object.entries(stijl).forEach(([key, value]) => {
|
|
22
|
-
root.style.setProperty(`--${key}`, value);
|
|
23
|
-
});
|
|
24
|
-
})
|
|
25
|
-
.catch(() => {
|
|
26
|
-
// huisstijl.json bestaat niet of is ongeldig — geen actie
|
|
27
|
-
});
|
|
28
|
-
}, []);
|
|
29
|
-
return (_jsxs(_Fragment, { children: [_jsxs("header", { className: "sticky top-0 z-30 flex h-16 items-center justify-between border-b border-border bg-card px-6", children: [_jsx("h1", { className: "text-xl font-semibold text-foreground", children: title }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "relative hidden md:block", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoeken...", className: "w-64 pl-9" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setBugsDialogOpen(true), children: [_jsx(Bug, { className: "mr-2 h-4 w-4" }), "Bugs melden"] }), _jsx(NotificationBell, {})] })] }), _jsx(BugsDialog, { open: bugsDialogOpen, onOpenChange: setBugsDialogOpen })] }));
|
|
30
|
-
}
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { Bug, Search } from "lucide-react";
|
|
4
|
+
import { Input } from "../ui/input";
|
|
5
|
+
import { Button } from "../ui/button";
|
|
6
|
+
import { BugsDialog } from "./BugsDialog";
|
|
7
|
+
import { NotificationBell } from "./NotificationBell";
|
|
8
|
+
export function Header({ title }) {
|
|
9
|
+
const [bugsDialogOpen, setBugsDialogOpen] = useState(false);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
fetch("/huisstijl.json")
|
|
12
|
+
.then((res) => {
|
|
13
|
+
if (!res.ok)
|
|
14
|
+
return;
|
|
15
|
+
return res.json();
|
|
16
|
+
})
|
|
17
|
+
.then((stijl) => {
|
|
18
|
+
if (!stijl)
|
|
19
|
+
return;
|
|
20
|
+
const root = document.documentElement;
|
|
21
|
+
Object.entries(stijl).forEach(([key, value]) => {
|
|
22
|
+
root.style.setProperty(`--${key}`, value);
|
|
23
|
+
});
|
|
24
|
+
})
|
|
25
|
+
.catch(() => {
|
|
26
|
+
// huisstijl.json bestaat niet of is ongeldig — geen actie
|
|
27
|
+
});
|
|
28
|
+
}, []);
|
|
29
|
+
return (_jsxs(_Fragment, { children: [_jsxs("header", { className: "sticky top-0 z-30 flex h-16 items-center justify-between border-b border-border bg-card px-6", children: [_jsx("h1", { className: "text-xl font-semibold text-foreground", children: title }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "relative hidden md:block", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoeken...", className: "w-64 pl-9" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setBugsDialogOpen(true), children: [_jsx(Bug, { className: "mr-2 h-4 w-4" }), "Bugs melden"] }), _jsx(NotificationBell, {})] })] }), _jsx(BugsDialog, { open: bugsDialogOpen, onOpenChange: setBugsDialogOpen })] }));
|
|
30
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { FlowModule } from "../../types/module";
|
|
2
|
-
export declare function InstellingenPage({ modules }: {
|
|
3
|
-
modules: FlowModule[];
|
|
4
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
1
|
+
import type { FlowModule } from "../../types/module";
|
|
2
|
+
export declare function InstellingenPage({ modules }: {
|
|
3
|
+
modules: FlowModule[];
|
|
4
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
//# sourceMappingURL=InstellingenPage.d.ts.map
|
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
3
|
-
import { Button } from "../ui/button";
|
|
4
|
-
import { Input } from "../ui/input";
|
|
5
|
-
import { Label } from "../ui/label";
|
|
6
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card";
|
|
7
|
-
import { supabase } from "../../supabase/client";
|
|
8
|
-
import { toast } from "sonner";
|
|
9
|
-
import { Loader2, User } from "lucide-react";
|
|
10
|
-
import { NotificationSettingsCard } from "../instellingen/NotificationSettingsCard";
|
|
11
|
-
// ─── Profile Card ─────────────────────────────────────────────────────────────
|
|
12
|
-
function ProfileCard() {
|
|
13
|
-
const [displayName, setDisplayName] = useState("");
|
|
14
|
-
const [saving, setSaving] = useState(false);
|
|
15
|
-
const handleUpdateProfile = async () => {
|
|
16
|
-
setSaving(true);
|
|
17
|
-
try {
|
|
18
|
-
const { data: { user } } = await supabase.auth.getUser();
|
|
19
|
-
if (!user)
|
|
20
|
-
throw new Error("Niet ingelogd");
|
|
21
|
-
const { error } = await supabase.from("profiles").update({ display_name: displayName.trim() }).eq("id", user.id);
|
|
22
|
-
if (error)
|
|
23
|
-
throw error;
|
|
24
|
-
toast.success("Profiel bijgewerkt");
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
toast.error("Fout bij het bijwerken van profiel");
|
|
28
|
-
}
|
|
29
|
-
finally {
|
|
30
|
-
setSaving(false);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(User, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Profielgegevens" })] }), _jsx(CardDescription, { children: "Werk je persoonlijke gegevens bij" })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "displayName", children: "Weergavenaam" }), _jsx(Input, { id: "displayName", value: displayName, onChange: (e) => setDisplayName(e.target.value), maxLength: 100, placeholder: "Je naam" })] }), _jsxs(Button, { onClick: handleUpdateProfile, disabled: saving, children: [saving && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Opslaan"] })] })] }));
|
|
34
|
-
}
|
|
35
|
-
// ─── Password Card ────────────────────────────────────────────────────────────
|
|
36
|
-
function PasswordCard() {
|
|
37
|
-
const [newPassword, setNewPassword] = useState("");
|
|
38
|
-
const [confirmPassword, setConfirmPassword] = useState("");
|
|
39
|
-
const [changingPassword, setChangingPassword] = useState(false);
|
|
40
|
-
const handleChangePassword = async () => {
|
|
41
|
-
if (newPassword.length < 6) {
|
|
42
|
-
toast.error("Wachtwoord moet minimaal 6 tekens bevatten");
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
if (newPassword !== confirmPassword) {
|
|
46
|
-
toast.error("Wachtwoorden komen niet overeen");
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
setChangingPassword(true);
|
|
50
|
-
try {
|
|
51
|
-
const { error } = await supabase.auth.updateUser({ password: newPassword });
|
|
52
|
-
if (error)
|
|
53
|
-
throw error;
|
|
54
|
-
toast.success("Wachtwoord bijgewerkt");
|
|
55
|
-
setNewPassword("");
|
|
56
|
-
setConfirmPassword("");
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
toast.error("Fout bij het wijzigen van wachtwoord");
|
|
60
|
-
}
|
|
61
|
-
finally {
|
|
62
|
-
setChangingPassword(false);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Wachtwoord wijzigen" }), _jsx(CardDescription, { children: "Kies een nieuw wachtwoord" })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "newPassword", children: "Nieuw wachtwoord" }), _jsx(Input, { id: "newPassword", type: "password", value: newPassword, onChange: (e) => setNewPassword(e.target.value), maxLength: 128, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "confirmPassword", children: "Bevestig wachtwoord" }), _jsx(Input, { id: "confirmPassword", type: "password", value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), maxLength: 128, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsxs(Button, { onClick: handleChangePassword, disabled: changingPassword, children: [changingPassword && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Wachtwoord wijzigen"] })] })] }));
|
|
66
|
-
}
|
|
67
|
-
// ─── Core cards registry ──────────────────────────────────────────────────────
|
|
68
|
-
const coreCards = [
|
|
69
|
-
{ component: ProfileCard, order: 0 },
|
|
70
|
-
{ component: PasswordCard, order: 1 },
|
|
71
|
-
{ component: NotificationSettingsCard, order: 2 },
|
|
72
|
-
];
|
|
73
|
-
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
74
|
-
export function InstellingenPage({ modules }) {
|
|
75
|
-
const moduleCards = modules.flatMap((m) => m.settingsCards);
|
|
76
|
-
const allCards = [...coreCards, ...moduleCards].sort((a, b) => a.order - b.order);
|
|
77
|
-
return (_jsx("main", { className: "p-6", children: _jsx("div", { className: "grid gap-6 lg:grid-cols-2", children: allCards.map((card, i) => _jsx(card.component, {}, i)) }) }));
|
|
78
|
-
}
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Button } from "../ui/button";
|
|
4
|
+
import { Input } from "../ui/input";
|
|
5
|
+
import { Label } from "../ui/label";
|
|
6
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card";
|
|
7
|
+
import { supabase } from "../../supabase/client";
|
|
8
|
+
import { toast } from "sonner";
|
|
9
|
+
import { Loader2, User } from "lucide-react";
|
|
10
|
+
import { NotificationSettingsCard } from "../instellingen/NotificationSettingsCard";
|
|
11
|
+
// ─── Profile Card ─────────────────────────────────────────────────────────────
|
|
12
|
+
function ProfileCard() {
|
|
13
|
+
const [displayName, setDisplayName] = useState("");
|
|
14
|
+
const [saving, setSaving] = useState(false);
|
|
15
|
+
const handleUpdateProfile = async () => {
|
|
16
|
+
setSaving(true);
|
|
17
|
+
try {
|
|
18
|
+
const { data: { user } } = await supabase.auth.getUser();
|
|
19
|
+
if (!user)
|
|
20
|
+
throw new Error("Niet ingelogd");
|
|
21
|
+
const { error } = await supabase.from("profiles").update({ display_name: displayName.trim() }).eq("id", user.id);
|
|
22
|
+
if (error)
|
|
23
|
+
throw error;
|
|
24
|
+
toast.success("Profiel bijgewerkt");
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
toast.error("Fout bij het bijwerken van profiel");
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
setSaving(false);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(User, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Profielgegevens" })] }), _jsx(CardDescription, { children: "Werk je persoonlijke gegevens bij" })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "displayName", children: "Weergavenaam" }), _jsx(Input, { id: "displayName", value: displayName, onChange: (e) => setDisplayName(e.target.value), maxLength: 100, placeholder: "Je naam" })] }), _jsxs(Button, { onClick: handleUpdateProfile, disabled: saving, children: [saving && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Opslaan"] })] })] }));
|
|
34
|
+
}
|
|
35
|
+
// ─── Password Card ────────────────────────────────────────────────────────────
|
|
36
|
+
function PasswordCard() {
|
|
37
|
+
const [newPassword, setNewPassword] = useState("");
|
|
38
|
+
const [confirmPassword, setConfirmPassword] = useState("");
|
|
39
|
+
const [changingPassword, setChangingPassword] = useState(false);
|
|
40
|
+
const handleChangePassword = async () => {
|
|
41
|
+
if (newPassword.length < 6) {
|
|
42
|
+
toast.error("Wachtwoord moet minimaal 6 tekens bevatten");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (newPassword !== confirmPassword) {
|
|
46
|
+
toast.error("Wachtwoorden komen niet overeen");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
setChangingPassword(true);
|
|
50
|
+
try {
|
|
51
|
+
const { error } = await supabase.auth.updateUser({ password: newPassword });
|
|
52
|
+
if (error)
|
|
53
|
+
throw error;
|
|
54
|
+
toast.success("Wachtwoord bijgewerkt");
|
|
55
|
+
setNewPassword("");
|
|
56
|
+
setConfirmPassword("");
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
toast.error("Fout bij het wijzigen van wachtwoord");
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
setChangingPassword(false);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Wachtwoord wijzigen" }), _jsx(CardDescription, { children: "Kies een nieuw wachtwoord" })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "newPassword", children: "Nieuw wachtwoord" }), _jsx(Input, { id: "newPassword", type: "password", value: newPassword, onChange: (e) => setNewPassword(e.target.value), maxLength: 128, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "confirmPassword", children: "Bevestig wachtwoord" }), _jsx(Input, { id: "confirmPassword", type: "password", value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), maxLength: 128, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" })] }), _jsxs(Button, { onClick: handleChangePassword, disabled: changingPassword, children: [changingPassword && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Wachtwoord wijzigen"] })] })] }));
|
|
66
|
+
}
|
|
67
|
+
// ─── Core cards registry ──────────────────────────────────────────────────────
|
|
68
|
+
const coreCards = [
|
|
69
|
+
{ component: ProfileCard, order: 0 },
|
|
70
|
+
{ component: PasswordCard, order: 1 },
|
|
71
|
+
{ component: NotificationSettingsCard, order: 2 },
|
|
72
|
+
];
|
|
73
|
+
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
74
|
+
export function InstellingenPage({ modules }) {
|
|
75
|
+
const moduleCards = modules.flatMap((m) => m.settingsCards);
|
|
76
|
+
const allCards = [...coreCards, ...moduleCards].sort((a, b) => a.order - b.order);
|
|
77
|
+
return (_jsx("main", { className: "p-6", children: _jsx("div", { className: "grid gap-6 lg:grid-cols-2", children: allCards.map((card, i) => _jsx(card.component, {}, i)) }) }));
|
|
78
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
interface LoginPageProps {
|
|
2
|
-
redirectTo: string;
|
|
3
|
-
}
|
|
4
|
-
export declare function LoginPage({ redirectTo }: LoginPageProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
export {};
|
|
1
|
+
interface LoginPageProps {
|
|
2
|
+
redirectTo: string;
|
|
3
|
+
}
|
|
4
|
+
export declare function LoginPage({ redirectTo }: LoginPageProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export {};
|
|
6
6
|
//# sourceMappingURL=LoginPage.d.ts.map
|