@openrails/billing-ui 0.251.0 → 0.252.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/CHANGELOG.md +9 -0
- package/dist/admin/messages.d.ts +8 -0
- package/dist/{admin-catalog-3pPEAuDY.js → admin-catalog-Bsxo2gyS.js} +2 -2
- package/dist/{admin-catalog-3pPEAuDY.js.map → admin-catalog-Bsxo2gyS.js.map} +1 -1
- package/dist/{admin-price-detail-BeGDPxKX.js → admin-price-detail-ByCmrx_W.js} +2 -3
- package/dist/admin-price-detail-ByCmrx_W.js.map +1 -0
- package/dist/{admin-price-wizard-ferZSTfO.js → admin-price-wizard-m7Qjhq6W.js} +2 -2
- package/dist/admin-price-wizard-m7Qjhq6W.js.map +1 -0
- package/dist/{admin-settings-C6eWFkDn.js → admin-settings-BIUgvclv.js} +99 -17
- package/dist/admin-settings-BIUgvclv.js.map +1 -0
- package/dist/admin.js +13 -5
- package/dist/admin.js.map +1 -1
- package/dist/client/generated/error-codes.d.ts +1 -1
- package/dist/client/generated/routes.d.ts +1 -1
- package/dist/client/generated/wire.d.ts +6 -6
- package/package.json +1 -2
- package/dist/admin-price-detail-BeGDPxKX.js.map +0 -1
- package/dist/admin-price-wizard-ferZSTfO.js.map +0 -1
- package/dist/admin-settings-C6eWFkDn.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type OpenRailsErrorCode = "admission_captured" | "admission_not_found" | "already_owned" | "application_required" | "as_of_required" | "authentication_required" | "authentication_unavailable" | "authorization_unavailable" | "billing_archive_integrity" | "billing_archive_invalid_artifact" | "billing_archive_merchant_mismatch" | "billing_archive_not_empty" | "billing_archive_platform_cards" | "billing_archive_unavailable" | "billing_archive_unsupported_state" | "billing_policy_not_found" | "cancel_unsupported_on_rail" | "captcha_invalid" | "captcha_required" | "card_attempts_blocked" | "card_custody_reserved" | "card_declined" | "card_not_saved" | "card_requires_https" | "catalog_benefit_overlap" | "catalog_revision_conflict" | "credential_expired" | "credential_revoked" | "credit_grant_held" | "credit_grant_not_found" | "credit_grant_unavailable" | "csrf_origin_denied" | "currency_unsupported" | "custodian_capture_unavailable" | "customer_action_required" | "customer_email_required" | "customer_issuer_mismatch" | "customer_not_found" | "customer_payment_unsupported" | "customer_session_required" | "dashboard_invalid" | "database_busy" | "default_payment_method_invalid" | "default_payment_method_required" | "default_rate_card_not_found" | "destructive_actions_held" | "embed_origins_required" | "entitlement_required" | "finding_action_failed" | "finding_not_actionable" | "finding_requires_merchant_config" | "hold_not_found" | "host_event_not_found" | "host_principal_invalid" | "idempotency_key_in_use" | "idempotency_key_required" | "idempotency_key_reused" | "insufficient_credits" | "insufficient_funds" | "internal_error" | "invalid_cursor" | "invalid_customer_id" | "invalid_host_event_request" | "invalid_param" | "invalid_payment_method" | "invalid_psp_reference" | "invalid_query" | "invalid_request_body" | "invalid_settlement_status_request" | "invoker_scoped_principal" | "merchant_config_read_only" | "merchant_mismatch" | "merchant_unresolved" | "meter_in_use" | "meter_rate_card_conflict" | "method_not_allowed" | "metrics_query_invalid" | "model_unavailable" | "network_account_linked" | "network_link_challenge_invalid" | "network_link_conflict" | "network_link_in_use" | "network_terms_required" | "order_action_not_allowed" | "order_attempt_closed" | "order_attempt_expired" | "order_has_recurring_line" | "order_line_unavailable" | "order_not_cancelable" | "order_not_payable" | "order_payment_in_progress" | "order_total_changed" | "pay_url_attempts_exhausted" | "pay_url_expired" | "pay_url_not_found" | "pay_url_unavailable" | "payment_duplicate_refused" | "payment_exceeds_due" | "payment_failed" | "payment_idempotency_conflict" | "payment_in_progress" | "payment_method_delete_failed" | "payment_method_delete_unsupported" | "payment_method_not_psp_vaulted" | "payment_method_not_usable" | "payment_method_psp_mismatch" | "payment_method_required" | "payment_method_same_vault" | "payment_method_stale" | "payment_method_update_failed" | "payment_method_update_unsupported" | "payment_not_found" | "payment_not_refundable" | "payment_not_retryable" | "payment_option_unavailable" | "payment_option_withheld" | "payment_provider_rejected" | "permission_required" | "platform_vault_unavailable" | "platform_vault_unreachable" | "price_change_currency_mismatch" | "price_change_target_archived" | "price_increase_notice_too_short" | "price_key_cadence_conflict" | "price_key_not_found" | "price_migration_not_found" | "price_not_found" | "price_not_sellable" | "product_in_use" | "product_not_found" | "product_tier_group_conflict" | "product_tier_group_in_use" | "provider_cancel_held" | "provider_outcome_unknown" | "provider_writes_held" | "psp_claim_requires_proof" | "psp_credentials_rejected" | "psp_exists" | "psp_key_taken" | "psp_last_active" | "psp_not_found" | "purchase_review_resolved" | "quantity_not_allowed" | "rate_card_product_not_found" | "rate_limit_exceeded" | "rebill_terms_committed" | "refund_failed" | "refund_rail_unavailable" | "refund_unsupported" | "request_body_too_large" | "resource_access_denied" | "resource_conflict" | "resource_not_found" | "revision_mismatch" | "route_not_found" | "running_charge_not_found" | "scheduled_change_exists" | "service_credential_customer_scope_denied" | "service_unavailable" | "solana_rpc_unavailable" | "solana_transaction_refused" | "step_up_required" | "step_up_unavailable" | "stored_credential_required" | "subscription_change_already_scheduled" | "subscription_change_cadence_unsupported" | "subscription_change_credit_exceeds_price" | "subscription_change_cycle_unknown" | "subscription_change_idempotency_conflict" | "subscription_change_idempotency_key_required" | "subscription_change_in_flight" | "subscription_change_period_unknown" | "subscription_change_provider_conflict" | "subscription_change_refused" | "subscription_change_renewal_due" | "subscription_change_requires_linked_plan" | "subscription_change_target_inactive" | "subscription_change_unsupported_on_rail" | "subscription_not_active" | "subscription_not_found" | "subscription_paid_through" | "subscription_resumable" | "trial_unsupported_on_rail" | "unknown_field" | "unsupported_media_type" | "usage_meter_invalid" | "usage_meter_not_found" | "usage_not_priced" | "usage_rate_card_invalid" | "webhook_account_mismatch" | "webhook_invalid";
|
|
1
|
+
export type OpenRailsErrorCode = "admission_captured" | "admission_not_found" | "already_owned" | "application_required" | "as_of_required" | "authentication_required" | "authentication_unavailable" | "authorization_unavailable" | "billing_archive_integrity" | "billing_archive_invalid_artifact" | "billing_archive_merchant_mismatch" | "billing_archive_not_empty" | "billing_archive_platform_cards" | "billing_archive_unavailable" | "billing_archive_unsupported_state" | "billing_policy_not_found" | "cancel_unsupported_on_rail" | "captcha_invalid" | "captcha_required" | "card_attempts_blocked" | "card_custody_reserved" | "card_declined" | "card_not_saved" | "card_requires_https" | "catalog_benefit_overlap" | "catalog_revision_conflict" | "credential_expired" | "credential_revoked" | "credit_grant_held" | "credit_grant_not_found" | "credit_grant_unavailable" | "csrf_origin_denied" | "currency_unsupported" | "custodian_capture_unavailable" | "customer_action_required" | "customer_email_required" | "customer_issuer_mismatch" | "customer_not_found" | "customer_payment_unsupported" | "customer_session_required" | "dashboard_invalid" | "database_busy" | "default_payment_method_invalid" | "default_payment_method_required" | "default_rate_card_not_found" | "destructive_actions_held" | "embed_origins_required" | "entitlement_required" | "finding_action_failed" | "finding_not_actionable" | "finding_requires_merchant_config" | "hold_not_found" | "host_event_not_found" | "host_principal_invalid" | "idempotency_key_in_use" | "idempotency_key_required" | "idempotency_key_reused" | "insufficient_credits" | "insufficient_funds" | "internal_error" | "invalid_cursor" | "invalid_customer_id" | "invalid_host_event_request" | "invalid_param" | "invalid_payment_method" | "invalid_psp_reference" | "invalid_query" | "invalid_request_body" | "invalid_settlement_status_request" | "invoker_scoped_principal" | "merchant_config_read_only" | "merchant_mismatch" | "merchant_unresolved" | "meter_in_use" | "meter_rate_card_conflict" | "method_not_allowed" | "metrics_query_invalid" | "model_unavailable" | "network_account_linked" | "network_link_challenge_invalid" | "network_link_conflict" | "network_link_in_use" | "network_terms_required" | "order_action_not_allowed" | "order_attempt_closed" | "order_attempt_expired" | "order_has_recurring_line" | "order_line_unavailable" | "order_not_cancelable" | "order_not_payable" | "order_payment_in_progress" | "order_total_changed" | "pay_url_attempts_exhausted" | "pay_url_expired" | "pay_url_not_found" | "pay_url_unavailable" | "payment_duplicate_refused" | "payment_exceeds_due" | "payment_failed" | "payment_idempotency_conflict" | "payment_in_progress" | "payment_method_delete_failed" | "payment_method_delete_unsupported" | "payment_method_not_psp_vaulted" | "payment_method_not_usable" | "payment_method_psp_mismatch" | "payment_method_required" | "payment_method_same_vault" | "payment_method_stale" | "payment_method_update_failed" | "payment_method_update_unsupported" | "payment_not_found" | "payment_not_refundable" | "payment_not_retryable" | "payment_option_unavailable" | "payment_option_withheld" | "payment_provider_rejected" | "permission_required" | "platform_vault_unavailable" | "platform_vault_unreachable" | "price_change_currency_mismatch" | "price_change_target_archived" | "price_increase_notice_too_short" | "price_key_cadence_conflict" | "price_key_not_found" | "price_migration_not_found" | "price_not_found" | "price_not_sellable" | "product_in_use" | "product_not_found" | "product_tier_group_conflict" | "product_tier_group_in_use" | "provider_cancel_held" | "provider_outcome_unknown" | "provider_writes_held" | "psp_claim_requires_proof" | "psp_credentials_rejected" | "psp_environment_mixed" | "psp_exists" | "psp_key_taken" | "psp_last_active" | "psp_not_found" | "purchase_review_resolved" | "quantity_not_allowed" | "rate_card_product_not_found" | "rate_limit_exceeded" | "rebill_terms_committed" | "refund_failed" | "refund_rail_unavailable" | "refund_unsupported" | "request_body_too_large" | "resource_access_denied" | "resource_conflict" | "resource_not_found" | "revision_mismatch" | "route_not_found" | "running_charge_not_found" | "scheduled_change_exists" | "service_credential_customer_scope_denied" | "service_unavailable" | "solana_rpc_unavailable" | "solana_transaction_refused" | "step_up_required" | "step_up_unavailable" | "stored_credential_required" | "subscription_change_already_scheduled" | "subscription_change_cadence_unsupported" | "subscription_change_credit_exceeds_price" | "subscription_change_cycle_unknown" | "subscription_change_idempotency_conflict" | "subscription_change_idempotency_key_required" | "subscription_change_in_flight" | "subscription_change_period_unknown" | "subscription_change_provider_conflict" | "subscription_change_refused" | "subscription_change_renewal_due" | "subscription_change_requires_linked_plan" | "subscription_change_target_inactive" | "subscription_change_unsupported_on_rail" | "subscription_not_active" | "subscription_not_found" | "subscription_paid_through" | "subscription_resumable" | "trial_unsupported_on_rail" | "unknown_field" | "unsupported_media_type" | "usage_meter_invalid" | "usage_meter_not_found" | "usage_not_priced" | "usage_rate_card_invalid" | "webhook_account_mismatch" | "webhook_invalid";
|
|
2
2
|
export type OpenRailsErrorType = "api_error" | "authentication_error" | "authorization_error" | "card_error" | "invalid_request_error" | "rate_limit_error";
|
|
3
3
|
export declare const OPENRAILS_ERROR_CODES: Readonly<Record<OpenRailsErrorCode, {
|
|
4
4
|
status: number;
|
|
@@ -1809,7 +1809,7 @@ export declare const OPENRAILS_ROUTES: readonly [{
|
|
|
1809
1809
|
readonly responses: {
|
|
1810
1810
|
readonly 201: "PSP";
|
|
1811
1811
|
};
|
|
1812
|
-
readonly errors: readonly ["invalid_param", "platform_vault_unreachable", "psp_claim_requires_proof", "psp_credentials_rejected", "psp_exists", "psp_key_taken", "revision_mismatch", "service_unavailable"];
|
|
1812
|
+
readonly errors: readonly ["invalid_param", "platform_vault_unreachable", "psp_claim_requires_proof", "psp_credentials_rejected", "psp_environment_mixed", "psp_exists", "psp_key_taken", "revision_mismatch", "service_unavailable"];
|
|
1813
1813
|
readonly errorSets: readonly ["tier:merchant", "request"];
|
|
1814
1814
|
}, {
|
|
1815
1815
|
readonly method: "GET";
|
|
@@ -130,8 +130,7 @@ export type ApplyPrice = {
|
|
|
130
130
|
trial_duration_hours?: number | null;
|
|
131
131
|
customer_amount?: CustomerAmount | null;
|
|
132
132
|
quantity?: Quantity | null;
|
|
133
|
-
psps?: string
|
|
134
|
-
psp_links?: Record<string, Record<string, string>> | null;
|
|
133
|
+
psps?: Record<string, Record<string, string>> | null;
|
|
135
134
|
amount?: string;
|
|
136
135
|
access_duration?: string | null;
|
|
137
136
|
billing_interval?: string | null;
|
|
@@ -460,6 +459,7 @@ export type CreateOrderParams = {
|
|
|
460
459
|
export type CreatePSPParams = {
|
|
461
460
|
key?: string;
|
|
462
461
|
rail?: "ccbill" | "nmi" | "solana" | "stripe";
|
|
462
|
+
environment?: string;
|
|
463
463
|
account_id?: string;
|
|
464
464
|
settings?: Record<string, unknown> | null;
|
|
465
465
|
credentials?: Record<string, string> | null;
|
|
@@ -493,8 +493,7 @@ export type CreatePriceParams = {
|
|
|
493
493
|
billing_interval_hours?: number;
|
|
494
494
|
trial_unit_amount?: string;
|
|
495
495
|
trial_duration_hours?: number;
|
|
496
|
-
psps?: string
|
|
497
|
-
psp_links?: Record<string, Record<string, string>>;
|
|
496
|
+
psps?: Record<string, Record<string, string>>;
|
|
498
497
|
archived?: boolean;
|
|
499
498
|
expected_revision?: number;
|
|
500
499
|
};
|
|
@@ -1031,7 +1030,7 @@ export type MerchantProfile = {
|
|
|
1031
1030
|
logo_url?: string;
|
|
1032
1031
|
from_email?: string;
|
|
1033
1032
|
support_url?: string;
|
|
1034
|
-
|
|
1033
|
+
subscribe_url?: string;
|
|
1035
1034
|
billing_url?: string;
|
|
1036
1035
|
};
|
|
1037
1036
|
export type MerchantSettings = {
|
|
@@ -1733,6 +1732,7 @@ export type RailDefinition = {
|
|
|
1733
1732
|
display_name: string;
|
|
1734
1733
|
credential_keys: string[];
|
|
1735
1734
|
setting_keys: string[];
|
|
1735
|
+
environments: string[];
|
|
1736
1736
|
};
|
|
1737
1737
|
export type RateCard = {
|
|
1738
1738
|
ordinal?: number;
|
|
@@ -2047,7 +2047,7 @@ export type UpdatePaymentMethodParams = {
|
|
|
2047
2047
|
};
|
|
2048
2048
|
export type UpdatePriceParams = {
|
|
2049
2049
|
archived?: boolean | null;
|
|
2050
|
-
|
|
2050
|
+
psps?: Record<string, Record<string, string>>;
|
|
2051
2051
|
expected_revision?: number;
|
|
2052
2052
|
};
|
|
2053
2053
|
export type UpdateProductParams = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openrails/billing-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.252.1",
|
|
4
4
|
"description": "Embeddable OpenRails purchase and account-billing UI: client, React hooks and styled components.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -84,7 +84,6 @@
|
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@eslint/js": "^10.0.1",
|
|
87
|
-
"@microsoft/api-extractor": "^7.58.12",
|
|
88
87
|
"@playwright/test": "1.60.0",
|
|
89
88
|
"@solana/kit": "^6.10.0",
|
|
90
89
|
"@solana/pay": "^1.0.26",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"admin-price-detail-BeGDPxKX.js","names":[],"sources":["../src/admin/pages/catalog/psp-links.tsx","../src/admin/pages/catalog/price-detail.tsx"],"sourcesContent":["import { AdminMessage } from \"#orck/admin/ui-context\"\nimport { useAdminServices } from \"#orck/admin/context\"\nimport { useQuery } from \"@tanstack/react-query\"\n\nimport { Badge } from \"#orck/admin/components/ui/badge\"\nimport { Button } from \"#orck/admin/components/ui/button\"\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from \"#orck/admin/components/ui/card\"\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"#orck/admin/components/ui/table\"\nimport type { PSPRoutingSkip } from \"#orck/admin/lib/api/types\"\nimport type { PSPLinkState, Price } from \"#orck/client/generated/wire\"\n\n// The catalog is declarative: the provider adapter pushes prices and the\n// read-back reconciliation is alert-only, so this surface displays a price's\n// psp_links and never edits them. It answers two different questions:\n// - Link state (`price.providers`): does OpenRails hold a link for this PSP,\n// and does the remote object still match?\n// - Purchase readiness (the routing preview): would a real purchase land\n// here now? A linked price can still be unroutable (PSP unarmed,\n// credentials missing).\n\n// SKIP_LABELS renders the routing skip vocabulary for operators. Keep the keys\n// in lockstep with internal/db/models/order_attempt.go.\nconst SKIP_LABELS: Record<PSPRoutingSkip, string> = {\n unknown_selector: \"That name matches no provider you have set up.\",\n ambiguous_selector:\n \"More than one provider on this rail is set up. Name the provider rather than the rail.\",\n not_armed: \"This provider is not set up here, or it was archived.\",\n credentials_missing:\n \"The provider is set up, but the credentials it needs are missing.\",\n link_missing: \"This price has not been sent to the provider yet.\",\n mode_unsupported:\n \"This provider cannot take this kind of payment (one-off or recurring).\",\n posture_disarmed:\n \"The provider's credentials failed their test-mode check, so it is switched off.\",\n service_unavailable: \"The connection to this provider is not running.\",\n resolve_failed: \"The provider could not be worked out at the time of asking.\",\n}\n\n// The engine names each outcome for its own logs. These are the same outcomes\n// as a sentence you can act on.\nconst SKIP_HEADLINES: Record<PSPRoutingSkip, string> = {\n unknown_selector: \"unknown provider\",\n ambiguous_selector: \"more than one match\",\n not_armed: \"not set up\",\n credentials_missing: \"credentials missing\",\n link_missing: \"not sent to provider\",\n mode_unsupported: \"cannot take this payment\",\n posture_disarmed: \"credentials failed check\",\n service_unavailable: \"connection not running\",\n resolve_failed: \"could not be worked out\",\n}\n\nfunction skipHeadline(skip?: string | null): string {\n if (!skip) return \"ready\"\n return SKIP_HEADLINES[skip as PSPRoutingSkip] ?? skip\n}\n\nfunction skipLabel(skip?: string | null) {\n if (!skip) return \"\"\n return SKIP_LABELS[skip as PSPRoutingSkip] ?? skip\n}\n\nconst OK_BADGE = \"bg-settled-surface text-settled\"\nconst WARN_BADGE = \"bg-held-surface text-held\"\nconst BAD_BADGE = \"bg-failed-surface text-failed\"\n\nfunction linkStatusClass(status: PSPLinkState[\"status\"]) {\n if (status === \"linked\") return OK_BADGE\n if (status === \"error\") return BAD_BADGE\n return WARN_BADGE\n}\n\n// syncStatusClass leaves \"unknown\" neutral, not a warning: nobody has checked\n// yet.\nfunction syncStatusClass(sync?: string) {\n switch (sync) {\n case \"in_sync\":\n return OK_BADGE\n case \"drifted\":\n case \"missing\":\n return BAD_BADGE\n case \"never_synced\":\n case \"sync_disabled\":\n return WARN_BADGE\n default:\n return \"\"\n }\n}\n\n// LINK_ID_ORDER puts the identifying fields first; anything else the adapter\n// stored follows in sorted order. `rail` is pulled out into its own column.\nconst LINK_ID_ORDER = [\n \"price_id\",\n \"product_id\",\n \"plan_id\",\n \"form_name\",\n \"flex_id\",\n \"recurring_billing_option_id\",\n \"lookup_key\",\n \"provider\",\n]\n\nfunction linkEntries(ids?: Record<string, string> | null) {\n const rest = Object.entries(ids ?? {}).filter(\n ([k, v]) => k !== \"rail\" && v !== \"\"\n )\n rest.sort(([a], [b]) => {\n const ia = LINK_ID_ORDER.indexOf(a)\n const ib = LINK_ID_ORDER.indexOf(b)\n if (ia !== ib)\n return (\n (ia < 0 ? LINK_ID_ORDER.length : ia) -\n (ib < 0 ? LINK_ID_ORDER.length : ib)\n )\n return a.localeCompare(b)\n })\n return rest\n}\n\nexport function PSPLinksCard({\n price,\n verifying,\n verified,\n onVerify,\n}: {\n price: Price\n verifying: boolean\n verified: boolean\n onVerify: () => void\n}) {\n const links = Object.entries(price.psps ?? {}).sort(([a], [b]) =>\n a.localeCompare(b)\n )\n return (\n <Card>\n <CardHeader className=\"flex flex-row items-start justify-between gap-2\">\n <div className=\"grid gap-1\">\n <CardTitle className=\"text-sm\">\n <AdminMessage id={\"Payment providers\"} />\n </CardTitle>\n <CardDescription>\n <AdminMessage\n id={\n \"Which providers this price has been sent to, and whether their copy still matches yours. Publishing the catalog is what sends it; this page only reports. Verify asks each provider what it currently holds, and changes nothing.\"\n }\n />\n </CardDescription>\n </div>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={verifying || !links.length}\n onClick={onVerify}\n >\n {verifying ? \"Verifying…\" : \"Verify\"}\n </Button>\n </CardHeader>\n <CardContent className=\"flex flex-col gap-3\">\n {!links.length ? (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage\n id={\n \"This price has not been sent to any provider, so nobody can be charged for it yet.\"\n }\n />\n </p>\n ) : (\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow className=\"hover:bg-transparent\">\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Provider\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Rail\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Sent\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Still matches\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Their reference\"} />\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {links.map(([psp, state]) => (\n <TableRow key={psp}>\n <TableCell className=\"font-mono text-xs\">{psp}</TableCell>\n <TableCell className=\"text-xs\">\n {state.ids?.rail ?? \"—\"}\n </TableCell>\n <TableCell>\n <Badge\n variant=\"secondary\"\n className={linkStatusClass(state.status)}\n >\n {state.status}\n </Badge>\n {state.message && (\n <p className=\"mt-1 max-w-xs text-xs text-muted-foreground\">\n {state.message}\n </p>\n )}\n </TableCell>\n <TableCell>\n <Badge\n variant=\"secondary\"\n className={syncStatusClass(state.sync_status)}\n >\n {state.sync_status ?? \"unknown\"}\n </Badge>\n {state.drift?.map((d) => (\n <p\n key={d.field}\n className=\"mt-1 text-xs text-muted-foreground\"\n >\n {d.field}\n <AdminMessage id={\": yours \"} />\n {d.openrails_value}\n <AdminMessage id={\" · theirs\"} /> {d.remote_value}\n </p>\n ))}\n </TableCell>\n <TableCell className=\"font-mono text-xs\">\n {linkEntries(state.ids).length ? (\n <div className=\"grid gap-0.5\">\n {linkEntries(state.ids).map(([k, v]) => (\n <span key={k}>\n {k}={v}\n </span>\n ))}\n </div>\n ) : (\n \"—\"\n )}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n )}\n {!!price.pending_manual_actions?.length && (\n <div className=\"border-held/40 rounded-md border p-3 text-xs\">\n {price.pending_manual_actions.map((a) => (\n <p key={`${a.psp}-${a.action}`}>\n <span className=\"font-mono\">{a.psp}</span>: {a.hint || a.action}\n </p>\n ))}\n </div>\n )}\n {!verified && !!links.length && (\n <p className=\"text-xs text-muted-foreground\">\n <AdminMessage\n id={\n \"Nothing is claimed about the providers' copies until you press Verify.\"\n }\n />\n </p>\n )}\n </CardContent>\n </Card>\n )\n}\n\n// PSPReadinessCard answers \"can someone buy this right now, and if not,\n// why?\" with the routing preview, so the console and purchase never disagree.\nexport function PSPReadinessCard({ price }: { price: Price }) {\n const { adminQueries } = useAdminServices()\n\n const {\n data: decision,\n isFetching: busy,\n refetch: reload,\n } = useQuery(adminQueries.pspRouting(price.id))\n\n const eligible = decision?.candidates.filter((c) => !c.skip) ?? []\n\n return (\n <Card>\n <CardHeader className=\"flex flex-row items-start justify-between gap-2\">\n <div className=\"grid gap-1\">\n <CardTitle className=\"text-sm\">\n <AdminMessage id={\"Can this be bought?\"} />\n </CardTitle>\n <CardDescription>\n <AdminMessage\n id={\n \"Which provider would take the money if someone bought this right now, and why the others were passed over. This asks the same question a real purchase asks, without starting one.\"\n }\n />\n </CardDescription>\n </div>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n onClick={() => void reload()}\n >\n {busy ? \"Checking…\" : \"Re-check\"}\n </Button>\n </CardHeader>\n <CardContent className=\"flex flex-col gap-3\">\n {!decision ? (\n <p className=\"text-sm text-muted-foreground\">\n {busy ? \"Checking…\" : \"This check could not be run.\"}\n </p>\n ) : (\n <>\n <p className=\"text-sm\">\n {decision.psp ? (\n <>\n <AdminMessage id={\"Money for this price would go to\"} />{\" \"}\n <span className=\"font-medium\">{decision.psp}</span>\n {decision.rail && (\n <>\n <AdminMessage id={\" on \"} />\n {decision.rail}\n </>\n )}\n .\n </>\n ) : (\n <span className=\"text-held\">\n <AdminMessage\n id={\n \"No provider can take this payment, so nobody can buy this price yet.\"\n }\n />\n </span>\n )}\n </p>\n {!decision.candidates.length ? (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"There were no providers to consider.\"} />\n </p>\n ) : (\n <div className=\"overflow-x-auto\">\n <Table>\n <TableHeader>\n <TableRow className=\"hover:bg-transparent\">\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Provider\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Rail\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Outcome\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Why\"} />\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {decision.candidates.map((c) => (\n <TableRow key={c.psp}>\n <TableCell className=\"font-mono text-xs\">\n {c.psp}\n {c.psp === decision.psp && (\n <Badge\n variant=\"secondary\"\n className={`ml-2 ${OK_BADGE}`}\n >\n <AdminMessage id={\"selected\"} />\n </Badge>\n )}\n </TableCell>\n <TableCell className=\"text-xs\">\n {c.rail || \"—\"}\n </TableCell>\n <TableCell>\n <Badge\n variant=\"secondary\"\n className={c.skip ? WARN_BADGE : OK_BADGE}\n >\n {skipHeadline(c.skip)}\n </Badge>\n </TableCell>\n <TableCell className=\"text-xs text-muted-foreground\">\n {skipLabel(c.skip)}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n )}\n {!!decision.candidates.length && eligible.length === 0 && (\n <p className=\"text-xs text-muted-foreground\">\n <AdminMessage\n id={\n '\"Not sent to provider\" is fixed by publishing the catalog. Everything else is fixed under Settings, Payment providers.'\n }\n />\n </p>\n )}\n </>\n )}\n </CardContent>\n </Card>\n )\n}\n","import { AdminMessage, useAdminUI } from \"#orck/admin/ui-context\"\nimport { useAdminServices } from \"#orck/admin/context\"\nimport { HugeiconsIcon } from \"@hugeicons/react\"\nimport { ArrowLeft01Icon } from \"@hugeicons/core-free-icons\"\nimport * as React from \"react\"\nimport { Link, useLocation, useNavigate } from \"#orck/admin/router\"\n\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\"\n\nimport { Fact } from \"#orck/admin/components/fact-card\"\nimport { Badge } from \"#orck/admin/components/ui/badge\"\nimport { useConfirm } from \"#orck/admin/lib/confirm\"\nimport { Button } from \"#orck/admin/components/ui/button\"\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n} from \"#orck/admin/components/ui/card\"\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"#orck/admin/components/ui/table\"\nimport { formatDate, shortId } from \"#orck/admin/lib/format\"\n\nimport {\n priceAmountLabel,\n priceIntervalLabel,\n} from \"#orck/admin/pages/catalog/price-format\"\nimport { PriceChangeWizard } from \"#orck/admin/pages/catalog/price-wizard\"\nimport {\n PSPReadinessCard,\n PSPLinksCard,\n} from \"#orck/admin/pages/catalog/psp-links\"\nimport { useCatalogWrites } from \"#orck/admin/lib/capabilities\"\n\n// A price ID can contain its own percent-encoded key bytes. Router params\n// decode an inner %2F again; decode the original URL segment exactly once.\nfunction priceIDFromPath(pathname: string): string {\n const segment = pathname.replace(/\\/+$/, \"\").split(\"/\").at(-1) ?? \"\"\n try {\n return decodeURIComponent(segment)\n } catch {\n return \"\"\n }\n}\n\n// PriceDetailPage (#777): the version chain (from the #774 pointer-movement\n// log, with dates) and any pending scheduled migration (progress + cancel)\n// for one price key — the wizard's Step 1 entry point (\"Change price\") also\n// lives here alongside the catalog list row.\nexport function PriceDetailPage() {\n const { text: adminText } = useAdminUI()\n\n const { adminQueries } = useAdminServices()\n\n const id = priceIDFromPath(useLocation().pathname)\n const writesAllowed = useCatalogWrites()\n const navigate = useNavigate()\n // verify (or#812) is opt-in: it makes GET price perform a live retrieve\n // against every attached provider, which is a network round trip per PSP.\n const [verify, setVerify] = React.useState(false)\n const {\n data: price,\n isPending: loading,\n isFetching,\n refetch: reload,\n } = useQuery(adminQueries.price(id, { verify, errorAction: \"Load price\" }))\n const { data: product } = useQuery(adminQueries.product(price?.product_id))\n const { data: history } = useQuery(adminQueries.priceHistory(price?.id))\n const { data: migrations } = useQuery(\n adminQueries.priceMigrations(product?.key, price?.key)\n )\n const latestBatch = migrations?.data?.[0]\n\n if (!id)\n return (\n <p role=\"alert\" className=\"text-sm text-muted-foreground\">\n Invalid price URL.\n </p>\n )\n\n // Only the FIRST load blanks the page; a verify refetch keeps the rendered\n // price in place so the button can show its own in-flight state.\n if (!price)\n return (\n <p className=\"text-sm text-muted-foreground\">\n {loading ? \"Loading…\" : \"Price not found.\"}\n </p>\n )\n\n const isPending = !!latestBatch && latestBatch.scheduled > 0\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex items-center gap-3\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => navigate(-1)}\n aria-label={adminText(\"Back\")}\n >\n <HugeiconsIcon icon={ArrowLeft01Icon} className=\"size-4\" />\n </Button>\n <div>\n <h2 className=\"flex items-center gap-2 text-sm\">\n {price.id}\n {price.archived && (\n <Badge variant=\"secondary\">\n <AdminMessage id={\"archived\"} />\n </Badge>\n )}\n </h2>\n <p className=\"text-xs text-muted-foreground\">\n {product?.display_name}\n </p>\n </div>\n <div className=\"ml-auto\">\n {writesAllowed && (\n <PriceChangeWizard\n price={price}\n productName={product?.display_name ?? \"…\"}\n productKey={product?.key}\n />\n )}\n </div>\n </div>\n\n <div className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n <Fact label={adminText(\"Product\")}>\n <Link className=\"underline-offset-2 hover:underline\" to={`/catalog`}>\n {product?.display_name ?? shortId(price.product_id, 13)}\n </Link>\n </Fact>\n <Fact label={adminText(\"Amount\")}>{priceAmountLabel(price)}</Fact>\n <Fact label={adminText(\"Renews\")}>\n {price.currency.toUpperCase()} · {priceIntervalLabel(price)}\n </Fact>\n <Fact label={adminText(\"Created\")}>{formatDate(price.created_at)}</Fact>\n </div>\n\n <PSPLinksCard\n price={price}\n verifying={verify && isFetching}\n verified={verify}\n onVerify={() => (verify ? void reload() : setVerify(true))}\n />\n\n <PSPReadinessCard price={price} />\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm\">\n <AdminMessage id={\"Price history\"} />\n </CardTitle>\n </CardHeader>\n <CardContent>\n {!history?.data?.length ? (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"No history yet.\"} />\n </p>\n ) : (\n <Table>\n <TableHeader>\n <TableRow className=\"hover:bg-transparent\">\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Amount\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Since\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"State\"} />\n </TableHead>\n <TableHead className=\"text-muted-foreground\">\n <AdminMessage id={\"Price\"} />\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {history.data.map((entry) => (\n <TableRow key={`${entry.price.id}-${entry.effective_at}`}>\n <TableCell>{priceAmountLabel(entry.price)}</TableCell>\n <TableCell>{formatDate(entry.effective_at)}</TableCell>\n <TableCell>\n {entry.archived ? (\n <Badge variant=\"secondary\">\n <AdminMessage id={\"retired\"} />\n </Badge>\n ) : (\n <Badge className=\"bg-settled-surface text-settled\">\n <AdminMessage id={\"offered\"} />\n </Badge>\n )}\n </TableCell>\n <TableCell>\n <Link\n className=\"text-xs underline-offset-2 hover:underline\"\n to={`/catalog/prices/${encodeURIComponent(entry.price.id)}`}\n title={entry.price.id}\n >\n {entry.price.id}\n </Link>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n )}\n </CardContent>\n </Card>\n\n {latestBatch && (\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-2\">\n <CardTitle className=\"text-sm\">\n {isPending\n ? \"Moving customers to the new price\"\n : \"Last move to a new price\"}\n </CardTitle>\n {isPending && (\n <CancelMigrationButton migrationId={latestBatch.id} />\n )}\n </CardHeader>\n <CardContent className=\"flex flex-col gap-2 text-sm\">\n <p>\n {latestBatch.applied}\n <AdminMessage id={\" of\"} />{\" \"}\n {latestBatch.applied +\n latestBatch.scheduled +\n latestBatch.canceled}{\" \"}\n <AdminMessage id={\"moved\"} />\n {latestBatch.scheduled > 0 &&\n ` · ${latestBatch.scheduled} still scheduled`}\n {\" · effective\"}\n {formatDate(latestBatch.effective_at)}\n </p>\n {latestBatch.skipped > 0 && (\n <p className=\"text-xs text-muted-foreground\">\n {latestBatch.skipped}\n <AdminMessage id={\" subscription\"} />\n {latestBatch.skipped === 1 ? \" was\" : \"s were\"}\n <AdminMessage\n id={\n \" left alone because another change was already scheduled for them.\"\n }\n />\n </p>\n )}\n </CardContent>\n </Card>\n )}\n </div>\n )\n}\n\nfunction CancelMigrationButton({ migrationId }: { migrationId: string }) {\n const { toast, toastApiError, adminMutations } = useAdminServices()\n\n const queryClient = useQueryClient()\n const cancelBatch = useMutation(\n adminMutations.cancelPriceMigration(queryClient)\n )\n const confirm = useConfirm()\n return (\n <Button\n variant=\"destructive\"\n size=\"sm\"\n disabled={cancelBatch.isPending}\n onClick={async () => {\n const canceling = await confirm({\n title: \"Cancel this migration?\",\n description:\n \"Subscribers not yet moved stay on their current price. Those already moved stay moved.\",\n confirmLabel: \"Cancel migration\",\n cancelLabel: \"Keep migrating\",\n tone: \"destructive\",\n })\n if (!canceling) return\n try {\n const result = await cancelBatch.mutateAsync(migrationId)\n toast.success(\n `Canceled ${result.canceled} pending move${result.canceled === 1 ? \"\" : \"s\"}. Already-migrated subscribers stay migrated.`\n )\n const release = result.rail_release_required.length\n if (release > 0)\n toast.warning(\n `${release} Stripe subscription${release === 1 ? \" still carries\" : \"s still carry\"} the move as a Stripe subscription schedule: release it in Stripe, or the price changes at period end.`\n )\n } catch (err) {\n toastApiError(err, \"Cancel migration\")\n }\n }}\n >\n {cancelBatch.isPending ? \"Canceling…\" : \"Cancel migration\"}\n </Button>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"admin-price-wizard-ferZSTfO.js","names":[],"sources":["../src/admin/pages/catalog/price-wizard-logic.ts","../src/admin/pages/catalog/price-wizard.tsx"],"sourcesContent":["// Pure logic for the price-change wizard, kept apart from React for unit tests.\nimport { formatNativeAmount } from \"#orck/admin/lib/format\"\n\nexport type PriceDirection = \"increase\" | \"decrease\" | \"unchanged\"\n\nexport function detectDirection(\n newAmount: string,\n currentAmount: string\n): PriceDirection {\n const next = BigInt(newAmount)\n const current = BigInt(currentAmount)\n if (next > current) return \"increase\"\n if (next < current) return \"decrease\"\n return \"unchanged\"\n}\n\nexport type MigrationMode = \"grandfather\" | \"migrate\"\n\n// DEFAULT_NOTICE_WINDOW_DAYS is the fallback until the merchant's\n// reprice_notice_window_days loads; it mirrors\n// subscriptions.DefaultPriceIncreaseNoticeDays so the UI never under-gates.\n// The server enforces the window too (price_increase_notice_too_short).\nexport const DEFAULT_NOTICE_WINDOW_DAYS = 30\n\n// defaultMigrationMode is the direction-aware Step 2 default: an increase\n// grandfathers (no new action), a decrease migrates now (never grandfathered).\nexport function defaultMigrationMode(direction: PriceDirection): MigrationMode {\n return direction === \"decrease\" ? \"migrate\" : \"grandfather\"\n}\n\n// minEffectiveDate is the earliest allowed migration date, or null when there\n// is no minimum (a decrease needs no notice).\nexport function minEffectiveDate(\n direction: PriceDirection,\n now: Date,\n noticeWindowDays: number\n): Date | null {\n if (direction !== \"increase\") return null\n const min = new Date(now)\n min.setUTCDate(min.getUTCDate() + noticeWindowDays)\n return min\n}\n\n// defaultEffectiveDate is Step 2's pre-filled migration date: the notice\n// window's floor for an increase, \"now\" for a decrease.\nexport function defaultEffectiveDate(\n direction: PriceDirection,\n now: Date,\n noticeWindowDays: number\n): Date {\n return minEffectiveDate(direction, now, noticeWindowDays) ?? now\n}\n\n// isEffectiveDateValid enforces the console-side notice-window gate: an\n// increase+migrate plan may not pick a date inside the notice window.\nexport function isEffectiveDateValid(\n direction: PriceDirection,\n effectiveAt: Date,\n now: Date,\n noticeWindowDays: number\n): boolean {\n const min = minEffectiveDate(direction, now, noticeWindowDays)\n return !min || effectiveAt.getTime() >= min.getTime()\n}\n\nexport interface MigrationPlan {\n mode: MigrationMode\n // ISO instant; only meaningful when mode === \"migrate\".\n effectiveAt: string\n}\n\nconst dateLabel = (iso: string) =>\n new Date(iso).toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n })\n\n// buildReviewText renders Step 3's plan in words, e.g. \"New subscribers pay $12\n// immediately. 1,204 existing subscribers keep $10 until Sep 1, then move to\n// $12 at their next renewal. Notices go out on confirm.\"\nexport function buildReviewText(params: {\n newAmount: string\n currentAmount: string\n currency: string\n affectedCount: number\n plan: MigrationPlan\n now: Date\n}): string {\n const { newAmount, currentAmount, currency, affectedCount, plan, now } =\n params\n const newLabel = formatNativeAmount(newAmount, currency)\n const oldLabel = formatNativeAmount(currentAmount, currency)\n const lead = `New subscribers pay ${newLabel} immediately.`\n\n if (affectedCount === 0) {\n return `${lead} No existing subscribers are on a prior version of this price.`\n }\n const subj = `${affectedCount.toLocaleString()} existing subscriber${affectedCount === 1 ? \"\" : \"s\"}`\n\n if (plan.mode === \"grandfather\") {\n return `${lead} ${subj} keep ${oldLabel} forever (grandfathered).`\n }\n\n // The engine notifies each affected subscriber at schedule time, however\n // far out effective_at is: always say so.\n const effective = new Date(plan.effectiveAt)\n const immediate = effective.getTime() <= now.getTime()\n if (immediate) {\n return `${lead} ${subj} move to ${newLabel} at their next renewal. Notices go out on confirm.`\n }\n return `${lead} ${subj} keep ${oldLabel} until ${dateLabel(plan.effectiveAt)}, then move to ${newLabel} at their next renewal. Notices go out on confirm.`\n}\n","import { AdminMessage, useAdminUI } from \"#orck/admin/ui-context\"\nimport { useAdminServices } from \"#orck/admin/context\"\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 { 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 type { PriceChangeDraft } from \"#orck/admin/lib/api/copilot\"\nimport { DIALOG_FORM } from \"#orck/admin/lib/dialog-width\"\nimport type { Price } from \"#orck/client/generated/wire\"\nimport {\n formatNativeAmount,\n nativeAmountFromInput,\n nativeAmountToInput,\n} from \"#orck/admin/lib/format\"\n\nimport { priceIntervalLabel } from \"#orck/admin/pages/catalog/price-format\"\nimport {\n buildReviewText,\n defaultEffectiveDate,\n defaultMigrationMode,\n DEFAULT_NOTICE_WINDOW_DAYS,\n detectDirection,\n isEffectiveDateValid,\n minEffectiveDate,\n type MigrationMode,\n} from \"#orck/admin/pages/catalog/price-wizard-logic\"\n\nconst toDateInputValue = (d: Date) => d.toISOString().slice(0, 10)\n\nconst priceChangeFormValues = (price: Price, draft?: PriceChangeDraft) => ({\n amountInput: nativeAmountToInput(\n draft?.new_amount ?? price.unit_amount,\n price.currency\n ),\n mode: (draft?.migration ? \"migrate\" : \"grandfather\") as MigrationMode,\n effectiveAt: draft?.migration?.effective_at\n ? toDateInputValue(new Date(draft.migration.effective_at))\n : \"\",\n})\n\n// PriceChangeWizard is the #777 console price-change wizard — the ONLY\n// amount-edit affordance on a price. Three steps: new amount -> migration\n// plan (direction-aware defaults, live affected-count preview, notice-window\n// gate on increase+migrate) -> review in words, then confirm applies the\n// catalog price update followed (if migrating) by the price migration.\n//\n// draft (#779): when the catalog copilot proposed this change, the dialog\n// opens PRE-FILLED straight at Step 3 (review) — the affected count already\n// rode in the draft, so no extra preview call is needed. Confirm behaves\n// EXACTLY as a hand-typed change (the same create-price and price-migration\n// calls).\nexport function PriceChangeWizard({\n price,\n productName,\n productKey,\n draft,\n onDone,\n}: {\n price: Price\n productName: string\n productKey: string | undefined\n draft?: PriceChangeDraft\n onDone?: () => void\n}) {\n const { toast, toastApiError, adminMutations, adminQueries } =\n useAdminServices()\n\n const { id: adminId } = useAdminUI()\n\n const queryClient = useQueryClient()\n const [open, setOpen] = React.useState(() => !!draft)\n const [step, setStep] = React.useState<1 | 2 | 3>(() => (draft ? 3 : 1))\n const previewPriceChange = useMutation(adminMutations.previewPriceChange())\n const changePrice = useMutation(adminMutations.changePrice(queryClient))\n const affectedCount =\n previewPriceChange.data?.matched ?? draft?.affected_count ?? null\n const { data: configuration } = useQuery({\n ...adminQueries.merchantConfiguration(),\n enabled: open,\n })\n const noticeWindowDays =\n configuration?.settings.reprice_notice_window_days ??\n DEFAULT_NOTICE_WINDOW_DAYS\n\n const [now] = React.useState(() => new Date())\n const form = useForm({\n defaultValues: priceChangeFormValues(price, draft),\n onSubmit: async ({ value }) => {\n if (!productKey || price.customer_amount) return\n const newAmount = nativeAmountFromInput(value.amountInput, price.currency)\n if (newAmount === null || BigInt(newAmount) <= 0n) return\n\n try {\n const created = await changePrice.mutateAsync({\n price: {\n product_id: price.product_id,\n unit_amount: newAmount,\n currency: price.currency,\n access_duration_hours: price.access_duration_hours ?? undefined,\n billing_interval_hours: price.billing_interval_hours ?? undefined,\n trial_unit_amount: price.trial_unit_amount ?? undefined,\n trial_duration_hours: price.trial_duration_hours ?? undefined,\n key: price.key,\n psps: Object.keys(price.psps ?? {}),\n expected_revision: price.revision,\n },\n migration:\n value.mode === \"migrate\"\n ? {\n productKey,\n priceKey: price.key,\n effectiveAt: new Date(value.effectiveAt).toISOString(),\n }\n : undefined,\n })\n if (created.pending_manual_actions?.length) {\n toast.warning(\n `Price updated. One step is left for you: ${created.pending_manual_actions.map((action) => `${action.psp}: ${action.hint}`).join(\". \")}`\n )\n } else {\n toast.success(\n value.mode === \"migrate\"\n ? \"Price updated and migration scheduled\"\n : \"Price updated\"\n )\n }\n handleOpenChange(false)\n onDone?.()\n } catch (err) {\n toastApiError(err, \"Change price\")\n }\n },\n })\n\n const reset = () => {\n setStep(1)\n form.reset(priceChangeFormValues(price))\n previewPriceChange.reset()\n }\n\n const handleOpenChange = (next: boolean) => {\n if (!next) {\n reset()\n }\n setOpen(next)\n }\n\n const enterStep2 = () => {\n if (!productKey || price.customer_amount) return\n const amount = nativeAmountFromInput(\n form.state.values.amountInput,\n price.currency\n )\n const direction = detectDirection(\n amount ?? price.unit_amount,\n price.unit_amount\n )\n form.setFieldValue(\"mode\", defaultMigrationMode(direction))\n form.setFieldValue(\n \"effectiveAt\",\n toDateInputValue(defaultEffectiveDate(direction, now, noticeWindowDays))\n )\n setStep(2)\n previewPriceChange.reset()\n previewPriceChange.mutate(\n { productKey, priceKey: price.key },\n { onError: (err) => toastApiError(err, \"Preview affected subscribers\") }\n )\n }\n\n // Archived (prior-version) rows are history, not the editable current\n // price. Each product/key pair has exactly one live edit target.\n // Wait for the product before allowing previews or scheduling a migration.\n if (price.archived || !productKey || price.customer_amount) {\n return (\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled\n title={\n price.customer_amount\n ? \"This price lets customers choose an amount. Change its limits through a catalog application or the client.\"\n : price.archived\n ? \"This is an archived version. Change the current price for this key instead.\"\n : \"Load the product before changing its price.\"\n }\n >\n <AdminMessage id={\"Change price\"} />\n </Button>\n )\n }\n\n return (\n <Dialog open={open} onOpenChange={handleOpenChange}>\n {!draft && (\n <DialogTrigger\n render={\n <Button variant=\"outline\" size=\"sm\">\n <AdminMessage id={\"Change price\"} />\n </Button>\n }\n />\n )}\n <DialogContent className={DIALOG_FORM}>\n <form\n className=\"grid gap-4\"\n onSubmit={(event) => {\n event.preventDefault()\n event.stopPropagation()\n if (step !== 3) return\n void form.handleSubmit()\n }}\n >\n <form.Subscribe\n selector={(state) => [state.values, state.isSubmitting] as const}\n >\n {([values, isSubmitting]) => {\n const newAmount =\n nativeAmountFromInput(values.amountInput, price.currency) ??\n price.unit_amount\n const direction = detectDirection(newAmount, price.unit_amount)\n const minDate = minEffectiveDate(direction, now, noticeWindowDays)\n const dateValid =\n values.mode !== \"migrate\" ||\n (!!values.effectiveAt &&\n isEffectiveDateValid(\n direction,\n new Date(values.effectiveAt),\n now,\n noticeWindowDays\n ))\n\n return (\n <>\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n <AdminMessage id={\"Change price for \"} />\n {productName}\n {draft && (\n <Badge variant=\"secondary\">\n <AdminMessage id={\"drafted by copilot\"} />\n </Badge>\n )}\n </DialogTitle>\n <DialogDescription>\n <AdminMessage id={\"Step \"} />\n {step}\n <AdminMessage id={\" of 3:\"} />{\" \"}\n {step === 1\n ? \"new amount\"\n : step === 2\n ? \"migration plan\"\n : \"review\"}\n </DialogDescription>\n </DialogHeader>\n\n {step === 1 && (\n <div className=\"grid gap-3\">\n <div className=\"grid grid-cols-2 gap-3 text-sm\">\n <div>\n <p className=\"text-xs text-muted-foreground uppercase\">\n <AdminMessage id={\"Current amount\"} />\n </p>\n <p className=\"font-medium\">\n {formatNativeAmount(\n price.unit_amount,\n price.currency\n )}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground uppercase\">\n <AdminMessage id={\"Currency · interval\"} />\n </p>\n <p className=\"font-medium\">\n {price.currency.toUpperCase()} ·{\" \"}\n {priceIntervalLabel(price)}\n </p>\n </div>\n </div>\n <form.Field\n name=\"amountInput\"\n validators={{\n onBlur: ({ value }) => {\n const amount = nativeAmountFromInput(\n value,\n price.currency\n )\n if (amount === null || BigInt(amount) <= 0n) {\n return \"Enter an amount greater than zero.\"\n }\n return undefined\n },\n }}\n >\n {(field) => (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={adminId(\"wiz-amount\")}>\n <AdminMessage id={\"New amount (major units)\"} />\n </Label>\n <Input\n id={adminId(\"wiz-amount\")}\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 autoFocus\n />\n <FormFieldErrors errors={field.state.meta.errors} />\n </div>\n )}\n </form.Field>\n {direction !== \"unchanged\" && (\n <p className=\"text-sm text-muted-foreground\">\n <AdminMessage id={\"This is a price\"} />{\" \"}\n <span className=\"font-medium text-foreground\">\n {direction === \"increase\" ? \"increase\" : \"decrease\"}\n </span>\n <AdminMessage\n id={\n \". Currency and interval stay locked to the current price.\"\n }\n />\n </p>\n )}\n </div>\n )}\n\n {step === 2 && (\n <div className=\"grid gap-3\">\n <p className=\"text-sm\">\n {affectedCount === null && previewPriceChange.isPending\n ? \"Checking affected subscribers…\"\n : `${(affectedCount ?? 0).toLocaleString()} active subscription${affectedCount === 1 ? \"\" : \"s\"} on prior versions of ${price.key}.`}\n </p>\n <form.Field name=\"mode\">\n {(field) => (\n <div className=\"grid gap-2\">\n <label className=\"flex items-start gap-2 rounded-md border p-3 text-sm\">\n <input\n type=\"radio\"\n name={field.name}\n className=\"mt-1\"\n checked={field.state.value === \"grandfather\"}\n onChange={() =>\n field.handleChange(\"grandfather\")\n }\n />\n <span>\n <span className=\"font-medium\">\n <AdminMessage id={\"Grandfather.\"} />\n </span>{\" \"}\n <AdminMessage\n id={\n \"Existing subscribers keep the current price.\"\n }\n />\n {direction === \"increase\" && (\n <Badge variant=\"secondary\" className=\"ml-2\">\n <AdminMessage id={\"default\"} />\n </Badge>\n )}\n </span>\n </label>\n <label className=\"flex items-start gap-2 rounded-md border p-3 text-sm\">\n <input\n type=\"radio\"\n name={field.name}\n className=\"mt-1\"\n checked={field.state.value === \"migrate\"}\n onChange={() => field.handleChange(\"migrate\")}\n />\n <span>\n <span className=\"font-medium\">\n <AdminMessage id={\"Migrate.\"} />\n </span>{\" \"}\n <AdminMessage\n id={\n \"Move everyone to the new price at their first renewal on or after a date you choose.\"\n }\n />\n {direction === \"decrease\" && (\n <Badge variant=\"secondary\" className=\"ml-2\">\n <AdminMessage id={\"default\"} />\n </Badge>\n )}\n </span>\n </label>\n </div>\n )}\n </form.Field>\n {values.mode === \"migrate\" && (\n <form.Field name=\"effectiveAt\">\n {(field) => (\n <div className=\"grid gap-1.5\">\n <Label htmlFor={adminId(\"wiz-effective\")}>\n <AdminMessage id={\"Effective date\"} />\n </Label>\n <Input\n id={adminId(\"wiz-effective\")}\n type=\"date\"\n value={field.state.value}\n min={\n minDate\n ? toDateInputValue(minDate)\n : undefined\n }\n onBlur={field.handleBlur}\n onChange={(event) =>\n field.handleChange(event.target.value)\n }\n />\n {direction === \"increase\" ? (\n <p className=\"text-xs text-muted-foreground\">\n <AdminMessage id={\"Must be at least \"} />\n {noticeWindowDays}\n <AdminMessage\n id={\n \" days away. Card networks require that much notice before a price goes up. You can change the number in Settings.\"\n }\n />\n </p>\n ) : (\n <p className=\"text-xs text-muted-foreground\">\n <AdminMessage\n id={\n \"A price going down needs no notice, so today is fine.\"\n }\n />\n </p>\n )}\n {!dateValid && (\n <p\n className=\"text-xs text-destructive\"\n role=\"alert\"\n >\n <AdminMessage id={\"Pick a later date.\"} />\n </p>\n )}\n <p className=\"text-xs text-muted-foreground\">\n <AdminMessage\n id={\n \"A reprice_scheduled notice is queued for every affected subscriber as soon as you confirm.\"\n }\n />\n </p>\n </div>\n )}\n </form.Field>\n )}\n </div>\n )}\n\n {step === 3 && (\n <div className=\"grid gap-3\">\n <p className=\"rounded-md border bg-muted/40 p-3 text-sm\">\n {buildReviewText({\n newAmount,\n currentAmount: price.unit_amount,\n currency: price.currency,\n affectedCount: affectedCount ?? 0,\n plan: {\n mode: values.mode,\n effectiveAt:\n values.mode === \"migrate\"\n ? new Date(values.effectiveAt).toISOString()\n : \"\",\n },\n now,\n })}\n </p>\n </div>\n )}\n\n <DialogFooter>\n {step > 1 && (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled={isSubmitting}\n onClick={() => setStep((value) => (value - 1) as 1 | 2)}\n >\n <AdminMessage id={\"Back\"} />\n </Button>\n )}\n {step === 1 && (\n <Button\n type=\"button\"\n disabled={\n !values.amountInput ||\n direction === \"unchanged\" ||\n BigInt(newAmount) <= 0n\n }\n onClick={enterStep2}\n >\n <AdminMessage id={\"Next\"} />\n </Button>\n )}\n {step === 2 && (\n <Button\n type=\"button\"\n disabled={!dateValid || previewPriceChange.isPending}\n onClick={() => setStep(3)}\n >\n <AdminMessage id={\"Next\"} />\n </Button>\n )}\n {step === 3 && (\n <Button type=\"submit\" disabled={isSubmitting}>\n {isSubmitting ? \"Working…\" : \"Confirm\"}\n </Button>\n )}\n </DialogFooter>\n </>\n )\n }}\n </form.Subscribe>\n </form>\n </DialogContent>\n </Dialog>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"admin-settings-C6eWFkDn.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 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 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=\"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(\"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\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 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 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, so anything\n // typed for the previous one is not carried over.\n form.setFieldValue(\"credentials\", {})\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.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.key,\n state.values.accountID,\n state.canSubmit,\n state.isSubmitting,\n ] as const\n }\n >\n {([rail, key, accountID, canSubmit, isSubmitting]) => (\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 !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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"}
|