@lodashventure/medusa-membership 0.4.13 → 0.4.15

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.
@@ -0,0 +1,1102 @@
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
3
+ import { toast, Container, Heading, Switch, Label, Text, Input, Button, createDataTableColumnHelper, usePrompt, useDataTable, DataTable, Drawer, Textarea } from "@medusajs/ui";
4
+ import axios from "axios";
5
+ import { useState, useEffect } from "react";
6
+ import Medusa from "@medusajs/js-sdk";
7
+ import { Users } from "@medusajs/icons";
8
+ import "@medusajs/admin-shared";
9
+ const useMembershipConfig = (storeId, options) => {
10
+ const [data, setData] = useState(void 0);
11
+ const [isLoading, setIsLoading] = useState(true);
12
+ const [error, setError] = useState(null);
13
+ const fetchConfig = async () => {
14
+ if (!storeId) {
15
+ setError(new Error("No store id found"));
16
+ setIsLoading(false);
17
+ return;
18
+ }
19
+ try {
20
+ setIsLoading(true);
21
+ setError(null);
22
+ const { data: config2 } = await axios.get(
23
+ `/admin/membership/config/${storeId}`
24
+ );
25
+ setData(config2);
26
+ } catch (err) {
27
+ setError(
28
+ err instanceof Error ? err : new Error("Failed to fetch config")
29
+ );
30
+ } finally {
31
+ setIsLoading(false);
32
+ }
33
+ };
34
+ useEffect(() => {
35
+ fetchConfig();
36
+ }, [storeId, options == null ? void 0 : options.enabled]);
37
+ return {
38
+ data,
39
+ isLoading,
40
+ error,
41
+ refetch: fetchConfig
42
+ };
43
+ };
44
+ const sdk = new Medusa({
45
+ baseUrl: "/",
46
+ debug: false,
47
+ auth: {
48
+ type: "session"
49
+ }
50
+ });
51
+ const useStore = () => {
52
+ const [data, setData] = useState(void 0);
53
+ const [isLoading, setIsLoading] = useState(true);
54
+ const [error, setError] = useState(null);
55
+ const fetchStore = async () => {
56
+ var _a;
57
+ try {
58
+ setIsLoading(true);
59
+ setError(null);
60
+ const response = await sdk.admin.store.list();
61
+ const activeStore = (_a = response.stores) == null ? void 0 : _a[0];
62
+ if (!activeStore) {
63
+ throw new Error("No active store found");
64
+ }
65
+ setData(activeStore);
66
+ } catch (err) {
67
+ setError(err instanceof Error ? err : new Error("Failed to fetch store"));
68
+ } finally {
69
+ setIsLoading(false);
70
+ }
71
+ };
72
+ useEffect(() => {
73
+ fetchStore();
74
+ }, []);
75
+ return {
76
+ data,
77
+ isLoading,
78
+ error,
79
+ refetch: fetchStore
80
+ };
81
+ };
82
+ const usePromotionCollectable = (id) => {
83
+ const [data, setData] = useState(void 0);
84
+ const [isLoading, setIsLoading] = useState(true);
85
+ const [error, setError] = useState(null);
86
+ const fetchCollectable = async () => {
87
+ try {
88
+ setIsLoading(true);
89
+ setError(null);
90
+ const response = await axios.get(
91
+ `/admin/promotions/${id}/update-collectable`
92
+ );
93
+ setData(response);
94
+ } catch (err) {
95
+ setError(
96
+ err instanceof Error ? err : new Error("Failed to fetch collectable")
97
+ );
98
+ } finally {
99
+ setIsLoading(false);
100
+ }
101
+ };
102
+ useEffect(() => {
103
+ fetchCollectable();
104
+ }, [id]);
105
+ return {
106
+ data,
107
+ isLoading,
108
+ error,
109
+ refetch: fetchCollectable
110
+ };
111
+ };
112
+ const useUpdatePromotionCollectable = (id) => {
113
+ const [isPending, setIsPending] = useState(false);
114
+ const [error, setError] = useState(null);
115
+ const mutate = async (data, { onSuccess } = {}) => {
116
+ try {
117
+ setIsPending(true);
118
+ setError(null);
119
+ const response = await axios.patch(
120
+ `/admin/promotions/${id}/update-collectable`,
121
+ data
122
+ );
123
+ toast.success("Coupon collection updated successfully", {
124
+ dismissable: true,
125
+ duration: 2500
126
+ });
127
+ if (onSuccess) {
128
+ onSuccess();
129
+ }
130
+ return response.data;
131
+ } catch (err) {
132
+ const error2 = err instanceof Error ? err : new Error("Failed to update coupon collection");
133
+ setError(error2);
134
+ toast.error("Failed to update coupon collection", {
135
+ dismissable: true,
136
+ duration: 2500
137
+ });
138
+ throw error2;
139
+ } finally {
140
+ setIsPending(false);
141
+ }
142
+ };
143
+ return {
144
+ mutate,
145
+ isPending,
146
+ error
147
+ };
148
+ };
149
+ const PromotionWidget = ({ data }) => {
150
+ const { id } = data;
151
+ const { data: collectable } = usePromotionCollectable(id);
152
+ const { data: store } = useStore();
153
+ const { data: config2 } = useMembershipConfig(store == null ? void 0 : store.id);
154
+ const { mutate: updateCollectable } = useUpdatePromotionCollectable(id);
155
+ const handleToggleCollectable = async (checked) => {
156
+ try {
157
+ updateCollectable({
158
+ enabled: checked
159
+ });
160
+ toast.success("Coupon collection updated successfully", {
161
+ dismissable: true,
162
+ duration: 2500
163
+ });
164
+ } catch (error) {
165
+ toast.error("Failed to update coupon collection", {
166
+ dismissable: true,
167
+ duration: 2500
168
+ });
169
+ }
170
+ };
171
+ if (!(config2 == null ? void 0 : config2.enable_promotion_collection)) {
172
+ return null;
173
+ }
174
+ if (data.is_automatic) {
175
+ return null;
176
+ }
177
+ return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-6 gap-4", children: [
178
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Coupon Collection" }) }),
179
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 py-2", children: [
180
+ /* @__PURE__ */ jsx(
181
+ Switch,
182
+ {
183
+ checked: collectable == null ? void 0 : collectable.data.enabled,
184
+ onCheckedChange: handleToggleCollectable
185
+ }
186
+ ),
187
+ /* @__PURE__ */ jsx(Label, { children: "Can be collected by customer" })
188
+ ] })
189
+ ] });
190
+ };
191
+ defineWidgetConfig({
192
+ zone: "promotion.details.side.after"
193
+ });
194
+ const useUpdateMembershipConfig = () => {
195
+ const [isPending, setIsPending] = useState(false);
196
+ const [error, setError] = useState(null);
197
+ const mutate = async ({ storeId, config: config2 }, {
198
+ onSuccess
199
+ } = {}) => {
200
+ if (!storeId) {
201
+ throw new Error("No store id found");
202
+ }
203
+ try {
204
+ setIsPending(true);
205
+ setError(null);
206
+ const response = await axios.patch(
207
+ `/admin/membership/config/${storeId}`,
208
+ config2
209
+ );
210
+ if (onSuccess) {
211
+ onSuccess(response.data);
212
+ }
213
+ return response.data;
214
+ } catch (err) {
215
+ const error2 = err instanceof Error ? err : new Error("Failed to update config");
216
+ setError(error2);
217
+ throw error2;
218
+ } finally {
219
+ setIsPending(false);
220
+ }
221
+ };
222
+ return {
223
+ mutate,
224
+ isPending,
225
+ error
226
+ };
227
+ };
228
+ const MembershipPage = () => {
229
+ const { data: store } = useStore();
230
+ const [config2, setConfig] = useState({
231
+ enable_membership: false,
232
+ membership_points_multiplier: 1,
233
+ enable_membership_notification: false,
234
+ enable_wishlist: true,
235
+ enable_promotion_collection: true
236
+ });
237
+ const { data: curConfig, error, refetch } = useMembershipConfig(store == null ? void 0 : store.id);
238
+ const { mutate: saveConfig, isPending } = useUpdateMembershipConfig();
239
+ const [spendingAmount, setSpendingAmount] = useState(
240
+ 1 / config2.membership_points_multiplier
241
+ );
242
+ useEffect(() => {
243
+ if (curConfig) {
244
+ setConfig(curConfig);
245
+ setSpendingAmount(1 / curConfig.membership_points_multiplier);
246
+ }
247
+ }, [curConfig]);
248
+ const handleSave = async () => {
249
+ var _a, _b;
250
+ try {
251
+ await saveConfig({
252
+ storeId: store == null ? void 0 : store.id,
253
+ config: config2
254
+ });
255
+ toast.success("Settings saved successfully", {
256
+ dismissable: true,
257
+ duration: 2500
258
+ });
259
+ await refetch();
260
+ } catch (error2) {
261
+ if (axios.isAxiosError(error2)) {
262
+ toast.error("Error saving settings", {
263
+ description: (_b = (_a = error2.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message,
264
+ dismissable: true,
265
+ duration: 2500
266
+ });
267
+ } else {
268
+ toast.error("Error saving settings", {
269
+ dismissable: true,
270
+ duration: 2500
271
+ });
272
+ }
273
+ }
274
+ };
275
+ if (error || !config2) {
276
+ return /* @__PURE__ */ jsxs("div", { children: [
277
+ "Error: ",
278
+ error == null ? void 0 : error.message
279
+ ] });
280
+ }
281
+ return /* @__PURE__ */ jsxs(Container, { className: "p-8 rounded-lg", children: [
282
+ /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold mb-4", children: "Membership Settings" }),
283
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
284
+ /* @__PURE__ */ jsx(
285
+ Switch,
286
+ {
287
+ checked: config2.enable_membership,
288
+ onCheckedChange: (checked) => setConfig((prev) => ({ ...prev, enable_membership: checked }))
289
+ }
290
+ ),
291
+ "Enable Membership points"
292
+ ] }),
293
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 mt-4", children: [
294
+ /* @__PURE__ */ jsx(Text, { children: "Spending" }),
295
+ /* @__PURE__ */ jsx(
296
+ Input,
297
+ {
298
+ className: "w-32",
299
+ type: "number",
300
+ value: spendingAmount,
301
+ onChange: (e) => {
302
+ const value = Number(e.target.value) || 0;
303
+ const ratio = 1 / value;
304
+ setConfig((prev) => ({
305
+ ...prev,
306
+ membership_points_multiplier: ratio
307
+ }));
308
+ setSpendingAmount(value);
309
+ }
310
+ }
311
+ ),
312
+ /* @__PURE__ */ jsx(Text, { children: "To get 1 point" })
313
+ ] }),
314
+ /* @__PURE__ */ jsx("hr", { className: "my-4" }),
315
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 my-2", children: [
316
+ /* @__PURE__ */ jsx(
317
+ Switch,
318
+ {
319
+ checked: config2.enable_membership_notification,
320
+ onCheckedChange: (checked) => setConfig((prev) => ({
321
+ ...prev,
322
+ enable_membership_notification: checked
323
+ }))
324
+ }
325
+ ),
326
+ "Enable Membership Notification"
327
+ ] }),
328
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 my-2", children: [
329
+ /* @__PURE__ */ jsx(
330
+ Switch,
331
+ {
332
+ checked: config2.enable_wishlist,
333
+ onCheckedChange: (checked) => setConfig((prev) => ({
334
+ ...prev,
335
+ enable_wishlist: checked
336
+ }))
337
+ }
338
+ ),
339
+ "Enable Membership Wishlist"
340
+ ] }),
341
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 my-2", children: [
342
+ /* @__PURE__ */ jsx(
343
+ Switch,
344
+ {
345
+ checked: config2.enable_promotion_collection,
346
+ onCheckedChange: (checked) => setConfig((prev) => ({
347
+ ...prev,
348
+ enable_promotion_collection: checked
349
+ }))
350
+ }
351
+ ),
352
+ "Enable Promotion Collection"
353
+ ] }),
354
+ /* @__PURE__ */ jsx(Button, { onClick: handleSave, disabled: isPending, className: "mt-5", children: isPending ? "Saving..." : "Save Settings" })
355
+ ] });
356
+ };
357
+ const BannerUpload = () => {
358
+ return /* @__PURE__ */ jsx(MembershipPage, {});
359
+ };
360
+ const config$2 = defineRouteConfig({
361
+ label: "Membership",
362
+ icon: Users
363
+ });
364
+ const useCreateMembershipConsent = () => {
365
+ const [isPending, setIsPending] = useState(false);
366
+ const [error, setError] = useState(null);
367
+ const mutate = async (data, { onSuccess } = {}) => {
368
+ try {
369
+ setIsPending(true);
370
+ setError(null);
371
+ const response = await axios.post("/admin/membership-consents", data);
372
+ if (onSuccess) {
373
+ onSuccess();
374
+ }
375
+ return response.data;
376
+ } catch (err) {
377
+ const error2 = err instanceof Error ? err : new Error("Failed to create consent");
378
+ setError(error2);
379
+ throw error2;
380
+ } finally {
381
+ setIsPending(false);
382
+ }
383
+ };
384
+ return {
385
+ mutate,
386
+ isPending,
387
+ error
388
+ };
389
+ };
390
+ const useDeleteMembershipConsent = () => {
391
+ const [isPending, setIsPending] = useState(false);
392
+ const [error, setError] = useState(null);
393
+ const mutate = async (id, { onSuccess } = {}) => {
394
+ try {
395
+ setIsPending(true);
396
+ setError(null);
397
+ await axios.delete(`/admin/membership-consents/${id}`);
398
+ if (onSuccess) {
399
+ onSuccess();
400
+ }
401
+ } catch (err) {
402
+ const error2 = err instanceof Error ? err : new Error("Failed to delete consent");
403
+ setError(error2);
404
+ throw error2;
405
+ } finally {
406
+ setIsPending(false);
407
+ }
408
+ };
409
+ return {
410
+ mutate,
411
+ isPending,
412
+ error
413
+ };
414
+ };
415
+ const useMembershipConsents = () => {
416
+ const [data, setData] = useState(void 0);
417
+ const [isLoading, setIsLoading] = useState(true);
418
+ const [error, setError] = useState(null);
419
+ const fetchConsents = async () => {
420
+ try {
421
+ setIsLoading(true);
422
+ setError(null);
423
+ const response = await axios.get("/admin/membership-consents");
424
+ setData(
425
+ response.data
426
+ );
427
+ } catch (err) {
428
+ setError(
429
+ err instanceof Error ? err : new Error("Failed to fetch consents")
430
+ );
431
+ } finally {
432
+ setIsLoading(false);
433
+ }
434
+ };
435
+ useEffect(() => {
436
+ fetchConsents();
437
+ }, []);
438
+ return {
439
+ data,
440
+ isLoading,
441
+ error,
442
+ refetch: fetchConsents
443
+ };
444
+ };
445
+ const useUpdateMembershipConsent = () => {
446
+ const [isPending, setIsPending] = useState(false);
447
+ const [error, setError] = useState(null);
448
+ const mutate = async (consentKey, { onSuccess } = {}) => {
449
+ try {
450
+ setIsPending(true);
451
+ setError(null);
452
+ const response = await axios.patch(
453
+ `/admin/membership-consents/${consentKey.id}`,
454
+ {
455
+ name: consentKey.name,
456
+ description: consentKey.description
457
+ }
458
+ );
459
+ if (onSuccess) {
460
+ onSuccess();
461
+ }
462
+ return response.data;
463
+ } catch (err) {
464
+ const error2 = err instanceof Error ? err : new Error("Failed to update consent");
465
+ setError(error2);
466
+ throw error2;
467
+ } finally {
468
+ setIsPending(false);
469
+ }
470
+ };
471
+ return {
472
+ mutate,
473
+ isPending,
474
+ error
475
+ };
476
+ };
477
+ const columnHelper$1 = createDataTableColumnHelper();
478
+ const columns$1 = [
479
+ columnHelper$1.accessor("name", {
480
+ header: "Name"
481
+ }),
482
+ columnHelper$1.accessor("description", {
483
+ header: "Description",
484
+ maxSize: 250
485
+ })
486
+ ];
487
+ const MembershipConsentsRenderer = () => {
488
+ const dialog = usePrompt();
489
+ const [pagination, setPagination] = useState({
490
+ pageSize: 1e3,
491
+ pageIndex: 0
492
+ });
493
+ const [isDrawerOpen, setIsDrawerOpen] = useState(false);
494
+ const [editingConsent, setEditingConsent] = useState(null);
495
+ const [formData, setFormData] = useState({
496
+ name: "",
497
+ description: ""
498
+ });
499
+ const { data, isLoading, refetch } = useMembershipConsents();
500
+ const createConsent = useCreateMembershipConsent();
501
+ const updateConsent = useUpdateMembershipConsent();
502
+ const deleteConsent = useDeleteMembershipConsent();
503
+ const table = useDataTable({
504
+ columns: columns$1,
505
+ data: (data == null ? void 0 : data.consent_keys) || [],
506
+ getRowId: (consent) => consent.id,
507
+ rowCount: (data == null ? void 0 : data.count) || 0,
508
+ isLoading,
509
+ pagination: {
510
+ state: pagination,
511
+ onPaginationChange: setPagination
512
+ },
513
+ onRowClick: (_, row) => {
514
+ openDrawer(row.original);
515
+ }
516
+ });
517
+ const handleInputChange = (e) => {
518
+ const { name, value } = e.target;
519
+ setFormData((prev) => ({
520
+ ...prev,
521
+ [name]: name === "min_points" ? Number(value) : value
522
+ }));
523
+ };
524
+ const handleTextAreaChange = (e) => {
525
+ const { name, value } = e.target;
526
+ setFormData((prev) => ({
527
+ ...prev,
528
+ [name]: value
529
+ }));
530
+ };
531
+ const handleReset = () => {
532
+ setFormData({
533
+ name: "",
534
+ description: ""
535
+ });
536
+ };
537
+ const openDrawer = (consentKey) => {
538
+ if (consentKey) {
539
+ setEditingConsent(consentKey);
540
+ setFormData({
541
+ name: consentKey.name,
542
+ description: consentKey.description
543
+ });
544
+ } else {
545
+ setEditingConsent(null);
546
+ handleReset();
547
+ }
548
+ setIsDrawerOpen(true);
549
+ };
550
+ const closeDrawer = () => {
551
+ setIsDrawerOpen(false);
552
+ setEditingConsent(null);
553
+ handleReset();
554
+ refetch();
555
+ };
556
+ const handleSubmit = async () => {
557
+ var _a, _b;
558
+ try {
559
+ if (!formData.name) {
560
+ toast.error("Name is required", { dismissable: true, duration: 2500 });
561
+ return;
562
+ }
563
+ if (editingConsent) {
564
+ await updateConsent.mutateAsync({ id: editingConsent.id, ...formData });
565
+ toast.success("Consent updated successfully", {
566
+ dismissable: true,
567
+ duration: 2500
568
+ });
569
+ } else {
570
+ await createConsent.mutateAsync(formData);
571
+ toast.success("Consent created successfully", {
572
+ dismissable: true,
573
+ duration: 2500
574
+ });
575
+ }
576
+ closeDrawer();
577
+ } catch (error) {
578
+ if (axios.isAxiosError(error)) {
579
+ toast.error("Error saving consent", {
580
+ description: (_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message,
581
+ dismissable: true,
582
+ duration: 2500
583
+ });
584
+ } else {
585
+ toast.error("Error saving consent", {
586
+ dismissable: true,
587
+ duration: 2500
588
+ });
589
+ }
590
+ }
591
+ };
592
+ const handleDelete = async (id) => {
593
+ var _a, _b;
594
+ try {
595
+ const confirmed = await dialog({
596
+ title: "Delete Membership Consent",
597
+ description: `Are you sure you want to delete the \`${editingConsent == null ? void 0 : editingConsent.name}\` consent?`
598
+ });
599
+ if (!confirmed) {
600
+ return;
601
+ }
602
+ await deleteConsent.mutateAsync(id);
603
+ toast.success("Consent deleted successfully", {
604
+ dismissable: true,
605
+ duration: 2500
606
+ });
607
+ closeDrawer();
608
+ } catch (error) {
609
+ if (axios.isAxiosError(error)) {
610
+ toast.error("Error deleting consent", {
611
+ description: (_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message,
612
+ dismissable: true,
613
+ duration: 2500
614
+ });
615
+ } else {
616
+ toast.error("Error deleting consent", {
617
+ dismissable: true,
618
+ duration: 2500
619
+ });
620
+ }
621
+ }
622
+ };
623
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
624
+ /* @__PURE__ */ jsx(Container, { className: "flex flex-col p-0 overflow-hidden", children: /* @__PURE__ */ jsxs(DataTable, { instance: table, children: [
625
+ /* @__PURE__ */ jsxs(DataTable.Toolbar, { className: "flex flex-col items-start justify-between gap-2 md:flex-row md:items-center", children: [
626
+ /* @__PURE__ */ jsx(Heading, { children: "Membership Consents" }),
627
+ /* @__PURE__ */ jsx(Button, { onClick: () => openDrawer(), children: "Add Consent" })
628
+ ] }),
629
+ /* @__PURE__ */ jsx(DataTable.Table, {})
630
+ ] }) }),
631
+ /* @__PURE__ */ jsx(Drawer, { open: isDrawerOpen, onOpenChange: setIsDrawerOpen, children: /* @__PURE__ */ jsxs(Drawer.Content, { children: [
632
+ /* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children: editingConsent ? "Edit Consent" : "Add Consent" }) }),
633
+ /* @__PURE__ */ jsxs(Drawer.Body, { "aria-description": "Membership Consent Form", children: [
634
+ /* @__PURE__ */ jsxs(
635
+ "div",
636
+ {
637
+ className: "space-y-4",
638
+ "aria-description": "Membership Consent Form",
639
+ children: [
640
+ /* @__PURE__ */ jsx(Text, { children: "Name" }),
641
+ /* @__PURE__ */ jsx(
642
+ Input,
643
+ {
644
+ name: "name",
645
+ value: formData.name,
646
+ onChange: handleInputChange
647
+ }
648
+ ),
649
+ /* @__PURE__ */ jsx(Text, { children: "Description" }),
650
+ /* @__PURE__ */ jsx(
651
+ Textarea,
652
+ {
653
+ className: "truncate",
654
+ name: "description",
655
+ value: formData.description,
656
+ onChange: handleTextAreaChange
657
+ }
658
+ )
659
+ ]
660
+ }
661
+ ),
662
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 mt-6", children: [
663
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: closeDrawer, children: "Cancel" }),
664
+ /* @__PURE__ */ jsx(Button, { onClick: handleSubmit, children: editingConsent ? "Update" : "Create" })
665
+ ] })
666
+ ] }),
667
+ /* @__PURE__ */ jsx(Drawer.Footer, { className: "flex justify-start", children: editingConsent && /* @__PURE__ */ jsx(
668
+ Button,
669
+ {
670
+ variant: "danger",
671
+ onClick: () => {
672
+ handleDelete(editingConsent.id);
673
+ },
674
+ children: "Delete"
675
+ }
676
+ ) })
677
+ ] }) })
678
+ ] });
679
+ };
680
+ const MembershipConsentPage = () => {
681
+ return /* @__PURE__ */ jsx(MembershipConsentsRenderer, {});
682
+ };
683
+ const config$1 = defineRouteConfig({
684
+ label: "Membership Consents"
685
+ });
686
+ const useCreateMembershipTier = () => {
687
+ const [isPending, setIsPending] = useState(false);
688
+ const [error, setError] = useState(null);
689
+ const mutate = async (data, { onSuccess } = {}) => {
690
+ try {
691
+ setIsPending(true);
692
+ setError(null);
693
+ const response = await axios.post("/admin/member-tiers", data);
694
+ if (onSuccess) {
695
+ onSuccess();
696
+ }
697
+ return response.data;
698
+ } catch (err) {
699
+ const error2 = err instanceof Error ? err : new Error("Failed to create tier");
700
+ setError(error2);
701
+ throw error2;
702
+ } finally {
703
+ setIsPending(false);
704
+ }
705
+ };
706
+ return {
707
+ mutate,
708
+ isPending,
709
+ error
710
+ };
711
+ };
712
+ const useDeleteMembershipTier = () => {
713
+ const [isPending, setIsPending] = useState(false);
714
+ const [error, setError] = useState(null);
715
+ const mutate = async (id, { onSuccess } = {}) => {
716
+ try {
717
+ setIsPending(true);
718
+ setError(null);
719
+ await axios.delete(`/admin/member-tiers/${id}`);
720
+ if (onSuccess) {
721
+ onSuccess();
722
+ }
723
+ } catch (err) {
724
+ const error2 = err instanceof Error ? err : new Error("Failed to delete tier");
725
+ setError(error2);
726
+ throw error2;
727
+ } finally {
728
+ setIsPending(false);
729
+ }
730
+ };
731
+ return {
732
+ mutate,
733
+ isPending,
734
+ error
735
+ };
736
+ };
737
+ const useMembershipTiers = (params = {
738
+ limit: 15,
739
+ offset: 0,
740
+ order: {
741
+ min_points: "ASC"
742
+ }
743
+ }) => {
744
+ var _a;
745
+ const [data, setData] = useState(void 0);
746
+ const [isLoading, setIsLoading] = useState(true);
747
+ const [error, setError] = useState(null);
748
+ const fetchTiers = async () => {
749
+ try {
750
+ setIsLoading(true);
751
+ setError(null);
752
+ const response = await axios.get("/admin/member-tiers", {
753
+ params
754
+ });
755
+ setData(
756
+ response.data
757
+ );
758
+ } catch (err) {
759
+ setError(err instanceof Error ? err : new Error("Failed to fetch tiers"));
760
+ } finally {
761
+ setIsLoading(false);
762
+ }
763
+ };
764
+ useEffect(() => {
765
+ fetchTiers();
766
+ }, [params.limit, params.offset, (_a = params.order) == null ? void 0 : _a.min_points]);
767
+ return {
768
+ data,
769
+ isLoading,
770
+ error,
771
+ refetch: fetchTiers
772
+ };
773
+ };
774
+ const useUpdateMembershipTier = () => {
775
+ const [isPending, setIsPending] = useState(false);
776
+ const [error, setError] = useState(null);
777
+ const mutate = async (tier, { onSuccess } = {}) => {
778
+ try {
779
+ setIsPending(true);
780
+ setError(null);
781
+ const response = await axios.patch(`/admin/member-tiers/${tier.id}`, {
782
+ name: tier.name,
783
+ description: tier.description,
784
+ privilege_description: tier.privilege_description,
785
+ min_points: tier.min_points
786
+ });
787
+ if (onSuccess) {
788
+ onSuccess();
789
+ }
790
+ return response.data;
791
+ } catch (err) {
792
+ const error2 = err instanceof Error ? err : new Error("Failed to update tier");
793
+ setError(error2);
794
+ throw error2;
795
+ } finally {
796
+ setIsPending(false);
797
+ }
798
+ };
799
+ return {
800
+ mutate,
801
+ isPending,
802
+ error
803
+ };
804
+ };
805
+ const columnHelper = createDataTableColumnHelper();
806
+ const columns = [
807
+ columnHelper.accessor("min_points", {
808
+ header: "Min Points"
809
+ }),
810
+ columnHelper.accessor("name", {
811
+ header: "Name"
812
+ }),
813
+ columnHelper.accessor("description", {
814
+ header: "Description",
815
+ maxSize: 250
816
+ }),
817
+ columnHelper.accessor("privilege_description", {
818
+ header: "Privilege Description",
819
+ maxSize: 250
820
+ })
821
+ ];
822
+ const MembershipTierRenderer = () => {
823
+ const dialog = usePrompt();
824
+ const [pagination, setPagination] = useState({
825
+ pageSize: 1e3,
826
+ pageIndex: 0
827
+ });
828
+ const [isDrawerOpen, setIsDrawerOpen] = useState(false);
829
+ const [editingTier, setEditingTier] = useState(null);
830
+ const [formData, setFormData] = useState({
831
+ name: "",
832
+ description: "",
833
+ privilege_description: "",
834
+ min_points: 0
835
+ });
836
+ const { data, isLoading, refetch } = useMembershipTiers({
837
+ limit: pagination.pageSize,
838
+ offset: pagination.pageIndex * pagination.pageSize
839
+ });
840
+ const createTier = useCreateMembershipTier();
841
+ const updateTier = useUpdateMembershipTier();
842
+ const deleteTier = useDeleteMembershipTier();
843
+ const table = useDataTable({
844
+ columns,
845
+ data: (data == null ? void 0 : data.tiers) || [],
846
+ getRowId: (tier) => tier.id,
847
+ rowCount: (data == null ? void 0 : data.count) || 0,
848
+ isLoading,
849
+ pagination: {
850
+ state: pagination,
851
+ onPaginationChange: setPagination
852
+ },
853
+ onRowClick: (_, row) => {
854
+ openDrawer(row.original);
855
+ }
856
+ });
857
+ const handleInputChange = (e) => {
858
+ const { name, value } = e.target;
859
+ setFormData((prev) => ({
860
+ ...prev,
861
+ [name]: name === "min_points" ? Number(value) : value
862
+ }));
863
+ };
864
+ const handleTextAreaChange = (e) => {
865
+ const { name, value } = e.target;
866
+ setFormData((prev) => ({
867
+ ...prev,
868
+ [name]: value
869
+ }));
870
+ };
871
+ const handleReset = () => {
872
+ setFormData({
873
+ name: "",
874
+ description: "",
875
+ privilege_description: "",
876
+ min_points: 0
877
+ });
878
+ };
879
+ const openDrawer = (tier) => {
880
+ if (tier) {
881
+ setEditingTier(tier);
882
+ setFormData({
883
+ name: tier.name,
884
+ description: tier.description,
885
+ privilege_description: tier.privilege_description,
886
+ min_points: tier.min_points
887
+ });
888
+ } else {
889
+ setEditingTier(null);
890
+ handleReset();
891
+ }
892
+ setIsDrawerOpen(true);
893
+ };
894
+ const closeDrawer = () => {
895
+ setIsDrawerOpen(false);
896
+ setEditingTier(null);
897
+ handleReset();
898
+ refetch();
899
+ };
900
+ const handleSubmit = async () => {
901
+ var _a, _b;
902
+ try {
903
+ if (!formData.name) {
904
+ toast.error("Name is required", { dismissable: true, duration: 2500 });
905
+ return;
906
+ }
907
+ if (editingTier) {
908
+ await updateTier.mutateAsync({ id: editingTier.id, ...formData });
909
+ toast.success("Tier updated successfully", {
910
+ dismissable: true,
911
+ duration: 2500
912
+ });
913
+ } else {
914
+ await createTier.mutateAsync(formData);
915
+ toast.success("Tier created successfully", {
916
+ dismissable: true,
917
+ duration: 2500
918
+ });
919
+ }
920
+ closeDrawer();
921
+ } catch (error) {
922
+ if (axios.isAxiosError(error)) {
923
+ toast.error("Error saving tier", {
924
+ description: (_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message,
925
+ dismissable: true,
926
+ duration: 2500
927
+ });
928
+ } else {
929
+ toast.error("Error saving tier", {
930
+ dismissable: true,
931
+ duration: 2500
932
+ });
933
+ }
934
+ }
935
+ };
936
+ const handleDelete = async (id) => {
937
+ var _a, _b;
938
+ try {
939
+ const confirmed = await dialog({
940
+ title: "Delete Membership Tier",
941
+ description: `Are you sure you want to delete the \`${editingTier == null ? void 0 : editingTier.name}\` tier?`
942
+ });
943
+ if (!confirmed) {
944
+ return;
945
+ }
946
+ await deleteTier.mutateAsync(id);
947
+ toast.success("Tier deleted successfully", {
948
+ dismissable: true,
949
+ duration: 2500
950
+ });
951
+ closeDrawer();
952
+ } catch (error) {
953
+ if (axios.isAxiosError(error)) {
954
+ toast.error("Error deleting tier", {
955
+ description: (_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message,
956
+ dismissable: true,
957
+ duration: 2500
958
+ });
959
+ } else {
960
+ toast.error("Error deleting tier", {
961
+ dismissable: true,
962
+ duration: 2500
963
+ });
964
+ }
965
+ }
966
+ };
967
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
968
+ /* @__PURE__ */ jsx(Container, { className: "flex flex-col p-0 overflow-hidden", children: /* @__PURE__ */ jsxs(DataTable, { instance: table, children: [
969
+ /* @__PURE__ */ jsxs(DataTable.Toolbar, { className: "flex flex-col items-start justify-between gap-2 md:flex-row md:items-center", children: [
970
+ /* @__PURE__ */ jsx(Heading, { children: "Membership Tiers" }),
971
+ /* @__PURE__ */ jsx(Button, { onClick: () => openDrawer(), children: "Add Tier" })
972
+ ] }),
973
+ /* @__PURE__ */ jsx(DataTable.Table, {})
974
+ ] }) }),
975
+ /* @__PURE__ */ jsx(Drawer, { open: isDrawerOpen, onOpenChange: setIsDrawerOpen, children: /* @__PURE__ */ jsxs(Drawer.Content, { children: [
976
+ /* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children: editingTier ? "Edit Tier" : "Add Tier" }) }),
977
+ /* @__PURE__ */ jsxs(Drawer.Body, { "aria-description": "Membership Tier Form", children: [
978
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", "aria-description": "Membership Tier Form", children: [
979
+ /* @__PURE__ */ jsx(Text, { children: "Name" }),
980
+ /* @__PURE__ */ jsx(
981
+ Input,
982
+ {
983
+ name: "name",
984
+ value: formData.name,
985
+ onChange: handleInputChange
986
+ }
987
+ ),
988
+ /* @__PURE__ */ jsx(Text, { children: "Description" }),
989
+ /* @__PURE__ */ jsx(
990
+ Textarea,
991
+ {
992
+ className: "truncate",
993
+ name: "description",
994
+ value: formData.description,
995
+ onChange: handleTextAreaChange
996
+ }
997
+ ),
998
+ /* @__PURE__ */ jsx(Text, { children: "Privilege Description" }),
999
+ /* @__PURE__ */ jsx(
1000
+ Textarea,
1001
+ {
1002
+ className: "truncate",
1003
+ name: "privilege_description",
1004
+ value: formData.privilege_description,
1005
+ onChange: handleTextAreaChange
1006
+ }
1007
+ ),
1008
+ /* @__PURE__ */ jsx(Text, { children: "Points" }),
1009
+ /* @__PURE__ */ jsx(
1010
+ Input,
1011
+ {
1012
+ name: "min_points",
1013
+ type: "number",
1014
+ value: formData.min_points,
1015
+ onChange: handleInputChange
1016
+ }
1017
+ )
1018
+ ] }),
1019
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 mt-6", children: [
1020
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: closeDrawer, children: "Cancel" }),
1021
+ /* @__PURE__ */ jsx(Button, { onClick: handleSubmit, children: editingTier ? "Update" : "Create" })
1022
+ ] })
1023
+ ] }),
1024
+ /* @__PURE__ */ jsx(Drawer.Footer, { className: "flex justify-start", children: editingTier && /* @__PURE__ */ jsx(
1025
+ Button,
1026
+ {
1027
+ variant: "danger",
1028
+ onClick: () => {
1029
+ handleDelete(editingTier.id);
1030
+ },
1031
+ children: "Delete"
1032
+ }
1033
+ ) })
1034
+ ] }) })
1035
+ ] });
1036
+ };
1037
+ const MembershipTierPage = () => {
1038
+ return /* @__PURE__ */ jsx(MembershipTierRenderer, {});
1039
+ };
1040
+ const config = defineRouteConfig({
1041
+ label: "Membership Tiers"
1042
+ });
1043
+ const widgetModule = { widgets: [
1044
+ {
1045
+ Component: PromotionWidget,
1046
+ zone: ["promotion.details.side.after"]
1047
+ }
1048
+ ] };
1049
+ const routeModule = {
1050
+ routes: [
1051
+ {
1052
+ Component: BannerUpload,
1053
+ path: "/membership"
1054
+ },
1055
+ {
1056
+ Component: MembershipConsentPage,
1057
+ path: "/membership/member-consents"
1058
+ },
1059
+ {
1060
+ Component: MembershipTierPage,
1061
+ path: "/membership/membertier"
1062
+ }
1063
+ ]
1064
+ };
1065
+ const menuItemModule = {
1066
+ menuItems: [
1067
+ {
1068
+ label: config$2.label,
1069
+ icon: config$2.icon,
1070
+ path: "/membership",
1071
+ nested: void 0
1072
+ },
1073
+ {
1074
+ label: config$1.label,
1075
+ icon: void 0,
1076
+ path: "/membership/member-consents",
1077
+ nested: void 0
1078
+ },
1079
+ {
1080
+ label: config.label,
1081
+ icon: void 0,
1082
+ path: "/membership/membertier",
1083
+ nested: void 0
1084
+ }
1085
+ ]
1086
+ };
1087
+ const formModule = { customFields: {} };
1088
+ const displayModule = {
1089
+ displays: {}
1090
+ };
1091
+ const i18nModule = { resources: {} };
1092
+ const plugin = {
1093
+ widgetModule,
1094
+ routeModule,
1095
+ menuItemModule,
1096
+ formModule,
1097
+ displayModule,
1098
+ i18nModule
1099
+ };
1100
+ export {
1101
+ plugin as default
1102
+ };