@openrails/billing-ui 0.251.0 → 0.252.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-settings-BIUgvclv.js","names":[],"sources":["../src/admin/pages/settings/configuration-edit.ts","../src/admin/pages/settings/setting-fields.tsx","../src/admin/pages/settings/notifications.tsx","../src/admin/pages/settings/index.tsx"],"sourcesContent":["import { useAdminServices } from \"#orck/admin/context\"\nimport * as React from \"react\"\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\"\n\nimport { ApiError } from \"#orck/admin/lib/api/client\"\nimport type {\n MerchantConfigurationState,\n UpdateMerchantConfigurationParams,\n} from \"#orck/client/generated/wire\"\n\ntype Change = Omit<UpdateMerchantConfigurationParams, \"expected_revision\">\n\n// useConfigurationEdit edits the merchant's configuration at the revision it\n// held when the edit opened, so a change made since is refused instead of\n// overwritten; a refused edit closes and the configuration reloads.\nexport function useConfigurationEdit(\n configuration: MerchantConfigurationState | undefined\n) {\n const { toast, toastApiError, toastStaleEdit, adminMutations } =\n useAdminServices()\n\n const [revision, setRevision] = React.useState<number | null>(null)\n const queryClient = useQueryClient()\n const update = useMutation(\n adminMutations.updateMerchantConfiguration(queryClient)\n )\n return {\n editing: revision !== null,\n open: () => {\n if (configuration) setRevision(configuration.revision)\n },\n close: () => setRevision(null),\n save: async (change: Change, saved: string, action: string) => {\n if (revision === null) return\n try {\n await update.mutateAsync({ ...change, expected_revision: revision })\n toast.success(saved)\n setRevision(null)\n } catch (err) {\n if (err instanceof ApiError && err.revisionMismatch) {\n toastStaleEdit(\"The configuration\")\n setRevision(null)\n return\n }\n toastApiError(err, action)\n if (err instanceof ApiError && err.merchantConfigReadOnly)\n setRevision(null)\n }\n },\n }\n}\n","import { AdminMessage, useAdminUI } from \"#orck/admin/ui-context\"\nimport { useAdminServices } from \"#orck/admin/context\"\nimport * as React from \"react\"\nimport { useQuery } from \"@tanstack/react-query\"\n\nimport { Button } from \"#orck/admin/components/ui/button\"\nimport { Label } from \"#orck/admin/components/ui/label\"\n\n// ReadOnlyNotice explains the missing edit controls when the merchant's\n// configuration comes from a file.\nexport function ReadOnlyNotice() {\n const { adminQueries } = useAdminServices()\n\n const { data } = useQuery(adminQueries.config())\n if (!data || data.capabilities.features?.merchant_config_edits) return null\n return (\n <p className=\"rounded-md border px-3 py-2 text-sm text-muted-foreground\">\n <AdminMessage\n id={\n \"Read-only: this merchant's configuration comes from a file. Change it there.\"\n }\n />\n </p>\n )\n}\n\n// EditActions is a section's Edit button, or Cancel and Save while editing.\n// Without edits it renders nothing.\nexport function EditActions({\n edits,\n editing,\n form,\n saving,\n canSave = true,\n onEdit,\n onCancel,\n}: {\n edits: boolean\n editing: boolean\n form: string\n saving: boolean\n canSave?: boolean\n onEdit: () => void\n onCancel: () => void\n}) {\n if (!edits) return null\n if (!editing)\n return (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={onEdit}>\n <AdminMessage id={\"Edit\"} />\n </Button>\n )\n return (\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={saving}\n onClick={onCancel}\n >\n <AdminMessage id={\"Cancel\"} />\n </Button>\n <Button type=\"submit\" size=\"sm\" form={form} disabled={!canSave || saving}>\n {saving ? \"Saving…\" : \"Save\"}\n </Button>\n </div>\n )\n}\n\nexport function SettingDetail({\n label,\n value,\n}: {\n label: string\n value?: string\n}) {\n return (\n <div className=\"grid gap-1.5 md:grid-cols-[11rem_minmax(0,1fr)] md:gap-6\">\n <dt className=\"text-sm text-muted-foreground\">{label}</dt>\n <dd\n className={\n value\n ? \"min-w-0 text-sm break-words\"\n : \"text-sm text-muted-foreground\"\n }\n >\n {value || \"Not set\"}\n </dd>\n </div>\n )\n}\n\nexport function SettingEditField({\n label,\n id,\n children,\n}: {\n label: string\n id: string\n children: React.ReactNode\n}) {\n const { id: adminId } = useAdminUI()\n\n return (\n <div className=\"grid gap-2 md:grid-cols-[11rem_minmax(0,1fr)] md:items-center md:gap-6\">\n <Label htmlFor={adminId(id)}>{label}</Label>\n <div className=\"min-w-0\">{children}</div>\n </div>\n )\n}\n","import { AdminMessage, useAdminUI } from \"#orck/admin/ui-context\"\nimport { useAdminServices } from \"#orck/admin/context\"\nimport { HugeiconsIcon } from \"@hugeicons/react\"\nimport { Add01Icon, Delete02Icon } from \"@hugeicons/core-free-icons\"\nimport * as React from \"react\"\n\nimport { useForm } from \"@tanstack/react-form\"\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\"\n\nimport { FormFieldErrors } from \"#orck/admin/components/form-field-errors\"\nimport { TypedConfirmDialog } from \"#orck/admin/components/typed-confirm-dialog\"\nimport { Badge } from \"#orck/admin/components/ui/badge\"\nimport { Button } from \"#orck/admin/components/ui/button\"\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"#orck/admin/components/ui/dialog\"\nimport { Input } from \"#orck/admin/components/ui/input\"\nimport { Label } from \"#orck/admin/components/ui/label\"\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"#orck/admin/components/ui/select\"\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"#orck/admin/components/ui/table\"\nimport { Switch } from \"#orck/admin/components/ui/switch\"\nimport { DIALOG_FORM } from \"#orck/admin/lib/dialog-width\"\nimport type {\n AlertWebhook,\n MerchantConfigurationState,\n} from \"#orck/client/generated/wire\"\nimport { useMerchantConfigEdits } from \"#orck/admin/lib/capabilities\"\n\nimport { useConfigurationEdit } from \"./configuration-edit\"\nimport {\n EditActions,\n ReadOnlyNotice,\n SettingDetail,\n SettingEditField,\n} from \"./setting-fields\"\n\ntype WebhookFormat = AlertWebhook[\"format\"]\n\n// --- Page ------------------------------------------------------------------\n\nexport function NotificationsTab() {\n const { adminQueries } = useAdminServices()\n\n const configurationQuery = useQuery(\n adminQueries.merchantConfiguration(\"Load settings\")\n )\n const webhooksQuery = useQuery(adminQueries.webhooks())\n const edits = useMerchantConfigEdits()\n\n const hooks = webhooksQuery.data?.data ?? []\n\n return (\n <div className=\"flex flex-col gap-10\">\n <ReadOnlyNotice />\n {configurationQuery.data ? (\n <NotificationEmailSection\n configuration={configurationQuery.data}\n edits={edits}\n />\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"Loading…\"} />\n </p>\n )}\n <WebhooksSection\n webhooks={hooks}\n loading={webhooksQuery.isPending}\n edits={edits}\n />\n </div>\n )\n}\n\n// --- Alert email -----------------------------------------------------------\n\nfunction NotificationEmailSection({\n configuration,\n edits,\n}: {\n configuration: MerchantConfigurationState\n edits: boolean\n}) {\n const { id: adminId, text: adminText } = useAdminUI()\n\n const email = configuration.settings.alert_email ?? \"\"\n const edit = useConfigurationEdit(configuration)\n const form = useForm({\n defaultValues: { email },\n // An empty address clears the email channel.\n onSubmit: ({ value }) =>\n edit.save(\n { settings: { alert_email: value.email.trim() } },\n value.email.trim() ? \"Alert email saved\" : \"Alert email cleared\",\n \"Save alert email\"\n ),\n })\n\n return (\n <section className=\"grid gap-5\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"grid gap-1\">\n <h2 className=\"text-base font-semibold\">\n <AdminMessage id={\"Email delivery\"} />\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"Send email alerts to this address.\"} />\n </p>\n </div>\n <form.Subscribe selector={(state) => state.isSubmitting}>\n {(isSubmitting) => (\n <EditActions\n edits={edits}\n editing={edit.editing}\n form=\"alert-email-form\"\n saving={isSubmitting}\n onEdit={() => {\n form.reset({ email })\n edit.open()\n }}\n onCancel={edit.close}\n />\n )}\n </form.Subscribe>\n </div>\n {edit.editing ? (\n <form\n id={adminId(\"alert-email-form\")}\n onSubmit={(event) => {\n event.preventDefault()\n event.stopPropagation()\n void form.handleSubmit()\n }}\n >\n <form.Field name=\"email\">\n {(field) => (\n <SettingEditField\n label={adminText(\"Alert email\")}\n id={\"alert-email\"}\n >\n <Input\n id={adminId(\"alert-email\")}\n type=\"email\"\n placeholder={adminText(\"alerts@example.com\")}\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) => field.handleChange(event.target.value)}\n />\n </SettingEditField>\n )}\n </form.Field>\n </form>\n ) : (\n <dl>\n <SettingDetail label={adminText(\"Alert email\")} value={email} />\n </dl>\n )}\n </section>\n )\n}\n\n// --- Webhooks --------------------------------------------------------------\n\nconst WEBHOOK_FORMATS: { value: WebhookFormat; label: string }[] = [\n { value: \"generic\", label: \"Generic (your own receiver)\" },\n { value: \"discord\", label: \"Discord\" },\n { value: \"slack\", label: \"Slack\" },\n]\n\nfunction WebhooksSection({\n webhooks,\n loading,\n edits,\n}: {\n webhooks: AlertWebhook[]\n loading: boolean\n edits: boolean\n}) {\n return (\n <section className=\"grid gap-5\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"grid max-w-2xl gap-1\">\n <h2 className=\"text-base font-semibold\">\n <AdminMessage id={\"Webhooks\"} />\n </h2>\n <p className=\"text-sm text-pretty text-muted-foreground\">\n <AdminMessage\n id={\"Send alerts to Discord, Slack, or your own endpoint.\"}\n />\n </p>\n </div>\n {edits && <WebhookDialog />}\n </div>\n {loading ? (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"Loading…\"} />\n </p>\n ) : webhooks.length === 0 ? (\n <p className=\"py-2 text-sm text-muted-foreground\">\n <AdminMessage id={\"No webhooks configured.\"} />\n </p>\n ) : (\n <Table className=\"min-w-[36rem]\">\n <TableHeader>\n <TableRow className=\"hover:bg-transparent\">\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Name\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Format\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">URL</TableHead>\n {edits && (\n <TableHead className=\"text-right text-muted-foreground\">\n <AdminMessage id={\"Action\"} />\n </TableHead>\n )}\n </TableRow>\n </TableHeader>\n <TableBody>\n {webhooks.map((w) => (\n <WebhookRow key={w.id} webhook={w} edits={edits} />\n ))}\n </TableBody>\n </Table>\n )}\n </section>\n )\n}\n\nfunction WebhookRow({\n webhook,\n edits,\n}: {\n webhook: AlertWebhook\n edits: boolean\n}) {\n const { toast, toastApiError, adminMutations } = useAdminServices()\n\n const { text: adminText } = useAdminUI()\n\n const [confirmOpen, setConfirmOpen] = React.useState(false)\n const queryClient = useQueryClient()\n const removeWebhook = useMutation(adminMutations.deleteWebhook(queryClient))\n const name = webhook.name ?? webhook.destination_host\n return (\n <TableRow className={webhook.enabled ? undefined : \"opacity-60\"}>\n <TableCell className=\"py-3 font-medium\">{name}</TableCell>\n <TableCell className=\"py-3\">\n <span className=\"flex flex-wrap gap-1\">\n <Badge variant=\"secondary\">{webhook.format}</Badge>\n {!webhook.enabled && (\n <Badge variant=\"outline\">\n <AdminMessage id={\"disabled\"} />\n </Badge>\n )}\n </span>\n </TableCell>\n <TableCell\n className=\"max-w-[22rem] truncate py-3 text-xs text-muted-foreground\"\n title={webhook.destination_host}\n >\n {webhook.destination_host}\n </TableCell>\n {edits && (\n <TableCell className=\"py-3 text-right\">\n <div className=\"flex justify-end gap-1\">\n <WebhookDialog webhook={webhook} />\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={adminText(\"Delete webhook\")}\n onClick={() => setConfirmOpen(true)}\n >\n <HugeiconsIcon\n icon={Delete02Icon}\n className=\"size-4 text-muted-foreground\"\n />\n </Button>\n <TypedConfirmDialog\n open={confirmOpen}\n onOpenChange={setConfirmOpen}\n title={`Delete \"${name}\"?`}\n description={adminText(\n \"Operational notifications will stop delivering to this webhook. This cannot be undone.\"\n )}\n confirmationWord=\"DELETE\"\n actionLabel=\"Delete webhook\"\n onConfirm={async () => {\n try {\n await removeWebhook.mutateAsync(webhook.id)\n toast.success(\"Webhook deleted\")\n } catch (err) {\n toastApiError(err, \"Delete webhook\")\n }\n }}\n />\n </div>\n </TableCell>\n )}\n </TableRow>\n )\n}\n\n// WebhookDialog adds a webhook or edits one. The URL carries the receiver's\n// secret, so it is write-only: an edit leaves it blank to keep it.\nfunction WebhookDialog({ webhook }: { webhook?: AlertWebhook }) {\n const { toast, toastApiError, adminMutations } = useAdminServices()\n\n const { id: adminId, text: adminText } = useAdminUI()\n\n const [open, setOpen] = React.useState(false)\n const queryClient = useQueryClient()\n const addWebhook = useMutation(adminMutations.createWebhook(queryClient))\n const updateWebhook = useMutation(adminMutations.updateWebhook(queryClient))\n const initial = () => ({\n name: webhook?.name ?? \"\",\n url: \"\",\n format: webhook?.format ?? (\"generic\" as WebhookFormat),\n enabled: webhook?.enabled ?? true,\n })\n const form = useForm({\n defaultValues: initial(),\n onSubmit: async ({ value }) => {\n const url = value.url.trim()\n try {\n if (webhook)\n await updateWebhook.mutateAsync({\n id: webhook.id,\n webhook: {\n name: value.name.trim() || null,\n format: value.format,\n enabled: value.enabled,\n ...(url ? { url } : {}),\n },\n })\n else\n await addWebhook.mutateAsync({\n name: value.name.trim(),\n url,\n format: value.format,\n })\n toast.success(webhook ? \"Webhook saved\" : \"Webhook added\")\n handleOpen(false)\n } catch (err) {\n toastApiError(err, webhook ? \"Save webhook\" : \"Add webhook\")\n }\n },\n })\n\n const handleOpen = (next: boolean) => {\n if (next) form.reset(initial())\n else form.reset()\n setOpen(next)\n }\n\n return (\n <Dialog open={open} onOpenChange={handleOpen}>\n <DialogTrigger\n render={\n <Button size=\"sm\" variant=\"outline\">\n {!webhook && <HugeiconsIcon icon={Add01Icon} className=\"size-4\" />}{\" \"}\n {webhook ? \"Edit\" : \"Add webhook\"}\n </Button>\n }\n />\n <DialogContent className={DIALOG_FORM}>\n <DialogHeader>\n <DialogTitle>\n {webhook\n ? `Edit ${webhook.name ?? webhook.destination_host}`\n : \"Add webhook\"}\n </DialogTitle>\n <DialogDescription>\n {webhook\n ? \"The current URL is never shown. Paste a new one to replace it, or leave it blank to keep it.\"\n : \"Paste a webhook address from Discord, Slack, or your own alert receiver.\"}\n </DialogDescription>\n </DialogHeader>\n <form\n onSubmit={(event) => {\n event.preventDefault()\n event.stopPropagation()\n void form.handleSubmit()\n }}\n className=\"grid gap-4\"\n >\n <div className=\"grid gap-3\">\n <form.Field\n name=\"name\"\n validators={{\n onChange: ({ value }) =>\n webhook || value.trim() ? undefined : \"Enter a webhook name\",\n }}\n >\n {(field) => (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={adminId(\"wh-name\")}>\n <AdminMessage id={\"Name\"} />\n </Label>\n <Input\n id={adminId(\"wh-name\")}\n placeholder={adminText(\"e.g. #billing-alerts\")}\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) => field.handleChange(event.target.value)}\n aria-invalid={field.state.meta.errors.length > 0}\n />\n <FormFieldErrors errors={field.state.meta.errors} />\n </div>\n )}\n </form.Field>\n <form.Field name=\"format\">\n {(field) => (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={adminId(\"wh-format\")}>\n <AdminMessage id={\"Format\"} />\n </Label>\n <Select\n value={field.state.value}\n onValueChange={(value) =>\n field.handleChange(value as WebhookFormat)\n }\n >\n <SelectTrigger id={adminId(\"wh-format\")} className=\"w-full\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {WEBHOOK_FORMATS.map((format) => (\n <SelectItem key={format.value} value={format.value}>\n {format.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n )}\n </form.Field>\n <form.Field\n name=\"url\"\n validators={{\n onChange: ({ value }) => {\n if (!value.trim())\n return webhook ? undefined : \"Enter a webhook URL\"\n try {\n new URL(value)\n return undefined\n } catch {\n return \"Enter a valid webhook URL\"\n }\n },\n }}\n >\n {(field) => (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={adminId(\"wh-url\")}>\n <AdminMessage id={\"Webhook URL\"} />\n </Label>\n <Input\n id={adminId(\"wh-url\")}\n type=\"url\"\n autoComplete=\"off\"\n className=\"text-xs\"\n placeholder={\n webhook\n ? \"unchanged\"\n : \"https://discord.com/api/webhooks/…\"\n }\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) => field.handleChange(event.target.value)}\n aria-invalid={field.state.meta.errors.length > 0}\n />\n <FormFieldErrors errors={field.state.meta.errors} />\n </div>\n )}\n </form.Field>\n {webhook && (\n <form.Field name=\"enabled\">\n {(field) => (\n <div className=\"flex items-center gap-2\">\n <Switch\n id={adminId(\"wh-enabled\")}\n checked={field.state.value}\n onCheckedChange={field.handleChange}\n />\n <Label htmlFor={adminId(\"wh-enabled\")}>\n <AdminMessage id={\"Enabled\"} />\n </Label>\n </div>\n )}\n </form.Field>\n )}\n </div>\n <DialogFooter>\n <form.Subscribe\n selector={(state) =>\n [\n state.values.name,\n state.values.url,\n state.isDirty,\n state.canSubmit,\n state.isSubmitting,\n ] as const\n }\n >\n {([name, url, isDirty, canSubmit, isSubmitting]) => (\n <Button\n type=\"submit\"\n disabled={\n (webhook ? !isDirty : !name.trim() || !url.trim()) ||\n !canSubmit ||\n isSubmitting\n }\n >\n {isSubmitting ? \"Saving…\" : webhook ? \"Save\" : \"Add webhook\"}\n </Button>\n )}\n </form.Subscribe>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n","import { AdminMessage, useAdminUI } from \"#orck/admin/ui-context\"\nimport { useAdminContext } from \"#orck/admin/context\"\nimport { useAdminServices } from \"#orck/admin/context\"\nimport * as React from \"react\"\nimport { useSearchParams } from \"#orck/admin/router\"\n\nimport { useForm } from \"@tanstack/react-form\"\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\"\n\nimport { Badge } from \"#orck/admin/components/ui/badge\"\nimport { TypedConfirmDialog } from \"#orck/admin/components/typed-confirm-dialog\"\nimport { FormFieldErrors } from \"#orck/admin/components/form-field-errors\"\nimport { useConfirm } from \"#orck/admin/lib/confirm\"\nimport { Button } from \"#orck/admin/components/ui/button\"\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"#orck/admin/components/ui/dialog\"\nimport { Input } from \"#orck/admin/components/ui/input\"\nimport { Label } from \"#orck/admin/components/ui/label\"\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"#orck/admin/components/ui/select\"\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"#orck/admin/components/ui/table\"\nimport {\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from \"#orck/admin/components/ui/tabs\"\nimport type {\n MerchantConfigurationState,\n PSP,\n PSPCredential,\n RailDefinition,\n} from \"#orck/client/generated/wire\"\nimport {\n amountFromInput,\n currencyScale,\n formatDate,\n formatNativeAmount,\n} from \"#orck/admin/lib/format\"\nimport { DIALOG_FORM } from \"#orck/admin/lib/dialog-width\"\n\nimport { ApiError } from \"#orck/admin/lib/api/client\"\n\nimport {\n settingsTab,\n useAdminArea,\n useAdminUpdates,\n useMerchantConfig,\n useMerchantConfigEdits,\n} from \"#orck/admin/lib/capabilities\"\nimport { useConfigurationEdit } from \"./configuration-edit\"\nimport {\n EditActions,\n ReadOnlyNotice,\n SettingDetail,\n SettingEditField,\n} from \"./setting-fields\"\nimport { NotificationsTab } from \"./notifications\"\n\nconst LINE_TAB =\n \"flex-none px-0 after:bg-primary group-data-horizontal/tabs:after:bottom-[-1px]\"\n\nexport function SettingsPage() {\n // The tab lives in the URL so a settings page can be linked to, and so the\n // back button steps through tabs the way it looks like it should.\n const [params, setParams] = useSearchParams()\n const config = useMerchantConfig()\n const admin = useAdminArea()\n const hosted = useAdminContext().props.settingsTabs ?? []\n const values = hosted.map((h) => h.value)\n const first = settingsTab(null, config, admin, values)\n const tab = settingsTab(params.get(\"tab\"), config, admin, values)\n\n return (\n <Tabs\n value={tab}\n onValueChange={(next) => {\n const updated = new URLSearchParams(params)\n if (!next || next === first) updated.delete(\"tab\")\n else updated.set(\"tab\", next)\n setParams(updated)\n }}\n className=\"flex flex-col gap-4\"\n >\n <div className=\"overflow-x-auto\">\n <TabsList\n variant=\"line\"\n className=\"w-max min-w-full justify-start gap-6 rounded-none p-0\"\n >\n {config && (\n <TabsTrigger value=\"merchant\" className={LINE_TAB}>\n <AdminMessage id={\"Merchant\"} />\n </TabsTrigger>\n )}\n {config && (\n <TabsTrigger value=\"notifications\" className={LINE_TAB}>\n <AdminMessage id={\"Notifications\"} />\n </TabsTrigger>\n )}\n {config && (\n <TabsTrigger value=\"psps\" className={LINE_TAB}>\n <AdminMessage id={\"PSPs\"} />\n </TabsTrigger>\n )}\n {admin && (\n <TabsTrigger value=\"customer-controls\" className={LINE_TAB}>\n <AdminMessage id={\"Customer controls\"} />\n </TabsTrigger>\n )}\n {hosted.map((h) => (\n <TabsTrigger key={h.value} value={h.value} className={LINE_TAB}>\n {h.title}\n </TabsTrigger>\n ))}\n </TabsList>\n </div>\n {config && (\n <TabsContent value=\"merchant\">\n <MerchantSettingsTab />\n </TabsContent>\n )}\n {config && (\n <TabsContent value=\"notifications\">\n <NotificationsTab />\n </TabsContent>\n )}\n {config && (\n <TabsContent value=\"psps\">\n <PSPsTab />\n </TabsContent>\n )}\n {admin && (\n <TabsContent value=\"customer-controls\">\n <CustomerControlsTab />\n </TabsContent>\n )}\n {hosted.map((h) => (\n <TabsContent key={h.value} value={h.value}>\n {h.element}\n </TabsContent>\n ))}\n </Tabs>\n )\n}\n\nexport function MerchantSettingsTab() {\n const { adminQueries } = useAdminServices()\n\n const { data } = useQuery(adminQueries.merchantConfiguration(\"Load settings\"))\n const edits = useMerchantConfigEdits()\n if (!data)\n return (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"Loading…\"} />\n </p>\n )\n return (\n <div className=\"grid gap-10\">\n <ReadOnlyNotice />\n <MerchantProfileForm configuration={data} edits={edits} />\n <RepriceNoticeWindowForm configuration={data} edits={edits} />\n </div>\n )\n}\n\nfunction MerchantProfileForm({\n configuration,\n edits,\n}: {\n configuration: MerchantConfigurationState\n edits: boolean\n}) {\n const { id: adminId, text: adminText } = useAdminUI()\n\n // The merchant's one name is the configuration's display_name.\n const profile = configuration.settings.profile\n const current = () => ({\n displayName: configuration.display_name,\n fromEmail: profile?.from_email ?? \"\",\n supportURL: profile?.support_url ?? \"\",\n subscribeURL: profile?.subscribe_url ?? \"\",\n billingURL: profile?.billing_url ?? \"\",\n logoURL: profile?.logo_url ?? \"\",\n })\n const edit = useConfigurationEdit(configuration)\n const form = useForm({\n defaultValues: current(),\n onSubmit: ({ value }) =>\n edit.save(\n {\n display_name: value.displayName.trim(),\n settings: {\n profile: {\n from_email: value.fromEmail || undefined,\n support_url: value.supportURL || undefined,\n subscribe_url: value.subscribeURL || undefined,\n billing_url: value.billingURL || undefined,\n logo_url: value.logoURL || undefined,\n },\n },\n },\n \"Profile saved\",\n \"Save profile\"\n ),\n })\n\n return (\n <section className=\"grid gap-5\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"grid gap-1\">\n <h2 className=\"text-base font-semibold\">\n <AdminMessage id={\"Merchant profile\"} />\n </h2>\n <p className=\"text-sm text-pretty text-muted-foreground\">\n <AdminMessage\n id={\n \"Customer-facing merchant details used on invoices and emails.\"\n }\n />\n </p>\n </div>\n <form.Subscribe\n selector={(state) => [state.canSubmit, state.isSubmitting]}\n >\n {([canSubmit, isSubmitting]) => (\n <EditActions\n edits={edits}\n editing={edit.editing}\n form=\"merchant-profile-form\"\n saving={isSubmitting}\n canSave={canSubmit}\n onEdit={() => {\n form.reset(current())\n edit.open()\n }}\n onCancel={edit.close}\n />\n )}\n </form.Subscribe>\n </div>\n\n {edit.editing ? (\n <form\n id={adminId(\"merchant-profile-form\")}\n onSubmit={(event) => {\n event.preventDefault()\n event.stopPropagation()\n void form.handleSubmit()\n }}\n className=\"grid gap-4\"\n >\n <form.Field\n name=\"displayName\"\n validators={{\n onChange: ({ value }) =>\n value.trim() ? undefined : \"Enter the merchant's name\",\n }}\n >\n {(field) => (\n <SettingEditField label={adminText(\"Display name\")} id={\"s-name\"}>\n <div className=\"grid gap-1.5\">\n <Input\n id={adminId(\"s-name\")}\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) => field.handleChange(event.target.value)}\n autoComplete=\"organization\"\n aria-invalid={field.state.meta.errors.length > 0}\n />\n <FormFieldErrors errors={field.state.meta.errors} />\n </div>\n </SettingEditField>\n )}\n </form.Field>\n <form.Field name=\"fromEmail\">\n {(field) => (\n <SettingEditField label={adminText(\"From email\")} id={\"s-email\"}>\n <Input\n id={adminId(\"s-email\")}\n type=\"email\"\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) => field.handleChange(event.target.value)}\n autoComplete=\"email\"\n />\n </SettingEditField>\n )}\n </form.Field>\n <form.Field name=\"supportURL\">\n {(field) => (\n <SettingEditField\n label={adminText(\"Support URL\")}\n id={\"s-support\"}\n >\n <Input\n id={adminId(\"s-support\")}\n type=\"url\"\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) => field.handleChange(event.target.value)}\n placeholder={adminText(\"https://example.com/support\")}\n />\n </SettingEditField>\n )}\n </form.Field>\n <form.Field name=\"subscribeURL\">\n {(field) => (\n <SettingEditField\n label={adminText(\"Subscribe URL\")}\n id={\"s-subscribe\"}\n >\n <Input\n id={adminId(\"s-subscribe\")}\n type=\"url\"\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) => field.handleChange(event.target.value)}\n placeholder={adminText(\"https://example.com/subscribe\")}\n />\n </SettingEditField>\n )}\n </form.Field>\n <form.Field name=\"billingURL\">\n {(field) => (\n <SettingEditField\n label={adminText(\"Billing URL\")}\n id={\"s-billing\"}\n >\n <Input\n id={adminId(\"s-billing\")}\n type=\"url\"\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) => field.handleChange(event.target.value)}\n placeholder={adminText(\"https://example.com/account\")}\n />\n </SettingEditField>\n )}\n </form.Field>\n <form.Field name=\"logoURL\">\n {(field) => (\n <SettingEditField label={adminText(\"Logo URL\")} id={\"s-logo\"}>\n <Input\n id={adminId(\"s-logo\")}\n type=\"url\"\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) => field.handleChange(event.target.value)}\n placeholder={adminText(\"https://example.com/logo.png\")}\n />\n </SettingEditField>\n )}\n </form.Field>\n </form>\n ) : (\n <dl className=\"grid gap-4\">\n <SettingDetail\n label={adminText(\"Display name\")}\n value={configuration.display_name}\n />\n <SettingDetail\n label={adminText(\"From email\")}\n value={profile?.from_email}\n />\n <SettingDetail\n label={adminText(\"Support URL\")}\n value={profile?.support_url}\n />\n <SettingDetail\n label={adminText(\"Subscribe URL\")}\n value={profile?.subscribe_url}\n />\n <SettingDetail\n label={adminText(\"Billing URL\")}\n value={profile?.billing_url}\n />\n <SettingDetail\n label={adminText(\"Logo URL\")}\n value={profile?.logo_url}\n />\n </dl>\n )}\n </section>\n )\n}\n\n// RepriceNoticeWindowForm (#781): the minimum advance notice (days) a\n// subscription price increase must give existing subscribers. The price-change\n// wizard reads the same value; the API enforces it regardless.\nfunction RepriceNoticeWindowForm({\n configuration,\n edits,\n}: {\n configuration: MerchantConfigurationState\n edits: boolean\n}) {\n const { id: adminId, text: adminText } = useAdminUI()\n\n const days = configuration.settings.reprice_notice_window_days ?? 30\n const edit = useConfigurationEdit(configuration)\n const form = useForm({\n defaultValues: { days: String(days) },\n onSubmit: ({ value }) =>\n edit.save(\n { settings: { reprice_notice_window_days: Number(value.days) } },\n \"Notice window saved\",\n \"Save notice window\"\n ),\n })\n\n return (\n <section className=\"grid gap-5\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"grid max-w-2xl gap-1\">\n <h2 className=\"text-base font-semibold\">\n <AdminMessage id={\"Pricing policies\"} />\n </h2>\n <p className=\"text-sm text-pretty text-muted-foreground\">\n <AdminMessage\n id={\n \"Set how much notice customers receive before a price increase.\"\n }\n />\n </p>\n </div>\n <form.Subscribe\n selector={(state) => [state.canSubmit, state.isSubmitting]}\n >\n {([canSubmit, isSubmitting]) => (\n <EditActions\n edits={edits}\n editing={edit.editing}\n form=\"notice-window-form\"\n saving={isSubmitting}\n canSave={canSubmit}\n onEdit={() => {\n form.reset({ days: String(days) })\n edit.open()\n }}\n onCancel={edit.close}\n />\n )}\n </form.Subscribe>\n </div>\n\n {edit.editing ? (\n <form\n id={adminId(\"notice-window-form\")}\n onSubmit={(event) => {\n event.preventDefault()\n event.stopPropagation()\n void form.handleSubmit()\n }}\n >\n <form.Field\n name=\"days\"\n validators={{\n onChange: ({ value }) => {\n const days = Number(value)\n return value.trim() && Number.isInteger(days) && days >= 0\n ? undefined\n : \"Enter a whole number of days\"\n },\n }}\n >\n {(field) => (\n <SettingEditField\n label={adminText(\"Notice period (days)\")}\n id={\"s-notice-window\"}\n >\n <div className=\"grid gap-1.5\">\n <Input\n id={adminId(\"s-notice-window\")}\n type=\"number\"\n step=\"1\"\n min=\"0\"\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) => field.handleChange(event.target.value)}\n aria-invalid={field.state.meta.errors.length > 0}\n />\n <FormFieldErrors errors={field.state.meta.errors} />\n </div>\n </SettingEditField>\n )}\n </form.Field>\n </form>\n ) : (\n <dl>\n <SettingDetail\n label={adminText(\"Notice period\")}\n value={`${days} ${days === 1 ? \"day\" : \"days\"}`}\n />\n </dl>\n )}\n </section>\n )\n}\n\nexport function PSPsTab() {\n const { selectedMerchant, adminQueries } = useAdminServices()\n\n const psps = useQuery(adminQueries.psps())\n const rails = useQuery(adminQueries.rails())\n const edits = useMerchantConfigEdits()\n if (psps.isPending || rails.isPending)\n return (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"Loading…\"} />\n </p>\n )\n\n const railDefinitions = rails.data ?? []\n\n return (\n <div className=\"grid gap-6\">\n <ReadOnlyNotice />\n <section className=\"grid gap-5\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"grid gap-1\">\n <h2 className=\"text-base font-semibold\">\n <AdminMessage id={\"PSPs\"} />\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage\n id={\n \"The accounts this merchant takes payments through, one or more per rail.\"\n }\n />\n </p>\n </div>\n {edits && (\n <PSPDialog\n key={selectedMerchant() ?? \"\"}\n railDefinitions={railDefinitions}\n />\n )}\n </div>\n {!psps.data?.data?.length ? (\n <p className=\"py-2 text-sm text-muted-foreground\">\n <AdminMessage id={\"No PSPs configured.\"} />\n </p>\n ) : (\n <Table className=\"min-w-[44rem]\">\n <TableHeader>\n <TableRow className=\"hover:bg-transparent\">\n <TableHead className=\"text-muted-foreground\">PSP</TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Environment\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Credentials\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"State\"} />\n </TableHead>\n {edits && (\n <TableHead className=\"text-right text-muted-foreground\">\n <AdminMessage id={\"Action\"} />\n </TableHead>\n )}\n </TableRow>\n </TableHeader>\n <TableBody>\n {psps.data.data.map((psp) => (\n <PSPRow\n key={psp.id}\n psp={psp}\n railDefinitions={railDefinitions}\n edits={edits}\n />\n ))}\n </TableBody>\n </Table>\n )}\n </section>\n </div>\n )\n}\n\n// Credentials are write-only: a PSP shows whether each is set, never a value.\nfunction credentialTitle(c: PSPCredential) {\n if (c.validated_at) return `Validated ${formatDate(c.validated_at)}`\n return c.configured ? \"Configured\" : \"Not configured\"\n}\n\nfunction PSPRow({\n psp,\n railDefinitions,\n edits,\n}: {\n psp: PSP\n railDefinitions: RailDefinition[]\n edits: boolean\n}) {\n const {\n toast,\n toastApiError,\n toastStaleEdit,\n adminMutations,\n selectedMerchant,\n } = useAdminServices()\n\n const { text: adminText } = useAdminUI()\n\n const queryClient = useQueryClient()\n const updatePSP = useMutation(adminMutations.updatePSP(queryClient))\n const [confirmLastOpen, setConfirmLastOpen] = React.useState(false)\n const confirm = useConfirm()\n const definition = railDefinitions.find((d) => d.rail === psp.rail)\n const railName = definition?.display_name ?? psp.rail\n const credentials = psp.credentials ?? {}\n // Archive exactly this PSP (#655). It never contacts the provider, so a\n // terminated account archives too. The rail's last active PSP needs an\n // explicit confirmation.\n const archive = async (allowLast: boolean) => {\n if (\n !allowLast &&\n !(await confirm({\n title: `Archive ${psp.key}?`,\n description:\n \"New purchase stops routing to it. Existing subscriptions, refunds and webhooks keep using it.\",\n confirmLabel: \"Archive PSP\",\n }))\n )\n return\n try {\n await updatePSP.mutateAsync({\n id: psp.id,\n psp: {\n archived: true,\n expected_revision: psp.revision,\n ...(allowLast ? { allow_last: true } : {}),\n },\n })\n toast.success(\"PSP archived\")\n } catch (err) {\n if (err instanceof ApiError && err.code === \"psp_last_active\") {\n if (!allowLast) setConfirmLastOpen(true)\n else toastApiError(err, \"Archive PSP\")\n return\n }\n if (err instanceof ApiError && err.revisionMismatch) {\n toastStaleEdit(`PSP ${psp.key}`)\n return\n }\n toastApiError(err, \"Archive PSP\")\n }\n }\n return (\n <TableRow className={psp.archived ? \"opacity-60\" : undefined}>\n <TableCell className=\"py-3\">\n <div className=\"grid min-w-0 leading-tight\">\n <span className=\"font-medium\">{psp.key}</span>\n <span className=\"truncate text-xs text-muted-foreground\">\n {railName} · {psp.account_id}\n </span>\n </div>\n </TableCell>\n <TableCell className=\"py-3 capitalize\">{psp.environment}</TableCell>\n <TableCell className=\"py-3\">\n {Object.keys(credentials).length > 0 ? (\n <span className=\"flex flex-wrap gap-1\">\n {Object.entries(credentials).map(([name, credential]) => (\n <Badge\n key={name}\n variant=\"secondary\"\n className={\n credential.configured ? \"\" : \"bg-held-surface text-held\"\n }\n title={credentialTitle(credential)}\n >\n {name}\n </Badge>\n ))}\n </span>\n ) : (\n <span className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"None required\"} />\n </span>\n )}\n </TableCell>\n <TableCell className=\"py-3\">\n {psp.archived && psp.open_obligations === 0 ? (\n <Badge variant=\"secondary\">\n <AdminMessage id={\"archived\"} />\n </Badge>\n ) : psp.archived ? (\n <Badge variant=\"secondary\" className=\"bg-held-surface text-held\">\n <AdminMessage id={\"draining (\"} />\n {psp.open_obligations})\n </Badge>\n ) : (\n <Badge\n variant=\"secondary\"\n className=\"bg-settled-surface text-settled\"\n >\n <AdminMessage id={\"active\"} />\n </Badge>\n )}\n </TableCell>\n {edits && (\n <TableCell className=\"py-3 text-right\">\n {!psp.archived && (\n <div className=\"flex justify-end gap-2\">\n <RotateCredentialsDialog\n key={`${selectedMerchant() ?? \"\"}:${psp.id}`}\n psp={psp}\n credentialKeys={\n definition?.credential_keys ?? Object.keys(credentials)\n }\n />\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={updatePSP.isPending}\n onClick={() => archive(false)}\n >\n <AdminMessage id={\"Archive\"} />\n </Button>\n <TypedConfirmDialog\n open={confirmLastOpen}\n onOpenChange={setConfirmLastOpen}\n title={`Archive the last active ${railName} PSP?`}\n description={adminText(\n \"New purchase on this rail is refused until another PSP is armed. Existing subscriptions, refunds and webhooks keep using this one.\"\n )}\n confirmationWord=\"ARCHIVE\"\n actionLabel=\"Archive PSP\"\n onConfirm={() => archive(true)}\n />\n </div>\n )}\n </TableCell>\n )}\n </TableRow>\n )\n}\n\n// RotateCredentialsDialog writes new credentials; the current ones are never\n// shown. The provider checks the new ones before anything is stored, so a\n// rejected credential changes nothing. The edit names the revision read when\n// the dialog opened, and secret fields clear whenever it closes.\nexport function RotateCredentialsDialog({\n psp,\n credentialKeys,\n}: {\n psp: PSP\n credentialKeys: string[]\n}) {\n const {\n toast,\n toastApiError,\n toastStaleEdit,\n adminMutations,\n selectedMerchant,\n } = useAdminServices()\n\n const { id: adminId } = useAdminUI()\n\n const [open, setOpen] = React.useState(false)\n const [merchant] = React.useState(() => selectedMerchant() ?? \"\")\n const revision = React.useRef(psp.revision)\n const queryClient = useQueryClient()\n const updatePSP = useMutation(adminMutations.updatePSP(queryClient))\n const credentials = psp.credentials ?? {}\n const form = useForm({\n defaultValues: { credentials: {} as Record<string, string> },\n onSubmit: async ({ value }) => {\n const supplied = Object.fromEntries(\n Object.entries(value.credentials).filter(([, item]) => item.trim())\n )\n try {\n if ((selectedMerchant() ?? \"\") !== merchant)\n throw new Error(\"Merchant changed; reopen this form\")\n await updatePSP.mutateAsync({\n id: psp.id,\n psp: { expected_revision: revision.current, credentials: supplied },\n })\n toast.success(`${psp.key} credentials validated and rotated`)\n close(false)\n } catch (err) {\n if (err instanceof ApiError && err.revisionMismatch) {\n toastStaleEdit(`PSP ${psp.key}`)\n close(false)\n return\n }\n toastApiError(err, \"Rotate credentials\")\n } finally {\n updatePSP.reset()\n }\n },\n })\n\n const close = (next: boolean) => {\n if (!next) form.reset()\n if (next) revision.current = psp.revision\n setOpen(next)\n }\n\n return (\n <Dialog open={open} onOpenChange={close}>\n <DialogTrigger\n render={\n <Button variant=\"outline\" size=\"sm\">\n <AdminMessage id={\"Rotate\"} />\n </Button>\n }\n />\n <DialogContent className={DIALOG_FORM}>\n <DialogHeader>\n <DialogTitle>\n <AdminMessage id={\"Rotate \"} />\n {psp.key}\n <AdminMessage id={\" credentials · \"} />\n {psp.account_id}\n </DialogTitle>\n <DialogDescription>\n <AdminMessage\n id={\n \"Current credentials are never shown. A new one is validated against the live provider before it is stored; if that check fails, nothing is written. Leave a field blank to keep the credential it holds now.\"\n }\n />\n </DialogDescription>\n </DialogHeader>\n <form\n onSubmit={(event) => {\n event.preventDefault()\n event.stopPropagation()\n void form.handleSubmit()\n }}\n className=\"grid gap-4\"\n >\n <form.Field name=\"credentials\">\n {(field) => (\n <div className=\"grid gap-3\">\n {credentialKeys.map((name) => {\n const current = credentials[name]\n return (\n <Field key={name} label={name} id={`rot-${psp.id}-${name}`}>\n <Input\n id={adminId(`rot-${psp.id}-${name}`)}\n type=\"password\"\n autoComplete=\"new-password\"\n placeholder={\n current?.configured ? \"unchanged\" : \"not configured\"\n }\n value={field.state.value[name] ?? \"\"}\n onChange={(event) =>\n field.handleChange({\n ...field.state.value,\n [name]: event.target.value,\n })\n }\n />\n {current?.validated_at && (\n <p className=\"text-xs text-muted-foreground\">\n <AdminMessage id={\"Last validated \"} />\n {formatDate(current.validated_at)}\n </p>\n )}\n </Field>\n )\n })}\n <p className=\"text-xs text-muted-foreground\">\n <AdminMessage\n id={\n \"Once this succeeds, the new credential is used for the next charge and every one after it. Previous webhook signing secrets remain accepted while their required overlap is active.\"\n }\n />\n </p>\n </div>\n )}\n </form.Field>\n <DialogFooter>\n <form.Subscribe\n selector={(state) =>\n [state.values.credentials, state.isSubmitting] as const\n }\n >\n {([values, isSubmitting]) => (\n <Button\n type=\"submit\"\n disabled={\n isSubmitting ||\n !Object.values(values).some((value) => value.trim())\n }\n >\n {isSubmitting ? \"Validating…\" : \"Validate & rotate\"}\n </Button>\n )}\n </form.Subscribe>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n\n// Where the account ID comes from differs per rail, and the merchant reads it\n// off a different dashboard each time. Naming that is the only way this field\n// is answerable without a support ticket.\nfunction accountIDHint(rail: string): string | undefined {\n switch (rail) {\n case \"nmi\":\n return \"The Gateway ID shown in your NMI dashboard.\"\n case \"stripe\":\n return \"Your Stripe account ID. It starts with acct_.\"\n case \"ccbill\":\n return \"Your account and sub-account joined with a dash, such as 999999-0000.\"\n case \"solana\":\n return \"Taken from your signing key, so whatever you enter here is ignored.\"\n default:\n return undefined\n }\n}\n\n// What each environment means for money, in the merchant's words.\nconst ENVIRONMENT_LABELS: Record<string, string> = {\n live: \"Live: real money\",\n test: \"Test: the provider's own system, no money moves\",\n sandbox: \"Sandbox: the provider's separate sandbox\",\n}\n\nconst RAIL_NAMES: Record<string, string> = {\n nmi: \"NMI\",\n ccbill: \"CCBill\",\n stripe: \"Stripe\",\n solana: \"Solana\",\n}\n\n// Several rails carry the same display name (\"Credit Card\"), so the rail has to\n// stay visible or the list offers the same option twice.\nfunction railProviderLabel(rail: string, displayName: string): string {\n const railName = RAIL_NAMES[rail] ?? rail\n if (!displayName || displayName.toLowerCase() === railName.toLowerCase()) {\n return railName\n }\n return `${displayName} (${railName})`\n}\n\n// Credential names arrive as storage keys. Read them as words, and keep the\n// acronyms the merchant sees on the provider's own dashboard.\nconst CREDENTIAL_ACRONYMS: Record<string, string> = {\n api: \"API\",\n id: \"ID\",\n url: \"URL\",\n}\n\nfunction credentialLabel(name: string): string {\n const words = name.split(/[_-]+/).filter(Boolean)\n return words\n .map((word, index) => {\n const acronym = CREDENTIAL_ACRONYMS[word.toLowerCase()]\n if (acronym) return acronym\n if (index > 0) return word.toLowerCase()\n return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()\n })\n .join(\" \")\n}\n\nfunction PSPDialog({ railDefinitions }: { railDefinitions: RailDefinition[] }) {\n const { toast, toastApiError, adminMutations, selectedMerchant } =\n useAdminServices()\n\n const { text: adminText, id: adminId } = useAdminUI()\n\n const [open, setOpen] = React.useState(false)\n const [merchant] = React.useState(() => selectedMerchant() ?? \"\")\n const queryClient = useQueryClient()\n const createPSP = useMutation(adminMutations.createPSP(queryClient))\n const form = useForm({\n defaultValues: {\n rail: \"\",\n environment: \"\",\n key: \"\",\n accountID: \"\",\n credentials: {} as Record<string, string>,\n },\n onSubmit: async ({ value }) => {\n const credentials = Object.fromEntries(\n Object.entries(value.credentials).filter(([, item]) => item !== \"\")\n )\n try {\n if ((selectedMerchant() ?? \"\") !== merchant)\n throw new Error(\"Merchant changed; reopen this form\")\n await createPSP.mutateAsync({\n key: value.key.trim().toLowerCase(),\n rail: value.rail as PSP[\"rail\"],\n environment: value.environment,\n account_id: value.accountID.trim(),\n ...(Object.keys(credentials).length ? { credentials } : {}),\n })\n form.reset()\n toast.success(\"PSP added\")\n setOpen(false)\n } catch (err) {\n toastApiError(err, \"Add PSP\")\n } finally {\n createPSP.reset()\n }\n },\n })\n\n const handleOpenChange = (next: boolean) => {\n if (!next) form.reset()\n setOpen(next)\n }\n\n return (\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <DialogTrigger\n render={\n <Button size=\"sm\">\n <AdminMessage id={\"Add PSP\"} />\n </Button>\n }\n />\n <DialogContent className={DIALOG_FORM}>\n <DialogHeader>\n <DialogTitle>\n <AdminMessage id={\"Add a PSP\"} />\n </DialogTitle>\n <DialogDescription>\n <AdminMessage\n id={\n \"Connect the account that will take money for you. Credentials go straight into the secret store and are never shown again.\"\n }\n />\n </DialogDescription>\n </DialogHeader>\n <form\n onSubmit={(event) => {\n event.preventDefault()\n event.stopPropagation()\n void form.handleSubmit()\n }}\n className=\"grid gap-4\"\n >\n <div className=\"grid gap-3\">\n <form.Field\n name=\"rail\"\n validators={{\n onChange: ({ value }) =>\n value ? undefined : \"Choose a payment rail\",\n }}\n >\n {(field) => (\n <Field label={adminText(\"Payment rail\")} id={\"pv-rail\"}>\n <Select\n items={railDefinitions.map((definition) => ({\n value: definition.rail,\n label: railProviderLabel(\n definition.rail,\n definition.display_name\n ),\n }))}\n value={field.state.value || null}\n onValueChange={(value) => {\n field.handleChange(value ?? \"\")\n // Each rail asks for different credentials and has its\n // own environments, so neither carries over.\n form.setFieldValue(\"credentials\", {})\n form.setFieldValue(\"environment\", \"\")\n }}\n >\n <SelectTrigger\n id={adminId(\"pv-rail\")}\n className=\"w-full\"\n aria-invalid={field.state.meta.errors.length > 0}\n >\n <SelectValue placeholder={adminText(\"Pick a rail…\")} />\n </SelectTrigger>\n <SelectContent>\n {railDefinitions.map((definition) => (\n <SelectItem\n key={definition.rail}\n value={definition.rail}\n >\n {railProviderLabel(\n definition.rail,\n definition.display_name\n )}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <FormFieldErrors errors={field.state.meta.errors} />\n </Field>\n )}\n </form.Field>\n <form.Subscribe selector={(state) => state.values.rail}>\n {(rail) => {\n const environments =\n railDefinitions.find((definition) => definition.rail === rail)\n ?.environments ?? []\n return (\n <form.Field\n name=\"environment\"\n validators={{\n onChange: ({ value }) =>\n value\n ? undefined\n : \"Choose whether it moves real money\",\n }}\n >\n {(field) => (\n <Field\n label={adminText(\"Environment\")}\n id={\"pv-environment\"}\n hint=\"Whether this PSP moves real money. It is checked with the provider before the PSP is used.\"\n >\n <Select\n items={environments.map((environment) => ({\n value: environment,\n label: adminText(\n ENVIRONMENT_LABELS[environment] ?? environment\n ),\n }))}\n value={field.state.value || null}\n onValueChange={(value) =>\n field.handleChange(value ?? \"\")\n }\n >\n <SelectTrigger\n id={adminId(\"pv-environment\")}\n className=\"w-full\"\n disabled={!rail}\n aria-invalid={field.state.meta.errors.length > 0}\n >\n <SelectValue\n placeholder={adminText(\"Pick an environment…\")}\n />\n </SelectTrigger>\n <SelectContent>\n {environments.map((environment) => (\n <SelectItem key={environment} value={environment}>\n {adminText(\n ENVIRONMENT_LABELS[environment] ?? environment\n )}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <FormFieldErrors errors={field.state.meta.errors} />\n </Field>\n )}\n </form.Field>\n )\n }}\n </form.Subscribe>\n <form.Field\n name=\"key\"\n validators={{\n onChange: ({ value }) =>\n /^[a-z0-9][a-z0-9_-]{0,62}$/.test(value.trim().toLowerCase())\n ? undefined\n : \"Lowercase letters, digits, - or _\",\n }}\n >\n {(field) => (\n <Field\n label={adminText(\"Key\")}\n id={\"pv-key\"}\n hint=\"Your name for this PSP, such as mobius. Prices and purchase name it by this key.\"\n >\n <Input\n id={adminId(\"pv-key\")}\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) => field.handleChange(event.target.value)}\n aria-invalid={field.state.meta.errors.length > 0}\n />\n <FormFieldErrors errors={field.state.meta.errors} />\n </Field>\n )}\n </form.Field>\n <form.Subscribe selector={(state) => state.values.rail}>\n {(rail) => (\n <form.Field\n name=\"accountID\"\n validators={{\n onChange: ({ value }) =>\n value.trim()\n ? undefined\n : \"Enter the provider account ID\",\n }}\n >\n {(field) => (\n <Field\n label={adminText(\"Account ID\")}\n id={\"pv-acct\"}\n hint={accountIDHint(rail)}\n >\n <Input\n id={adminId(\"pv-acct\")}\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) =>\n field.handleChange(event.target.value)\n }\n aria-invalid={field.state.meta.errors.length > 0}\n />\n <FormFieldErrors errors={field.state.meta.errors} />\n </Field>\n )}\n </form.Field>\n )}\n </form.Subscribe>\n <form.Subscribe selector={(state) => state.values.rail}>\n {(rail) => {\n const selectedProvider = railDefinitions.find(\n (definition) => definition.rail === rail\n )\n return (\n <form.Field name=\"credentials\">\n {(field) => (\n <>\n {selectedProvider?.credential_keys.map((name) => (\n <Field\n key={name}\n label={credentialLabel(name)}\n id={`pv-credential-${name}`}\n >\n <Input\n id={adminId(`pv-credential-${name}`)}\n type=\"password\"\n autoComplete=\"new-password\"\n value={field.state.value[name] ?? \"\"}\n onChange={(event) =>\n field.handleChange({\n ...field.state.value,\n [name]: event.target.value,\n })\n }\n />\n </Field>\n ))}\n </>\n )}\n </form.Field>\n )\n }}\n </form.Subscribe>\n </div>\n <DialogFooter>\n <form.Subscribe\n selector={(state) =>\n [\n state.values.rail,\n state.values.environment,\n state.values.key,\n state.values.accountID,\n state.canSubmit,\n state.isSubmitting,\n ] as const\n }\n >\n {([\n rail,\n environment,\n key,\n accountID,\n canSubmit,\n isSubmitting,\n ]) => (\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => handleOpenChange(false)}\n >\n <AdminMessage id={\"Cancel\"} />\n </Button>\n <Button\n type=\"submit\"\n disabled={\n !rail ||\n !environment ||\n !key.trim() ||\n !accountID.trim() ||\n !canSubmit ||\n isSubmitting\n }\n >\n {isSubmitting ? \"Saving…\" : \"Add PSP\"}\n </Button>\n </>\n )}\n </form.Subscribe>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n\nfunction CustomerControlsTab() {\n const { toast, toastApiError, adminMutations } = useAdminServices()\n\n const { text: adminText, id: adminId } = useAdminUI()\n\n const [result, setResult] = React.useState<{\n customerID: string\n currency: string\n creditLimit: string\n trustLevel: string\n }>()\n const lookupControls = useMutation(adminMutations.lookupCustomerControls())\n const updateCreditLimit = useMutation(adminMutations.setCreditLimit())\n const canUpdate = useAdminUpdates()\n const creditForm = useForm({\n defaultValues: { newLimit: \"\" },\n onSubmit: async ({ value }) => {\n if (!result) return\n const scale = currencyScale(result.currency)\n const amount =\n scale === undefined ? null : amountFromInput(value.newLimit, scale)\n if (amount === null || amount.startsWith(\"-\")) return\n try {\n await updateCreditLimit.mutateAsync({\n customerId: result.customerID,\n currency: result.currency,\n amount,\n })\n setResult((current) =>\n current ? { ...current, creditLimit: amount } : current\n )\n creditForm.reset()\n toast.success(\"Credit limit updated\")\n } catch (err) {\n toastApiError(err, \"Set credit limit\")\n }\n },\n })\n const lookupForm = useForm({\n defaultValues: { customerID: \"\", currency: \"usd\" },\n onSubmit: async ({ value }) => {\n const customerID = value.customerID.trim()\n const currency = value.currency.trim().toLowerCase()\n try {\n const controls = await lookupControls.mutateAsync({\n customerId: customerID,\n currency,\n })\n setResult({ customerID, ...controls })\n creditForm.reset()\n } catch (err) {\n toastApiError(err, \"Lookup customer controls\")\n setResult(undefined)\n }\n },\n })\n\n return (\n <div className=\"grid gap-10\">\n <section className=\"grid gap-5\">\n <div className=\"grid gap-1\">\n <h2 className=\"text-base font-semibold\">\n <AdminMessage id={\"Customer lookup\"} />\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"Find a customer by ID and currency.\"} />\n </p>\n </div>\n <form\n onSubmit={(event) => {\n event.preventDefault()\n event.stopPropagation()\n void lookupForm.handleSubmit()\n }}\n className=\"grid gap-4 md:grid-cols-[minmax(0,1fr)_10rem_auto] md:items-end\"\n >\n <lookupForm.Field\n name=\"customerID\"\n validators={{\n onChange: ({ value }) =>\n value.trim() ? undefined : \"Enter a customer ID\",\n }}\n >\n {(field) => (\n <Field\n label={adminText(\"Customer ID\")}\n id={\"customer-controls-id\"}\n >\n <Input\n id={adminId(\"customer-controls-id\")}\n placeholder={adminText(\"Customer UUID\")}\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) => field.handleChange(event.target.value)}\n aria-invalid={field.state.meta.errors.length > 0}\n />\n <FormFieldErrors errors={field.state.meta.errors} />\n </Field>\n )}\n </lookupForm.Field>\n <lookupForm.Field\n name=\"currency\"\n validators={{\n onChange: ({ value }) =>\n value.trim() ? undefined : \"Enter a currency\",\n }}\n >\n {(field) => (\n <Field\n label={adminText(\"Currency\")}\n id={\"customer-controls-currency\"}\n >\n <Input\n id={adminId(\"customer-controls-currency\")}\n placeholder={adminText(\"USD\")}\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) => field.handleChange(event.target.value)}\n aria-invalid={field.state.meta.errors.length > 0}\n />\n <FormFieldErrors errors={field.state.meta.errors} />\n </Field>\n )}\n </lookupForm.Field>\n <lookupForm.Subscribe\n selector={(state) =>\n [\n state.values.customerID,\n state.values.currency,\n state.canSubmit,\n state.isSubmitting,\n ] as const\n }\n >\n {([customerID, currency, canSubmit, isSubmitting]) => (\n <Button\n type=\"submit\"\n variant=\"outline\"\n disabled={\n !customerID.trim() ||\n !currency.trim() ||\n !canSubmit ||\n isSubmitting\n }\n >\n {isSubmitting ? \"Looking up…\" : \"Look up\"}\n </Button>\n )}\n </lookupForm.Subscribe>\n </form>\n </section>\n\n {result && (\n <section className=\"grid gap-5\">\n <div className=\"grid gap-1\">\n <h2 className=\"text-base font-semibold\">\n <AdminMessage id={\"Credit and trust\"} />\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n <span className=\"font-mono text-xs break-all text-foreground\">\n {result.customerID}\n </span>{\" \"}\n · {result.currency.toUpperCase()}\n </p>\n </div>\n <dl className=\"grid gap-4\">\n <SettingDetail\n label={adminText(\"Trust level\")}\n value={result.trustLevel || \"Default\"}\n />\n <SettingDetail\n label={adminText(\"Credit limit\")}\n value={\n result.creditLimit !== \"0\"\n ? formatNativeAmount(result.creditLimit, result.currency)\n : \"Off\"\n }\n />\n </dl>\n {canUpdate && (\n <form\n onSubmit={(event) => {\n event.preventDefault()\n event.stopPropagation()\n void creditForm.handleSubmit()\n }}\n >\n <SettingEditField\n label={`New limit (${result.currency.toUpperCase()})`}\n id={\"customer-controls-limit\"}\n >\n <creditForm.Field\n name=\"newLimit\"\n validators={{\n onChange: ({ value }) => {\n const scale = currencyScale(result.currency)\n const amount =\n scale === undefined\n ? null\n : amountFromInput(value, scale)\n return value !== \"\" &&\n amount !== null &&\n !amount.startsWith(\"-\")\n ? undefined\n : \"Enter a valid amount\"\n },\n }}\n >\n {(field) => (\n <div className=\"grid gap-1.5\">\n <div className=\"flex gap-2\">\n <Input\n id={adminId(\"customer-controls-limit\")}\n placeholder=\"0.00\"\n type=\"number\"\n step=\"any\"\n min=\"0\"\n value={field.state.value}\n onBlur={field.handleBlur}\n onChange={(event) =>\n field.handleChange(event.target.value)\n }\n aria-invalid={field.state.meta.errors.length > 0}\n />\n <creditForm.Subscribe\n selector={(state) =>\n [\n state.values.newLimit,\n state.canSubmit,\n state.isSubmitting,\n ] as const\n }\n >\n {([newLimit, canSubmit, isSubmitting]) => (\n <Button\n type=\"submit\"\n disabled={!newLimit || !canSubmit || isSubmitting}\n >\n {isSubmitting ? \"Updating…\" : \"Update\"}\n </Button>\n )}\n </creditForm.Subscribe>\n </div>\n <FormFieldErrors errors={field.state.meta.errors} />\n <p className=\"text-xs text-muted-foreground\">\n <AdminMessage id={\"Enter 0 to turn credit off.\"} />\n </p>\n </div>\n )}\n </creditForm.Field>\n </SettingEditField>\n </form>\n )}\n </section>\n )}\n </div>\n )\n}\n\n// hint carries the invisible constraint: where a value comes from, or what\n// happens if it is left empty. Anything the control already says is left out.\nfunction Field({\n label,\n id,\n hint,\n children,\n}: {\n label: string\n id: string\n hint?: string\n children: React.ReactNode\n}) {\n const { id: adminId } = useAdminUI()\n\n return (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={adminId(id)}>{label}</Label>\n {hint ? (\n <p className=\"text-[13px] text-muted-foreground\">{hint}</p>\n ) : null}\n {children}\n </div>\n )\n}\n"],"mappings":"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"}
package/dist/admin.js CHANGED
@@ -64,7 +64,7 @@ var ze = [
64
64
  },
65
65
  {
66
66
  path: "/catalog/prices",
67
- Component: A(() => import("./admin-catalog-3pPEAuDY.js").then((e) => ({ default: e.CatalogPricesPage })))
67
+ Component: A(() => import("./admin-catalog-Bsxo2gyS.js").then((e) => ({ default: e.CatalogPricesPage })))
68
68
  },
69
69
  {
70
70
  path: "/subscriptions",
@@ -72,7 +72,7 @@ var ze = [
72
72
  },
73
73
  {
74
74
  path: "/catalog/drift",
75
- Component: A(() => import("./admin-catalog-3pPEAuDY.js").then((e) => ({ default: e.CatalogDriftPage })))
75
+ Component: A(() => import("./admin-catalog-Bsxo2gyS.js").then((e) => ({ default: e.CatalogDriftPage })))
76
76
  },
77
77
  {
78
78
  path: "/customers",
@@ -84,7 +84,7 @@ var ze = [
84
84
  },
85
85
  {
86
86
  path: "/settings",
87
- Component: A(() => import("./admin-settings-C6eWFkDn.js").then((e) => ({ default: e.SettingsPage })))
87
+ Component: A(() => import("./admin-settings-BIUgvclv.js").then((e) => ({ default: e.SettingsPage })))
88
88
  },
89
89
  {
90
90
  path: "/reports",
@@ -92,7 +92,7 @@ var ze = [
92
92
  },
93
93
  {
94
94
  path: "/catalog",
95
- Component: A(() => import("./admin-catalog-3pPEAuDY.js").then((e) => ({ default: e.CatalogProductsPage })))
95
+ Component: A(() => import("./admin-catalog-Bsxo2gyS.js").then((e) => ({ default: e.CatalogProductsPage })))
96
96
  },
97
97
  {
98
98
  path: "/orders",
@@ -124,7 +124,7 @@ var ze = [
124
124
  },
125
125
  {
126
126
  path: "/catalog/prices/:id",
127
- Component: A(() => import("./admin-price-detail-BeGDPxKX.js").then((e) => ({ default: e.PriceDetailPage })))
127
+ Component: A(() => import("./admin-price-detail-ByCmrx_W.js").then((e) => ({ default: e.PriceDetailPage })))
128
128
  },
129
129
  {
130
130
  path: "/subscriptions/:id",
@@ -1587,6 +1587,7 @@ var bn = {
1587
1587
  "Billing contacts": "Billing contacts",
1588
1588
  "Billing interval": "Billing interval",
1589
1589
  "Billing support": "Billing support",
1590
+ "Billing URL": "Billing URL",
1590
1591
  "Can this be bought?": "Can this be bought?",
1591
1592
  Cancel: "Cancel",
1592
1593
  "Cancel subscription": "Cancel subscription",
@@ -1922,6 +1923,10 @@ var bn = {
1922
1923
  "Pick a product": "Pick a product",
1923
1924
  "Pick a product…": "Pick a product…",
1924
1925
  "Pick a rail…": "Pick a rail…",
1926
+ "Pick an environment…": "Pick an environment…",
1927
+ "Live: real money": "Live: real money",
1928
+ "Test: the provider's own system, no money moves": "Test: the provider's own system, no money moves",
1929
+ "Sandbox: the provider's separate sandbox": "Sandbox: the provider's separate sandbox",
1925
1930
  "Pick a stored payment method": "Pick a stored payment method",
1926
1931
  "Pinned to an archived (prior) version": "Pinned to an archived (prior) version",
1927
1932
  "Points future renewals of this subscription at another of the customer&apos;s stored cards (same rail), or back at their default card for its currency.": "Points future renewals of this subscription at another of the customer&apos;s stored cards (same rail), or back at their default card for its currency.",
@@ -2046,6 +2051,7 @@ var bn = {
2046
2051
  Subscriptions: "Subscriptions",
2047
2052
  "Subscriptions started and canceled each day (UTC), newest first.": "Subscriptions started and canceled each day (UTC), newest first.",
2048
2053
  "Sum a numeric field": "Sum a numeric field",
2054
+ "Subscribe URL": "Subscribe URL",
2049
2055
  "Support URL": "Support URL",
2050
2056
  Synced: "Synced",
2051
2057
  "Take the product back": "Take the product back",
@@ -2133,7 +2139,9 @@ var bn = {
2133
2139
  "eu, us": "eu, us",
2134
2140
  "gpt-5": "gpt-5",
2135
2141
  "held ": "held ",
2142
+ "https://example.com/account": "https://example.com/account",
2136
2143
  "https://example.com/logo.png": "https://example.com/logo.png",
2144
+ "https://example.com/subscribe": "https://example.com/subscribe",
2137
2145
  "https://example.com/support": "https://example.com/support",
2138
2146
  "key: ": "key: ",
2139
2147
  "metadata.model": "metadata.model",