@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,140 @@
1
+ import { notify, titleCase } from '@thescaffold/ngx-core';
2
+ import { firstValueFrom } from 'rxjs';
3
+ import { L as ListPageDirective } from './isaiahiroko-tilmart-ngx-apps-admin-list-page.base-Cl7JZ-Tp.mjs';
4
+
5
+ // R1 production-readiness pass. Every small config-table resource in the
6
+ // PRD's back-office map (brands, coupons, promotions, redirects, routes,
7
+ // banners, flash sales, channels, shipping zones/methods, return
8
+ // policies, …) was shipped list-only by P7.1 — ops could see the rows
9
+ // but never create or edit one. Rebuilding each as its own bespoke
10
+ // editor (the Catalog page's approach) would be 10+ near-identical
11
+ // files; this shared base + its one template
12
+ // (`simple-crud.component.html`) render a plain field-by-field form
13
+ // driven by a `CrudFieldSpec[]`, so a concrete page is just its resource
14
+ // mount + column/field lists. Pages with real bespoke workflows (order
15
+ // transitions, shipment dispatch, warranty triage) still hand-roll their
16
+ // own — this is only for the "name/code/a few flags" shape.
17
+ class SimpleCrudDirective extends ListPageDirective {
18
+ constructor() {
19
+ super(...arguments);
20
+ // Set true when the resource has no `remove` support worth exposing
21
+ // (e.g. rows other records reference by id).
22
+ this.hideDelete = false;
23
+ this.editing = false;
24
+ this.saving = false;
25
+ this.draft = null;
26
+ // Dynamic option lists referenced by a field's `optionsKey` — set by a
27
+ // subclass's `init` override before calling `super.init` isn't
28
+ // possible (arrow field, not a method), so subclasses instead load
29
+ // these in their own `init` and re-set `this.dynamicOptions` whenever
30
+ // ready; the template re-reads it on every render.
31
+ this.dynamicOptions = {};
32
+ }
33
+ extraCollectionFields() {
34
+ return {
35
+ links: {
36
+ secondary: [{ title: 'Edit', fn: (_link, item) => this.edit(item) }],
37
+ },
38
+ };
39
+ }
40
+ startCreate() {
41
+ this.editing = true;
42
+ this.draft = this.emptyDraft();
43
+ }
44
+ edit(item) {
45
+ this.editing = true;
46
+ const draft = { ...item };
47
+ // The API returns `json`-type fields already parsed (an object/
48
+ // array); the textarea needs the printed form back, or Angular
49
+ // stringifies it as `[object Object]`.
50
+ for (const field of this.fields) {
51
+ if (field.type === 'json' && draft[field.key] != null && typeof draft[field.key] !== 'string') {
52
+ draft[field.key] = JSON.stringify(draft[field.key], null, 2);
53
+ }
54
+ }
55
+ this.draft = draft;
56
+ }
57
+ cancelEdit() {
58
+ this.editing = false;
59
+ this.draft = null;
60
+ }
61
+ emptyDraft() {
62
+ const draft = {};
63
+ for (const field of this.fields) {
64
+ draft[field.key] = field.type === 'boolean' ? false : '';
65
+ }
66
+ return draft;
67
+ }
68
+ async save() {
69
+ if (!this.draft)
70
+ return;
71
+ this.saving = true;
72
+ try {
73
+ const payload = this.buildPayload();
74
+ if (this.draft['id']) {
75
+ await firstValueFrom(this.dataSource.update(this.draft['id'], payload));
76
+ }
77
+ else {
78
+ await firstValueFrom(this.dataSource.create(payload));
79
+ }
80
+ notify(`${titleCase(this.pluralName)} saved.`, undefined, 'success');
81
+ this.editing = false;
82
+ this.draft = null;
83
+ this.list(this.page, this.perPage, this.query);
84
+ }
85
+ catch {
86
+ notify(`Could not save (check required fields).`, undefined, 'error');
87
+ }
88
+ finally {
89
+ this.saving = false;
90
+ }
91
+ }
92
+ async remove(item) {
93
+ try {
94
+ await firstValueFrom(this.dataSource.remove(item.id));
95
+ notify(`${titleCase(this.pluralName)} deleted.`, undefined, 'success');
96
+ this.list(this.page, this.perPage, this.query);
97
+ }
98
+ catch {
99
+ notify(`Could not delete — it may still be referenced elsewhere.`, undefined, 'error');
100
+ }
101
+ }
102
+ buildPayload() {
103
+ if (!this.draft)
104
+ return {};
105
+ const payload = {};
106
+ for (const field of this.fields) {
107
+ const raw = this.draft[field.key];
108
+ switch (field.type) {
109
+ case 'number':
110
+ payload[field.key] = raw !== null && raw !== undefined && `${raw}` !== '' ? Number(raw) : undefined;
111
+ break;
112
+ case 'select':
113
+ if (field.numericSelect) {
114
+ payload[field.key] = raw !== null && raw !== undefined && `${raw}` !== '' ? Number(raw) : undefined;
115
+ }
116
+ else {
117
+ payload[field.key] = raw || undefined;
118
+ }
119
+ break;
120
+ case 'boolean':
121
+ payload[field.key] = !!raw;
122
+ break;
123
+ case 'json':
124
+ try {
125
+ payload[field.key] = raw && `${raw}`.trim() ? JSON.parse(raw) : undefined;
126
+ }
127
+ catch {
128
+ payload[field.key] = undefined;
129
+ }
130
+ break;
131
+ default:
132
+ payload[field.key] = raw || undefined;
133
+ }
134
+ }
135
+ return payload;
136
+ }
137
+ }
138
+
139
+ export { SimpleCrudDirective as S };
140
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs","sources":["../../../projects/admin/src/lib/pages/_shared/simple-crud.base.ts"],"sourcesContent":["import { ApiService, BaseDirective, notify, titleCase, using } from '@thescaffold/ngx-core';\nimport { firstValueFrom, takeUntil } from 'rxjs';\nimport { ListPageDirective } from './list-page.base';\n\nexport interface CrudFieldOption {\n value: string;\n label: string;\n}\n\nexport interface CrudFieldSpec {\n key: string;\n label: string;\n type: 'text' | 'textarea' | 'number' | 'boolean' | 'select' | 'date' | 'json';\n options?: CrudFieldOption[];\n // Static option lists resolve via `options`; a dynamic one (e.g. a\n // dropdown of categories loaded from the API) sets this instead —\n // populated by the subclass before `list()` first renders the form.\n optionsKey?: string;\n // A `select` whose option values are numeric (e.g. a status-code\n // picker) — `buildPayload` sends a Number instead of the raw string.\n numericSelect?: boolean;\n placeholder?: string;\n required?: boolean;\n}\n\n// R1 production-readiness pass. Every small config-table resource in the\n// PRD's back-office map (brands, coupons, promotions, redirects, routes,\n// banners, flash sales, channels, shipping zones/methods, return\n// policies, …) was shipped list-only by P7.1 — ops could see the rows\n// but never create or edit one. Rebuilding each as its own bespoke\n// editor (the Catalog page's approach) would be 10+ near-identical\n// files; this shared base + its one template\n// (`simple-crud.component.html`) render a plain field-by-field form\n// driven by a `CrudFieldSpec[]`, so a concrete page is just its resource\n// mount + column/field lists. Pages with real bespoke workflows (order\n// transitions, shipment dispatch, warranty triage) still hand-roll their\n// own — this is only for the \"name/code/a few flags\" shape.\nexport abstract class SimpleCrudDirective extends ListPageDirective {\n protected abstract override dataSource: ApiService;\n protected abstract override columns: { key: string; value: string; type?: string; class?: string }[];\n protected abstract override pluralName: string;\n protected abstract fields: CrudFieldSpec[];\n protected abstract pageTitle: string;\n protected abstract pageSubtitle: string;\n protected abstract singularLabel: string;\n // Set true when the resource has no `remove` support worth exposing\n // (e.g. rows other records reference by id).\n protected hideDelete = false;\n\n editing = false;\n saving = false;\n draft: Record<string, any> | null = null;\n // Dynamic option lists referenced by a field's `optionsKey` — set by a\n // subclass's `init` override before calling `super.init` isn't\n // possible (arrow field, not a method), so subclasses instead load\n // these in their own `init` and re-set `this.dynamicOptions` whenever\n // ready; the template re-reads it on every render.\n dynamicOptions: Record<string, CrudFieldOption[]> = {};\n\n protected override extraCollectionFields() {\n return {\n links: {\n secondary: [{ title: 'Edit', fn: (_link: any, item: any) => this.edit(item) }],\n },\n };\n }\n\n startCreate(): void {\n this.editing = true;\n this.draft = this.emptyDraft();\n }\n\n edit(item: any): void {\n this.editing = true;\n const draft = { ...item };\n // The API returns `json`-type fields already parsed (an object/\n // array); the textarea needs the printed form back, or Angular\n // stringifies it as `[object Object]`.\n for (const field of this.fields) {\n if (field.type === 'json' && draft[field.key] != null && typeof draft[field.key] !== 'string') {\n draft[field.key] = JSON.stringify(draft[field.key], null, 2);\n }\n }\n this.draft = draft;\n }\n\n cancelEdit(): void {\n this.editing = false;\n this.draft = null;\n }\n\n protected emptyDraft(): Record<string, any> {\n const draft: Record<string, any> = {};\n for (const field of this.fields) {\n draft[field.key] = field.type === 'boolean' ? false : '';\n }\n return draft;\n }\n\n async save(): Promise<void> {\n if (!this.draft) return;\n this.saving = true;\n try {\n const payload = this.buildPayload();\n if (this.draft['id']) {\n await firstValueFrom(this.dataSource.update(this.draft['id'], payload));\n } else {\n await firstValueFrom(this.dataSource.create(payload));\n }\n notify(`${titleCase(this.pluralName)} saved.`, undefined, 'success');\n this.editing = false;\n this.draft = null;\n this.list(this.page, this.perPage, this.query);\n } catch {\n notify(`Could not save (check required fields).`, undefined, 'error');\n } finally {\n this.saving = false;\n }\n }\n\n async remove(item: any): Promise<void> {\n try {\n await firstValueFrom(this.dataSource.remove(item.id));\n notify(`${titleCase(this.pluralName)} deleted.`, undefined, 'success');\n this.list(this.page, this.perPage, this.query);\n } catch {\n notify(`Could not delete — it may still be referenced elsewhere.`, undefined, 'error');\n }\n }\n\n protected buildPayload(): Record<string, any> {\n if (!this.draft) return {};\n const payload: Record<string, any> = {};\n for (const field of this.fields) {\n const raw = this.draft[field.key];\n switch (field.type) {\n case 'number':\n payload[field.key] = raw !== null && raw !== undefined && `${raw}` !== '' ? Number(raw) : undefined;\n break;\n case 'select':\n if (field.numericSelect) {\n payload[field.key] = raw !== null && raw !== undefined && `${raw}` !== '' ? Number(raw) : undefined;\n } else {\n payload[field.key] = raw || undefined;\n }\n break;\n case 'boolean':\n payload[field.key] = !!raw;\n break;\n case 'json':\n try {\n payload[field.key] = raw && `${raw}`.trim() ? JSON.parse(raw) : undefined;\n } catch {\n payload[field.key] = undefined;\n }\n break;\n default:\n payload[field.key] = raw || undefined;\n }\n }\n return payload;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
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 { using, SharedModule } from '@thescaffold/ngx-core';
6
+ import * as i2 from '@thescaffold/ngx-ui';
7
+ import { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';
8
+ import { takeUntil } from 'rxjs';
9
+ import { S as SimpleCrudDirective } from './isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs';
10
+ import * as i1 from '@angular/forms';
11
+ import * as i3 from '@angular/common';
12
+
13
+ // R1 production-readiness pass — CAT-08 (partial). Was a flat read-only
14
+ // table; this makes available/reserved/incoming/threshold/policy
15
+ // editable directly. Reason-coded adjustment history, transfers between
16
+ // locations and serial-number registers are real, separate scope (no
17
+ // `StockAdjustment`-style entity exists yet — that's a new audit-trail
18
+ // feature, not a form to add) — most new variant/location stock rows
19
+ // still get created from the Catalogue product editor, which has proper
20
+ // variant/location pickers; this page is for adjusting an existing row.
21
+ class StockComponent extends SimpleCrudDirective {
22
+ constructor() {
23
+ super(...arguments);
24
+ this.locationSource = this.apiService.on('apps/commerce/stock-location');
25
+ this.pageTitle = 'Stock';
26
+ this.pageSubtitle = 'Per-variant, per-location levels — available, reserved and incoming.';
27
+ this.singularLabel = 'stock level';
28
+ this.pluralName = 'stock';
29
+ this.dataSource = this.apiService.on('apps/commerce/stock-level');
30
+ this.columns = [
31
+ { key: 'variantId', value: 'Variant' },
32
+ { key: 'locationId', value: 'Location' },
33
+ { key: 'available', value: 'Available' },
34
+ { key: 'reserved', value: 'Reserved' },
35
+ { key: 'incoming', value: 'Incoming' },
36
+ ];
37
+ this.fields = [
38
+ { key: 'variantId', label: 'Variant id', type: 'text', required: true, placeholder: 'paste from the Catalogue editor' },
39
+ { key: 'locationId', label: 'Location', type: 'select', optionsKey: 'locations', required: true },
40
+ { key: 'available', label: 'Available', type: 'number' },
41
+ { key: 'reserved', label: 'Reserved', type: 'number' },
42
+ { key: 'incoming', label: 'Incoming', type: 'number' },
43
+ { key: 'lowStockThreshold', label: 'Low-stock threshold', type: 'number' },
44
+ { key: 'leadTimeDays', label: 'Lead time (days)', type: 'number' },
45
+ {
46
+ key: 'outOfStockPolicy',
47
+ label: 'Out-of-stock policy',
48
+ type: 'select',
49
+ options: [
50
+ { value: 'hide', label: 'Hide' },
51
+ { value: 'show', label: 'Show (sold out)' },
52
+ { value: 'backorder', label: 'Backorder' },
53
+ { value: 'preorder', label: 'Preorder' },
54
+ ],
55
+ },
56
+ ];
57
+ this.init = () => {
58
+ this.list();
59
+ this.locationSource
60
+ .getMany({ page: 1, perPage: 100 })
61
+ .pipe(takeUntil(this.$))
62
+ .subscribe(using((response) => {
63
+ this.dynamicOptions['locations'] = (response?.data ?? []).map((l) => ({
64
+ value: l.id,
65
+ label: l.name,
66
+ }));
67
+ }));
68
+ };
69
+ }
70
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StockComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
71
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StockComponent, isStandalone: false, selector: "x-stock", 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" }] }); }
72
+ }
73
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StockComponent, decorators: [{
74
+ type: Component,
75
+ args: [{ standalone: false, selector: 'x-stock', 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"] }]
76
+ }] });
77
+
78
+ class StockModule {
79
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StockModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
80
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: StockModule, declarations: [StockComponent], imports: [SharedModule,
81
+ ElementsModule,
82
+ WebgetsModule, i1$1.RouterModule] }); }
83
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StockModule, imports: [SharedModule,
84
+ ElementsModule,
85
+ WebgetsModule,
86
+ RouterModule.forChild([{ path: '', component: StockComponent }])] }); }
87
+ }
88
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StockModule, decorators: [{
89
+ type: NgModule,
90
+ args: [{
91
+ declarations: [StockComponent],
92
+ imports: [
93
+ SharedModule,
94
+ ElementsModule,
95
+ WebgetsModule,
96
+ RouterModule.forChild([{ path: '', component: StockComponent }]),
97
+ ],
98
+ }]
99
+ }] });
100
+
101
+ export { StockModule };
102
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-stock.module-Bt9W1oGR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-stock.module-Bt9W1oGR.mjs","sources":["../../../projects/admin/src/lib/pages/stock/stock.component.ts","../../../projects/admin/src/lib/pages/_shared/simple-crud.component.html","../../../projects/admin/src/lib/pages/stock/stock.module.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { using } from '@thescaffold/ngx-core';\nimport { takeUntil } from 'rxjs';\nimport { CrudFieldSpec, SimpleCrudDirective } from '../_shared/simple-crud.base';\n\n// R1 production-readiness pass — CAT-08 (partial). Was a flat read-only\n// table; this makes available/reserved/incoming/threshold/policy\n// editable directly. Reason-coded adjustment history, transfers between\n// locations and serial-number registers are real, separate scope (no\n// `StockAdjustment`-style entity exists yet — that's a new audit-trail\n// feature, not a form to add) — most new variant/location stock rows\n// still get created from the Catalogue product editor, which has proper\n// variant/location pickers; this page is for adjusting an existing row.\n@Component({\n standalone: false,\n selector: 'x-stock',\n templateUrl: '../_shared/simple-crud.component.html',\n styleUrl: '../_shared/simple-crud.component.scss',\n})\nexport class StockComponent extends SimpleCrudDirective {\n private readonly locationSource = this.apiService.on('apps/commerce/stock-location');\n\n protected pageTitle = 'Stock';\n protected pageSubtitle = 'Per-variant, per-location levels — available, reserved and incoming.';\n protected singularLabel = 'stock level';\n protected pluralName = 'stock';\n protected dataSource = this.apiService.on('apps/commerce/stock-level');\n\n protected columns = [\n { key: 'variantId', value: 'Variant' },\n { key: 'locationId', value: 'Location' },\n { key: 'available', value: 'Available' },\n { key: 'reserved', value: 'Reserved' },\n { key: 'incoming', value: 'Incoming' },\n ];\n\n protected fields: CrudFieldSpec[] = [\n { key: 'variantId', label: 'Variant id', type: 'text', required: true, placeholder: 'paste from the Catalogue editor' },\n { key: 'locationId', label: 'Location', type: 'select', optionsKey: 'locations', required: true },\n { key: 'available', label: 'Available', type: 'number' },\n { key: 'reserved', label: 'Reserved', type: 'number' },\n { key: 'incoming', label: 'Incoming', type: 'number' },\n { key: 'lowStockThreshold', label: 'Low-stock threshold', type: 'number' },\n { key: 'leadTimeDays', label: 'Lead time (days)', type: 'number' },\n {\n key: 'outOfStockPolicy',\n label: 'Out-of-stock policy',\n type: 'select',\n options: [\n { value: 'hide', label: 'Hide' },\n { value: 'show', label: 'Show (sold out)' },\n { value: 'backorder', label: 'Backorder' },\n { value: 'preorder', label: 'Preorder' },\n ],\n },\n ];\n\n override init = () => {\n this.list();\n this.locationSource\n .getMany({ page: 1, perPage: 100 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((response: any) => {\n this.dynamicOptions['locations'] = (response?.data ?? []).map((l: any) => ({\n value: l.id,\n label: l.name,\n }));\n }),\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 { StockComponent } from './stock.component';\n\n@NgModule({\n declarations: [StockComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: StockComponent }]),\n ],\n})\nexport class StockModule {}\n"],"names":["i1"],"mappings":"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"}
@@ -1,13 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, Component, NgModule } from '@angular/core';
3
- import * as i1$2 from '@angular/router';
3
+ import * as i1$1 from '@angular/router';
4
4
  import { ActivatedRoute, RouterModule } from '@angular/router';
5
5
  import { BaseDirective, using, SharedModule } from '@thescaffold/ngx-core';
6
- import * as i1$1 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
9
  import * as i1 from '@angular/forms';
10
- import * as i2 from '@angular/common';
10
+ import * as i3 from '@angular/common';
11
11
 
12
12
  // P7.14 (N.4) — trace one order end-to-end without leaving the back
13
13
  // office. Reads `GET apps/commerce/reports/trace?ref=<...>` (order
@@ -61,7 +61,7 @@ class TraceComponent extends BaseDirective {
61
61
  .join(' · ');
62
62
  }
63
63
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: TraceComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
64
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: TraceComponent, isStandalone: false, selector: "x-trace", usesInheritance: true, ngImport: i0, template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">Trace</div>\n <div class=\"x-page-subtitle\">\n Follow one order end-to-end \u2014 status, payments, fulfilment, returns,\n notifications and audit \u2014 from here alone (N.4).\n </div>\n</div>\n\n<form class=\"x-tr-search\" (ngSubmit)=\"run()\">\n <input\n type=\"search\"\n placeholder=\"Order number, order id, or payment reference\"\n [(ngModel)]=\"ref\"\n name=\"ref\"\n />\n <button type=\"submit\" [disabled]=\"loading || !ref.trim()\">Trace</button>\n</form>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else if (notFound) {\n <p class=\"x-tr-empty\">Nothing found for that reference.</p>\n} @else if (data) {\n <div class=\"x-tr-summary\">\n <div><strong>{{ data.order.orderNumber }}</strong></div>\n <div class=\"x-tr-muted\">\n {{ data.order.status }} \u00B7\n \u20A6{{ data.order.grandTotal | number: '1.0-0' }} {{ data.order.currency }} \u00B7\n placed {{ data.order.createdAt | date: 'medium' }}\n </div>\n <div class=\"x-tr-counts\">\n <span>{{ data.payments.length }} payment(s)</span>\n <span>{{ data.shipments.length }} shipment(s)</span>\n <span>{{ data.returns.length }} return(s)</span>\n <span>{{ data.notifications.length }} notification(s)</span>\n <span>{{ data.auditEntries.length }} audit entr(ies)</span>\n </div>\n </div>\n\n <ol class=\"x-tr-timeline\">\n @for (e of data.timeline; track $index) {\n <li class=\"x-tr-event x-tr-kind-{{ e.kind }}\">\n <div class=\"x-tr-when\">{{ e.at ? (e.at | date: 'short') : '\u2014' }}</div>\n <div class=\"x-tr-body\">\n <span class=\"x-tr-tag\">{{ e.kind }}</span>\n <span class=\"x-tr-label\">{{ e.label }}</span>\n @if (detailText(e.detail)) {\n <div class=\"x-tr-detail\">{{ detailText(e.detail) }}</div>\n }\n </div>\n </li>\n }\n </ol>\n}\n\n<style>\n .x-page-head { margin-bottom: 16px; }\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 .x-tr-search { display: flex; gap: 8px; margin-bottom: 20px; }\n .x-tr-search input { flex: 1; max-width: 420px; font: inherit; padding: 8px 12px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); }\n .x-tr-search button { font: inherit; font-weight: 600; padding: 8px 16px; border: 0; border-radius: 6px; background: var(--color-brand); color: #fff; cursor: pointer; }\n .x-tr-search button:disabled { opacity: 0.5; }\n .x-tr-empty { color: var(--color-text-muted); font-size: 13px; }\n .x-tr-summary { border: 1px solid var(--color-border); border-radius: 10px; padding: 14px; margin-bottom: 18px; }\n .x-tr-muted { color: var(--color-text-muted); font-size: 13px; margin-top: 3px; }\n .x-tr-counts { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; font-size: 12px; color: var(--color-text-muted); }\n .x-tr-timeline { list-style: none; margin: 0; padding: 0; border-left: 2px solid var(--color-border); }\n .x-tr-event { display: flex; gap: 14px; padding: 8px 0 8px 16px; position: relative; }\n .x-tr-event::before { content: ''; position: absolute; left: -7px; top: 13px; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-muted); }\n .x-tr-kind-payment::before, .x-tr-kind-payment-attempt::before { background: var(--color-brand); }\n .x-tr-kind-delivery-exception::before, .x-tr-kind-return::before { background: var(--color-danger, #c0392b); }\n .x-tr-when { font-size: 11.5px; color: var(--color-text-muted); min-width: 96px; padding-top: 2px; }\n .x-tr-tag { font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; color: var(--color-text-muted); margin-right: 8px; }\n .x-tr-label { font-size: 13.5px; color: var(--color-text); }\n .x-tr-detail { font-size: 12px; color: var(--color-text-muted); margin-top: 2px; }\n</style>\n", styles: [".x-page-head{margin-bottom:16px}.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-tr-search{display:flex;gap:8px;margin-bottom:20px}.x-tr-search input{flex:1;max-width:420px;font:inherit;padding:8px 12px;border:1px solid var(--color-border);border-radius:6px;background:var(--color-surface, #fff);color:var(--color-text)}.x-tr-search button{font:inherit;font-weight:600;padding:8px 16px;border:0;border-radius:6px;background:var(--color-brand);color:#fff;cursor:pointer}.x-tr-search button:disabled{opacity:.5}.x-tr-empty{color:var(--color-text-muted);font-size:13px}.x-tr-summary{border:1px solid var(--color-border);border-radius:10px;padding:14px;margin-bottom:18px}.x-tr-muted{color:var(--color-text-muted);font-size:13px;margin-top:3px}.x-tr-counts{display:flex;flex-wrap:wrap;gap:12px;margin-top:10px;font-size:12px;color:var(--color-text-muted)}.x-tr-timeline{list-style:none;margin:0;padding:0;border-left:2px solid var(--color-border)}.x-tr-event{display:flex;gap:14px;padding:8px 0 8px 16px;position:relative}.x-tr-event:before{content:\"\";position:absolute;left:-7px;top:13px;width:10px;height:10px;border-radius:50%;background:var(--color-text-muted)}.x-tr-kind-payment:before,.x-tr-kind-payment-attempt:before{background:var(--color-brand)}.x-tr-kind-delivery-exception:before,.x-tr-kind-return:before{background:var(--color-danger, #c0392b)}.x-tr-when{font-size:11.5px;color:var(--color-text-muted);min-width:96px;padding-top:2px}.x-tr-tag{font-size:10px;text-transform:uppercase;letter-spacing:.04em;font-weight:700;color:var(--color-text-muted);margin-right:8px}.x-tr-label{font-size:13.5px;color:var(--color-text)}.x-tr-detail{font-size:12px;color:var(--color-text-muted);margin-top:2px}\n"], dependencies: [{ kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i1$1.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "pipe", type: i2.DecimalPipe, name: "number" }, { kind: "pipe", type: i2.DatePipe, name: "date" }] }); }
64
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: TraceComponent, isStandalone: false, selector: "x-trace", usesInheritance: true, ngImport: i0, template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">Trace</div>\n <div class=\"x-page-subtitle\">\n Follow one order end-to-end \u2014 status, payments, fulfilment, returns,\n notifications and audit \u2014 from here alone (N.4).\n </div>\n</div>\n\n<form class=\"x-tr-search\" (ngSubmit)=\"run()\">\n <input\n type=\"search\"\n placeholder=\"Order number, order id, or payment reference\"\n [(ngModel)]=\"ref\"\n name=\"ref\"\n />\n <button type=\"submit\" [disabled]=\"loading || !ref.trim()\">Trace</button>\n</form>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else if (notFound) {\n <p class=\"x-tr-empty\">Nothing found for that reference.</p>\n} @else if (data) {\n <div class=\"x-tr-summary\">\n <div><strong>{{ data.order.orderNumber }}</strong></div>\n <div class=\"x-tr-muted\">\n {{ data.order.status }} \u00B7\n \u20A6{{ data.order.grandTotal | number: '1.0-0' }} {{ data.order.currency }} \u00B7\n placed {{ data.order.createdAt | date: 'medium' }}\n </div>\n <div class=\"x-tr-counts\">\n <span>{{ data.payments.length }} payment(s)</span>\n <span>{{ data.shipments.length }} shipment(s)</span>\n <span>{{ data.returns.length }} return(s)</span>\n <span>{{ data.notifications.length }} notification(s)</span>\n <span>{{ data.auditEntries.length }} audit entr(ies)</span>\n </div>\n </div>\n\n <ol class=\"x-tr-timeline\">\n @for (e of data.timeline; track $index) {\n <li class=\"x-tr-event x-tr-kind-{{ e.kind }}\">\n <div class=\"x-tr-when\">{{ e.at ? (e.at | date: 'short') : '\u2014' }}</div>\n <div class=\"x-tr-body\">\n <span class=\"x-tr-tag\">{{ e.kind }}</span>\n <span class=\"x-tr-label\">{{ e.label }}</span>\n @if (detailText(e.detail)) {\n <div class=\"x-tr-detail\">{{ detailText(e.detail) }}</div>\n }\n </div>\n </li>\n }\n </ol>\n}\n\n<style>\n .x-page-head { margin-bottom: 16px; }\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 .x-tr-search { display: flex; gap: 8px; margin-bottom: 20px; }\n .x-tr-search input { flex: 1; max-width: 420px; font: inherit; padding: 8px 12px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); }\n .x-tr-search button { font: inherit; font-weight: 600; padding: 8px 16px; border: 0; border-radius: 6px; background: var(--color-brand); color: #fff; cursor: pointer; }\n .x-tr-search button:disabled { opacity: 0.5; }\n .x-tr-empty { color: var(--color-text-muted); font-size: 13px; }\n .x-tr-summary { border: 1px solid var(--color-border); border-radius: 10px; padding: 14px; margin-bottom: 18px; }\n .x-tr-muted { color: var(--color-text-muted); font-size: 13px; margin-top: 3px; }\n .x-tr-counts { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; font-size: 12px; color: var(--color-text-muted); }\n .x-tr-timeline { list-style: none; margin: 0; padding: 0; border-left: 2px solid var(--color-border); }\n .x-tr-event { display: flex; gap: 14px; padding: 8px 0 8px 16px; position: relative; }\n .x-tr-event::before { content: ''; position: absolute; left: -7px; top: 13px; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-muted); }\n .x-tr-kind-payment::before, .x-tr-kind-payment-attempt::before { background: var(--color-brand); }\n .x-tr-kind-delivery-exception::before, .x-tr-kind-return::before { background: var(--color-danger, #c0392b); }\n .x-tr-when { font-size: 11.5px; color: var(--color-text-muted); min-width: 96px; padding-top: 2px; }\n .x-tr-tag { font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; color: var(--color-text-muted); margin-right: 8px; }\n .x-tr-label { font-size: 13.5px; color: var(--color-text); }\n .x-tr-detail { font-size: 12px; color: var(--color-text-muted); margin-top: 2px; }\n</style>\n", styles: [".x-page-head{margin-bottom:16px}.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-tr-search{display:flex;gap:8px;margin-bottom:20px}.x-tr-search input{flex:1;max-width:420px;font:inherit;padding:8px 12px;border:1px solid var(--color-border);border-radius:6px;background:var(--color-surface, #fff);color:var(--color-text)}.x-tr-search button{font:inherit;font-weight:600;padding:8px 16px;border:0;border-radius:6px;background:var(--color-brand);color:#fff;cursor:pointer}.x-tr-search button:disabled{opacity:.5}.x-tr-empty{color:var(--color-text-muted);font-size:13px}.x-tr-summary{border:1px solid var(--color-border);border-radius:10px;padding:14px;margin-bottom:18px}.x-tr-muted{color:var(--color-text-muted);font-size:13px;margin-top:3px}.x-tr-counts{display:flex;flex-wrap:wrap;gap:12px;margin-top:10px;font-size:12px;color:var(--color-text-muted)}.x-tr-timeline{list-style:none;margin:0;padding:0;border-left:2px solid var(--color-border)}.x-tr-event{display:flex;gap:14px;padding:8px 0 8px 16px;position:relative}.x-tr-event:before{content:\"\";position:absolute;left:-7px;top:13px;width:10px;height:10px;border-radius:50%;background:var(--color-text-muted)}.x-tr-kind-payment:before,.x-tr-kind-payment-attempt:before{background:var(--color-brand)}.x-tr-kind-delivery-exception:before,.x-tr-kind-return:before{background:var(--color-danger, #c0392b)}.x-tr-when{font-size:11.5px;color:var(--color-text-muted);min-width:96px;padding-top:2px}.x-tr-tag{font-size:10px;text-transform:uppercase;letter-spacing:.04em;font-weight:700;color:var(--color-text-muted);margin-right:8px}.x-tr-label{font-size:13.5px;color:var(--color-text)}.x-tr-detail{font-size:12px;color:var(--color-text-muted);margin-top:2px}\n"], dependencies: [{ kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i2.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "pipe", type: i3.DecimalPipe, name: "number" }, { kind: "pipe", type: i3.DatePipe, name: "date" }] }); }
65
65
  }
66
66
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: TraceComponent, decorators: [{
67
67
  type: Component,
@@ -71,7 +71,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
71
71
  class TraceModule {
72
72
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: TraceModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
73
73
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: TraceModule, declarations: [TraceComponent], imports: [SharedModule,
74
- ElementsModule, i1$2.RouterModule] }); }
74
+ ElementsModule, i1$1.RouterModule] }); }
75
75
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: TraceModule, imports: [SharedModule,
76
76
  ElementsModule,
77
77
  RouterModule.forChild([{ path: '', component: TraceComponent }])] }); }
@@ -89,4 +89,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
89
89
  }] });
90
90
 
91
91
  export { TraceModule };
92
- //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-trace.module-Pwr-82Rc.mjs.map
92
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-trace.module-9sRlGZCC.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-trace.module-Pwr-82Rc.mjs","sources":["../../../projects/admin/src/lib/pages/trace/trace.component.ts","../../../projects/admin/src/lib/pages/trace/trace.component.html","../../../projects/admin/src/lib/pages/trace/trace.module.ts"],"sourcesContent":["import { Component, inject } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { BaseDirective, using } from '@thescaffold/ngx-core';\nimport { takeUntil } from 'rxjs';\n\ninterface TraceEvent {\n at: string | null;\n kind: string;\n label: string;\n detail?: Record<string, unknown>;\n}\n\n// P7.14 (N.4) — trace one order end-to-end without leaving the back\n// office. Reads `GET apps/commerce/reports/trace?ref=<...>` (order\n// number, order id, or a payment provider ref) and renders the merged\n// timeline: status history, payments + attempts, shipments + exceptions,\n// returns, the notifications sent for it, and its audit-log entries.\n@Component({\n standalone: false,\n selector: 'x-trace',\n templateUrl: './trace.component.html',\n})\nexport class TraceComponent extends BaseDirective {\n private readonly source = this.apiService.on('apps/commerce/reports');\n private readonly route = inject(ActivatedRoute);\n\n ref = '';\n loading = false;\n notFound = false;\n data: any = null;\n\n override init = () => {\n const q = this.route.snapshot.queryParamMap.get('ref');\n if (q) {\n this.ref = q;\n this.run();\n }\n };\n\n run(): void {\n const ref = this.ref.trim();\n if (!ref) return;\n this.loading = true;\n this.notFound = false;\n this.data = null;\n this.source\n .getOne(`trace?ref=${encodeURIComponent(ref)}`)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((res: any) => {\n const payload = res?.data ?? {};\n if (payload.ok === false || !payload.data) {\n this.notFound = true;\n } else {\n this.data = payload.data ?? payload;\n }\n this.loading = false;\n }),\n );\n }\n\n detailText(d?: Record<string, unknown>): string {\n if (!d) return '';\n return Object.entries(d)\n .filter(([, v]) => v != null && v !== '')\n .map(([k, v]) => `${k}: ${v}`)\n .join(' · ');\n }\n}\n","<div class=\"x-page-head\">\n <div class=\"x-page-title\">Trace</div>\n <div class=\"x-page-subtitle\">\n Follow one order end-to-end — status, payments, fulfilment, returns,\n notifications and audit — from here alone (N.4).\n </div>\n</div>\n\n<form class=\"x-tr-search\" (ngSubmit)=\"run()\">\n <input\n type=\"search\"\n placeholder=\"Order number, order id, or payment reference\"\n [(ngModel)]=\"ref\"\n name=\"ref\"\n />\n <button type=\"submit\" [disabled]=\"loading || !ref.trim()\">Trace</button>\n</form>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else if (notFound) {\n <p class=\"x-tr-empty\">Nothing found for that reference.</p>\n} @else if (data) {\n <div class=\"x-tr-summary\">\n <div><strong>{{ data.order.orderNumber }}</strong></div>\n <div class=\"x-tr-muted\">\n {{ data.order.status }} ·\n ₦{{ data.order.grandTotal | number: '1.0-0' }} {{ data.order.currency }} ·\n placed {{ data.order.createdAt | date: 'medium' }}\n </div>\n <div class=\"x-tr-counts\">\n <span>{{ data.payments.length }} payment(s)</span>\n <span>{{ data.shipments.length }} shipment(s)</span>\n <span>{{ data.returns.length }} return(s)</span>\n <span>{{ data.notifications.length }} notification(s)</span>\n <span>{{ data.auditEntries.length }} audit entr(ies)</span>\n </div>\n </div>\n\n <ol class=\"x-tr-timeline\">\n @for (e of data.timeline; track $index) {\n <li class=\"x-tr-event x-tr-kind-{{ e.kind }}\">\n <div class=\"x-tr-when\">{{ e.at ? (e.at | date: 'short') : '—' }}</div>\n <div class=\"x-tr-body\">\n <span class=\"x-tr-tag\">{{ e.kind }}</span>\n <span class=\"x-tr-label\">{{ e.label }}</span>\n @if (detailText(e.detail)) {\n <div class=\"x-tr-detail\">{{ detailText(e.detail) }}</div>\n }\n </div>\n </li>\n }\n </ol>\n}\n\n<style>\n .x-page-head { margin-bottom: 16px; }\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 .x-tr-search { display: flex; gap: 8px; margin-bottom: 20px; }\n .x-tr-search input { flex: 1; max-width: 420px; font: inherit; padding: 8px 12px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); }\n .x-tr-search button { font: inherit; font-weight: 600; padding: 8px 16px; border: 0; border-radius: 6px; background: var(--color-brand); color: #fff; cursor: pointer; }\n .x-tr-search button:disabled { opacity: 0.5; }\n .x-tr-empty { color: var(--color-text-muted); font-size: 13px; }\n .x-tr-summary { border: 1px solid var(--color-border); border-radius: 10px; padding: 14px; margin-bottom: 18px; }\n .x-tr-muted { color: var(--color-text-muted); font-size: 13px; margin-top: 3px; }\n .x-tr-counts { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; font-size: 12px; color: var(--color-text-muted); }\n .x-tr-timeline { list-style: none; margin: 0; padding: 0; border-left: 2px solid var(--color-border); }\n .x-tr-event { display: flex; gap: 14px; padding: 8px 0 8px 16px; position: relative; }\n .x-tr-event::before { content: ''; position: absolute; left: -7px; top: 13px; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-muted); }\n .x-tr-kind-payment::before, .x-tr-kind-payment-attempt::before { background: var(--color-brand); }\n .x-tr-kind-delivery-exception::before, .x-tr-kind-return::before { background: var(--color-danger, #c0392b); }\n .x-tr-when { font-size: 11.5px; color: var(--color-text-muted); min-width: 96px; padding-top: 2px; }\n .x-tr-tag { font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; color: var(--color-text-muted); margin-right: 8px; }\n .x-tr-label { font-size: 13.5px; color: var(--color-text); }\n .x-tr-detail { font-size: 12px; color: var(--color-text-muted); margin-top: 2px; }\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 { TraceComponent } from './trace.component';\n\n@NgModule({\n declarations: [TraceComponent],\n imports: [\n SharedModule,\n ElementsModule,\n RouterModule.forChild([{ path: '', component: TraceComponent }]),\n ],\n})\nexport class TraceModule {}\n"],"names":["i2","i3","i1"],"mappings":"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"}
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-trace.module-9sRlGZCC.mjs","sources":["../../../projects/admin/src/lib/pages/trace/trace.component.ts","../../../projects/admin/src/lib/pages/trace/trace.component.html","../../../projects/admin/src/lib/pages/trace/trace.module.ts"],"sourcesContent":["import { Component, inject } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { BaseDirective, using } from '@thescaffold/ngx-core';\nimport { takeUntil } from 'rxjs';\n\ninterface TraceEvent {\n at: string | null;\n kind: string;\n label: string;\n detail?: Record<string, unknown>;\n}\n\n// P7.14 (N.4) — trace one order end-to-end without leaving the back\n// office. Reads `GET apps/commerce/reports/trace?ref=<...>` (order\n// number, order id, or a payment provider ref) and renders the merged\n// timeline: status history, payments + attempts, shipments + exceptions,\n// returns, the notifications sent for it, and its audit-log entries.\n@Component({\n standalone: false,\n selector: 'x-trace',\n templateUrl: './trace.component.html',\n})\nexport class TraceComponent extends BaseDirective {\n private readonly source = this.apiService.on('apps/commerce/reports');\n private readonly route = inject(ActivatedRoute);\n\n ref = '';\n loading = false;\n notFound = false;\n data: any = null;\n\n override init = () => {\n const q = this.route.snapshot.queryParamMap.get('ref');\n if (q) {\n this.ref = q;\n this.run();\n }\n };\n\n run(): void {\n const ref = this.ref.trim();\n if (!ref) return;\n this.loading = true;\n this.notFound = false;\n this.data = null;\n this.source\n .getOne(`trace?ref=${encodeURIComponent(ref)}`)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((res: any) => {\n const payload = res?.data ?? {};\n if (payload.ok === false || !payload.data) {\n this.notFound = true;\n } else {\n this.data = payload.data ?? payload;\n }\n this.loading = false;\n }),\n );\n }\n\n detailText(d?: Record<string, unknown>): string {\n if (!d) return '';\n return Object.entries(d)\n .filter(([, v]) => v != null && v !== '')\n .map(([k, v]) => `${k}: ${v}`)\n .join(' · ');\n }\n}\n","<div class=\"x-page-head\">\n <div class=\"x-page-title\">Trace</div>\n <div class=\"x-page-subtitle\">\n Follow one order end-to-end — status, payments, fulfilment, returns,\n notifications and audit — from here alone (N.4).\n </div>\n</div>\n\n<form class=\"x-tr-search\" (ngSubmit)=\"run()\">\n <input\n type=\"search\"\n placeholder=\"Order number, order id, or payment reference\"\n [(ngModel)]=\"ref\"\n name=\"ref\"\n />\n <button type=\"submit\" [disabled]=\"loading || !ref.trim()\">Trace</button>\n</form>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else if (notFound) {\n <p class=\"x-tr-empty\">Nothing found for that reference.</p>\n} @else if (data) {\n <div class=\"x-tr-summary\">\n <div><strong>{{ data.order.orderNumber }}</strong></div>\n <div class=\"x-tr-muted\">\n {{ data.order.status }} ·\n ₦{{ data.order.grandTotal | number: '1.0-0' }} {{ data.order.currency }} ·\n placed {{ data.order.createdAt | date: 'medium' }}\n </div>\n <div class=\"x-tr-counts\">\n <span>{{ data.payments.length }} payment(s)</span>\n <span>{{ data.shipments.length }} shipment(s)</span>\n <span>{{ data.returns.length }} return(s)</span>\n <span>{{ data.notifications.length }} notification(s)</span>\n <span>{{ data.auditEntries.length }} audit entr(ies)</span>\n </div>\n </div>\n\n <ol class=\"x-tr-timeline\">\n @for (e of data.timeline; track $index) {\n <li class=\"x-tr-event x-tr-kind-{{ e.kind }}\">\n <div class=\"x-tr-when\">{{ e.at ? (e.at | date: 'short') : '—' }}</div>\n <div class=\"x-tr-body\">\n <span class=\"x-tr-tag\">{{ e.kind }}</span>\n <span class=\"x-tr-label\">{{ e.label }}</span>\n @if (detailText(e.detail)) {\n <div class=\"x-tr-detail\">{{ detailText(e.detail) }}</div>\n }\n </div>\n </li>\n }\n </ol>\n}\n\n<style>\n .x-page-head { margin-bottom: 16px; }\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 .x-tr-search { display: flex; gap: 8px; margin-bottom: 20px; }\n .x-tr-search input { flex: 1; max-width: 420px; font: inherit; padding: 8px 12px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); }\n .x-tr-search button { font: inherit; font-weight: 600; padding: 8px 16px; border: 0; border-radius: 6px; background: var(--color-brand); color: #fff; cursor: pointer; }\n .x-tr-search button:disabled { opacity: 0.5; }\n .x-tr-empty { color: var(--color-text-muted); font-size: 13px; }\n .x-tr-summary { border: 1px solid var(--color-border); border-radius: 10px; padding: 14px; margin-bottom: 18px; }\n .x-tr-muted { color: var(--color-text-muted); font-size: 13px; margin-top: 3px; }\n .x-tr-counts { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; font-size: 12px; color: var(--color-text-muted); }\n .x-tr-timeline { list-style: none; margin: 0; padding: 0; border-left: 2px solid var(--color-border); }\n .x-tr-event { display: flex; gap: 14px; padding: 8px 0 8px 16px; position: relative; }\n .x-tr-event::before { content: ''; position: absolute; left: -7px; top: 13px; width: 10px; height: 10px; border-radius: 50%; background: var(--color-text-muted); }\n .x-tr-kind-payment::before, .x-tr-kind-payment-attempt::before { background: var(--color-brand); }\n .x-tr-kind-delivery-exception::before, .x-tr-kind-return::before { background: var(--color-danger, #c0392b); }\n .x-tr-when { font-size: 11.5px; color: var(--color-text-muted); min-width: 96px; padding-top: 2px; }\n .x-tr-tag { font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; color: var(--color-text-muted); margin-right: 8px; }\n .x-tr-label { font-size: 13.5px; color: var(--color-text); }\n .x-tr-detail { font-size: 12px; color: var(--color-text-muted); margin-top: 2px; }\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 { TraceComponent } from './trace.component';\n\n@NgModule({\n declarations: [TraceComponent],\n imports: [\n SharedModule,\n ElementsModule,\n RouterModule.forChild([{ path: '', component: TraceComponent }]),\n ],\n})\nexport class TraceModule {}\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
- import { BaseDirective, using, titleCase, SharedModule } from '@thescaffold/ngx-core';
6
- import * as i1 from '@thescaffold/ngx-ui';
5
+ import { SharedModule } from '@thescaffold/ngx-core';
6
+ import * as i2 from '@thescaffold/ngx-ui';
7
7
  import { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';
8
- import { BehaviorSubject, takeUntil, map } from 'rxjs';
9
- import * as i2 from '@angular/common';
8
+ import { L as ListPageDirective } from './isaiahiroko-tilmart-ngx-apps-admin-list-page.base-Cl7JZ-Tp.mjs';
9
+ import * as i3 from '@angular/common';
10
10
 
11
11
  // Read-only, searchable User browser (docs/PLAN_2.md Phase 10 req #25) - no
12
12
  // list/detail existed anywhere in Tilmart's admin before this. Deliberately
@@ -17,7 +17,7 @@ import * as i2 from '@angular/common';
17
17
  // wide" - so this is a fresh page against apps/identity/user directly,
18
18
  // matching this app's established x-webget-collection pattern (routes,
19
19
  // inbox, payments) rather than pulling in that vendor module.
20
- class UsersComponent extends BaseDirective {
20
+ class UsersComponent extends ListPageDirective {
21
21
  constructor() {
22
22
  super(...arguments);
23
23
  this.name = 'user';
@@ -28,31 +28,6 @@ class UsersComponent extends BaseDirective {
28
28
  { key: 'type', value: 'Type', type: 'class', class: 'badge text-bg-light border' },
29
29
  { key: 'createdAt', value: 'Joined', type: 'date' },
30
30
  ];
31
- this.page = 1;
32
- this.perPage = 25;
33
- this.list$ = new BehaviorSubject(null);
34
- this.init = () => {
35
- this.list();
36
- };
37
- }
38
- list(page = this.page, perPage = this.perPage) {
39
- this.dataSource
40
- .getMany({ page, perPage })
41
- .pipe(takeUntil(this.$), map((response) => this.toCollection(response)))
42
- .subscribe(using((data) => this.list$.next(data)));
43
- }
44
- toCollection(response) {
45
- return {
46
- title: titleCase(this.pluralName),
47
- columns: this.columns,
48
- rows: response?.data,
49
- hasHeaders: true,
50
- hasTextFilter: true,
51
- page: response?.meta?.current_page,
52
- perPage: response?.meta?.per_page,
53
- total: response?.meta?.total,
54
- onPage: (page) => this.list(page),
55
- };
56
31
  }
57
32
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: UsersComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
58
33
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: UsersComponent, isStandalone: false, selector: "x-users", usesInheritance: true, ngImport: i0, template: `
@@ -66,7 +41,7 @@ class UsersComponent extends BaseDirective {
66
41
  } @else {
67
42
  <x-element-loader></x-element-loader>
68
43
  }
69
- `, isInline: true, 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: i1.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "component", type: i1.CollectionComponent, selector: "x-webget-collection", inputs: ["data"], outputs: ["search", "filter", "refresh", "select", "page", "show"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }] }); }
44
+ `, isInline: true, 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: "pipe", type: i3.AsyncPipe, name: "async" }] }); }
70
45
  }
71
46
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: UsersComponent, decorators: [{
72
47
  type: Component,
@@ -88,7 +63,7 @@ class UsersModule {
88
63
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: UsersModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
89
64
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: UsersModule, declarations: [UsersComponent], imports: [SharedModule,
90
65
  ElementsModule,
91
- WebgetsModule, i1$1.RouterModule] }); }
66
+ WebgetsModule, i1.RouterModule] }); }
92
67
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: UsersModule, imports: [SharedModule,
93
68
  ElementsModule,
94
69
  WebgetsModule,
@@ -108,4 +83,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
108
83
  }] });
109
84
 
110
85
  export { UsersModule };
111
- //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-users.module-Bx_7fI2g.mjs.map
86
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-users.module-C31RJJJ_.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-users.module-C31RJJJ_.mjs","sources":["../../../projects/admin/src/lib/pages/users/users.component.ts","../../../projects/admin/src/lib/pages/users/users.module.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { ListPageDirective } from '../_shared/list-page.base';\n\n// Read-only, searchable User browser (docs/PLAN_2.md Phase 10 req #25) - no\n// list/detail existed anywhere in Tilmart's admin before this. Deliberately\n// thin: identity's own vendor \"people\" Angular pages (ngx-apps/projects/\n// identity/src/lib/pages/people/) are workspace-team-membership management\n// (\"invite a teammate to my workspace\", backed by apps/identity/role-type),\n// the wrong shape entirely for \"browse every registered buyer platform-\n// wide\" - so this is a fresh page against apps/identity/user directly,\n// matching this app's established x-webget-collection pattern (routes,\n// inbox, payments) rather than pulling in that vendor module.\n@Component({\n standalone: false,\n selector: 'x-users',\n template: `\n <div class=\"x-page-head\">\n <div class=\"x-page-title\">Users</div>\n <div class=\"x-page-subtitle\">Every registered account, across all workspaces.</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 styles: [\n `\n .x-page-head {\n margin-bottom: 20px;\n }\n .x-page-title {\n font-size: 24px;\n font-weight: 800;\n color: var(--color-text);\n }\n .x-page-subtitle {\n font-size: 13.5px;\n color: var(--color-text-muted);\n margin-top: 2px;\n }\n `,\n ],\n})\nexport class UsersComponent extends ListPageDirective {\n name = 'user';\n protected pluralName = 'users';\n protected dataSource = this.apiService.on('apps/identity/user');\n\n protected columns = [\n { key: 'ref', value: 'Email / phone' },\n { key: 'type', value: 'Type', type: 'class', class: 'badge text-bg-light border' },\n { key: 'createdAt', value: 'Joined', type: 'date' },\n ];\n\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 { UsersComponent } from './users.component';\n\n@NgModule({\n declarations: [UsersComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: UsersComponent }]),\n ],\n})\nexport class UsersModule {}\n"],"names":["i1","i2"],"mappings":"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"}