@withpotter/components 0.13.0-next.123 → 0.13.0-next.58
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/billing/overview-tab.js +3 -4
- package/dist/billing/overview-tab.js.map +1 -1
- package/dist/billing/plans-tab.js +12 -6
- package/dist/billing/plans-tab.js.map +1 -1
- package/dist/charts/index.d.ts +1 -1
- package/dist/charts/index.js +1 -3
- package/dist/charts/index.js.map +1 -1
- package/dist/dashboard/activity-feed.d.ts +0 -2
- package/dist/dashboard/activity-feed.js +1 -4
- package/dist/dashboard/activity-feed.js.map +1 -1
- package/dist/dashboard/home/ask-controls.js +0 -1
- package/dist/dashboard/home/ask-controls.js.map +1 -1
- package/dist/dashboard/home/ask-mode-chip.js +4 -4
- package/dist/dashboard/home/ask-mode-chip.js.map +1 -1
- package/dist/dashboard/home/home-ask.js +3 -3
- package/dist/dashboard/home/home-ask.js.map +1 -1
- package/dist/dashboard/home/home-quick-actions.js +1 -9
- package/dist/dashboard/home/home-quick-actions.js.map +1 -1
- package/dist/dashboard/home/widgets/activity-widget.js +43 -56
- package/dist/dashboard/home/widgets/activity-widget.js.map +1 -1
- package/dist/dashboard/home/widgets/home-widgets.js +2 -3
- package/dist/dashboard/home/widgets/home-widgets.js.map +1 -1
- package/dist/dashboard/home/widgets/money-widgets.js +4 -9
- package/dist/dashboard/home/widgets/money-widgets.js.map +1 -1
- package/dist/dashboard/home/widgets/registry.d.ts +3 -6
- package/dist/dashboard/home/widgets/registry.js +2 -12
- package/dist/dashboard/home/widgets/registry.js.map +1 -1
- package/dist/dashboard/home/widgets/team-widget.js +3 -15
- package/dist/dashboard/home/widgets/team-widget.js.map +1 -1
- package/dist/dashboard/home/widgets/types.d.ts +1 -13
- package/dist/dashboard/home/widgets/types.js.map +1 -1
- package/dist/dashboard/home/widgets/use-home-layout.js +4 -9
- package/dist/dashboard/home/widgets/use-home-layout.js.map +1 -1
- package/dist/dashboard/home/widgets/website-widget.js +1 -9
- package/dist/dashboard/home/widgets/website-widget.js.map +1 -1
- package/dist/dashboard/page-transition.d.ts +0 -11
- package/dist/dashboard/page-transition.js +2 -9
- package/dist/dashboard/page-transition.js.map +1 -1
- package/dist/features/gift-card-artwork.js +1 -1
- package/dist/features/gift-card-artwork.js.map +1 -1
- package/dist/features/gift-cards/gift-cards-feature.js +518 -656
- package/dist/features/gift-cards/gift-cards-feature.js.map +1 -1
- package/dist/features/product-form.js +1 -1
- package/dist/features/product-form.js.map +1 -1
- package/dist/mobile/screens/billing-flows.js +6 -7
- package/dist/mobile/screens/billing-flows.js.map +1 -1
- package/dist/mobile/screens/billing.js +2 -2
- package/dist/mobile/screens/billing.js.map +1 -1
- package/dist/notifications-dropdown.js +1 -11
- package/dist/notifications-dropdown.js.map +1 -1
- package/dist/shell/notification-kinds.d.ts +2 -11
- package/dist/shell/notification-kinds.js +1 -16
- package/dist/shell/notification-kinds.js.map +1 -1
- package/dist/shell/notification-row.d.ts +2 -6
- package/dist/shell/notification-row.js +33 -59
- package/dist/shell/notification-row.js.map +1 -1
- package/dist/shell/product-switcher.js +0 -3
- package/dist/shell/product-switcher.js.map +1 -1
- package/dist/shell/settings-modal-context.d.ts +0 -2
- package/dist/shell/settings-modal-context.js +2 -4
- package/dist/shell/settings-modal-context.js.map +1 -1
- package/dist/shell/settings-panels.js +9 -29
- package/dist/shell/settings-panels.js.map +1 -1
- package/dist/shell/settings-rows.d.ts +2 -7
- package/dist/shell/settings-rows.js +36 -93
- package/dist/shell/settings-rows.js.map +1 -1
- package/dist/shell/shell.css +0 -192
- package/dist/shell/sidebar-footer.js +2 -6
- package/dist/shell/sidebar-footer.js.map +1 -1
- package/dist/shell/unified-settings-modal.d.ts +2 -3
- package/dist/shell/unified-settings-modal.js +74 -151
- package/dist/shell/unified-settings-modal.js.map +1 -1
- package/dist/sidebar/config.js +3 -19
- package/dist/sidebar/config.js.map +1 -1
- package/dist/sidebar/more-group.js +1 -1
- package/dist/sidebar/more-group.js.map +1 -1
- package/dist/sidebar/nav-renderers.js +0 -2
- package/dist/sidebar/nav-renderers.js.map +1 -1
- package/dist/sidebar/nav-section.js +3 -5
- package/dist/sidebar/nav-section.js.map +1 -1
- package/dist/sidebar/setup-progress.d.ts +1 -3
- package/dist/sidebar/setup-progress.js +2 -2
- package/dist/sidebar/setup-progress.js.map +1 -1
- package/dist/sidebar/spine-groups.js +1 -7
- package/dist/sidebar/spine-groups.js.map +1 -1
- package/dist/sidebar.js +5 -5
- package/dist/sidebar.js.map +1 -1
- package/dist/templates/template-live-thumb.js +5 -16
- package/dist/templates/template-live-thumb.js.map +1 -1
- package/package.json +5 -5
- package/dist/dashboard/activity-paid.d.ts +0 -10
- package/dist/dashboard/activity-paid.js +0 -7
- package/dist/dashboard/activity-paid.js.map +0 -1
- package/dist/dashboard/home/host-quick-actions.d.ts +0 -19
- package/dist/dashboard/home/host-quick-actions.js +0 -12
- package/dist/dashboard/home/host-quick-actions.js.map +0 -1
- package/dist/dashboard/home/widgets/host-widgets.d.ts +0 -6
- package/dist/dashboard/home/widgets/host-widgets.js +0 -16
- package/dist/dashboard/home/widgets/host-widgets.js.map +0 -1
- package/dist/dashboard/home/widgets/member-label.d.ts +0 -11
- package/dist/dashboard/home/widgets/member-label.js +0 -10
- package/dist/dashboard/home/widgets/member-label.js.map +0 -1
- package/dist/dashboard/home/widgets/middle-ellipsis.d.ts +0 -5
- package/dist/dashboard/home/widgets/middle-ellipsis.js +0 -11
- package/dist/dashboard/home/widgets/middle-ellipsis.js.map +0 -1
- package/dist/dashboard/home/widgets/revenue-series.d.ts +0 -12
- package/dist/dashboard/home/widgets/revenue-series.js +0 -26
- package/dist/dashboard/home/widgets/revenue-series.js.map +0 -1
- package/dist/dashboard/page-transition-key.d.ts +0 -7
- package/dist/dashboard/page-transition-key.js +0 -11
- package/dist/dashboard/page-transition-key.js.map +0 -1
- package/dist/features/gift-cards/gift-card-logic.d.ts +0 -41
- package/dist/features/gift-cards/gift-card-logic.js +0 -65
- package/dist/features/gift-cards/gift-card-logic.js.map +0 -1
- package/dist/features/gift-cards/hint.d.ts +0 -9
- package/dist/features/gift-cards/hint.js +0 -25
- package/dist/features/gift-cards/hint.js.map +0 -1
- package/dist/shell/notification-actions.d.ts +0 -12
- package/dist/shell/notification-actions.js +0 -18
- package/dist/shell/notification-actions.js.map +0 -1
- package/dist/shell/settings-groups.d.ts +0 -23
- package/dist/shell/settings-groups.js +0 -22
- package/dist/shell/settings-groups.js.map +0 -1
- package/dist/shell/settings-host.d.ts +0 -39
- package/dist/shell/settings-host.js +0 -37
- package/dist/shell/settings-host.js.map +0 -1
- package/dist/shell/sidebar-cards.d.ts +0 -32
- package/dist/shell/sidebar-cards.js +0 -37
- package/dist/shell/sidebar-cards.js.map +0 -1
- package/dist/templates/template-tile-source.d.ts +0 -24
- package/dist/templates/template-tile-source.js +0 -18
- package/dist/templates/template-tile-source.js.map +0 -1
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback, useEffect,
|
|
4
|
-
import {
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
+
import {
|
|
5
|
+
Copy,
|
|
6
|
+
Download,
|
|
7
|
+
Gift,
|
|
8
|
+
History,
|
|
9
|
+
Mail,
|
|
10
|
+
Plus,
|
|
11
|
+
Search,
|
|
12
|
+
X
|
|
13
|
+
} from "lucide-react";
|
|
5
14
|
import { useAuth } from "@withpotter/lib/auth-context";
|
|
6
15
|
import { normalizeCurrencyCode } from "@withpotter/lib/utils";
|
|
7
|
-
import { Button } from "@withpotter/ui-kit/button";
|
|
8
|
-
import { Field } from "@withpotter/ui-kit/field";
|
|
9
|
-
import { FormDialog } from "@withpotter/ui-kit/form-dialog";
|
|
10
16
|
import { InlineDeleteButton } from "@withpotter/ui-kit/inline-delete-button";
|
|
11
|
-
import { Input } from "@withpotter/ui-kit/input";
|
|
12
|
-
import { ListPage } from "@withpotter/ui-kit/list-page";
|
|
13
|
-
import { Textarea } from "@withpotter/ui-kit/textarea";
|
|
14
|
-
import { Tooltip } from "@withpotter/ui-kit/tooltip";
|
|
15
17
|
import {
|
|
16
18
|
giftCardsApi,
|
|
17
|
-
giftcardsApi,
|
|
18
19
|
communicationsApi
|
|
19
20
|
} from "@withpotter/lib/api";
|
|
20
21
|
import { useFormatCurrency } from "@withpotter/hooks/use-format-currency";
|
|
@@ -33,15 +34,6 @@ import {
|
|
|
33
34
|
GiftCardArtwork,
|
|
34
35
|
GIFT_CARD_PRESETS
|
|
35
36
|
} from "../gift-card-artwork";
|
|
36
|
-
import {
|
|
37
|
-
createInFlightDeduper,
|
|
38
|
-
currencySymbol,
|
|
39
|
-
isFirstRun,
|
|
40
|
-
PAGE_HINT_LINES,
|
|
41
|
-
STATUS_FILTERS,
|
|
42
|
-
STATUS_LABEL
|
|
43
|
-
} from "./gift-card-logic";
|
|
44
|
-
import { Hint } from "./hint";
|
|
45
37
|
const PAGE_SIZE = 20;
|
|
46
38
|
const LIVE = process.env.NEXT_PUBLIC_BOX_GIFT_CARDS_LIVE === "true";
|
|
47
39
|
const LIVE_REFRESH_MS = 2e3;
|
|
@@ -73,6 +65,24 @@ function statusClass(status) {
|
|
|
73
65
|
return "bg-[var(--bg-muted)] text-[var(--text-tertiary)]";
|
|
74
66
|
}
|
|
75
67
|
}
|
|
68
|
+
function Modal({ title, onClose, children }) {
|
|
69
|
+
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-end justify-center bg-black/40 sm:items-center sm:p-4", children: /* @__PURE__ */ jsxs("div", { className: "max-h-[92vh] w-full overflow-y-auto bg-[var(--bg-base)] sm:max-w-xl sm:rounded-md sm:border sm:border-[var(--border-default)]", children: [
|
|
70
|
+
/* @__PURE__ */ jsxs("div", { className: "sticky top-0 z-10 flex items-center justify-between border-b border-[var(--border-default)] bg-[var(--bg-base)] px-4 py-3", children: [
|
|
71
|
+
/* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-[var(--text-primary)]", children: title }),
|
|
72
|
+
/* @__PURE__ */ jsx(
|
|
73
|
+
"button",
|
|
74
|
+
{
|
|
75
|
+
type: "button",
|
|
76
|
+
onClick: onClose,
|
|
77
|
+
className: "rounded-md p-2 text-[var(--text-tertiary)] hover:bg-[var(--interactive-hover)]",
|
|
78
|
+
"aria-label": "Close",
|
|
79
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
] }),
|
|
83
|
+
children
|
|
84
|
+
] }) });
|
|
85
|
+
}
|
|
76
86
|
function IssueGiftCardModal({
|
|
77
87
|
currency,
|
|
78
88
|
merchantName,
|
|
@@ -80,16 +90,6 @@ function IssueGiftCardModal({
|
|
|
80
90
|
onCreated
|
|
81
91
|
}) {
|
|
82
92
|
const { tenant } = useAuth();
|
|
83
|
-
const formId = useId();
|
|
84
|
-
const ids = {
|
|
85
|
-
amount: `${formId}-amount`,
|
|
86
|
-
recipientName: `${formId}-recipient-name`,
|
|
87
|
-
recipientEmail: `${formId}-recipient-email`,
|
|
88
|
-
purchaserName: `${formId}-purchaser-name`,
|
|
89
|
-
purchaserEmail: `${formId}-purchaser-email`,
|
|
90
|
-
message: `${formId}-message`,
|
|
91
|
-
expiresAt: `${formId}-expires-at`
|
|
92
|
-
};
|
|
93
93
|
const [form, setForm] = useState({
|
|
94
94
|
originalAmount: 0,
|
|
95
95
|
currency,
|
|
@@ -115,233 +115,178 @@ function IssueGiftCardModal({
|
|
|
115
115
|
setSaving(false);
|
|
116
116
|
}
|
|
117
117
|
};
|
|
118
|
-
const
|
|
119
|
-
return /* @__PURE__ */ jsx(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
118
|
+
const inputClass = "w-full rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] px-3 py-2 text-sm text-[var(--text-primary)] focus:border-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-500/20";
|
|
119
|
+
return /* @__PURE__ */ jsx(Modal, { title: "Issue gift card", onClose, children: /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-5 p-4", children: [
|
|
120
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 sm:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)] sm:items-start", children: [
|
|
121
|
+
/* @__PURE__ */ jsx(
|
|
122
|
+
GiftCardArtwork,
|
|
123
|
+
{
|
|
124
|
+
design: form.design ?? GIFT_CARD_PRESETS[0],
|
|
125
|
+
merchantName,
|
|
126
|
+
amount: form.originalAmount > 0 ? new Intl.NumberFormat(void 0, {
|
|
127
|
+
style: "currency",
|
|
128
|
+
currency: form.currency || currency
|
|
129
|
+
}).format(form.originalAmount) : void 0,
|
|
130
|
+
recipientName: form.recipientName,
|
|
131
|
+
senderName: form.purchaserName
|
|
132
|
+
}
|
|
133
|
+
),
|
|
134
|
+
/* @__PURE__ */ jsxs("fieldset", { children: [
|
|
135
|
+
/* @__PURE__ */ jsx("legend", { className: "text-sm font-medium text-[var(--text-secondary)]", children: "Design" }),
|
|
136
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 grid grid-cols-4 gap-2 sm:grid-cols-1", children: GIFT_CARD_PRESETS.map((design) => {
|
|
137
|
+
const selected = form.design?.designId === design.designId;
|
|
138
|
+
return /* @__PURE__ */ jsxs(
|
|
139
|
+
"button",
|
|
140
|
+
{
|
|
141
|
+
type: "button",
|
|
142
|
+
onClick: () => setField("design", design),
|
|
143
|
+
className: `flex min-w-0 items-center gap-2 rounded-md border p-2 text-left text-xs font-medium ${selected ? "border-brand-500 bg-brand-500/10 text-[var(--text-primary)]" : "border-[var(--border-default)] text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]"}`,
|
|
144
|
+
"aria-label": design.name,
|
|
145
|
+
"aria-pressed": selected,
|
|
146
|
+
children: [
|
|
147
|
+
/* @__PURE__ */ jsx(
|
|
148
|
+
"span",
|
|
149
|
+
{
|
|
150
|
+
className: "h-5 w-5 shrink-0 rounded-sm border border-black/10",
|
|
151
|
+
style: { backgroundColor: design.palette.background }
|
|
152
|
+
}
|
|
153
|
+
),
|
|
154
|
+
/* @__PURE__ */ jsx("span", { className: "hidden truncate sm:block", children: design.name })
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
design.designId
|
|
158
|
+
);
|
|
159
|
+
}) })
|
|
160
|
+
] })
|
|
161
|
+
] }),
|
|
162
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
|
|
163
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1.5", children: [
|
|
164
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--text-secondary)]", children: "Amount" }),
|
|
165
|
+
/* @__PURE__ */ jsx(
|
|
166
|
+
"input",
|
|
167
|
+
{
|
|
168
|
+
className: inputClass,
|
|
169
|
+
type: "number",
|
|
170
|
+
min: "0.01",
|
|
171
|
+
step: "0.01",
|
|
172
|
+
required: true,
|
|
173
|
+
value: form.originalAmount || "",
|
|
174
|
+
onChange: (event) => setField("originalAmount", Number(event.target.value))
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
] }),
|
|
178
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1.5", children: [
|
|
179
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--text-secondary)]", children: "Currency" }),
|
|
180
|
+
/* @__PURE__ */ jsx(
|
|
181
|
+
"input",
|
|
182
|
+
{
|
|
183
|
+
className: inputClass,
|
|
184
|
+
readOnly: true,
|
|
185
|
+
required: true,
|
|
186
|
+
value: form.currency
|
|
187
|
+
}
|
|
188
|
+
)
|
|
189
|
+
] }),
|
|
190
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1.5", children: [
|
|
191
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--text-secondary)]", children: "Recipient name" }),
|
|
192
|
+
/* @__PURE__ */ jsx(
|
|
193
|
+
"input",
|
|
194
|
+
{
|
|
195
|
+
className: inputClass,
|
|
196
|
+
value: form.recipientName ?? "",
|
|
197
|
+
onChange: (event) => setField("recipientName", event.target.value)
|
|
198
|
+
}
|
|
199
|
+
),
|
|
200
|
+
/* @__PURE__ */ jsx("span", { className: "block text-xs text-[var(--text-muted)]", children: "The person who receives the gift card and spends it." })
|
|
201
|
+
] }),
|
|
202
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1.5", children: [
|
|
203
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--text-secondary)]", children: "Recipient email" }),
|
|
129
204
|
/* @__PURE__ */ jsx(
|
|
130
|
-
|
|
205
|
+
"input",
|
|
131
206
|
{
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
207
|
+
className: inputClass,
|
|
208
|
+
type: "email",
|
|
209
|
+
value: form.recipientEmail ?? "",
|
|
210
|
+
onChange: (event) => setField("recipientEmail", event.target.value)
|
|
136
211
|
}
|
|
137
212
|
)
|
|
138
213
|
] }),
|
|
139
|
-
|
|
140
|
-
"
|
|
214
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1.5", children: [
|
|
215
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--text-secondary)]", children: "Purchaser name" }),
|
|
216
|
+
/* @__PURE__ */ jsx(
|
|
217
|
+
"input",
|
|
218
|
+
{
|
|
219
|
+
className: inputClass,
|
|
220
|
+
value: form.purchaserName ?? "",
|
|
221
|
+
onChange: (event) => setField("purchaserName", event.target.value)
|
|
222
|
+
}
|
|
223
|
+
),
|
|
224
|
+
/* @__PURE__ */ jsx("span", { className: "block text-xs text-[var(--text-muted)]", children: "The person paying for the gift card." })
|
|
225
|
+
] }),
|
|
226
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1.5", children: [
|
|
227
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--text-secondary)]", children: "Purchaser email" }),
|
|
228
|
+
/* @__PURE__ */ jsx(
|
|
229
|
+
"input",
|
|
230
|
+
{
|
|
231
|
+
className: inputClass,
|
|
232
|
+
type: "email",
|
|
233
|
+
value: form.purchaserEmail ?? "",
|
|
234
|
+
onChange: (event) => setField("purchaserEmail", event.target.value)
|
|
235
|
+
}
|
|
236
|
+
)
|
|
237
|
+
] })
|
|
238
|
+
] }),
|
|
239
|
+
/* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
|
|
240
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--text-secondary)]", children: "Personal message" }),
|
|
241
|
+
/* @__PURE__ */ jsx(
|
|
242
|
+
"textarea",
|
|
141
243
|
{
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-4 md:self-start", children: [
|
|
147
|
-
/* @__PURE__ */ jsx(
|
|
148
|
-
GiftCardArtwork,
|
|
149
|
-
{
|
|
150
|
-
design: form.design ?? GIFT_CARD_PRESETS[0],
|
|
151
|
-
merchantName,
|
|
152
|
-
amount: form.originalAmount > 0 ? new Intl.NumberFormat(void 0, {
|
|
153
|
-
style: "currency",
|
|
154
|
-
currency: form.currency || currency
|
|
155
|
-
}).format(form.originalAmount) : void 0,
|
|
156
|
-
recipientName: form.recipientName,
|
|
157
|
-
senderName: form.purchaserName
|
|
158
|
-
}
|
|
159
|
-
),
|
|
160
|
-
/* @__PURE__ */ jsxs("fieldset", { children: [
|
|
161
|
-
/* @__PURE__ */ jsx("legend", { className: "text-sm font-medium text-[var(--text-secondary)]", children: "Design" }),
|
|
162
|
-
/* @__PURE__ */ jsx("div", { className: "mt-2 grid grid-cols-4 gap-2 md:grid-cols-2", children: GIFT_CARD_PRESETS.map((design) => {
|
|
163
|
-
const selected = form.design?.designId === design.designId;
|
|
164
|
-
return /* @__PURE__ */ jsxs(
|
|
165
|
-
"button",
|
|
166
|
-
{
|
|
167
|
-
type: "button",
|
|
168
|
-
onClick: () => setField("design", design),
|
|
169
|
-
className: `flex min-w-0 items-center gap-2 rounded-md border p-2 text-left text-xs font-medium ${selected ? "border-brand-500 bg-brand-500/10 text-[var(--text-primary)]" : "border-[var(--border-default)] text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]"}`,
|
|
170
|
-
"aria-label": design.name,
|
|
171
|
-
"aria-pressed": selected,
|
|
172
|
-
children: [
|
|
173
|
-
/* @__PURE__ */ jsx(
|
|
174
|
-
"span",
|
|
175
|
-
{
|
|
176
|
-
className: "h-5 w-5 shrink-0 rounded-sm border border-black/10",
|
|
177
|
-
style: { backgroundColor: design.palette.background }
|
|
178
|
-
}
|
|
179
|
-
),
|
|
180
|
-
/* @__PURE__ */ jsx("span", { className: "hidden truncate md:block", children: design.name })
|
|
181
|
-
]
|
|
182
|
-
},
|
|
183
|
-
design.designId
|
|
184
|
-
);
|
|
185
|
-
}) })
|
|
186
|
-
] })
|
|
187
|
-
] }),
|
|
188
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
189
|
-
/* @__PURE__ */ jsx(
|
|
190
|
-
Field,
|
|
191
|
-
{
|
|
192
|
-
label: "Amount",
|
|
193
|
-
htmlFor: ids.amount,
|
|
194
|
-
required: true,
|
|
195
|
-
hint: `In ${form.currency || currency}, your business currency. It cannot be changed here.`,
|
|
196
|
-
children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
197
|
-
/* @__PURE__ */ jsx(
|
|
198
|
-
"span",
|
|
199
|
-
{
|
|
200
|
-
"aria-hidden": true,
|
|
201
|
-
className: "pointer-events-none absolute inset-y-0 left-3 flex items-center text-sm text-[var(--text-tertiary)]",
|
|
202
|
-
children: symbol
|
|
203
|
-
}
|
|
204
|
-
),
|
|
205
|
-
/* @__PURE__ */ jsx(
|
|
206
|
-
Input,
|
|
207
|
-
{
|
|
208
|
-
id: ids.amount,
|
|
209
|
-
type: "number",
|
|
210
|
-
min: "0.01",
|
|
211
|
-
step: "0.01",
|
|
212
|
-
required: true,
|
|
213
|
-
"aria-required": true,
|
|
214
|
-
className: symbol.length > 2 ? "pl-14" : "pl-8",
|
|
215
|
-
value: form.originalAmount || "",
|
|
216
|
-
onChange: (event) => setField("originalAmount", Number(event.target.value))
|
|
217
|
-
}
|
|
218
|
-
)
|
|
219
|
-
] })
|
|
220
|
-
}
|
|
221
|
-
),
|
|
222
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
|
|
223
|
-
/* @__PURE__ */ jsx(
|
|
224
|
-
Field,
|
|
225
|
-
{
|
|
226
|
-
label: "Recipient name",
|
|
227
|
-
htmlFor: ids.recipientName,
|
|
228
|
-
hint: "Optional. Who receives and spends the card.",
|
|
229
|
-
children: /* @__PURE__ */ jsx(
|
|
230
|
-
Input,
|
|
231
|
-
{
|
|
232
|
-
id: ids.recipientName,
|
|
233
|
-
value: form.recipientName ?? "",
|
|
234
|
-
onChange: (event) => setField("recipientName", event.target.value)
|
|
235
|
-
}
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
),
|
|
239
|
-
/* @__PURE__ */ jsx(
|
|
240
|
-
Field,
|
|
241
|
-
{
|
|
242
|
-
label: "Recipient email",
|
|
243
|
-
htmlFor: ids.recipientEmail,
|
|
244
|
-
hint: "Optional. Nothing is emailed when you issue. Send it from the card's details.",
|
|
245
|
-
children: /* @__PURE__ */ jsx(
|
|
246
|
-
Input,
|
|
247
|
-
{
|
|
248
|
-
id: ids.recipientEmail,
|
|
249
|
-
type: "email",
|
|
250
|
-
value: form.recipientEmail ?? "",
|
|
251
|
-
onChange: (event) => setField("recipientEmail", event.target.value)
|
|
252
|
-
}
|
|
253
|
-
)
|
|
254
|
-
}
|
|
255
|
-
),
|
|
256
|
-
/* @__PURE__ */ jsx(
|
|
257
|
-
Field,
|
|
258
|
-
{
|
|
259
|
-
label: "Purchaser name",
|
|
260
|
-
htmlFor: ids.purchaserName,
|
|
261
|
-
hint: "Optional. Who is paying for the card.",
|
|
262
|
-
children: /* @__PURE__ */ jsx(
|
|
263
|
-
Input,
|
|
264
|
-
{
|
|
265
|
-
id: ids.purchaserName,
|
|
266
|
-
value: form.purchaserName ?? "",
|
|
267
|
-
onChange: (event) => setField("purchaserName", event.target.value)
|
|
268
|
-
}
|
|
269
|
-
)
|
|
270
|
-
}
|
|
271
|
-
),
|
|
272
|
-
/* @__PURE__ */ jsx(
|
|
273
|
-
Field,
|
|
274
|
-
{
|
|
275
|
-
label: "Purchaser email",
|
|
276
|
-
htmlFor: ids.purchaserEmail,
|
|
277
|
-
hint: "Optional.",
|
|
278
|
-
children: /* @__PURE__ */ jsx(
|
|
279
|
-
Input,
|
|
280
|
-
{
|
|
281
|
-
id: ids.purchaserEmail,
|
|
282
|
-
type: "email",
|
|
283
|
-
value: form.purchaserEmail ?? "",
|
|
284
|
-
onChange: (event) => setField("purchaserEmail", event.target.value)
|
|
285
|
-
}
|
|
286
|
-
)
|
|
287
|
-
}
|
|
288
|
-
)
|
|
289
|
-
] }),
|
|
290
|
-
/* @__PURE__ */ jsx(
|
|
291
|
-
Field,
|
|
292
|
-
{
|
|
293
|
-
label: "Personal message",
|
|
294
|
-
htmlFor: ids.message,
|
|
295
|
-
hint: "Optional. Up to 500 characters.",
|
|
296
|
-
children: /* @__PURE__ */ jsx(
|
|
297
|
-
Textarea,
|
|
298
|
-
{
|
|
299
|
-
id: ids.message,
|
|
300
|
-
rows: 3,
|
|
301
|
-
maxLength: 500,
|
|
302
|
-
value: form.personalMessage ?? "",
|
|
303
|
-
onChange: (event) => setField("personalMessage", event.target.value)
|
|
304
|
-
}
|
|
305
|
-
)
|
|
306
|
-
}
|
|
307
|
-
),
|
|
308
|
-
/* @__PURE__ */ jsx(
|
|
309
|
-
Field,
|
|
310
|
-
{
|
|
311
|
-
label: "Expiration date",
|
|
312
|
-
htmlFor: ids.expiresAt,
|
|
313
|
-
hint: "Optional. Leave it empty and the card has no expiry date.",
|
|
314
|
-
children: /* @__PURE__ */ jsx(
|
|
315
|
-
Input,
|
|
316
|
-
{
|
|
317
|
-
id: ids.expiresAt,
|
|
318
|
-
type: "date",
|
|
319
|
-
min: minimumExpirationDate(),
|
|
320
|
-
value: form.expiresAt ?? "",
|
|
321
|
-
onChange: (event) => setField("expiresAt", event.target.value)
|
|
322
|
-
}
|
|
323
|
-
)
|
|
324
|
-
}
|
|
325
|
-
),
|
|
326
|
-
error && /* @__PURE__ */ jsx(
|
|
327
|
-
"p",
|
|
328
|
-
{
|
|
329
|
-
role: "alert",
|
|
330
|
-
className: "rounded-md bg-[var(--error-bg)] px-3 py-2 text-sm text-[var(--error)]",
|
|
331
|
-
children: error
|
|
332
|
-
}
|
|
333
|
-
)
|
|
334
|
-
] })
|
|
335
|
-
]
|
|
244
|
+
className: `${inputClass} min-h-24 resize-y`,
|
|
245
|
+
maxLength: 500,
|
|
246
|
+
value: form.personalMessage ?? "",
|
|
247
|
+
onChange: (event) => setField("personalMessage", event.target.value)
|
|
336
248
|
}
|
|
337
249
|
)
|
|
338
|
-
}
|
|
339
|
-
|
|
250
|
+
] }),
|
|
251
|
+
/* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
|
|
252
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--text-secondary)]", children: "Expiration date" }),
|
|
253
|
+
/* @__PURE__ */ jsx(
|
|
254
|
+
"input",
|
|
255
|
+
{
|
|
256
|
+
className: inputClass,
|
|
257
|
+
type: "date",
|
|
258
|
+
min: minimumExpirationDate(),
|
|
259
|
+
value: form.expiresAt ?? "",
|
|
260
|
+
onChange: (event) => setField("expiresAt", event.target.value)
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
] }),
|
|
264
|
+
error && /* @__PURE__ */ jsx("p", { className: "rounded-md bg-[var(--error-bg)] px-3 py-2 text-sm text-[var(--error)]", children: error }),
|
|
265
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 border-t border-[var(--border-default)] pt-4", children: [
|
|
266
|
+
/* @__PURE__ */ jsx(
|
|
267
|
+
"button",
|
|
268
|
+
{
|
|
269
|
+
type: "button",
|
|
270
|
+
onClick: onClose,
|
|
271
|
+
className: "rounded-md border border-[var(--border-default)] px-4 py-2 text-sm font-medium text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]",
|
|
272
|
+
children: "Cancel"
|
|
273
|
+
}
|
|
274
|
+
),
|
|
275
|
+
/* @__PURE__ */ jsx(
|
|
276
|
+
"button",
|
|
277
|
+
{
|
|
278
|
+
type: "submit",
|
|
279
|
+
disabled: saving || form.originalAmount <= 0,
|
|
280
|
+
className: "rounded-md bg-brand-500 px-4 py-2 text-sm font-medium text-[var(--text-inverse)] hover:bg-brand-600 disabled:opacity-50",
|
|
281
|
+
children: saving ? "Issuing..." : "Issue gift card"
|
|
282
|
+
}
|
|
283
|
+
)
|
|
284
|
+
] })
|
|
285
|
+
] }) });
|
|
340
286
|
}
|
|
341
287
|
function BalanceModal({ onClose, initialCode = "" }) {
|
|
342
288
|
const { tenant } = useAuth();
|
|
343
289
|
const formatCurrency = useFormatCurrency();
|
|
344
|
-
const codeId = useId();
|
|
345
290
|
const [code, setCode] = useState(initialCode);
|
|
346
291
|
const [checking, setChecking] = useState(false);
|
|
347
292
|
const [result, setResult] = useState(null);
|
|
@@ -359,36 +304,35 @@ function BalanceModal({ onClose, initialCode = "" }) {
|
|
|
359
304
|
setChecking(false);
|
|
360
305
|
}
|
|
361
306
|
};
|
|
362
|
-
return /* @__PURE__ */ jsx(
|
|
363
|
-
/* @__PURE__ */
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
setResult(null);
|
|
307
|
+
return /* @__PURE__ */ jsx(Modal, { title: "Check gift card balance", onClose, children: /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-4 p-4", children: [
|
|
308
|
+
/* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
|
|
309
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--text-secondary)]", children: "Gift card code" }),
|
|
310
|
+
/* @__PURE__ */ jsx(
|
|
311
|
+
"input",
|
|
312
|
+
{
|
|
313
|
+
autoFocus: true,
|
|
314
|
+
className: "w-full rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] px-3 py-2 font-mono text-sm uppercase text-[var(--text-primary)] focus:border-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-500/20",
|
|
315
|
+
value: code,
|
|
316
|
+
onChange: (event) => {
|
|
317
|
+
setCode(event.target.value.toUpperCase());
|
|
318
|
+
setResult(null);
|
|
319
|
+
}
|
|
376
320
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
321
|
+
)
|
|
322
|
+
] }),
|
|
323
|
+
result && /* @__PURE__ */ jsx(
|
|
380
324
|
"div",
|
|
381
325
|
{
|
|
382
326
|
className: `rounded-md p-4 ${result.valid ? "bg-[var(--success-bg)] text-[var(--success)]" : "bg-[var(--error-bg)] text-[var(--error)]"}`,
|
|
383
327
|
children: result.valid ? `Available balance: ${formatCurrency(result.balance ?? 0)}` : result.error || "This gift card is not valid."
|
|
384
328
|
}
|
|
385
|
-
)
|
|
329
|
+
),
|
|
386
330
|
/* @__PURE__ */ jsx(
|
|
387
|
-
|
|
331
|
+
"button",
|
|
388
332
|
{
|
|
389
333
|
type: "submit",
|
|
390
|
-
className: "w-full",
|
|
391
334
|
disabled: checking || !code.trim(),
|
|
335
|
+
className: "w-full rounded-md bg-brand-500 px-4 py-2 text-sm font-medium text-[var(--text-inverse)] hover:bg-brand-600 disabled:opacity-50",
|
|
392
336
|
children: checking ? "Checking..." : "Check balance"
|
|
393
337
|
}
|
|
394
338
|
)
|
|
@@ -465,7 +409,7 @@ function GiftCardDetailModal({
|
|
|
465
409
|
if (!tenant?.slug) return;
|
|
466
410
|
setSaving(true);
|
|
467
411
|
try {
|
|
468
|
-
await
|
|
412
|
+
await giftCardsApi.delete(tenant.slug, giftCard.id);
|
|
469
413
|
toast({ title: "Gift card cancelled" });
|
|
470
414
|
onChanged();
|
|
471
415
|
onClose();
|
|
@@ -474,63 +418,60 @@ function GiftCardDetailModal({
|
|
|
474
418
|
setSaving(false);
|
|
475
419
|
}
|
|
476
420
|
};
|
|
477
|
-
return /* @__PURE__ */ jsx(
|
|
421
|
+
return /* @__PURE__ */ jsx(Modal, { title: "Gift card details", onClose, children: /* @__PURE__ */ jsxs("div", { className: "space-y-5 p-4", children: [
|
|
478
422
|
/* @__PURE__ */ jsx(GiftCardArtwork, { ...artworkData }),
|
|
479
423
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
480
|
-
/* @__PURE__ */
|
|
481
|
-
|
|
424
|
+
/* @__PURE__ */ jsxs(
|
|
425
|
+
"button",
|
|
482
426
|
{
|
|
483
427
|
type: "button",
|
|
484
|
-
variant: "secondary",
|
|
485
|
-
icon: /* @__PURE__ */ jsx(Download, {}),
|
|
486
428
|
onClick: () => downloadGiftCardArtwork(artworkData),
|
|
487
|
-
|
|
429
|
+
className: "flex items-center justify-center gap-2 rounded-md border border-[var(--border-default)] px-3 py-2 text-sm font-medium text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]",
|
|
430
|
+
children: [
|
|
431
|
+
/* @__PURE__ */ jsx(Download, { className: "h-4 w-4" }),
|
|
432
|
+
"Download"
|
|
433
|
+
]
|
|
488
434
|
}
|
|
489
435
|
),
|
|
490
|
-
/* @__PURE__ */
|
|
491
|
-
|
|
436
|
+
/* @__PURE__ */ jsxs(
|
|
437
|
+
"button",
|
|
492
438
|
{
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
disabled: sharing || !giftCard.recipientEmail,
|
|
503
|
-
children: sharing ? "Sending..." : "Send by email"
|
|
504
|
-
}
|
|
505
|
-
) })
|
|
439
|
+
type: "button",
|
|
440
|
+
onClick: sendByEmail,
|
|
441
|
+
disabled: sharing || !giftCard.recipientEmail,
|
|
442
|
+
title: giftCard.recipientEmail ? void 0 : "Add a recipient email to send this card",
|
|
443
|
+
className: "flex items-center justify-center gap-2 rounded-md bg-brand-500 px-3 py-2 text-sm font-medium text-[var(--text-inverse)] hover:bg-brand-600 disabled:cursor-not-allowed disabled:opacity-50",
|
|
444
|
+
children: [
|
|
445
|
+
/* @__PURE__ */ jsx(Mail, { className: "h-4 w-4" }),
|
|
446
|
+
sharing ? "Sending..." : "Send by email"
|
|
447
|
+
]
|
|
506
448
|
}
|
|
507
449
|
)
|
|
508
450
|
] }),
|
|
509
451
|
/* @__PURE__ */ jsxs("div", { className: "rounded-md border border-[var(--border-default)] bg-[var(--bg-subtle)] p-4", children: [
|
|
510
452
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
511
453
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
512
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-[var(--text-muted)]", children: "Code" }),
|
|
454
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase text-[var(--text-muted)]", children: "Code" }),
|
|
513
455
|
/* @__PURE__ */ jsx("p", { className: "mt-1 font-mono text-xl font-semibold text-[var(--text-primary)]", children: giftCard.code })
|
|
514
456
|
] }),
|
|
515
457
|
/* @__PURE__ */ jsx(
|
|
516
|
-
|
|
458
|
+
"button",
|
|
517
459
|
{
|
|
518
460
|
type: "button",
|
|
519
|
-
variant: "secondary",
|
|
520
|
-
size: "icon-sm",
|
|
521
|
-
"aria-label": "Copy code",
|
|
522
461
|
onClick: copyCode,
|
|
523
|
-
|
|
462
|
+
className: "rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] p-2 text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]",
|
|
463
|
+
title: "Copy code",
|
|
464
|
+
children: /* @__PURE__ */ jsx(Copy, { className: "h-4 w-4" })
|
|
524
465
|
}
|
|
525
466
|
)
|
|
526
467
|
] }),
|
|
527
468
|
/* @__PURE__ */ jsxs("div", { className: "mt-4 grid grid-cols-2 gap-4", children: [
|
|
528
469
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
529
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: "Balance
|
|
470
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: "Balance" }),
|
|
530
471
|
/* @__PURE__ */ jsx("p", { className: "font-semibold text-[var(--text-primary)]", children: formatCurrency(Number(giftCard.balance), giftCard.currency) })
|
|
531
472
|
] }),
|
|
532
473
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
533
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: "
|
|
474
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: "Issued value" }),
|
|
534
475
|
/* @__PURE__ */ jsx("p", { className: "font-semibold text-[var(--text-primary)]", children: formatCurrency(
|
|
535
476
|
Number(giftCard.originalAmount),
|
|
536
477
|
giftCard.currency
|
|
@@ -550,41 +491,44 @@ function GiftCardDetailModal({
|
|
|
550
491
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-[var(--text-primary)]", children: "Redeem value" }),
|
|
551
492
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
552
493
|
/* @__PURE__ */ jsx(
|
|
553
|
-
|
|
494
|
+
"input",
|
|
554
495
|
{
|
|
555
|
-
label: "Amount",
|
|
556
496
|
type: "number",
|
|
557
497
|
min: "0.01",
|
|
558
498
|
max: Number(giftCard.balance),
|
|
559
499
|
step: "0.01",
|
|
560
500
|
required: true,
|
|
561
|
-
"
|
|
501
|
+
placeholder: "Amount",
|
|
562
502
|
value: amount,
|
|
563
|
-
onChange: (event) => setAmount(event.target.value)
|
|
503
|
+
onChange: (event) => setAmount(event.target.value),
|
|
504
|
+
className: "rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] px-3 py-2 text-sm focus:border-brand-500 focus:outline-none"
|
|
564
505
|
}
|
|
565
506
|
),
|
|
566
507
|
/* @__PURE__ */ jsx(
|
|
567
|
-
|
|
508
|
+
"input",
|
|
568
509
|
{
|
|
569
|
-
|
|
510
|
+
placeholder: "Order or reference ID",
|
|
570
511
|
value: referenceId,
|
|
571
|
-
onChange: (event) => setReferenceId(event.target.value)
|
|
512
|
+
onChange: (event) => setReferenceId(event.target.value),
|
|
513
|
+
className: "rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] px-3 py-2 text-sm focus:border-brand-500 focus:outline-none"
|
|
572
514
|
}
|
|
573
515
|
)
|
|
574
516
|
] }),
|
|
575
517
|
/* @__PURE__ */ jsx(
|
|
576
|
-
|
|
518
|
+
"input",
|
|
577
519
|
{
|
|
578
|
-
|
|
520
|
+
placeholder: "Note (optional)",
|
|
579
521
|
value: note,
|
|
580
|
-
onChange: (event) => setNote(event.target.value)
|
|
522
|
+
onChange: (event) => setNote(event.target.value),
|
|
523
|
+
className: "w-full rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] px-3 py-2 text-sm focus:border-brand-500 focus:outline-none"
|
|
581
524
|
}
|
|
582
525
|
),
|
|
583
526
|
/* @__PURE__ */ jsx(
|
|
584
|
-
|
|
527
|
+
"button",
|
|
585
528
|
{
|
|
586
529
|
type: "submit",
|
|
587
530
|
disabled: saving || !amount || Number(amount) <= 0,
|
|
531
|
+
className: "rounded-md bg-brand-500 px-4 py-2 text-sm font-medium text-[var(--text-inverse)] hover:bg-brand-600 disabled:opacity-50",
|
|
588
532
|
children: saving ? "Redeeming..." : "Redeem gift card"
|
|
589
533
|
}
|
|
590
534
|
)
|
|
@@ -612,15 +556,8 @@ function GiftCardDetailModal({
|
|
|
612
556
|
redemption.id
|
|
613
557
|
)) })
|
|
614
558
|
] }),
|
|
615
|
-
error && /* @__PURE__ */ jsx(
|
|
616
|
-
|
|
617
|
-
{
|
|
618
|
-
role: "alert",
|
|
619
|
-
className: "rounded-md bg-[var(--error-bg)] px-3 py-2 text-sm text-[var(--error)]",
|
|
620
|
-
children: error
|
|
621
|
-
}
|
|
622
|
-
),
|
|
623
|
-
giftCard.status !== "cancelled" && /* @__PURE__ */ jsx("div", { className: "flex justify-end border-t border-[var(--border-default)] pt-4", children: /* @__PURE__ */ jsx(
|
|
559
|
+
error && /* @__PURE__ */ jsx("p", { className: "rounded-md bg-[var(--error-bg)] px-3 py-2 text-sm text-[var(--error)]", children: error }),
|
|
560
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-end border-t border-[var(--border-default)] pt-4", children: /* @__PURE__ */ jsx(
|
|
624
561
|
InlineDeleteButton,
|
|
625
562
|
{
|
|
626
563
|
onDelete: remove,
|
|
@@ -636,14 +573,12 @@ function GiftCardsFeature() {
|
|
|
636
573
|
const { tenant } = useAuth();
|
|
637
574
|
const formatCurrency = useFormatCurrency();
|
|
638
575
|
const { toast } = useToast();
|
|
639
|
-
const slug = tenant?.slug;
|
|
640
576
|
const currency = normalizeCurrencyCode(
|
|
641
577
|
tenant?.settings?.currency
|
|
642
578
|
);
|
|
643
579
|
const [giftCards, setGiftCards] = useState([]);
|
|
644
580
|
const [stats, setStats] = useState(null);
|
|
645
581
|
const [loading, setLoading] = useState(true);
|
|
646
|
-
const [loaded, setLoaded] = useState(false);
|
|
647
582
|
const [search, setSearch] = useState("");
|
|
648
583
|
const [status, setStatus] = useState("");
|
|
649
584
|
const [page, setPage] = useState(1);
|
|
@@ -651,386 +586,313 @@ function GiftCardsFeature() {
|
|
|
651
586
|
const [showIssue, setShowIssue] = useState(false);
|
|
652
587
|
const [showBalance, setShowBalance] = useState(false);
|
|
653
588
|
const [selected, setSelected] = useState(null);
|
|
654
|
-
const [dedupe] = useState(createInFlightDeduper);
|
|
655
|
-
const toastRef = useRef(toast);
|
|
656
|
-
useEffect(() => {
|
|
657
|
-
toastRef.current = toast;
|
|
658
|
-
});
|
|
659
589
|
const latestRead = useRef(0);
|
|
660
590
|
const readGiftCards = useCallback(
|
|
661
591
|
async (quiet) => {
|
|
662
|
-
if (!slug) return;
|
|
592
|
+
if (!tenant?.slug) return;
|
|
663
593
|
const read = quiet ? latestRead.current : ++latestRead.current;
|
|
664
594
|
if (!quiet) setLoading(true);
|
|
665
|
-
const query = {
|
|
666
|
-
...status ? { status } : {},
|
|
667
|
-
...search.trim() ? { search: search.trim() } : {},
|
|
668
|
-
page,
|
|
669
|
-
limit: PAGE_SIZE
|
|
670
|
-
};
|
|
671
595
|
try {
|
|
672
|
-
const list = await
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
596
|
+
const [list, giftCardStats] = await Promise.all([
|
|
597
|
+
giftCardsApi.list(tenant.slug, {
|
|
598
|
+
...status ? { status } : {},
|
|
599
|
+
...search.trim() ? { search: search.trim() } : {},
|
|
600
|
+
page,
|
|
601
|
+
limit: PAGE_SIZE
|
|
602
|
+
}),
|
|
603
|
+
giftCardsApi.stats(tenant.slug)
|
|
604
|
+
]);
|
|
676
605
|
if (read !== latestRead.current) return;
|
|
677
606
|
setGiftCards(list.rows);
|
|
678
607
|
setCount(list.count);
|
|
608
|
+
setStats(giftCardStats);
|
|
679
609
|
} catch (loadError) {
|
|
680
610
|
if (quiet) throw loadError;
|
|
681
|
-
|
|
611
|
+
toast({
|
|
682
612
|
title: "Unable to load gift cards",
|
|
683
613
|
description: errorMessage(loadError)
|
|
684
614
|
});
|
|
685
615
|
} finally {
|
|
686
|
-
if (!quiet
|
|
687
|
-
setLoading(false);
|
|
688
|
-
setLoaded(true);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
},
|
|
692
|
-
[dedupe, page, search, slug, status]
|
|
693
|
-
);
|
|
694
|
-
const readStats = useCallback(
|
|
695
|
-
async (quiet) => {
|
|
696
|
-
if (!slug) return;
|
|
697
|
-
try {
|
|
698
|
-
setStats(await dedupe.run(`stats:${slug}`, () => giftCardsApi.stats(slug)));
|
|
699
|
-
} catch (statsError) {
|
|
700
|
-
if (quiet) throw statsError;
|
|
616
|
+
if (!quiet) setLoading(false);
|
|
701
617
|
}
|
|
702
618
|
},
|
|
703
|
-
[
|
|
704
|
-
);
|
|
705
|
-
const reloadAll = useCallback(
|
|
706
|
-
() => Promise.all([readGiftCards(false), readStats(false)]),
|
|
707
|
-
[readGiftCards, readStats]
|
|
619
|
+
[page, search, status, tenant?.slug, toast]
|
|
708
620
|
);
|
|
621
|
+
const loadGiftCards = useCallback(() => readGiftCards(false), [readGiftCards]);
|
|
709
622
|
useEffect(() => {
|
|
710
|
-
|
|
711
|
-
}, [
|
|
712
|
-
useEffect(() => {
|
|
713
|
-
readStats(false);
|
|
714
|
-
}, [readStats]);
|
|
623
|
+
loadGiftCards();
|
|
624
|
+
}, [loadGiftCards]);
|
|
715
625
|
useQuietPoll(
|
|
716
|
-
useCallback(
|
|
717
|
-
() => Promise.all([readGiftCards(true), readStats(true)]),
|
|
718
|
-
[readGiftCards, readStats]
|
|
719
|
-
),
|
|
626
|
+
useCallback(() => readGiftCards(true), [readGiftCards]),
|
|
720
627
|
LIVE_REFRESH_MS,
|
|
721
628
|
LIVE
|
|
722
629
|
);
|
|
723
|
-
useRegisterRefresh(
|
|
630
|
+
useRegisterRefresh(loadGiftCards);
|
|
724
631
|
const copyCode = async (event, code) => {
|
|
725
632
|
event.stopPropagation();
|
|
726
633
|
await navigator.clipboard.writeText(code);
|
|
727
634
|
toast({ title: "Gift card code copied", description: code });
|
|
728
635
|
};
|
|
729
636
|
const totalPages = Math.max(1, Math.ceil(count / PAGE_SIZE));
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
637
|
+
const filters = [
|
|
638
|
+
{ value: "", label: "All" },
|
|
639
|
+
{ value: "active", label: "Active" },
|
|
640
|
+
{ value: "used", label: "Used" },
|
|
641
|
+
{ value: "expired", label: "Expired" },
|
|
642
|
+
{ value: "cancelled", label: "Cancelled" }
|
|
643
|
+
];
|
|
644
|
+
return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-7xl space-y-5 lg:space-y-6", children: [
|
|
645
|
+
/* @__PURE__ */ jsx(
|
|
646
|
+
MobilePageHeader,
|
|
647
|
+
{
|
|
648
|
+
title: "Gift cards",
|
|
649
|
+
action: { label: "Issue gift card", onClick: () => setShowIssue(true) }
|
|
650
|
+
}
|
|
651
|
+
),
|
|
652
|
+
/* @__PURE__ */ jsxs("div", { className: "hidden items-center justify-between gap-3 lg:flex", children: [
|
|
653
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
654
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold text-[var(--text-primary)]", children: "Gift cards" }),
|
|
655
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-[var(--text-tertiary)]", children: "Credit a customer pays for now and spends later. Discounts and loyalty points cost the customer nothing." })
|
|
656
|
+
] }),
|
|
657
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
658
|
+
!LIVE && /* @__PURE__ */ jsx(RefreshButton, { onClick: loadGiftCards, refreshing: loading }),
|
|
659
|
+
/* @__PURE__ */ jsxs(
|
|
660
|
+
"button",
|
|
661
|
+
{
|
|
662
|
+
type: "button",
|
|
663
|
+
onClick: () => setShowBalance(true),
|
|
664
|
+
className: "flex items-center gap-2 rounded-md border border-[var(--border-default)] px-3 py-2 text-sm font-medium text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]",
|
|
665
|
+
children: [
|
|
666
|
+
/* @__PURE__ */ jsx(Search, { className: "h-4 w-4" }),
|
|
667
|
+
"Check balance"
|
|
668
|
+
]
|
|
669
|
+
}
|
|
670
|
+
),
|
|
671
|
+
/* @__PURE__ */ jsxs(
|
|
672
|
+
"button",
|
|
673
|
+
{
|
|
674
|
+
type: "button",
|
|
675
|
+
onClick: () => setShowIssue(true),
|
|
676
|
+
className: "flex items-center gap-2 rounded-md bg-brand-500 px-4 py-2 text-sm font-medium text-[var(--text-inverse)] hover:bg-brand-600",
|
|
677
|
+
children: [
|
|
678
|
+
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
|
|
679
|
+
"Issue gift card"
|
|
680
|
+
]
|
|
681
|
+
}
|
|
682
|
+
)
|
|
683
|
+
] })
|
|
684
|
+
] }),
|
|
685
|
+
/* @__PURE__ */ jsx("div", { className: "hidden lg:block", children: /* @__PURE__ */ jsxs(MetricRow, { children: [
|
|
686
|
+
/* @__PURE__ */ jsx(
|
|
687
|
+
MetricTile,
|
|
749
688
|
{
|
|
750
|
-
|
|
751
|
-
|
|
689
|
+
label: "Issued value",
|
|
690
|
+
value: formatCurrency(stats?.totalIssued ?? 0, currency)
|
|
752
691
|
}
|
|
753
692
|
),
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
693
|
+
/* @__PURE__ */ jsx(
|
|
694
|
+
MetricTile,
|
|
695
|
+
{
|
|
696
|
+
label: "Outstanding",
|
|
697
|
+
value: formatCurrency(stats?.outstanding ?? 0, currency)
|
|
698
|
+
}
|
|
699
|
+
),
|
|
700
|
+
/* @__PURE__ */ jsx(
|
|
701
|
+
MetricTile,
|
|
702
|
+
{
|
|
703
|
+
label: "Redeemed",
|
|
704
|
+
value: formatCurrency(stats?.redeemed ?? 0, currency)
|
|
705
|
+
}
|
|
706
|
+
),
|
|
707
|
+
/* @__PURE__ */ jsx(MetricTile, { label: "Active cards", value: stats?.activeCards ?? 0 })
|
|
708
|
+
] }) }),
|
|
709
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-px overflow-hidden rounded-md bg-[var(--border-default)] lg:hidden", children: [
|
|
710
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-[var(--bg-subtle)] p-4", children: [
|
|
711
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: "Outstanding" }),
|
|
712
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-lg font-semibold text-[var(--text-primary)]", children: formatCurrency(stats?.outstanding ?? 0, currency) })
|
|
770
713
|
] }),
|
|
771
|
-
children: [
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
] }),
|
|
808
|
-
hasCards && /* @__PURE__ */ jsx("div", { className: "flex gap-2 overflow-x-auto lg:hidden", children: /* @__PURE__ */ jsx(
|
|
809
|
-
Button,
|
|
714
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-[var(--bg-subtle)] p-4", children: [
|
|
715
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: "Active cards" }),
|
|
716
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-lg font-semibold text-[var(--text-primary)]", children: stats?.activeCards ?? 0 })
|
|
717
|
+
] })
|
|
718
|
+
] }),
|
|
719
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 overflow-x-auto lg:hidden", children: [
|
|
720
|
+
/* @__PURE__ */ jsxs(
|
|
721
|
+
"button",
|
|
722
|
+
{
|
|
723
|
+
type: "button",
|
|
724
|
+
onClick: () => setShowIssue(true),
|
|
725
|
+
className: "flex shrink-0 items-center gap-1.5 rounded-full bg-brand-500 px-3.5 py-2 text-sm font-medium text-[var(--text-inverse)]",
|
|
726
|
+
children: [
|
|
727
|
+
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
|
|
728
|
+
"Issue gift card"
|
|
729
|
+
]
|
|
730
|
+
}
|
|
731
|
+
),
|
|
732
|
+
/* @__PURE__ */ jsxs(
|
|
733
|
+
"button",
|
|
734
|
+
{
|
|
735
|
+
type: "button",
|
|
736
|
+
onClick: () => setShowBalance(true),
|
|
737
|
+
className: "flex shrink-0 items-center gap-1.5 rounded-full bg-[var(--bg-subtle)] px-3.5 py-2 text-sm font-medium text-[var(--text-secondary)]",
|
|
738
|
+
children: [
|
|
739
|
+
/* @__PURE__ */ jsx(Search, { className: "h-4 w-4" }),
|
|
740
|
+
"Check balance"
|
|
741
|
+
]
|
|
742
|
+
}
|
|
743
|
+
)
|
|
744
|
+
] }),
|
|
745
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 lg:flex-row lg:items-center", children: [
|
|
746
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex-1", children: [
|
|
747
|
+
/* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[var(--text-muted)]" }),
|
|
748
|
+
/* @__PURE__ */ jsx(
|
|
749
|
+
"input",
|
|
810
750
|
{
|
|
811
|
-
type: "
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
751
|
+
type: "search",
|
|
752
|
+
placeholder: "Search code, recipient, or email",
|
|
753
|
+
value: search,
|
|
754
|
+
onChange: (event) => {
|
|
755
|
+
setSearch(event.target.value);
|
|
756
|
+
setPage(1);
|
|
757
|
+
},
|
|
758
|
+
className: "w-full rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] py-2 pl-10 pr-3 text-sm focus:border-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-500/20"
|
|
817
759
|
}
|
|
818
|
-
)
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
760
|
+
)
|
|
761
|
+
] }),
|
|
762
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-2 overflow-x-auto", children: filters.map((filter) => /* @__PURE__ */ jsx(
|
|
763
|
+
"button",
|
|
764
|
+
{
|
|
765
|
+
type: "button",
|
|
766
|
+
onClick: () => {
|
|
767
|
+
setStatus(filter.value);
|
|
768
|
+
setPage(1);
|
|
769
|
+
},
|
|
770
|
+
className: `shrink-0 rounded-md px-3 py-2 text-sm font-medium ${status === filter.value ? "bg-brand-500 text-[var(--text-inverse)]" : "border border-[var(--border-default)] bg-[var(--bg-base)] text-[var(--text-secondary)] hover:bg-[var(--interactive-hover)]"}`,
|
|
771
|
+
children: filter.label
|
|
772
|
+
},
|
|
773
|
+
filter.value
|
|
774
|
+
)) })
|
|
775
|
+
] }),
|
|
776
|
+
loading && giftCards.length === 0 && /* @__PURE__ */ jsx(InlineListSkeleton, {}),
|
|
777
|
+
!loading && giftCards.length === 0 && /* @__PURE__ */ jsx(
|
|
778
|
+
TableEmptyState,
|
|
779
|
+
{
|
|
780
|
+
icon: Gift,
|
|
781
|
+
title: search || status ? "No gift cards found" : "Issue your first gift card",
|
|
782
|
+
description: search || status ? "Try changing the search or status filter" : "Create a stored-value card for a customer or walk-in purchase"
|
|
783
|
+
}
|
|
784
|
+
),
|
|
785
|
+
giftCards.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
786
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2 lg:hidden", children: giftCards.map((giftCard) => /* @__PURE__ */ jsxs(
|
|
787
|
+
"button",
|
|
788
|
+
{
|
|
789
|
+
type: "button",
|
|
790
|
+
onClick: () => setSelected(giftCard),
|
|
791
|
+
className: "flex w-full items-center justify-between gap-3 rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] p-4 text-left",
|
|
792
|
+
children: [
|
|
793
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
794
|
+
/* @__PURE__ */ jsx("p", { className: "truncate font-mono text-sm font-semibold text-[var(--text-primary)]", children: giftCard.code }),
|
|
795
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm text-[var(--text-tertiary)]", children: giftCard.recipientName || giftCard.recipientEmail || "Walk-in" })
|
|
796
|
+
] }),
|
|
797
|
+
/* @__PURE__ */ jsxs("div", { className: "shrink-0 text-right", children: [
|
|
798
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-[var(--text-primary)]", children: formatCurrency(Number(giftCard.balance), giftCard.currency) }),
|
|
799
|
+
/* @__PURE__ */ jsx("span", { className: `text-xs capitalize ${statusClass(giftCard.status)}`, children: giftCard.status })
|
|
800
|
+
] })
|
|
801
|
+
]
|
|
802
|
+
},
|
|
803
|
+
giftCard.id
|
|
804
|
+
)) }),
|
|
805
|
+
/* @__PURE__ */ jsx("div", { className: "hidden overflow-x-auto rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] lg:block", children: /* @__PURE__ */ jsxs("table", { className: "w-full min-w-[760px]", children: [
|
|
806
|
+
/* @__PURE__ */ jsx("thead", { className: "border-b border-[var(--border-default)] bg-[var(--bg-subtle)]", children: /* @__PURE__ */ jsx("tr", { children: ["Code", "Recipient", "Issued", "Balance", "Expires", "Status"].map(
|
|
807
|
+
(heading) => /* @__PURE__ */ jsx(
|
|
808
|
+
"th",
|
|
845
809
|
{
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
810
|
+
className: "px-4 py-3 text-left text-xs font-medium uppercase text-[var(--text-tertiary)]",
|
|
811
|
+
children: heading
|
|
812
|
+
},
|
|
813
|
+
heading
|
|
814
|
+
)
|
|
815
|
+
) }) }),
|
|
816
|
+
/* @__PURE__ */ jsx("tbody", { className: "divide-y divide-[var(--border-default)]", children: giftCards.map((giftCard) => /* @__PURE__ */ jsxs(
|
|
817
|
+
"tr",
|
|
818
|
+
{
|
|
819
|
+
onClick: () => setSelected(giftCard),
|
|
820
|
+
className: "cursor-pointer hover:bg-[var(--interactive-hover)]",
|
|
821
|
+
children: [
|
|
822
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
823
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-sm font-medium text-[var(--text-primary)]", children: giftCard.code }),
|
|
824
|
+
/* @__PURE__ */ jsx(
|
|
825
|
+
"button",
|
|
853
826
|
{
|
|
854
827
|
type: "button",
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
"
|
|
858
|
-
className: "
|
|
859
|
-
onClick: () => {
|
|
860
|
-
setStatus(filter.value);
|
|
861
|
-
setPage(1);
|
|
862
|
-
},
|
|
863
|
-
children: filter.label
|
|
828
|
+
onClick: (event) => copyCode(event, giftCard.code),
|
|
829
|
+
className: "rounded p-1 text-[var(--text-muted)] hover:text-[var(--text-primary)]",
|
|
830
|
+
title: "Copy code",
|
|
831
|
+
children: /* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" })
|
|
864
832
|
}
|
|
865
|
-
)
|
|
866
|
-
})
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
description: "Create a stored-value card for a customer or walk-in purchase",
|
|
880
|
-
action: { label: "Issue gift card", onClick: openIssue }
|
|
881
|
-
}
|
|
882
|
-
),
|
|
883
|
-
/* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(
|
|
884
|
-
Button,
|
|
885
|
-
{
|
|
886
|
-
type: "button",
|
|
887
|
-
variant: "ghost",
|
|
888
|
-
icon: /* @__PURE__ */ jsx(Search, {}),
|
|
889
|
-
onClick: openBalance,
|
|
890
|
-
children: "Check balance"
|
|
891
|
-
}
|
|
892
|
-
) })
|
|
893
|
-
] }),
|
|
894
|
-
loaded && !loading && !firstRun && giftCards.length === 0 && /* @__PURE__ */ jsx(
|
|
895
|
-
TableEmptyState,
|
|
896
|
-
{
|
|
897
|
-
icon: Gift,
|
|
898
|
-
title: "No gift cards found",
|
|
899
|
-
description: "Try changing the search or status filter",
|
|
900
|
-
action: hasFilters ? {
|
|
901
|
-
label: "Clear search and filters",
|
|
902
|
-
onClick: () => {
|
|
903
|
-
setSearch("");
|
|
904
|
-
setStatus("");
|
|
905
|
-
setPage(1);
|
|
906
|
-
}
|
|
907
|
-
} : void 0
|
|
908
|
-
}
|
|
909
|
-
),
|
|
910
|
-
giftCards.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
911
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-2 lg:hidden", children: giftCards.map((giftCard) => /* @__PURE__ */ jsxs(
|
|
912
|
-
"button",
|
|
913
|
-
{
|
|
914
|
-
type: "button",
|
|
915
|
-
onClick: () => setSelected(giftCard),
|
|
916
|
-
className: "flex w-full items-center justify-between gap-3 rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] p-4 text-left",
|
|
917
|
-
children: [
|
|
918
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
919
|
-
/* @__PURE__ */ jsx("p", { className: "truncate font-mono text-sm font-semibold text-[var(--text-primary)]", children: giftCard.code }),
|
|
920
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-sm text-[var(--text-tertiary)]", children: giftCard.recipientName || giftCard.recipientEmail || "Walk-in" })
|
|
921
|
-
] }),
|
|
922
|
-
/* @__PURE__ */ jsxs("div", { className: "shrink-0 text-right", children: [
|
|
923
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-[var(--text-primary)]", children: formatCurrency(Number(giftCard.balance), giftCard.currency) }),
|
|
924
|
-
/* @__PURE__ */ jsx("span", { className: `text-xs ${statusClass(giftCard.status)}`, children: STATUS_LABEL[giftCard.status] })
|
|
925
|
-
] })
|
|
926
|
-
]
|
|
927
|
-
},
|
|
928
|
-
giftCard.id
|
|
929
|
-
)) }),
|
|
930
|
-
/* @__PURE__ */ jsx("div", { className: "hidden overflow-x-auto rounded-md border border-[var(--border-default)] bg-[var(--bg-base)] lg:block", children: /* @__PURE__ */ jsxs("table", { className: "w-full min-w-[760px]", children: [
|
|
931
|
-
/* @__PURE__ */ jsx("thead", { className: "border-b border-[var(--border-default)] bg-[var(--bg-subtle)]", children: /* @__PURE__ */ jsx("tr", { children: ["Code", "Recipient", "Issued", "Balance", "Expires", "Status"].map(
|
|
932
|
-
(heading) => /* @__PURE__ */ jsx(
|
|
933
|
-
"th",
|
|
833
|
+
)
|
|
834
|
+
] }) }),
|
|
835
|
+
/* @__PURE__ */ jsxs("td", { className: "px-4 py-3", children: [
|
|
836
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--text-primary)]", children: giftCard.recipientName || "Walk-in" }),
|
|
837
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: giftCard.recipientEmail })
|
|
838
|
+
] }),
|
|
839
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-sm text-[var(--text-secondary)]", children: formatCurrency(
|
|
840
|
+
Number(giftCard.originalAmount),
|
|
841
|
+
giftCard.currency
|
|
842
|
+
) }),
|
|
843
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-sm font-medium text-[var(--text-primary)]", children: formatCurrency(Number(giftCard.balance), giftCard.currency) }),
|
|
844
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-sm text-[var(--text-secondary)]", children: dateLabel(giftCard.expiresAt) }),
|
|
845
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx(
|
|
846
|
+
"span",
|
|
934
847
|
{
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
{
|
|
984
|
-
className: `inline-flex rounded-full px-2.5 py-1 text-xs font-medium ${statusClass(giftCard.status)}`,
|
|
985
|
-
children: STATUS_LABEL[giftCard.status]
|
|
986
|
-
}
|
|
987
|
-
) })
|
|
988
|
-
]
|
|
989
|
-
},
|
|
990
|
-
giftCard.id
|
|
991
|
-
)) })
|
|
992
|
-
] }) }),
|
|
993
|
-
/* @__PURE__ */ jsx(
|
|
994
|
-
TablePagination,
|
|
995
|
-
{
|
|
996
|
-
page,
|
|
997
|
-
totalPages,
|
|
998
|
-
pageSize: PAGE_SIZE,
|
|
999
|
-
totalItems: count,
|
|
1000
|
-
entityName: "gift cards",
|
|
1001
|
-
onPageChange: setPage
|
|
1002
|
-
}
|
|
1003
|
-
)
|
|
1004
|
-
] }),
|
|
1005
|
-
showIssue && /* @__PURE__ */ jsx(
|
|
1006
|
-
IssueGiftCardModal,
|
|
1007
|
-
{
|
|
1008
|
-
currency,
|
|
1009
|
-
merchantName: tenant?.businessName || "Gift card",
|
|
1010
|
-
onClose: () => setShowIssue(false),
|
|
1011
|
-
onCreated: (giftCard) => {
|
|
1012
|
-
setShowIssue(false);
|
|
1013
|
-
setSelected(giftCard);
|
|
1014
|
-
toast({
|
|
1015
|
-
title: "Gift card issued",
|
|
1016
|
-
description: giftCard.code
|
|
1017
|
-
});
|
|
1018
|
-
reloadAll();
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
),
|
|
1022
|
-
showBalance && /* @__PURE__ */ jsx(BalanceModal, { onClose: () => setShowBalance(false) }),
|
|
1023
|
-
selected && /* @__PURE__ */ jsx(
|
|
1024
|
-
GiftCardDetailModal,
|
|
1025
|
-
{
|
|
1026
|
-
giftCard: selected,
|
|
1027
|
-
onClose: () => setSelected(null),
|
|
1028
|
-
onChanged: reloadAll
|
|
1029
|
-
}
|
|
1030
|
-
)
|
|
1031
|
-
]
|
|
1032
|
-
}
|
|
1033
|
-
);
|
|
848
|
+
className: `inline-flex rounded-full px-2.5 py-1 text-xs font-medium capitalize ${statusClass(giftCard.status)}`,
|
|
849
|
+
children: giftCard.status
|
|
850
|
+
}
|
|
851
|
+
) })
|
|
852
|
+
]
|
|
853
|
+
},
|
|
854
|
+
giftCard.id
|
|
855
|
+
)) })
|
|
856
|
+
] }) }),
|
|
857
|
+
/* @__PURE__ */ jsx(
|
|
858
|
+
TablePagination,
|
|
859
|
+
{
|
|
860
|
+
page,
|
|
861
|
+
totalPages,
|
|
862
|
+
pageSize: PAGE_SIZE,
|
|
863
|
+
totalItems: count,
|
|
864
|
+
entityName: "gift cards",
|
|
865
|
+
onPageChange: setPage
|
|
866
|
+
}
|
|
867
|
+
)
|
|
868
|
+
] }),
|
|
869
|
+
showIssue && /* @__PURE__ */ jsx(
|
|
870
|
+
IssueGiftCardModal,
|
|
871
|
+
{
|
|
872
|
+
currency,
|
|
873
|
+
merchantName: tenant?.businessName || "Gift card",
|
|
874
|
+
onClose: () => setShowIssue(false),
|
|
875
|
+
onCreated: (giftCard) => {
|
|
876
|
+
setShowIssue(false);
|
|
877
|
+
setSelected(giftCard);
|
|
878
|
+
toast({
|
|
879
|
+
title: "Gift card issued",
|
|
880
|
+
description: giftCard.code
|
|
881
|
+
});
|
|
882
|
+
loadGiftCards();
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
),
|
|
886
|
+
showBalance && /* @__PURE__ */ jsx(BalanceModal, { onClose: () => setShowBalance(false) }),
|
|
887
|
+
selected && /* @__PURE__ */ jsx(
|
|
888
|
+
GiftCardDetailModal,
|
|
889
|
+
{
|
|
890
|
+
giftCard: selected,
|
|
891
|
+
onClose: () => setSelected(null),
|
|
892
|
+
onChanged: loadGiftCards
|
|
893
|
+
}
|
|
894
|
+
)
|
|
895
|
+
] });
|
|
1034
896
|
}
|
|
1035
897
|
export {
|
|
1036
898
|
GiftCardsFeature
|