order-management 0.0.95 → 0.0.97

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.
@@ -1,9 +1,9 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
3
- import { useState, useEffect, useRef, useCallback, useMemo } from "react";
4
- import { Text, Container, Heading, Badge, Button, Label, Select, Textarea, Input } from "@medusajs/ui";
5
- import { CashSolid, PencilSquareSolid, Trash, CreditCard, Receipt, ArrowPath, CheckCircle, ArrowLeft } from "@medusajs/icons";
6
- import { useNavigate, useParams } from "react-router-dom";
3
+ import { useState, useEffect, useRef, useMemo, useCallback } from "react";
4
+ import { Text, Container, Heading, Badge, Button, Label, Select, Textarea, Popover, clx, Input, Copy, Skeleton, Tooltip, toast } from "@medusajs/ui";
5
+ import { CashSolid, PencilSquareSolid, Trash, ChevronDownMini, Check, ArrowPath, InformationCircle, ExclamationCircle, MagnifierAlert, ChevronRightMini, Receipt, CreditCard, ArrowLeft, ClockSolid, ShieldCheck, SquareTwoStack, CheckCircle } from "@medusajs/icons";
6
+ import { useNavigate, useLocation, useSearchParams, useParams } from "react-router-dom";
7
7
  function roundRectPath(ctx, x, y, w, h, r) {
8
8
  ctx.beginPath();
9
9
  ctx.moveTo(x + r, y);
@@ -923,6 +923,1295 @@ const OrderRefundContextWidget = (props) => {
923
923
  defineWidgetConfig({
924
924
  zone: "order.details.after"
925
925
  });
926
+ const AUDITED_TABLE_NAMES = [
927
+ "account_deletion_request",
928
+ "account_holder",
929
+ "api_key",
930
+ "application_method_buy_rules",
931
+ "application_method_target_rules",
932
+ "auth_identity",
933
+ "auth_mfa_factor",
934
+ "auth_mfa_recovery_code",
935
+ "auth_password_reset_token",
936
+ "auth_verification",
937
+ "cancel_order_reason_mapping",
938
+ "capture",
939
+ "cart",
940
+ "cart_address",
941
+ "cart_line_item",
942
+ "cart_line_item_adjustment",
943
+ "cart_line_item_tax_line",
944
+ "cart_payment_collection",
945
+ "cart_promotion",
946
+ "cart_shipping_method",
947
+ "cart_shipping_method_adjustment",
948
+ "cart_shipping_method_tax_line",
949
+ "contact_email_subscription",
950
+ "contact_request",
951
+ "contact_request_comment",
952
+ "credit_line",
953
+ "currency",
954
+ "customer",
955
+ "customer_account_holder",
956
+ "customer_address",
957
+ "customer_group",
958
+ "customer_group_customer",
959
+ "dynamic_config_value",
960
+ "fulfillment",
961
+ "fulfillment_address",
962
+ "fulfillment_item",
963
+ "fulfillment_label",
964
+ "fulfillment_provider",
965
+ "fulfillment_set",
966
+ "geo_zone",
967
+ "image",
968
+ "inventory_count_item",
969
+ "inventory_count_session",
970
+ "inventory_item",
971
+ "inventory_level",
972
+ "inventory_movement",
973
+ "inventory_purchase",
974
+ "inventory_purchase_attachment",
975
+ "inventory_purchase_audit_event",
976
+ "inventory_purchase_batch",
977
+ "inventory_purchase_line_item",
978
+ "invite",
979
+ "invite_rbac_role",
980
+ "invoice",
981
+ "invoice_sequence",
982
+ "layout_configuration",
983
+ "link_module_migrations",
984
+ "location_fulfillment_provider",
985
+ "location_fulfillment_set",
986
+ "loyalty_gift_card",
987
+ "mikro_orm_migrations",
988
+ "notification",
989
+ "notification_provider",
990
+ "notification_tokens",
991
+ "order",
992
+ "order_address",
993
+ "order_cart",
994
+ "order_change",
995
+ "order_change_action",
996
+ "order_claim",
997
+ "order_claim_item",
998
+ "order_claim_item_image",
999
+ "order_credit_line",
1000
+ "order_exchange",
1001
+ "order_exchange_item",
1002
+ "order_fulfillment",
1003
+ "order_item",
1004
+ "order_line_item",
1005
+ "order_line_item_adjustment",
1006
+ "order_line_item_tax_line",
1007
+ "order_payment_collection",
1008
+ "order_promotion",
1009
+ "order_shipping",
1010
+ "order_shipping_method",
1011
+ "order_shipping_method_adjustment",
1012
+ "order_shipping_method_tax_line",
1013
+ "order_summary",
1014
+ "order_transaction",
1015
+ "otp_verification",
1016
+ "payment",
1017
+ "payment_collection",
1018
+ "payment_collection_payment_providers",
1019
+ "payment_detail",
1020
+ "payment_provider",
1021
+ "payment_session",
1022
+ "payout_request",
1023
+ "price",
1024
+ "price_list",
1025
+ "price_list_rule",
1026
+ "price_preference",
1027
+ "price_rule",
1028
+ "price_set",
1029
+ "product",
1030
+ "product_category",
1031
+ "product_category_product",
1032
+ "product_collection",
1033
+ "product_display_priority",
1034
+ "product_option",
1035
+ "product_option_value",
1036
+ "product_product_option",
1037
+ "product_product_option_value",
1038
+ "product_sales_channel",
1039
+ "product_shipping_profile",
1040
+ "product_tag",
1041
+ "product_tags",
1042
+ "product_type",
1043
+ "product_variant",
1044
+ "product_variant_inventory_item",
1045
+ "product_variant_option",
1046
+ "product_variant_price_set",
1047
+ "product_variant_product_image",
1048
+ "product_view",
1049
+ "promotion",
1050
+ "promotion_application_method",
1051
+ "promotion_campaign",
1052
+ "promotion_campaign_budget",
1053
+ "promotion_campaign_budget_usage",
1054
+ "promotion_promotion_rule",
1055
+ "promotion_rule",
1056
+ "promotion_rule_value",
1057
+ "property_label",
1058
+ "provider_identity",
1059
+ "publishable_api_key_sales_channel",
1060
+ "purchase_supplier",
1061
+ "referral_link",
1062
+ "referral_reward",
1063
+ "refund",
1064
+ "refund_payment_mapping",
1065
+ "refund_reason",
1066
+ "region",
1067
+ "region_country",
1068
+ "region_payment_provider",
1069
+ "reservation_item",
1070
+ "return",
1071
+ "return_fulfillment",
1072
+ "return_item",
1073
+ "return_reason",
1074
+ "review",
1075
+ "sales_channel",
1076
+ "sales_channel_stock_location",
1077
+ "script_migrations",
1078
+ "search_index",
1079
+ "search_index_sync",
1080
+ "search_index_version",
1081
+ "search_postgres_index",
1082
+ "service_zone",
1083
+ "shipment_tracking_event",
1084
+ "shipping_option",
1085
+ "shipping_option_price_set",
1086
+ "shipping_option_rule",
1087
+ "shipping_option_type",
1088
+ "shipping_profile",
1089
+ "skip_merge_cart",
1090
+ "stock_location",
1091
+ "stock_location_address",
1092
+ "store",
1093
+ "store_credit_account",
1094
+ "store_credit_account_transaction",
1095
+ "store_currency",
1096
+ "store_locale",
1097
+ "tax_provider",
1098
+ "tax_rate",
1099
+ "tax_rate_rule",
1100
+ "tax_region",
1101
+ "telegram_config",
1102
+ "telegram_notification_log",
1103
+ "user",
1104
+ "user_preference",
1105
+ "user_rbac_role",
1106
+ "view_configuration",
1107
+ "wallet_account",
1108
+ "wallet_credit_lot",
1109
+ "wallet_gift_card",
1110
+ "wallet_hold",
1111
+ "wallet_ledger_entry",
1112
+ "wallet_rate_limit",
1113
+ "wallet_refund_intent",
1114
+ "wallet_top_up_request",
1115
+ "wishlist",
1116
+ "workflow_execution"
1117
+ ];
1118
+ const ENTITY_CATEGORIES = [
1119
+ { id: "orders", label: "Orders", description: "Orders, items, returns, exchanges and claims" },
1120
+ { id: "carts", label: "Carts", description: "Shopping carts and checkout data" },
1121
+ { id: "customers", label: "Customers", description: "Customers, addresses and groups" },
1122
+ { id: "catalog", label: "Catalog", description: "Products, variants, categories and collections" },
1123
+ { id: "pricing", label: "Pricing & Promotions", description: "Prices, price lists and promotions" },
1124
+ { id: "payments", label: "Payments & Refunds", description: "Payments, sessions, captures and refunds" },
1125
+ { id: "fulfillment", label: "Fulfillment & Shipping", description: "Fulfillments, shipping options and tracking" },
1126
+ { id: "inventory", label: "Inventory & Purchasing", description: "Stock, locations, reservations and purchases" },
1127
+ { id: "wallet", label: "Wallet & Credits", description: "Wallets, store credit and gift cards" },
1128
+ { id: "access", label: "Users & Access", description: "Admin users, logins, roles and API keys" },
1129
+ { id: "store", label: "Store & Regions", description: "Store, regions, currencies, taxes and sales channels" },
1130
+ { id: "engagement", label: "Engagement", description: "Reviews, wishlists, referrals and contact requests" },
1131
+ { id: "notifications", label: "Notifications", description: "Notifications and messaging" },
1132
+ { id: "system", label: "System & Configuration", description: "Settings, search, workflows and migrations" }
1133
+ ];
1134
+ const ENTITY_DEFINITIONS = {
1135
+ // Orders
1136
+ order: { label: "Order", category: "orders" },
1137
+ order_address: { label: "Order Address", category: "orders" },
1138
+ order_cart: { label: "Order–Cart Link", category: "orders" },
1139
+ order_change: { label: "Order Change", category: "orders" },
1140
+ order_change_action: { label: "Order Change Action", category: "orders" },
1141
+ order_claim: { label: "Claim", category: "orders" },
1142
+ order_claim_item: { label: "Claim Item", category: "orders" },
1143
+ order_claim_item_image: { label: "Claim Item Image", category: "orders" },
1144
+ order_credit_line: { label: "Order Credit Line", category: "orders" },
1145
+ order_exchange: { label: "Exchange", category: "orders" },
1146
+ order_exchange_item: { label: "Exchange Item", category: "orders" },
1147
+ order_fulfillment: { label: "Order–Fulfillment Link", category: "orders" },
1148
+ order_item: { label: "Order Item Quantity", category: "orders" },
1149
+ order_line_item: { label: "Order Item", category: "orders" },
1150
+ order_line_item_adjustment: { label: "Order Item Discount", category: "orders" },
1151
+ order_line_item_tax_line: { label: "Order Item Tax", category: "orders" },
1152
+ order_payment_collection: { label: "Order–Payment Link", category: "orders" },
1153
+ order_promotion: { label: "Order Promotion", category: "orders" },
1154
+ order_shipping: { label: "Order Shipping", category: "orders" },
1155
+ order_shipping_method: { label: "Order Shipping Method", category: "orders" },
1156
+ order_shipping_method_adjustment: { label: "Order Shipping Discount", category: "orders" },
1157
+ order_shipping_method_tax_line: { label: "Order Shipping Tax", category: "orders" },
1158
+ order_summary: { label: "Order Summary", category: "orders" },
1159
+ order_transaction: { label: "Order Transaction", category: "orders" },
1160
+ return: { label: "Return", category: "orders" },
1161
+ return_fulfillment: { label: "Return–Fulfillment Link", category: "orders" },
1162
+ return_item: { label: "Return Item", category: "orders" },
1163
+ return_reason: { label: "Return Reason", category: "orders" },
1164
+ cancel_order_reason_mapping: { label: "Order Cancellation Reason", category: "orders" },
1165
+ credit_line: { label: "Credit Line", category: "orders" },
1166
+ invoice: { label: "Invoice", category: "orders" },
1167
+ invoice_sequence: { label: "Invoice Number Sequence", category: "orders" },
1168
+ // Carts
1169
+ cart: { label: "Cart", category: "carts" },
1170
+ cart_address: { label: "Cart Address", category: "carts" },
1171
+ cart_line_item: { label: "Cart Item", category: "carts" },
1172
+ cart_line_item_adjustment: { label: "Cart Item Discount", category: "carts" },
1173
+ cart_line_item_tax_line: { label: "Cart Item Tax", category: "carts" },
1174
+ cart_payment_collection: { label: "Cart–Payment Link", category: "carts" },
1175
+ cart_promotion: { label: "Cart Promotion", category: "carts" },
1176
+ cart_shipping_method: { label: "Cart Shipping Method", category: "carts" },
1177
+ cart_shipping_method_adjustment: { label: "Cart Shipping Discount", category: "carts" },
1178
+ cart_shipping_method_tax_line: { label: "Cart Shipping Tax", category: "carts" },
1179
+ skip_merge_cart: { label: "Cart Merge Exclusion", category: "carts" },
1180
+ // Customers
1181
+ customer: { label: "Customer", category: "customers" },
1182
+ customer_address: { label: "Customer Address", category: "customers" },
1183
+ customer_group: { label: "Customer Group", category: "customers" },
1184
+ customer_group_customer: { label: "Customer Group Member", category: "customers" },
1185
+ customer_account_holder: { label: "Customer–Account Holder Link", category: "customers" },
1186
+ account_holder: { label: "Payment Account Holder", category: "customers" },
1187
+ account_deletion_request: { label: "Account Deletion Request", category: "customers" },
1188
+ payment_detail: { label: "Customer Refund Method", category: "customers" },
1189
+ otp_verification: { label: "OTP Verification", category: "customers" },
1190
+ // Catalog
1191
+ product: { label: "Product", category: "catalog" },
1192
+ product_variant: { label: "Product Variant", category: "catalog" },
1193
+ product_category: { label: "Product Category", category: "catalog" },
1194
+ product_category_product: { label: "Product–Category Link", category: "catalog" },
1195
+ product_collection: { label: "Collection", category: "catalog" },
1196
+ product_option: { label: "Product Option", category: "catalog" },
1197
+ product_option_value: { label: "Product Option Value", category: "catalog" },
1198
+ product_product_option: { label: "Product–Option Link", category: "catalog" },
1199
+ product_product_option_value: { label: "Product–Option Value Link", category: "catalog" },
1200
+ product_tag: { label: "Product Tag", category: "catalog" },
1201
+ product_tags: { label: "Product–Tag Link", category: "catalog" },
1202
+ product_type: { label: "Product Type", category: "catalog" },
1203
+ product_sales_channel: { label: "Product–Sales Channel Link", category: "catalog" },
1204
+ product_shipping_profile: { label: "Product–Shipping Profile Link", category: "catalog" },
1205
+ product_variant_inventory_item: { label: "Variant–Inventory Link", category: "catalog" },
1206
+ product_variant_option: { label: "Variant Option", category: "catalog" },
1207
+ product_variant_price_set: { label: "Variant–Price Link", category: "catalog" },
1208
+ product_variant_product_image: { label: "Variant Image", category: "catalog" },
1209
+ product_display_priority: { label: "Product Display Priority", category: "catalog" },
1210
+ product_view: { label: "Product View", category: "catalog" },
1211
+ image: { label: "Product Image", category: "catalog" },
1212
+ property_label: { label: "Property Label", category: "catalog" },
1213
+ // Pricing & promotions
1214
+ price: { label: "Price", category: "pricing" },
1215
+ price_list: { label: "Price List", category: "pricing" },
1216
+ price_list_rule: { label: "Price List Rule", category: "pricing" },
1217
+ price_preference: { label: "Price Preference", category: "pricing" },
1218
+ price_rule: { label: "Price Rule", category: "pricing" },
1219
+ price_set: { label: "Price Set", category: "pricing" },
1220
+ promotion: { label: "Promotion", category: "pricing" },
1221
+ promotion_application_method: { label: "Promotion Application Method", category: "pricing" },
1222
+ promotion_campaign: { label: "Campaign", category: "pricing" },
1223
+ promotion_campaign_budget: { label: "Campaign Budget", category: "pricing" },
1224
+ promotion_campaign_budget_usage: { label: "Campaign Budget Usage", category: "pricing" },
1225
+ promotion_promotion_rule: { label: "Promotion–Rule Link", category: "pricing" },
1226
+ promotion_rule: { label: "Promotion Rule", category: "pricing" },
1227
+ promotion_rule_value: { label: "Promotion Rule Value", category: "pricing" },
1228
+ application_method_buy_rules: { label: "Promotion Buy Rule", category: "pricing" },
1229
+ application_method_target_rules: { label: "Promotion Target Rule", category: "pricing" },
1230
+ // Payments
1231
+ payment: { label: "Payment", category: "payments" },
1232
+ payment_collection: { label: "Payment Collection", category: "payments" },
1233
+ payment_collection_payment_providers: { label: "Payment Collection–Provider Link", category: "payments" },
1234
+ payment_provider: { label: "Payment Provider", category: "payments" },
1235
+ payment_session: { label: "Payment Session", category: "payments" },
1236
+ capture: { label: "Payment Capture", category: "payments" },
1237
+ refund: { label: "Refund", category: "payments" },
1238
+ refund_reason: { label: "Refund Reason", category: "payments" },
1239
+ refund_payment_mapping: { label: "Return Refund Mapping", category: "payments" },
1240
+ payout_request: { label: "Payout Request", category: "payments" },
1241
+ // Fulfillment
1242
+ fulfillment: { label: "Fulfillment", category: "fulfillment" },
1243
+ fulfillment_address: { label: "Fulfillment Address", category: "fulfillment" },
1244
+ fulfillment_item: { label: "Fulfillment Item", category: "fulfillment" },
1245
+ fulfillment_label: { label: "Shipping Label", category: "fulfillment" },
1246
+ fulfillment_provider: { label: "Fulfillment Provider", category: "fulfillment" },
1247
+ fulfillment_set: { label: "Fulfillment Set", category: "fulfillment" },
1248
+ geo_zone: { label: "Geo Zone", category: "fulfillment" },
1249
+ service_zone: { label: "Service Zone", category: "fulfillment" },
1250
+ shipping_option: { label: "Shipping Option", category: "fulfillment" },
1251
+ shipping_option_price_set: { label: "Shipping Option–Price Link", category: "fulfillment" },
1252
+ shipping_option_rule: { label: "Shipping Option Rule", category: "fulfillment" },
1253
+ shipping_option_type: { label: "Shipping Option Type", category: "fulfillment" },
1254
+ shipping_profile: { label: "Shipping Profile", category: "fulfillment" },
1255
+ shipment_tracking_event: { label: "Shipment Tracking Event", category: "fulfillment" },
1256
+ location_fulfillment_provider: { label: "Location–Fulfillment Provider Link", category: "fulfillment" },
1257
+ location_fulfillment_set: { label: "Location–Fulfillment Set Link", category: "fulfillment" },
1258
+ // Inventory
1259
+ inventory_item: { label: "Inventory Item", category: "inventory" },
1260
+ inventory_level: { label: "Inventory Level", category: "inventory" },
1261
+ inventory_movement: { label: "Inventory Movement", category: "inventory" },
1262
+ inventory_count_item: { label: "Stock Count Item", category: "inventory" },
1263
+ inventory_count_session: { label: "Stock Count Session", category: "inventory" },
1264
+ inventory_purchase: { label: "Purchase", category: "inventory" },
1265
+ inventory_purchase_attachment: { label: "Purchase Attachment", category: "inventory" },
1266
+ inventory_purchase_audit_event: { label: "Purchase Audit Event", category: "inventory" },
1267
+ inventory_purchase_batch: { label: "Purchase Batch", category: "inventory" },
1268
+ inventory_purchase_line_item: { label: "Purchase Item", category: "inventory" },
1269
+ purchase_supplier: { label: "Supplier", category: "inventory" },
1270
+ reservation_item: { label: "Stock Reservation", category: "inventory" },
1271
+ stock_location: { label: "Stock Location", category: "inventory" },
1272
+ stock_location_address: { label: "Stock Location Address", category: "inventory" },
1273
+ sales_channel_stock_location: { label: "Sales Channel–Location Link", category: "inventory" },
1274
+ // Wallet
1275
+ wallet_account: { label: "Wallet Account", category: "wallet" },
1276
+ wallet_credit_lot: { label: "Wallet Credit Lot", category: "wallet" },
1277
+ wallet_gift_card: { label: "Wallet Gift Card", category: "wallet" },
1278
+ wallet_hold: { label: "Wallet Hold", category: "wallet" },
1279
+ wallet_ledger_entry: { label: "Wallet Ledger Entry", category: "wallet" },
1280
+ wallet_rate_limit: { label: "Wallet Rate Limit", category: "wallet" },
1281
+ wallet_refund_intent: { label: "Wallet Refund", category: "wallet" },
1282
+ wallet_top_up_request: { label: "Wallet Top-up Request", category: "wallet" },
1283
+ store_credit_account: { label: "Store Credit Account", category: "wallet" },
1284
+ store_credit_account_transaction: { label: "Store Credit Transaction", category: "wallet" },
1285
+ loyalty_gift_card: { label: "Loyalty Gift Card", category: "wallet" },
1286
+ // Users & access
1287
+ user: { label: "Admin User", category: "access" },
1288
+ user_preference: { label: "User Preference", category: "access" },
1289
+ user_rbac_role: { label: "User Role", category: "access" },
1290
+ invite: { label: "Admin Invite", category: "access" },
1291
+ invite_rbac_role: { label: "Invite Role", category: "access" },
1292
+ api_key: { label: "API Key", category: "access" },
1293
+ publishable_api_key_sales_channel: { label: "API Key–Sales Channel Link", category: "access" },
1294
+ auth_identity: { label: "Login Identity", category: "access" },
1295
+ auth_mfa_factor: { label: "MFA Factor", category: "access" },
1296
+ auth_mfa_recovery_code: { label: "MFA Recovery Code", category: "access" },
1297
+ auth_password_reset_token: { label: "Password Reset Token", category: "access" },
1298
+ auth_verification: { label: "Auth Verification", category: "access" },
1299
+ provider_identity: { label: "Login Provider Identity", category: "access" },
1300
+ // Store & regions
1301
+ store: { label: "Store", category: "store" },
1302
+ store_currency: { label: "Store Currency", category: "store" },
1303
+ store_locale: { label: "Store Locale", category: "store" },
1304
+ currency: { label: "Currency", category: "store" },
1305
+ region: { label: "Region", category: "store" },
1306
+ region_country: { label: "Region Country", category: "store" },
1307
+ region_payment_provider: { label: "Region–Payment Provider Link", category: "store" },
1308
+ sales_channel: { label: "Sales Channel", category: "store" },
1309
+ tax_provider: { label: "Tax Provider", category: "store" },
1310
+ tax_rate: { label: "Tax Rate", category: "store" },
1311
+ tax_rate_rule: { label: "Tax Rate Rule", category: "store" },
1312
+ tax_region: { label: "Tax Region", category: "store" },
1313
+ // Engagement
1314
+ review: { label: "Review", category: "engagement" },
1315
+ wishlist: { label: "Wishlist", category: "engagement" },
1316
+ referral_link: { label: "Referral Link", category: "engagement" },
1317
+ referral_reward: { label: "Referral Reward", category: "engagement" },
1318
+ contact_request: { label: "Contact Request", category: "engagement" },
1319
+ contact_request_comment: { label: "Contact Request Comment", category: "engagement" },
1320
+ contact_email_subscription: { label: "Email Subscription", category: "engagement" },
1321
+ // Notifications
1322
+ notification: { label: "Notification", category: "notifications" },
1323
+ notification_provider: { label: "Notification Provider", category: "notifications" },
1324
+ notification_tokens: { label: "Push Notification Token", category: "notifications" },
1325
+ telegram_config: { label: "Telegram Settings", category: "notifications" },
1326
+ telegram_notification_log: { label: "Telegram Notification Log", category: "notifications" },
1327
+ // System
1328
+ dynamic_config_value: { label: "Plugin Setting", category: "system" },
1329
+ layout_configuration: { label: "Layout Configuration", category: "system" },
1330
+ view_configuration: { label: "View Configuration", category: "system" },
1331
+ workflow_execution: { label: "Workflow Execution", category: "system" },
1332
+ search_index: { label: "Search Index", category: "system" },
1333
+ search_index_sync: { label: "Search Index Sync", category: "system" },
1334
+ search_index_version: { label: "Search Index Version", category: "system" },
1335
+ search_postgres_index: { label: "Search Index Entry", category: "system" },
1336
+ link_module_migrations: { label: "Link Migration Record", category: "system" },
1337
+ mikro_orm_migrations: { label: "Database Migration Record", category: "system" },
1338
+ script_migrations: { label: "Script Migration Record", category: "system" }
1339
+ };
1340
+ const CATEGORY_BY_ID = new Map(ENTITY_CATEGORIES.map((c) => [c.id, c]));
1341
+ const SYSTEM_CATEGORY = CATEGORY_BY_ID.get("system");
1342
+ function humanizeTableName(tableName) {
1343
+ return tableName.split("_").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
1344
+ }
1345
+ function getEntityInfo(tableName) {
1346
+ const def = ENTITY_DEFINITIONS[tableName];
1347
+ return {
1348
+ tableName,
1349
+ label: def?.label ?? humanizeTableName(tableName),
1350
+ category: def && CATEGORY_BY_ID.get(def.category) || SYSTEM_CATEGORY
1351
+ };
1352
+ }
1353
+ function getEntitiesByCategory() {
1354
+ const groups = /* @__PURE__ */ new Map();
1355
+ for (const table of AUDITED_TABLE_NAMES) {
1356
+ const info = getEntityInfo(table);
1357
+ const list = groups.get(info.category.id) ?? [];
1358
+ list.push(info);
1359
+ groups.set(info.category.id, list);
1360
+ }
1361
+ return ENTITY_CATEGORIES.filter((category) => groups.has(category.id)).map((category) => ({
1362
+ category,
1363
+ entities: (groups.get(category.id) ?? []).sort((a, b) => a.label.localeCompare(b.label))
1364
+ }));
1365
+ }
1366
+ function getCategory(id) {
1367
+ return CATEGORY_BY_ID.get(id);
1368
+ }
1369
+ function getTablesForCategory(id) {
1370
+ return AUDITED_TABLE_NAMES.filter((table) => getEntityInfo(table).category.id === id);
1371
+ }
1372
+ const GROUPS = getEntitiesByCategory();
1373
+ function triggerLabel(entity, category) {
1374
+ if (entity) return getEntityInfo(entity).label;
1375
+ if (category) return `All ${getCategory(category)?.label ?? category}`;
1376
+ return "All entities";
1377
+ }
1378
+ const EntityPicker = ({ entity, category, onChange }) => {
1379
+ const [open, setOpen] = useState(false);
1380
+ const [search, setSearch] = useState("");
1381
+ const searchRef = useRef(null);
1382
+ const filteredGroups = useMemo(() => {
1383
+ const term = search.trim().toLowerCase();
1384
+ if (!term) return GROUPS;
1385
+ return GROUPS.map((group) => {
1386
+ const categoryMatches = group.category.label.toLowerCase().includes(term);
1387
+ const entities = categoryMatches ? group.entities : group.entities.filter(
1388
+ (e) => e.label.toLowerCase().includes(term) || e.tableName.includes(term.replace(/\s+/g, "_"))
1389
+ );
1390
+ return { ...group, entities };
1391
+ }).filter((group) => group.entities.length > 0);
1392
+ }, [search]);
1393
+ const select = (next) => {
1394
+ onChange(next);
1395
+ setOpen(false);
1396
+ setSearch("");
1397
+ };
1398
+ const firstMatch = filteredGroups[0]?.entities[0];
1399
+ const isAll = !entity && !category;
1400
+ return /* @__PURE__ */ jsxs(
1401
+ Popover,
1402
+ {
1403
+ open,
1404
+ onOpenChange: (next) => {
1405
+ setOpen(next);
1406
+ if (!next) setSearch("");
1407
+ },
1408
+ children: [
1409
+ /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
1410
+ "button",
1411
+ {
1412
+ type: "button",
1413
+ "aria-label": "Filter by entity",
1414
+ className: clx(
1415
+ "txt-compact-small flex h-7 w-full min-w-[200px] items-center justify-between gap-2 rounded-md bg-ui-bg-field px-2 text-left shadow-borders-base outline-none transition-fg",
1416
+ "hover:bg-ui-bg-field-hover focus-visible:shadow-borders-interactive-with-active md:w-auto",
1417
+ isAll ? "text-ui-fg-muted" : "text-ui-fg-base"
1418
+ ),
1419
+ children: [
1420
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: triggerLabel(entity, category) }),
1421
+ /* @__PURE__ */ jsx(ChevronDownMini, { className: "shrink-0 text-ui-fg-muted" })
1422
+ ]
1423
+ }
1424
+ ) }),
1425
+ /* @__PURE__ */ jsxs(
1426
+ Popover.Content,
1427
+ {
1428
+ align: "start",
1429
+ className: "w-[min(360px,calc(100vw-2rem))] p-0",
1430
+ onOpenAutoFocus: (event) => {
1431
+ event.preventDefault();
1432
+ searchRef.current?.focus();
1433
+ },
1434
+ children: [
1435
+ /* @__PURE__ */ jsx("div", { className: "border-b border-ui-border-base p-2", children: /* @__PURE__ */ jsx(
1436
+ Input,
1437
+ {
1438
+ ref: searchRef,
1439
+ size: "small",
1440
+ type: "search",
1441
+ placeholder: "Search entities, e.g. order, wallet…",
1442
+ value: search,
1443
+ onChange: (e) => setSearch(e.target.value),
1444
+ onKeyDown: (e) => {
1445
+ if (e.key === "Enter" && firstMatch) {
1446
+ e.preventDefault();
1447
+ select({ entity: firstMatch.tableName, category: "" });
1448
+ }
1449
+ },
1450
+ "aria-label": "Search entities"
1451
+ }
1452
+ ) }),
1453
+ /* @__PURE__ */ jsxs("div", { className: "max-h-[360px] overflow-y-auto p-1", role: "listbox", "aria-label": "Entities", children: [
1454
+ !search.trim() ? /* @__PURE__ */ jsx(PickerOption, { selected: isAll, onSelect: () => select({ entity: "", category: "" }), children: "All entities" }) : null,
1455
+ filteredGroups.length === 0 ? /* @__PURE__ */ jsxs(Text, { size: "small", className: "px-3 py-6 text-center text-ui-fg-muted", children: [
1456
+ "No entities match “",
1457
+ search.trim(),
1458
+ "”"
1459
+ ] }) : null,
1460
+ filteredGroups.map((group) => /* @__PURE__ */ jsxs("div", { className: "mt-1", children: [
1461
+ /* @__PURE__ */ jsx("div", { className: "px-2 pb-1 pt-2 text-xs font-medium uppercase tracking-wide text-ui-fg-muted", children: group.category.label }),
1462
+ !search.trim() ? /* @__PURE__ */ jsxs(
1463
+ PickerOption,
1464
+ {
1465
+ selected: !entity && category === group.category.id,
1466
+ onSelect: () => select({ entity: "", category: group.category.id }),
1467
+ secondary: group.category.description,
1468
+ children: [
1469
+ "All ",
1470
+ group.category.label
1471
+ ]
1472
+ }
1473
+ ) : null,
1474
+ group.entities.map((info) => /* @__PURE__ */ jsx(
1475
+ PickerOption,
1476
+ {
1477
+ selected: entity === info.tableName,
1478
+ onSelect: () => select({ entity: info.tableName, category: "" }),
1479
+ secondary: info.tableName,
1480
+ secondaryMono: true,
1481
+ indent: !search.trim(),
1482
+ children: info.label
1483
+ },
1484
+ info.tableName
1485
+ ))
1486
+ ] }, group.category.id))
1487
+ ] })
1488
+ ]
1489
+ }
1490
+ )
1491
+ ]
1492
+ }
1493
+ );
1494
+ };
1495
+ const PickerOption = ({ selected, onSelect, children, secondary, secondaryMono, indent }) => /* @__PURE__ */ jsxs(
1496
+ "button",
1497
+ {
1498
+ type: "button",
1499
+ role: "option",
1500
+ "aria-selected": selected,
1501
+ onClick: onSelect,
1502
+ className: clx(
1503
+ "flex w-full items-start gap-2 rounded-md px-2 py-1.5 text-left outline-none transition-fg",
1504
+ "hover:bg-ui-bg-base-hover focus-visible:bg-ui-bg-base-hover",
1505
+ indent && "pl-4"
1506
+ ),
1507
+ children: [
1508
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 flex size-4 shrink-0 items-center justify-center text-ui-fg-interactive", children: selected ? /* @__PURE__ */ jsx(Check, {}) : null }),
1509
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col", children: [
1510
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm text-ui-fg-base", children }),
1511
+ secondary ? /* @__PURE__ */ jsx("span", { className: clx("truncate text-xs text-ui-fg-muted", secondaryMono && "font-mono"), children: secondary }) : null
1512
+ ] })
1513
+ ]
1514
+ }
1515
+ );
1516
+ const CopyableId = ({ value, emptyLabel = "No ID", className }) => {
1517
+ if (!value) {
1518
+ return /* @__PURE__ */ jsx(Text, { size: "small", className: "italic text-ui-fg-muted", children: emptyLabel });
1519
+ }
1520
+ return /* @__PURE__ */ jsxs("span", { className: clx("inline-flex min-w-0 max-w-full items-center gap-1", className), children: [
1521
+ /* @__PURE__ */ jsx("span", { className: "truncate font-mono text-xs text-ui-fg-base", title: value, children: value }),
1522
+ /* @__PURE__ */ jsx("span", { onClick: (e) => e.stopPropagation(), className: "inline-flex shrink-0", children: /* @__PURE__ */ jsx(Copy, { content: value, variant: "mini", className: "text-ui-fg-muted", "aria-label": `Copy ${value}` }) })
1523
+ ] });
1524
+ };
1525
+ const RELATIVE_UNITS = [
1526
+ { unit: "year", seconds: 60 * 60 * 24 * 365 },
1527
+ { unit: "month", seconds: 60 * 60 * 24 * 30 },
1528
+ { unit: "week", seconds: 60 * 60 * 24 * 7 },
1529
+ { unit: "day", seconds: 60 * 60 * 24 },
1530
+ { unit: "hour", seconds: 60 * 60 },
1531
+ { unit: "minute", seconds: 60 }
1532
+ ];
1533
+ function toDate(value) {
1534
+ const date = value instanceof Date ? value : new Date(value);
1535
+ return Number.isNaN(date.getTime()) ? null : date;
1536
+ }
1537
+ function formatRelativeTime(value, now = /* @__PURE__ */ new Date()) {
1538
+ const date = toDate(value);
1539
+ if (!date) return "—";
1540
+ const diffSeconds = Math.round((date.getTime() - now.getTime()) / 1e3);
1541
+ if (Math.abs(diffSeconds) < 45) return "just now";
1542
+ const rtf = new Intl.RelativeTimeFormat(void 0, { numeric: "auto" });
1543
+ for (const { unit, seconds } of RELATIVE_UNITS) {
1544
+ if (Math.abs(diffSeconds) >= seconds) {
1545
+ return rtf.format(Math.round(diffSeconds / seconds), unit);
1546
+ }
1547
+ }
1548
+ return rtf.format(Math.round(diffSeconds / 60), "minute");
1549
+ }
1550
+ function formatExactDateTime(value) {
1551
+ const date = toDate(value);
1552
+ if (!date) return "—";
1553
+ return new Intl.DateTimeFormat(void 0, {
1554
+ day: "2-digit",
1555
+ month: "short",
1556
+ year: "numeric",
1557
+ hour: "2-digit",
1558
+ minute: "2-digit",
1559
+ second: "2-digit",
1560
+ timeZoneName: "short"
1561
+ }).format(date);
1562
+ }
1563
+ function formatDate(value) {
1564
+ const date = toDate(value);
1565
+ if (!date) return "—";
1566
+ return new Intl.DateTimeFormat(void 0, { day: "2-digit", month: "short", year: "numeric" }).format(date);
1567
+ }
1568
+ function formatNumber(value) {
1569
+ return new Intl.NumberFormat().format(value);
1570
+ }
1571
+ function formatMoney(amount, currencyCode) {
1572
+ const code = currencyCode?.trim().toUpperCase();
1573
+ if (code && /^[A-Z]{3}$/.test(code)) {
1574
+ try {
1575
+ return new Intl.NumberFormat(void 0, { style: "currency", currency: code }).format(amount);
1576
+ } catch {
1577
+ }
1578
+ }
1579
+ return code ? `${code} ${formatNumber(amount)}` : formatNumber(amount);
1580
+ }
1581
+ const SOURCE_ALIASES = {
1582
+ admin: { kind: "admin", label: "Admin Panel" },
1583
+ admin_panel: { kind: "admin", label: "Admin Panel" },
1584
+ dashboard: { kind: "admin", label: "Admin Panel" },
1585
+ api: { kind: "api", label: "API" },
1586
+ store_api: { kind: "api", label: "Storefront API" },
1587
+ admin_api: { kind: "api", label: "Admin API" },
1588
+ system: { kind: "system", label: "System" },
1589
+ job: { kind: "job", label: "Background Job" },
1590
+ background_job: { kind: "job", label: "Background Job" },
1591
+ scheduled_job: { kind: "job", label: "Background Job" },
1592
+ cron: { kind: "job", label: "Background Job" },
1593
+ workflow: { kind: "workflow", label: "Workflow" },
1594
+ migration: { kind: "system", label: "Migration" }
1595
+ };
1596
+ function describeSource(source) {
1597
+ if (!source || !source.trim()) return { kind: "unknown", label: "Unknown" };
1598
+ const key = source.trim().toLowerCase().replace(/[\s-]+/g, "_");
1599
+ return SOURCE_ALIASES[key] ?? { kind: "other", label: source.trim() };
1600
+ }
1601
+ function sourceBadgeColor(kind) {
1602
+ switch (kind) {
1603
+ case "admin":
1604
+ return "blue";
1605
+ case "api":
1606
+ return "purple";
1607
+ case "job":
1608
+ case "workflow":
1609
+ return "orange";
1610
+ case "system":
1611
+ return "green";
1612
+ default:
1613
+ return "grey";
1614
+ }
1615
+ }
1616
+ const CARDS = [
1617
+ { key: "total", label: "Total deleted", hint: "All records captured since auditing began" },
1618
+ { key: "today", label: "Today", hint: "Deleted since midnight (your time)" },
1619
+ { key: "last_7_days", label: "Last 7 days", hint: "Including today" },
1620
+ { key: "tables_affected", label: "Entity types affected", hint: "Distinct kinds of records deleted" }
1621
+ ];
1622
+ const SummaryCards = ({ stats, loading, failed }) => {
1623
+ if (failed && !stats) return null;
1624
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3 lg:grid-cols-4", children: CARDS.map((card) => /* @__PURE__ */ jsxs(
1625
+ "div",
1626
+ {
1627
+ className: "flex flex-col gap-1 rounded-lg border border-ui-border-base bg-ui-bg-subtle px-4 py-3",
1628
+ title: card.hint,
1629
+ children: [
1630
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: card.label }),
1631
+ loading || !stats ? /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-16" }) : /* @__PURE__ */ jsx(Text, { className: "text-2xl font-semibold leading-8 text-ui-fg-base", children: formatNumber(stats[card.key]) })
1632
+ ]
1633
+ },
1634
+ card.key
1635
+ )) });
1636
+ };
1637
+ const BASE = "/admin/deleted-records-audit";
1638
+ class AuditApiError extends Error {
1639
+ status;
1640
+ constructor(message, status) {
1641
+ super(message);
1642
+ this.status = status;
1643
+ }
1644
+ }
1645
+ async function getJson(url, signal) {
1646
+ const response = await fetch(url, { credentials: "include", signal });
1647
+ if (!response.ok) {
1648
+ let message = response.statusText || "Request failed";
1649
+ try {
1650
+ const body = await response.json();
1651
+ if (body && typeof body === "object" && "message" in body && typeof body.message === "string") {
1652
+ message = body.message;
1653
+ }
1654
+ } catch {
1655
+ }
1656
+ throw new AuditApiError(message, response.status);
1657
+ }
1658
+ return await response.json();
1659
+ }
1660
+ function fetchAuditList(params, signal) {
1661
+ return getJson(`${BASE}?${params.toString()}`, signal);
1662
+ }
1663
+ async function fetchAuditStats(signal) {
1664
+ const todayStart = /* @__PURE__ */ new Date();
1665
+ todayStart.setHours(0, 0, 0, 0);
1666
+ const params = new URLSearchParams({ today_start: todayStart.toISOString() });
1667
+ const body = await getJson(`${BASE}/stats?${params.toString()}`, signal);
1668
+ return body.stats;
1669
+ }
1670
+ async function fetchAuditDetail(id, signal) {
1671
+ const body = await getJson(`${BASE}/${encodeURIComponent(id)}`, signal);
1672
+ return body.audit;
1673
+ }
1674
+ const LAST_LIST_URL_KEY = "om.deleted-records-audit.last-list-search";
1675
+ const DATE_PRESETS = [
1676
+ { value: "any", label: "Any time" },
1677
+ { value: "today", label: "Today" },
1678
+ { value: "yesterday", label: "Yesterday" },
1679
+ { value: "7d", label: "Last 7 days" },
1680
+ { value: "30d", label: "Last 30 days" },
1681
+ { value: "custom", label: "Custom range" }
1682
+ ];
1683
+ const EMPTY_FILTERS = {
1684
+ q: "",
1685
+ entity: "",
1686
+ category: "",
1687
+ date: "any",
1688
+ from: "",
1689
+ to: "",
1690
+ source: "",
1691
+ deletedBy: ""
1692
+ };
1693
+ const URL_KEYS = {
1694
+ q: "q",
1695
+ entity: "entity",
1696
+ category: "category",
1697
+ date: "date",
1698
+ from: "from",
1699
+ to: "to",
1700
+ source: "source",
1701
+ deletedBy: "deleted_by"
1702
+ };
1703
+ const DATE_PRESET_VALUES = new Set(DATE_PRESETS.map((p) => p.value));
1704
+ const DAY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
1705
+ function filtersFromSearchParams(params) {
1706
+ const read = (key) => params.get(URL_KEYS[key])?.trim() ?? "";
1707
+ const date = read("date");
1708
+ const from = read("from");
1709
+ const to = read("to");
1710
+ return {
1711
+ q: read("q"),
1712
+ entity: read("entity"),
1713
+ category: read("category"),
1714
+ date: DATE_PRESET_VALUES.has(date) ? date : "any",
1715
+ from: DAY_PATTERN.test(from) ? from : "",
1716
+ to: DAY_PATTERN.test(to) ? to : "",
1717
+ source: read("source"),
1718
+ deletedBy: read("deletedBy")
1719
+ };
1720
+ }
1721
+ function filtersToSearchParams(filters) {
1722
+ const params = new URLSearchParams();
1723
+ for (const key of Object.keys(URL_KEYS)) {
1724
+ const value = filters[key];
1725
+ if (!value || value === EMPTY_FILTERS[key]) continue;
1726
+ if ((key === "from" || key === "to") && filters.date !== "custom") continue;
1727
+ params.set(URL_KEYS[key], value);
1728
+ }
1729
+ return params;
1730
+ }
1731
+ function hasActiveFilters(filters) {
1732
+ return filtersToSearchParams(filters).toString() !== "";
1733
+ }
1734
+ function startOfDay(date) {
1735
+ const d = new Date(date);
1736
+ d.setHours(0, 0, 0, 0);
1737
+ return d;
1738
+ }
1739
+ function endOfDay(date) {
1740
+ const d = new Date(date);
1741
+ d.setHours(23, 59, 59, 999);
1742
+ return d;
1743
+ }
1744
+ function parseDay(day) {
1745
+ if (!DAY_PATTERN.test(day)) return null;
1746
+ const [y, m, d] = day.split("-").map(Number);
1747
+ const date = new Date(y, m - 1, d);
1748
+ return Number.isNaN(date.getTime()) ? null : date;
1749
+ }
1750
+ function resolveDateRange(filters, now = /* @__PURE__ */ new Date()) {
1751
+ const today = startOfDay(now);
1752
+ switch (filters.date) {
1753
+ case "today":
1754
+ return { from: today };
1755
+ case "yesterday": {
1756
+ const y = new Date(today);
1757
+ y.setDate(y.getDate() - 1);
1758
+ return { from: y, to: endOfDay(y) };
1759
+ }
1760
+ case "7d": {
1761
+ const from = new Date(today);
1762
+ from.setDate(from.getDate() - 6);
1763
+ return { from };
1764
+ }
1765
+ case "30d": {
1766
+ const from = new Date(today);
1767
+ from.setDate(from.getDate() - 29);
1768
+ return { from };
1769
+ }
1770
+ case "custom": {
1771
+ const from = parseDay(filters.from);
1772
+ const to = parseDay(filters.to);
1773
+ return { from: from ?? void 0, to: to ? endOfDay(to) : void 0 };
1774
+ }
1775
+ default:
1776
+ return {};
1777
+ }
1778
+ }
1779
+ function filtersToApiParams(filters, page, now = /* @__PURE__ */ new Date()) {
1780
+ const params = new URLSearchParams();
1781
+ params.set("limit", String(page.limit));
1782
+ params.set("offset", String(page.offset));
1783
+ if (filters.q) params.set("q", filters.q);
1784
+ if (filters.entity) {
1785
+ params.set("table_name", filters.entity);
1786
+ } else if (filters.category) {
1787
+ const tables = getTablesForCategory(filters.category);
1788
+ if (tables.length) params.set("table_names", tables.join(","));
1789
+ }
1790
+ const range = resolveDateRange(filters, now);
1791
+ if (range.from) params.set("date_from", range.from.toISOString());
1792
+ if (range.to) params.set("date_to", range.to.toISOString());
1793
+ if (filters.source) params.set("source", filters.source);
1794
+ if (filters.deletedBy) params.set("deleted_by", filters.deletedBy);
1795
+ return params;
1796
+ }
1797
+ const PAGE_SIZE = 50;
1798
+ const RESTORE_PAGE_SIZE = 100;
1799
+ const MAX_RESTORE_ROWS = 500;
1800
+ const ALL = "__all__";
1801
+ const UNKNOWN_SOURCE = "unknown";
1802
+ const LOADED_COUNT_KEY_PREFIX = "om.deleted-records-audit.loaded:";
1803
+ const LAST_VIEWED_KEY = "om.deleted-records-audit.last-viewed";
1804
+ function readSession(key) {
1805
+ try {
1806
+ return window.sessionStorage.getItem(key);
1807
+ } catch {
1808
+ return null;
1809
+ }
1810
+ }
1811
+ function writeSession(key, value) {
1812
+ try {
1813
+ window.sessionStorage.setItem(key, value);
1814
+ } catch {
1815
+ }
1816
+ }
1817
+ const DeletedRecordsPage = () => {
1818
+ const navigate = useNavigate();
1819
+ const location = useLocation();
1820
+ const [searchParams, setSearchParams] = useSearchParams();
1821
+ const filters = useMemo(() => filtersFromSearchParams(searchParams), [searchParams]);
1822
+ const filterKey = filtersToSearchParams(filters).toString();
1823
+ const filtersActive = hasActiveFilters(filters);
1824
+ const [searchText, setSearchText] = useState(filters.q);
1825
+ const [items, setItems] = useState([]);
1826
+ const [count, setCount] = useState(0);
1827
+ const [isLoading, setIsLoading] = useState(true);
1828
+ const [isFetchingMore, setIsFetchingMore] = useState(false);
1829
+ const [error, setError] = useState(null);
1830
+ const [stats, setStats] = useState(null);
1831
+ const [statsLoading, setStatsLoading] = useState(true);
1832
+ const [statsFailed, setStatsFailed] = useState(false);
1833
+ const [reloadToken, setReloadToken] = useState(0);
1834
+ const [highlightId, setHighlightId] = useState(null);
1835
+ const restoredScrollRef = useRef(false);
1836
+ const updateFilters = useCallback(
1837
+ (patch) => {
1838
+ const next = { ...filters, ...patch };
1839
+ setSearchParams(filtersToSearchParams(next), { replace: true });
1840
+ },
1841
+ [filters, setSearchParams]
1842
+ );
1843
+ useEffect(() => {
1844
+ if (searchText.trim() === filters.q) return;
1845
+ const handle = setTimeout(() => updateFilters({ q: searchText.trim() }), 300);
1846
+ return () => clearTimeout(handle);
1847
+ }, [searchText, filters.q, updateFilters]);
1848
+ useEffect(() => {
1849
+ setSearchText((current) => current.trim() === filters.q ? current : filters.q);
1850
+ }, [filters.q]);
1851
+ useEffect(() => {
1852
+ writeSession(LAST_LIST_URL_KEY, location.search);
1853
+ }, [location.search]);
1854
+ const loadStats = useCallback(async (signal) => {
1855
+ setStatsLoading(true);
1856
+ setStatsFailed(false);
1857
+ try {
1858
+ setStats(await fetchAuditStats(signal));
1859
+ } catch (e) {
1860
+ if (!signal?.aborted) setStatsFailed(true);
1861
+ } finally {
1862
+ if (!signal?.aborted) setStatsLoading(false);
1863
+ }
1864
+ }, []);
1865
+ useEffect(() => {
1866
+ const controller = new AbortController();
1867
+ void loadStats(controller.signal);
1868
+ return () => controller.abort();
1869
+ }, [loadStats, reloadToken]);
1870
+ useEffect(() => {
1871
+ const controller = new AbortController();
1872
+ const restoreTarget = Math.min(
1873
+ MAX_RESTORE_ROWS,
1874
+ Math.max(PAGE_SIZE, Number(readSession(LOADED_COUNT_KEY_PREFIX + filterKey)) || 0)
1875
+ );
1876
+ setIsLoading(true);
1877
+ setError(null);
1878
+ (async () => {
1879
+ try {
1880
+ const collected = [];
1881
+ let total = 0;
1882
+ while (collected.length < restoreTarget) {
1883
+ const limit = restoreTarget > PAGE_SIZE ? RESTORE_PAGE_SIZE : PAGE_SIZE;
1884
+ const page = await fetchAuditList(
1885
+ filtersToApiParams(filters, { limit, offset: collected.length }),
1886
+ controller.signal
1887
+ );
1888
+ total = page.count;
1889
+ collected.push(...page.audits);
1890
+ if (page.audits.length < limit || collected.length >= total) break;
1891
+ }
1892
+ if (controller.signal.aborted) return;
1893
+ setItems(collected.slice(0, restoreTarget));
1894
+ setCount(total);
1895
+ } catch (e) {
1896
+ if (controller.signal.aborted) return;
1897
+ setItems([]);
1898
+ setCount(0);
1899
+ setError(e instanceof Error ? e.message : "Unknown error");
1900
+ } finally {
1901
+ if (!controller.signal.aborted) setIsLoading(false);
1902
+ }
1903
+ })();
1904
+ return () => controller.abort();
1905
+ }, [filterKey, reloadToken]);
1906
+ useEffect(() => {
1907
+ if (!isLoading) writeSession(LOADED_COUNT_KEY_PREFIX + filterKey, String(items.length));
1908
+ }, [items.length, isLoading, filterKey]);
1909
+ useEffect(() => {
1910
+ if (isLoading || restoredScrollRef.current) return;
1911
+ restoredScrollRef.current = true;
1912
+ const lastViewed = readSession(LAST_VIEWED_KEY);
1913
+ if (!lastViewed || !items.some((item) => item.id === lastViewed)) return;
1914
+ setHighlightId(lastViewed);
1915
+ requestAnimationFrame(() => {
1916
+ const visible = Array.from(document.querySelectorAll(`[data-audit-row="${lastViewed}"]`)).find(
1917
+ (el) => el.offsetParent !== null
1918
+ );
1919
+ visible?.scrollIntoView({ block: "center" });
1920
+ });
1921
+ const handle = setTimeout(() => setHighlightId(null), 2500);
1922
+ return () => clearTimeout(handle);
1923
+ }, [isLoading, items]);
1924
+ const loadMore = async () => {
1925
+ setIsFetchingMore(true);
1926
+ try {
1927
+ const page = await fetchAuditList(filtersToApiParams(filters, { limit: PAGE_SIZE, offset: items.length }));
1928
+ setItems((prev) => [...prev, ...page.audits]);
1929
+ setCount(page.count);
1930
+ } catch (e) {
1931
+ setError(e instanceof Error ? e.message : "Unknown error");
1932
+ } finally {
1933
+ setIsFetchingMore(false);
1934
+ }
1935
+ };
1936
+ const openDetail = (id) => {
1937
+ writeSession(LAST_VIEWED_KEY, id);
1938
+ navigate(`/deleted-records-audit/${id}`, { state: { fromList: true } });
1939
+ };
1940
+ const clearFilters = () => {
1941
+ setSearchText("");
1942
+ setSearchParams(filtersToSearchParams(EMPTY_FILTERS), { replace: true });
1943
+ };
1944
+ const refresh = () => setReloadToken((t) => t + 1);
1945
+ const hasMore = items.length < count;
1946
+ const knownSources = stats?.sources ?? [];
1947
+ const knownDeletedBy = stats?.deleted_by ?? [];
1948
+ const attributionMissing = !!stats && stats.total > 0 && knownSources.length === 0 && knownDeletedBy.length === 0;
1949
+ return /* @__PURE__ */ jsx("div", { className: "w-full p-3 md:p-6", children: /* @__PURE__ */ jsxs(Container, { className: "mx-auto flex w-full max-w-7xl flex-col gap-6 p-4 md:p-6", children: [
1950
+ /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-3 md:flex-row md:items-start md:justify-between", children: [
1951
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
1952
+ /* @__PURE__ */ jsx(Heading, { level: "h1", children: "Deleted Records" }),
1953
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Review records that were permanently deleted from the system." })
1954
+ ] }),
1955
+ /* @__PURE__ */ jsxs(Button, { variant: "secondary", size: "small", onClick: refresh, isLoading: isLoading && items.length > 0, children: [
1956
+ /* @__PURE__ */ jsx(ArrowPath, {}),
1957
+ "Refresh"
1958
+ ] })
1959
+ ] }),
1960
+ /* @__PURE__ */ jsx(SummaryCards, { stats, loading: statsLoading, failed: statsFailed }),
1961
+ /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-3", "aria-label": "Search and filters", children: [
1962
+ /* @__PURE__ */ jsx(
1963
+ Input,
1964
+ {
1965
+ type: "search",
1966
+ size: "base",
1967
+ placeholder: "Search by record ID, e.g. order_01J…, prod_…, cus_…",
1968
+ value: searchText,
1969
+ onChange: (e) => setSearchText(e.target.value),
1970
+ "aria-label": "Search by record ID",
1971
+ className: "w-full"
1972
+ }
1973
+ ),
1974
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 md:flex-row md:flex-wrap md:items-center", children: [
1975
+ /* @__PURE__ */ jsx(
1976
+ EntityPicker,
1977
+ {
1978
+ entity: filters.entity,
1979
+ category: filters.category,
1980
+ onChange: (next) => updateFilters(next)
1981
+ }
1982
+ ),
1983
+ /* @__PURE__ */ jsx("div", { className: "md:w-[160px]", children: /* @__PURE__ */ jsxs(
1984
+ Select,
1985
+ {
1986
+ size: "small",
1987
+ value: filters.date,
1988
+ onValueChange: (value) => updateFilters({ date: value }),
1989
+ children: [
1990
+ /* @__PURE__ */ jsx(Select.Trigger, { "aria-label": "Filter by date", children: /* @__PURE__ */ jsx(Select.Value, {}) }),
1991
+ /* @__PURE__ */ jsx(Select.Content, { children: DATE_PRESETS.map((preset) => /* @__PURE__ */ jsx(Select.Item, { value: preset.value, children: preset.label }, preset.value)) })
1992
+ ]
1993
+ }
1994
+ ) }),
1995
+ filters.date === "custom" ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1996
+ /* @__PURE__ */ jsx(
1997
+ Input,
1998
+ {
1999
+ type: "date",
2000
+ size: "small",
2001
+ value: filters.from,
2002
+ max: filters.to || void 0,
2003
+ onChange: (e) => updateFilters({ from: e.target.value }),
2004
+ "aria-label": "From date"
2005
+ }
2006
+ ),
2007
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "to" }),
2008
+ /* @__PURE__ */ jsx(
2009
+ Input,
2010
+ {
2011
+ type: "date",
2012
+ size: "small",
2013
+ value: filters.to,
2014
+ min: filters.from || void 0,
2015
+ onChange: (e) => updateFilters({ to: e.target.value }),
2016
+ "aria-label": "To date"
2017
+ }
2018
+ )
2019
+ ] }) : null,
2020
+ knownSources.length > 0 || filters.source ? /* @__PURE__ */ jsx("div", { className: "md:w-[170px]", children: /* @__PURE__ */ jsxs(
2021
+ Select,
2022
+ {
2023
+ size: "small",
2024
+ value: filters.source || ALL,
2025
+ onValueChange: (value) => updateFilters({ source: value === ALL ? "" : value }),
2026
+ children: [
2027
+ /* @__PURE__ */ jsx(Select.Trigger, { "aria-label": "Filter by source", children: /* @__PURE__ */ jsx(Select.Value, {}) }),
2028
+ /* @__PURE__ */ jsxs(Select.Content, { children: [
2029
+ /* @__PURE__ */ jsx(Select.Item, { value: ALL, children: "All sources" }),
2030
+ knownSources.map((source) => /* @__PURE__ */ jsx(Select.Item, { value: source, children: describeSource(source).label }, source)),
2031
+ /* @__PURE__ */ jsx(Select.Item, { value: UNKNOWN_SOURCE, children: "Unknown" })
2032
+ ] })
2033
+ ]
2034
+ }
2035
+ ) }) : null,
2036
+ knownDeletedBy.length > 0 || filters.deletedBy ? /* @__PURE__ */ jsxs("div", { className: "md:w-[200px]", children: [
2037
+ /* @__PURE__ */ jsx(
2038
+ Input,
2039
+ {
2040
+ size: "small",
2041
+ list: "deleted-records-audit-deleted-by",
2042
+ placeholder: "Deleted by (all users)",
2043
+ defaultValue: filters.deletedBy,
2044
+ onBlur: (e) => updateFilters({ deletedBy: e.target.value.trim() }),
2045
+ onKeyDown: (e) => {
2046
+ if (e.key === "Enter") updateFilters({ deletedBy: e.currentTarget.value.trim() });
2047
+ },
2048
+ "aria-label": "Filter by who deleted the record"
2049
+ },
2050
+ filters.deletedBy
2051
+ ),
2052
+ /* @__PURE__ */ jsx("datalist", { id: "deleted-records-audit-deleted-by", children: knownDeletedBy.map((value) => /* @__PURE__ */ jsx("option", { value }, value)) })
2053
+ ] }) : null,
2054
+ filtersActive ? /* @__PURE__ */ jsx(Button, { variant: "transparent", size: "small", onClick: clearFilters, children: "Clear filters" }) : null
2055
+ ] })
2056
+ ] }),
2057
+ attributionMissing ? /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-ui-border-base bg-ui-bg-subtle px-3 py-2", children: [
2058
+ /* @__PURE__ */ jsx(InformationCircle, { className: "mt-0.5 shrink-0 text-ui-fg-muted" }),
2059
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Who deleted a record and where the deletion came from are not being recorded yet, so those columns show “Unknown”. Every deleted record is still captured in full." })
2060
+ ] }) : null,
2061
+ !isLoading && !error && items.length > 0 ? /* @__PURE__ */ jsxs(Text, { size: "small", className: "text-ui-fg-subtle", "aria-live": "polite", children: [
2062
+ "Showing ",
2063
+ formatNumber(items.length),
2064
+ " of ",
2065
+ formatNumber(count),
2066
+ " ",
2067
+ count === 1 ? "record" : "records",
2068
+ filtersActive ? " matching your filters" : ""
2069
+ ] }) : null,
2070
+ error ? /* @__PURE__ */ jsx(ErrorState, { onRetry: refresh, detail: error }) : isLoading ? /* @__PURE__ */ jsx(ListSkeleton, {}) : items.length === 0 ? filtersActive ? /* @__PURE__ */ jsx(
2071
+ EmptyState,
2072
+ {
2073
+ title: "No deleted records found",
2074
+ body: "There are no audit records matching your current filters.",
2075
+ action: /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: clearFilters, children: "Clear filters" })
2076
+ }
2077
+ ) : /* @__PURE__ */ jsx(
2078
+ EmptyState,
2079
+ {
2080
+ title: "No deleted records yet",
2081
+ body: "Hard-deleted records will appear here automatically when a monitored database record is permanently deleted."
2082
+ }
2083
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
2084
+ /* @__PURE__ */ jsx(AuditTable, { items, highlightId, onOpen: openDetail }),
2085
+ /* @__PURE__ */ jsx(AuditCards, { items, highlightId, onOpen: openDetail })
2086
+ ] }),
2087
+ !error && !isLoading && hasMore ? /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(Button, { variant: "secondary", isLoading: isFetchingMore, onClick: loadMore, children: "Load more" }) }) : null
2088
+ ] }) });
2089
+ };
2090
+ const HEADER_CELL = "px-3 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted";
2091
+ const DeletedWhen = ({ occurredAt }) => /* @__PURE__ */ jsx(Tooltip, { content: formatExactDateTime(occurredAt), children: /* @__PURE__ */ jsx("span", { className: "cursor-default text-sm text-ui-fg-base", children: formatRelativeTime(occurredAt) }) });
2092
+ const SourceBadge = ({ source }) => {
2093
+ const info = describeSource(source);
2094
+ if (info.kind === "unknown") {
2095
+ return /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Unknown" });
2096
+ }
2097
+ return /* @__PURE__ */ jsx(Badge, { size: "2xsmall", color: sourceBadgeColor(info.kind), className: "shrink-0 whitespace-nowrap", children: info.label });
2098
+ };
2099
+ const AuditTable = ({ items, highlightId, onOpen }) => /* @__PURE__ */ jsx("div", { className: "hidden w-full overflow-hidden rounded-xl border border-ui-border-base md:block", children: /* @__PURE__ */ jsxs("table", { className: "w-full table-fixed divide-y divide-ui-border-base", children: [
2100
+ /* @__PURE__ */ jsx("thead", { className: "bg-ui-bg-subtle", children: /* @__PURE__ */ jsxs("tr", { children: [
2101
+ /* @__PURE__ */ jsx("th", { className: clx(HEADER_CELL, "w-[22%]"), children: "Entity" }),
2102
+ /* @__PURE__ */ jsx("th", { className: clx(HEADER_CELL, "w-[24%]"), children: "Record" }),
2103
+ /* @__PURE__ */ jsx("th", { className: clx(HEADER_CELL, "w-[13%]"), children: "Deleted" }),
2104
+ /* @__PURE__ */ jsx("th", { className: clx(HEADER_CELL, "w-[13%]"), children: "Deleted by" }),
2105
+ /* @__PURE__ */ jsx("th", { className: clx(HEADER_CELL, "w-[11%]"), children: "Source" }),
2106
+ /* @__PURE__ */ jsx("th", { className: clx(HEADER_CELL, "w-[8%]"), children: "Status" }),
2107
+ /* @__PURE__ */ jsx("th", { className: clx(HEADER_CELL, "w-[9%]"), children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Action" }) })
2108
+ ] }) }),
2109
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-ui-border-subtle", children: items.map((item) => {
2110
+ const entity = getEntityInfo(item.table_name);
2111
+ return /* @__PURE__ */ jsxs(
2112
+ "tr",
2113
+ {
2114
+ "data-audit-row": item.id,
2115
+ tabIndex: 0,
2116
+ onClick: () => onOpen(item.id),
2117
+ onKeyDown: (e) => {
2118
+ if (e.key === "Enter") onOpen(item.id);
2119
+ },
2120
+ className: clx(
2121
+ "cursor-pointer outline-none transition-colors hover:bg-ui-bg-subtle/60 focus-visible:bg-ui-bg-subtle",
2122
+ highlightId === item.id && "bg-ui-bg-highlight"
2123
+ ),
2124
+ children: [
2125
+ /* @__PURE__ */ jsx("td", { className: "min-w-0 px-3 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col", children: [
2126
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-ui-fg-base", title: entity.label, children: entity.label }),
2127
+ /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-ui-fg-muted", children: entity.category.label })
2128
+ ] }) }),
2129
+ /* @__PURE__ */ jsx("td", { className: "min-w-0 px-3 py-3", children: /* @__PURE__ */ jsx(CopyableId, { value: item.record_id }) }),
2130
+ /* @__PURE__ */ jsx("td", { className: "min-w-0 px-3 py-3", children: /* @__PURE__ */ jsx(DeletedWhen, { occurredAt: item.occurred_at }) }),
2131
+ /* @__PURE__ */ jsx("td", { className: "min-w-0 truncate px-3 py-3 text-sm", title: item.deleted_by ?? void 0, children: item.deleted_by ? /* @__PURE__ */ jsx("span", { className: "text-ui-fg-base", children: item.deleted_by }) : /* @__PURE__ */ jsx("span", { className: "text-ui-fg-muted", children: "Unknown" }) }),
2132
+ /* @__PURE__ */ jsx("td", { className: "min-w-0 px-3 py-3", children: /* @__PURE__ */ jsx(SourceBadge, { source: item.source }) }),
2133
+ /* @__PURE__ */ jsx("td", { className: "min-w-0 px-3 py-3", children: /* @__PURE__ */ jsx(Badge, { size: "2xsmall", color: "red", children: "Deleted" }) }),
2134
+ /* @__PURE__ */ jsx("td", { className: "px-3 py-3 text-right", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-0.5 whitespace-nowrap text-sm font-medium text-ui-fg-interactive", children: [
2135
+ "View",
2136
+ /* @__PURE__ */ jsx(ChevronRightMini, {})
2137
+ ] }) })
2138
+ ]
2139
+ },
2140
+ item.id
2141
+ );
2142
+ }) })
2143
+ ] }) });
2144
+ const AuditCards = ({ items, highlightId, onOpen }) => /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-2 md:hidden", children: items.map((item) => {
2145
+ const entity = getEntityInfo(item.table_name);
2146
+ return /* @__PURE__ */ jsx("li", { "data-audit-row": item.id, children: /* @__PURE__ */ jsxs(
2147
+ "div",
2148
+ {
2149
+ role: "button",
2150
+ tabIndex: 0,
2151
+ onClick: () => onOpen(item.id),
2152
+ onKeyDown: (e) => {
2153
+ if (e.key === "Enter") onOpen(item.id);
2154
+ },
2155
+ className: clx(
2156
+ "flex w-full flex-col gap-2 rounded-lg border border-ui-border-base p-3 text-left outline-none hover:bg-ui-bg-subtle/60 focus-visible:bg-ui-bg-subtle",
2157
+ highlightId === item.id && "bg-ui-bg-highlight"
2158
+ ),
2159
+ children: [
2160
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
2161
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col", children: [
2162
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-ui-fg-base", children: entity.label }),
2163
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-ui-fg-muted", children: entity.category.label })
2164
+ ] }),
2165
+ /* @__PURE__ */ jsx(DeletedWhen, { occurredAt: item.occurred_at })
2166
+ ] }),
2167
+ /* @__PURE__ */ jsx(CopyableId, { value: item.record_id }),
2168
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
2169
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
2170
+ /* @__PURE__ */ jsx(SourceBadge, { source: item.source }),
2171
+ item.deleted_by ? /* @__PURE__ */ jsxs("span", { className: "truncate text-xs text-ui-fg-subtle", children: [
2172
+ "by ",
2173
+ item.deleted_by
2174
+ ] }) : null
2175
+ ] }),
2176
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex shrink-0 items-center whitespace-nowrap text-sm font-medium text-ui-fg-interactive", children: [
2177
+ "View details",
2178
+ /* @__PURE__ */ jsx(ChevronRightMini, {})
2179
+ ] })
2180
+ ] })
2181
+ ]
2182
+ }
2183
+ ) }, item.id);
2184
+ }) });
2185
+ const ListSkeleton = () => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0 overflow-hidden rounded-xl border border-ui-border-base", "aria-busy": "true", children: [
2186
+ /* @__PURE__ */ jsx("div", { className: "bg-ui-bg-subtle px-3 py-3", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-40" }) }),
2187
+ Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 border-t border-ui-border-subtle px-3 py-4", children: [
2188
+ /* @__PURE__ */ jsxs("div", { className: "flex w-[22%] flex-col gap-1.5", children: [
2189
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-28" }),
2190
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-16" })
2191
+ ] }),
2192
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-[20%]" }),
2193
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-[10%]" }),
2194
+ /* @__PURE__ */ jsx(Skeleton, { className: "hidden h-3.5 w-[10%] md:block" }),
2195
+ /* @__PURE__ */ jsx(Skeleton, { className: "hidden h-5 w-14 rounded-full md:block" })
2196
+ ] }, i))
2197
+ ] });
2198
+ const EmptyState = ({ title, body, action }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 rounded-lg border border-dashed border-ui-border-strong px-6 py-12 text-center", children: [
2199
+ /* @__PURE__ */ jsx(MagnifierAlert, { className: "text-ui-fg-muted" }),
2200
+ /* @__PURE__ */ jsx(Heading, { level: "h3", children: title }),
2201
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "max-w-md text-ui-fg-subtle", children: body }),
2202
+ action ? /* @__PURE__ */ jsx("div", { className: "mt-2", children: action }) : null
2203
+ ] });
2204
+ const ErrorState = ({ onRetry, detail }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 rounded-lg border border-ui-border-base px-6 py-12 text-center", children: [
2205
+ /* @__PURE__ */ jsx(ExclamationCircle, { className: "text-ui-fg-error" }),
2206
+ /* @__PURE__ */ jsx(Heading, { level: "h3", children: "Unable to load deleted records" }),
2207
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "max-w-md text-ui-fg-subtle", children: "We couldn’t retrieve the audit records right now. Check your connection and try again." }),
2208
+ /* @__PURE__ */ jsx(Text, { size: "xsmall", className: "max-w-md font-mono text-ui-fg-muted", children: detail }),
2209
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", className: "mt-2", onClick: onRetry, children: "Try again" })
2210
+ ] });
2211
+ const config$9 = defineRouteConfig({
2212
+ label: "Deleted Records",
2213
+ icon: Trash
2214
+ });
926
2215
  const useDebounce$3 = (value, delay) => {
927
2216
  const [debouncedValue, setDebouncedValue] = useState(value);
928
2217
  useEffect(() => {
@@ -939,19 +2228,27 @@ const getStatusBadgeClass$7 = (status) => {
939
2228
  if (s2 === "pending" || s2 === "requires_more") return "bg-ui-tag-orange-bg text-ui-tag-orange-text";
940
2229
  return "bg-ui-tag-purple-bg text-ui-tag-purple-text";
941
2230
  };
942
- const PaymentsPage = () => {
2231
+ const RefundsPage = () => {
943
2232
  const navigate = useNavigate();
944
2233
  const [items, setItems] = useState([]);
945
- const [statusFilter, setStatusFilter] = useState("all");
946
2234
  const [orderIdSearch, setOrderIdSearch] = useState("");
947
2235
  const debouncedOrderId = useDebounce$3(orderIdSearch, 300);
2236
+ const [paymentStatusFilter, setPaymentStatusFilter] = useState("all");
2237
+ const [providerSearch, setProviderSearch] = useState("");
2238
+ const debouncedProvider = useDebounce$3(providerSearch, 300);
2239
+ const [currencySearch, setCurrencySearch] = useState("");
2240
+ const debouncedCurrency = useDebounce$3(currencySearch, 300);
2241
+ const [dateFrom, setDateFrom] = useState("");
2242
+ const [dateTo, setDateTo] = useState("");
2243
+ const [amountMin, setAmountMin] = useState("");
2244
+ const [amountMax, setAmountMax] = useState("");
948
2245
  const [isLoading, setIsLoading] = useState(true);
949
2246
  const [isFetchingMore, setIsFetchingMore] = useState(false);
950
2247
  const [error, setError] = useState(null);
951
2248
  const [offset, setOffset] = useState(0);
952
2249
  const [count, setCount] = useState(0);
953
2250
  const limit = 50;
954
- const loadTransactions = useCallback(
2251
+ const loadRefunds = useCallback(
955
2252
  async (nextOffset, replace) => {
956
2253
  try {
957
2254
  if (replace) setIsLoading(true);
@@ -960,18 +2257,25 @@ const PaymentsPage = () => {
960
2257
  const params = new URLSearchParams();
961
2258
  params.set("limit", String(limit));
962
2259
  params.set("offset", String(nextOffset));
963
- if (statusFilter !== "all") params.set("status", statusFilter);
964
2260
  if (debouncedOrderId.trim()) params.set("order_id", debouncedOrderId.trim());
965
- const response = await fetch(
966
- `/admin/payment-transactions?${params.toString()}`,
967
- { credentials: "include" }
968
- );
2261
+ if (paymentStatusFilter !== "all") params.set("payment_status", paymentStatusFilter);
2262
+ if (debouncedProvider.trim()) params.set("provider_id", debouncedProvider.trim());
2263
+ if (debouncedCurrency.trim()) params.set("currency_code", debouncedCurrency.trim());
2264
+ if (dateFrom.trim()) params.set("date_from", dateFrom.trim());
2265
+ if (dateTo.trim()) params.set("date_to", dateTo.trim());
2266
+ const min = amountMin.trim() ? Number(amountMin) : void 0;
2267
+ const max = amountMax.trim() ? Number(amountMax) : void 0;
2268
+ if (min != null && !Number.isNaN(min)) params.set("amount_min", String(min));
2269
+ if (max != null && !Number.isNaN(max)) params.set("amount_max", String(max));
2270
+ const response = await fetch(`/admin/refunds?${params.toString()}`, {
2271
+ credentials: "include"
2272
+ });
969
2273
  if (!response.ok) {
970
2274
  const text = await response.text();
971
- throw new Error(text || "Failed to load payment transactions");
2275
+ throw new Error(text || "Failed to load refunds");
972
2276
  }
973
2277
  const payload = await response.json();
974
- const list = payload.transactions ?? [];
2278
+ const list = payload.refunds ?? [];
975
2279
  setCount(payload.count ?? 0);
976
2280
  setOffset(nextOffset + list.length);
977
2281
  setItems((prev) => replace ? list : [...prev, ...list]);
@@ -982,242 +2286,27 @@ const PaymentsPage = () => {
982
2286
  setIsFetchingMore(false);
983
2287
  }
984
2288
  },
985
- [statusFilter, debouncedOrderId]
2289
+ [
2290
+ debouncedOrderId,
2291
+ paymentStatusFilter,
2292
+ debouncedProvider,
2293
+ debouncedCurrency,
2294
+ dateFrom,
2295
+ dateTo,
2296
+ amountMin,
2297
+ amountMax
2298
+ ]
986
2299
  );
987
2300
  useEffect(() => {
988
- void loadTransactions(0, true);
989
- }, [loadTransactions]);
2301
+ void loadRefunds(0, true);
2302
+ }, [loadRefunds]);
990
2303
  const hasMore = useMemo(() => offset < count, [offset, count]);
991
- const displayStatus = (t) => {
992
- const s2 = t.payment_id != null && t.payment_status != null && t.payment_status !== "" ? t.payment_status : t.session_status ?? "";
993
- return s2 !== "" ? s2 : "—";
994
- };
995
- const displayAmount = (t) => {
996
- const code = (t.currency_code ?? "USD").toUpperCase();
997
- return `${code} ${Number(t.amount)}`;
998
- };
999
- return /* @__PURE__ */ jsx("div", { className: "w-full p-6", children: /* @__PURE__ */ jsxs(Container, { className: "mx-auto flex w-full max-w-7xl flex-col gap-6 p-6", children: [
1000
- /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
1001
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
1002
- /* @__PURE__ */ jsx(Heading, { level: "h1", children: "Payments" }),
1003
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "All payment attempts — completed, pending, failed, requires action" })
1004
- ] }),
1005
- /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: () => loadTransactions(0, true), children: "Refresh" })
1006
- ] }),
1007
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
1008
- /* @__PURE__ */ jsx(
1009
- Input,
1010
- {
1011
- placeholder: "Search by Order ID",
1012
- value: orderIdSearch,
1013
- onChange: (e) => setOrderIdSearch(e.target.value),
1014
- className: "md:max-w-sm",
1015
- "aria-label": "Search by order ID"
1016
- }
1017
- ),
1018
- /* @__PURE__ */ jsx("div", { className: "flex gap-3", children: /* @__PURE__ */ jsxs(
1019
- "select",
1020
- {
1021
- value: statusFilter,
1022
- onChange: (e) => setStatusFilter(e.target.value),
1023
- className: "h-9 rounded-md border border-ui-border-base bg-transparent px-3 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive md:max-w-xs",
1024
- "aria-label": "Filter by status",
1025
- children: [
1026
- /* @__PURE__ */ jsx("option", { value: "all", children: "All statuses" }),
1027
- /* @__PURE__ */ jsx("option", { value: "pending", children: "Pending" }),
1028
- /* @__PURE__ */ jsx("option", { value: "requires_more", children: "Requires more" }),
1029
- /* @__PURE__ */ jsx("option", { value: "error", children: "Error" }),
1030
- /* @__PURE__ */ jsx("option", { value: "canceled", children: "Canceled" }),
1031
- /* @__PURE__ */ jsx("option", { value: "authorized", children: "Authorized" }),
1032
- /* @__PURE__ */ jsx("option", { value: "captured", children: "Captured" })
1033
- ]
1034
- }
1035
- ) })
1036
- ] }),
1037
- error ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-ui-border-strong p-6 text-center", children: [
1038
- /* @__PURE__ */ jsx(Text, { weight: "plus", className: "text-ui-fg-error", children: error }),
1039
- /* @__PURE__ */ jsx("div", { className: "mt-4 flex justify-center", children: /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => loadTransactions(0, true), children: "Try again" }) })
1040
- ] }) : null,
1041
- isLoading ? /* @__PURE__ */ jsx("div", { className: "flex justify-center py-16", children: /* @__PURE__ */ jsx(Text, { children: "Loading payments…" }) }) : items.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-dashed border-ui-border-strong p-10 text-center", children: [
1042
- /* @__PURE__ */ jsx(Heading, { level: "h3", className: "text-xl", children: "No payment transactions yet" }),
1043
- /* @__PURE__ */ jsx(Text, { size: "small", className: "mt-2 text-ui-fg-subtle", children: "Payment attempts will appear here." })
1044
- ] }) : /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-xl border border-ui-border-base", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full divide-y divide-ui-border-base", children: [
1045
- /* @__PURE__ */ jsx("thead", { className: "bg-ui-bg-subtle", children: /* @__PURE__ */ jsxs("tr", { children: [
1046
- /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Order ID" }),
1047
- /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Amount" }),
1048
- /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Provider" }),
1049
- /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Status" }),
1050
- /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Finalized" }),
1051
- /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Created" }),
1052
- /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Actions" })
1053
- ] }) }),
1054
- /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-ui-border-subtle", children: items.map((t) => /* @__PURE__ */ jsxs(
1055
- "tr",
1056
- {
1057
- className: "hover:bg-ui-bg-subtle/60 cursor-pointer",
1058
- onClick: () => navigate(`/payments/${t.payment_session_id}`),
1059
- children: [
1060
- /* @__PURE__ */ jsx("td", { className: "px-4 py-4 font-medium text-ui-fg-base", children: t.order_id ?? "—" }),
1061
- /* @__PURE__ */ jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: displayAmount(t) }),
1062
- /* @__PURE__ */ jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: t.provider_id }),
1063
- /* @__PURE__ */ jsx("td", { className: "px-4 py-4", children: /* @__PURE__ */ jsx(
1064
- Badge,
1065
- {
1066
- size: "2xsmall",
1067
- className: `uppercase ${getStatusBadgeClass$7(displayStatus(t))}`,
1068
- children: displayStatus(t) !== "—" ? displayStatus(t).replace(/_/g, " ") : "—"
1069
- }
1070
- ) }),
1071
- /* @__PURE__ */ jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: t.payment_id != null ? "Yes" : "No" }),
1072
- /* @__PURE__ */ jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: new Date(t.created_at).toLocaleDateString("en-US", {
1073
- year: "numeric",
1074
- month: "short",
1075
- day: "numeric",
1076
- hour: "numeric",
1077
- minute: "2-digit",
1078
- hour12: true
1079
- }) }),
1080
- /* @__PURE__ */ jsxs("td", { className: "px-4 py-4", children: [
1081
- /* @__PURE__ */ jsx(
1082
- Button,
1083
- {
1084
- variant: "transparent",
1085
- size: "small",
1086
- onClick: (e) => {
1087
- e.stopPropagation();
1088
- navigate(`/payments/${t.payment_session_id}`);
1089
- },
1090
- children: "View details"
1091
- }
1092
- ),
1093
- t.order_id ? /* @__PURE__ */ jsx(
1094
- Button,
1095
- {
1096
- variant: "transparent",
1097
- size: "small",
1098
- onClick: (e) => {
1099
- e.stopPropagation();
1100
- navigate(`/orders/${t.order_id}`);
1101
- },
1102
- children: "Order"
1103
- }
1104
- ) : null
1105
- ] })
1106
- ]
1107
- },
1108
- t.payment_session_id
1109
- )) })
1110
- ] }) }),
1111
- hasMore ? /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(
1112
- Button,
1113
- {
1114
- variant: "secondary",
1115
- isLoading: isFetchingMore,
1116
- onClick: () => loadTransactions(offset, false),
1117
- children: "Load more"
1118
- }
1119
- ) }) : null
1120
- ] }) });
1121
- };
1122
- const config$7 = defineRouteConfig({
1123
- label: "Payments",
1124
- icon: CreditCard
1125
- });
1126
- const useDebounce$2 = (value, delay) => {
1127
- const [debouncedValue, setDebouncedValue] = useState(value);
1128
- useEffect(() => {
1129
- const handler = setTimeout(() => setDebouncedValue(value), delay);
1130
- return () => clearTimeout(handler);
1131
- }, [value, delay]);
1132
- return debouncedValue;
1133
- };
1134
- const getStatusBadgeClass$6 = (status) => {
1135
- const s2 = status.toLowerCase();
1136
- if (s2 === "captured" || s2 === "completed") return "bg-ui-tag-green-bg text-ui-tag-green-text";
1137
- if (s2 === "authorized") return "bg-ui-tag-blue-bg text-ui-tag-blue-text";
1138
- if (s2 === "error" || s2 === "canceled" || s2 === "cancelled") return "bg-ui-tag-red-bg text-ui-tag-red-text";
1139
- if (s2 === "pending" || s2 === "requires_more") return "bg-ui-tag-orange-bg text-ui-tag-orange-text";
1140
- return "bg-ui-tag-purple-bg text-ui-tag-purple-text";
1141
- };
1142
- const RefundsPage = () => {
1143
- const navigate = useNavigate();
1144
- const [items, setItems] = useState([]);
1145
- const [orderIdSearch, setOrderIdSearch] = useState("");
1146
- const debouncedOrderId = useDebounce$2(orderIdSearch, 300);
1147
- const [paymentStatusFilter, setPaymentStatusFilter] = useState("all");
1148
- const [providerSearch, setProviderSearch] = useState("");
1149
- const debouncedProvider = useDebounce$2(providerSearch, 300);
1150
- const [currencySearch, setCurrencySearch] = useState("");
1151
- const debouncedCurrency = useDebounce$2(currencySearch, 300);
1152
- const [dateFrom, setDateFrom] = useState("");
1153
- const [dateTo, setDateTo] = useState("");
1154
- const [amountMin, setAmountMin] = useState("");
1155
- const [amountMax, setAmountMax] = useState("");
1156
- const [isLoading, setIsLoading] = useState(true);
1157
- const [isFetchingMore, setIsFetchingMore] = useState(false);
1158
- const [error, setError] = useState(null);
1159
- const [offset, setOffset] = useState(0);
1160
- const [count, setCount] = useState(0);
1161
- const limit = 50;
1162
- const loadRefunds = useCallback(
1163
- async (nextOffset, replace) => {
1164
- try {
1165
- if (replace) setIsLoading(true);
1166
- else setIsFetchingMore(true);
1167
- setError(null);
1168
- const params = new URLSearchParams();
1169
- params.set("limit", String(limit));
1170
- params.set("offset", String(nextOffset));
1171
- if (debouncedOrderId.trim()) params.set("order_id", debouncedOrderId.trim());
1172
- if (paymentStatusFilter !== "all") params.set("payment_status", paymentStatusFilter);
1173
- if (debouncedProvider.trim()) params.set("provider_id", debouncedProvider.trim());
1174
- if (debouncedCurrency.trim()) params.set("currency_code", debouncedCurrency.trim());
1175
- if (dateFrom.trim()) params.set("date_from", dateFrom.trim());
1176
- if (dateTo.trim()) params.set("date_to", dateTo.trim());
1177
- const min = amountMin.trim() ? Number(amountMin) : void 0;
1178
- const max = amountMax.trim() ? Number(amountMax) : void 0;
1179
- if (min != null && !Number.isNaN(min)) params.set("amount_min", String(min));
1180
- if (max != null && !Number.isNaN(max)) params.set("amount_max", String(max));
1181
- const response = await fetch(`/admin/refunds?${params.toString()}`, {
1182
- credentials: "include"
1183
- });
1184
- if (!response.ok) {
1185
- const text = await response.text();
1186
- throw new Error(text || "Failed to load refunds");
1187
- }
1188
- const payload = await response.json();
1189
- const list = payload.refunds ?? [];
1190
- setCount(payload.count ?? 0);
1191
- setOffset(nextOffset + list.length);
1192
- setItems((prev) => replace ? list : [...prev, ...list]);
1193
- } catch (e) {
1194
- setError(e instanceof Error ? e.message : "Failed to load");
1195
- } finally {
1196
- setIsLoading(false);
1197
- setIsFetchingMore(false);
1198
- }
1199
- },
1200
- [
1201
- debouncedOrderId,
1202
- paymentStatusFilter,
1203
- debouncedProvider,
1204
- debouncedCurrency,
1205
- dateFrom,
1206
- dateTo,
1207
- amountMin,
1208
- amountMax
1209
- ]
1210
- );
1211
- useEffect(() => {
1212
- void loadRefunds(0, true);
1213
- }, [loadRefunds]);
1214
- const hasMore = useMemo(() => offset < count, [offset, count]);
1215
- const displayAmount = (r) => {
1216
- const code = (r.currency_code ?? "USD").toUpperCase();
1217
- return `${code} ${Number(r.amount)}`;
1218
- };
1219
- const displayStatus = (r) => {
1220
- const s2 = r.payment_status ?? "";
2304
+ const displayAmount = (r) => {
2305
+ const code = (r.currency_code ?? "USD").toUpperCase();
2306
+ return `${code} ${Number(r.amount)}`;
2307
+ };
2308
+ const displayStatus = (r) => {
2309
+ const s2 = r.payment_status ?? "";
1221
2310
  return s2 !== "" ? s2 : "—";
1222
2311
  };
1223
2312
  return /* @__PURE__ */ jsx("div", { className: "w-full p-6", children: /* @__PURE__ */ jsxs(Container, { className: "mx-auto flex w-full max-w-7xl flex-col gap-6 p-6", children: [
@@ -1360,7 +2449,7 @@ const RefundsPage = () => {
1360
2449
  Badge,
1361
2450
  {
1362
2451
  size: "2xsmall",
1363
- className: `uppercase ${getStatusBadgeClass$6(displayStatus(r))}`,
2452
+ className: `uppercase ${getStatusBadgeClass$7(displayStatus(r))}`,
1364
2453
  children: displayStatus(r) !== "—" ? displayStatus(r).replace(/_/g, " ") : "—"
1365
2454
  }
1366
2455
  ) }),
@@ -1414,11 +2503,11 @@ const RefundsPage = () => {
1414
2503
  ) }) : null
1415
2504
  ] }) });
1416
2505
  };
1417
- const config$6 = defineRouteConfig({
2506
+ const config$8 = defineRouteConfig({
1418
2507
  label: "Refunds",
1419
2508
  icon: Receipt
1420
2509
  });
1421
- const useDebounce$1 = (value, delay) => {
2510
+ const useDebounce$2 = (value, delay) => {
1422
2511
  const [debouncedValue, setDebouncedValue] = useState(value);
1423
2512
  useEffect(() => {
1424
2513
  const handler = setTimeout(() => setDebouncedValue(value), delay);
@@ -1426,7 +2515,7 @@ const useDebounce$1 = (value, delay) => {
1426
2515
  }, [value, delay]);
1427
2516
  return debouncedValue;
1428
2517
  };
1429
- const getStatusBadgeClass$5 = (status) => {
2518
+ const getStatusBadgeClass$6 = (status) => {
1430
2519
  const statusLower = status.toLowerCase();
1431
2520
  if (statusLower === "requested") {
1432
2521
  return "bg-ui-tag-orange-bg text-ui-tag-orange-text";
@@ -1451,7 +2540,7 @@ const ReturnsPage = () => {
1451
2540
  const [statusFilter, setStatusFilter] = useState("all");
1452
2541
  const [createdByFilter, setCreatedByFilter] = useState("all");
1453
2542
  const [searchQuery, setSearchQuery] = useState("");
1454
- const debouncedSearchQuery = useDebounce$1(searchQuery, 300);
2543
+ const debouncedSearchQuery = useDebounce$2(searchQuery, 300);
1455
2544
  const [isLoading, setIsLoading] = useState(true);
1456
2545
  const [isFetchingMore, setIsFetchingMore] = useState(false);
1457
2546
  const [error, setError] = useState(null);
@@ -1596,7 +2685,7 @@ const ReturnsPage = () => {
1596
2685
  Badge,
1597
2686
  {
1598
2687
  size: "2xsmall",
1599
- className: `uppercase ${getStatusBadgeClass$5(returnOrder.status)}`,
2688
+ className: `uppercase ${getStatusBadgeClass$6(returnOrder.status)}`,
1600
2689
  children: returnOrder.status.replace(/_/g, " ")
1601
2690
  }
1602
2691
  ) }),
@@ -1655,11 +2744,11 @@ const ReturnsPage = () => {
1655
2744
  ) }) : null
1656
2745
  ] }) });
1657
2746
  };
1658
- const config$5 = defineRouteConfig({
2747
+ const config$7 = defineRouteConfig({
1659
2748
  label: "Return Orders",
1660
2749
  icon: ArrowPath
1661
2750
  });
1662
- const useDebounce = (value, delay) => {
2751
+ const useDebounce$1 = (value, delay) => {
1663
2752
  const [debouncedValue, setDebouncedValue] = useState(value);
1664
2753
  useEffect(() => {
1665
2754
  const handler = setTimeout(() => setDebouncedValue(value), delay);
@@ -1667,7 +2756,7 @@ const useDebounce = (value, delay) => {
1667
2756
  }, [value, delay]);
1668
2757
  return debouncedValue;
1669
2758
  };
1670
- const getStatusBadgeClass$4 = (status) => {
2759
+ const getStatusBadgeClass$5 = (status) => {
1671
2760
  const statusLower = status.toLowerCase();
1672
2761
  if (statusLower === "requested") {
1673
2762
  return "bg-ui-tag-orange-bg text-ui-tag-orange-text";
@@ -1692,7 +2781,7 @@ const SwapsPage = () => {
1692
2781
  const [statusFilter, setStatusFilter] = useState("all");
1693
2782
  const [createdByFilter, setCreatedByFilter] = useState("all");
1694
2783
  const [searchQuery, setSearchQuery] = useState("");
1695
- const debouncedSearchQuery = useDebounce(searchQuery, 300);
2784
+ const debouncedSearchQuery = useDebounce$1(searchQuery, 300);
1696
2785
  const [isLoading, setIsLoading] = useState(true);
1697
2786
  const [isFetchingMore, setIsFetchingMore] = useState(false);
1698
2787
  const [error, setError] = useState(null);
@@ -1834,7 +2923,7 @@ const SwapsPage = () => {
1834
2923
  Badge,
1835
2924
  {
1836
2925
  size: "2xsmall",
1837
- className: `uppercase ${getStatusBadgeClass$4(swap.status)}`,
2926
+ className: `uppercase ${getStatusBadgeClass$5(swap.status)}`,
1838
2927
  children: swap.status.replace(/_/g, " ")
1839
2928
  }
1840
2929
  ) }),
@@ -1884,11 +2973,19 @@ const SwapsPage = () => {
1884
2973
  ) }) : null
1885
2974
  ] }) });
1886
2975
  };
1887
- const config$4 = defineRouteConfig({
2976
+ const config$6 = defineRouteConfig({
1888
2977
  label: "Exchanges",
1889
2978
  icon: ArrowPath
1890
2979
  });
1891
- const getStatusBadgeClass$3 = (status) => {
2980
+ const useDebounce = (value, delay) => {
2981
+ const [debouncedValue, setDebouncedValue] = useState(value);
2982
+ useEffect(() => {
2983
+ const handler = setTimeout(() => setDebouncedValue(value), delay);
2984
+ return () => clearTimeout(handler);
2985
+ }, [value, delay]);
2986
+ return debouncedValue;
2987
+ };
2988
+ const getStatusBadgeClass$4 = (status) => {
1892
2989
  const s2 = status.toLowerCase();
1893
2990
  if (s2 === "captured" || s2 === "completed") return "bg-ui-tag-green-bg text-ui-tag-green-text";
1894
2991
  if (s2 === "authorized") return "bg-ui-tag-blue-bg text-ui-tag-blue-text";
@@ -1896,117 +2993,933 @@ const getStatusBadgeClass$3 = (status) => {
1896
2993
  if (s2 === "pending" || s2 === "requires_more") return "bg-ui-tag-orange-bg text-ui-tag-orange-text";
1897
2994
  return "bg-ui-tag-purple-bg text-ui-tag-purple-text";
1898
2995
  };
1899
- const PaymentDetailPage = () => {
2996
+ const PaymentsPage = () => {
1900
2997
  const navigate = useNavigate();
1901
- const params = useParams();
1902
- const id = params?.id?.trim();
1903
- const [detail, setDetail] = useState(null);
1904
- const [loading, setLoading] = useState(!!id);
1905
- const [error, setError] = useState(null);
1906
- useEffect(() => {
1907
- if (!id) {
1908
- setLoading(false);
1909
- return;
2998
+ const [items, setItems] = useState([]);
2999
+ const [statusFilter, setStatusFilter] = useState("all");
3000
+ const [orderIdSearch, setOrderIdSearch] = useState("");
3001
+ const debouncedOrderId = useDebounce(orderIdSearch, 300);
3002
+ const [isLoading, setIsLoading] = useState(true);
3003
+ const [isFetchingMore, setIsFetchingMore] = useState(false);
3004
+ const [error, setError] = useState(null);
3005
+ const [offset, setOffset] = useState(0);
3006
+ const [count, setCount] = useState(0);
3007
+ const limit = 50;
3008
+ const loadTransactions = useCallback(
3009
+ async (nextOffset, replace) => {
3010
+ try {
3011
+ if (replace) setIsLoading(true);
3012
+ else setIsFetchingMore(true);
3013
+ setError(null);
3014
+ const params = new URLSearchParams();
3015
+ params.set("limit", String(limit));
3016
+ params.set("offset", String(nextOffset));
3017
+ if (statusFilter !== "all") params.set("status", statusFilter);
3018
+ if (debouncedOrderId.trim()) params.set("order_id", debouncedOrderId.trim());
3019
+ const response = await fetch(
3020
+ `/admin/payment-transactions?${params.toString()}`,
3021
+ { credentials: "include" }
3022
+ );
3023
+ if (!response.ok) {
3024
+ const text = await response.text();
3025
+ throw new Error(text || "Failed to load payment transactions");
3026
+ }
3027
+ const payload = await response.json();
3028
+ const list = payload.transactions ?? [];
3029
+ setCount(payload.count ?? 0);
3030
+ setOffset(nextOffset + list.length);
3031
+ setItems((prev) => replace ? list : [...prev, ...list]);
3032
+ } catch (e) {
3033
+ setError(e instanceof Error ? e.message : "Failed to load");
3034
+ } finally {
3035
+ setIsLoading(false);
3036
+ setIsFetchingMore(false);
3037
+ }
3038
+ },
3039
+ [statusFilter, debouncedOrderId]
3040
+ );
3041
+ useEffect(() => {
3042
+ void loadTransactions(0, true);
3043
+ }, [loadTransactions]);
3044
+ const hasMore = useMemo(() => offset < count, [offset, count]);
3045
+ const displayStatus = (t) => {
3046
+ const s2 = t.payment_id != null && t.payment_status != null && t.payment_status !== "" ? t.payment_status : t.session_status ?? "";
3047
+ return s2 !== "" ? s2 : "—";
3048
+ };
3049
+ const displayAmount = (t) => {
3050
+ const code = (t.currency_code ?? "USD").toUpperCase();
3051
+ return `${code} ${Number(t.amount)}`;
3052
+ };
3053
+ return /* @__PURE__ */ jsx("div", { className: "w-full p-6", children: /* @__PURE__ */ jsxs(Container, { className: "mx-auto flex w-full max-w-7xl flex-col gap-6 p-6", children: [
3054
+ /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
3055
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
3056
+ /* @__PURE__ */ jsx(Heading, { level: "h1", children: "Payments" }),
3057
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "All payment attempts — completed, pending, failed, requires action" })
3058
+ ] }),
3059
+ /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: () => loadTransactions(0, true), children: "Refresh" })
3060
+ ] }),
3061
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
3062
+ /* @__PURE__ */ jsx(
3063
+ Input,
3064
+ {
3065
+ placeholder: "Search by Order ID",
3066
+ value: orderIdSearch,
3067
+ onChange: (e) => setOrderIdSearch(e.target.value),
3068
+ className: "md:max-w-sm",
3069
+ "aria-label": "Search by order ID"
3070
+ }
3071
+ ),
3072
+ /* @__PURE__ */ jsx("div", { className: "flex gap-3", children: /* @__PURE__ */ jsxs(
3073
+ "select",
3074
+ {
3075
+ value: statusFilter,
3076
+ onChange: (e) => setStatusFilter(e.target.value),
3077
+ className: "h-9 rounded-md border border-ui-border-base bg-transparent px-3 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive md:max-w-xs",
3078
+ "aria-label": "Filter by status",
3079
+ children: [
3080
+ /* @__PURE__ */ jsx("option", { value: "all", children: "All statuses" }),
3081
+ /* @__PURE__ */ jsx("option", { value: "pending", children: "Pending" }),
3082
+ /* @__PURE__ */ jsx("option", { value: "requires_more", children: "Requires more" }),
3083
+ /* @__PURE__ */ jsx("option", { value: "error", children: "Error" }),
3084
+ /* @__PURE__ */ jsx("option", { value: "canceled", children: "Canceled" }),
3085
+ /* @__PURE__ */ jsx("option", { value: "authorized", children: "Authorized" }),
3086
+ /* @__PURE__ */ jsx("option", { value: "captured", children: "Captured" })
3087
+ ]
3088
+ }
3089
+ ) })
3090
+ ] }),
3091
+ error ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-ui-border-strong p-6 text-center", children: [
3092
+ /* @__PURE__ */ jsx(Text, { weight: "plus", className: "text-ui-fg-error", children: error }),
3093
+ /* @__PURE__ */ jsx("div", { className: "mt-4 flex justify-center", children: /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => loadTransactions(0, true), children: "Try again" }) })
3094
+ ] }) : null,
3095
+ isLoading ? /* @__PURE__ */ jsx("div", { className: "flex justify-center py-16", children: /* @__PURE__ */ jsx(Text, { children: "Loading payments…" }) }) : items.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-dashed border-ui-border-strong p-10 text-center", children: [
3096
+ /* @__PURE__ */ jsx(Heading, { level: "h3", className: "text-xl", children: "No payment transactions yet" }),
3097
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "mt-2 text-ui-fg-subtle", children: "Payment attempts will appear here." })
3098
+ ] }) : /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-xl border border-ui-border-base", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full divide-y divide-ui-border-base", children: [
3099
+ /* @__PURE__ */ jsx("thead", { className: "bg-ui-bg-subtle", children: /* @__PURE__ */ jsxs("tr", { children: [
3100
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Order ID" }),
3101
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Amount" }),
3102
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Provider" }),
3103
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Status" }),
3104
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Finalized" }),
3105
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Created" }),
3106
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Actions" })
3107
+ ] }) }),
3108
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-ui-border-subtle", children: items.map((t) => /* @__PURE__ */ jsxs(
3109
+ "tr",
3110
+ {
3111
+ className: "hover:bg-ui-bg-subtle/60 cursor-pointer",
3112
+ onClick: () => navigate(`/payments/${t.payment_session_id}`),
3113
+ children: [
3114
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-4 font-medium text-ui-fg-base", children: t.order_id ?? "—" }),
3115
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: displayAmount(t) }),
3116
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: t.provider_id }),
3117
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-4", children: /* @__PURE__ */ jsx(
3118
+ Badge,
3119
+ {
3120
+ size: "2xsmall",
3121
+ className: `uppercase ${getStatusBadgeClass$4(displayStatus(t))}`,
3122
+ children: displayStatus(t) !== "—" ? displayStatus(t).replace(/_/g, " ") : "—"
3123
+ }
3124
+ ) }),
3125
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: t.payment_id != null ? "Yes" : "No" }),
3126
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: new Date(t.created_at).toLocaleDateString("en-US", {
3127
+ year: "numeric",
3128
+ month: "short",
3129
+ day: "numeric",
3130
+ hour: "numeric",
3131
+ minute: "2-digit",
3132
+ hour12: true
3133
+ }) }),
3134
+ /* @__PURE__ */ jsxs("td", { className: "px-4 py-4", children: [
3135
+ /* @__PURE__ */ jsx(
3136
+ Button,
3137
+ {
3138
+ variant: "transparent",
3139
+ size: "small",
3140
+ onClick: (e) => {
3141
+ e.stopPropagation();
3142
+ navigate(`/payments/${t.payment_session_id}`);
3143
+ },
3144
+ children: "View details"
3145
+ }
3146
+ ),
3147
+ t.order_id ? /* @__PURE__ */ jsx(
3148
+ Button,
3149
+ {
3150
+ variant: "transparent",
3151
+ size: "small",
3152
+ onClick: (e) => {
3153
+ e.stopPropagation();
3154
+ navigate(`/orders/${t.order_id}`);
3155
+ },
3156
+ children: "Order"
3157
+ }
3158
+ ) : null
3159
+ ] })
3160
+ ]
3161
+ },
3162
+ t.payment_session_id
3163
+ )) })
3164
+ ] }) }),
3165
+ hasMore ? /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(
3166
+ Button,
3167
+ {
3168
+ variant: "secondary",
3169
+ isLoading: isFetchingMore,
3170
+ onClick: () => loadTransactions(offset, false),
3171
+ children: "Load more"
3172
+ }
3173
+ ) }) : null
3174
+ ] }) });
3175
+ };
3176
+ const config$5 = defineRouteConfig({
3177
+ label: "Payments",
3178
+ icon: CreditCard
3179
+ });
3180
+ const MASKED_VALUE = "••••••••••••";
3181
+ const SENSITIVE_KEY_PATTERNS = [
3182
+ /password|passwd|passphrase/i,
3183
+ /secret/i,
3184
+ /token/i,
3185
+ /api_?key$/i,
3186
+ /private_?key/i,
3187
+ /access_?key/i,
3188
+ /(^|_)hash(ed)?($|_)/i,
3189
+ /(^|_)salt$/i,
3190
+ /^cvv$|^cvc$|security_code/i,
3191
+ /card_?number/i,
3192
+ /account_?number/i,
3193
+ /credential/i,
3194
+ /authorization/i,
3195
+ /recovery_?code/i,
3196
+ /(^|_)otp($|_)/i,
3197
+ /verification_?code/i,
3198
+ /^code_hash$/i,
3199
+ /webhook_?signature/i
3200
+ ];
3201
+ const TABLE_SENSITIVE_KEYS = {
3202
+ otp_verification: /* @__PURE__ */ new Set(["code", "value"]),
3203
+ auth_verification: /* @__PURE__ */ new Set(["code", "value"]),
3204
+ auth_mfa_factor: /* @__PURE__ */ new Set(["value", "config"])
3205
+ };
3206
+ const SAFE_KEYS = /* @__PURE__ */ new Set([
3207
+ "token_type",
3208
+ "redacted",
3209
+ "api_key_id",
3210
+ "token_id",
3211
+ "notification_token_id"
3212
+ ]);
3213
+ function isSensitiveKey(key) {
3214
+ const normalized = key.trim().toLowerCase();
3215
+ if (SAFE_KEYS.has(normalized)) return false;
3216
+ return SENSITIVE_KEY_PATTERNS.some((pattern) => pattern.test(normalized));
3217
+ }
3218
+ const FULLY_SECRET_TABLES = /* @__PURE__ */ new Set(["auth_mfa_recovery_code", "auth_password_reset_token"]);
3219
+ const ALWAYS_VISIBLE_KEYS = /* @__PURE__ */ new Set(["id", "created_at", "updated_at", "deleted_at", "expires_at", "used_at"]);
3220
+ function maskValue(value, ctx, depth) {
3221
+ if (Array.isArray(value)) return value.map((item) => maskValue(item, ctx, depth + 1));
3222
+ if (value !== null && typeof value === "object") {
3223
+ const out = {};
3224
+ for (const [key, inner] of Object.entries(value)) {
3225
+ const topLevel = depth === 0;
3226
+ const secret = inner !== null && inner !== void 0 && inner !== "" && (isSensitiveKey(key) || topLevel && ctx.tableKeys?.has(key.toLowerCase()) === true || topLevel && ctx.maskAll && !ALWAYS_VISIBLE_KEYS.has(key) && !key.endsWith("_id"));
3227
+ if (secret) {
3228
+ ctx.count += 1;
3229
+ out[key] = MASKED_VALUE;
3230
+ } else {
3231
+ out[key] = maskValue(inner, ctx, depth + 1);
3232
+ }
1910
3233
  }
1911
- let cancelled = false;
1912
- setLoading(true);
1913
- setError(null);
1914
- fetch(`/admin/payment-transactions/${id}`, { credentials: "include" }).then((res) => {
1915
- if (!res.ok) throw new Error(res.statusText || "Failed to load");
1916
- return res.json();
1917
- }).then((data) => {
1918
- if (!cancelled) setDetail(data);
1919
- }).catch((e) => {
1920
- if (!cancelled) setError(e instanceof Error ? e.message : "Failed to load");
1921
- }).finally(() => {
1922
- if (!cancelled) setLoading(false);
3234
+ return out;
3235
+ }
3236
+ return value;
3237
+ }
3238
+ function maskSensitiveData(data, tableName) {
3239
+ const ctx = {
3240
+ count: 0,
3241
+ maskAll: tableName ? FULLY_SECRET_TABLES.has(tableName) : false,
3242
+ tableKeys: tableName ? TABLE_SENSITIVE_KEYS[tableName] : void 0
3243
+ };
3244
+ const value = maskValue(data, ctx, 0);
3245
+ return { value, maskedCount: ctx.count };
3246
+ }
3247
+ const AUTO_EXPAND_DEPTH = 1;
3248
+ const LONG_STRING = 200;
3249
+ const JsonViewer = ({ data }) => {
3250
+ const [generation, setGeneration] = useState(0);
3251
+ const [expandAll, setExpandAll] = useState(null);
3252
+ const setAll = (value) => {
3253
+ setExpandAll(value);
3254
+ setGeneration((g) => g + 1);
3255
+ };
3256
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
3257
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
3258
+ /* @__PURE__ */ jsx(Button, { variant: "transparent", size: "small", onClick: () => setAll(true), children: "Expand all" }),
3259
+ /* @__PURE__ */ jsx(Button, { variant: "transparent", size: "small", onClick: () => setAll(false), children: "Collapse all" })
3260
+ ] }),
3261
+ /* @__PURE__ */ jsx("div", { className: "max-h-[560px] overflow-auto rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3 font-mono text-xs leading-5", children: /* @__PURE__ */ jsx(JsonNode, { value: data, depth: 0, forceExpand: expandAll }, generation) })
3262
+ ] });
3263
+ };
3264
+ const JsonNode = ({ name, value, depth, forceExpand }) => {
3265
+ const isContainer = value !== null && typeof value === "object";
3266
+ const [open, setOpen] = useState(forceExpand ?? depth < AUTO_EXPAND_DEPTH);
3267
+ const label = name !== void 0 ? /* @__PURE__ */ jsxs("span", { className: "text-ui-fg-subtle", children: [
3268
+ name,
3269
+ /* @__PURE__ */ jsx("span", { className: "text-ui-fg-muted", children: ": " })
3270
+ ] }) : null;
3271
+ if (!isContainer) {
3272
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start", style: { paddingLeft: depth ? 16 : 0 }, children: [
3273
+ /* @__PURE__ */ jsx("span", { className: "w-4 shrink-0" }),
3274
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 break-words [overflow-wrap:anywhere]", children: [
3275
+ label,
3276
+ /* @__PURE__ */ jsx(Primitive, { value })
3277
+ ] })
3278
+ ] });
3279
+ }
3280
+ const entries = Array.isArray(value) ? value.map((v, i) => [String(i), v]) : Object.entries(value);
3281
+ const [openBracket, closeBracket] = Array.isArray(value) ? ["[", "]"] : ["{", "}"];
3282
+ const summary = Array.isArray(value) ? `${entries.length} items` : `${entries.length} keys`;
3283
+ return /* @__PURE__ */ jsxs("div", { style: { paddingLeft: depth ? 16 : 0 }, children: [
3284
+ /* @__PURE__ */ jsxs(
3285
+ "button",
3286
+ {
3287
+ type: "button",
3288
+ onClick: () => setOpen((o) => !o),
3289
+ className: "flex items-start text-left hover:bg-ui-bg-base-hover",
3290
+ "aria-expanded": open,
3291
+ children: [
3292
+ /* @__PURE__ */ jsx("span", { className: "flex w-4 shrink-0 items-center pt-0.5 text-ui-fg-muted", children: entries.length ? open ? /* @__PURE__ */ jsx(ChevronDownMini, {}) : /* @__PURE__ */ jsx(ChevronRightMini, {}) : null }),
3293
+ /* @__PURE__ */ jsxs("span", { children: [
3294
+ label,
3295
+ /* @__PURE__ */ jsx("span", { className: "text-ui-fg-muted", children: openBracket }),
3296
+ !open || entries.length === 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
3297
+ entries.length ? /* @__PURE__ */ jsx("span", { className: "px-1 italic text-ui-fg-muted", children: summary }) : null,
3298
+ /* @__PURE__ */ jsx("span", { className: "text-ui-fg-muted", children: closeBracket })
3299
+ ] }) : null
3300
+ ] })
3301
+ ]
3302
+ }
3303
+ ),
3304
+ open && entries.length ? /* @__PURE__ */ jsxs(Fragment, { children: [
3305
+ entries.map(([key, child]) => /* @__PURE__ */ jsx(JsonNode, { name: key, value: child, depth: depth + 1, forceExpand }, key)),
3306
+ /* @__PURE__ */ jsx("div", { style: { paddingLeft: 16 }, className: "text-ui-fg-muted", children: closeBracket })
3307
+ ] }) : null
3308
+ ] });
3309
+ };
3310
+ const Primitive = ({ value }) => {
3311
+ const [showFull, setShowFull] = useState(false);
3312
+ if (value === MASKED_VALUE) {
3313
+ return /* @__PURE__ */ jsx("span", { className: "rounded bg-ui-tag-orange-bg px-1 text-ui-tag-orange-text", title: "Hidden for security", children: MASKED_VALUE });
3314
+ }
3315
+ if (value === null || value === void 0) return /* @__PURE__ */ jsx("span", { className: "text-ui-fg-muted", children: "null" });
3316
+ if (typeof value === "boolean") return /* @__PURE__ */ jsx("span", { className: "text-ui-tag-purple-text", children: String(value) });
3317
+ if (typeof value === "number") return /* @__PURE__ */ jsx("span", { className: "text-ui-tag-blue-text", children: value });
3318
+ const text = String(value);
3319
+ const truncated = !showFull && text.length > LONG_STRING;
3320
+ return /* @__PURE__ */ jsxs("span", { className: clx("text-ui-tag-green-text"), children: [
3321
+ '"',
3322
+ truncated ? `${text.slice(0, LONG_STRING)}…` : text,
3323
+ '"',
3324
+ text.length > LONG_STRING ? /* @__PURE__ */ jsx(
3325
+ "button",
3326
+ {
3327
+ type: "button",
3328
+ onClick: () => setShowFull((s2) => !s2),
3329
+ className: "ml-1 font-sans text-ui-fg-interactive hover:underline",
3330
+ children: showFull ? "show less" : `show all (${text.length} chars)`
3331
+ }
3332
+ ) : null
3333
+ ] });
3334
+ };
3335
+ const GUIDANCE = {
3336
+ orders: {
3337
+ headline: "Order data affects customers, payments and reports.",
3338
+ recreate: "Orders can’t be recreated identically from the admin. If this deletion was not intended, ask a developer to restore it from a database backup using the snapshot on this page.",
3339
+ adminSection: { label: "Go to Orders", path: "/orders" }
3340
+ },
3341
+ carts: {
3342
+ headline: "Carts are usually cleaned up automatically.",
3343
+ recreate: "No action is normally needed. Customers can simply add items to a new cart."
3344
+ },
3345
+ customers: {
3346
+ headline: "Customer data may be needed for orders, support and compliance.",
3347
+ recreate: "If it was deleted by mistake, recreate it from Customers using the values shown above. A recreated customer gets a new ID and must reset their password.",
3348
+ adminSection: { label: "Go to Customers", path: "/customers" }
3349
+ },
3350
+ catalog: {
3351
+ headline: "Catalog changes affect what customers can see and buy.",
3352
+ recreate: "If it was deleted by mistake, recreate it from Products using the values shown above. It will get a new ID.",
3353
+ adminSection: { label: "Go to Products", path: "/products" }
3354
+ },
3355
+ pricing: {
3356
+ headline: "Pricing and promotions affect what customers pay.",
3357
+ recreate: "If it was deleted by mistake, recreate it from Promotions or Price Lists using the values shown above.",
3358
+ adminSection: { label: "Go to Promotions", path: "/promotions" }
3359
+ },
3360
+ payments: {
3361
+ headline: "Payment records are financial data.",
3362
+ recreate: "Do not recreate payment records by hand—this can make balances inconsistent. If this deletion was not expected, escalate to a developer or your finance team."
3363
+ },
3364
+ fulfillment: {
3365
+ headline: "Shipping and fulfillment settings affect checkout and delivery.",
3366
+ recreate: "If it was deleted by mistake, recreate it from Settings → Locations & Shipping using the values shown above.",
3367
+ adminSection: { label: "Go to Settings", path: "/settings" }
3368
+ },
3369
+ inventory: {
3370
+ headline: "Inventory changes can affect stock levels and availability.",
3371
+ recreate: "If it was deleted by mistake, recreate it from Inventory using the values shown above and re-check stock quantities.",
3372
+ adminSection: { label: "Go to Inventory", path: "/inventory" }
3373
+ },
3374
+ wallet: {
3375
+ headline: "Wallet and credit records are financial data.",
3376
+ recreate: "Do not recreate balances by hand. If this deletion was not expected, escalate to a developer or your finance team so balances can be verified."
3377
+ },
3378
+ access: {
3379
+ headline: "Access records control who can sign in.",
3380
+ recreate: "Secrets such as passwords and keys can’t be restored. Re-invite the user or create a new API key if access is still needed.",
3381
+ adminSection: { label: "Go to Settings", path: "/settings" }
3382
+ },
3383
+ store: {
3384
+ headline: "Store settings affect regions, currencies and taxes.",
3385
+ recreate: "If it was deleted by mistake, recreate it from Settings using the values shown above.",
3386
+ adminSection: { label: "Go to Settings", path: "/settings" }
3387
+ },
3388
+ engagement: {
3389
+ headline: "Customer-generated content.",
3390
+ recreate: "These usually can’t be recreated on the customer’s behalf. Keep the snapshot on this page if you need a record of it."
3391
+ },
3392
+ notifications: {
3393
+ headline: "Notification records are usually cleaned up automatically.",
3394
+ recreate: "No action is normally needed."
3395
+ },
3396
+ system: {
3397
+ headline: "Internal system data, usually removed automatically by the platform.",
3398
+ recreate: "No action is normally needed. If you suspect a problem, share this page with a developer."
3399
+ }
3400
+ };
3401
+ function getNextSteps(category) {
3402
+ return GUIDANCE[category];
3403
+ }
3404
+ const RELATED_LINKS = [
3405
+ { key: "order_id", label: "Order", path: (id) => `/orders/${id}`, selfTables: ["order"] },
3406
+ { key: "customer_id", label: "Customer", path: (id) => `/customers/${id}`, selfTables: ["customer"] },
3407
+ { key: "product_id", label: "Product", path: (id) => `/products/${id}`, selfTables: ["product"] }
3408
+ ];
3409
+ function getRelatedLinks(tableName, oldData) {
3410
+ if (!oldData) return [];
3411
+ return RELATED_LINKS.flatMap((link) => {
3412
+ const id = oldData[link.key];
3413
+ if (link.selfTables.includes(tableName) || typeof id !== "string" || !id.trim()) return [];
3414
+ return [{ label: link.label, id, path: link.path(id) }];
3415
+ });
3416
+ }
3417
+ const fullName = (row) => {
3418
+ const parts = [row.first_name, row.last_name].filter(
3419
+ (part) => typeof part === "string" && part.trim() !== ""
3420
+ );
3421
+ return parts.length ? parts.join(" ") : void 0;
3422
+ };
3423
+ const addressLine = (row) => {
3424
+ const parts = [row.address_1, row.address_2, row.city, row.province, row.postal_code, row.country_code].filter((part) => typeof part === "string" && part.trim() !== "").map((part) => part.trim());
3425
+ return parts.length ? parts.join(", ") : void 0;
3426
+ };
3427
+ const NAME = { key: "name", label: "Name", derive: fullName };
3428
+ const ADDRESS = { key: "address", label: "Address", derive: addressLine };
3429
+ const CREATED = { key: "created_at", label: "Created", kind: "date" };
3430
+ const ENTITY_FIELDS = {
3431
+ order: [
3432
+ { key: "display_id", label: "Order number" },
3433
+ { key: "status", label: "Status", kind: "status" },
3434
+ { key: "email", label: "Customer email" },
3435
+ { key: "customer_id", label: "Customer ID", kind: "id" },
3436
+ { key: "currency_code", label: "Currency" },
3437
+ { key: "region_id", label: "Region ID", kind: "id" },
3438
+ { key: "sales_channel_id", label: "Sales channel ID", kind: "id" },
3439
+ { key: "canceled_at", label: "Canceled", kind: "date" },
3440
+ CREATED
3441
+ ],
3442
+ order_line_item: [
3443
+ { key: "title", label: "Item" },
3444
+ { key: "variant_title", label: "Variant" },
3445
+ { key: "variant_sku", label: "SKU" },
3446
+ { key: "unit_price", label: "Unit price", kind: "money" },
3447
+ { key: "product_id", label: "Product ID", kind: "id" },
3448
+ { key: "variant_id", label: "Variant ID", kind: "id" },
3449
+ CREATED
3450
+ ],
3451
+ cart_line_item: [
3452
+ { key: "title", label: "Item" },
3453
+ { key: "variant_sku", label: "SKU" },
3454
+ { key: "quantity", label: "Quantity", kind: "number" },
3455
+ { key: "unit_price", label: "Unit price", kind: "money" },
3456
+ { key: "cart_id", label: "Cart ID", kind: "id" },
3457
+ { key: "variant_id", label: "Variant ID", kind: "id" },
3458
+ CREATED
3459
+ ],
3460
+ cart: [
3461
+ { key: "email", label: "Email" },
3462
+ { key: "customer_id", label: "Customer ID", kind: "id" },
3463
+ { key: "currency_code", label: "Currency" },
3464
+ { key: "region_id", label: "Region ID", kind: "id" },
3465
+ { key: "completed_at", label: "Completed", kind: "date" },
3466
+ CREATED
3467
+ ],
3468
+ customer: [
3469
+ NAME,
3470
+ { key: "email", label: "Email" },
3471
+ { key: "phone", label: "Phone" },
3472
+ { key: "company_name", label: "Company" },
3473
+ { key: "has_account", label: "Registered account", kind: "boolean" },
3474
+ CREATED
3475
+ ],
3476
+ customer_address: [
3477
+ { key: "address_name", label: "Label" },
3478
+ NAME,
3479
+ ADDRESS,
3480
+ { key: "phone", label: "Phone" },
3481
+ { key: "customer_id", label: "Customer ID", kind: "id" },
3482
+ { key: "is_default_shipping", label: "Default shipping", kind: "boolean" },
3483
+ CREATED
3484
+ ],
3485
+ order_address: [NAME, ADDRESS, { key: "phone", label: "Phone" }, CREATED],
3486
+ cart_address: [NAME, ADDRESS, { key: "phone", label: "Phone" }, CREATED],
3487
+ product: [
3488
+ { key: "title", label: "Title" },
3489
+ { key: "status", label: "Status", kind: "status" },
3490
+ { key: "handle", label: "Handle" },
3491
+ { key: "subtitle", label: "Subtitle" },
3492
+ { key: "collection_id", label: "Collection ID", kind: "id" },
3493
+ { key: "is_giftcard", label: "Gift card", kind: "boolean" },
3494
+ CREATED
3495
+ ],
3496
+ product_variant: [
3497
+ { key: "title", label: "Title" },
3498
+ { key: "sku", label: "SKU" },
3499
+ { key: "barcode", label: "Barcode" },
3500
+ { key: "product_id", label: "Product ID", kind: "id" },
3501
+ { key: "manage_inventory", label: "Manages inventory", kind: "boolean" },
3502
+ CREATED
3503
+ ],
3504
+ product_category: [
3505
+ { key: "name", label: "Name" },
3506
+ { key: "handle", label: "Handle" },
3507
+ { key: "is_active", label: "Active", kind: "boolean" },
3508
+ { key: "parent_category_id", label: "Parent category ID", kind: "id" },
3509
+ CREATED
3510
+ ],
3511
+ product_collection: [{ key: "title", label: "Title" }, { key: "handle", label: "Handle" }, CREATED],
3512
+ payment: [
3513
+ { key: "amount", label: "Amount", kind: "money" },
3514
+ { key: "currency_code", label: "Currency" },
3515
+ { key: "provider_id", label: "Provider" },
3516
+ { key: "captured_at", label: "Captured", kind: "date" },
3517
+ { key: "canceled_at", label: "Canceled", kind: "date" },
3518
+ { key: "payment_collection_id", label: "Payment collection ID", kind: "id" },
3519
+ CREATED
3520
+ ],
3521
+ payment_session: [
3522
+ { key: "amount", label: "Amount", kind: "money" },
3523
+ { key: "currency_code", label: "Currency" },
3524
+ { key: "provider_id", label: "Provider" },
3525
+ { key: "status", label: "Status", kind: "status" },
3526
+ { key: "payment_collection_id", label: "Payment collection ID", kind: "id" },
3527
+ CREATED
3528
+ ],
3529
+ payment_collection: [
3530
+ { key: "amount", label: "Amount", kind: "money" },
3531
+ { key: "currency_code", label: "Currency" },
3532
+ { key: "status", label: "Status", kind: "status" },
3533
+ CREATED
3534
+ ],
3535
+ refund: [
3536
+ { key: "amount", label: "Amount", kind: "money" },
3537
+ { key: "payment_id", label: "Payment ID", kind: "id" },
3538
+ { key: "refund_reason_id", label: "Refund reason ID", kind: "id" },
3539
+ { key: "note", label: "Note" },
3540
+ { key: "created_by", label: "Created by" },
3541
+ CREATED
3542
+ ],
3543
+ return: [
3544
+ { key: "display_id", label: "Return number" },
3545
+ { key: "status", label: "Status", kind: "status" },
3546
+ { key: "order_id", label: "Order ID", kind: "id" },
3547
+ { key: "refund_amount", label: "Refund amount", kind: "money" },
3548
+ { key: "received_at", label: "Received", kind: "date" },
3549
+ CREATED
3550
+ ],
3551
+ payment_detail: [
3552
+ { key: "type", label: "Method type" },
3553
+ { key: "customer_id", label: "Customer ID", kind: "id" },
3554
+ { key: "is_default", label: "Default method", kind: "boolean" },
3555
+ CREATED
3556
+ ],
3557
+ user: [NAME, { key: "email", label: "Email" }, CREATED],
3558
+ invite: [
3559
+ { key: "email", label: "Email" },
3560
+ { key: "accepted", label: "Accepted", kind: "boolean" },
3561
+ { key: "expires_at", label: "Expires", kind: "date" },
3562
+ CREATED
3563
+ ],
3564
+ api_key: [
3565
+ { key: "title", label: "Title" },
3566
+ { key: "type", label: "Type" },
3567
+ { key: "redacted", label: "Key (redacted)" },
3568
+ { key: "revoked_at", label: "Revoked", kind: "date" },
3569
+ CREATED
3570
+ ],
3571
+ promotion: [
3572
+ { key: "code", label: "Promotion code" },
3573
+ { key: "type", label: "Type" },
3574
+ { key: "status", label: "Status", kind: "status" },
3575
+ { key: "is_automatic", label: "Automatic", kind: "boolean" },
3576
+ CREATED
3577
+ ],
3578
+ fulfillment: [
3579
+ { key: "provider_id", label: "Provider" },
3580
+ { key: "location_id", label: "Location ID", kind: "id" },
3581
+ { key: "shipped_at", label: "Shipped", kind: "date" },
3582
+ { key: "delivered_at", label: "Delivered", kind: "date" },
3583
+ { key: "canceled_at", label: "Canceled", kind: "date" },
3584
+ CREATED
3585
+ ],
3586
+ inventory_item: [
3587
+ { key: "sku", label: "SKU" },
3588
+ { key: "title", label: "Title" },
3589
+ { key: "requires_shipping", label: "Requires shipping", kind: "boolean" },
3590
+ CREATED
3591
+ ],
3592
+ inventory_level: [
3593
+ { key: "inventory_item_id", label: "Inventory item ID", kind: "id" },
3594
+ { key: "location_id", label: "Location ID", kind: "id" },
3595
+ { key: "stocked_quantity", label: "Stocked", kind: "number" },
3596
+ { key: "reserved_quantity", label: "Reserved", kind: "number" },
3597
+ CREATED
3598
+ ],
3599
+ review: [
3600
+ { key: "title", label: "Title" },
3601
+ { key: "rating", label: "Rating", kind: "number" },
3602
+ { key: "status", label: "Status", kind: "status" },
3603
+ { key: "product_id", label: "Product ID", kind: "id" },
3604
+ { key: "customer_id", label: "Customer ID", kind: "id" },
3605
+ CREATED
3606
+ ]
3607
+ };
3608
+ const GENERIC_FIELDS = [
3609
+ { key: "display_id", label: "Number" },
3610
+ { key: "title", label: "Title" },
3611
+ { key: "name", label: "Name" },
3612
+ NAME,
3613
+ { key: "email", label: "Email" },
3614
+ { key: "phone", label: "Phone" },
3615
+ { key: "handle", label: "Handle" },
3616
+ { key: "sku", label: "SKU" },
3617
+ { key: "code", label: "Code" },
3618
+ { key: "status", label: "Status", kind: "status" },
3619
+ { key: "type", label: "Type" },
3620
+ { key: "amount", label: "Amount", kind: "money" },
3621
+ { key: "quantity", label: "Quantity", kind: "number" },
3622
+ { key: "currency_code", label: "Currency" },
3623
+ { key: "order_id", label: "Order ID", kind: "id" },
3624
+ { key: "customer_id", label: "Customer ID", kind: "id" },
3625
+ { key: "product_id", label: "Product ID", kind: "id" },
3626
+ { key: "payment_id", label: "Payment ID", kind: "id" },
3627
+ { key: "provider_id", label: "Provider" },
3628
+ CREATED,
3629
+ { key: "updated_at", label: "Last updated", kind: "date" }
3630
+ ];
3631
+ const MAX_GENERIC_FIELDS = 8;
3632
+ function amountOf(value) {
3633
+ if (typeof value === "number" && Number.isFinite(value)) return value;
3634
+ if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) return Number(value);
3635
+ if (value !== null && typeof value === "object" && "value" in value) {
3636
+ return amountOf(value.value);
3637
+ }
3638
+ return null;
3639
+ }
3640
+ function formatValue(raw, kind, row) {
3641
+ if (raw === null || raw === void 0 || raw === "") return null;
3642
+ if (raw === MASKED_VALUE) return { value: MASKED_VALUE, kind: "masked" };
3643
+ switch (kind) {
3644
+ case "money": {
3645
+ const amount = amountOf(raw);
3646
+ if (amount === null) return null;
3647
+ const currency = typeof row.currency_code === "string" ? row.currency_code : null;
3648
+ return { value: formatMoney(amount, currency), kind };
3649
+ }
3650
+ case "date":
3651
+ return typeof raw === "string" || raw instanceof Date ? { value: formatDate(raw), kind } : null;
3652
+ case "boolean":
3653
+ return { value: raw === true || raw === "true" ? "Yes" : "No", kind };
3654
+ case "number": {
3655
+ const n = amountOf(raw);
3656
+ return n === null ? null : { value: formatNumber(n), kind };
3657
+ }
3658
+ case "status":
3659
+ return { value: String(raw).replace(/_/g, " "), kind };
3660
+ default:
3661
+ if (typeof raw === "object") return null;
3662
+ if (typeof raw === "string" && raw.length > 160) return { value: `${raw.slice(0, 157)}…`, kind };
3663
+ return { value: String(raw), kind };
3664
+ }
3665
+ }
3666
+ function summarizeOldData(tableName, oldData) {
3667
+ if (!oldData) return [];
3668
+ const { value: row } = maskSensitiveData(oldData, tableName);
3669
+ const specific = ENTITY_FIELDS[tableName];
3670
+ const specs = specific ?? GENERIC_FIELDS;
3671
+ const fields = [];
3672
+ const seenLabels = /* @__PURE__ */ new Set();
3673
+ for (const spec of specs) {
3674
+ const raw = spec.derive ? spec.derive(row) : row[spec.key];
3675
+ const formatted = formatValue(raw, spec.kind ?? "text", row);
3676
+ if (!formatted || seenLabels.has(spec.label)) continue;
3677
+ seenLabels.add(spec.label);
3678
+ const value = spec.key === "currency_code" ? formatted.value.toUpperCase() : formatted.value;
3679
+ fields.push({ key: spec.key, label: spec.label, value, kind: formatted.kind });
3680
+ if (!specific && fields.length >= MAX_GENERIC_FIELDS) break;
3681
+ }
3682
+ return fields;
3683
+ }
3684
+ function readLastListSearch() {
3685
+ try {
3686
+ return window.sessionStorage.getItem(LAST_LIST_URL_KEY) ?? "";
3687
+ } catch {
3688
+ return "";
3689
+ }
3690
+ }
3691
+ const DeletedRecordDetailPage = () => {
3692
+ const navigate = useNavigate();
3693
+ const location = useLocation();
3694
+ const { id = "" } = useParams();
3695
+ const [state, setState] = useState({ status: "loading" });
3696
+ const [reloadToken, setReloadToken] = useState(0);
3697
+ const cameFromList = location.state?.fromList === true;
3698
+ const goBack = () => {
3699
+ if (cameFromList) navigate(-1);
3700
+ else navigate(`/deleted-records-audit${readLastListSearch()}`);
3701
+ };
3702
+ useEffect(() => {
3703
+ const controller = new AbortController();
3704
+ setState({ status: "loading" });
3705
+ fetchAuditDetail(id, controller.signal).then((audit) => setState({ status: "ready", audit })).catch((e) => {
3706
+ if (controller.signal.aborted) return;
3707
+ setState({
3708
+ status: "error",
3709
+ message: e instanceof Error ? e.message : "Unknown error",
3710
+ notFound: e instanceof AuditApiError && e.status === 404
3711
+ });
1923
3712
  });
1924
- return () => {
1925
- cancelled = true;
1926
- };
1927
- }, [id]);
1928
- const displayStatus = detail ? (detail.payment_id != null && detail.payment_status != null && detail.payment_status !== "" ? detail.payment_status : detail.session_status ?? "") || "—" : "";
1929
- const sessionStatusRaw = detail?.session_status ?? "—";
1930
- const paymentStatusRaw = detail?.payment_id != null ? detail?.payment_status ?? "—" : "—";
1931
- const displayAmount = detail ? `${(detail.currency_code ?? "USD").toUpperCase()} ${Number(detail.amount)}` : "";
1932
- return /* @__PURE__ */ jsx("div", { className: "w-full p-6", children: /* @__PURE__ */ jsxs(Container, { className: "mx-auto flex w-full max-w-4xl flex-col gap-6 p-6", children: [
1933
- /* @__PURE__ */ jsx("header", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
1934
- /* @__PURE__ */ jsx(Button, { variant: "transparent", size: "small", onClick: () => navigate("/payments"), children: "← Payments" }),
1935
- /* @__PURE__ */ jsx(Heading, { level: "h1", children: "Payment session" }),
1936
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: id ?? "—" })
3713
+ return () => controller.abort();
3714
+ }, [id, reloadToken]);
3715
+ return /* @__PURE__ */ jsx("div", { className: "w-full p-3 md:p-6", children: /* @__PURE__ */ jsxs(Container, { className: "mx-auto flex w-full max-w-5xl flex-col gap-6 p-4 md:p-6", children: [
3716
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(Button, { variant: "transparent", size: "small", onClick: goBack, className: "-ml-2", children: [
3717
+ /* @__PURE__ */ jsx(ArrowLeft, {}),
3718
+ "Back to Deleted Records"
1937
3719
  ] }) }),
1938
- error ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-ui-border-strong p-6 text-center", children: [
1939
- /* @__PURE__ */ jsx(Text, { weight: "plus", className: "text-ui-fg-error", children: error }),
1940
- /* @__PURE__ */ jsx(Button, { variant: "secondary", className: "mt-4", onClick: () => navigate("/payments"), children: "Back to list" })
1941
- ] }) : loading ? /* @__PURE__ */ jsx("div", { className: "flex justify-center py-16", children: /* @__PURE__ */ jsx(Text, { children: "Loading…" }) }) : detail ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
1942
- /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-ui-border-base p-6 flex flex-col gap-4", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [
1943
- /* @__PURE__ */ jsxs("div", { children: [
1944
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Order ID" }),
1945
- /* @__PURE__ */ jsx("div", { className: "mt-1", children: detail.order_id ? /* @__PURE__ */ jsx(
1946
- Button,
1947
- {
1948
- variant: "transparent",
1949
- size: "small",
1950
- onClick: () => navigate(`/orders/${detail.order_id}`),
1951
- children: detail.order_id
1952
- }
1953
- ) : /* @__PURE__ */ jsx(Text, { children: "—" }) })
1954
- ] }),
1955
- /* @__PURE__ */ jsxs("div", { children: [
1956
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Amount" }),
1957
- /* @__PURE__ */ jsx(Text, { className: "mt-1 block", children: displayAmount })
1958
- ] }),
1959
- /* @__PURE__ */ jsxs("div", { children: [
1960
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Provider" }),
1961
- /* @__PURE__ */ jsx(Text, { className: "mt-1 block", children: detail.provider_id })
1962
- ] }),
1963
- /* @__PURE__ */ jsxs("div", { children: [
1964
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Status" }),
1965
- /* @__PURE__ */ jsx("div", { className: "mt-1", children: /* @__PURE__ */ jsx(
1966
- Badge,
1967
- {
1968
- size: "2xsmall",
1969
- className: `uppercase ${getStatusBadgeClass$3(displayStatus)}`,
1970
- children: displayStatus !== "—" ? displayStatus.replace(/_/g, " ") : "—"
1971
- }
1972
- ) })
1973
- ] }),
1974
- /* @__PURE__ */ jsxs("div", { children: [
1975
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Session status (DB)" }),
1976
- /* @__PURE__ */ jsx(Text, { className: "mt-1 block", children: sessionStatusRaw })
3720
+ state.status === "loading" ? /* @__PURE__ */ jsx(DetailSkeleton, {}) : state.status === "error" ? /* @__PURE__ */ jsx(
3721
+ DetailError,
3722
+ {
3723
+ notFound: state.notFound,
3724
+ message: state.message,
3725
+ onRetry: () => setReloadToken((t) => t + 1),
3726
+ onBack: goBack
3727
+ }
3728
+ ) : /* @__PURE__ */ jsx(DetailContent, { audit: state.audit })
3729
+ ] }) });
3730
+ };
3731
+ const DetailContent = ({ audit }) => {
3732
+ const navigate = useNavigate();
3733
+ const entity = getEntityInfo(audit.table_name);
3734
+ const source = describeSource(audit.source);
3735
+ const summary = useMemo(() => summarizeOldData(audit.table_name, audit.old_data), [audit]);
3736
+ const masked = useMemo(() => maskSensitiveData(audit.old_data, audit.table_name), [audit]);
3737
+ const guidance = getNextSteps(entity.category.id);
3738
+ const related = getRelatedLinks(audit.table_name, audit.old_data);
3739
+ const [showRaw, setShowRaw] = useState(false);
3740
+ const copyJson = async () => {
3741
+ try {
3742
+ await navigator.clipboard.writeText(JSON.stringify(masked.value, null, 2));
3743
+ toast.success("Record data copied", {
3744
+ description: masked.maskedCount ? "Sensitive values were left hidden." : void 0
3745
+ });
3746
+ } catch {
3747
+ toast.error("Couldn’t copy to clipboard");
3748
+ }
3749
+ };
3750
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3751
+ /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-3", children: [
3752
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
3753
+ /* @__PURE__ */ jsxs(Badge, { size: "small", color: "red", children: [
3754
+ /* @__PURE__ */ jsx(Trash, {}),
3755
+ "Deleted"
1977
3756
  ] }),
1978
- /* @__PURE__ */ jsxs("div", { children: [
1979
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Payment status (DB)" }),
1980
- /* @__PURE__ */ jsx(Text, { className: "mt-1 block", children: paymentStatusRaw })
3757
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: entity.category.label })
3758
+ ] }),
3759
+ /* @__PURE__ */ jsxs(Heading, { level: "h1", children: [
3760
+ "Deleted ",
3761
+ entity.label
3762
+ ] }),
3763
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
3764
+ /* @__PURE__ */ jsx(CopyableId, { value: audit.record_id, emptyLabel: "This record type has no ID column", className: "text-sm" }),
3765
+ /* @__PURE__ */ jsxs(Text, { size: "xsmall", className: "text-ui-fg-muted", children: [
3766
+ "Database table: ",
3767
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: audit.table_name })
3768
+ ] })
3769
+ ] })
3770
+ ] }),
3771
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-ui-tag-orange-border bg-ui-tag-orange-bg px-4 py-3", children: [
3772
+ /* @__PURE__ */ jsx(InformationCircle, { className: "mt-0.5 shrink-0 text-ui-tag-orange-icon" }),
3773
+ /* @__PURE__ */ jsxs(Text, { size: "small", className: "text-ui-tag-orange-text", children: [
3774
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "This is a historical snapshot" }),
3775
+ " captured immediately before deletion. The record no longer exists in the database, so changes elsewhere in the admin are not reflected here."
3776
+ ] })
3777
+ ] }),
3778
+ /* @__PURE__ */ jsx(Section, { title: "Deletion information", children: /* @__PURE__ */ jsxs("dl", { className: "grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2", children: [
3779
+ /* @__PURE__ */ jsx(Field, { label: "Deleted", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
3780
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-ui-fg-base", children: formatExactDateTime(audit.occurred_at) }),
3781
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-xs text-ui-fg-muted", children: [
3782
+ /* @__PURE__ */ jsx(ClockSolid, { className: "size-3" }),
3783
+ formatRelativeTime(audit.occurred_at)
3784
+ ] })
3785
+ ] }) }),
3786
+ /* @__PURE__ */ jsx(Field, { label: "Deleted by", children: audit.deleted_by ? /* @__PURE__ */ jsx("span", { className: "break-all text-sm text-ui-fg-base", children: audit.deleted_by }) : /* @__PURE__ */ jsx("span", { className: "text-sm text-ui-fg-muted", children: "Not recorded" }) }),
3787
+ /* @__PURE__ */ jsx(Field, { label: "Source", children: source.kind === "unknown" ? /* @__PURE__ */ jsx("span", { className: "text-sm text-ui-fg-muted", children: "Unknown" }) : /* @__PURE__ */ jsx(Badge, { size: "small", color: sourceBadgeColor(source.kind), children: source.label }) }),
3788
+ /* @__PURE__ */ jsx(Field, { label: "Action", children: /* @__PURE__ */ jsx("span", { className: "text-sm text-ui-fg-base", children: "Permanently deleted" }) })
3789
+ ] }) }),
3790
+ /* @__PURE__ */ jsx(
3791
+ Section,
3792
+ {
3793
+ title: "Record summary",
3794
+ description: `Key details of this ${entity.label.toLowerCase()} at the moment it was deleted.`,
3795
+ children: summary.length ? /* @__PURE__ */ jsx("dl", { className: "grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2", children: summary.map((field) => /* @__PURE__ */ jsx(Field, { label: field.label, children: field.kind === "id" ? /* @__PURE__ */ jsx(CopyableId, { value: field.value }) : field.kind === "status" ? /* @__PURE__ */ jsx(Badge, { size: "2xsmall", color: "grey", className: "capitalize", children: field.value }) : field.kind === "masked" ? /* @__PURE__ */ jsx("span", { className: "text-sm text-ui-fg-muted", children: "Hidden for security" }) : /* @__PURE__ */ jsx("span", { className: "break-words text-sm text-ui-fg-base", children: field.value }) }, field.label)) }) : /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "No key details could be extracted for this record type. See the original record data below." })
3796
+ }
3797
+ ),
3798
+ /* @__PURE__ */ jsx(
3799
+ Section,
3800
+ {
3801
+ title: "Original record data",
3802
+ description: "The complete data captured before deletion.",
3803
+ actions: showRaw && audit.old_data ? /* @__PURE__ */ jsxs(Button, { variant: "secondary", size: "small", onClick: copyJson, children: [
3804
+ /* @__PURE__ */ jsx(SquareTwoStack, {}),
3805
+ "Copy JSON"
3806
+ ] }) : null,
3807
+ children: audit.old_data ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
3808
+ masked.maskedCount > 0 ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3809
+ /* @__PURE__ */ jsx(ShieldCheck, { className: "shrink-0 text-ui-fg-muted" }),
3810
+ /* @__PURE__ */ jsxs(Text, { size: "small", className: "text-ui-fg-subtle", children: [
3811
+ masked.maskedCount,
3812
+ " sensitive ",
3813
+ masked.maskedCount === 1 ? "value is" : "values are",
3814
+ " hidden for security (passwords, tokens, card or account numbers)."
3815
+ ] })
3816
+ ] }) : null,
3817
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(Button, { variant: "secondary", size: "small", onClick: () => setShowRaw((s2) => !s2), "aria-expanded": showRaw, children: [
3818
+ showRaw ? /* @__PURE__ */ jsx(ChevronDownMini, {}) : /* @__PURE__ */ jsx(ChevronRightMini, {}),
3819
+ showRaw ? "Hide full record" : "View full record"
3820
+ ] }) }),
3821
+ showRaw ? /* @__PURE__ */ jsx(JsonViewer, { data: masked.value }) : null
3822
+ ] }) : /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "No data was captured for this deletion." })
3823
+ }
3824
+ ),
3825
+ /* @__PURE__ */ jsx(Section, { title: "What you can do now", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
3826
+ /* @__PURE__ */ jsxs(Text, { size: "small", className: "text-ui-fg-base", children: [
3827
+ "This record was permanently deleted. ",
3828
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Automatic restoration is not available" }),
3829
+ " ",
3830
+ "from this screen."
3831
+ ] }),
3832
+ /* @__PURE__ */ jsxs("ol", { className: "flex list-decimal flex-col gap-3 pl-5 text-sm text-ui-fg-subtle marker:text-ui-fg-muted", children: [
3833
+ /* @__PURE__ */ jsxs("li", { children: [
3834
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-ui-fg-base", children: "Check whether this was expected." }),
3835
+ " ",
3836
+ audit.deleted_by || source.kind !== "unknown" ? "Confirm with the person or system shown above." : `Who deleted it wasn’t recorded—check with your team or integrations active around ${formatExactDateTime(audit.occurred_at)}.`,
3837
+ " ",
3838
+ guidance.headline
1981
3839
  ] }),
1982
- /* @__PURE__ */ jsxs("div", { children: [
1983
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Finalized" }),
1984
- /* @__PURE__ */ jsx(Text, { className: "mt-1 block", children: detail.payment_id != null ? "Yes" : "No" })
3840
+ /* @__PURE__ */ jsxs("li", { children: [
3841
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-ui-fg-base", children: "If it needs to come back:" }),
3842
+ " ",
3843
+ guidance.recreate
1985
3844
  ] }),
1986
- /* @__PURE__ */ jsxs("div", { children: [
1987
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Created" }),
1988
- /* @__PURE__ */ jsx(Text, { className: "mt-1 block", children: new Date(detail.created_at).toLocaleDateString("en-US", {
1989
- year: "numeric",
1990
- month: "short",
1991
- day: "numeric",
1992
- hour: "numeric",
1993
- minute: "2-digit",
1994
- hour12: true
1995
- }) })
3845
+ /* @__PURE__ */ jsxs("li", { children: [
3846
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-ui-fg-base", children: "Need the details?" }),
3847
+ " Use “View full record” above to see or copy everything that was stored. Sensitive values stay hidden."
1996
3848
  ] })
1997
- ] }) }),
1998
- Object.keys(detail.data ?? {}).length > 0 ? /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-ui-border-base p-6", children: [
1999
- /* @__PURE__ */ jsx(Heading, { level: "h2", className: "text-lg mb-4", children: "Provider data" }),
2000
- /* @__PURE__ */ jsx("pre", { className: "text-xs bg-ui-bg-subtle p-4 rounded-lg overflow-auto max-h-96", children: JSON.stringify(detail.data, null, 2) })
2001
- ] }) : /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-ui-border-base p-6", children: /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "No provider data for this session." }) })
2002
- ] }) : null
2003
- ] }) });
3849
+ ] }),
3850
+ guidance.adminSection || related.length ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
3851
+ guidance.adminSection ? /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: () => navigate(guidance.adminSection.path), children: guidance.adminSection.label }) : null,
3852
+ related.map((link) => /* @__PURE__ */ jsxs(
3853
+ Button,
3854
+ {
3855
+ variant: "transparent",
3856
+ size: "small",
3857
+ onClick: () => navigate(link.path),
3858
+ title: `Opens ${link.label.toLowerCase()} ${link.id} if it still exists`,
3859
+ children: [
3860
+ "Open related ",
3861
+ link.label.toLowerCase(),
3862
+ /* @__PURE__ */ jsx(ChevronRightMini, {})
3863
+ ]
3864
+ },
3865
+ link.path
3866
+ ))
3867
+ ] }) : null,
3868
+ related.length ? /* @__PURE__ */ jsx(Text, { size: "xsmall", className: "text-ui-fg-muted", children: "Related records open only if they haven’t been deleted too." }) : null
3869
+ ] }) })
3870
+ ] });
2004
3871
  };
2005
- const config$3 = defineRouteConfig({
2006
- label: "Payment details",
2007
- icon: CreditCard
2008
- });
2009
- const getStatusBadgeClass$2 = (status) => {
3872
+ const Section = ({
3873
+ title,
3874
+ description,
3875
+ actions,
3876
+ children
3877
+ }) => /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-4 rounded-lg border border-ui-border-base p-5", children: [
3878
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-2", children: [
3879
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
3880
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: title }),
3881
+ description ? /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: description }) : null
3882
+ ] }),
3883
+ actions
3884
+ ] }),
3885
+ children
3886
+ ] });
3887
+ const Field = ({ label, children }) => /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
3888
+ /* @__PURE__ */ jsx("dt", { className: "text-xs font-medium uppercase tracking-wide text-ui-fg-muted", children: label }),
3889
+ /* @__PURE__ */ jsx("dd", { className: "min-w-0", children })
3890
+ ] });
3891
+ const DetailSkeleton = () => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", "aria-busy": "true", children: [
3892
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
3893
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-24 rounded-full" }),
3894
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-64" }),
3895
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-48" })
3896
+ ] }),
3897
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-14 w-full" }),
3898
+ Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 rounded-lg border border-ui-border-base p-5", children: [
3899
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-40" }),
3900
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: Array.from({ length: 4 }).map((__, j) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
3901
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-20" }),
3902
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-36" })
3903
+ ] }, j)) })
3904
+ ] }, i))
3905
+ ] });
3906
+ const DetailError = ({
3907
+ notFound,
3908
+ message,
3909
+ onRetry,
3910
+ onBack
3911
+ }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 rounded-lg border border-ui-border-base px-6 py-12 text-center", children: [
3912
+ /* @__PURE__ */ jsx(ExclamationCircle, { className: clx(notFound ? "text-ui-fg-muted" : "text-ui-fg-error") }),
3913
+ /* @__PURE__ */ jsx(Heading, { level: "h3", children: notFound ? "Audit entry not found" : "Unable to load this deleted record" }),
3914
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "max-w-md text-ui-fg-subtle", children: notFound ? "This audit entry doesn’t exist. The link may be incorrect." : "We couldn’t retrieve the audit entry right now. Check your connection and try again." }),
3915
+ !notFound ? /* @__PURE__ */ jsx(Text, { size: "xsmall", className: "max-w-md font-mono text-ui-fg-muted", children: message }) : null,
3916
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 flex gap-2", children: [
3917
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "small", onClick: onBack, children: "Back to Deleted Records" }),
3918
+ !notFound ? /* @__PURE__ */ jsx(Button, { variant: "primary", size: "small", onClick: onRetry, children: "Try again" }) : null
3919
+ ] })
3920
+ ] });
3921
+ const config$4 = defineRouteConfig({});
3922
+ const getStatusBadgeClass$3 = (status) => {
2010
3923
  const s2 = status.toLowerCase();
2011
3924
  if (s2 === "captured" || s2 === "completed") return "bg-ui-tag-green-bg text-ui-tag-green-text";
2012
3925
  if (s2 === "authorized") return "bg-ui-tag-blue-bg text-ui-tag-blue-text";
@@ -2116,7 +4029,7 @@ const RefundDetailPage = () => {
2116
4029
  Badge,
2117
4030
  {
2118
4031
  size: "2xsmall",
2119
- className: `uppercase ${getStatusBadgeClass$2(paymentStatus)}`,
4032
+ className: `uppercase ${getStatusBadgeClass$3(paymentStatus)}`,
2120
4033
  children: paymentStatus !== "—" ? paymentStatus.replace(/_/g, " ") : "—"
2121
4034
  }
2122
4035
  ) })
@@ -2193,11 +4106,11 @@ const RefundDetailPage = () => {
2193
4106
  ] }) : null
2194
4107
  ] }) });
2195
4108
  };
2196
- const config$2 = defineRouteConfig({
4109
+ const config$3 = defineRouteConfig({
2197
4110
  label: "Refund details",
2198
4111
  icon: Receipt
2199
4112
  });
2200
- const getStatusBadgeClass$1 = (status) => {
4113
+ const getStatusBadgeClass$2 = (status) => {
2201
4114
  const statusLower = status.toLowerCase();
2202
4115
  if (statusLower === "requested") {
2203
4116
  return "bg-ui-tag-orange-bg text-ui-tag-orange-text";
@@ -2330,7 +4243,7 @@ const ReturnDetailPage = () => {
2330
4243
  Badge,
2331
4244
  {
2332
4245
  size: "small",
2333
- className: `uppercase ${getStatusBadgeClass$1(returnOrder.status)}`,
4246
+ className: `uppercase ${getStatusBadgeClass$2(returnOrder.status)}`,
2334
4247
  children: returnOrder.status.replace(/_/g, " ")
2335
4248
  }
2336
4249
  )
@@ -2413,7 +4326,7 @@ const ReturnDetailPage = () => {
2413
4326
  Badge,
2414
4327
  {
2415
4328
  size: "2xsmall",
2416
- className: `uppercase ${getStatusBadgeClass$1(entry.status)}`,
4329
+ className: `uppercase ${getStatusBadgeClass$2(entry.status)}`,
2417
4330
  children: entry.status.replace(/_/g, " ")
2418
4331
  }
2419
4332
  ) }),
@@ -2475,11 +4388,11 @@ const ReturnDetailPage = () => {
2475
4388
  updateSuccess && /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-ui-border-success bg-ui-bg-success-subtle p-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-success", children: "Action completed successfully" }) })
2476
4389
  ] }) });
2477
4390
  };
2478
- const config$1 = defineRouteConfig({
4391
+ const config$2 = defineRouteConfig({
2479
4392
  label: "Return Order Details",
2480
4393
  icon: CheckCircle
2481
4394
  });
2482
- const getStatusBadgeClass = (status) => {
4395
+ const getStatusBadgeClass$1 = (status) => {
2483
4396
  const statusLower = status.toLowerCase();
2484
4397
  if (statusLower === "requested") {
2485
4398
  return "bg-ui-tag-orange-bg text-ui-tag-orange-text";
@@ -2616,7 +4529,7 @@ const SwapDetailPage = () => {
2616
4529
  Badge,
2617
4530
  {
2618
4531
  size: "small",
2619
- className: `uppercase ${getStatusBadgeClass(swap.status)}`,
4532
+ className: `uppercase ${getStatusBadgeClass$1(swap.status)}`,
2620
4533
  children: swap.status.replace(/_/g, " ")
2621
4534
  }
2622
4535
  )
@@ -2684,7 +4597,7 @@ const SwapDetailPage = () => {
2684
4597
  Badge,
2685
4598
  {
2686
4599
  size: "2xsmall",
2687
- className: `uppercase ${getStatusBadgeClass(entry.status)}`,
4600
+ className: `uppercase ${getStatusBadgeClass$1(entry.status)}`,
2688
4601
  children: entry.status.replace(/_/g, " ")
2689
4602
  }
2690
4603
  ) }),
@@ -2753,10 +4666,128 @@ const SwapDetailPage = () => {
2753
4666
  ] })
2754
4667
  ] }) });
2755
4668
  };
2756
- const config = defineRouteConfig({
4669
+ const config$1 = defineRouteConfig({
2757
4670
  label: "Swap Details",
2758
4671
  icon: ArrowPath
2759
4672
  });
4673
+ const getStatusBadgeClass = (status) => {
4674
+ const s2 = status.toLowerCase();
4675
+ if (s2 === "captured" || s2 === "completed") return "bg-ui-tag-green-bg text-ui-tag-green-text";
4676
+ if (s2 === "authorized") return "bg-ui-tag-blue-bg text-ui-tag-blue-text";
4677
+ if (s2 === "error" || s2 === "canceled" || s2 === "cancelled") return "bg-ui-tag-red-bg text-ui-tag-red-text";
4678
+ if (s2 === "pending" || s2 === "requires_more") return "bg-ui-tag-orange-bg text-ui-tag-orange-text";
4679
+ return "bg-ui-tag-purple-bg text-ui-tag-purple-text";
4680
+ };
4681
+ const PaymentDetailPage = () => {
4682
+ const navigate = useNavigate();
4683
+ const params = useParams();
4684
+ const id = params?.id?.trim();
4685
+ const [detail, setDetail] = useState(null);
4686
+ const [loading, setLoading] = useState(!!id);
4687
+ const [error, setError] = useState(null);
4688
+ useEffect(() => {
4689
+ if (!id) {
4690
+ setLoading(false);
4691
+ return;
4692
+ }
4693
+ let cancelled = false;
4694
+ setLoading(true);
4695
+ setError(null);
4696
+ fetch(`/admin/payment-transactions/${id}`, { credentials: "include" }).then((res) => {
4697
+ if (!res.ok) throw new Error(res.statusText || "Failed to load");
4698
+ return res.json();
4699
+ }).then((data) => {
4700
+ if (!cancelled) setDetail(data);
4701
+ }).catch((e) => {
4702
+ if (!cancelled) setError(e instanceof Error ? e.message : "Failed to load");
4703
+ }).finally(() => {
4704
+ if (!cancelled) setLoading(false);
4705
+ });
4706
+ return () => {
4707
+ cancelled = true;
4708
+ };
4709
+ }, [id]);
4710
+ const displayStatus = detail ? (detail.payment_id != null && detail.payment_status != null && detail.payment_status !== "" ? detail.payment_status : detail.session_status ?? "") || "—" : "";
4711
+ const sessionStatusRaw = detail?.session_status ?? "—";
4712
+ const paymentStatusRaw = detail?.payment_id != null ? detail?.payment_status ?? "—" : "—";
4713
+ const displayAmount = detail ? `${(detail.currency_code ?? "USD").toUpperCase()} ${Number(detail.amount)}` : "";
4714
+ return /* @__PURE__ */ jsx("div", { className: "w-full p-6", children: /* @__PURE__ */ jsxs(Container, { className: "mx-auto flex w-full max-w-4xl flex-col gap-6 p-6", children: [
4715
+ /* @__PURE__ */ jsx("header", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
4716
+ /* @__PURE__ */ jsx(Button, { variant: "transparent", size: "small", onClick: () => navigate("/payments"), children: "← Payments" }),
4717
+ /* @__PURE__ */ jsx(Heading, { level: "h1", children: "Payment session" }),
4718
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: id ?? "—" })
4719
+ ] }) }),
4720
+ error ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-ui-border-strong p-6 text-center", children: [
4721
+ /* @__PURE__ */ jsx(Text, { weight: "plus", className: "text-ui-fg-error", children: error }),
4722
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", className: "mt-4", onClick: () => navigate("/payments"), children: "Back to list" })
4723
+ ] }) : loading ? /* @__PURE__ */ jsx("div", { className: "flex justify-center py-16", children: /* @__PURE__ */ jsx(Text, { children: "Loading…" }) }) : detail ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
4724
+ /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-ui-border-base p-6 flex flex-col gap-4", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [
4725
+ /* @__PURE__ */ jsxs("div", { children: [
4726
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Order ID" }),
4727
+ /* @__PURE__ */ jsx("div", { className: "mt-1", children: detail.order_id ? /* @__PURE__ */ jsx(
4728
+ Button,
4729
+ {
4730
+ variant: "transparent",
4731
+ size: "small",
4732
+ onClick: () => navigate(`/orders/${detail.order_id}`),
4733
+ children: detail.order_id
4734
+ }
4735
+ ) : /* @__PURE__ */ jsx(Text, { children: "—" }) })
4736
+ ] }),
4737
+ /* @__PURE__ */ jsxs("div", { children: [
4738
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Amount" }),
4739
+ /* @__PURE__ */ jsx(Text, { className: "mt-1 block", children: displayAmount })
4740
+ ] }),
4741
+ /* @__PURE__ */ jsxs("div", { children: [
4742
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Provider" }),
4743
+ /* @__PURE__ */ jsx(Text, { className: "mt-1 block", children: detail.provider_id })
4744
+ ] }),
4745
+ /* @__PURE__ */ jsxs("div", { children: [
4746
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Status" }),
4747
+ /* @__PURE__ */ jsx("div", { className: "mt-1", children: /* @__PURE__ */ jsx(
4748
+ Badge,
4749
+ {
4750
+ size: "2xsmall",
4751
+ className: `uppercase ${getStatusBadgeClass(displayStatus)}`,
4752
+ children: displayStatus !== "—" ? displayStatus.replace(/_/g, " ") : "—"
4753
+ }
4754
+ ) })
4755
+ ] }),
4756
+ /* @__PURE__ */ jsxs("div", { children: [
4757
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Session status (DB)" }),
4758
+ /* @__PURE__ */ jsx(Text, { className: "mt-1 block", children: sessionStatusRaw })
4759
+ ] }),
4760
+ /* @__PURE__ */ jsxs("div", { children: [
4761
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Payment status (DB)" }),
4762
+ /* @__PURE__ */ jsx(Text, { className: "mt-1 block", children: paymentStatusRaw })
4763
+ ] }),
4764
+ /* @__PURE__ */ jsxs("div", { children: [
4765
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Finalized" }),
4766
+ /* @__PURE__ */ jsx(Text, { className: "mt-1 block", children: detail.payment_id != null ? "Yes" : "No" })
4767
+ ] }),
4768
+ /* @__PURE__ */ jsxs("div", { children: [
4769
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "Created" }),
4770
+ /* @__PURE__ */ jsx(Text, { className: "mt-1 block", children: new Date(detail.created_at).toLocaleDateString("en-US", {
4771
+ year: "numeric",
4772
+ month: "short",
4773
+ day: "numeric",
4774
+ hour: "numeric",
4775
+ minute: "2-digit",
4776
+ hour12: true
4777
+ }) })
4778
+ ] })
4779
+ ] }) }),
4780
+ Object.keys(detail.data ?? {}).length > 0 ? /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-ui-border-base p-6", children: [
4781
+ /* @__PURE__ */ jsx(Heading, { level: "h2", className: "text-lg mb-4", children: "Provider data" }),
4782
+ /* @__PURE__ */ jsx("pre", { className: "text-xs bg-ui-bg-subtle p-4 rounded-lg overflow-auto max-h-96", children: JSON.stringify(detail.data, null, 2) })
4783
+ ] }) : /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-ui-border-base p-6", children: /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-muted", children: "No provider data for this session." }) })
4784
+ ] }) : null
4785
+ ] }) });
4786
+ };
4787
+ const config = defineRouteConfig({
4788
+ label: "Payment details",
4789
+ icon: CreditCard
4790
+ });
2760
4791
  const i18nTranslations0 = {};
2761
4792
  const widgetModule = { widgets: [
2762
4793
  {
@@ -2773,43 +4804,53 @@ const widgetModule = { widgets: [
2773
4804
  const routeModule = {
2774
4805
  routes: [
2775
4806
  {
2776
- Component: PaymentsPage,
2777
- path: "/payments",
2778
- handle: { label: config$7.label, translationNs: config$7.translationNs }
4807
+ Component: DeletedRecordsPage,
4808
+ path: "/deleted-records-audit",
4809
+ handle: { label: config$9.label, translationNs: config$9.translationNs }
2779
4810
  },
2780
4811
  {
2781
4812
  Component: RefundsPage,
2782
4813
  path: "/refunds",
2783
- handle: { label: config$6.label, translationNs: config$6.translationNs }
4814
+ handle: { label: config$8.label, translationNs: config$8.translationNs }
2784
4815
  },
2785
4816
  {
2786
4817
  Component: ReturnsPage,
2787
4818
  path: "/returns",
2788
- handle: { label: config$5.label, translationNs: config$5.translationNs }
4819
+ handle: { label: config$7.label, translationNs: config$7.translationNs }
2789
4820
  },
2790
4821
  {
2791
4822
  Component: SwapsPage,
2792
4823
  path: "/swaps",
2793
- handle: { label: config$4.label, translationNs: config$4.translationNs }
4824
+ handle: { label: config$6.label, translationNs: config$6.translationNs }
2794
4825
  },
2795
4826
  {
2796
- Component: PaymentDetailPage,
2797
- path: "/payments/:id",
2798
- handle: { label: config$3.label, translationNs: config$3.translationNs }
4827
+ Component: PaymentsPage,
4828
+ path: "/payments",
4829
+ handle: { label: config$5.label, translationNs: config$5.translationNs }
4830
+ },
4831
+ {
4832
+ Component: DeletedRecordDetailPage,
4833
+ path: "/deleted-records-audit/:id",
4834
+ handle: { label: config$4.label, translationNs: config$4.translationNs }
2799
4835
  },
2800
4836
  {
2801
4837
  Component: RefundDetailPage,
2802
4838
  path: "/refunds/:id",
2803
- handle: { label: config$2.label, translationNs: config$2.translationNs }
4839
+ handle: { label: config$3.label, translationNs: config$3.translationNs }
2804
4840
  },
2805
4841
  {
2806
4842
  Component: ReturnDetailPage,
2807
4843
  path: "/returns/:id",
2808
- handle: { label: config$1.label, translationNs: config$1.translationNs }
4844
+ handle: { label: config$2.label, translationNs: config$2.translationNs }
2809
4845
  },
2810
4846
  {
2811
4847
  Component: SwapDetailPage,
2812
4848
  path: "/swaps/:id",
4849
+ handle: { label: config$1.label, translationNs: config$1.translationNs }
4850
+ },
4851
+ {
4852
+ Component: PaymentDetailPage,
4853
+ path: "/payments/:id",
2813
4854
  handle: { label: config.label, translationNs: config.translationNs }
2814
4855
  }
2815
4856
  ]
@@ -2817,8 +4858,16 @@ const routeModule = {
2817
4858
  const menuItemModule = {
2818
4859
  menuItems: [
2819
4860
  {
2820
- label: config$7.label,
2821
- icon: config$7.icon,
4861
+ label: config$9.label,
4862
+ icon: config$9.icon,
4863
+ path: "/deleted-records-audit",
4864
+ nested: void 0,
4865
+ rank: void 0,
4866
+ translationNs: void 0
4867
+ },
4868
+ {
4869
+ label: config$5.label,
4870
+ icon: config$5.icon,
2822
4871
  path: "/payments",
2823
4872
  nested: void 0,
2824
4873
  rank: void 0,
@@ -2827,55 +4876,55 @@ const menuItemModule = {
2827
4876
  {
2828
4877
  label: config$6.label,
2829
4878
  icon: config$6.icon,
2830
- path: "/refunds",
4879
+ path: "/swaps",
2831
4880
  nested: void 0,
2832
4881
  rank: void 0,
2833
4882
  translationNs: void 0
2834
4883
  },
2835
4884
  {
2836
- label: config$5.label,
2837
- icon: config$5.icon,
4885
+ label: config$7.label,
4886
+ icon: config$7.icon,
2838
4887
  path: "/returns",
2839
4888
  nested: void 0,
2840
4889
  rank: void 0,
2841
4890
  translationNs: void 0
2842
4891
  },
2843
4892
  {
2844
- label: config$4.label,
2845
- icon: config$4.icon,
2846
- path: "/swaps",
4893
+ label: config$8.label,
4894
+ icon: config$8.icon,
4895
+ path: "/refunds",
2847
4896
  nested: void 0,
2848
4897
  rank: void 0,
2849
4898
  translationNs: void 0
2850
4899
  },
2851
4900
  {
2852
- label: config$3.label,
2853
- icon: config$3.icon,
4901
+ label: config.label,
4902
+ icon: config.icon,
2854
4903
  path: "/payments/:id",
2855
4904
  nested: void 0,
2856
4905
  rank: void 0,
2857
4906
  translationNs: void 0
2858
4907
  },
2859
4908
  {
2860
- label: config$2.label,
2861
- icon: config$2.icon,
2862
- path: "/refunds/:id",
4909
+ label: config$1.label,
4910
+ icon: config$1.icon,
4911
+ path: "/swaps/:id",
2863
4912
  nested: void 0,
2864
4913
  rank: void 0,
2865
4914
  translationNs: void 0
2866
4915
  },
2867
4916
  {
2868
- label: config$1.label,
2869
- icon: config$1.icon,
4917
+ label: config$2.label,
4918
+ icon: config$2.icon,
2870
4919
  path: "/returns/:id",
2871
4920
  nested: void 0,
2872
4921
  rank: void 0,
2873
4922
  translationNs: void 0
2874
4923
  },
2875
4924
  {
2876
- label: config.label,
2877
- icon: config.icon,
2878
- path: "/swaps/:id",
4925
+ label: config$3.label,
4926
+ icon: config$3.icon,
4927
+ path: "/refunds/:id",
2879
4928
  nested: void 0,
2880
4929
  rank: void 0,
2881
4930
  translationNs: void 0