@burdenoff/microfe-atheletebridge 2026.1103.2 → 2026.1107.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/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 +2 -2
- package/dist/atheletebridge/hooks/useRegistrationApi.d.ts.map +1 -1
- package/dist/atheletebridge/hooks/useRegistrationApi.js +1 -1
- package/dist/atheletebridge/hooks/useRegistrationApi.js.map +1 -1
- package/dist/atheletebridge/pages/registrations/RegisterPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/registrations/RegisterPage.js +4 -2
- package/dist/atheletebridge/pages/registrations/RegisterPage.js.map +1 -1
- package/dist/atheletebridge/pages/registrations/RegistrationSubmissionPage.d.ts.map +1 -1
- package/dist/atheletebridge/pages/registrations/RegistrationSubmissionPage.js +132 -107
- package/dist/atheletebridge/pages/registrations/RegistrationSubmissionPage.js.map +1 -1
- package/dist/atheletebridge/types/registration.d.ts +11 -0
- package/dist/atheletebridge/types/registration.d.ts.map +1 -1
- 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/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegistrationSubmissionPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/registrations/RegistrationSubmissionPage.tsx"],"sourcesContent":["/**\n * Registration submission detail (club staff)\n * @module atheletebridge/pages/registrations\n *\n * One registration: the participant's answers, the accept / decline review\n * and the payment ledger — record a payment as it arrives, void one recorded\n * in error, or record a refund (BOFF-4135). The ledger panel only renders for\n * staff who can read payments.\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router';\nimport { ArrowLeft, Ban, Check, Loader2, Plus, RotateCcw, X } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { LoadingState, ErrorState } from '../../components/LoadingState';\nimport { FormField, fieldInputClass } from '../../components/form';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport {\n useRecordRegistrationPayment,\n useRefundRegistrationPayment,\n useRegistrationForm,\n useRegistrationPayments,\n useRegistrationSubmission,\n useReviewRegistration,\n useVoidRegistrationPayment,\n} from '../../hooks/useRegistrationApi';\nimport { formatDate } from '../../utils/formatters';\nimport { formatMoney, minorToInput, parseMoney } from '../../utils/money';\nimport { PAYMENT_METHODS, paymentMethodLabel } from '../../utils/registration';\nimport type {\n AthRegistrationPayment,\n AthRegistrationPaymentMethod,\n AthRegistrationSubmission,\n} from '../../types/registration';\n\nconst secondaryButton =\n 'inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50';\nconst primaryButton =\n 'inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50';\n\nconst errorMessage = (err: unknown, fallback: string) =>\n err instanceof Error ? err.message : fallback;\n\nfunction ReviewPanel({ submission }: { submission: AthRegistrationSubmission }) {\n const review = useReviewRegistration();\n const [notes, setNotes] = useState(submission.reviewNotes ?? '');\n const [error, setError] = useState<string | null>(null);\n if (submission.status === 'WITHDRAWN') {\n return <p className=\"text-sm text-text-secondary\">The family withdrew this registration.</p>;\n }\n const decide = async (decision: 'ACCEPT' | 'DECLINE') => {\n setError(null);\n try {\n await review.mutateAsync({ id: submission.id, decision, notes: notes.trim() || undefined });\n } catch (err) {\n setError(errorMessage(err, 'Could not save the decision.'));\n }\n };\n return (\n <div className=\"space-y-3\">\n <FormField label=\"Review notes\" htmlFor=\"reviewNotes\" hint=\"Visible to club staff only.\">\n <textarea\n id=\"reviewNotes\"\n rows={2}\n className={fieldInputClass}\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n />\n </FormField>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n className={primaryButton}\n disabled={review.isPending || submission.status === 'ACCEPTED'}\n onClick={() => void decide('ACCEPT')}\n >\n <Check className=\"h-4 w-4\" /> Accept\n </button>\n <button\n type=\"button\"\n className={secondaryButton}\n disabled={review.isPending || submission.status === 'DECLINED'}\n onClick={() => void decide('DECLINE')}\n >\n <X className=\"h-4 w-4\" /> Decline\n </button>\n </div>\n {submission.reviewedAt && (\n <p className=\"text-xs text-text-secondary\">\n Last reviewed {formatDate(submission.reviewedAt)}\n </p>\n )}\n {error && (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error}\n </p>\n )}\n </div>\n );\n}\n\nfunction RecordPaymentForm({ submission }: { submission: AthRegistrationSubmission }) {\n const record = useRecordRegistrationPayment();\n const [amountText, setAmountText] = useState(\n minorToInput(submission.amountOutstanding, submission.currency)\n );\n const [method, setMethod] = useState<AthRegistrationPaymentMethod>('CASH');\n const [reference, setReference] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const submit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n const amount = parseMoney(amountText, submission.currency);\n if (amount === null || amount <= 0) {\n setError('Enter the amount received.');\n return;\n }\n try {\n await record.mutateAsync({\n submissionId: submission.id,\n amount,\n method,\n reference: reference.trim() || undefined,\n });\n setReference('');\n } catch (err) {\n setError(errorMessage(err, 'Could not record the payment.'));\n }\n };\n\n return (\n <form\n onSubmit={submit}\n className=\"grid grid-cols-1 gap-3 sm:grid-cols-[9rem_12rem_1fr_auto]\"\n aria-label=\"Record a payment\"\n >\n <FormField label={`Amount (${submission.currency})`} htmlFor=\"paymentAmount\">\n <input\n id=\"paymentAmount\"\n inputMode=\"decimal\"\n className={fieldInputClass}\n value={amountText}\n onChange={(e) => setAmountText(e.target.value)}\n />\n </FormField>\n <FormField label=\"Method\" htmlFor=\"paymentMethod\">\n <select\n id=\"paymentMethod\"\n className={fieldInputClass}\n value={method}\n onChange={(e) => setMethod(e.target.value as AthRegistrationPaymentMethod)}\n >\n {PAYMENT_METHODS.map((value) => (\n <option key={value} value={value}>\n {paymentMethodLabel(value)}\n </option>\n ))}\n </select>\n </FormField>\n <FormField label=\"Reference\" htmlFor=\"paymentReference\">\n <input\n id=\"paymentReference\"\n className={fieldInputClass}\n value={reference}\n onChange={(e) => setReference(e.target.value)}\n placeholder=\"Receipt / bank / cheque no.\"\n />\n </FormField>\n <div className=\"flex items-end\">\n <button type=\"submit\" className={primaryButton} disabled={record.isPending}>\n {record.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4\" />\n )}{' '}\n Record\n </button>\n </div>\n {error && (\n <p role=\"alert\" className=\"text-sm text-status-error-text sm:col-span-4\">\n {error}\n </p>\n )}\n </form>\n );\n}\n\nfunction PaymentRow({ payment }: { payment: AthRegistrationPayment }) {\n const voidPayment = useVoidRegistrationPayment();\n const refund = useRefundRegistrationPayment();\n const [error, setError] = useState<string | null>(null);\n const remaining = payment.amount - payment.refundedAmount;\n\n const onVoid = async () => {\n const reason = window.prompt('Why is this payment being voided?');\n if (!reason?.trim()) return;\n setError(null);\n try {\n await voidPayment.mutateAsync({ id: payment.id, reason: reason.trim() });\n } catch (err) {\n setError(errorMessage(err, 'Could not void the payment.'));\n }\n };\n\n const onRefund = async () => {\n const amountText = window.prompt(\n `Refund how much? (up to ${formatMoney(remaining, payment.currency)})`,\n minorToInput(remaining, payment.currency)\n );\n if (amountText === null) return;\n const amount = parseMoney(amountText, payment.currency);\n if (amount === null || amount <= 0) {\n setError('Enter a valid refund amount.');\n return;\n }\n if (amount > remaining) {\n setError(\n `You can refund at most ${formatMoney(remaining, payment.currency)} of this payment.`\n );\n return;\n }\n const reason = window.prompt('Reason for the refund?');\n if (!reason?.trim()) return;\n setError(null);\n try {\n await refund.mutateAsync({ id: payment.id, amount, reason: reason.trim() });\n } catch (err) {\n setError(errorMessage(err, 'Could not record the refund.'));\n }\n };\n\n const busy = voidPayment.isPending || refund.isPending;\n return (\n <li className=\"space-y-1 px-4 py-3\" data-testid=\"registration-payment\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <span className=\"min-w-0\">\n <span className=\"block text-sm font-medium tabular-nums text-text-primary\">\n {formatMoney(payment.amount, payment.currency)}\n {payment.refundedAmount > 0 && (\n <span className=\"ml-2 text-xs text-text-secondary\">\n refunded {formatMoney(payment.refundedAmount, payment.currency)}\n </span>\n )}\n </span>\n <span className=\"block text-xs text-text-secondary\">\n {paymentMethodLabel(payment.method)} · {formatDate(payment.receivedAt)}\n {payment.reference ? ` · ${payment.reference}` : ''}\n </span>\n {payment.voidReason && (\n <span className=\"block text-xs text-text-secondary\">Voided: {payment.voidReason}</span>\n )}\n {payment.refundReason && (\n <span className=\"block text-xs text-text-secondary\">\n Refund: {payment.refundReason}\n </span>\n )}\n </span>\n <span className=\"flex items-center gap-2\">\n <StatusBadge status={payment.status} />\n {payment.status !== 'VOIDED' && remaining > 0 && (\n <button\n type=\"button\"\n className={secondaryButton}\n disabled={busy}\n onClick={() => void onRefund()}\n >\n <RotateCcw className=\"h-4 w-4\" /> Refund\n </button>\n )}\n {payment.status === 'RECORDED' && payment.refundedAmount === 0 && (\n <button\n type=\"button\"\n className={secondaryButton}\n disabled={busy}\n onClick={() => void onVoid()}\n >\n <Ban className=\"h-4 w-4\" /> Void\n </button>\n )}\n </span>\n </div>\n {error && (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error}\n </p>\n )}\n </li>\n );\n}\n\nfunction PaymentsPanel({ submission }: { submission: AthRegistrationSubmission }) {\n const payments = useRegistrationPayments(submission.id);\n // FORBIDDEN (no payment-ledger access) or any failure: hide the ledger.\n if (payments.isError) return null;\n return (\n <section\n className=\"space-y-4 rounded-lg border border-border-seam bg-bg-surface p-4\"\n aria-label=\"Payments\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <h2 className=\"text-base font-semibold text-text-primary\">Payments</h2>\n <span className=\"text-sm text-text-secondary\">\n Paid{' '}\n <strong className=\"tabular-nums text-text-primary\">\n {formatMoney(submission.amountPaid, submission.currency)}\n </strong>{' '}\n of {formatMoney(submission.amountDue, submission.currency)} ·{' '}\n <span data-testid=\"submission-outstanding\">\n {formatMoney(submission.amountOutstanding, submission.currency)} outstanding\n </span>\n </span>\n </div>\n {submission.amountOutstanding > 0 && submission.status !== 'WITHDRAWN' && (\n <RecordPaymentForm\n key={`${submission.id}-${submission.amountOutstanding}`}\n submission={submission}\n />\n )}\n {payments.isLoading ? (\n <LoadingState label=\"Loading payments…\" />\n ) : (payments.data ?? []).length === 0 ? (\n <p className=\"text-sm text-text-secondary\">No payments recorded yet.</p>\n ) : (\n <ul className=\"divide-y divide-border-seam rounded-lg border border-border-seam\">\n {(payments.data ?? []).map((payment) => (\n <PaymentRow key={payment.id} payment={payment} />\n ))}\n </ul>\n )}\n </section>\n );\n}\n\nexport function RegistrationSubmissionPage() {\n const { id: formId, submissionId } = useParams<{ id: string; submissionId: string }>();\n const { navigateTo } = useAthleteBridge();\n const submission = useRegistrationSubmission(submissionId);\n const form = useRegistrationForm(formId);\n\n if (submission.isLoading) {\n return (\n <PageLayout title=\"Registration\">\n <LoadingState label=\"Loading registration…\" />\n </PageLayout>\n );\n }\n if (submission.isError || !submission.data) {\n return (\n <PageLayout title=\"Registration\">\n <ErrorState\n title=\"Registration not found\"\n message=\"This registration does not exist or you do not have access to it.\"\n onRetry={() => navigateTo(formId ? `/registrations/${formId}` : '/registrations')}\n />\n </PageLayout>\n );\n }\n\n const data = submission.data;\n const fields = form.data?.fields ?? [];\n const fees = form.data?.fees ?? [];\n const labelFor = (key: string) => fields.find((field) => field.key === key)?.label ?? key;\n const answerKeys = [\n ...fields.map((field) => field.key).filter((key) => key in data.answers),\n ...Object.keys(data.answers).filter((key) => !fields.some((field) => field.key === key)),\n ];\n\n return (\n <PageLayout\n title={\n <span className=\"flex flex-wrap items-center gap-3\">\n {data.participantName} <StatusBadge status={data.status} />{' '}\n <StatusBadge status={data.paymentStatus} />\n </span>\n }\n description={`${data.formTitle} · submitted ${formatDate(data.submittedAt)} · ${\n data.source === 'STAFF_ENTRY' ? 'entered by staff' : 'via share link'\n }`}\n actions={\n <button\n type=\"button\"\n className={secondaryButton}\n onClick={() => navigateTo(`/registrations/${data.formId}`)}\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back to form\n </button>\n }\n >\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-[1fr_22rem]\">\n <div className=\"space-y-6\">\n <section\n className=\"rounded-lg border border-border-seam bg-bg-surface p-4\"\n aria-label=\"Answers\"\n >\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">Details</h2>\n <dl className=\"grid grid-cols-1 gap-x-6 gap-y-3 text-sm sm:grid-cols-2\">\n {[\n ['Contact', data.contactName],\n ['Email', data.contactEmail],\n ['Phone', data.contactPhone],\n [\n 'Waiver accepted',\n data.waiverAcceptedAt ? formatDate(data.waiverAcceptedAt) : null,\n ],\n ]\n .filter(([, value]) => value)\n .map(([label, value]) => (\n <div key={label}>\n <dt className=\"text-xs text-text-secondary\">{label}</dt>\n <dd className=\"text-text-primary\">{value}</dd>\n </div>\n ))}\n {answerKeys.map((key) => {\n const value = data.answers[key];\n return (\n <div key={key}>\n <dt className=\"text-xs text-text-secondary\">{labelFor(key)}</dt>\n <dd className=\"text-text-primary\">\n {typeof value === 'boolean' ? (value ? 'Yes' : 'No') : String(value)}\n </dd>\n </div>\n );\n })}\n </dl>\n {fees.length > 0 && (\n <div className=\"mt-4 border-t border-border-seam pt-3\">\n <p className=\"mb-2 text-xs text-text-secondary\">Fees</p>\n <ul className=\"space-y-1 text-sm\">\n {fees\n .filter((fee) => data.selectedFeeIds.includes(fee.id))\n .map((fee) => (\n <li key={fee.id} className=\"flex justify-between text-text-primary\">\n <span>{fee.name}</span>\n <span className=\"tabular-nums\">\n {formatMoney(fee.amount, data.currency)}\n </span>\n </li>\n ))}\n </ul>\n </div>\n )}\n </section>\n <PaymentsPanel submission={data} />\n </div>\n <section\n className=\"h-fit rounded-lg border border-border-seam bg-bg-surface p-4\"\n aria-label=\"Review\"\n >\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">Review</h2>\n <ReviewPanel key={data.id} submission={data} />\n {data.status === 'ACCEPTED' && form.data?.teamId && (\n <button\n type=\"button\"\n className={`${secondaryButton} mt-4 w-full justify-center`}\n onClick={() => navigateTo(`/teams/${form.data?.teamId}`)}\n >\n Open the team roster to add this athlete\n </button>\n )}\n </section>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RegistrationSubmissionPage.js","names":[],"sources":["../../../../src/atheletebridge/pages/registrations/RegistrationSubmissionPage.tsx"],"sourcesContent":["/**\n * Registration submission detail (club staff)\n * @module atheletebridge/pages/registrations\n *\n * One registration: the participant's answers, the accept / decline review\n * and the payment ledger — record a payment as it arrives, void one recorded\n * in error, or record a refund (BOFF-4135). The ledger panel only renders for\n * staff who can read payments.\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router';\nimport {\n AlertTriangle,\n ArrowLeft,\n Ban,\n Check,\n Loader2,\n Plus,\n RotateCcw,\n Users,\n X,\n} from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { LoadingState, ErrorState } from '../../components/LoadingState';\nimport { FormField, fieldInputClass } from '../../components/form';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { useAthleteBridge } from '../../context/AthleteBridgeContext';\nimport {\n useRecordRegistrationPayment,\n useRefundRegistrationPayment,\n useRegistrationForm,\n useRegistrationPayments,\n useRegistrationSubmission,\n useReviewRegistration,\n useVoidRegistrationPayment,\n} from '../../hooks/useRegistrationApi';\nimport { formatDate } from '../../utils/formatters';\nimport { formatMoney, minorToInput, parseMoney } from '../../utils/money';\nimport { PAYMENT_METHODS, paymentMethodLabel, rosterNotice } from '../../utils/registration';\nimport type {\n AthRegistrationPayment,\n AthRegistrationPaymentMethod,\n AthRegistrationSubmission,\n} from '../../types/registration';\n\nconst secondaryButton =\n 'inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50';\nconst primaryButton =\n 'inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50';\n\nconst errorMessage = (err: unknown, fallback: string) =>\n err instanceof Error ? err.message : fallback;\n\nfunction ReviewPanel({ submission }: { submission: AthRegistrationSubmission }) {\n const review = useReviewRegistration();\n const [notes, setNotes] = useState(submission.reviewNotes ?? '');\n const [error, setError] = useState<string | null>(null);\n if (submission.status === 'WITHDRAWN') {\n return <p className=\"text-sm text-text-secondary\">The family withdrew this registration.</p>;\n }\n const decide = async (decision: 'ACCEPT' | 'DECLINE') => {\n setError(null);\n try {\n await review.mutateAsync({ id: submission.id, decision, notes: notes.trim() || undefined });\n } catch (err) {\n setError(errorMessage(err, 'Could not save the decision.'));\n }\n };\n return (\n <div className=\"space-y-3\">\n <FormField label=\"Review notes\" htmlFor=\"reviewNotes\" hint=\"Visible to club staff only.\">\n <textarea\n id=\"reviewNotes\"\n rows={2}\n className={fieldInputClass}\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n />\n </FormField>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n className={primaryButton}\n disabled={review.isPending || submission.status === 'ACCEPTED'}\n onClick={() => void decide('ACCEPT')}\n >\n <Check className=\"h-4 w-4\" /> Accept\n </button>\n <button\n type=\"button\"\n className={secondaryButton}\n disabled={review.isPending || submission.status === 'DECLINED'}\n onClick={() => void decide('DECLINE')}\n >\n <X className=\"h-4 w-4\" /> Decline\n </button>\n </div>\n {submission.reviewedAt && (\n <p className=\"text-xs text-text-secondary\">\n Last reviewed {formatDate(submission.reviewedAt)}\n </p>\n )}\n {error && (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error}\n </p>\n )}\n </div>\n );\n}\n\nfunction RosterConfirmation({\n submission,\n formTeamId,\n}: {\n submission: AthRegistrationSubmission;\n formTeamId: string | null;\n}) {\n const { navigateTo } = useAthleteBridge();\n const notice = rosterNotice(submission);\n if (!notice) {\n // Accepted before roster placement existed (BOFF-10806): keep the manual path.\n if (submission.status !== 'ACCEPTED' || submission.rosterOutcome || !formTeamId) return null;\n return (\n <button\n type=\"button\"\n className={`${secondaryButton} mt-4 w-full justify-center`}\n onClick={() => navigateTo(`/teams/${formTeamId}`)}\n >\n Open the team roster to add this athlete\n </button>\n );\n }\n const success = notice.tone === 'success';\n return (\n <div\n role=\"status\"\n data-testid=\"registration-roster-notice\"\n className={`mt-4 space-y-2 rounded-lg border p-3 text-sm ${\n success\n ? 'border-status-success-border bg-status-success-bg-subtle text-status-success-text'\n : 'border-status-warning-border bg-status-warning-bg-subtle text-status-warning-text'\n }`}\n >\n <p className=\"flex items-start gap-2\">\n {success ? (\n <Check className=\"mt-0.5 h-4 w-4 shrink-0\" aria-hidden=\"true\" />\n ) : (\n <AlertTriangle className=\"mt-0.5 h-4 w-4 shrink-0\" aria-hidden=\"true\" />\n )}\n <span>{notice.message}</span>\n </p>\n {notice.teamId && (\n <button\n type=\"button\"\n className={`${secondaryButton} w-full justify-center`}\n onClick={() => navigateTo(`/teams/${notice.teamId}`)}\n >\n <Users className=\"h-4 w-4\" /> Open the team roster\n </button>\n )}\n </div>\n );\n}\n\nfunction RecordPaymentForm({ submission }: { submission: AthRegistrationSubmission }) {\n const record = useRecordRegistrationPayment();\n const [amountText, setAmountText] = useState(\n minorToInput(submission.amountOutstanding, submission.currency)\n );\n const [method, setMethod] = useState<AthRegistrationPaymentMethod>('CASH');\n const [reference, setReference] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const submit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n const amount = parseMoney(amountText, submission.currency);\n if (amount === null || amount <= 0) {\n setError('Enter the amount received.');\n return;\n }\n try {\n await record.mutateAsync({\n submissionId: submission.id,\n amount,\n method,\n reference: reference.trim() || undefined,\n });\n setReference('');\n } catch (err) {\n setError(errorMessage(err, 'Could not record the payment.'));\n }\n };\n\n return (\n <form\n onSubmit={submit}\n className=\"grid grid-cols-1 gap-3 sm:grid-cols-[9rem_12rem_1fr_auto]\"\n aria-label=\"Record a payment\"\n >\n <FormField label={`Amount (${submission.currency})`} htmlFor=\"paymentAmount\">\n <input\n id=\"paymentAmount\"\n inputMode=\"decimal\"\n className={fieldInputClass}\n value={amountText}\n onChange={(e) => setAmountText(e.target.value)}\n />\n </FormField>\n <FormField label=\"Method\" htmlFor=\"paymentMethod\">\n <select\n id=\"paymentMethod\"\n className={fieldInputClass}\n value={method}\n onChange={(e) => setMethod(e.target.value as AthRegistrationPaymentMethod)}\n >\n {PAYMENT_METHODS.map((value) => (\n <option key={value} value={value}>\n {paymentMethodLabel(value)}\n </option>\n ))}\n </select>\n </FormField>\n <FormField label=\"Reference\" htmlFor=\"paymentReference\">\n <input\n id=\"paymentReference\"\n className={fieldInputClass}\n value={reference}\n onChange={(e) => setReference(e.target.value)}\n placeholder=\"Receipt / bank / cheque no.\"\n />\n </FormField>\n <div className=\"flex items-end\">\n <button type=\"submit\" className={primaryButton} disabled={record.isPending}>\n {record.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4\" />\n )}{' '}\n Record\n </button>\n </div>\n {error && (\n <p role=\"alert\" className=\"text-sm text-status-error-text sm:col-span-4\">\n {error}\n </p>\n )}\n </form>\n );\n}\n\nfunction PaymentRow({ payment }: { payment: AthRegistrationPayment }) {\n const voidPayment = useVoidRegistrationPayment();\n const refund = useRefundRegistrationPayment();\n const [error, setError] = useState<string | null>(null);\n const remaining = payment.amount - payment.refundedAmount;\n\n const onVoid = async () => {\n const reason = window.prompt('Why is this payment being voided?');\n if (!reason?.trim()) return;\n setError(null);\n try {\n await voidPayment.mutateAsync({ id: payment.id, reason: reason.trim() });\n } catch (err) {\n setError(errorMessage(err, 'Could not void the payment.'));\n }\n };\n\n const onRefund = async () => {\n const amountText = window.prompt(\n `Refund how much? (up to ${formatMoney(remaining, payment.currency)})`,\n minorToInput(remaining, payment.currency)\n );\n if (amountText === null) return;\n const amount = parseMoney(amountText, payment.currency);\n if (amount === null || amount <= 0) {\n setError('Enter a valid refund amount.');\n return;\n }\n if (amount > remaining) {\n setError(\n `You can refund at most ${formatMoney(remaining, payment.currency)} of this payment.`\n );\n return;\n }\n const reason = window.prompt('Reason for the refund?');\n if (!reason?.trim()) return;\n setError(null);\n try {\n await refund.mutateAsync({ id: payment.id, amount, reason: reason.trim() });\n } catch (err) {\n setError(errorMessage(err, 'Could not record the refund.'));\n }\n };\n\n const busy = voidPayment.isPending || refund.isPending;\n return (\n <li className=\"space-y-1 px-4 py-3\" data-testid=\"registration-payment\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <span className=\"min-w-0\">\n <span className=\"block text-sm font-medium tabular-nums text-text-primary\">\n {formatMoney(payment.amount, payment.currency)}\n {payment.refundedAmount > 0 && (\n <span className=\"ml-2 text-xs text-text-secondary\">\n refunded {formatMoney(payment.refundedAmount, payment.currency)}\n </span>\n )}\n </span>\n <span className=\"block text-xs text-text-secondary\">\n {paymentMethodLabel(payment.method)} · {formatDate(payment.receivedAt)}\n {payment.reference ? ` · ${payment.reference}` : ''}\n </span>\n {payment.voidReason && (\n <span className=\"block text-xs text-text-secondary\">Voided: {payment.voidReason}</span>\n )}\n {payment.refundReason && (\n <span className=\"block text-xs text-text-secondary\">\n Refund: {payment.refundReason}\n </span>\n )}\n </span>\n <span className=\"flex items-center gap-2\">\n <StatusBadge status={payment.status} />\n {payment.status !== 'VOIDED' && remaining > 0 && (\n <button\n type=\"button\"\n className={secondaryButton}\n disabled={busy}\n onClick={() => void onRefund()}\n >\n <RotateCcw className=\"h-4 w-4\" /> Refund\n </button>\n )}\n {payment.status === 'RECORDED' && payment.refundedAmount === 0 && (\n <button\n type=\"button\"\n className={secondaryButton}\n disabled={busy}\n onClick={() => void onVoid()}\n >\n <Ban className=\"h-4 w-4\" /> Void\n </button>\n )}\n </span>\n </div>\n {error && (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error}\n </p>\n )}\n </li>\n );\n}\n\nfunction PaymentsPanel({ submission }: { submission: AthRegistrationSubmission }) {\n const payments = useRegistrationPayments(submission.id);\n // FORBIDDEN (no payment-ledger access) or any failure: hide the ledger.\n if (payments.isError) return null;\n return (\n <section\n className=\"space-y-4 rounded-lg border border-border-seam bg-bg-surface p-4\"\n aria-label=\"Payments\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <h2 className=\"text-base font-semibold text-text-primary\">Payments</h2>\n <span className=\"text-sm text-text-secondary\">\n Paid{' '}\n <strong className=\"tabular-nums text-text-primary\">\n {formatMoney(submission.amountPaid, submission.currency)}\n </strong>{' '}\n of {formatMoney(submission.amountDue, submission.currency)} ·{' '}\n <span data-testid=\"submission-outstanding\">\n {formatMoney(submission.amountOutstanding, submission.currency)} outstanding\n </span>\n </span>\n </div>\n {submission.amountOutstanding > 0 && submission.status !== 'WITHDRAWN' && (\n <RecordPaymentForm\n key={`${submission.id}-${submission.amountOutstanding}`}\n submission={submission}\n />\n )}\n {payments.isLoading ? (\n <LoadingState label=\"Loading payments…\" />\n ) : (payments.data ?? []).length === 0 ? (\n <p className=\"text-sm text-text-secondary\">No payments recorded yet.</p>\n ) : (\n <ul className=\"divide-y divide-border-seam rounded-lg border border-border-seam\">\n {(payments.data ?? []).map((payment) => (\n <PaymentRow key={payment.id} payment={payment} />\n ))}\n </ul>\n )}\n </section>\n );\n}\n\nexport function RegistrationSubmissionPage() {\n const { id: formId, submissionId } = useParams<{ id: string; submissionId: string }>();\n const { navigateTo } = useAthleteBridge();\n const submission = useRegistrationSubmission(submissionId);\n const form = useRegistrationForm(formId);\n\n if (submission.isLoading) {\n return (\n <PageLayout title=\"Registration\">\n <LoadingState label=\"Loading registration…\" />\n </PageLayout>\n );\n }\n if (submission.isError || !submission.data) {\n return (\n <PageLayout title=\"Registration\">\n <ErrorState\n title=\"Registration not found\"\n message=\"This registration does not exist or you do not have access to it.\"\n onRetry={() => navigateTo(formId ? `/registrations/${formId}` : '/registrations')}\n />\n </PageLayout>\n );\n }\n\n const data = submission.data;\n const fields = form.data?.fields ?? [];\n const fees = form.data?.fees ?? [];\n const labelFor = (key: string) => fields.find((field) => field.key === key)?.label ?? key;\n const answerKeys = [\n ...fields.map((field) => field.key).filter((key) => key in data.answers),\n ...Object.keys(data.answers).filter((key) => !fields.some((field) => field.key === key)),\n ];\n\n return (\n <PageLayout\n title={\n <span className=\"flex flex-wrap items-center gap-3\">\n {data.participantName} <StatusBadge status={data.status} />{' '}\n <StatusBadge status={data.paymentStatus} />\n </span>\n }\n description={`${data.formTitle} · submitted ${formatDate(data.submittedAt)} · ${\n data.source === 'STAFF_ENTRY' ? 'entered by staff' : 'via share link'\n }`}\n actions={\n <button\n type=\"button\"\n className={secondaryButton}\n onClick={() => navigateTo(`/registrations/${data.formId}`)}\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back to form\n </button>\n }\n >\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-[1fr_22rem]\">\n <div className=\"space-y-6\">\n <section\n className=\"rounded-lg border border-border-seam bg-bg-surface p-4\"\n aria-label=\"Answers\"\n >\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">Details</h2>\n <dl className=\"grid grid-cols-1 gap-x-6 gap-y-3 text-sm sm:grid-cols-2\">\n {[\n ['Contact', data.contactName],\n ['Email', data.contactEmail],\n ['Phone', data.contactPhone],\n [\n 'Waiver accepted',\n data.waiverAcceptedAt ? formatDate(data.waiverAcceptedAt) : null,\n ],\n ]\n .filter(([, value]) => value)\n .map(([label, value]) => (\n <div key={label}>\n <dt className=\"text-xs text-text-secondary\">{label}</dt>\n <dd className=\"text-text-primary\">{value}</dd>\n </div>\n ))}\n {answerKeys.map((key) => {\n const value = data.answers[key];\n return (\n <div key={key}>\n <dt className=\"text-xs text-text-secondary\">{labelFor(key)}</dt>\n <dd className=\"text-text-primary\">\n {typeof value === 'boolean' ? (value ? 'Yes' : 'No') : String(value)}\n </dd>\n </div>\n );\n })}\n </dl>\n {fees.length > 0 && (\n <div className=\"mt-4 border-t border-border-seam pt-3\">\n <p className=\"mb-2 text-xs text-text-secondary\">Fees</p>\n <ul className=\"space-y-1 text-sm\">\n {fees\n .filter((fee) => data.selectedFeeIds.includes(fee.id))\n .map((fee) => (\n <li key={fee.id} className=\"flex justify-between text-text-primary\">\n <span>{fee.name}</span>\n <span className=\"tabular-nums\">\n {formatMoney(fee.amount, data.currency)}\n </span>\n </li>\n ))}\n </ul>\n </div>\n )}\n </section>\n <PaymentsPanel submission={data} />\n </div>\n <section\n className=\"h-fit rounded-lg border border-border-seam bg-bg-surface p-4\"\n aria-label=\"Review\"\n >\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">Review</h2>\n <ReviewPanel key={data.id} submission={data} />\n <RosterConfirmation submission={data} formTeamId={form.data?.teamId ?? null} />\n </section>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -9,6 +9,8 @@ export type AthRegistrationPaymentStatus = 'NOT_REQUIRED' | 'UNPAID' | 'PARTIALL
|
|
|
9
9
|
export type AthRegistrationPaymentMethod = 'CASH' | 'BANK_TRANSFER' | 'CARD_TERMINAL' | 'CHEQUE' | 'ONLINE' | 'OTHER';
|
|
10
10
|
export type AthRegistrationPaymentRecordStatus = 'RECORDED' | 'REFUNDED' | 'VOIDED';
|
|
11
11
|
export type AthRegistrationDecision = 'ACCEPT' | 'DECLINE';
|
|
12
|
+
/** What accepting a team-linked registration did to the roster (BOFF-10806). */
|
|
13
|
+
export type AthRegistrationRosterOutcome = 'ADDED' | 'ALREADY_ON_ROSTER' | 'NO_LINKED_USER' | 'TEAM_UNAVAILABLE';
|
|
12
14
|
export type AthRegistrationAnswerValue = string | number | boolean;
|
|
13
15
|
export interface AthRegistrationField {
|
|
14
16
|
id: string;
|
|
@@ -65,6 +67,7 @@ export interface AthRegistrationSubmission {
|
|
|
65
67
|
submittedByUserId: string | null;
|
|
66
68
|
participantName: string;
|
|
67
69
|
participantUserId: string | null;
|
|
70
|
+
participantIsRegistrant: boolean;
|
|
68
71
|
contactName: string | null;
|
|
69
72
|
contactEmail: string | null;
|
|
70
73
|
contactPhone: string | null;
|
|
@@ -79,6 +82,10 @@ export interface AthRegistrationSubmission {
|
|
|
79
82
|
waiverAcceptedAt: string | null;
|
|
80
83
|
reviewNotes: string | null;
|
|
81
84
|
reviewedAt: string | null;
|
|
85
|
+
rosterOutcome: AthRegistrationRosterOutcome | null;
|
|
86
|
+
rosterMemberId: string | null;
|
|
87
|
+
rosterTeamId: string | null;
|
|
88
|
+
rosterTeamName: string | null;
|
|
82
89
|
submittedAt: string;
|
|
83
90
|
createdAt: string;
|
|
84
91
|
updatedAt: string;
|
|
@@ -205,6 +212,10 @@ export interface AthRegistrationEntryInput {
|
|
|
205
212
|
selectedFeeIds?: string[];
|
|
206
213
|
waiverAccepted?: boolean;
|
|
207
214
|
}
|
|
215
|
+
export interface AthSubmitRegistrationInput extends AthRegistrationEntryInput {
|
|
216
|
+
/** The signed-in registrant is the athlete (so acceptance can roster them). */
|
|
217
|
+
participantIsRegistrant?: boolean;
|
|
218
|
+
}
|
|
208
219
|
export interface AthRecordRegistrationInput extends AthRegistrationEntryInput {
|
|
209
220
|
formId: string;
|
|
210
221
|
participantUserId?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../../src/atheletebridge/types/registration.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,MAAM,MAAM,yBAAyB,GAAG,OAAO,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AACtF,MAAM,MAAM,wBAAwB,GAClC,YAAY,GAAG,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;AAC7F,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG,UAAU,CAAC;AAC7D,MAAM,MAAM,qBAAqB,GAAG,cAAc,GAAG,aAAa,CAAC;AACnE,MAAM,MAAM,+BAA+B,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,GAAG,WAAW,CAAC;AAClG,MAAM,MAAM,4BAA4B,GACtC,cAAc,GAAG,QAAQ,GAAG,gBAAgB,GAAG,MAAM,GAAG,UAAU,CAAC;AACrE,MAAM,MAAM,4BAA4B,GACtC,MAAM,GAAG,eAAe,GAAG,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC7E,MAAM,MAAM,kCAAkC,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AACpF,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../../src/atheletebridge/types/registration.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,4 +14,23 @@ export declare function keyFromLabel(label: string, taken: ReadonlySet<string>):
|
|
|
14
14
|
export declare function registrationShareUrl(origin: string, basePath: string, slug: string): string;
|
|
15
15
|
export declare function paymentMethodLabel(method: string): string;
|
|
16
16
|
export declare const PAYMENT_METHODS: string[];
|
|
17
|
+
/** Roster confirmation shown on an accepted, team-linked registration. */
|
|
18
|
+
export interface RosterNotice {
|
|
19
|
+
tone: 'success' | 'warning';
|
|
20
|
+
message: string;
|
|
21
|
+
/** Team whose roster the "Open roster" action should go to, when it exists. */
|
|
22
|
+
teamId: string | null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Explain what accepting this registration did to the team roster
|
|
26
|
+
* (BOFF-10806). Returns null when there is nothing to say: the registration is
|
|
27
|
+
* not accepted, or its form is not linked to a team.
|
|
28
|
+
*/
|
|
29
|
+
export declare function rosterNotice(submission: {
|
|
30
|
+
status: string;
|
|
31
|
+
rosterOutcome: string | null;
|
|
32
|
+
rosterTeamId: string | null;
|
|
33
|
+
rosterTeamName: string | null;
|
|
34
|
+
participantName: string;
|
|
35
|
+
}): RosterNotice | null;
|
|
17
36
|
//# sourceMappingURL=registration.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../../src/atheletebridge/utils/registration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAc9E;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG3F;AAWD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,eAAO,MAAM,eAAe,UAAqC,CAAC"}
|
|
1
|
+
{"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../../src/atheletebridge/utils/registration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAc9E;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG3F;AAWD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,eAAO,MAAM,eAAe,UAAqC,CAAC;AAElE,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;CACzB,GAAG,YAAY,GAAG,IAAI,CAwBtB"}
|
|
@@ -19,7 +19,34 @@ function r(e) {
|
|
|
19
19
|
return n[e] ?? e;
|
|
20
20
|
}
|
|
21
21
|
var i = Object.keys(n);
|
|
22
|
+
function a(e) {
|
|
23
|
+
if (e.status !== "ACCEPTED" || !e.rosterOutcome) return null;
|
|
24
|
+
let t = e.rosterTeamName ?? "the team", n = e.rosterTeamName ? e.rosterTeamId : null;
|
|
25
|
+
switch (e.rosterOutcome) {
|
|
26
|
+
case "ADDED": return {
|
|
27
|
+
tone: "success",
|
|
28
|
+
message: `Added to ${t}`,
|
|
29
|
+
teamId: n
|
|
30
|
+
};
|
|
31
|
+
case "ALREADY_ON_ROSTER": return {
|
|
32
|
+
tone: "success",
|
|
33
|
+
message: `Already on the ${t} roster`,
|
|
34
|
+
teamId: n
|
|
35
|
+
};
|
|
36
|
+
case "NO_LINKED_USER": return {
|
|
37
|
+
tone: "warning",
|
|
38
|
+
message: `${e.participantName} has no AthleteBridge account linked to this registration, so they were not added to ${t}. Add them from the team roster.`,
|
|
39
|
+
teamId: n
|
|
40
|
+
};
|
|
41
|
+
case "TEAM_UNAVAILABLE": return {
|
|
42
|
+
tone: "warning",
|
|
43
|
+
message: "The team linked to this form was archived, so no one was added to a roster.",
|
|
44
|
+
teamId: null
|
|
45
|
+
};
|
|
46
|
+
default: return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
22
49
|
//#endregion
|
|
23
|
-
export { i as PAYMENT_METHODS, e as keyFromLabel, r as paymentMethodLabel, t as registrationShareUrl };
|
|
50
|
+
export { i as PAYMENT_METHODS, e as keyFromLabel, r as paymentMethodLabel, t as registrationShareUrl, a as rosterNotice };
|
|
24
51
|
|
|
25
52
|
//# sourceMappingURL=registration.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registration.js","names":[],"sources":["../../../src/atheletebridge/utils/registration.ts"],"sourcesContent":["/**\n * Registration helpers (BOFF-4135).\n * @module atheletebridge/utils/registration\n */\n\n/**\n * Derive a stable question key from its label, matching the server's key rule\n * (`^[a-z][a-z0-9._-]{0,79}$`) and de-duplicating against keys already used.\n */\nexport function keyFromLabel(label: string, taken: ReadonlySet<string>): string {\n const base =\n label\n .toLowerCase()\n .normalize('NFKD')\n .replace(/\\p{M}/gu, '')\n .split(/[^a-z0-9]+/)\n .filter(Boolean)\n .join('_')\n .replace(/^[^a-z]+/, '')\n .slice(0, 60) || 'question';\n let key = base;\n for (let n = 2; taken.has(key); n++) key = `${base}_${n}`;\n return key;\n}\n\n/**\n * Absolute share link for a form slug. The family opens it inside their own\n * AthleteBridge app (any workspace) — the slug alone identifies the form.\n */\nexport function registrationShareUrl(origin: string, basePath: string, slug: string): string {\n const base = basePath === '/' ? '' : basePath.replace(/\\/$/, '');\n return `${origin}${base}/register/${encodeURIComponent(slug)}`;\n}\n\nconst PAYMENT_METHOD_LABELS: Record<string, string> = {\n CASH: 'Cash',\n BANK_TRANSFER: 'Bank transfer',\n CARD_TERMINAL: 'Card (terminal)',\n CHEQUE: 'Cheque',\n ONLINE: 'Online',\n OTHER: 'Other',\n};\n\nexport function paymentMethodLabel(method: string): string {\n return PAYMENT_METHOD_LABELS[method] ?? method;\n}\n\nexport const PAYMENT_METHODS = Object.keys(PAYMENT_METHOD_LABELS);\n"],"mappings":";AASA,SAAgB,EAAa,GAAe,GAAoC;CAC9E,IAAM,IACJ,EACG,YAAY,EACZ,UAAU,MAAM,EAChB,QAAQ,WAAW,EAAE,EACrB,MAAM,YAAY,EAClB,OAAO,OAAO,EACd,KAAK,GAAG,EACR,QAAQ,YAAY,EAAE,EACtB,MAAM,GAAG,EAAE,KAAK,YACjB,IAAM;CACV,KAAK,IAAI,IAAI,GAAG,EAAM,IAAI,CAAG,GAAG,KAAK,IAAM,GAAG,EAAK,GAAG;CACtD,OAAO;AACT;AAMA,SAAgB,EAAqB,GAAgB,GAAkB,GAAsB;CAE3F,OAAO,GAAG,IADG,MAAa,MAAM,KAAK,EAAS,QAAQ,OAAO,EAAE,EACvC,YAAY,mBAAmB,CAAI;AAC7D;AAEA,IAAM,IAAgD;CACpD,MAAM;CACN,eAAe;CACf,eAAe;CACf,QAAQ;CACR,QAAQ;CACR,OAAO;AACT;AAEA,SAAgB,EAAmB,GAAwB;CACzD,OAAO,EAAsB,MAAW;AAC1C;AAEA,IAAa,IAAkB,OAAO,KAAK,CAAqB"}
|
|
1
|
+
{"version":3,"file":"registration.js","names":[],"sources":["../../../src/atheletebridge/utils/registration.ts"],"sourcesContent":["/**\n * Registration helpers (BOFF-4135).\n * @module atheletebridge/utils/registration\n */\n\n/**\n * Derive a stable question key from its label, matching the server's key rule\n * (`^[a-z][a-z0-9._-]{0,79}$`) and de-duplicating against keys already used.\n */\nexport function keyFromLabel(label: string, taken: ReadonlySet<string>): string {\n const base =\n label\n .toLowerCase()\n .normalize('NFKD')\n .replace(/\\p{M}/gu, '')\n .split(/[^a-z0-9]+/)\n .filter(Boolean)\n .join('_')\n .replace(/^[^a-z]+/, '')\n .slice(0, 60) || 'question';\n let key = base;\n for (let n = 2; taken.has(key); n++) key = `${base}_${n}`;\n return key;\n}\n\n/**\n * Absolute share link for a form slug. The family opens it inside their own\n * AthleteBridge app (any workspace) — the slug alone identifies the form.\n */\nexport function registrationShareUrl(origin: string, basePath: string, slug: string): string {\n const base = basePath === '/' ? '' : basePath.replace(/\\/$/, '');\n return `${origin}${base}/register/${encodeURIComponent(slug)}`;\n}\n\nconst PAYMENT_METHOD_LABELS: Record<string, string> = {\n CASH: 'Cash',\n BANK_TRANSFER: 'Bank transfer',\n CARD_TERMINAL: 'Card (terminal)',\n CHEQUE: 'Cheque',\n ONLINE: 'Online',\n OTHER: 'Other',\n};\n\nexport function paymentMethodLabel(method: string): string {\n return PAYMENT_METHOD_LABELS[method] ?? method;\n}\n\nexport const PAYMENT_METHODS = Object.keys(PAYMENT_METHOD_LABELS);\n\n/** Roster confirmation shown on an accepted, team-linked registration. */\nexport interface RosterNotice {\n tone: 'success' | 'warning';\n message: string;\n /** Team whose roster the \"Open roster\" action should go to, when it exists. */\n teamId: string | null;\n}\n\n/**\n * Explain what accepting this registration did to the team roster\n * (BOFF-10806). Returns null when there is nothing to say: the registration is\n * not accepted, or its form is not linked to a team.\n */\nexport function rosterNotice(submission: {\n status: string;\n rosterOutcome: string | null;\n rosterTeamId: string | null;\n rosterTeamName: string | null;\n participantName: string;\n}): RosterNotice | null {\n if (submission.status !== 'ACCEPTED' || !submission.rosterOutcome) return null;\n const team = submission.rosterTeamName ?? 'the team';\n const teamId = submission.rosterTeamName ? submission.rosterTeamId : null;\n switch (submission.rosterOutcome) {\n case 'ADDED':\n return { tone: 'success', message: `Added to ${team}`, teamId };\n case 'ALREADY_ON_ROSTER':\n return { tone: 'success', message: `Already on the ${team} roster`, teamId };\n case 'NO_LINKED_USER':\n return {\n tone: 'warning',\n message: `${submission.participantName} has no AthleteBridge account linked to this registration, so they were not added to ${team}. Add them from the team roster.`,\n teamId,\n };\n case 'TEAM_UNAVAILABLE':\n return {\n tone: 'warning',\n message: 'The team linked to this form was archived, so no one was added to a roster.',\n teamId: null,\n };\n default:\n return null;\n }\n}\n"],"mappings":";AASA,SAAgB,EAAa,GAAe,GAAoC;CAC9E,IAAM,IACJ,EACG,YAAY,EACZ,UAAU,MAAM,EAChB,QAAQ,WAAW,EAAE,EACrB,MAAM,YAAY,EAClB,OAAO,OAAO,EACd,KAAK,GAAG,EACR,QAAQ,YAAY,EAAE,EACtB,MAAM,GAAG,EAAE,KAAK,YACjB,IAAM;CACV,KAAK,IAAI,IAAI,GAAG,EAAM,IAAI,CAAG,GAAG,KAAK,IAAM,GAAG,EAAK,GAAG;CACtD,OAAO;AACT;AAMA,SAAgB,EAAqB,GAAgB,GAAkB,GAAsB;CAE3F,OAAO,GAAG,IADG,MAAa,MAAM,KAAK,EAAS,QAAQ,OAAO,EAAE,EACvC,YAAY,mBAAmB,CAAI;AAC7D;AAEA,IAAM,IAAgD;CACpD,MAAM;CACN,eAAe;CACf,eAAe;CACf,QAAQ;CACR,QAAQ;CACR,OAAO;AACT;AAEA,SAAgB,EAAmB,GAAwB;CACzD,OAAO,EAAsB,MAAW;AAC1C;AAEA,IAAa,IAAkB,OAAO,KAAK,CAAqB;AAehE,SAAgB,EAAa,GAML;CACtB,IAAI,EAAW,WAAW,cAAc,CAAC,EAAW,eAAe,OAAO;CAC1E,IAAM,IAAO,EAAW,kBAAkB,YACpC,IAAS,EAAW,iBAAiB,EAAW,eAAe;CACrE,QAAQ,EAAW,eAAnB;EACE,KAAK,SACH,OAAO;GAAE,MAAM;GAAW,SAAS,YAAY;GAAQ;EAAO;EAChE,KAAK,qBACH,OAAO;GAAE,MAAM;GAAW,SAAS,kBAAkB,EAAK;GAAU;EAAO;EAC7E,KAAK,kBACH,OAAO;GACL,MAAM;GACN,SAAS,GAAG,EAAW,gBAAgB,uFAAuF,EAAK;GACnI;EACF;EACF,KAAK,oBACH,OAAO;GACL,MAAM;GACN,SAAS;GACT,QAAQ;EACV;EACF,SACE,OAAO;CACX;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-atheletebridge",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.1107.1",
|
|
4
4
|
"description": "Burdenoff AthleteBridge Microfrontend - Sports, coaching & training platform for teams, fixtures, programs, sessions and athlete performance",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|