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