@burdenoff/microfe-atheletebridge 2026.1103.2 → 2026.1108.1
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/atheletebridge/components/registration/OnlineCheckout.d.ts +11 -0
- package/dist/atheletebridge/components/registration/OnlineCheckout.d.ts.map +1 -0
- package/dist/atheletebridge/components/registration/OnlineCheckout.js +102 -0
- package/dist/atheletebridge/components/registration/OnlineCheckout.js.map +1 -0
- package/dist/atheletebridge/components/registration/OnlinePaymentsCard.d.ts +4 -0
- package/dist/atheletebridge/components/registration/OnlinePaymentsCard.d.ts.map +1 -0
- package/dist/atheletebridge/components/registration/OnlinePaymentsCard.js +150 -0
- package/dist/atheletebridge/components/registration/OnlinePaymentsCard.js.map +1 -0
- package/dist/atheletebridge/components/registration/RegistrationEntryFields.d.ts +6 -0
- package/dist/atheletebridge/components/registration/RegistrationEntryFields.d.ts.map +1 -1
- package/dist/atheletebridge/components/registration/RegistrationEntryFields.js +23 -11
- package/dist/atheletebridge/components/registration/RegistrationEntryFields.js.map +1 -1
- package/dist/atheletebridge/components/registration/entryRules.d.ts +2 -0
- package/dist/atheletebridge/components/registration/entryRules.d.ts.map +1 -1
- package/dist/atheletebridge/components/registration/entryRules.js +2 -1
- package/dist/atheletebridge/components/registration/entryRules.js.map +1 -1
- package/dist/atheletebridge/hooks/useRegistrationApi.d.ts +19 -2
- package/dist/atheletebridge/hooks/useRegistrationApi.d.ts.map +1 -1
- package/dist/atheletebridge/hooks/useRegistrationApi.js +76 -33
- package/dist/atheletebridge/hooks/useRegistrationApi.js.map +1 -1
- package/dist/atheletebridge/pages/registrations/MyRegistrationsPage.d.ts +3 -1
- package/dist/atheletebridge/pages/registrations/MyRegistrationsPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/registrations/MyRegistrationsPage.js +71 -64
- package/dist/atheletebridge/pages/registrations/MyRegistrationsPage.js.map +1 -1
- package/dist/atheletebridge/pages/registrations/RegisterPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/registrations/RegisterPage.js +82 -77
- package/dist/atheletebridge/pages/registrations/RegisterPage.js.map +1 -1
- package/dist/atheletebridge/pages/registrations/RegistrationFormsListPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/registrations/RegistrationFormsListPage.js +49 -47
- package/dist/atheletebridge/pages/registrations/RegistrationFormsListPage.js.map +1 -1
- package/dist/atheletebridge/pages/registrations/RegistrationSubmissionPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/registrations/RegistrationSubmissionPage.js +140 -109
- package/dist/atheletebridge/pages/registrations/RegistrationSubmissionPage.js.map +1 -1
- package/dist/atheletebridge/types/registration.d.ts +49 -0
- package/dist/atheletebridge/types/registration.d.ts.map +1 -1
- package/dist/atheletebridge/utils/onlineCheckout.d.ts +69 -0
- package/dist/atheletebridge/utils/onlineCheckout.d.ts.map +1 -0
- package/dist/atheletebridge/utils/onlineCheckout.js +61 -0
- package/dist/atheletebridge/utils/onlineCheckout.js.map +1 -0
- package/dist/atheletebridge/utils/registration.d.ts +19 -0
- package/dist/atheletebridge/utils/registration.d.ts.map +1 -1
- package/dist/atheletebridge/utils/registration.js +28 -1
- package/dist/atheletebridge/utils/registration.js.map +1 -1
- package/dist/generated/global-types.d.ts +6164 -1907
- package/dist/generated/global-types.d.ts.map +1 -1
- package/dist/generated/wspace-types.d.ts +60796 -3422
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -6,148 +6,153 @@ import { EmptyState as i } from "../../components/EmptyState.js";
|
|
|
6
6
|
import { ErrorState as a, LoadingState as o } from "../../components/LoadingState.js";
|
|
7
7
|
import { usePublicRegistrationForm as s, useSubmitRegistration as c } from "../../hooks/useRegistrationApi.js";
|
|
8
8
|
import { formatMoney as l } from "../../utils/money.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
9
|
+
import { canPayOnline as u } from "../../utils/onlineCheckout.js";
|
|
10
|
+
import { PayOnlineButton as d } from "../../components/registration/OnlineCheckout.js";
|
|
11
|
+
import { EMPTY_REGISTRATION_ENTRY as f, cleanAnswers as p, missingRequiredAnswer as m } from "../../components/registration/entryRules.js";
|
|
12
|
+
import { RegistrationEntryFields as h } from "../../components/registration/RegistrationEntryFields.js";
|
|
13
|
+
import { useState as g } from "react";
|
|
14
|
+
import { CheckCircle2 as _, ClipboardX as v, Loader2 as y, Send as b } from "lucide-react";
|
|
15
|
+
import { useParams as x } from "react-router";
|
|
16
|
+
import { jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
15
17
|
//#region src/atheletebridge/pages/registrations/RegisterPage.tsx
|
|
16
|
-
function
|
|
17
|
-
let { slug:
|
|
18
|
-
if (
|
|
18
|
+
function w() {
|
|
19
|
+
let { slug: w } = x(), { navigateTo: T } = e(), E = s(w), D = c(), [O, k] = g(f), [A, j] = g(null), [M, N] = g(null);
|
|
20
|
+
if (E.isLoading) return /* @__PURE__ */ S(t, {
|
|
19
21
|
title: "Registration",
|
|
20
|
-
children: /* @__PURE__ */
|
|
22
|
+
children: /* @__PURE__ */ S(o, { label: "Loading registration form…" })
|
|
21
23
|
});
|
|
22
|
-
if (
|
|
24
|
+
if (E.isError) return /* @__PURE__ */ S(t, {
|
|
23
25
|
title: "Registration",
|
|
24
|
-
children: /* @__PURE__ */
|
|
26
|
+
children: /* @__PURE__ */ S(a, { onRetry: () => void E.refetch() })
|
|
25
27
|
});
|
|
26
|
-
if (!
|
|
28
|
+
if (!E.data || !w) return /* @__PURE__ */ S(t, {
|
|
27
29
|
title: "Registration",
|
|
28
|
-
children: /* @__PURE__ */
|
|
30
|
+
children: /* @__PURE__ */ S(i, {
|
|
29
31
|
illustration: "empty-data",
|
|
30
|
-
icon: /* @__PURE__ */
|
|
32
|
+
icon: /* @__PURE__ */ S(v, { className: "h-10 w-10" }),
|
|
31
33
|
title: "This registration link is not available",
|
|
32
34
|
description: "The club may not have published it yet, or the link is incorrect. Check the link with the club."
|
|
33
35
|
})
|
|
34
36
|
});
|
|
35
|
-
let
|
|
36
|
-
if (
|
|
37
|
-
title:
|
|
38
|
-
children: /* @__PURE__ */
|
|
37
|
+
let P = E.data;
|
|
38
|
+
if (M) return /* @__PURE__ */ S(t, {
|
|
39
|
+
title: P.title,
|
|
40
|
+
children: /* @__PURE__ */ C("div", {
|
|
39
41
|
className: "max-w-xl space-y-4 rounded-lg border border-border-seam bg-bg-surface p-6",
|
|
40
42
|
"data-testid": "registration-receipt",
|
|
41
43
|
children: [
|
|
42
|
-
/* @__PURE__ */
|
|
44
|
+
/* @__PURE__ */ C("p", {
|
|
43
45
|
className: "flex items-center gap-2 text-lg font-semibold text-text-primary",
|
|
44
|
-
children: [/* @__PURE__ */
|
|
46
|
+
children: [/* @__PURE__ */ S(_, { className: "h-6 w-6 text-status-success-text" }), " Registration received"]
|
|
45
47
|
}),
|
|
46
|
-
/* @__PURE__ */
|
|
48
|
+
/* @__PURE__ */ C("p", {
|
|
47
49
|
className: "text-sm text-text-secondary",
|
|
48
50
|
children: [
|
|
49
|
-
|
|
51
|
+
M.participantName,
|
|
50
52
|
" is registered for ",
|
|
51
|
-
|
|
52
|
-
".
|
|
53
|
-
|
|
54
|
-
"."
|
|
53
|
+
M.formTitle,
|
|
54
|
+
".",
|
|
55
|
+
" ",
|
|
56
|
+
u(M) ? "The club will review it. You can pay the fee online now, or later from My registrations." : M.amountDue > 0 ? "The club will review it and confirm payment details with you." : "The club will review it."
|
|
55
57
|
]
|
|
56
58
|
}),
|
|
57
|
-
/* @__PURE__ */
|
|
59
|
+
/* @__PURE__ */ C("div", {
|
|
58
60
|
className: "flex flex-wrap items-center gap-2 text-sm",
|
|
59
61
|
children: [
|
|
60
|
-
/* @__PURE__ */
|
|
61
|
-
/* @__PURE__ */
|
|
62
|
-
|
|
62
|
+
/* @__PURE__ */ S(n, { status: M.status }),
|
|
63
|
+
/* @__PURE__ */ S(n, { status: M.paymentStatus }),
|
|
64
|
+
M.amountDue > 0 && /* @__PURE__ */ C("span", {
|
|
63
65
|
className: "tabular-nums text-text-primary",
|
|
64
|
-
children: [l(
|
|
66
|
+
children: [l(M.amountOutstanding, M.currency), " due"]
|
|
65
67
|
})
|
|
66
68
|
]
|
|
67
69
|
}),
|
|
68
|
-
/* @__PURE__ */
|
|
70
|
+
u(M) && /* @__PURE__ */ S("div", { children: /* @__PURE__ */ S(d, { registration: M }) }),
|
|
71
|
+
/* @__PURE__ */ S("button", {
|
|
69
72
|
type: "button",
|
|
70
|
-
onClick: () =>
|
|
73
|
+
onClick: () => T("/my-registrations"),
|
|
71
74
|
className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90",
|
|
72
75
|
children: "View my registrations"
|
|
73
76
|
})
|
|
74
77
|
]
|
|
75
78
|
})
|
|
76
79
|
});
|
|
77
|
-
let
|
|
78
|
-
e.preventDefault(),
|
|
79
|
-
let t =
|
|
80
|
+
let F = async (e) => {
|
|
81
|
+
e.preventDefault(), j(null);
|
|
82
|
+
let t = m(P.fields, O);
|
|
80
83
|
if (t) {
|
|
81
|
-
|
|
84
|
+
j(`Please answer "${t}".`);
|
|
82
85
|
return;
|
|
83
86
|
}
|
|
84
|
-
if (
|
|
85
|
-
|
|
87
|
+
if (P.waiverText && !O.waiverAccepted) {
|
|
88
|
+
j("Please accept the waiver to register.");
|
|
86
89
|
return;
|
|
87
90
|
}
|
|
88
91
|
try {
|
|
89
|
-
|
|
90
|
-
slug:
|
|
92
|
+
N(await D.mutateAsync({
|
|
93
|
+
slug: w,
|
|
91
94
|
input: {
|
|
92
|
-
participantName:
|
|
93
|
-
contactName:
|
|
94
|
-
contactEmail:
|
|
95
|
-
contactPhone:
|
|
96
|
-
answers:
|
|
97
|
-
selectedFeeIds:
|
|
98
|
-
waiverAccepted:
|
|
95
|
+
participantName: O.participantName.trim(),
|
|
96
|
+
contactName: O.contactName.trim() || void 0,
|
|
97
|
+
contactEmail: O.contactEmail.trim() || void 0,
|
|
98
|
+
contactPhone: O.contactPhone.trim() || void 0,
|
|
99
|
+
answers: p(O.answers),
|
|
100
|
+
selectedFeeIds: O.optionalFeeIds,
|
|
101
|
+
waiverAccepted: O.waiverAccepted,
|
|
102
|
+
participantIsRegistrant: O.participantIsRegistrant
|
|
99
103
|
}
|
|
100
104
|
}));
|
|
101
105
|
} catch (e) {
|
|
102
|
-
|
|
106
|
+
j(e instanceof Error ? e.message : "Could not submit the registration.");
|
|
103
107
|
}
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
}, I = [
|
|
109
|
+
P.season,
|
|
110
|
+
P.closesAt ? `Closes ${r(P.closesAt)}` : null,
|
|
111
|
+
P.spotsRemaining == null ? null : `${P.spotsRemaining} places left`
|
|
108
112
|
].filter(Boolean).join(" · ");
|
|
109
|
-
return /* @__PURE__ */
|
|
110
|
-
title:
|
|
111
|
-
description:
|
|
112
|
-
children: /* @__PURE__ */
|
|
113
|
+
return /* @__PURE__ */ S(t, {
|
|
114
|
+
title: P.title,
|
|
115
|
+
description: I || void 0,
|
|
116
|
+
children: /* @__PURE__ */ C("div", {
|
|
113
117
|
className: "max-w-2xl space-y-6",
|
|
114
|
-
children: [
|
|
118
|
+
children: [P.description && /* @__PURE__ */ S("p", {
|
|
115
119
|
className: "whitespace-pre-line text-sm text-text-secondary",
|
|
116
|
-
children:
|
|
117
|
-
}),
|
|
118
|
-
onSubmit:
|
|
120
|
+
children: P.description
|
|
121
|
+
}), P.isOpen ? /* @__PURE__ */ C("form", {
|
|
122
|
+
onSubmit: F,
|
|
119
123
|
className: "space-y-6",
|
|
120
124
|
children: [
|
|
121
|
-
/* @__PURE__ */
|
|
122
|
-
fields:
|
|
123
|
-
fees:
|
|
124
|
-
currency:
|
|
125
|
-
waiverText:
|
|
126
|
-
state:
|
|
127
|
-
onChange:
|
|
125
|
+
/* @__PURE__ */ S(h, {
|
|
126
|
+
fields: P.fields,
|
|
127
|
+
fees: P.fees,
|
|
128
|
+
currency: P.currency,
|
|
129
|
+
waiverText: P.waiverText,
|
|
130
|
+
state: O,
|
|
131
|
+
onChange: k,
|
|
132
|
+
offerSelfRegistration: !0
|
|
128
133
|
}),
|
|
129
|
-
|
|
134
|
+
A && /* @__PURE__ */ S("p", {
|
|
130
135
|
role: "alert",
|
|
131
136
|
className: "text-sm text-status-error-text",
|
|
132
|
-
children:
|
|
137
|
+
children: A
|
|
133
138
|
}),
|
|
134
|
-
/* @__PURE__ */
|
|
139
|
+
/* @__PURE__ */ C("button", {
|
|
135
140
|
type: "submit",
|
|
136
|
-
disabled:
|
|
141
|
+
disabled: D.isPending,
|
|
137
142
|
className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60",
|
|
138
143
|
children: [
|
|
139
|
-
|
|
144
|
+
D.isPending ? /* @__PURE__ */ S(y, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ S(b, { className: "h-4 w-4" }),
|
|
140
145
|
" ",
|
|
141
146
|
"Submit registration"
|
|
142
147
|
]
|
|
143
148
|
})
|
|
144
149
|
]
|
|
145
|
-
}) : /* @__PURE__ */
|
|
150
|
+
}) : /* @__PURE__ */ C("p", {
|
|
146
151
|
role: "status",
|
|
147
152
|
className: "rounded-lg border border-status-warning-border bg-status-warning-bg-subtle p-4 text-sm text-status-warning-text",
|
|
148
153
|
children: [
|
|
149
154
|
"This form is not accepting registrations right now",
|
|
150
|
-
|
|
155
|
+
P.opensAt && new Date(P.opensAt) > /* @__PURE__ */ new Date() ? ` — it opens ${r(P.opensAt)}` : "",
|
|
151
156
|
"."
|
|
152
157
|
]
|
|
153
158
|
})]
|
|
@@ -155,6 +160,6 @@ function S() {
|
|
|
155
160
|
});
|
|
156
161
|
}
|
|
157
162
|
//#endregion
|
|
158
|
-
export {
|
|
163
|
+
export { w as RegisterPage };
|
|
159
164
|
|
|
160
165
|
//# sourceMappingURL=RegisterPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegisterPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/registrations/RegisterPage.tsx"],"sourcesContent":["/**\n * Family registration (share link)\n * @module atheletebridge/pages/registrations\n *\n * Opened from a club's share link (`/register/:slug`). Any signed-in user —\n * typically a parent who is not a member of the club's workspace — can fill\n * in a published form; the registration is filed with the club and tracked\n * under \"My registrations\" (BOFF-4135).\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router';\nimport { CheckCircle2, ClipboardX, Loader2, Send } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/LoadingState';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { RegistrationEntryFields } from '../../components/registration/RegistrationEntryFields';\nimport {\n EMPTY_REGISTRATION_ENTRY,\n type RegistrationEntryState,\n cleanAnswers,\n missingRequiredAnswer,\n} from '../../components/registration/entryRules';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport { usePublicRegistrationForm, useSubmitRegistration } from '../../hooks/useRegistrationApi';\nimport { formatDate } from '../../utils/formatters';\nimport { formatMoney } from '../../utils/money';\nimport type { AthMyRegistration } from '../../types/registration';\n\nexport function RegisterPage() {\n const { slug } = useParams<{ slug: string }>();\n const { navigateTo } = useAthleteBridge();\n const form = usePublicRegistrationForm(slug);\n const submit = useSubmitRegistration();\n const [state, setState] = useState<RegistrationEntryState>(EMPTY_REGISTRATION_ENTRY);\n const [error, setError] = useState<string | null>(null);\n const [receipt, setReceipt] = useState<AthMyRegistration | null>(null);\n\n if (form.isLoading) {\n return (\n <PageLayout title=\"Registration\">\n <LoadingState label=\"Loading registration form…\" />\n </PageLayout>\n );\n }\n if (form.isError) {\n return (\n <PageLayout title=\"Registration\">\n <ErrorState onRetry={() => void form.refetch()} />\n </PageLayout>\n );\n }\n if (!form.data || !slug) {\n return (\n <PageLayout title=\"Registration\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<ClipboardX className=\"h-10 w-10\" />}\n title=\"This registration link is not available\"\n description=\"The club may not have published it yet, or the link is incorrect. Check the link with the club.\"\n />\n </PageLayout>\n );\n }\n const data = form.data;\n\n if (receipt) {\n return (\n <PageLayout title={data.title}>\n <div\n className=\"max-w-xl space-y-4 rounded-lg border border-border-seam bg-bg-surface p-6\"\n data-testid=\"registration-receipt\"\n >\n <p className=\"flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <CheckCircle2 className=\"h-6 w-6 text-status-success-text\" /> Registration received\n </p>\n <p className=\"text-sm text-text-secondary\">\n {receipt.participantName} is registered for {receipt.formTitle}. The club will review it\n {receipt.amountDue > 0 ? ' and confirm payment details with you' : ''}.\n </p>\n <div className=\"flex flex-wrap items-center gap-2 text-sm\">\n <StatusBadge status={receipt.status} />\n <StatusBadge status={receipt.paymentStatus} />\n {receipt.amountDue > 0 && (\n <span className=\"tabular-nums text-text-primary\">\n {formatMoney(receipt.amountOutstanding, receipt.currency)} due\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('/my-registrations')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n View my registrations\n </button>\n </div>\n </PageLayout>\n );\n }\n\n const onSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n const missing = missingRequiredAnswer(data.fields, state);\n if (missing) {\n setError(`Please answer \"${missing}\".`);\n return;\n }\n if (data.waiverText && !state.waiverAccepted) {\n setError('Please accept the waiver to register.');\n return;\n }\n try {\n const result = await submit.mutateAsync({\n slug,\n input: {\n participantName: state.participantName.trim(),\n contactName: state.contactName.trim() || undefined,\n contactEmail: state.contactEmail.trim() || undefined,\n contactPhone: state.contactPhone.trim() || undefined,\n answers: cleanAnswers(state.answers),\n selectedFeeIds: state.optionalFeeIds,\n waiverAccepted: state.waiverAccepted,\n },\n });\n setReceipt(result);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Could not submit the registration.');\n }\n };\n\n const subtitle = [\n data.season,\n data.closesAt ? `Closes ${formatDate(data.closesAt)}` : null,\n data.spotsRemaining != null ? `${data.spotsRemaining} places left` : null,\n ]\n .filter(Boolean)\n .join(' · ');\n\n return (\n <PageLayout title={data.title} description={subtitle || undefined}>\n <div className=\"max-w-2xl space-y-6\">\n {data.description && (\n <p className=\"whitespace-pre-line text-sm text-text-secondary\">{data.description}</p>\n )}\n {!data.isOpen ? (\n <p\n role=\"status\"\n className=\"rounded-lg border border-status-warning-border bg-status-warning-bg-subtle p-4 text-sm text-status-warning-text\"\n >\n This form is not accepting registrations right now\n {data.opensAt && new Date(data.opensAt) > new Date()\n ? ` — it opens ${formatDate(data.opensAt)}`\n : ''}\n .\n </p>\n ) : (\n <form onSubmit={onSubmit} className=\"space-y-6\">\n <RegistrationEntryFields\n fields={data.fields}\n fees={data.fees}\n currency={data.currency}\n waiverText={data.waiverText}\n state={state}\n onChange={setState}\n />\n {error && (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error}\n </p>\n )}\n <button\n type=\"submit\"\n disabled={submit.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60\"\n >\n {submit.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Send className=\"h-4 w-4\" />\n )}{' '}\n Submit registration\n </button>\n </form>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RegisterPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/registrations/RegisterPage.tsx"],"sourcesContent":["/**\n * Family registration (share link)\n * @module atheletebridge/pages/registrations\n *\n * Opened from a club's share link (`/register/:slug`). Any signed-in user —\n * typically a parent who is not a member of the club's workspace — can fill\n * in a published form; the registration is filed with the club and tracked\n * under \"My registrations\" (BOFF-4135).\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router';\nimport { CheckCircle2, ClipboardX, Loader2, Send } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/LoadingState';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { PayOnlineButton } from '../../components/registration/OnlineCheckout';\nimport { RegistrationEntryFields } from '../../components/registration/RegistrationEntryFields';\nimport {\n EMPTY_REGISTRATION_ENTRY,\n type RegistrationEntryState,\n cleanAnswers,\n missingRequiredAnswer,\n} from '../../components/registration/entryRules';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport { usePublicRegistrationForm, useSubmitRegistration } from '../../hooks/useRegistrationApi';\nimport { formatDate } from '../../utils/formatters';\nimport { formatMoney } from '../../utils/money';\nimport { canPayOnline } from '../../utils/onlineCheckout';\nimport type { AthMyRegistration } from '../../types/registration';\n\nexport function RegisterPage() {\n const { slug } = useParams<{ slug: string }>();\n const { navigateTo } = useAthleteBridge();\n const form = usePublicRegistrationForm(slug);\n const submit = useSubmitRegistration();\n const [state, setState] = useState<RegistrationEntryState>(EMPTY_REGISTRATION_ENTRY);\n const [error, setError] = useState<string | null>(null);\n const [receipt, setReceipt] = useState<AthMyRegistration | null>(null);\n\n if (form.isLoading) {\n return (\n <PageLayout title=\"Registration\">\n <LoadingState label=\"Loading registration form…\" />\n </PageLayout>\n );\n }\n if (form.isError) {\n return (\n <PageLayout title=\"Registration\">\n <ErrorState onRetry={() => void form.refetch()} />\n </PageLayout>\n );\n }\n if (!form.data || !slug) {\n return (\n <PageLayout title=\"Registration\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<ClipboardX className=\"h-10 w-10\" />}\n title=\"This registration link is not available\"\n description=\"The club may not have published it yet, or the link is incorrect. Check the link with the club.\"\n />\n </PageLayout>\n );\n }\n const data = form.data;\n\n if (receipt) {\n return (\n <PageLayout title={data.title}>\n <div\n className=\"max-w-xl space-y-4 rounded-lg border border-border-seam bg-bg-surface p-6\"\n data-testid=\"registration-receipt\"\n >\n <p className=\"flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <CheckCircle2 className=\"h-6 w-6 text-status-success-text\" /> Registration received\n </p>\n <p className=\"text-sm text-text-secondary\">\n {receipt.participantName} is registered for {receipt.formTitle}.{' '}\n {canPayOnline(receipt)\n ? 'The club will review it. You can pay the fee online now, or later from My registrations.'\n : receipt.amountDue > 0\n ? 'The club will review it and confirm payment details with you.'\n : 'The club will review it.'}\n </p>\n <div className=\"flex flex-wrap items-center gap-2 text-sm\">\n <StatusBadge status={receipt.status} />\n <StatusBadge status={receipt.paymentStatus} />\n {receipt.amountDue > 0 && (\n <span className=\"tabular-nums text-text-primary\">\n {formatMoney(receipt.amountOutstanding, receipt.currency)} due\n </span>\n )}\n </div>\n {canPayOnline(receipt) && (\n <div>\n <PayOnlineButton registration={receipt} />\n </div>\n )}\n <button\n type=\"button\"\n onClick={() => navigateTo('/my-registrations')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n View my registrations\n </button>\n </div>\n </PageLayout>\n );\n }\n\n const onSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n const missing = missingRequiredAnswer(data.fields, state);\n if (missing) {\n setError(`Please answer \"${missing}\".`);\n return;\n }\n if (data.waiverText && !state.waiverAccepted) {\n setError('Please accept the waiver to register.');\n return;\n }\n try {\n const result = await submit.mutateAsync({\n slug,\n input: {\n participantName: state.participantName.trim(),\n contactName: state.contactName.trim() || undefined,\n contactEmail: state.contactEmail.trim() || undefined,\n contactPhone: state.contactPhone.trim() || undefined,\n answers: cleanAnswers(state.answers),\n selectedFeeIds: state.optionalFeeIds,\n waiverAccepted: state.waiverAccepted,\n participantIsRegistrant: state.participantIsRegistrant,\n },\n });\n setReceipt(result);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Could not submit the registration.');\n }\n };\n\n const subtitle = [\n data.season,\n data.closesAt ? `Closes ${formatDate(data.closesAt)}` : null,\n data.spotsRemaining != null ? `${data.spotsRemaining} places left` : null,\n ]\n .filter(Boolean)\n .join(' · ');\n\n return (\n <PageLayout title={data.title} description={subtitle || undefined}>\n <div className=\"max-w-2xl space-y-6\">\n {data.description && (\n <p className=\"whitespace-pre-line text-sm text-text-secondary\">{data.description}</p>\n )}\n {!data.isOpen ? (\n <p\n role=\"status\"\n className=\"rounded-lg border border-status-warning-border bg-status-warning-bg-subtle p-4 text-sm text-status-warning-text\"\n >\n This form is not accepting registrations right now\n {data.opensAt && new Date(data.opensAt) > new Date()\n ? ` — it opens ${formatDate(data.opensAt)}`\n : ''}\n .\n </p>\n ) : (\n <form onSubmit={onSubmit} className=\"space-y-6\">\n <RegistrationEntryFields\n fields={data.fields}\n fees={data.fees}\n currency={data.currency}\n waiverText={data.waiverText}\n state={state}\n onChange={setState}\n offerSelfRegistration\n />\n {error && (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error}\n </p>\n )}\n <button\n type=\"submit\"\n disabled={submit.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-60\"\n >\n {submit.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Send className=\"h-4 w-4\" />\n )}{' '}\n Submit registration\n </button>\n </form>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegistrationFormsListPage.d.ts","sourceRoot":"","sources":["../../../../src/atheletebridge/pages/registrations/RegistrationFormsListPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"RegistrationFormsListPage.d.ts","sourceRoot":"","sources":["../../../../src/atheletebridge/pages/registrations/RegistrationFormsListPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2BH,wBAAgB,yBAAyB,gCA2HxC"}
|
|
@@ -10,11 +10,12 @@ import { FilterSelect as l } from "../../components/FilterSelect.js";
|
|
|
10
10
|
import { useRegistrationForms as u } from "../../hooks/useRegistrationApi.js";
|
|
11
11
|
import { formatMoney as d } from "../../utils/money.js";
|
|
12
12
|
import { PaymentSummaryPanel as f } from "../../components/registration/PaymentSummaryPanel.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
13
|
+
import { OnlinePaymentsCard as p } from "../../components/registration/OnlinePaymentsCard.js";
|
|
14
|
+
import { useState as m } from "react";
|
|
15
|
+
import { ClipboardList as h, Plus as g } from "lucide-react";
|
|
16
|
+
import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
16
17
|
//#region src/atheletebridge/pages/registrations/RegistrationFormsListPage.tsx
|
|
17
|
-
var
|
|
18
|
+
var b = 20, x = [
|
|
18
19
|
{
|
|
19
20
|
value: "DRAFT",
|
|
20
21
|
label: "Draft"
|
|
@@ -28,63 +29,64 @@ var y = 20, b = [
|
|
|
28
29
|
label: "Closed"
|
|
29
30
|
}
|
|
30
31
|
];
|
|
31
|
-
function
|
|
32
|
-
let
|
|
33
|
-
status:
|
|
32
|
+
function S() {
|
|
33
|
+
let S = e(), { navigateTo: C } = t(), [w, T] = m(""), [E, D] = m(0), O = u({
|
|
34
|
+
status: w || void 0,
|
|
34
35
|
pagination: {
|
|
35
|
-
limit:
|
|
36
|
-
offset:
|
|
36
|
+
limit: b,
|
|
37
|
+
offset: E * b
|
|
37
38
|
}
|
|
38
|
-
}),
|
|
39
|
+
}), k = O.data?.items ?? [], A = /* @__PURE__ */ y("button", {
|
|
39
40
|
type: "button",
|
|
40
|
-
onClick: () =>
|
|
41
|
+
onClick: () => C("/registrations/new"),
|
|
41
42
|
className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90",
|
|
42
43
|
children: [
|
|
43
|
-
/* @__PURE__ */
|
|
44
|
+
/* @__PURE__ */ v(g, { className: "h-4 w-4" }),
|
|
44
45
|
" ",
|
|
45
|
-
|
|
46
|
+
S("atheletebridge.pages.registrations.new", "New registration form")
|
|
46
47
|
]
|
|
47
48
|
});
|
|
48
|
-
return /* @__PURE__ */
|
|
49
|
-
title:
|
|
50
|
-
description:
|
|
49
|
+
return /* @__PURE__ */ y(n, {
|
|
50
|
+
title: S("atheletebridge.pages.registrations.title", "Registrations"),
|
|
51
|
+
description: S("atheletebridge.pages.registrations.description", "Season, team and program registration forms, the families who signed up, and the fees you have collected."),
|
|
51
52
|
hero: !0,
|
|
52
53
|
gradientTitle: !0,
|
|
53
|
-
actions:
|
|
54
|
+
actions: A,
|
|
54
55
|
children: [
|
|
55
|
-
/* @__PURE__ */
|
|
56
|
-
/* @__PURE__ */
|
|
56
|
+
/* @__PURE__ */ v(f, {}),
|
|
57
|
+
/* @__PURE__ */ v(p, {}),
|
|
58
|
+
/* @__PURE__ */ v("div", {
|
|
57
59
|
className: "mb-4 flex flex-wrap items-center gap-3",
|
|
58
|
-
children: /* @__PURE__ */
|
|
59
|
-
ariaLabel:
|
|
60
|
-
allLabel:
|
|
61
|
-
value:
|
|
60
|
+
children: /* @__PURE__ */ v(l, {
|
|
61
|
+
ariaLabel: S("atheletebridge.pages.registrations.filterStatus", "Filter by status"),
|
|
62
|
+
allLabel: S("atheletebridge.pages.registrations.allStatuses", "All statuses"),
|
|
63
|
+
value: w,
|
|
62
64
|
onChange: (e) => {
|
|
63
|
-
|
|
65
|
+
T(e), D(0);
|
|
64
66
|
},
|
|
65
|
-
options:
|
|
67
|
+
options: x
|
|
66
68
|
})
|
|
67
69
|
}),
|
|
68
|
-
|
|
70
|
+
O.isLoading ? /* @__PURE__ */ v(s, { label: S("atheletebridge.pages.registrations.loading", "Loading registration forms…") }) : O.isError ? /* @__PURE__ */ v(o, { onRetry: () => void O.refetch() }) : k.length === 0 ? /* @__PURE__ */ v(a, {
|
|
69
71
|
illustration: "empty-data",
|
|
70
|
-
icon: /* @__PURE__ */
|
|
71
|
-
title:
|
|
72
|
-
description:
|
|
73
|
-
action:
|
|
74
|
-
}) : /* @__PURE__ */
|
|
72
|
+
icon: /* @__PURE__ */ v(h, { className: "h-10 w-10" }),
|
|
73
|
+
title: S("atheletebridge.pages.registrations.empty", "No registration forms yet"),
|
|
74
|
+
description: S("atheletebridge.pages.registrations.emptyHint", "Create a form with the questions and fees for your season, publish it, and share the link with families."),
|
|
75
|
+
action: A
|
|
76
|
+
}) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("ul", {
|
|
75
77
|
className: "divide-y divide-border-seam overflow-hidden rounded-lg border border-border-seam bg-bg-surface",
|
|
76
|
-
children:
|
|
78
|
+
children: k.map((e) => {
|
|
77
79
|
let t = e.fees.filter((e) => e.feeType === "REQUIRED").reduce((e, t) => e + t.amount, 0);
|
|
78
|
-
return /* @__PURE__ */
|
|
80
|
+
return /* @__PURE__ */ v("li", { children: /* @__PURE__ */ y("button", {
|
|
79
81
|
type: "button",
|
|
80
|
-
onClick: () =>
|
|
82
|
+
onClick: () => C(`/registrations/${e.id}`),
|
|
81
83
|
className: "flex w-full flex-wrap items-center justify-between gap-3 px-4 py-3 text-left transition-colors hover:bg-bg-sunken",
|
|
82
|
-
children: [/* @__PURE__ */
|
|
84
|
+
children: [/* @__PURE__ */ y("span", {
|
|
83
85
|
className: "min-w-0",
|
|
84
|
-
children: [/* @__PURE__ */
|
|
86
|
+
children: [/* @__PURE__ */ v("span", {
|
|
85
87
|
className: "block truncate font-medium text-text-primary",
|
|
86
88
|
children: e.title
|
|
87
|
-
}), /* @__PURE__ */
|
|
89
|
+
}), /* @__PURE__ */ y("span", {
|
|
88
90
|
className: "block text-xs text-text-secondary",
|
|
89
91
|
children: [[
|
|
90
92
|
e.season,
|
|
@@ -95,33 +97,33 @@ function x() {
|
|
|
95
97
|
minute: void 0
|
|
96
98
|
})}` : ""]
|
|
97
99
|
})]
|
|
98
|
-
}), /* @__PURE__ */
|
|
100
|
+
}), /* @__PURE__ */ y("span", {
|
|
99
101
|
className: "flex items-center gap-3",
|
|
100
102
|
children: [
|
|
101
|
-
/* @__PURE__ */
|
|
103
|
+
/* @__PURE__ */ v("span", {
|
|
102
104
|
className: "text-sm tabular-nums text-text-secondary",
|
|
103
105
|
children: t > 0 ? d(t, e.currency) : "Free"
|
|
104
106
|
}),
|
|
105
|
-
e.status === "PUBLISHED" && !e.isOpen ? /* @__PURE__ */
|
|
107
|
+
e.status === "PUBLISHED" && !e.isOpen ? /* @__PURE__ */ v(r, {
|
|
106
108
|
status: "Not open",
|
|
107
109
|
variant: "warning"
|
|
108
110
|
}) : null,
|
|
109
|
-
/* @__PURE__ */
|
|
111
|
+
/* @__PURE__ */ v(r, { status: e.status })
|
|
110
112
|
]
|
|
111
113
|
})]
|
|
112
114
|
}) }, e.id);
|
|
113
115
|
})
|
|
114
|
-
}), /* @__PURE__ */
|
|
116
|
+
}), /* @__PURE__ */ v(c, {
|
|
115
117
|
className: "mt-6",
|
|
116
|
-
page:
|
|
117
|
-
hasMore:
|
|
118
|
-
onPrev: () =>
|
|
119
|
-
onNext: () =>
|
|
118
|
+
page: E,
|
|
119
|
+
hasMore: O.data?.pageInfo.hasMore ?? !1,
|
|
120
|
+
onPrev: () => D((e) => Math.max(0, e - 1)),
|
|
121
|
+
onNext: () => D((e) => e + 1)
|
|
120
122
|
})] })
|
|
121
123
|
]
|
|
122
124
|
});
|
|
123
125
|
}
|
|
124
126
|
//#endregion
|
|
125
|
-
export {
|
|
127
|
+
export { S as RegistrationFormsListPage };
|
|
126
128
|
|
|
127
129
|
//# sourceMappingURL=RegistrationFormsListPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegistrationFormsListPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/registrations/RegistrationFormsListPage.tsx"],"sourcesContent":["/**\n * Registration forms list (club staff)\n * @module atheletebridge/pages/registrations\n *\n * Every registration form in the workspace with its lifecycle state, intake\n * and the workspace-wide fee reconciliation summary (BOFF-4135).\n */\n\nimport { useState } from 'react';\nimport { ClipboardList, Plus } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/LoadingState';\nimport { FilterSelect } from '../../components/FilterSelect';\nimport { CursorPager } from '../../components/CursorPager';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { PaymentSummaryPanel } from '../../components/registration/PaymentSummaryPanel';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport { useRegistrationForms } from '../../hooks/useRegistrationApi';\nimport { useAbTranslate } from '../../i18n';\nimport { formatDate } from '../../utils/formatters';\nimport { formatMoney } from '../../utils/money';\nimport type { AthRegistrationFormStatus } from '../../types/registration';\n\nconst PAGE_SIZE = 20;\n\nconst STATUS_OPTS: ReadonlyArray<{ value: AthRegistrationFormStatus; label: string }> = [\n { value: 'DRAFT', label: 'Draft' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'CLOSED', label: 'Closed' },\n];\n\nexport function RegistrationFormsListPage() {\n const tr = useAbTranslate();\n const { navigateTo } = useAthleteBridge();\n const [status, setStatus] = useState<AthRegistrationFormStatus | ''>('');\n const [page, setPage] = useState(0);\n\n const forms = useRegistrationForms({\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },\n });\n const items = forms.data?.items ?? [];\n\n const newButton = (\n <button\n type=\"button\"\n onClick={() => navigateTo('/registrations/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" />{' '}\n {tr('atheletebridge.pages.registrations.new', 'New registration form')}\n </button>\n );\n\n return (\n <PageLayout\n title={tr('atheletebridge.pages.registrations.title', 'Registrations')}\n description={tr(\n 'atheletebridge.pages.registrations.description',\n 'Season, team and program registration forms, the families who signed up, and the fees you have collected.'\n )}\n hero\n gradientTitle\n actions={newButton}\n >\n <PaymentSummaryPanel />\n\n <div className=\"mb-4 flex flex-wrap items-center gap-3\">\n <FilterSelect\n ariaLabel={tr('atheletebridge.pages.registrations.filterStatus', 'Filter by status')}\n allLabel={tr('atheletebridge.pages.registrations.allStatuses', 'All statuses')}\n value={status}\n onChange={(v) => {\n setStatus(v);\n setPage(0);\n }}\n options={STATUS_OPTS}\n />\n </div>\n\n {forms.isLoading ? (\n <LoadingState\n label={tr('atheletebridge.pages.registrations.loading', 'Loading registration forms…')}\n />\n ) : forms.isError ? (\n <ErrorState onRetry={() => void forms.refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ClipboardList className=\"h-10 w-10\" />}\n title={tr('atheletebridge.pages.registrations.empty', 'No registration forms yet')}\n description={tr(\n 'atheletebridge.pages.registrations.emptyHint',\n 'Create a form with the questions and fees for your season, publish it, and share the link with families.'\n )}\n action={newButton}\n />\n ) : (\n <>\n <ul className=\"divide-y divide-border-seam overflow-hidden rounded-lg border border-border-seam bg-bg-surface\">\n {items.map((form) => {\n const total = form.fees\n .filter((fee) => fee.feeType === 'REQUIRED')\n .reduce((sum, fee) => sum + fee.amount, 0);\n return (\n <li key={form.id}>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/registrations/${form.id}`)}\n className=\"flex w-full flex-wrap items-center justify-between gap-3 px-4 py-3 text-left transition-colors hover:bg-bg-sunken\"\n >\n <span className=\"min-w-0\">\n <span className=\"block truncate font-medium text-text-primary\">\n {form.title}\n </span>\n <span className=\"block text-xs text-text-secondary\">\n {[\n form.season,\n `${form.submissionCount} registered`,\n form.maxSubmissions ? `cap ${form.maxSubmissions}` : null,\n ]\n .filter(Boolean)\n .join(' · ')}\n {form.closesAt\n ? ` · closes ${formatDate(form.closesAt, { hour: undefined, minute: undefined })}`\n : ''}\n </span>\n </span>\n <span className=\"flex items-center gap-3\">\n <span className=\"text-sm tabular-nums text-text-secondary\">\n {total > 0 ? formatMoney(total, form.currency) : 'Free'}\n </span>\n {form.status === 'PUBLISHED' && !form.isOpen ? (\n <StatusBadge status=\"Not open\" variant=\"warning\" />\n ) : null}\n <StatusBadge status={form.status} />\n </span>\n </button>\n </li>\n );\n })}\n </ul>\n <CursorPager\n className=\"mt-6\"\n page={page}\n hasMore={forms.data?.pageInfo.hasMore ?? false}\n onPrev={() => setPage((p) => Math.max(0, p - 1))}\n onNext={() => setPage((p) => p + 1)}\n />\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"RegistrationFormsListPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/registrations/RegistrationFormsListPage.tsx"],"sourcesContent":["/**\n * Registration forms list (club staff)\n * @module atheletebridge/pages/registrations\n *\n * Every registration form in the workspace with its lifecycle state, intake\n * and the workspace-wide fee reconciliation summary (BOFF-4135).\n */\n\nimport { useState } from 'react';\nimport { ClipboardList, Plus } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/LoadingState';\nimport { FilterSelect } from '../../components/FilterSelect';\nimport { CursorPager } from '../../components/CursorPager';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { OnlinePaymentsCard } from '../../components/registration/OnlinePaymentsCard';\nimport { PaymentSummaryPanel } from '../../components/registration/PaymentSummaryPanel';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport { useRegistrationForms } from '../../hooks/useRegistrationApi';\nimport { useAbTranslate } from '../../i18n';\nimport { formatDate } from '../../utils/formatters';\nimport { formatMoney } from '../../utils/money';\nimport type { AthRegistrationFormStatus } from '../../types/registration';\n\nconst PAGE_SIZE = 20;\n\nconst STATUS_OPTS: ReadonlyArray<{ value: AthRegistrationFormStatus; label: string }> = [\n { value: 'DRAFT', label: 'Draft' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'CLOSED', label: 'Closed' },\n];\n\nexport function RegistrationFormsListPage() {\n const tr = useAbTranslate();\n const { navigateTo } = useAthleteBridge();\n const [status, setStatus] = useState<AthRegistrationFormStatus | ''>('');\n const [page, setPage] = useState(0);\n\n const forms = useRegistrationForms({\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset: page * PAGE_SIZE },\n });\n const items = forms.data?.items ?? [];\n\n const newButton = (\n <button\n type=\"button\"\n onClick={() => navigateTo('/registrations/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" />{' '}\n {tr('atheletebridge.pages.registrations.new', 'New registration form')}\n </button>\n );\n\n return (\n <PageLayout\n title={tr('atheletebridge.pages.registrations.title', 'Registrations')}\n description={tr(\n 'atheletebridge.pages.registrations.description',\n 'Season, team and program registration forms, the families who signed up, and the fees you have collected.'\n )}\n hero\n gradientTitle\n actions={newButton}\n >\n <PaymentSummaryPanel />\n <OnlinePaymentsCard />\n\n <div className=\"mb-4 flex flex-wrap items-center gap-3\">\n <FilterSelect\n ariaLabel={tr('atheletebridge.pages.registrations.filterStatus', 'Filter by status')}\n allLabel={tr('atheletebridge.pages.registrations.allStatuses', 'All statuses')}\n value={status}\n onChange={(v) => {\n setStatus(v);\n setPage(0);\n }}\n options={STATUS_OPTS}\n />\n </div>\n\n {forms.isLoading ? (\n <LoadingState\n label={tr('atheletebridge.pages.registrations.loading', 'Loading registration forms…')}\n />\n ) : forms.isError ? (\n <ErrorState onRetry={() => void forms.refetch()} />\n ) : items.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ClipboardList className=\"h-10 w-10\" />}\n title={tr('atheletebridge.pages.registrations.empty', 'No registration forms yet')}\n description={tr(\n 'atheletebridge.pages.registrations.emptyHint',\n 'Create a form with the questions and fees for your season, publish it, and share the link with families.'\n )}\n action={newButton}\n />\n ) : (\n <>\n <ul className=\"divide-y divide-border-seam overflow-hidden rounded-lg border border-border-seam bg-bg-surface\">\n {items.map((form) => {\n const total = form.fees\n .filter((fee) => fee.feeType === 'REQUIRED')\n .reduce((sum, fee) => sum + fee.amount, 0);\n return (\n <li key={form.id}>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/registrations/${form.id}`)}\n className=\"flex w-full flex-wrap items-center justify-between gap-3 px-4 py-3 text-left transition-colors hover:bg-bg-sunken\"\n >\n <span className=\"min-w-0\">\n <span className=\"block truncate font-medium text-text-primary\">\n {form.title}\n </span>\n <span className=\"block text-xs text-text-secondary\">\n {[\n form.season,\n `${form.submissionCount} registered`,\n form.maxSubmissions ? `cap ${form.maxSubmissions}` : null,\n ]\n .filter(Boolean)\n .join(' · ')}\n {form.closesAt\n ? ` · closes ${formatDate(form.closesAt, { hour: undefined, minute: undefined })}`\n : ''}\n </span>\n </span>\n <span className=\"flex items-center gap-3\">\n <span className=\"text-sm tabular-nums text-text-secondary\">\n {total > 0 ? formatMoney(total, form.currency) : 'Free'}\n </span>\n {form.status === 'PUBLISHED' && !form.isOpen ? (\n <StatusBadge status=\"Not open\" variant=\"warning\" />\n ) : null}\n <StatusBadge status={form.status} />\n </span>\n </button>\n </li>\n );\n })}\n </ul>\n <CursorPager\n className=\"mt-6\"\n page={page}\n hasMore={forms.data?.pageInfo.hasMore ?? false}\n onPrev={() => setPage((p) => Math.max(0, p - 1))}\n onNext={() => setPage((p) => p + 1)}\n />\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegistrationSubmissionPage.d.ts","sourceRoot":"","sources":["../../../../src/atheletebridge/pages/registrations/RegistrationSubmissionPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"RegistrationSubmissionPage.d.ts","sourceRoot":"","sources":["../../../../src/atheletebridge/pages/registrations/RegistrationSubmissionPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiZH,wBAAgB,0BAA0B,gCA0HzC"}
|