@isaiahiroko/tilmart-ngx-apps-admin 0.2.0 → 0.2.3

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.
Files changed (110) hide show
  1. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-articles.module-OeLRnPwN.mjs → isaiahiroko-tilmart-ngx-apps-admin-articles.module-Cpx9BZcg.mjs} +5 -5
  2. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-articles.module-OeLRnPwN.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-articles.module-Cpx9BZcg.mjs.map} +1 -1
  3. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-attribute-templates.module-CwQnW1cH.mjs +239 -0
  4. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-attribute-templates.module-CwQnW1cH.mjs.map +1 -0
  5. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-audit.module-C9gvw8X2.mjs → isaiahiroko-tilmart-ngx-apps-admin-audit.module-D5YaCThD.mjs} +6 -6
  6. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-audit.module-C9gvw8X2.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-audit.module-D5YaCThD.mjs.map} +1 -1
  7. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-banners.module-KSapnx6Y.mjs +76 -0
  8. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-banners.module-KSapnx6Y.mjs.map +1 -0
  9. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-brands.module-7I0ni-tQ.mjs +70 -0
  10. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-brands.module-7I0ni-tQ.mjs.map +1 -0
  11. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-bulk-upload.module-BGTbsYUP.mjs +370 -0
  12. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-bulk-upload.module-BGTbsYUP.mjs.map +1 -0
  13. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-BwjfFXDU.mjs +562 -0
  14. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-BwjfFXDU.mjs.map +1 -0
  15. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-categories.module-OTUfenf0.mjs +94 -0
  16. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-categories.module-OTUfenf0.mjs.map +1 -0
  17. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Dk9mz7Wj.mjs +137 -0
  18. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Dk9mz7Wj.mjs.map +1 -0
  19. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-xN-JUWwp.mjs → isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-JiFnNJj_.mjs} +6 -6
  20. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-xN-JUWwp.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-JiFnNJj_.mjs.map} +1 -1
  21. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-content.module-DLdKAUqc.mjs → isaiahiroko-tilmart-ngx-apps-admin-content.module-Bx6Zj0WX.mjs} +5 -5
  22. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-content.module-DLdKAUqc.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-content.module-Bx6Zj0WX.mjs.map} +1 -1
  23. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CoRh6Hxq.mjs +85 -0
  24. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CoRh6Hxq.mjs.map +1 -0
  25. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-CslGiOdq.mjs +123 -0
  26. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-CslGiOdq.mjs.map +1 -0
  27. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-Bfgh3zE5.mjs → isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-CvFmMoJe.mjs} +6 -6
  28. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-Bfgh3zE5.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-CvFmMoJe.mjs.map} +1 -1
  29. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-C6tPNlup.mjs → isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-CuZwNVPO.mjs} +6 -6
  30. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-C6tPNlup.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-CuZwNVPO.mjs.map} +1 -1
  31. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-flash-sales.module-BPRnC17l.mjs +83 -0
  32. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-flash-sales.module-BPRnC17l.mjs.map +1 -0
  33. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-homepage.module-BZ3UmxE3.mjs → isaiahiroko-tilmart-ngx-apps-admin-homepage.module-vtAoIwSu.mjs} +5 -5
  34. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-homepage.module-BZ3UmxE3.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-homepage.module-vtAoIwSu.mjs.map} +1 -1
  35. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-inbox.module-BZDTa1L-.mjs → isaiahiroko-tilmart-ngx-apps-admin-inbox.module-CRkfjWQT.mjs} +6 -6
  36. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-inbox.module-BZDTa1L-.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-inbox.module-CRkfjWQT.mjs.map} +1 -1
  37. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-list-page.base-Cl7JZ-Tp.mjs +66 -0
  38. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-list-page.base-Cl7JZ-Tp.mjs.map +1 -0
  39. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-macros.module-BO4bdER2.mjs → isaiahiroko-tilmart-ngx-apps-admin-macros.module-CElxL9W4.mjs} +5 -5
  40. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-macros.module-BO4bdER2.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-macros.module-CElxL9W4.mjs.map} +1 -1
  41. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-moderation.module-CZOCrias.mjs → isaiahiroko-tilmart-ngx-apps-admin-moderation.module-PGmFX7nk.mjs} +6 -6
  42. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-moderation.module-CZOCrias.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-moderation.module-PGmFX7nk.mjs.map} +1 -1
  43. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-navigation.module-DoKzzRgn.mjs → isaiahiroko-tilmart-ngx-apps-admin-navigation.module-De1Cvqqa.mjs} +6 -6
  44. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-navigation.module-DoKzzRgn.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-navigation.module-De1Cvqqa.mjs.map} +1 -1
  45. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-order-board.module-DIOjhptn.mjs → isaiahiroko-tilmart-ngx-apps-admin-order-board.module-CmLNiEEP.mjs} +4 -4
  46. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-order-board.module-DIOjhptn.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-order-board.module-CmLNiEEP.mjs.map} +1 -1
  47. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-orders.module-vldWwcAr.mjs +190 -0
  48. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-orders.module-vldWwcAr.mjs.map +1 -0
  49. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-DstLDYM7.mjs +114 -0
  50. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-DstLDYM7.mjs.map +1 -0
  51. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG03OY1q.mjs +94 -0
  52. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG03OY1q.mjs.map +1 -0
  53. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-queues.module-K-zYYVFy.mjs → isaiahiroko-tilmart-ngx-apps-admin-queues.module-D7iLwX0A.mjs} +6 -6
  54. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-queues.module-K-zYYVFy.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-queues.module-D7iLwX0A.mjs.map} +1 -1
  55. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-oQw5Xo5T.mjs +78 -0
  56. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-oQw5Xo5T.mjs.map +1 -0
  57. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-referrals.module-QLNhuh7q.mjs → isaiahiroko-tilmart-ngx-apps-admin-referrals.module-BIAY4L_u.mjs} +9 -34
  58. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-referrals.module-BIAY4L_u.mjs.map +1 -0
  59. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-reports.module-DBZov0We.mjs → isaiahiroko-tilmart-ngx-apps-admin-reports.module-DORiIVLQ.mjs} +5 -5
  60. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-reports.module-DBZov0We.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-reports.module-DORiIVLQ.mjs.map} +1 -1
  61. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-C9Iu9CX6.mjs +163 -0
  62. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-C9Iu9CX6.mjs.map +1 -0
  63. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-CGBBHwId.mjs +81 -0
  64. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-CGBBHwId.mjs.map +1 -0
  65. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-Cz6xPNwB.mjs +318 -0
  66. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-Cz6xPNwB.mjs.map +1 -0
  67. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-shipments.module-XuaOKr8v.mjs → isaiahiroko-tilmart-ngx-apps-admin-shipments.module-Cs5CLamm.mjs} +13 -32
  68. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-shipments.module-Cs5CLamm.mjs.map +1 -0
  69. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs +140 -0
  70. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs.map +1 -0
  71. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-Bt9W1oGR.mjs +102 -0
  72. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-Bt9W1oGR.mjs.map +1 -0
  73. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-trace.module-Pwr-82Rc.mjs → isaiahiroko-tilmart-ngx-apps-admin-trace.module-9sRlGZCC.mjs} +6 -6
  74. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-trace.module-Pwr-82Rc.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-trace.module-9sRlGZCC.mjs.map} +1 -1
  75. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-users.module-Bx_7fI2g.mjs → isaiahiroko-tilmart-ngx-apps-admin-users.module-C31RJJJ_.mjs} +9 -34
  76. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-users.module-C31RJJJ_.mjs.map +1 -0
  77. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin.mjs +104 -72
  78. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin.mjs.map +1 -1
  79. package/package.json +1 -1
  80. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-baskets.module-_lHPl1dT.mjs +0 -294
  81. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-baskets.module-_lHPl1dT.mjs.map +0 -1
  82. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-Bvx2tVOi.mjs +0 -224
  83. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-Bvx2tVOi.mjs.map +0 -1
  84. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Ccsd8Yx3.mjs +0 -106
  85. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Ccsd8Yx3.mjs.map +0 -1
  86. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CQNCSlsd.mjs +0 -106
  87. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CQNCSlsd.mjs.map +0 -1
  88. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-12XdV9zo.mjs +0 -106
  89. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-12XdV9zo.mjs.map +0 -1
  90. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-orders.module-Ddw49QW0.mjs +0 -185
  91. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-orders.module-Ddw49QW0.mjs.map +0 -1
  92. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-BhqAtTZs.mjs +0 -106
  93. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-BhqAtTZs.mjs.map +0 -1
  94. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-procurement.module-qXW-g0pf.mjs +0 -274
  95. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-procurement.module-qXW-g0pf.mjs.map +0 -1
  96. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG9uTDqX.mjs +0 -106
  97. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG9uTDqX.mjs.map +0 -1
  98. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-Hm2dO_bC.mjs +0 -106
  99. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-Hm2dO_bC.mjs.map +0 -1
  100. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-referrals.module-QLNhuh7q.mjs.map +0 -1
  101. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-Bd2gdo6_.mjs +0 -106
  102. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-Bd2gdo6_.mjs.map +0 -1
  103. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-BkVY06ci.mjs +0 -105
  104. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-BkVY06ci.mjs.map +0 -1
  105. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-DKeZTldO.mjs +0 -222
  106. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-DKeZTldO.mjs.map +0 -1
  107. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-shipments.module-XuaOKr8v.mjs.map +0 -1
  108. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-DYiAkF7E.mjs +0 -107
  109. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-DYiAkF7E.mjs.map +0 -1
  110. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-users.module-Bx_7fI2g.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-orders.module-vldWwcAr.mjs","sources":["../../../projects/admin/src/lib/pages/orders/orders.component.ts","../../../projects/admin/src/lib/pages/orders/orders.component.html","../../../projects/admin/src/lib/pages/orders/orders.module.ts"],"sourcesContent":["import { Component, TemplateRef, ViewChild, inject } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { notify, using } from '@thescaffold/ngx-core';\nimport { downloadBlob } from '@isaiahiroko/tilmart-ngx-ui';\nimport { FlyoutService } from '@thescaffold/ngx-ui';\nimport { takeUntil } from 'rxjs';\nimport { ListPageDirective } from '../_shared/list-page.base';\n\nfunction naira(amount: number, currency = 'NGN'): string {\n return `${currency} ${Math.round(amount).toLocaleString('en-US')}`;\n}\n\n// Ops action buttons per current status — each entry is a permitted\n// `OrderWorkflow.transition` target. Matches the PRD Appendix B matrix;\n// fulfilment-driven statuses (dispatched, delivered, …) are set by the\n// Shipments page, not here.\nconst ACTIONS: Record<string, { to: string; label: string }[]> = {\n payment_received: [{ to: 'confirmed', label: 'Confirm' }, { to: 'on_hold', label: 'Hold' }],\n on_hold: [{ to: 'confirmed', label: 'Release' }, { to: 'cancelled', label: 'Cancel' }],\n confirmed: [{ to: 'processing', label: 'Start preparing' }, { to: 'cancelled', label: 'Cancel' }],\n processing: [{ to: 'cancelled', label: 'Cancel' }],\n pending_payment: [{ to: 'cancelled', label: 'Cancel' }],\n};\n\n@Component({\n standalone: false,\n selector: 'x-orders',\n templateUrl: './orders.component.html',\n styleUrl: './orders.component.scss',\n})\nexport class OrdersComponent extends ListPageDirective {\n private readonly flyoutService = inject(FlyoutService);\n private readonly route = inject(ActivatedRoute);\n\n protected pluralName = 'orders';\n protected dataSource = this.apiService.on('apps/commerce/order');\n private readonly lineSource = this.apiService.on('apps/commerce/order-line');\n private readonly historySource = this.apiService.on('apps/commerce/order-status-history');\n private readonly paymentSource = this.apiService.on('apps/commerce/payment');\n private readonly shipmentSource = this.apiService.on('apps/commerce/shipment');\n\n protected columns = [\n { key: 'orderNumber', value: 'Order' },\n { key: 'status', value: 'Status', type: 'class', class: 'badge text-bg-light border' },\n { key: 'grandTotalLabel', value: 'Total' },\n { key: 'paidLabel', value: 'Paid' },\n { key: 'placedAt', value: 'Placed', type: 'date' },\n ];\n\n detail: any = null;\n detailActions: { to: string; label: string }[] = [];\n naira = naira;\n\n @ViewChild('detailFlyout', { read: TemplateRef, static: true })\n detailFlyout!: TemplateRef<any>;\n\n override init = () => {\n this.query = this.route.snapshot.queryParamMap.get('q') ?? '';\n this.list();\n };\n\n view(item: any) {\n this.detail = null;\n this.detailActions = ACTIONS[item.status] ?? [];\n this.flyoutService.open(this.detailFlyout);\n\n this.lineSource\n .getMany({ orderId: item.id, page: 1, perPage: 200 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using(({ data: lines }: { data: any[] }) => {\n this.detail = {\n id: item.id,\n orderNumber: item.orderNumber,\n status: item.status,\n currency: item.currency,\n businessName: item.businessName,\n recipientName: item.recipientName,\n recipientPhone: item.recipientPhone,\n subtotal: Number(item.subtotal ?? 0),\n deliveryTotal: Number(item.deliveryTotal ?? 0),\n taxTotal: Number(item.taxTotal ?? 0),\n grandTotal: Number(item.grandTotal ?? 0),\n amountPaid: Number(item.amountPaid ?? 0),\n codAmountDue: Number(item.codAmountDue ?? 0),\n lines: lines ?? [],\n history: [],\n payments: [],\n shipments: [],\n };\n\n this.historySource\n .getMany({ orderId: item.id, page: 1, perPage: 100 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(using(({ data }: { data: any[] }) => {\n if (this.detail) this.detail.history = data ?? [];\n }));\n this.paymentSource\n .getMany({ orderId: item.id, page: 1, perPage: 20 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(using(({ data }: { data: any[] }) => {\n if (this.detail) this.detail.payments = data ?? [];\n }));\n this.shipmentSource\n .getMany({ orderId: item.id, page: 1, perPage: 50 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(using(({ data }: { data: any[] }) => {\n if (this.detail) this.detail.shipments = data ?? [];\n }));\n }),\n );\n }\n\n // ORD-11 — the order's VAT invoice as a PDF. Fetched as a Blob (auth\n // header, not a cookie) and saved under the invoice's document number.\n downloadingInvoice = false;\n downloadInvoice() {\n if (!this.detail || this.downloadingInvoice) return;\n const { id, orderNumber } = this.detail;\n this.downloadingInvoice = true;\n this.apiService.requestService\n .raw<Blob>(\n 'get',\n `apps/commerce/document/order/${id}/invoice`,\n { format: 'pdf' },\n { responseType: 'blob' },\n )\n .pipe(takeUntil(this.$))\n .subscribe({\n next: (blob) => {\n this.downloadingInvoice = false;\n downloadBlob(blob, `INV-${orderNumber}.pdf`);\n },\n error: () => {\n this.downloadingInvoice = false;\n notify('Could not generate the invoice PDF.', undefined, 'error');\n },\n });\n }\n\n transition(to: string) {\n if (!this.detail) return;\n this.apiService\n .post(`apps/commerce/order/${this.detail.id}/transition`, { toStatus: to })\n .pipe(takeUntil(this.$))\n .subscribe(\n using((res: any) => {\n if (res?.status === 'error') {\n notify(res.message || 'That transition is not allowed.', undefined, 'error');\n return;\n }\n notify(`Order moved to ${to.replace(/_/g, ' ')}.`, undefined, 'success');\n this.flyoutService.dismiss();\n this.list();\n }),\n );\n }\n\n protected override mapRow(order: any) {\n return {\n ...order,\n grandTotalLabel: naira(Number(order.grandTotal ?? 0), order.currency),\n paidLabel: naira(Number(order.amountPaid ?? 0), order.currency),\n };\n }\n\n protected override extraCollectionFields() {\n return {\n links: {\n secondary: [{ title: 'View', fn: (_link: any, item: any) => this.view(item) }],\n },\n };\n }\n}\n","<div class=\"x-page-head\">\n <div class=\"x-page-title\">Orders</div>\n <div class=\"x-page-subtitle\">Every order — search, drill in, move it forward.</div>\n</div>\n\n@if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n} @else {\n <x-element-loader></x-element-loader>\n}\n\n<ng-template #detailFlyout>\n <x-webget-flyout [data]=\"{ title: 'Order detail' }\">\n <div class=\"p-4 d-flex flex-column gap-3\">\n @if (detail) {\n <div>\n <div class=\"fw-bold font-monospace\">\n {{ detail.orderNumber }}\n <a\n class=\"small ms-2\"\n [routerLink]=\"['/admin/trace']\"\n [queryParams]=\"{ ref: detail.orderNumber }\"\n >full trace →</a>\n </div>\n <span class=\"badge text-bg-light border mt-1\">{{ detail.status }}</span>\n @if (detail.businessName) {\n <div class=\"text-secondary small mt-1\">Invoice to: {{ detail.businessName }}</div>\n }\n @if (detail.recipientName) {\n <div class=\"text-secondary small\">Recipient: {{ detail.recipientName }} · {{ detail.recipientPhone }}</div>\n }\n </div>\n\n <div class=\"d-flex flex-wrap gap-2\">\n <button\n type=\"button\"\n class=\"btn btn-sm btn-outline-primary\"\n [disabled]=\"downloadingInvoice\"\n (click)=\"downloadInvoice()\"\n >\n {{ downloadingInvoice ? 'Preparing…' : 'Download invoice (PDF)' }}\n </button>\n </div>\n\n @if (detailActions.length) {\n <div class=\"d-flex flex-wrap gap-2\">\n @for (action of detailActions; track action.to) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-dark\" (click)=\"transition(action.to)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <table class=\"table table-sm\">\n <thead><tr><th>Item</th><th>Qty</th><th class=\"text-end\">Amount</th></tr></thead>\n <tbody>\n @for (line of detail.lines; track line.id) {\n <tr>\n <td>{{ line.nameSnapshot || line.variantId }}</td>\n <td>{{ line.quantity }}</td>\n <td class=\"text-end\">{{ naira(line.unitPrice * line.quantity, detail.currency) }}</td>\n </tr>\n } @empty {\n <tr><td colspan=\"3\" class=\"text-secondary\">No lines.</td></tr>\n }\n </tbody>\n </table>\n\n <div class=\"small\">\n <div class=\"d-flex justify-content-between\"><span>Subtotal</span><span>{{ naira(detail.subtotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Delivery</span><span>{{ naira(detail.deliveryTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>VAT (incl.)</span><span>{{ naira(detail.taxTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between fw-bold\"><span>Total</span><span>{{ naira(detail.grandTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Paid</span><span>{{ naira(detail.amountPaid, detail.currency) }}</span></div>\n @if (detail.codAmountDue > 0) {\n <div class=\"d-flex justify-content-between text-warning\"><span>Due on delivery</span><span>{{ naira(detail.codAmountDue, detail.currency) }}</span></div>\n }\n </div>\n\n <hr class=\"my-1\" />\n <div>\n <div class=\"fw-bold small mb-1\">Payments</div>\n @for (payment of detail.payments; track payment.id) {\n <div class=\"small text-secondary\">\n {{ payment.provider }} · {{ payment.method || 'card' }} · {{ payment.status }}\n · {{ naira(payment.amount, payment.currency) }}\n </div>\n } @empty {\n <div class=\"small text-secondary\">None.</div>\n }\n </div>\n\n <div>\n <div class=\"fw-bold small mb-1\">Shipments</div>\n @for (shipment of detail.shipments; track shipment.id) {\n <div class=\"small text-secondary\">\n {{ shipment.status }}@if (shipment.trackingRef) { · {{ shipment.trackingRef }} }\n </div>\n } @empty {\n <div class=\"small text-secondary\">None yet — create one on the Shipments page.</div>\n }\n </div>\n\n <div>\n <div class=\"fw-bold small mb-1\">History</div>\n @for (row of detail.history; track row.id) {\n <div class=\"small text-secondary\">\n {{ row.toStatus }} · {{ row.createdAt | date: 'MMM d, h:mm a' }}\n @if (row.reason) { — {{ row.reason }} }\n </div>\n } @empty {\n <div class=\"small text-secondary\">No transitions recorded.</div>\n }\n </div>\n } @else {\n <x-element-loader></x-element-loader>\n }\n </div>\n </x-webget-flyout>\n</ng-template>\n","import { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { SharedModule } from '@thescaffold/ngx-core';\nimport { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';\nimport { OrdersComponent } from './orders.component';\n\n@NgModule({\n declarations: [OrdersComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: OrdersComponent }]),\n ],\n})\nexport class OrdersModule {}\n"],"names":[],"mappings":";;;;;;;;;;;;AAQA,SAAS,KAAK,CAAC,MAAc,EAAE,QAAQ,GAAG,KAAK,EAAA;AAC7C,IAAA,OAAO,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE;AACpE;AAEA;AACA;AACA;AACA;AACA,MAAM,OAAO,GAAoD;IAC/D,gBAAgB,EAAE,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC3F,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IACtF,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IACjG,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClD,eAAe,EAAE,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;CACxD;AAQK,MAAO,eAAgB,SAAQ,iBAAiB,CAAA;AANtD,IAAA,WAAA,GAAA;;AAOmB,QAAA,IAAA,CAAA,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;AACrC,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC;QAErC,IAAA,CAAA,UAAU,GAAG,QAAQ;QACrB,IAAA,CAAA,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,qBAAqB,CAAC;QAC/C,IAAA,CAAA,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,0BAA0B,CAAC;QAC3D,IAAA,CAAA,aAAa,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,oCAAoC,CAAC;QACxE,IAAA,CAAA,aAAa,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,uBAAuB,CAAC;QAC3D,IAAA,CAAA,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,wBAAwB,CAAC;AAEpE,QAAA,IAAA,CAAA,OAAO,GAAG;AAClB,YAAA,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE;AACtC,YAAA,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,4BAA4B,EAAE;AACtF,YAAA,EAAE,GAAG,EAAE,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE;AAC1C,YAAA,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE;YACnC,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE;SACnD;QAED,IAAA,CAAA,MAAM,GAAQ,IAAI;QAClB,IAAA,CAAA,aAAa,GAAoC,EAAE;QACnD,IAAA,CAAA,KAAK,GAAG,KAAK;QAKJ,IAAA,CAAA,IAAI,GAAG,MAAK;AACnB,YAAA,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE;YAC7D,IAAI,CAAC,IAAI,EAAE;AACb,QAAA,CAAC;;;QAwDD,IAAA,CAAA,kBAAkB,GAAG,KAAK;AA0D3B,IAAA;AAhHC,IAAA,IAAI,CAAC,IAAS,EAAA;AACZ,QAAA,IAAI,CAAC,MAAM,GAAG,IAAI;QAClB,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;QAC/C,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC;AAE1C,QAAA,IAAI,CAAC;AACF,aAAA,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAS;AAC1D,aAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;aACtB,SAAS,CACR,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAmB,KAAI;YACzC,IAAI,CAAC,MAAM,GAAG;gBACZ,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,YAAY,EAAE,IAAI,CAAC,YAAY;gBAC/B,aAAa,EAAE,IAAI,CAAC,aAAa;gBACjC,cAAc,EAAE,IAAI,CAAC,cAAc;gBACnC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;gBACpC,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC;gBAC9C,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;gBACpC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;gBACxC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;gBACxC,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC;gBAC5C,KAAK,EAAE,KAAK,IAAI,EAAE;AAClB,gBAAA,OAAO,EAAE,EAAE;AACX,gBAAA,QAAQ,EAAE,EAAE;AACZ,gBAAA,SAAS,EAAE,EAAE;aACd;AAED,YAAA,IAAI,CAAC;AACF,iBAAA,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAS;AAC1D,iBAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;iBACtB,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,EAAmB,KAAI;gBAC7C,IAAI,IAAI,CAAC,MAAM;oBAAE,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,IAAI,EAAE;YACnD,CAAC,CAAC,CAAC;AACL,YAAA,IAAI,CAAC;AACF,iBAAA,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAS;AACzD,iBAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;iBACtB,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,EAAmB,KAAI;gBAC7C,IAAI,IAAI,CAAC,MAAM;oBAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,IAAI,IAAI,EAAE;YACpD,CAAC,CAAC,CAAC;AACL,YAAA,IAAI,CAAC;AACF,iBAAA,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAS;AACzD,iBAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;iBACtB,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,EAAmB,KAAI;gBAC7C,IAAI,IAAI,CAAC,MAAM;oBAAE,IAAI,CAAC,MAAM,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE;YACrD,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CACH;IACL;IAKA,eAAe,GAAA;AACb,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,kBAAkB;YAAE;QAC7C,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,MAAM;AACvC,QAAA,IAAI,CAAC,kBAAkB,GAAG,IAAI;QAC9B,IAAI,CAAC,UAAU,CAAC;AACb,aAAA,GAAG,CACF,KAAK,EACL,gCAAgC,EAAE,CAAA,QAAA,CAAU,EAC5C,EAAE,MAAM,EAAE,KAAK,EAAE,EACjB,EAAE,YAAY,EAAE,MAAM,EAAE;AAEzB,aAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACtB,aAAA,SAAS,CAAC;AACT,YAAA,IAAI,EAAE,CAAC,IAAI,KAAI;AACb,gBAAA,IAAI,CAAC,kBAAkB,GAAG,KAAK;AAC/B,gBAAA,YAAY,CAAC,IAAI,EAAE,OAAO,WAAW,CAAA,IAAA,CAAM,CAAC;YAC9C,CAAC;YACD,KAAK,EAAE,MAAK;AACV,gBAAA,IAAI,CAAC,kBAAkB,GAAG,KAAK;AAC/B,gBAAA,MAAM,CAAC,qCAAqC,EAAE,SAAS,EAAE,OAAO,CAAC;YACnE,CAAC;AACF,SAAA,CAAC;IACN;AAEA,IAAA,UAAU,CAAC,EAAU,EAAA;QACnB,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE;AAClB,QAAA,IAAI,CAAC;AACF,aAAA,IAAI,CAAC,CAAA,oBAAA,EAAuB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAA,WAAA,CAAa,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;AACzE,aAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACtB,aAAA,SAAS,CACR,KAAK,CAAC,CAAC,GAAQ,KAAI;AACjB,YAAA,IAAI,GAAG,EAAE,MAAM,KAAK,OAAO,EAAE;gBAC3B,MAAM,CAAC,GAAG,CAAC,OAAO,IAAI,iCAAiC,EAAE,SAAS,EAAE,OAAO,CAAC;gBAC5E;YACF;AACA,YAAA,MAAM,CAAC,CAAA,eAAA,EAAkB,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,CAAC;AACxE,YAAA,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE;YAC5B,IAAI,CAAC,IAAI,EAAE;QACb,CAAC,CAAC,CACH;IACL;AAEmB,IAAA,MAAM,CAAC,KAAU,EAAA;QAClC,OAAO;AACL,YAAA,GAAG,KAAK;AACR,YAAA,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC;AACrE,YAAA,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC;SAChE;IACH;IAEmB,qBAAqB,GAAA;QACtC,OAAO;AACL,YAAA,KAAK,EAAE;gBACL,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,KAAU,EAAE,IAAS,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAC/E,aAAA;SACF;IACH;+GA9IW,eAAe,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;mGAAf,eAAe,EAAA,YAAA,EAAA,KAAA,EAAA,QAAA,EAAA,UAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,cAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,cAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,IAAA,EAuBS,WAAW,EAAA,MAAA,EAAA,IAAA,EAAA,CAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECrDhD,4mKAyHA,EAAA,MAAA,EAAA,CAAA,wLAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,UAAA,EAAA,QAAA,EAAA,cAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,aAAA,EAAA,UAAA,EAAA,qBAAA,EAAA,OAAA,EAAA,MAAA,EAAA,YAAA,EAAA,kBAAA,EAAA,oBAAA,EAAA,YAAA,EAAA,YAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,mBAAA,EAAA,QAAA,EAAA,qBAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,OAAA,EAAA,CAAA,QAAA,EAAA,QAAA,EAAA,SAAA,EAAA,QAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAA,EAAA,CAAA,SAAA,EAAA,IAAA,EAAA,OAAA,EAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAA,EAAA,CAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,EAAA,CAAA,CAAA;;4FD3Fa,eAAe,EAAA,UAAA,EAAA,CAAA;kBAN3B,SAAS;AACI,YAAA,IAAA,EAAA,CAAA,EAAA,UAAA,EAAA,KAAK,YACP,UAAU,EAAA,QAAA,EAAA,4mKAAA,EAAA,MAAA,EAAA,CAAA,wLAAA,CAAA,EAAA;;sBA2BnB,SAAS;uBAAC,cAAc,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE;;;MEtCnD,YAAY,CAAA;+GAAZ,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,QAAA,EAAA,CAAA,CAAA;gHAAZ,YAAY,EAAA,YAAA,EAAA,CARR,eAAe,CAAA,EAAA,OAAA,EAAA,CAE5B,YAAY;YACZ,cAAc;YACd,aAAa,EAAA,EAAA,CAAA,YAAA,CAAA,EAAA,CAAA,CAAA;AAIJ,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,mBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,YAAY,YANrB,YAAY;YACZ,cAAc;YACd,aAAa;AACb,YAAA,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC,CAAA,EAAA,CAAA,CAAA;;4FAGxD,YAAY,EAAA,UAAA,EAAA,CAAA;kBATxB,QAAQ;AAAC,YAAA,IAAA,EAAA,CAAA;oBACR,YAAY,EAAE,CAAC,eAAe,CAAC;AAC/B,oBAAA,OAAO,EAAE;wBACP,YAAY;wBACZ,cAAc;wBACd,aAAa;AACb,wBAAA,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC;AAClE,qBAAA;AACF,iBAAA;;;;;"}
@@ -0,0 +1,114 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, TemplateRef, ViewChild, Component, NgModule } from '@angular/core';
3
+ import * as i1 from '@angular/router';
4
+ import { RouterModule } from '@angular/router';
5
+ import { notify, SharedModule } from '@thescaffold/ngx-core';
6
+ import * as i2 from '@thescaffold/ngx-ui';
7
+ import { FlyoutService, ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';
8
+ import { firstValueFrom } from 'rxjs';
9
+ import { L as ListPageDirective } from './isaiahiroko-tilmart-ngx-apps-admin-list-page.base-Cl7JZ-Tp.mjs';
10
+ import * as i3 from '@angular/common';
11
+
12
+ // R1 production-readiness pass — PAY-13. Was list-only with no drill-in
13
+ // at all. Adds a detail flyout (attempts history, failure codes) and
14
+ // wires the ops "re-verify with provider" action that already existed
15
+ // server-side (`OrderWorkflow.verifyPayment`) but had no button.
16
+ //
17
+ // PAY-10 (refunds) is deliberately not touched here — there is no
18
+ // refund workflow on the backend at all (no ledger entry, no provider
19
+ // refund-API call, no reason-code/permission model), so a "Refund"
20
+ // button in this pass would either no-op or move money with none of
21
+ // that in place. That's real, separate scope, not a UI gap.
22
+ class PaymentsComponent extends ListPageDirective {
23
+ constructor() {
24
+ super(...arguments);
25
+ this.flyoutService = inject(FlyoutService);
26
+ this.pluralName = 'payments';
27
+ this.dataSource = this.apiService.on('apps/commerce/payment');
28
+ this.attemptSource = this.apiService.on('apps/commerce/payment-attempt');
29
+ this.columns = [
30
+ { key: 'provider', value: 'Provider', type: 'class', class: 'badge text-bg-light border' },
31
+ { key: 'amount', value: 'Amount' },
32
+ { key: 'currency', value: 'Currency' },
33
+ { key: 'status', value: 'Status', type: 'class', class: 'badge text-bg-light border' },
34
+ { key: 'paidAt', value: 'Paid at', type: 'date' },
35
+ ];
36
+ this.detail = null;
37
+ this.attempts = [];
38
+ this.verifying = false;
39
+ }
40
+ extraCollectionFields() {
41
+ return {
42
+ links: {
43
+ secondary: [{ title: 'View', fn: (_link, item) => this.view(item) }],
44
+ },
45
+ };
46
+ }
47
+ async view(item) {
48
+ this.detail = null;
49
+ this.attempts = [];
50
+ this.flyoutService.open(this.detailFlyout);
51
+ const [detailRes, attemptsRes] = await Promise.all([
52
+ firstValueFrom(this.dataSource.getOne(item.id)),
53
+ firstValueFrom(this.attemptSource.getMany({ paymentId: item.id, page: 1, perPage: 50 })),
54
+ ]);
55
+ this.detail = detailRes?.data ?? item;
56
+ this.attempts = attemptsRes?.data ?? [];
57
+ }
58
+ async reverify() {
59
+ if (!this.detail)
60
+ return;
61
+ this.verifying = true;
62
+ try {
63
+ const res = await firstValueFrom(this.apiService.post(`apps/commerce/payment/${this.detail.id}/verify`));
64
+ if (res?.status === 'error') {
65
+ notify(res.message || 'Verification failed.', undefined, 'error');
66
+ return;
67
+ }
68
+ notify('Re-verified with the provider.', undefined, 'success');
69
+ await this.view(this.detail);
70
+ this.list(this.page, this.perPage);
71
+ }
72
+ catch {
73
+ notify('Verification failed.', undefined, 'error');
74
+ }
75
+ finally {
76
+ this.verifying = false;
77
+ }
78
+ }
79
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PaymentsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
80
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: PaymentsComponent, isStandalone: false, selector: "x-payments", viewQueries: [{ propertyName: "detailFlyout", first: true, predicate: ["detailFlyout"], descendants: true, read: TemplateRef, static: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">Payments</div>\n <div class=\"x-page-subtitle\">Every cycle payment, across providers.</div>\n</div>\n\n@if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n} @else {\n <x-element-loader></x-element-loader>\n}\n\n<ng-template #detailFlyout>\n <x-webget-flyout [data]=\"{ title: 'Payment detail' }\">\n <div class=\"p-4 d-flex flex-column gap-3\">\n @if (detail) {\n <div>\n <span class=\"badge text-bg-light border\">{{ detail.provider }}</span>\n <span class=\"badge text-bg-light border\">{{ detail.status }}</span>\n <span class=\"badge text-bg-light border\">{{ detail.method || 'card' }}</span>\n </div>\n <div class=\"small\">\n <div class=\"d-flex justify-content-between\"><span>Amount</span><span>{{ detail.currency }} {{ detail.amount }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Provider ref</span><span class=\"font-monospace\">{{ detail.providerRef }}</span></div>\n @if (detail.paidAt) {\n <div class=\"d-flex justify-content-between\"><span>Paid at</span><span>{{ detail.paidAt | date: 'MMM d, h:mm a' }}</span></div>\n }\n @if (detail.proofRef) {\n <div class=\"d-flex justify-content-between\"><span>Proof ref</span><span>{{ detail.proofRef }}</span></div>\n }\n </div>\n\n <button type=\"button\" class=\"btn btn-sm btn-outline-dark\" [disabled]=\"verifying\" (click)=\"reverify()\">\n {{ verifying ? 'Verifying\u2026' : 'Re-verify with provider' }}\n </button>\n\n <hr class=\"my-1\" />\n\n <div>\n <div class=\"fw-bold small mb-1\">Attempts</div>\n @for (a of attempts; track a.id) {\n <div class=\"small text-secondary\">\n {{ a.status }} \u00B7 {{ a.source || 'unknown' }}\n @if (a.failureCode) { \u2014 {{ a.failureCode }} }\n \u00B7 {{ a.createdAt | date: 'MMM d, h:mm a' }}\n </div>\n } @empty {\n <div class=\"small text-secondary\">No attempts recorded.</div>\n }\n </div>\n } @else {\n <x-element-loader></x-element-loader>\n }\n </div>\n </x-webget-flyout>\n</ng-template>\n", styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}\n"], dependencies: [{ kind: "component", type: i2.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "component", type: i2.CollectionComponent, selector: "x-webget-collection", inputs: ["data"], outputs: ["search", "filter", "refresh", "select", "page", "show"] }, { kind: "component", type: i2.FlyoutComponent, selector: "x-webget-flyout", inputs: ["data"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.DatePipe, name: "date" }] }); }
81
+ }
82
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PaymentsComponent, decorators: [{
83
+ type: Component,
84
+ args: [{ standalone: false, selector: 'x-payments', template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">Payments</div>\n <div class=\"x-page-subtitle\">Every cycle payment, across providers.</div>\n</div>\n\n@if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n} @else {\n <x-element-loader></x-element-loader>\n}\n\n<ng-template #detailFlyout>\n <x-webget-flyout [data]=\"{ title: 'Payment detail' }\">\n <div class=\"p-4 d-flex flex-column gap-3\">\n @if (detail) {\n <div>\n <span class=\"badge text-bg-light border\">{{ detail.provider }}</span>\n <span class=\"badge text-bg-light border\">{{ detail.status }}</span>\n <span class=\"badge text-bg-light border\">{{ detail.method || 'card' }}</span>\n </div>\n <div class=\"small\">\n <div class=\"d-flex justify-content-between\"><span>Amount</span><span>{{ detail.currency }} {{ detail.amount }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Provider ref</span><span class=\"font-monospace\">{{ detail.providerRef }}</span></div>\n @if (detail.paidAt) {\n <div class=\"d-flex justify-content-between\"><span>Paid at</span><span>{{ detail.paidAt | date: 'MMM d, h:mm a' }}</span></div>\n }\n @if (detail.proofRef) {\n <div class=\"d-flex justify-content-between\"><span>Proof ref</span><span>{{ detail.proofRef }}</span></div>\n }\n </div>\n\n <button type=\"button\" class=\"btn btn-sm btn-outline-dark\" [disabled]=\"verifying\" (click)=\"reverify()\">\n {{ verifying ? 'Verifying\u2026' : 'Re-verify with provider' }}\n </button>\n\n <hr class=\"my-1\" />\n\n <div>\n <div class=\"fw-bold small mb-1\">Attempts</div>\n @for (a of attempts; track a.id) {\n <div class=\"small text-secondary\">\n {{ a.status }} \u00B7 {{ a.source || 'unknown' }}\n @if (a.failureCode) { \u2014 {{ a.failureCode }} }\n \u00B7 {{ a.createdAt | date: 'MMM d, h:mm a' }}\n </div>\n } @empty {\n <div class=\"small text-secondary\">No attempts recorded.</div>\n }\n </div>\n } @else {\n <x-element-loader></x-element-loader>\n }\n </div>\n </x-webget-flyout>\n</ng-template>\n", styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}\n"] }]
85
+ }], propDecorators: { detailFlyout: [{
86
+ type: ViewChild,
87
+ args: ['detailFlyout', { read: TemplateRef, static: true }]
88
+ }] } });
89
+
90
+ class PaymentsModule {
91
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PaymentsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
92
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: PaymentsModule, declarations: [PaymentsComponent], imports: [SharedModule,
93
+ ElementsModule,
94
+ WebgetsModule, i1.RouterModule] }); }
95
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PaymentsModule, imports: [SharedModule,
96
+ ElementsModule,
97
+ WebgetsModule,
98
+ RouterModule.forChild([{ path: '', component: PaymentsComponent }])] }); }
99
+ }
100
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PaymentsModule, decorators: [{
101
+ type: NgModule,
102
+ args: [{
103
+ declarations: [PaymentsComponent],
104
+ imports: [
105
+ SharedModule,
106
+ ElementsModule,
107
+ WebgetsModule,
108
+ RouterModule.forChild([{ path: '', component: PaymentsComponent }]),
109
+ ],
110
+ }]
111
+ }] });
112
+
113
+ export { PaymentsModule };
114
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-payments.module-DstLDYM7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-payments.module-DstLDYM7.mjs","sources":["../../../projects/admin/src/lib/pages/payments/payments.component.ts","../../../projects/admin/src/lib/pages/payments/payments.component.html","../../../projects/admin/src/lib/pages/payments/payments.module.ts"],"sourcesContent":["import { Component, TemplateRef, ViewChild, inject } from '@angular/core';\nimport { notify } from '@thescaffold/ngx-core';\nimport { FlyoutService } from '@thescaffold/ngx-ui';\nimport { firstValueFrom } from 'rxjs';\nimport { ListPageDirective } from '../_shared/list-page.base';\n\n// R1 production-readiness pass — PAY-13. Was list-only with no drill-in\n// at all. Adds a detail flyout (attempts history, failure codes) and\n// wires the ops \"re-verify with provider\" action that already existed\n// server-side (`OrderWorkflow.verifyPayment`) but had no button.\n//\n// PAY-10 (refunds) is deliberately not touched here — there is no\n// refund workflow on the backend at all (no ledger entry, no provider\n// refund-API call, no reason-code/permission model), so a \"Refund\"\n// button in this pass would either no-op or move money with none of\n// that in place. That's real, separate scope, not a UI gap.\n@Component({\n standalone: false,\n selector: 'x-payments',\n templateUrl: './payments.component.html',\n styleUrl: './payments.component.scss',\n})\nexport class PaymentsComponent extends ListPageDirective {\n private readonly flyoutService = inject(FlyoutService);\n\n protected pluralName = 'payments';\n protected dataSource = this.apiService.on('apps/commerce/payment');\n private readonly attemptSource = this.apiService.on('apps/commerce/payment-attempt');\n\n protected columns = [\n { key: 'provider', value: 'Provider', type: 'class', class: 'badge text-bg-light border' },\n { key: 'amount', value: 'Amount' },\n { key: 'currency', value: 'Currency' },\n { key: 'status', value: 'Status', type: 'class', class: 'badge text-bg-light border' },\n { key: 'paidAt', value: 'Paid at', type: 'date' },\n ];\n\n detail: any = null;\n attempts: any[] = [];\n verifying = false;\n\n @ViewChild('detailFlyout', { read: TemplateRef, static: true })\n detailFlyout!: TemplateRef<any>;\n\n protected override extraCollectionFields() {\n return {\n links: {\n secondary: [{ title: 'View', fn: (_link: any, item: any) => this.view(item) }],\n },\n };\n }\n\n async view(item: any): Promise<void> {\n this.detail = null;\n this.attempts = [];\n this.flyoutService.open(this.detailFlyout);\n\n const [detailRes, attemptsRes]: any = await Promise.all([\n firstValueFrom(this.dataSource.getOne(item.id)),\n firstValueFrom(this.attemptSource.getMany({ paymentId: item.id, page: 1, perPage: 50 } as any)),\n ]);\n this.detail = detailRes?.data ?? item;\n this.attempts = attemptsRes?.data ?? [];\n }\n\n async reverify(): Promise<void> {\n if (!this.detail) return;\n this.verifying = true;\n try {\n const res: any = await firstValueFrom(\n this.apiService.post(`apps/commerce/payment/${this.detail.id}/verify`),\n );\n if (res?.status === 'error') {\n notify(res.message || 'Verification failed.', undefined, 'error');\n return;\n }\n notify('Re-verified with the provider.', undefined, 'success');\n await this.view(this.detail);\n this.list(this.page, this.perPage);\n } catch {\n notify('Verification failed.', undefined, 'error');\n } finally {\n this.verifying = false;\n }\n }\n}\n","<div class=\"x-page-head\">\n <div class=\"x-page-title\">Payments</div>\n <div class=\"x-page-subtitle\">Every cycle payment, across providers.</div>\n</div>\n\n@if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n} @else {\n <x-element-loader></x-element-loader>\n}\n\n<ng-template #detailFlyout>\n <x-webget-flyout [data]=\"{ title: 'Payment detail' }\">\n <div class=\"p-4 d-flex flex-column gap-3\">\n @if (detail) {\n <div>\n <span class=\"badge text-bg-light border\">{{ detail.provider }}</span>\n <span class=\"badge text-bg-light border\">{{ detail.status }}</span>\n <span class=\"badge text-bg-light border\">{{ detail.method || 'card' }}</span>\n </div>\n <div class=\"small\">\n <div class=\"d-flex justify-content-between\"><span>Amount</span><span>{{ detail.currency }} {{ detail.amount }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Provider ref</span><span class=\"font-monospace\">{{ detail.providerRef }}</span></div>\n @if (detail.paidAt) {\n <div class=\"d-flex justify-content-between\"><span>Paid at</span><span>{{ detail.paidAt | date: 'MMM d, h:mm a' }}</span></div>\n }\n @if (detail.proofRef) {\n <div class=\"d-flex justify-content-between\"><span>Proof ref</span><span>{{ detail.proofRef }}</span></div>\n }\n </div>\n\n <button type=\"button\" class=\"btn btn-sm btn-outline-dark\" [disabled]=\"verifying\" (click)=\"reverify()\">\n {{ verifying ? 'Verifying…' : 'Re-verify with provider' }}\n </button>\n\n <hr class=\"my-1\" />\n\n <div>\n <div class=\"fw-bold small mb-1\">Attempts</div>\n @for (a of attempts; track a.id) {\n <div class=\"small text-secondary\">\n {{ a.status }} · {{ a.source || 'unknown' }}\n @if (a.failureCode) { — {{ a.failureCode }} }\n · {{ a.createdAt | date: 'MMM d, h:mm a' }}\n </div>\n } @empty {\n <div class=\"small text-secondary\">No attempts recorded.</div>\n }\n </div>\n } @else {\n <x-element-loader></x-element-loader>\n }\n </div>\n </x-webget-flyout>\n</ng-template>\n","import { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { SharedModule } from '@thescaffold/ngx-core';\nimport { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';\nimport { PaymentsComponent } from './payments.component';\n\n@NgModule({\n declarations: [PaymentsComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: PaymentsComponent }]),\n ],\n})\nexport class PaymentsModule {}\n"],"names":["i1","i2"],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Component, NgModule } from '@angular/core';
3
+ import * as i1$1 from '@angular/router';
4
+ import { RouterModule } from '@angular/router';
5
+ import { SharedModule } from '@thescaffold/ngx-core';
6
+ import * as i2 from '@thescaffold/ngx-ui';
7
+ import { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';
8
+ import { S as SimpleCrudDirective } from './isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs';
9
+ import * as i1 from '@angular/forms';
10
+ import * as i3 from '@angular/common';
11
+
12
+ // R1 production-readiness pass — PROMO-02. Was list-only; ops had no way
13
+ // to configure a discount rule. `conditions`/`actions` stay raw JSON —
14
+ // a full visual rule builder is real, separate scope beyond this pass.
15
+ class PromotionsComponent extends SimpleCrudDirective {
16
+ constructor() {
17
+ super(...arguments);
18
+ this.pageTitle = 'Promotions';
19
+ this.pageSubtitle = 'Automatic and coupon-gated discount rules evaluated at checkout.';
20
+ this.singularLabel = 'promotion';
21
+ this.pluralName = 'promotions';
22
+ this.dataSource = this.apiService.on('apps/commerce/promotion');
23
+ this.columns = [
24
+ { key: 'name', value: 'Name' },
25
+ { key: 'status', value: 'Status', type: 'class', class: 'badge text-bg-light border' },
26
+ { key: 'priority', value: 'Priority' },
27
+ { key: 'usageCount', value: 'Used' },
28
+ ];
29
+ this.fields = [
30
+ { key: 'name', label: 'Name', type: 'text', required: true },
31
+ { key: 'description', label: 'Description', type: 'textarea' },
32
+ {
33
+ key: 'status',
34
+ label: 'Status',
35
+ type: 'select',
36
+ options: [
37
+ { value: 'draft', label: 'Draft' },
38
+ { value: 'active', label: 'Active' },
39
+ { value: 'paused', label: 'Paused' },
40
+ { value: 'expired', label: 'Expired' },
41
+ ],
42
+ },
43
+ { key: 'priority', label: 'Priority (lower runs first)', type: 'number' },
44
+ { key: 'stackable', label: 'Stackable with other promotions', type: 'boolean' },
45
+ { key: 'requiresCoupon', label: 'Requires a coupon code', type: 'boolean' },
46
+ { key: 'startsAt', label: 'Starts at', type: 'date' },
47
+ { key: 'endsAt', label: 'Ends at', type: 'date' },
48
+ {
49
+ key: 'conditions',
50
+ label: 'Conditions (JSON)',
51
+ type: 'json',
52
+ placeholder: '{"minSubtotal": 50000, "categoryIds": []}',
53
+ },
54
+ {
55
+ key: 'actions',
56
+ label: 'Actions (JSON array)',
57
+ type: 'json',
58
+ placeholder: '[{"type": "percent_off", "value": 10}]',
59
+ },
60
+ ];
61
+ }
62
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PromotionsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
63
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: PromotionsComponent, isStandalone: false, selector: "x-promotions", usesInheritance: true, ngImport: i0, template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">{{ pageTitle }}</div>\n <div class=\"x-page-subtitle\">{{ pageSubtitle }}</div>\n</div>\n\n@if (!editing) {\n <div class=\"x-scrud-toolbar\">\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" (click)=\"startCreate()\">+ New {{ singularLabel }}</button>\n </div>\n\n @if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n } @else {\n <x-element-loader></x-element-loader>\n }\n} @else if (draft) {\n <div class=\"x-scrud-editor\">\n <div class=\"x-scrud-editor-head\">\n <div class=\"x-scrud-editor-title\">{{ draft['id'] ? 'Edit' : 'New' }} {{ singularLabel }}</div>\n <div class=\"x-scrud-editor-actions\">\n @if (draft['id'] && !hideDelete) {\n <button type=\"button\" class=\"x-scrud-btn x-scrud-danger\" (click)=\"remove(draft); cancelEdit()\">Delete</button>\n }\n <button type=\"button\" class=\"x-scrud-btn\" (click)=\"cancelEdit()\">Close</button>\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" [disabled]=\"saving\" (click)=\"save()\">\n {{ saving ? 'Saving\u2026' : 'Save' }}\n </button>\n </div>\n </div>\n\n <div class=\"x-scrud-grid\">\n @for (f of fields; track f.key) {\n <label class=\"x-scrud-f\" [class.x-scrud-f-wide]=\"f.type === 'textarea' || f.type === 'json'\">\n {{ f.label }}\n @if (f.required) {\n <span class=\"x-scrud-required\">*</span>\n }\n @switch (f.type) {\n @case ('boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"draft[f.key]\" />\n }\n @case ('select') {\n <select [(ngModel)]=\"draft[f.key]\">\n <option value=\"\">\u2014 choose \u2014</option>\n @for (opt of (f.optionsKey ? dynamicOptions[f.optionsKey] : f.options); track opt.value) {\n <option [value]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @case ('textarea') {\n <textarea rows=\"3\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\"></textarea>\n }\n @case ('json') {\n <textarea rows=\"4\" class=\"x-scrud-json\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || '{}'\"></textarea>\n }\n @case ('number') {\n <input type=\"number\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n @case ('date') {\n <input type=\"datetime-local\" [(ngModel)]=\"draft[f.key]\" />\n }\n @default {\n <input type=\"text\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n }\n </label>\n }\n </div>\n </div>\n}\n", styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}.x-scrud-toolbar{display:flex;justify-content:flex-end;margin-bottom:14px}.x-scrud-btn{border:1px solid var(--color-border);background:transparent;border-radius:var(--radius-md);padding:6px 14px;font-size:12.5px;font-weight:700;cursor:pointer;color:var(--color-text-muted)}.x-scrud-btn:disabled{opacity:.5;cursor:default}.x-scrud-btn-primary{border-color:var(--color-accent);color:var(--color-accent-hover)}.x-scrud-danger{color:var(--color-danger, #c0362c)}.x-scrud-editor{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;display:flex;flex-direction:column;gap:14px}.x-scrud-editor-head{display:flex;align-items:center;justify-content:space-between}.x-scrud-editor-title{font-size:16px;font-weight:800;color:var(--color-text)}.x-scrud-editor-actions{display:flex;gap:8px}.x-scrud-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.x-scrud-f{display:flex;flex-direction:column;gap:4px;font-size:11.5px;color:var(--color-text-muted);font-weight:700}.x-scrud-f-wide{grid-column:1/-1}.x-scrud-f input,.x-scrud-f select,.x-scrud-f textarea{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:6px 10px;font-size:13px;background:var(--color-surface);color:var(--color-text);font-weight:400}.x-scrud-f input[type=checkbox]{width:18px;height:18px}.x-scrud-json{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.x-scrud-required{color:var(--color-danger, #c0362c)}\n"], dependencies: [{ kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i2.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "component", type: i2.CollectionComponent, selector: "x-webget-collection", inputs: ["data"], outputs: ["search", "filter", "refresh", "select", "page", "show"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }] }); }
64
+ }
65
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PromotionsComponent, decorators: [{
66
+ type: Component,
67
+ args: [{ standalone: false, selector: 'x-promotions', template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">{{ pageTitle }}</div>\n <div class=\"x-page-subtitle\">{{ pageSubtitle }}</div>\n</div>\n\n@if (!editing) {\n <div class=\"x-scrud-toolbar\">\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" (click)=\"startCreate()\">+ New {{ singularLabel }}</button>\n </div>\n\n @if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n } @else {\n <x-element-loader></x-element-loader>\n }\n} @else if (draft) {\n <div class=\"x-scrud-editor\">\n <div class=\"x-scrud-editor-head\">\n <div class=\"x-scrud-editor-title\">{{ draft['id'] ? 'Edit' : 'New' }} {{ singularLabel }}</div>\n <div class=\"x-scrud-editor-actions\">\n @if (draft['id'] && !hideDelete) {\n <button type=\"button\" class=\"x-scrud-btn x-scrud-danger\" (click)=\"remove(draft); cancelEdit()\">Delete</button>\n }\n <button type=\"button\" class=\"x-scrud-btn\" (click)=\"cancelEdit()\">Close</button>\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" [disabled]=\"saving\" (click)=\"save()\">\n {{ saving ? 'Saving\u2026' : 'Save' }}\n </button>\n </div>\n </div>\n\n <div class=\"x-scrud-grid\">\n @for (f of fields; track f.key) {\n <label class=\"x-scrud-f\" [class.x-scrud-f-wide]=\"f.type === 'textarea' || f.type === 'json'\">\n {{ f.label }}\n @if (f.required) {\n <span class=\"x-scrud-required\">*</span>\n }\n @switch (f.type) {\n @case ('boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"draft[f.key]\" />\n }\n @case ('select') {\n <select [(ngModel)]=\"draft[f.key]\">\n <option value=\"\">\u2014 choose \u2014</option>\n @for (opt of (f.optionsKey ? dynamicOptions[f.optionsKey] : f.options); track opt.value) {\n <option [value]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @case ('textarea') {\n <textarea rows=\"3\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\"></textarea>\n }\n @case ('json') {\n <textarea rows=\"4\" class=\"x-scrud-json\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || '{}'\"></textarea>\n }\n @case ('number') {\n <input type=\"number\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n @case ('date') {\n <input type=\"datetime-local\" [(ngModel)]=\"draft[f.key]\" />\n }\n @default {\n <input type=\"text\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n }\n </label>\n }\n </div>\n </div>\n}\n", styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}.x-scrud-toolbar{display:flex;justify-content:flex-end;margin-bottom:14px}.x-scrud-btn{border:1px solid var(--color-border);background:transparent;border-radius:var(--radius-md);padding:6px 14px;font-size:12.5px;font-weight:700;cursor:pointer;color:var(--color-text-muted)}.x-scrud-btn:disabled{opacity:.5;cursor:default}.x-scrud-btn-primary{border-color:var(--color-accent);color:var(--color-accent-hover)}.x-scrud-danger{color:var(--color-danger, #c0362c)}.x-scrud-editor{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;display:flex;flex-direction:column;gap:14px}.x-scrud-editor-head{display:flex;align-items:center;justify-content:space-between}.x-scrud-editor-title{font-size:16px;font-weight:800;color:var(--color-text)}.x-scrud-editor-actions{display:flex;gap:8px}.x-scrud-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.x-scrud-f{display:flex;flex-direction:column;gap:4px;font-size:11.5px;color:var(--color-text-muted);font-weight:700}.x-scrud-f-wide{grid-column:1/-1}.x-scrud-f input,.x-scrud-f select,.x-scrud-f textarea{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:6px 10px;font-size:13px;background:var(--color-surface);color:var(--color-text);font-weight:400}.x-scrud-f input[type=checkbox]{width:18px;height:18px}.x-scrud-json{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.x-scrud-required{color:var(--color-danger, #c0362c)}\n"] }]
68
+ }] });
69
+
70
+ class PromotionsModule {
71
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PromotionsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
72
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: PromotionsModule, declarations: [PromotionsComponent], imports: [SharedModule,
73
+ ElementsModule,
74
+ WebgetsModule, i1$1.RouterModule] }); }
75
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PromotionsModule, imports: [SharedModule,
76
+ ElementsModule,
77
+ WebgetsModule,
78
+ RouterModule.forChild([{ path: '', component: PromotionsComponent }])] }); }
79
+ }
80
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: PromotionsModule, decorators: [{
81
+ type: NgModule,
82
+ args: [{
83
+ declarations: [PromotionsComponent],
84
+ imports: [
85
+ SharedModule,
86
+ ElementsModule,
87
+ WebgetsModule,
88
+ RouterModule.forChild([{ path: '', component: PromotionsComponent }]),
89
+ ],
90
+ }]
91
+ }] });
92
+
93
+ export { PromotionsModule };
94
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG03OY1q.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG03OY1q.mjs","sources":["../../../projects/admin/src/lib/pages/promotions/promotions.component.ts","../../../projects/admin/src/lib/pages/_shared/simple-crud.component.html","../../../projects/admin/src/lib/pages/promotions/promotions.module.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { CrudFieldSpec, SimpleCrudDirective } from '../_shared/simple-crud.base';\n\n// R1 production-readiness pass — PROMO-02. Was list-only; ops had no way\n// to configure a discount rule. `conditions`/`actions` stay raw JSON —\n// a full visual rule builder is real, separate scope beyond this pass.\n@Component({\n standalone: false,\n selector: 'x-promotions',\n templateUrl: '../_shared/simple-crud.component.html',\n styleUrl: '../_shared/simple-crud.component.scss',\n})\nexport class PromotionsComponent extends SimpleCrudDirective {\n protected pageTitle = 'Promotions';\n protected pageSubtitle = 'Automatic and coupon-gated discount rules evaluated at checkout.';\n protected singularLabel = 'promotion';\n protected pluralName = 'promotions';\n protected dataSource = this.apiService.on('apps/commerce/promotion');\n\n protected columns = [\n { key: 'name', value: 'Name' },\n { key: 'status', value: 'Status', type: 'class', class: 'badge text-bg-light border' },\n { key: 'priority', value: 'Priority' },\n { key: 'usageCount', value: 'Used' },\n ];\n\n protected fields: CrudFieldSpec[] = [\n { key: 'name', label: 'Name', type: 'text', required: true },\n { key: 'description', label: 'Description', type: 'textarea' },\n {\n key: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { value: 'draft', label: 'Draft' },\n { value: 'active', label: 'Active' },\n { value: 'paused', label: 'Paused' },\n { value: 'expired', label: 'Expired' },\n ],\n },\n { key: 'priority', label: 'Priority (lower runs first)', type: 'number' },\n { key: 'stackable', label: 'Stackable with other promotions', type: 'boolean' },\n { key: 'requiresCoupon', label: 'Requires a coupon code', type: 'boolean' },\n { key: 'startsAt', label: 'Starts at', type: 'date' },\n { key: 'endsAt', label: 'Ends at', type: 'date' },\n {\n key: 'conditions',\n label: 'Conditions (JSON)',\n type: 'json',\n placeholder: '{\"minSubtotal\": 50000, \"categoryIds\": []}',\n },\n {\n key: 'actions',\n label: 'Actions (JSON array)',\n type: 'json',\n placeholder: '[{\"type\": \"percent_off\", \"value\": 10}]',\n },\n ];\n}\n","<div class=\"x-page-head\">\n <div class=\"x-page-title\">{{ pageTitle }}</div>\n <div class=\"x-page-subtitle\">{{ pageSubtitle }}</div>\n</div>\n\n@if (!editing) {\n <div class=\"x-scrud-toolbar\">\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" (click)=\"startCreate()\">+ New {{ singularLabel }}</button>\n </div>\n\n @if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n } @else {\n <x-element-loader></x-element-loader>\n }\n} @else if (draft) {\n <div class=\"x-scrud-editor\">\n <div class=\"x-scrud-editor-head\">\n <div class=\"x-scrud-editor-title\">{{ draft['id'] ? 'Edit' : 'New' }} {{ singularLabel }}</div>\n <div class=\"x-scrud-editor-actions\">\n @if (draft['id'] && !hideDelete) {\n <button type=\"button\" class=\"x-scrud-btn x-scrud-danger\" (click)=\"remove(draft); cancelEdit()\">Delete</button>\n }\n <button type=\"button\" class=\"x-scrud-btn\" (click)=\"cancelEdit()\">Close</button>\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" [disabled]=\"saving\" (click)=\"save()\">\n {{ saving ? 'Saving…' : 'Save' }}\n </button>\n </div>\n </div>\n\n <div class=\"x-scrud-grid\">\n @for (f of fields; track f.key) {\n <label class=\"x-scrud-f\" [class.x-scrud-f-wide]=\"f.type === 'textarea' || f.type === 'json'\">\n {{ f.label }}\n @if (f.required) {\n <span class=\"x-scrud-required\">*</span>\n }\n @switch (f.type) {\n @case ('boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"draft[f.key]\" />\n }\n @case ('select') {\n <select [(ngModel)]=\"draft[f.key]\">\n <option value=\"\">— choose —</option>\n @for (opt of (f.optionsKey ? dynamicOptions[f.optionsKey] : f.options); track opt.value) {\n <option [value]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @case ('textarea') {\n <textarea rows=\"3\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\"></textarea>\n }\n @case ('json') {\n <textarea rows=\"4\" class=\"x-scrud-json\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || '{}'\"></textarea>\n }\n @case ('number') {\n <input type=\"number\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n @case ('date') {\n <input type=\"datetime-local\" [(ngModel)]=\"draft[f.key]\" />\n }\n @default {\n <input type=\"text\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n }\n </label>\n }\n </div>\n </div>\n}\n","import { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { SharedModule } from '@thescaffold/ngx-core';\nimport { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';\nimport { PromotionsComponent } from './promotions.component';\n\n@NgModule({\n declarations: [PromotionsComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: PromotionsComponent }]),\n ],\n})\nexport class PromotionsModule {}\n"],"names":["i1"],"mappings":"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"}
@@ -1,12 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Component, NgModule } from '@angular/core';
3
- import * as i1$1 from '@angular/router';
3
+ import * as i1 from '@angular/router';
4
4
  import { RouterModule } from '@angular/router';
5
5
  import { BaseDirective, using, SharedModule } from '@thescaffold/ngx-core';
6
- import * as i1 from '@thescaffold/ngx-ui';
6
+ import * as i2 from '@thescaffold/ngx-ui';
7
7
  import { ElementsModule } from '@thescaffold/ngx-ui';
8
8
  import { takeUntil } from 'rxjs';
9
- import * as i2 from '@angular/common';
9
+ import * as i3 from '@angular/common';
10
10
 
11
11
  // P7.3 — TRD §8.10. The ops queue / SLA board: per-queue backlog, oldest
12
12
  // waiting item, and an SLA-breach flag, plus the registered cron
@@ -53,7 +53,7 @@ class QueuesComponent extends BaseDirective {
53
53
  return `${Math.round(hours / 24)}d`;
54
54
  }
55
55
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: QueuesComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
56
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: QueuesComponent, isStandalone: false, selector: "x-queues", usesInheritance: true, ngImport: i0, template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">Queues &amp; SLAs</div>\n <div class=\"x-page-subtitle\">\n Live ops backlog with target turnaround, plus the scheduled jobs that\n keep it moving.\n </div>\n</div>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-q-summary\">\n <div class=\"x-q-stat\">\n <div class=\"x-q-stat-value\">{{ totalBacklog }}</div>\n <div class=\"x-q-stat-label\">Items in queue</div>\n </div>\n <div class=\"x-q-stat\" [class.is-bad]=\"breachCount > 0\">\n <div class=\"x-q-stat-value\">{{ breachCount }}</div>\n <div class=\"x-q-stat-label\">SLA breaches</div>\n </div>\n @if (generatedAt) {\n <div class=\"x-q-generated\">as of {{ generatedAt | date: 'short' }}</div>\n }\n </div>\n\n <div class=\"x-q-grid\">\n @for (q of queues; track q.key) {\n <div class=\"x-q-card\" [class.is-breached]=\"q.breached\">\n <div class=\"x-q-card-label\">{{ q.label }}</div>\n <div class=\"x-q-card-count\">{{ q.count }}</div>\n <div class=\"x-q-card-meta\">\n <span>oldest {{ ageLabel(q.oldestAgeHours) }}</span>\n @if (q.slaHours != null) {\n <span class=\"x-q-sla\" [class.is-breached]=\"q.breached\">\n SLA {{ q.slaHours }}h\n </span>\n }\n </div>\n </div>\n }\n </div>\n\n <h2 class=\"x-q-crons-title\">Scheduled jobs</h2>\n <table class=\"x-q-crons\">\n <thead>\n <tr><th>Job</th><th>Schedule</th><th>Purpose</th></tr>\n </thead>\n <tbody>\n @for (c of crons; track c.name) {\n <tr>\n <td class=\"x-q-cron-name\">{{ c.name }}</td>\n <td class=\"x-q-cron-pattern\">{{ c.pattern }}</td>\n <td>{{ c.purpose }}</td>\n </tr>\n }\n </tbody>\n </table>\n}\n\n<style>\n .x-page-head { margin-bottom: 20px; }\n .x-page-title { font-size: 24px; font-weight: 800; color: var(--color-text); }\n .x-page-subtitle { font-size: 13.5px; color: var(--color-text-muted); margin-top: 2px; max-width: 620px; }\n\n .x-q-summary { display: flex; align-items: center; gap: 20px; margin-bottom: 20px; }\n .x-q-stat { border: 1px solid var(--color-border); border-radius: 10px; padding: 10px 16px; min-width: 110px; }\n .x-q-stat.is-bad { border-color: var(--color-danger, #c0392b); }\n .x-q-stat-value { font-size: 22px; font-weight: 800; color: var(--color-text); }\n .x-q-stat-label { font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); }\n .x-q-generated { font-size: 12px; color: var(--color-text-muted); }\n\n .x-q-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; margin-bottom: 32px; }\n .x-q-card { border: 1px solid var(--color-border); border-radius: 10px; padding: 12px 14px; }\n .x-q-card.is-breached { border-color: var(--color-danger, #c0392b); background: color-mix(in srgb, var(--color-danger, #c0392b) 6%, transparent); }\n .x-q-card-label { font-size: 12.5px; font-weight: 600; color: var(--color-text-muted); }\n .x-q-card-count { font-size: 26px; font-weight: 800; color: var(--color-text); margin: 4px 0 6px; }\n .x-q-card-meta { display: flex; justify-content: space-between; font-size: 11.5px; color: var(--color-text-muted); }\n .x-q-sla.is-breached { color: var(--color-danger, #c0392b); font-weight: 700; }\n\n .x-q-crons-title { font-size: 15px; font-weight: 700; color: var(--color-text); margin: 0 0 10px; }\n .x-q-crons { width: 100%; border-collapse: collapse; font-size: 13px; }\n .x-q-crons th, .x-q-crons td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--color-border); }\n .x-q-crons th { color: var(--color-text-muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.03em; }\n .x-q-cron-name, .x-q-cron-pattern { font-family: var(--font-mono, monospace); font-size: 12px; }\n</style>\n", styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px;max-width:620px}.x-q-summary{display:flex;align-items:center;gap:20px;margin-bottom:20px}.x-q-stat{border:1px solid var(--color-border);border-radius:10px;padding:10px 16px;min-width:110px}.x-q-stat.is-bad{border-color:var(--color-danger, #c0392b)}.x-q-stat-value{font-size:22px;font-weight:800;color:var(--color-text)}.x-q-stat-label{font-size:11.5px;text-transform:uppercase;letter-spacing:.03em;color:var(--color-text-muted)}.x-q-generated{font-size:12px;color:var(--color-text-muted)}.x-q-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px;margin-bottom:32px}.x-q-card{border:1px solid var(--color-border);border-radius:10px;padding:12px 14px}.x-q-card.is-breached{border-color:var(--color-danger, #c0392b);background:color-mix(in srgb,var(--color-danger, #c0392b) 6%,transparent)}.x-q-card-label{font-size:12.5px;font-weight:600;color:var(--color-text-muted)}.x-q-card-count{font-size:26px;font-weight:800;color:var(--color-text);margin:4px 0 6px}.x-q-card-meta{display:flex;justify-content:space-between;font-size:11.5px;color:var(--color-text-muted)}.x-q-sla.is-breached{color:var(--color-danger, #c0392b);font-weight:700}.x-q-crons-title{font-size:15px;font-weight:700;color:var(--color-text);margin:0 0 10px}.x-q-crons{width:100%;border-collapse:collapse;font-size:13px}.x-q-crons th,.x-q-crons td{text-align:left;padding:7px 10px;border-bottom:1px solid var(--color-border)}.x-q-crons th{color:var(--color-text-muted);font-size:11.5px;text-transform:uppercase;letter-spacing:.03em}.x-q-cron-name,.x-q-cron-pattern{font-family:var(--font-mono, monospace);font-size:12px}\n"], dependencies: [{ kind: "component", type: i1.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "pipe", type: i2.DatePipe, name: "date" }] }); }
56
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: QueuesComponent, isStandalone: false, selector: "x-queues", usesInheritance: true, ngImport: i0, template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">Queues &amp; SLAs</div>\n <div class=\"x-page-subtitle\">\n Live ops backlog with target turnaround, plus the scheduled jobs that\n keep it moving.\n </div>\n</div>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-q-summary\">\n <div class=\"x-q-stat\">\n <div class=\"x-q-stat-value\">{{ totalBacklog }}</div>\n <div class=\"x-q-stat-label\">Items in queue</div>\n </div>\n <div class=\"x-q-stat\" [class.is-bad]=\"breachCount > 0\">\n <div class=\"x-q-stat-value\">{{ breachCount }}</div>\n <div class=\"x-q-stat-label\">SLA breaches</div>\n </div>\n @if (generatedAt) {\n <div class=\"x-q-generated\">as of {{ generatedAt | date: 'short' }}</div>\n }\n </div>\n\n <div class=\"x-q-grid\">\n @for (q of queues; track q.key) {\n <div class=\"x-q-card\" [class.is-breached]=\"q.breached\">\n <div class=\"x-q-card-label\">{{ q.label }}</div>\n <div class=\"x-q-card-count\">{{ q.count }}</div>\n <div class=\"x-q-card-meta\">\n <span>oldest {{ ageLabel(q.oldestAgeHours) }}</span>\n @if (q.slaHours != null) {\n <span class=\"x-q-sla\" [class.is-breached]=\"q.breached\">\n SLA {{ q.slaHours }}h\n </span>\n }\n </div>\n </div>\n }\n </div>\n\n <h2 class=\"x-q-crons-title\">Scheduled jobs</h2>\n <table class=\"x-q-crons\">\n <thead>\n <tr><th>Job</th><th>Schedule</th><th>Purpose</th></tr>\n </thead>\n <tbody>\n @for (c of crons; track c.name) {\n <tr>\n <td class=\"x-q-cron-name\">{{ c.name }}</td>\n <td class=\"x-q-cron-pattern\">{{ c.pattern }}</td>\n <td>{{ c.purpose }}</td>\n </tr>\n }\n </tbody>\n </table>\n}\n\n<style>\n .x-page-head { margin-bottom: 20px; }\n .x-page-title { font-size: 24px; font-weight: 800; color: var(--color-text); }\n .x-page-subtitle { font-size: 13.5px; color: var(--color-text-muted); margin-top: 2px; max-width: 620px; }\n\n .x-q-summary { display: flex; align-items: center; gap: 20px; margin-bottom: 20px; }\n .x-q-stat { border: 1px solid var(--color-border); border-radius: 10px; padding: 10px 16px; min-width: 110px; }\n .x-q-stat.is-bad { border-color: var(--color-danger, #c0392b); }\n .x-q-stat-value { font-size: 22px; font-weight: 800; color: var(--color-text); }\n .x-q-stat-label { font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); }\n .x-q-generated { font-size: 12px; color: var(--color-text-muted); }\n\n .x-q-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; margin-bottom: 32px; }\n .x-q-card { border: 1px solid var(--color-border); border-radius: 10px; padding: 12px 14px; }\n .x-q-card.is-breached { border-color: var(--color-danger, #c0392b); background: color-mix(in srgb, var(--color-danger, #c0392b) 6%, transparent); }\n .x-q-card-label { font-size: 12.5px; font-weight: 600; color: var(--color-text-muted); }\n .x-q-card-count { font-size: 26px; font-weight: 800; color: var(--color-text); margin: 4px 0 6px; }\n .x-q-card-meta { display: flex; justify-content: space-between; font-size: 11.5px; color: var(--color-text-muted); }\n .x-q-sla.is-breached { color: var(--color-danger, #c0392b); font-weight: 700; }\n\n .x-q-crons-title { font-size: 15px; font-weight: 700; color: var(--color-text); margin: 0 0 10px; }\n .x-q-crons { width: 100%; border-collapse: collapse; font-size: 13px; }\n .x-q-crons th, .x-q-crons td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--color-border); }\n .x-q-crons th { color: var(--color-text-muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.03em; }\n .x-q-cron-name, .x-q-cron-pattern { font-family: var(--font-mono, monospace); font-size: 12px; }\n</style>\n", styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px;max-width:620px}.x-q-summary{display:flex;align-items:center;gap:20px;margin-bottom:20px}.x-q-stat{border:1px solid var(--color-border);border-radius:10px;padding:10px 16px;min-width:110px}.x-q-stat.is-bad{border-color:var(--color-danger, #c0392b)}.x-q-stat-value{font-size:22px;font-weight:800;color:var(--color-text)}.x-q-stat-label{font-size:11.5px;text-transform:uppercase;letter-spacing:.03em;color:var(--color-text-muted)}.x-q-generated{font-size:12px;color:var(--color-text-muted)}.x-q-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px;margin-bottom:32px}.x-q-card{border:1px solid var(--color-border);border-radius:10px;padding:12px 14px}.x-q-card.is-breached{border-color:var(--color-danger, #c0392b);background:color-mix(in srgb,var(--color-danger, #c0392b) 6%,transparent)}.x-q-card-label{font-size:12.5px;font-weight:600;color:var(--color-text-muted)}.x-q-card-count{font-size:26px;font-weight:800;color:var(--color-text);margin:4px 0 6px}.x-q-card-meta{display:flex;justify-content:space-between;font-size:11.5px;color:var(--color-text-muted)}.x-q-sla.is-breached{color:var(--color-danger, #c0392b);font-weight:700}.x-q-crons-title{font-size:15px;font-weight:700;color:var(--color-text);margin:0 0 10px}.x-q-crons{width:100%;border-collapse:collapse;font-size:13px}.x-q-crons th,.x-q-crons td{text-align:left;padding:7px 10px;border-bottom:1px solid var(--color-border)}.x-q-crons th{color:var(--color-text-muted);font-size:11.5px;text-transform:uppercase;letter-spacing:.03em}.x-q-cron-name,.x-q-cron-pattern{font-family:var(--font-mono, monospace);font-size:12px}\n"], dependencies: [{ kind: "component", type: i2.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "pipe", type: i3.DatePipe, name: "date" }] }); }
57
57
  }
58
58
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: QueuesComponent, decorators: [{
59
59
  type: Component,
@@ -63,7 +63,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
63
63
  class QueuesModule {
64
64
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: QueuesModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
65
65
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: QueuesModule, declarations: [QueuesComponent], imports: [SharedModule,
66
- ElementsModule, i1$1.RouterModule] }); }
66
+ ElementsModule, i1.RouterModule] }); }
67
67
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: QueuesModule, imports: [SharedModule,
68
68
  ElementsModule,
69
69
  RouterModule.forChild([{ path: '', component: QueuesComponent }])] }); }
@@ -81,4 +81,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
81
81
  }] });
82
82
 
83
83
  export { QueuesModule };
84
- //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-queues.module-K-zYYVFy.mjs.map
84
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-queues.module-D7iLwX0A.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-queues.module-K-zYYVFy.mjs","sources":["../../../projects/admin/src/lib/pages/queues/queues.component.ts","../../../projects/admin/src/lib/pages/queues/queues.component.html","../../../projects/admin/src/lib/pages/queues/queues.module.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { BaseDirective, using } from '@thescaffold/ngx-core';\nimport { takeUntil } from 'rxjs';\n\ninterface OpsQueue {\n key: string;\n label: string;\n count: number;\n oldestAgeHours: number | null;\n slaHours: number | null;\n breached: boolean;\n}\n\ninterface CronDef {\n name: string;\n pattern: string;\n purpose: string;\n}\n\n// P7.3 — TRD §8.10. The ops queue / SLA board: per-queue backlog, oldest\n// waiting item, and an SLA-breach flag, plus the registered cron\n// catalogue so ops can see the scheduled work that keeps the queues\n// moving. Backed by `GET apps/commerce/reports/ops-queues`\n// (`ScheduledWorkflow.opsQueueSnapshot`).\n@Component({\n standalone: false,\n selector: 'x-queues',\n templateUrl: './queues.component.html',\n})\nexport class QueuesComponent extends BaseDirective {\n private readonly source = this.apiService.on('apps/commerce/reports');\n\n loading = true;\n generatedAt?: string;\n queues: OpsQueue[] = [];\n crons: CronDef[] = [];\n\n override init = () => {\n this.load();\n };\n\n load(): void {\n this.loading = true;\n this.source\n .getOne('ops-queues')\n .pipe(takeUntil(this.$))\n .subscribe(\n using((res: any) => {\n const data = res?.data ?? {};\n this.generatedAt = data.generatedAt;\n this.queues = data.queues ?? [];\n this.crons = data.crons ?? [];\n this.loading = false;\n }),\n );\n }\n\n get totalBacklog(): number {\n return this.queues.reduce((sum, q) => sum + q.count, 0);\n }\n\n get breachCount(): number {\n return this.queues.filter((q) => q.breached).length;\n }\n\n ageLabel(hours: number | null): string {\n if (hours == null) return '—';\n if (hours < 1) return '<1h';\n if (hours < 48) return `${Math.round(hours)}h`;\n return `${Math.round(hours / 24)}d`;\n }\n}\n","<div class=\"x-page-head\">\n <div class=\"x-page-title\">Queues &amp; SLAs</div>\n <div class=\"x-page-subtitle\">\n Live ops backlog with target turnaround, plus the scheduled jobs that\n keep it moving.\n </div>\n</div>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-q-summary\">\n <div class=\"x-q-stat\">\n <div class=\"x-q-stat-value\">{{ totalBacklog }}</div>\n <div class=\"x-q-stat-label\">Items in queue</div>\n </div>\n <div class=\"x-q-stat\" [class.is-bad]=\"breachCount > 0\">\n <div class=\"x-q-stat-value\">{{ breachCount }}</div>\n <div class=\"x-q-stat-label\">SLA breaches</div>\n </div>\n @if (generatedAt) {\n <div class=\"x-q-generated\">as of {{ generatedAt | date: 'short' }}</div>\n }\n </div>\n\n <div class=\"x-q-grid\">\n @for (q of queues; track q.key) {\n <div class=\"x-q-card\" [class.is-breached]=\"q.breached\">\n <div class=\"x-q-card-label\">{{ q.label }}</div>\n <div class=\"x-q-card-count\">{{ q.count }}</div>\n <div class=\"x-q-card-meta\">\n <span>oldest {{ ageLabel(q.oldestAgeHours) }}</span>\n @if (q.slaHours != null) {\n <span class=\"x-q-sla\" [class.is-breached]=\"q.breached\">\n SLA {{ q.slaHours }}h\n </span>\n }\n </div>\n </div>\n }\n </div>\n\n <h2 class=\"x-q-crons-title\">Scheduled jobs</h2>\n <table class=\"x-q-crons\">\n <thead>\n <tr><th>Job</th><th>Schedule</th><th>Purpose</th></tr>\n </thead>\n <tbody>\n @for (c of crons; track c.name) {\n <tr>\n <td class=\"x-q-cron-name\">{{ c.name }}</td>\n <td class=\"x-q-cron-pattern\">{{ c.pattern }}</td>\n <td>{{ c.purpose }}</td>\n </tr>\n }\n </tbody>\n </table>\n}\n\n<style>\n .x-page-head { margin-bottom: 20px; }\n .x-page-title { font-size: 24px; font-weight: 800; color: var(--color-text); }\n .x-page-subtitle { font-size: 13.5px; color: var(--color-text-muted); margin-top: 2px; max-width: 620px; }\n\n .x-q-summary { display: flex; align-items: center; gap: 20px; margin-bottom: 20px; }\n .x-q-stat { border: 1px solid var(--color-border); border-radius: 10px; padding: 10px 16px; min-width: 110px; }\n .x-q-stat.is-bad { border-color: var(--color-danger, #c0392b); }\n .x-q-stat-value { font-size: 22px; font-weight: 800; color: var(--color-text); }\n .x-q-stat-label { font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); }\n .x-q-generated { font-size: 12px; color: var(--color-text-muted); }\n\n .x-q-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; margin-bottom: 32px; }\n .x-q-card { border: 1px solid var(--color-border); border-radius: 10px; padding: 12px 14px; }\n .x-q-card.is-breached { border-color: var(--color-danger, #c0392b); background: color-mix(in srgb, var(--color-danger, #c0392b) 6%, transparent); }\n .x-q-card-label { font-size: 12.5px; font-weight: 600; color: var(--color-text-muted); }\n .x-q-card-count { font-size: 26px; font-weight: 800; color: var(--color-text); margin: 4px 0 6px; }\n .x-q-card-meta { display: flex; justify-content: space-between; font-size: 11.5px; color: var(--color-text-muted); }\n .x-q-sla.is-breached { color: var(--color-danger, #c0392b); font-weight: 700; }\n\n .x-q-crons-title { font-size: 15px; font-weight: 700; color: var(--color-text); margin: 0 0 10px; }\n .x-q-crons { width: 100%; border-collapse: collapse; font-size: 13px; }\n .x-q-crons th, .x-q-crons td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--color-border); }\n .x-q-crons th { color: var(--color-text-muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.03em; }\n .x-q-cron-name, .x-q-cron-pattern { font-family: var(--font-mono, monospace); font-size: 12px; }\n</style>\n","import { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { SharedModule } from '@thescaffold/ngx-core';\nimport { ElementsModule } from '@thescaffold/ngx-ui';\nimport { QueuesComponent } from './queues.component';\n\n@NgModule({\n declarations: [QueuesComponent],\n imports: [\n SharedModule,\n ElementsModule,\n RouterModule.forChild([{ path: '', component: QueuesComponent }]),\n ],\n})\nexport class QueuesModule {}\n"],"names":["i1"],"mappings":";;;;;;;;;;AAmBA;AACA;AACA;AACA;AACA;AAMM,MAAO,eAAgB,SAAQ,aAAa,CAAA;AALlD,IAAA,WAAA,GAAA;;QAMmB,IAAA,CAAA,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,uBAAuB,CAAC;QAErE,IAAA,CAAA,OAAO,GAAG,IAAI;QAEd,IAAA,CAAA,MAAM,GAAe,EAAE;QACvB,IAAA,CAAA,KAAK,GAAc,EAAE;QAEZ,IAAA,CAAA,IAAI,GAAG,MAAK;YACnB,IAAI,CAAC,IAAI,EAAE;AACb,QAAA,CAAC;AAgCF,IAAA;IA9BC,IAAI,GAAA;AACF,QAAA,IAAI,CAAC,OAAO,GAAG,IAAI;AACnB,QAAA,IAAI,CAAC;aACF,MAAM,CAAC,YAAY;AACnB,aAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACtB,aAAA,SAAS,CACR,KAAK,CAAC,CAAC,GAAQ,KAAI;AACjB,YAAA,MAAM,IAAI,GAAG,GAAG,EAAE,IAAI,IAAI,EAAE;AAC5B,YAAA,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW;YACnC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE;YAC/B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE;AAC7B,YAAA,IAAI,CAAC,OAAO,GAAG,KAAK;QACtB,CAAC,CAAC,CACH;IACL;AAEA,IAAA,IAAI,YAAY,GAAA;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACzD;AAEA,IAAA,IAAI,WAAW,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM;IACrD;AAEA,IAAA,QAAQ,CAAC,KAAoB,EAAA;QAC3B,IAAI,KAAK,IAAI,IAAI;AAAE,YAAA,OAAO,GAAG;QAC7B,IAAI,KAAK,GAAG,CAAC;AAAE,YAAA,OAAO,KAAK;QAC3B,IAAI,KAAK,GAAG,EAAE;YAAE,OAAO,CAAA,EAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG;QAC9C,OAAO,CAAA,EAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAA,CAAA,CAAG;IACrC;+GAzCW,eAAe,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAf,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,eAAe,4FC7B5B,s3HAqFA,EAAA,MAAA,EAAA,CAAA,qwDAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAA,EAAA,CAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,EAAA,CAAA,CAAA;;4FDxDa,eAAe,EAAA,UAAA,EAAA,CAAA;kBAL3B,SAAS;AACI,YAAA,IAAA,EAAA,CAAA,EAAA,UAAA,EAAA,KAAK,YACP,UAAU,EAAA,QAAA,EAAA,s3HAAA,EAAA;;;MEZT,YAAY,CAAA;+GAAZ,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,QAAA,EAAA,CAAA,CAAA;gHAAZ,YAAY,EAAA,YAAA,EAAA,CAPR,eAAe,CAAA,EAAA,OAAA,EAAA,CAE5B,YAAY;YACZ,cAAc,EAAAA,IAAA,CAAA,YAAA,CAAA,EAAA,CAAA,CAAA;AAIL,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,mBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,YAAY,YALrB,YAAY;YACZ,cAAc;AACd,YAAA,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC,CAAA,EAAA,CAAA,CAAA;;4FAGxD,YAAY,EAAA,UAAA,EAAA,CAAA;kBARxB,QAAQ;AAAC,YAAA,IAAA,EAAA,CAAA;oBACR,YAAY,EAAE,CAAC,eAAe,CAAC;AAC/B,oBAAA,OAAO,EAAE;wBACP,YAAY;wBACZ,cAAc;AACd,wBAAA,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC;AAClE,qBAAA;AACF,iBAAA;;;;;"}
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-queues.module-D7iLwX0A.mjs","sources":["../../../projects/admin/src/lib/pages/queues/queues.component.ts","../../../projects/admin/src/lib/pages/queues/queues.component.html","../../../projects/admin/src/lib/pages/queues/queues.module.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { BaseDirective, using } from '@thescaffold/ngx-core';\nimport { takeUntil } from 'rxjs';\n\ninterface OpsQueue {\n key: string;\n label: string;\n count: number;\n oldestAgeHours: number | null;\n slaHours: number | null;\n breached: boolean;\n}\n\ninterface CronDef {\n name: string;\n pattern: string;\n purpose: string;\n}\n\n// P7.3 — TRD §8.10. The ops queue / SLA board: per-queue backlog, oldest\n// waiting item, and an SLA-breach flag, plus the registered cron\n// catalogue so ops can see the scheduled work that keeps the queues\n// moving. Backed by `GET apps/commerce/reports/ops-queues`\n// (`ScheduledWorkflow.opsQueueSnapshot`).\n@Component({\n standalone: false,\n selector: 'x-queues',\n templateUrl: './queues.component.html',\n})\nexport class QueuesComponent extends BaseDirective {\n private readonly source = this.apiService.on('apps/commerce/reports');\n\n loading = true;\n generatedAt?: string;\n queues: OpsQueue[] = [];\n crons: CronDef[] = [];\n\n override init = () => {\n this.load();\n };\n\n load(): void {\n this.loading = true;\n this.source\n .getOne('ops-queues')\n .pipe(takeUntil(this.$))\n .subscribe(\n using((res: any) => {\n const data = res?.data ?? {};\n this.generatedAt = data.generatedAt;\n this.queues = data.queues ?? [];\n this.crons = data.crons ?? [];\n this.loading = false;\n }),\n );\n }\n\n get totalBacklog(): number {\n return this.queues.reduce((sum, q) => sum + q.count, 0);\n }\n\n get breachCount(): number {\n return this.queues.filter((q) => q.breached).length;\n }\n\n ageLabel(hours: number | null): string {\n if (hours == null) return '—';\n if (hours < 1) return '<1h';\n if (hours < 48) return `${Math.round(hours)}h`;\n return `${Math.round(hours / 24)}d`;\n }\n}\n","<div class=\"x-page-head\">\n <div class=\"x-page-title\">Queues &amp; SLAs</div>\n <div class=\"x-page-subtitle\">\n Live ops backlog with target turnaround, plus the scheduled jobs that\n keep it moving.\n </div>\n</div>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-q-summary\">\n <div class=\"x-q-stat\">\n <div class=\"x-q-stat-value\">{{ totalBacklog }}</div>\n <div class=\"x-q-stat-label\">Items in queue</div>\n </div>\n <div class=\"x-q-stat\" [class.is-bad]=\"breachCount > 0\">\n <div class=\"x-q-stat-value\">{{ breachCount }}</div>\n <div class=\"x-q-stat-label\">SLA breaches</div>\n </div>\n @if (generatedAt) {\n <div class=\"x-q-generated\">as of {{ generatedAt | date: 'short' }}</div>\n }\n </div>\n\n <div class=\"x-q-grid\">\n @for (q of queues; track q.key) {\n <div class=\"x-q-card\" [class.is-breached]=\"q.breached\">\n <div class=\"x-q-card-label\">{{ q.label }}</div>\n <div class=\"x-q-card-count\">{{ q.count }}</div>\n <div class=\"x-q-card-meta\">\n <span>oldest {{ ageLabel(q.oldestAgeHours) }}</span>\n @if (q.slaHours != null) {\n <span class=\"x-q-sla\" [class.is-breached]=\"q.breached\">\n SLA {{ q.slaHours }}h\n </span>\n }\n </div>\n </div>\n }\n </div>\n\n <h2 class=\"x-q-crons-title\">Scheduled jobs</h2>\n <table class=\"x-q-crons\">\n <thead>\n <tr><th>Job</th><th>Schedule</th><th>Purpose</th></tr>\n </thead>\n <tbody>\n @for (c of crons; track c.name) {\n <tr>\n <td class=\"x-q-cron-name\">{{ c.name }}</td>\n <td class=\"x-q-cron-pattern\">{{ c.pattern }}</td>\n <td>{{ c.purpose }}</td>\n </tr>\n }\n </tbody>\n </table>\n}\n\n<style>\n .x-page-head { margin-bottom: 20px; }\n .x-page-title { font-size: 24px; font-weight: 800; color: var(--color-text); }\n .x-page-subtitle { font-size: 13.5px; color: var(--color-text-muted); margin-top: 2px; max-width: 620px; }\n\n .x-q-summary { display: flex; align-items: center; gap: 20px; margin-bottom: 20px; }\n .x-q-stat { border: 1px solid var(--color-border); border-radius: 10px; padding: 10px 16px; min-width: 110px; }\n .x-q-stat.is-bad { border-color: var(--color-danger, #c0392b); }\n .x-q-stat-value { font-size: 22px; font-weight: 800; color: var(--color-text); }\n .x-q-stat-label { font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); }\n .x-q-generated { font-size: 12px; color: var(--color-text-muted); }\n\n .x-q-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; margin-bottom: 32px; }\n .x-q-card { border: 1px solid var(--color-border); border-radius: 10px; padding: 12px 14px; }\n .x-q-card.is-breached { border-color: var(--color-danger, #c0392b); background: color-mix(in srgb, var(--color-danger, #c0392b) 6%, transparent); }\n .x-q-card-label { font-size: 12.5px; font-weight: 600; color: var(--color-text-muted); }\n .x-q-card-count { font-size: 26px; font-weight: 800; color: var(--color-text); margin: 4px 0 6px; }\n .x-q-card-meta { display: flex; justify-content: space-between; font-size: 11.5px; color: var(--color-text-muted); }\n .x-q-sla.is-breached { color: var(--color-danger, #c0392b); font-weight: 700; }\n\n .x-q-crons-title { font-size: 15px; font-weight: 700; color: var(--color-text); margin: 0 0 10px; }\n .x-q-crons { width: 100%; border-collapse: collapse; font-size: 13px; }\n .x-q-crons th, .x-q-crons td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--color-border); }\n .x-q-crons th { color: var(--color-text-muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.03em; }\n .x-q-cron-name, .x-q-cron-pattern { font-family: var(--font-mono, monospace); font-size: 12px; }\n</style>\n","import { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { SharedModule } from '@thescaffold/ngx-core';\nimport { ElementsModule } from '@thescaffold/ngx-ui';\nimport { QueuesComponent } from './queues.component';\n\n@NgModule({\n declarations: [QueuesComponent],\n imports: [\n SharedModule,\n ElementsModule,\n RouterModule.forChild([{ path: '', component: QueuesComponent }]),\n ],\n})\nexport class QueuesModule {}\n"],"names":["i1","i2"],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Component, NgModule } from '@angular/core';
3
+ import * as i1$1 from '@angular/router';
4
+ import { RouterModule } from '@angular/router';
5
+ import { SharedModule } from '@thescaffold/ngx-core';
6
+ import * as i2 from '@thescaffold/ngx-ui';
7
+ import { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';
8
+ import { S as SimpleCrudDirective } from './isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs';
9
+ import * as i1 from '@angular/forms';
10
+ import * as i3 from '@angular/common';
11
+
12
+ // R1 production-readiness pass — CMS-06. Auto-minted on a slug change;
13
+ // was list-only otherwise, with no way for ops to add one manually to
14
+ // fix a stray 404.
15
+ class RedirectsComponent extends SimpleCrudDirective {
16
+ constructor() {
17
+ super(...arguments);
18
+ this.pageTitle = 'Redirects';
19
+ this.pageSubtitle = 'Stored 301/302 path redirects — auto-minted on slug changes (CMS-06), or add one by hand.';
20
+ this.singularLabel = 'redirect';
21
+ this.pluralName = 'redirects';
22
+ this.dataSource = this.apiService.on('apps/commerce/redirect');
23
+ this.columns = [
24
+ { key: 'fromPath', value: 'From' },
25
+ { key: 'toPath', value: 'To' },
26
+ { key: 'statusCode', value: 'Code' },
27
+ { key: 'hitCount', value: 'Hits' },
28
+ ];
29
+ this.fields = [
30
+ { key: 'fromPath', label: 'From path', type: 'text', required: true, placeholder: '/p/old-slug' },
31
+ { key: 'toPath', label: 'To path', type: 'text', required: true, placeholder: '/p/new-slug' },
32
+ {
33
+ key: 'statusCode',
34
+ label: 'Status code',
35
+ type: 'select',
36
+ numericSelect: true,
37
+ options: [
38
+ { value: '301', label: '301 — Permanent' },
39
+ { value: '302', label: '302 — Temporary' },
40
+ ],
41
+ },
42
+ { key: 'reason', label: 'Reason', type: 'text' },
43
+ { key: 'enabled', label: 'Enabled', type: 'boolean' },
44
+ ];
45
+ }
46
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: RedirectsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
47
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: RedirectsComponent, isStandalone: false, selector: "x-redirects", usesInheritance: true, ngImport: i0, template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">{{ pageTitle }}</div>\n <div class=\"x-page-subtitle\">{{ pageSubtitle }}</div>\n</div>\n\n@if (!editing) {\n <div class=\"x-scrud-toolbar\">\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" (click)=\"startCreate()\">+ New {{ singularLabel }}</button>\n </div>\n\n @if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n } @else {\n <x-element-loader></x-element-loader>\n }\n} @else if (draft) {\n <div class=\"x-scrud-editor\">\n <div class=\"x-scrud-editor-head\">\n <div class=\"x-scrud-editor-title\">{{ draft['id'] ? 'Edit' : 'New' }} {{ singularLabel }}</div>\n <div class=\"x-scrud-editor-actions\">\n @if (draft['id'] && !hideDelete) {\n <button type=\"button\" class=\"x-scrud-btn x-scrud-danger\" (click)=\"remove(draft); cancelEdit()\">Delete</button>\n }\n <button type=\"button\" class=\"x-scrud-btn\" (click)=\"cancelEdit()\">Close</button>\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" [disabled]=\"saving\" (click)=\"save()\">\n {{ saving ? 'Saving\u2026' : 'Save' }}\n </button>\n </div>\n </div>\n\n <div class=\"x-scrud-grid\">\n @for (f of fields; track f.key) {\n <label class=\"x-scrud-f\" [class.x-scrud-f-wide]=\"f.type === 'textarea' || f.type === 'json'\">\n {{ f.label }}\n @if (f.required) {\n <span class=\"x-scrud-required\">*</span>\n }\n @switch (f.type) {\n @case ('boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"draft[f.key]\" />\n }\n @case ('select') {\n <select [(ngModel)]=\"draft[f.key]\">\n <option value=\"\">\u2014 choose \u2014</option>\n @for (opt of (f.optionsKey ? dynamicOptions[f.optionsKey] : f.options); track opt.value) {\n <option [value]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @case ('textarea') {\n <textarea rows=\"3\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\"></textarea>\n }\n @case ('json') {\n <textarea rows=\"4\" class=\"x-scrud-json\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || '{}'\"></textarea>\n }\n @case ('number') {\n <input type=\"number\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n @case ('date') {\n <input type=\"datetime-local\" [(ngModel)]=\"draft[f.key]\" />\n }\n @default {\n <input type=\"text\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n }\n </label>\n }\n </div>\n </div>\n}\n", styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}.x-scrud-toolbar{display:flex;justify-content:flex-end;margin-bottom:14px}.x-scrud-btn{border:1px solid var(--color-border);background:transparent;border-radius:var(--radius-md);padding:6px 14px;font-size:12.5px;font-weight:700;cursor:pointer;color:var(--color-text-muted)}.x-scrud-btn:disabled{opacity:.5;cursor:default}.x-scrud-btn-primary{border-color:var(--color-accent);color:var(--color-accent-hover)}.x-scrud-danger{color:var(--color-danger, #c0362c)}.x-scrud-editor{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;display:flex;flex-direction:column;gap:14px}.x-scrud-editor-head{display:flex;align-items:center;justify-content:space-between}.x-scrud-editor-title{font-size:16px;font-weight:800;color:var(--color-text)}.x-scrud-editor-actions{display:flex;gap:8px}.x-scrud-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.x-scrud-f{display:flex;flex-direction:column;gap:4px;font-size:11.5px;color:var(--color-text-muted);font-weight:700}.x-scrud-f-wide{grid-column:1/-1}.x-scrud-f input,.x-scrud-f select,.x-scrud-f textarea{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:6px 10px;font-size:13px;background:var(--color-surface);color:var(--color-text);font-weight:400}.x-scrud-f input[type=checkbox]{width:18px;height:18px}.x-scrud-json{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.x-scrud-required{color:var(--color-danger, #c0362c)}\n"], dependencies: [{ kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i2.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "component", type: i2.CollectionComponent, selector: "x-webget-collection", inputs: ["data"], outputs: ["search", "filter", "refresh", "select", "page", "show"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }] }); }
48
+ }
49
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: RedirectsComponent, decorators: [{
50
+ type: Component,
51
+ args: [{ standalone: false, selector: 'x-redirects', template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">{{ pageTitle }}</div>\n <div class=\"x-page-subtitle\">{{ pageSubtitle }}</div>\n</div>\n\n@if (!editing) {\n <div class=\"x-scrud-toolbar\">\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" (click)=\"startCreate()\">+ New {{ singularLabel }}</button>\n </div>\n\n @if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n } @else {\n <x-element-loader></x-element-loader>\n }\n} @else if (draft) {\n <div class=\"x-scrud-editor\">\n <div class=\"x-scrud-editor-head\">\n <div class=\"x-scrud-editor-title\">{{ draft['id'] ? 'Edit' : 'New' }} {{ singularLabel }}</div>\n <div class=\"x-scrud-editor-actions\">\n @if (draft['id'] && !hideDelete) {\n <button type=\"button\" class=\"x-scrud-btn x-scrud-danger\" (click)=\"remove(draft); cancelEdit()\">Delete</button>\n }\n <button type=\"button\" class=\"x-scrud-btn\" (click)=\"cancelEdit()\">Close</button>\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" [disabled]=\"saving\" (click)=\"save()\">\n {{ saving ? 'Saving\u2026' : 'Save' }}\n </button>\n </div>\n </div>\n\n <div class=\"x-scrud-grid\">\n @for (f of fields; track f.key) {\n <label class=\"x-scrud-f\" [class.x-scrud-f-wide]=\"f.type === 'textarea' || f.type === 'json'\">\n {{ f.label }}\n @if (f.required) {\n <span class=\"x-scrud-required\">*</span>\n }\n @switch (f.type) {\n @case ('boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"draft[f.key]\" />\n }\n @case ('select') {\n <select [(ngModel)]=\"draft[f.key]\">\n <option value=\"\">\u2014 choose \u2014</option>\n @for (opt of (f.optionsKey ? dynamicOptions[f.optionsKey] : f.options); track opt.value) {\n <option [value]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @case ('textarea') {\n <textarea rows=\"3\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\"></textarea>\n }\n @case ('json') {\n <textarea rows=\"4\" class=\"x-scrud-json\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || '{}'\"></textarea>\n }\n @case ('number') {\n <input type=\"number\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n @case ('date') {\n <input type=\"datetime-local\" [(ngModel)]=\"draft[f.key]\" />\n }\n @default {\n <input type=\"text\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n }\n </label>\n }\n </div>\n </div>\n}\n", styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}.x-scrud-toolbar{display:flex;justify-content:flex-end;margin-bottom:14px}.x-scrud-btn{border:1px solid var(--color-border);background:transparent;border-radius:var(--radius-md);padding:6px 14px;font-size:12.5px;font-weight:700;cursor:pointer;color:var(--color-text-muted)}.x-scrud-btn:disabled{opacity:.5;cursor:default}.x-scrud-btn-primary{border-color:var(--color-accent);color:var(--color-accent-hover)}.x-scrud-danger{color:var(--color-danger, #c0362c)}.x-scrud-editor{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;display:flex;flex-direction:column;gap:14px}.x-scrud-editor-head{display:flex;align-items:center;justify-content:space-between}.x-scrud-editor-title{font-size:16px;font-weight:800;color:var(--color-text)}.x-scrud-editor-actions{display:flex;gap:8px}.x-scrud-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.x-scrud-f{display:flex;flex-direction:column;gap:4px;font-size:11.5px;color:var(--color-text-muted);font-weight:700}.x-scrud-f-wide{grid-column:1/-1}.x-scrud-f input,.x-scrud-f select,.x-scrud-f textarea{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:6px 10px;font-size:13px;background:var(--color-surface);color:var(--color-text);font-weight:400}.x-scrud-f input[type=checkbox]{width:18px;height:18px}.x-scrud-json{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.x-scrud-required{color:var(--color-danger, #c0362c)}\n"] }]
52
+ }] });
53
+
54
+ class RedirectsModule {
55
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: RedirectsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
56
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: RedirectsModule, declarations: [RedirectsComponent], imports: [SharedModule,
57
+ ElementsModule,
58
+ WebgetsModule, i1$1.RouterModule] }); }
59
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: RedirectsModule, imports: [SharedModule,
60
+ ElementsModule,
61
+ WebgetsModule,
62
+ RouterModule.forChild([{ path: '', component: RedirectsComponent }])] }); }
63
+ }
64
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: RedirectsModule, decorators: [{
65
+ type: NgModule,
66
+ args: [{
67
+ declarations: [RedirectsComponent],
68
+ imports: [
69
+ SharedModule,
70
+ ElementsModule,
71
+ WebgetsModule,
72
+ RouterModule.forChild([{ path: '', component: RedirectsComponent }]),
73
+ ],
74
+ }]
75
+ }] });
76
+
77
+ export { RedirectsModule };
78
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-redirects.module-oQw5Xo5T.mjs.map