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

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 (107) 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-catalog.module-DoxDNaNn.mjs +538 -0
  12. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-DoxDNaNn.mjs.map +1 -0
  13. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-categories.module-OTUfenf0.mjs +94 -0
  14. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-categories.module-OTUfenf0.mjs.map +1 -0
  15. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Dk9mz7Wj.mjs +137 -0
  16. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Dk9mz7Wj.mjs.map +1 -0
  17. 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
  18. 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
  19. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-content.module-DLdKAUqc.mjs → isaiahiroko-tilmart-ngx-apps-admin-content.module-Bx6Zj0WX.mjs} +5 -5
  20. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-content.module-DLdKAUqc.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-content.module-Bx6Zj0WX.mjs.map} +1 -1
  21. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CoRh6Hxq.mjs +85 -0
  22. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CoRh6Hxq.mjs.map +1 -0
  23. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-CslGiOdq.mjs +123 -0
  24. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-CslGiOdq.mjs.map +1 -0
  25. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-Bfgh3zE5.mjs → isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-CvFmMoJe.mjs} +6 -6
  26. 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
  27. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-C6tPNlup.mjs → isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-CuZwNVPO.mjs} +6 -6
  28. 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
  29. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-flash-sales.module-BPRnC17l.mjs +83 -0
  30. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-flash-sales.module-BPRnC17l.mjs.map +1 -0
  31. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-homepage.module-BZ3UmxE3.mjs → isaiahiroko-tilmart-ngx-apps-admin-homepage.module-vtAoIwSu.mjs} +5 -5
  32. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-homepage.module-BZ3UmxE3.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-homepage.module-vtAoIwSu.mjs.map} +1 -1
  33. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-inbox.module-BZDTa1L-.mjs → isaiahiroko-tilmart-ngx-apps-admin-inbox.module-CRkfjWQT.mjs} +6 -6
  34. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-inbox.module-BZDTa1L-.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-inbox.module-CRkfjWQT.mjs.map} +1 -1
  35. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-list-page.base-Cl7JZ-Tp.mjs +66 -0
  36. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-list-page.base-Cl7JZ-Tp.mjs.map +1 -0
  37. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-macros.module-BO4bdER2.mjs → isaiahiroko-tilmart-ngx-apps-admin-macros.module-CElxL9W4.mjs} +5 -5
  38. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-macros.module-BO4bdER2.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-macros.module-CElxL9W4.mjs.map} +1 -1
  39. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-moderation.module-CZOCrias.mjs → isaiahiroko-tilmart-ngx-apps-admin-moderation.module-PGmFX7nk.mjs} +6 -6
  40. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-moderation.module-CZOCrias.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-moderation.module-PGmFX7nk.mjs.map} +1 -1
  41. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-navigation.module-DoKzzRgn.mjs → isaiahiroko-tilmart-ngx-apps-admin-navigation.module-De1Cvqqa.mjs} +6 -6
  42. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-navigation.module-DoKzzRgn.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-navigation.module-De1Cvqqa.mjs.map} +1 -1
  43. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-order-board.module-DIOjhptn.mjs → isaiahiroko-tilmart-ngx-apps-admin-order-board.module-CmLNiEEP.mjs} +4 -4
  44. 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
  45. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-orders.module-Ddw49QW0.mjs → isaiahiroko-tilmart-ngx-apps-admin-orders.module-Cfeb0QjH.mjs} +15 -33
  46. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-orders.module-Cfeb0QjH.mjs.map +1 -0
  47. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-DstLDYM7.mjs +114 -0
  48. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-DstLDYM7.mjs.map +1 -0
  49. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG03OY1q.mjs +94 -0
  50. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG03OY1q.mjs.map +1 -0
  51. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-queues.module-K-zYYVFy.mjs → isaiahiroko-tilmart-ngx-apps-admin-queues.module-D7iLwX0A.mjs} +6 -6
  52. 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
  53. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-oQw5Xo5T.mjs +78 -0
  54. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-oQw5Xo5T.mjs.map +1 -0
  55. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-referrals.module-QLNhuh7q.mjs → isaiahiroko-tilmart-ngx-apps-admin-referrals.module-BIAY4L_u.mjs} +9 -34
  56. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-referrals.module-BIAY4L_u.mjs.map +1 -0
  57. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-reports.module-DBZov0We.mjs → isaiahiroko-tilmart-ngx-apps-admin-reports.module-DORiIVLQ.mjs} +5 -5
  58. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-reports.module-DBZov0We.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-reports.module-DORiIVLQ.mjs.map} +1 -1
  59. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-C9Iu9CX6.mjs +163 -0
  60. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-C9Iu9CX6.mjs.map +1 -0
  61. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-CGBBHwId.mjs +81 -0
  62. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-CGBBHwId.mjs.map +1 -0
  63. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-Cz6xPNwB.mjs +318 -0
  64. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-Cz6xPNwB.mjs.map +1 -0
  65. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-shipments.module-XuaOKr8v.mjs → isaiahiroko-tilmart-ngx-apps-admin-shipments.module-Cs5CLamm.mjs} +13 -32
  66. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-shipments.module-Cs5CLamm.mjs.map +1 -0
  67. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs +140 -0
  68. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs.map +1 -0
  69. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-Bt9W1oGR.mjs +102 -0
  70. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-Bt9W1oGR.mjs.map +1 -0
  71. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-trace.module-Pwr-82Rc.mjs → isaiahiroko-tilmart-ngx-apps-admin-trace.module-9sRlGZCC.mjs} +6 -6
  72. 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
  73. package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-users.module-Bx_7fI2g.mjs → isaiahiroko-tilmart-ngx-apps-admin-users.module-C31RJJJ_.mjs} +9 -34
  74. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-users.module-C31RJJJ_.mjs.map +1 -0
  75. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin.mjs +96 -72
  76. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin.mjs.map +1 -1
  77. package/package.json +1 -1
  78. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-baskets.module-_lHPl1dT.mjs +0 -294
  79. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-baskets.module-_lHPl1dT.mjs.map +0 -1
  80. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-Bvx2tVOi.mjs +0 -224
  81. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-Bvx2tVOi.mjs.map +0 -1
  82. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Ccsd8Yx3.mjs +0 -106
  83. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Ccsd8Yx3.mjs.map +0 -1
  84. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CQNCSlsd.mjs +0 -106
  85. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CQNCSlsd.mjs.map +0 -1
  86. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-12XdV9zo.mjs +0 -106
  87. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-12XdV9zo.mjs.map +0 -1
  88. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-orders.module-Ddw49QW0.mjs.map +0 -1
  89. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-BhqAtTZs.mjs +0 -106
  90. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-BhqAtTZs.mjs.map +0 -1
  91. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-procurement.module-qXW-g0pf.mjs +0 -274
  92. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-procurement.module-qXW-g0pf.mjs.map +0 -1
  93. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG9uTDqX.mjs +0 -106
  94. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG9uTDqX.mjs.map +0 -1
  95. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-Hm2dO_bC.mjs +0 -106
  96. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-Hm2dO_bC.mjs.map +0 -1
  97. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-referrals.module-QLNhuh7q.mjs.map +0 -1
  98. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-Bd2gdo6_.mjs +0 -106
  99. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-Bd2gdo6_.mjs.map +0 -1
  100. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-BkVY06ci.mjs +0 -105
  101. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-BkVY06ci.mjs.map +0 -1
  102. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-DKeZTldO.mjs +0 -222
  103. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-DKeZTldO.mjs.map +0 -1
  104. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-shipments.module-XuaOKr8v.mjs.map +0 -1
  105. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-DYiAkF7E.mjs +0 -107
  106. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-DYiAkF7E.mjs.map +0 -1
  107. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-users.module-Bx_7fI2g.mjs.map +0 -1
@@ -1,9 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Component, NgModule } from '@angular/core';
3
- import * as i1$2 from '@angular/router';
3
+ import * as i1$1 from '@angular/router';
4
4
  import { RouterModule } from '@angular/router';
5
5
  import { BaseDirective, using, notify, 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 { R as RichEditorComponent, a as RichEditorModule } from './isaiahiroko-tilmart-ngx-apps-admin-rich-editor.module-DW9Gy_QQ.mjs';
9
9
  import { takeUntil, firstValueFrom } from 'rxjs';
@@ -120,7 +120,7 @@ class ArticlesComponent extends BaseDirective {
120
120
  }
121
121
  }
122
122
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ArticlesComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
123
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: ArticlesComponent, isStandalone: false, selector: "x-articles", usesInheritance: true, ngImport: i0, template: "<div class=\"x-art-head\">\n <div class=\"x-art-title\">Learn articles</div>\n <div class=\"x-art-sub\">Guides, how-tos and glossary entries for the storefront Learn hub.</div>\n</div>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-art-layout\">\n <aside class=\"x-art-list\">\n <div class=\"x-art-new\">\n <input type=\"text\" placeholder=\"slug\" [(ngModel)]=\"newSlug\" />\n <input type=\"text\" placeholder=\"Title\" [(ngModel)]=\"newTitle\" />\n <button type=\"button\" (click)=\"create()\">Add article</button>\n </div>\n @for (a of articles; track a.id) {\n <button\n type=\"button\"\n class=\"x-art-list-item\"\n [class.x-art-list-active]=\"selected?.id === a.id\"\n (click)=\"select(a)\"\n >\n <span class=\"x-art-list-title\">{{ a.title }}</span>\n <span class=\"x-art-list-meta\">{{ a.category || '\u2014' }} \u00B7 {{ a.status }}</span>\n </button>\n } @empty {\n <div class=\"x-art-empty\">No articles yet.</div>\n }\n </aside>\n\n <section class=\"x-art-editor\">\n @if (!selected) {\n <div class=\"x-art-empty\">Pick an article, or add one.</div>\n } @else {\n <div class=\"x-art-f-row\">\n <label class=\"x-art-f\">\n Title\n <input type=\"text\" [(ngModel)]=\"selected.title\" />\n </label>\n <label class=\"x-art-f\">\n Slug\n <input type=\"text\" [(ngModel)]=\"selected.slug\" />\n </label>\n </div>\n <label class=\"x-art-f\">\n Summary\n <input type=\"text\" [(ngModel)]=\"selected.summary\" />\n </label>\n <div class=\"x-art-f-row\">\n <label class=\"x-art-f\">\n Category\n <select [(ngModel)]=\"selected.category\">\n @for (c of categories; track c) { <option [value]=\"c\">{{ c }}</option> }\n </select>\n </label>\n <label class=\"x-art-f\">\n Author\n <input type=\"text\" [(ngModel)]=\"selected.authorName\" />\n </label>\n <label class=\"x-art-f\">\n Read minutes\n <input type=\"number\" [(ngModel)]=\"selected.readMinutes\" />\n </label>\n </div>\n <label class=\"x-art-f\">\n Tags (comma-separated)\n <input type=\"text\" [(ngModel)]=\"tagsInput\" />\n </label>\n <div class=\"x-art-f-row\">\n <label class=\"x-art-f\">\n Status\n <select [(ngModel)]=\"selected.status\">\n @for (s of statuses; track s) { <option [value]=\"s\">{{ s }}</option> }\n </select>\n </label>\n <label class=\"x-art-f\">\n Publish at\n <input type=\"datetime-local\" [(ngModel)]=\"selected.publishAt\" />\n </label>\n </div>\n <label class=\"x-art-f\">\n Body\n <x-rich-editor [content]=\"selected.body || ''\" (contentChange)=\"onBodyChange($event)\"></x-rich-editor>\n </label>\n <div class=\"x-art-actions\">\n <button type=\"button\" class=\"x-art-btn x-art-btn-primary\" (click)=\"save()\">Save</button>\n <button type=\"button\" class=\"x-art-btn x-art-danger\" (click)=\"remove()\">Delete</button>\n </div>\n }\n </section>\n </div>\n}\n\n<style>\n .x-art-head { margin-bottom: 20px; }\n .x-art-title { font-size: 24px; font-weight: 800; color: var(--color-text); }\n .x-art-sub { font-size: 13.5px; color: var(--color-text-muted); margin-top: 2px; }\n .x-art-empty { font-size: 14px; color: var(--color-text-muted); padding: 12px 0; }\n .x-art-layout { display: grid; grid-template-columns: 260px 1fr; gap: 20px; align-items: start; }\n .x-art-list { display: flex; flex-direction: column; gap: 6px; }\n .x-art-new { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }\n .x-art-new input, .x-art-f input, .x-art-f select {\n border: 1px solid var(--color-border); border-radius: var(--radius-md);\n padding: 6px 10px; font-size: 13px; background: var(--color-surface); color: var(--color-text);\n }\n .x-art-new button, .x-art-btn {\n border: 1px solid var(--color-border); background: transparent; border-radius: var(--radius-md);\n padding: 6px 14px; font-size: 12.5px; font-weight: 700; cursor: pointer; color: var(--color-text-muted);\n }\n .x-art-btn-primary { border-color: var(--color-accent); color: var(--color-accent-hover); }\n .x-art-danger { color: var(--color-danger, #c0362c); }\n .x-art-list-item {\n text-align: left; border: 1px solid var(--color-border); border-radius: var(--radius-md);\n background: var(--color-surface); padding: 8px 10px; cursor: pointer;\n display: flex; flex-direction: column; gap: 2px;\n }\n .x-art-list-active { border-color: var(--color-accent); }\n .x-art-list-title { font-size: 13px; font-weight: 700; color: var(--color-text); }\n .x-art-list-meta { font-size: 11px; color: var(--color-text-muted); }\n .x-art-editor {\n min-width: 0; background: var(--color-surface); border: 1px solid var(--color-border);\n border-radius: var(--radius-lg); padding: 16px; display: flex; flex-direction: column; gap: 10px;\n }\n .x-art-f { display: flex; flex-direction: column; gap: 4px; flex: 1; font-size: 11.5px; color: var(--color-text-muted); font-weight: 700; }\n .x-art-f-row { display: flex; gap: 10px; }\n .x-art-actions { display: flex; gap: 8px; }\n x-rich-editor { display: block; border: 1px solid var(--color-border); border-radius: var(--radius-md); min-height: 200px; }\n</style>\n", styles: [".x-art-head{margin-bottom:20px}.x-art-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-art-sub{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}.x-art-empty{font-size:14px;color:var(--color-text-muted);padding:12px 0}.x-art-layout{display:grid;grid-template-columns:260px 1fr;gap:20px;align-items:start}.x-art-list{display:flex;flex-direction:column;gap:6px}.x-art-new{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}.x-art-new input,.x-art-f input,.x-art-f select{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)}.x-art-new button,.x-art-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-art-btn-primary{border-color:var(--color-accent);color:var(--color-accent-hover)}.x-art-danger{color:var(--color-danger, #c0362c)}.x-art-list-item{text-align:left;border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);padding:8px 10px;cursor:pointer;display:flex;flex-direction:column;gap:2px}.x-art-list-active{border-color:var(--color-accent)}.x-art-list-title{font-size:13px;font-weight:700;color:var(--color-text)}.x-art-list-meta{font-size:11px;color:var(--color-text-muted)}.x-art-editor{min-width:0;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;display:flex;flex-direction:column;gap:10px}.x-art-f{display:flex;flex-direction:column;gap:4px;flex:1;font-size:11.5px;color:var(--color-text-muted);font-weight:700}.x-art-f-row{display:flex;gap:10px}.x-art-actions{display:flex;gap:8px}x-rich-editor{display:block;border:1px solid var(--color-border);border-radius:var(--radius-md);min-height:200px}\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.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: i1$1.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "component", type: RichEditorComponent, selector: "x-rich-editor", inputs: ["content"], outputs: ["contentChange"] }] }); }
123
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: ArticlesComponent, isStandalone: false, selector: "x-articles", usesInheritance: true, ngImport: i0, template: "<div class=\"x-art-head\">\n <div class=\"x-art-title\">Learn articles</div>\n <div class=\"x-art-sub\">Guides, how-tos and glossary entries for the storefront Learn hub.</div>\n</div>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-art-layout\">\n <aside class=\"x-art-list\">\n <div class=\"x-art-new\">\n <input type=\"text\" placeholder=\"slug\" [(ngModel)]=\"newSlug\" />\n <input type=\"text\" placeholder=\"Title\" [(ngModel)]=\"newTitle\" />\n <button type=\"button\" (click)=\"create()\">Add article</button>\n </div>\n @for (a of articles; track a.id) {\n <button\n type=\"button\"\n class=\"x-art-list-item\"\n [class.x-art-list-active]=\"selected?.id === a.id\"\n (click)=\"select(a)\"\n >\n <span class=\"x-art-list-title\">{{ a.title }}</span>\n <span class=\"x-art-list-meta\">{{ a.category || '\u2014' }} \u00B7 {{ a.status }}</span>\n </button>\n } @empty {\n <div class=\"x-art-empty\">No articles yet.</div>\n }\n </aside>\n\n <section class=\"x-art-editor\">\n @if (!selected) {\n <div class=\"x-art-empty\">Pick an article, or add one.</div>\n } @else {\n <div class=\"x-art-f-row\">\n <label class=\"x-art-f\">\n Title\n <input type=\"text\" [(ngModel)]=\"selected.title\" />\n </label>\n <label class=\"x-art-f\">\n Slug\n <input type=\"text\" [(ngModel)]=\"selected.slug\" />\n </label>\n </div>\n <label class=\"x-art-f\">\n Summary\n <input type=\"text\" [(ngModel)]=\"selected.summary\" />\n </label>\n <div class=\"x-art-f-row\">\n <label class=\"x-art-f\">\n Category\n <select [(ngModel)]=\"selected.category\">\n @for (c of categories; track c) { <option [value]=\"c\">{{ c }}</option> }\n </select>\n </label>\n <label class=\"x-art-f\">\n Author\n <input type=\"text\" [(ngModel)]=\"selected.authorName\" />\n </label>\n <label class=\"x-art-f\">\n Read minutes\n <input type=\"number\" [(ngModel)]=\"selected.readMinutes\" />\n </label>\n </div>\n <label class=\"x-art-f\">\n Tags (comma-separated)\n <input type=\"text\" [(ngModel)]=\"tagsInput\" />\n </label>\n <div class=\"x-art-f-row\">\n <label class=\"x-art-f\">\n Status\n <select [(ngModel)]=\"selected.status\">\n @for (s of statuses; track s) { <option [value]=\"s\">{{ s }}</option> }\n </select>\n </label>\n <label class=\"x-art-f\">\n Publish at\n <input type=\"datetime-local\" [(ngModel)]=\"selected.publishAt\" />\n </label>\n </div>\n <label class=\"x-art-f\">\n Body\n <x-rich-editor [content]=\"selected.body || ''\" (contentChange)=\"onBodyChange($event)\"></x-rich-editor>\n </label>\n <div class=\"x-art-actions\">\n <button type=\"button\" class=\"x-art-btn x-art-btn-primary\" (click)=\"save()\">Save</button>\n <button type=\"button\" class=\"x-art-btn x-art-danger\" (click)=\"remove()\">Delete</button>\n </div>\n }\n </section>\n </div>\n}\n\n<style>\n .x-art-head { margin-bottom: 20px; }\n .x-art-title { font-size: 24px; font-weight: 800; color: var(--color-text); }\n .x-art-sub { font-size: 13.5px; color: var(--color-text-muted); margin-top: 2px; }\n .x-art-empty { font-size: 14px; color: var(--color-text-muted); padding: 12px 0; }\n .x-art-layout { display: grid; grid-template-columns: 260px 1fr; gap: 20px; align-items: start; }\n .x-art-list { display: flex; flex-direction: column; gap: 6px; }\n .x-art-new { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }\n .x-art-new input, .x-art-f input, .x-art-f select {\n border: 1px solid var(--color-border); border-radius: var(--radius-md);\n padding: 6px 10px; font-size: 13px; background: var(--color-surface); color: var(--color-text);\n }\n .x-art-new button, .x-art-btn {\n border: 1px solid var(--color-border); background: transparent; border-radius: var(--radius-md);\n padding: 6px 14px; font-size: 12.5px; font-weight: 700; cursor: pointer; color: var(--color-text-muted);\n }\n .x-art-btn-primary { border-color: var(--color-accent); color: var(--color-accent-hover); }\n .x-art-danger { color: var(--color-danger, #c0362c); }\n .x-art-list-item {\n text-align: left; border: 1px solid var(--color-border); border-radius: var(--radius-md);\n background: var(--color-surface); padding: 8px 10px; cursor: pointer;\n display: flex; flex-direction: column; gap: 2px;\n }\n .x-art-list-active { border-color: var(--color-accent); }\n .x-art-list-title { font-size: 13px; font-weight: 700; color: var(--color-text); }\n .x-art-list-meta { font-size: 11px; color: var(--color-text-muted); }\n .x-art-editor {\n min-width: 0; background: var(--color-surface); border: 1px solid var(--color-border);\n border-radius: var(--radius-lg); padding: 16px; display: flex; flex-direction: column; gap: 10px;\n }\n .x-art-f { display: flex; flex-direction: column; gap: 4px; flex: 1; font-size: 11.5px; color: var(--color-text-muted); font-weight: 700; }\n .x-art-f-row { display: flex; gap: 10px; }\n .x-art-actions { display: flex; gap: 8px; }\n x-rich-editor { display: block; border: 1px solid var(--color-border); border-radius: var(--radius-md); min-height: 200px; }\n</style>\n", styles: [".x-art-head{margin-bottom:20px}.x-art-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-art-sub{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}.x-art-empty{font-size:14px;color:var(--color-text-muted);padding:12px 0}.x-art-layout{display:grid;grid-template-columns:260px 1fr;gap:20px;align-items:start}.x-art-list{display:flex;flex-direction:column;gap:6px}.x-art-new{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}.x-art-new input,.x-art-f input,.x-art-f select{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)}.x-art-new button,.x-art-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-art-btn-primary{border-color:var(--color-accent);color:var(--color-accent-hover)}.x-art-danger{color:var(--color-danger, #c0362c)}.x-art-list-item{text-align:left;border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);padding:8px 10px;cursor:pointer;display:flex;flex-direction:column;gap:2px}.x-art-list-active{border-color:var(--color-accent)}.x-art-list-title{font-size:13px;font-weight:700;color:var(--color-text)}.x-art-list-meta{font-size:11px;color:var(--color-text-muted)}.x-art-editor{min-width:0;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;display:flex;flex-direction:column;gap:10px}.x-art-f{display:flex;flex-direction:column;gap:4px;flex:1;font-size:11.5px;color:var(--color-text-muted);font-weight:700}.x-art-f-row{display:flex;gap:10px}.x-art-actions{display:flex;gap:8px}x-rich-editor{display:block;border:1px solid var(--color-border);border-radius:var(--radius-md);min-height:200px}\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.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: RichEditorComponent, selector: "x-rich-editor", inputs: ["content"], outputs: ["contentChange"] }] }); }
124
124
  }
125
125
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ArticlesComponent, decorators: [{
126
126
  type: Component,
@@ -131,7 +131,7 @@ class ArticlesModule {
131
131
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ArticlesModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
132
132
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: ArticlesModule, declarations: [ArticlesComponent], imports: [SharedModule,
133
133
  ElementsModule,
134
- RichEditorModule, i1$2.RouterModule] }); }
134
+ RichEditorModule, i1$1.RouterModule] }); }
135
135
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ArticlesModule, imports: [SharedModule,
136
136
  ElementsModule,
137
137
  RichEditorModule,
@@ -151,4 +151,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
151
151
  }] });
152
152
 
153
153
  export { ArticlesModule };
154
- //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-articles.module-OeLRnPwN.mjs.map
154
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-articles.module-Cpx9BZcg.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-articles.module-OeLRnPwN.mjs","sources":["../../../projects/admin/src/lib/pages/articles/articles.component.ts","../../../projects/admin/src/lib/pages/articles/articles.component.html","../../../projects/admin/src/lib/pages/articles/articles.module.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { BaseDirective, notify, using } from '@thescaffold/ngx-core';\nimport { firstValueFrom, takeUntil } from 'rxjs';\n\ninterface ArticleVm {\n id: string;\n slug: string;\n title: string;\n summary?: string;\n body?: string;\n category?: string;\n tags?: string[];\n authorName?: string;\n readMinutes?: number;\n status: string;\n publishAt?: string;\n}\n\nconst STATUSES = ['draft', 'published', 'archived'];\nconst CATEGORIES = [\n // Learn hub\n 'guides',\n 'how-to',\n 'glossary',\n 'news',\n 'case-study',\n // Help centre (SUP-01)\n 'getting-started',\n 'orders-delivery',\n 'payments-refunds',\n 'returns-warranty',\n 'account',\n 'faq',\n];\n\n// P6.11, CMS-02 — the Learn-hub article editor. List + a metadata form\n// with a WYSIWYG body. The storefront `/learn` hub renders the published\n// set. ops CRUD is covered by `admin:*:apps:commerce:*`.\n@Component({\n standalone: false,\n selector: 'x-articles',\n templateUrl: './articles.component.html',\n})\nexport class ArticlesComponent extends BaseDirective {\n private readonly source = this.apiService.on('apps/commerce/article');\n\n readonly statuses = STATUSES;\n readonly categories = CATEGORIES;\n\n loading = true;\n articles: ArticleVm[] = [];\n selected?: ArticleVm;\n tagsInput = '';\n\n newSlug = '';\n newTitle = '';\n\n override init = () => {\n this.load();\n };\n\n load(): void {\n this.loading = true;\n this.source\n .getMany({ perPage: 200 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((res: any) => {\n this.articles = (res?.data ?? []).sort((a: any, b: any) =>\n String(a.title).localeCompare(String(b.title)),\n );\n this.loading = false;\n if (this.selected) {\n this.selected = this.articles.find((a) => a.id === this.selected!.id);\n }\n }),\n );\n }\n\n async create(): Promise<void> {\n const slug = this.newSlug.trim().toLowerCase().replace(/\\s+/g, '-');\n const title = this.newTitle.trim();\n if (!slug || !title) return;\n try {\n const res: any = await firstValueFrom(\n this.source.create({ slug, title, status: 'draft', category: 'guides' } as any),\n );\n this.newSlug = '';\n this.newTitle = '';\n notify('Article created.', undefined, 'success');\n this.load();\n if (res?.data?.id) setTimeout(() => this.select({ ...res.data }), 200);\n } catch {\n notify('Could not create the article (slug may be taken).', undefined, 'error');\n }\n }\n\n select(article: ArticleVm): void {\n this.selected = article;\n this.tagsInput = (article.tags ?? []).join(', ');\n }\n\n onBodyChange(html: string): void {\n if (this.selected) this.selected.body = html;\n }\n\n async save(): Promise<void> {\n if (!this.selected) return;\n const a = this.selected;\n try {\n await firstValueFrom(\n this.source.update(a.id, {\n slug: a.slug,\n title: a.title,\n summary: a.summary || undefined,\n body: a.body || undefined,\n category: a.category || undefined,\n tags: this.tagsInput.split(',').map((t) => t.trim()).filter(Boolean),\n authorName: a.authorName || undefined,\n readMinutes: a.readMinutes ? Number(a.readMinutes) : undefined,\n status: a.status,\n publishAt: a.publishAt ? new Date(a.publishAt).toISOString() : null,\n } as any),\n );\n notify('Article saved.', undefined, 'success');\n this.load();\n } catch {\n notify('Could not save the article.', undefined, 'error');\n }\n }\n\n async remove(): Promise<void> {\n if (!this.selected) return;\n try {\n await firstValueFrom(this.source.remove(this.selected.id));\n this.selected = undefined;\n notify('Article deleted.', undefined, 'success');\n this.load();\n } catch {\n notify('Could not delete the article.', undefined, 'error');\n }\n }\n}\n","<div class=\"x-art-head\">\n <div class=\"x-art-title\">Learn articles</div>\n <div class=\"x-art-sub\">Guides, how-tos and glossary entries for the storefront Learn hub.</div>\n</div>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-art-layout\">\n <aside class=\"x-art-list\">\n <div class=\"x-art-new\">\n <input type=\"text\" placeholder=\"slug\" [(ngModel)]=\"newSlug\" />\n <input type=\"text\" placeholder=\"Title\" [(ngModel)]=\"newTitle\" />\n <button type=\"button\" (click)=\"create()\">Add article</button>\n </div>\n @for (a of articles; track a.id) {\n <button\n type=\"button\"\n class=\"x-art-list-item\"\n [class.x-art-list-active]=\"selected?.id === a.id\"\n (click)=\"select(a)\"\n >\n <span class=\"x-art-list-title\">{{ a.title }}</span>\n <span class=\"x-art-list-meta\">{{ a.category || '—' }} · {{ a.status }}</span>\n </button>\n } @empty {\n <div class=\"x-art-empty\">No articles yet.</div>\n }\n </aside>\n\n <section class=\"x-art-editor\">\n @if (!selected) {\n <div class=\"x-art-empty\">Pick an article, or add one.</div>\n } @else {\n <div class=\"x-art-f-row\">\n <label class=\"x-art-f\">\n Title\n <input type=\"text\" [(ngModel)]=\"selected.title\" />\n </label>\n <label class=\"x-art-f\">\n Slug\n <input type=\"text\" [(ngModel)]=\"selected.slug\" />\n </label>\n </div>\n <label class=\"x-art-f\">\n Summary\n <input type=\"text\" [(ngModel)]=\"selected.summary\" />\n </label>\n <div class=\"x-art-f-row\">\n <label class=\"x-art-f\">\n Category\n <select [(ngModel)]=\"selected.category\">\n @for (c of categories; track c) { <option [value]=\"c\">{{ c }}</option> }\n </select>\n </label>\n <label class=\"x-art-f\">\n Author\n <input type=\"text\" [(ngModel)]=\"selected.authorName\" />\n </label>\n <label class=\"x-art-f\">\n Read minutes\n <input type=\"number\" [(ngModel)]=\"selected.readMinutes\" />\n </label>\n </div>\n <label class=\"x-art-f\">\n Tags (comma-separated)\n <input type=\"text\" [(ngModel)]=\"tagsInput\" />\n </label>\n <div class=\"x-art-f-row\">\n <label class=\"x-art-f\">\n Status\n <select [(ngModel)]=\"selected.status\">\n @for (s of statuses; track s) { <option [value]=\"s\">{{ s }}</option> }\n </select>\n </label>\n <label class=\"x-art-f\">\n Publish at\n <input type=\"datetime-local\" [(ngModel)]=\"selected.publishAt\" />\n </label>\n </div>\n <label class=\"x-art-f\">\n Body\n <x-rich-editor [content]=\"selected.body || ''\" (contentChange)=\"onBodyChange($event)\"></x-rich-editor>\n </label>\n <div class=\"x-art-actions\">\n <button type=\"button\" class=\"x-art-btn x-art-btn-primary\" (click)=\"save()\">Save</button>\n <button type=\"button\" class=\"x-art-btn x-art-danger\" (click)=\"remove()\">Delete</button>\n </div>\n }\n </section>\n </div>\n}\n\n<style>\n .x-art-head { margin-bottom: 20px; }\n .x-art-title { font-size: 24px; font-weight: 800; color: var(--color-text); }\n .x-art-sub { font-size: 13.5px; color: var(--color-text-muted); margin-top: 2px; }\n .x-art-empty { font-size: 14px; color: var(--color-text-muted); padding: 12px 0; }\n .x-art-layout { display: grid; grid-template-columns: 260px 1fr; gap: 20px; align-items: start; }\n .x-art-list { display: flex; flex-direction: column; gap: 6px; }\n .x-art-new { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }\n .x-art-new input, .x-art-f input, .x-art-f select {\n border: 1px solid var(--color-border); border-radius: var(--radius-md);\n padding: 6px 10px; font-size: 13px; background: var(--color-surface); color: var(--color-text);\n }\n .x-art-new button, .x-art-btn {\n border: 1px solid var(--color-border); background: transparent; border-radius: var(--radius-md);\n padding: 6px 14px; font-size: 12.5px; font-weight: 700; cursor: pointer; color: var(--color-text-muted);\n }\n .x-art-btn-primary { border-color: var(--color-accent); color: var(--color-accent-hover); }\n .x-art-danger { color: var(--color-danger, #c0362c); }\n .x-art-list-item {\n text-align: left; border: 1px solid var(--color-border); border-radius: var(--radius-md);\n background: var(--color-surface); padding: 8px 10px; cursor: pointer;\n display: flex; flex-direction: column; gap: 2px;\n }\n .x-art-list-active { border-color: var(--color-accent); }\n .x-art-list-title { font-size: 13px; font-weight: 700; color: var(--color-text); }\n .x-art-list-meta { font-size: 11px; color: var(--color-text-muted); }\n .x-art-editor {\n min-width: 0; background: var(--color-surface); border: 1px solid var(--color-border);\n border-radius: var(--radius-lg); padding: 16px; display: flex; flex-direction: column; gap: 10px;\n }\n .x-art-f { display: flex; flex-direction: column; gap: 4px; flex: 1; font-size: 11.5px; color: var(--color-text-muted); font-weight: 700; }\n .x-art-f-row { display: flex; gap: 10px; }\n .x-art-actions { display: flex; gap: 8px; }\n x-rich-editor { display: block; border: 1px solid var(--color-border); border-radius: var(--radius-md); min-height: 200px; }\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 { RichEditorModule } from '../_shared/rich-editor/rich-editor.module';\nimport { ArticlesComponent } from './articles.component';\n\n@NgModule({\n declarations: [ArticlesComponent],\n imports: [\n SharedModule,\n ElementsModule,\n RichEditorModule,\n RouterModule.forChild([{ path: '', component: ArticlesComponent }]),\n ],\n})\nexport class ArticlesModule {}\n"],"names":["i2","i3.RichEditorComponent","i1"],"mappings":"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"}
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-articles.module-Cpx9BZcg.mjs","sources":["../../../projects/admin/src/lib/pages/articles/articles.component.ts","../../../projects/admin/src/lib/pages/articles/articles.component.html","../../../projects/admin/src/lib/pages/articles/articles.module.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { BaseDirective, notify, using } from '@thescaffold/ngx-core';\nimport { firstValueFrom, takeUntil } from 'rxjs';\n\ninterface ArticleVm {\n id: string;\n slug: string;\n title: string;\n summary?: string;\n body?: string;\n category?: string;\n tags?: string[];\n authorName?: string;\n readMinutes?: number;\n status: string;\n publishAt?: string;\n}\n\nconst STATUSES = ['draft', 'published', 'archived'];\nconst CATEGORIES = [\n // Learn hub\n 'guides',\n 'how-to',\n 'glossary',\n 'news',\n 'case-study',\n // Help centre (SUP-01)\n 'getting-started',\n 'orders-delivery',\n 'payments-refunds',\n 'returns-warranty',\n 'account',\n 'faq',\n];\n\n// P6.11, CMS-02 — the Learn-hub article editor. List + a metadata form\n// with a WYSIWYG body. The storefront `/learn` hub renders the published\n// set. ops CRUD is covered by `admin:*:apps:commerce:*`.\n@Component({\n standalone: false,\n selector: 'x-articles',\n templateUrl: './articles.component.html',\n})\nexport class ArticlesComponent extends BaseDirective {\n private readonly source = this.apiService.on('apps/commerce/article');\n\n readonly statuses = STATUSES;\n readonly categories = CATEGORIES;\n\n loading = true;\n articles: ArticleVm[] = [];\n selected?: ArticleVm;\n tagsInput = '';\n\n newSlug = '';\n newTitle = '';\n\n override init = () => {\n this.load();\n };\n\n load(): void {\n this.loading = true;\n this.source\n .getMany({ perPage: 200 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((res: any) => {\n this.articles = (res?.data ?? []).sort((a: any, b: any) =>\n String(a.title).localeCompare(String(b.title)),\n );\n this.loading = false;\n if (this.selected) {\n this.selected = this.articles.find((a) => a.id === this.selected!.id);\n }\n }),\n );\n }\n\n async create(): Promise<void> {\n const slug = this.newSlug.trim().toLowerCase().replace(/\\s+/g, '-');\n const title = this.newTitle.trim();\n if (!slug || !title) return;\n try {\n const res: any = await firstValueFrom(\n this.source.create({ slug, title, status: 'draft', category: 'guides' } as any),\n );\n this.newSlug = '';\n this.newTitle = '';\n notify('Article created.', undefined, 'success');\n this.load();\n if (res?.data?.id) setTimeout(() => this.select({ ...res.data }), 200);\n } catch {\n notify('Could not create the article (slug may be taken).', undefined, 'error');\n }\n }\n\n select(article: ArticleVm): void {\n this.selected = article;\n this.tagsInput = (article.tags ?? []).join(', ');\n }\n\n onBodyChange(html: string): void {\n if (this.selected) this.selected.body = html;\n }\n\n async save(): Promise<void> {\n if (!this.selected) return;\n const a = this.selected;\n try {\n await firstValueFrom(\n this.source.update(a.id, {\n slug: a.slug,\n title: a.title,\n summary: a.summary || undefined,\n body: a.body || undefined,\n category: a.category || undefined,\n tags: this.tagsInput.split(',').map((t) => t.trim()).filter(Boolean),\n authorName: a.authorName || undefined,\n readMinutes: a.readMinutes ? Number(a.readMinutes) : undefined,\n status: a.status,\n publishAt: a.publishAt ? new Date(a.publishAt).toISOString() : null,\n } as any),\n );\n notify('Article saved.', undefined, 'success');\n this.load();\n } catch {\n notify('Could not save the article.', undefined, 'error');\n }\n }\n\n async remove(): Promise<void> {\n if (!this.selected) return;\n try {\n await firstValueFrom(this.source.remove(this.selected.id));\n this.selected = undefined;\n notify('Article deleted.', undefined, 'success');\n this.load();\n } catch {\n notify('Could not delete the article.', undefined, 'error');\n }\n }\n}\n","<div class=\"x-art-head\">\n <div class=\"x-art-title\">Learn articles</div>\n <div class=\"x-art-sub\">Guides, how-tos and glossary entries for the storefront Learn hub.</div>\n</div>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-art-layout\">\n <aside class=\"x-art-list\">\n <div class=\"x-art-new\">\n <input type=\"text\" placeholder=\"slug\" [(ngModel)]=\"newSlug\" />\n <input type=\"text\" placeholder=\"Title\" [(ngModel)]=\"newTitle\" />\n <button type=\"button\" (click)=\"create()\">Add article</button>\n </div>\n @for (a of articles; track a.id) {\n <button\n type=\"button\"\n class=\"x-art-list-item\"\n [class.x-art-list-active]=\"selected?.id === a.id\"\n (click)=\"select(a)\"\n >\n <span class=\"x-art-list-title\">{{ a.title }}</span>\n <span class=\"x-art-list-meta\">{{ a.category || '—' }} · {{ a.status }}</span>\n </button>\n } @empty {\n <div class=\"x-art-empty\">No articles yet.</div>\n }\n </aside>\n\n <section class=\"x-art-editor\">\n @if (!selected) {\n <div class=\"x-art-empty\">Pick an article, or add one.</div>\n } @else {\n <div class=\"x-art-f-row\">\n <label class=\"x-art-f\">\n Title\n <input type=\"text\" [(ngModel)]=\"selected.title\" />\n </label>\n <label class=\"x-art-f\">\n Slug\n <input type=\"text\" [(ngModel)]=\"selected.slug\" />\n </label>\n </div>\n <label class=\"x-art-f\">\n Summary\n <input type=\"text\" [(ngModel)]=\"selected.summary\" />\n </label>\n <div class=\"x-art-f-row\">\n <label class=\"x-art-f\">\n Category\n <select [(ngModel)]=\"selected.category\">\n @for (c of categories; track c) { <option [value]=\"c\">{{ c }}</option> }\n </select>\n </label>\n <label class=\"x-art-f\">\n Author\n <input type=\"text\" [(ngModel)]=\"selected.authorName\" />\n </label>\n <label class=\"x-art-f\">\n Read minutes\n <input type=\"number\" [(ngModel)]=\"selected.readMinutes\" />\n </label>\n </div>\n <label class=\"x-art-f\">\n Tags (comma-separated)\n <input type=\"text\" [(ngModel)]=\"tagsInput\" />\n </label>\n <div class=\"x-art-f-row\">\n <label class=\"x-art-f\">\n Status\n <select [(ngModel)]=\"selected.status\">\n @for (s of statuses; track s) { <option [value]=\"s\">{{ s }}</option> }\n </select>\n </label>\n <label class=\"x-art-f\">\n Publish at\n <input type=\"datetime-local\" [(ngModel)]=\"selected.publishAt\" />\n </label>\n </div>\n <label class=\"x-art-f\">\n Body\n <x-rich-editor [content]=\"selected.body || ''\" (contentChange)=\"onBodyChange($event)\"></x-rich-editor>\n </label>\n <div class=\"x-art-actions\">\n <button type=\"button\" class=\"x-art-btn x-art-btn-primary\" (click)=\"save()\">Save</button>\n <button type=\"button\" class=\"x-art-btn x-art-danger\" (click)=\"remove()\">Delete</button>\n </div>\n }\n </section>\n </div>\n}\n\n<style>\n .x-art-head { margin-bottom: 20px; }\n .x-art-title { font-size: 24px; font-weight: 800; color: var(--color-text); }\n .x-art-sub { font-size: 13.5px; color: var(--color-text-muted); margin-top: 2px; }\n .x-art-empty { font-size: 14px; color: var(--color-text-muted); padding: 12px 0; }\n .x-art-layout { display: grid; grid-template-columns: 260px 1fr; gap: 20px; align-items: start; }\n .x-art-list { display: flex; flex-direction: column; gap: 6px; }\n .x-art-new { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }\n .x-art-new input, .x-art-f input, .x-art-f select {\n border: 1px solid var(--color-border); border-radius: var(--radius-md);\n padding: 6px 10px; font-size: 13px; background: var(--color-surface); color: var(--color-text);\n }\n .x-art-new button, .x-art-btn {\n border: 1px solid var(--color-border); background: transparent; border-radius: var(--radius-md);\n padding: 6px 14px; font-size: 12.5px; font-weight: 700; cursor: pointer; color: var(--color-text-muted);\n }\n .x-art-btn-primary { border-color: var(--color-accent); color: var(--color-accent-hover); }\n .x-art-danger { color: var(--color-danger, #c0362c); }\n .x-art-list-item {\n text-align: left; border: 1px solid var(--color-border); border-radius: var(--radius-md);\n background: var(--color-surface); padding: 8px 10px; cursor: pointer;\n display: flex; flex-direction: column; gap: 2px;\n }\n .x-art-list-active { border-color: var(--color-accent); }\n .x-art-list-title { font-size: 13px; font-weight: 700; color: var(--color-text); }\n .x-art-list-meta { font-size: 11px; color: var(--color-text-muted); }\n .x-art-editor {\n min-width: 0; background: var(--color-surface); border: 1px solid var(--color-border);\n border-radius: var(--radius-lg); padding: 16px; display: flex; flex-direction: column; gap: 10px;\n }\n .x-art-f { display: flex; flex-direction: column; gap: 4px; flex: 1; font-size: 11.5px; color: var(--color-text-muted); font-weight: 700; }\n .x-art-f-row { display: flex; gap: 10px; }\n .x-art-actions { display: flex; gap: 8px; }\n x-rich-editor { display: block; border: 1px solid var(--color-border); border-radius: var(--radius-md); min-height: 200px; }\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 { RichEditorModule } from '../_shared/rich-editor/rich-editor.module';\nimport { ArticlesComponent } from './articles.component';\n\n@NgModule({\n declarations: [ArticlesComponent],\n imports: [\n SharedModule,\n ElementsModule,\n RichEditorModule,\n RouterModule.forChild([{ path: '', component: ArticlesComponent }]),\n ],\n})\nexport class ArticlesModule {}\n"],"names":["i3.RichEditorComponent","i1"],"mappings":"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"}
@@ -0,0 +1,239 @@
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 { BaseDirective, using, notify, SharedModule } from '@thescaffold/ngx-core';
6
+ import * as i2 from '@thescaffold/ngx-ui';
7
+ import { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';
8
+ import { takeUntil, firstValueFrom } from 'rxjs';
9
+ import * as i1 from '@angular/forms';
10
+
11
+ const DATA_TYPES = ['number', 'range', 'enum', 'boolean', 'text'];
12
+ // R1 production-readiness pass — CAT-04. Previously missing entirely:
13
+ // the Catalogue product editor resolves and reads a category's
14
+ // attribute template, but there was no page to author one — every spec
15
+ // field ops needed had to be seeded via a migration. This is a
16
+ // template-level list + a nested, inline-editable table of its
17
+ // definitions (add/edit/remove a row directly — no separate save step
18
+ // per field beyond that row's own "Save").
19
+ class AttributeTemplatesComponent extends BaseDirective {
20
+ constructor() {
21
+ super(...arguments);
22
+ this.templateSource = this.apiService.on('apps/commerce/attribute-template');
23
+ this.definitionSource = this.apiService.on('apps/commerce/attribute-definition');
24
+ this.categorySource = this.apiService.on('apps/commerce/category');
25
+ this.dataTypes = DATA_TYPES;
26
+ this.loading = true;
27
+ this.templates = [];
28
+ this.categories = [];
29
+ this.selected = null;
30
+ this.definitions = [];
31
+ this.newTemplateName = '';
32
+ this.init = () => {
33
+ this.load();
34
+ this.loadCategories();
35
+ };
36
+ }
37
+ load() {
38
+ this.loading = true;
39
+ this.templateSource
40
+ .getMany({ page: 1, perPage: 200 })
41
+ .pipe(takeUntil(this.$))
42
+ .subscribe(using((response) => {
43
+ this.templates = (response?.data ?? []).sort((a, b) => String(a.name).localeCompare(String(b.name)));
44
+ this.loading = false;
45
+ if (this.selected) {
46
+ this.selected = this.templates.find((t) => t.id === this.selected.id) ?? null;
47
+ }
48
+ }));
49
+ }
50
+ loadCategories() {
51
+ this.categorySource
52
+ .getMany({ page: 1, perPage: 300 })
53
+ .pipe(takeUntil(this.$))
54
+ .subscribe(using((response) => {
55
+ this.categories = (response?.data ?? []).sort((a, b) => String(a.name).localeCompare(String(b.name)));
56
+ }));
57
+ }
58
+ async createTemplate() {
59
+ const name = this.newTemplateName.trim();
60
+ if (!name)
61
+ return;
62
+ try {
63
+ const res = await firstValueFrom(this.templateSource.create({ name }));
64
+ this.newTemplateName = '';
65
+ notify('Template created.', undefined, 'success');
66
+ this.load();
67
+ if (res?.data)
68
+ setTimeout(() => this.select({ ...res.data }), 200);
69
+ }
70
+ catch {
71
+ notify('Could not create the template.', undefined, 'error');
72
+ }
73
+ }
74
+ async saveTemplate() {
75
+ if (!this.selected)
76
+ return;
77
+ try {
78
+ await firstValueFrom(this.templateSource.update(this.selected.id, {
79
+ name: this.selected.name,
80
+ categoryId: this.selected.categoryId || undefined,
81
+ }));
82
+ notify('Template saved.', undefined, 'success');
83
+ this.load();
84
+ }
85
+ catch {
86
+ notify('Could not save the template.', undefined, 'error');
87
+ }
88
+ }
89
+ async removeTemplate() {
90
+ if (!this.selected)
91
+ return;
92
+ try {
93
+ await firstValueFrom(this.templateSource.remove(this.selected.id));
94
+ this.selected = null;
95
+ this.definitions = [];
96
+ notify('Template deleted.', undefined, 'success');
97
+ this.load();
98
+ }
99
+ catch {
100
+ notify('Could not delete — it may still be attached to a category.', undefined, 'error');
101
+ }
102
+ }
103
+ select(template) {
104
+ this.selected = template;
105
+ this.definitions = [];
106
+ this.definitionSource
107
+ .getMany({ templateId: template.id, page: 1, perPage: 200 })
108
+ .pipe(takeUntil(this.$))
109
+ .subscribe(using((response) => {
110
+ this.definitions = (response?.data ?? [])
111
+ .sort((a, b) => (a.position ?? 0) - (b.position ?? 0))
112
+ .map((d) => this.toVm(d));
113
+ }));
114
+ }
115
+ toVm(d) {
116
+ return {
117
+ id: d.id,
118
+ key: d.key ?? '',
119
+ label: d.label ?? '',
120
+ unit: d.unit ?? '',
121
+ dataType: d.dataType ?? 'number',
122
+ allowedValuesInput: (d.allowedValues ?? []).join(', '),
123
+ helpText: d.helpText ?? '',
124
+ plainLanguage: d.plainLanguage ?? '',
125
+ required: !!d.required,
126
+ filterable: !!d.filterable,
127
+ comparable: !!d.comparable,
128
+ showInSpecTable: d.showInSpecTable !== false,
129
+ group: d.group ?? '',
130
+ position: d.position ?? this.definitions.length,
131
+ };
132
+ }
133
+ addDefinition() {
134
+ this.definitions.push({
135
+ key: '',
136
+ label: '',
137
+ unit: '',
138
+ dataType: 'number',
139
+ allowedValuesInput: '',
140
+ helpText: '',
141
+ plainLanguage: '',
142
+ required: false,
143
+ filterable: false,
144
+ comparable: false,
145
+ showInSpecTable: true,
146
+ group: '',
147
+ position: this.definitions.length,
148
+ });
149
+ }
150
+ async saveDefinition(def) {
151
+ if (!this.selected || !def.key.trim() || !def.label.trim()) {
152
+ notify('Key and label are required.', undefined, 'error');
153
+ return;
154
+ }
155
+ def.saving = true;
156
+ try {
157
+ const payload = {
158
+ templateId: this.selected.id,
159
+ key: def.key.trim(),
160
+ label: def.label.trim(),
161
+ unit: def.unit?.trim() || undefined,
162
+ dataType: def.dataType,
163
+ allowedValues: def.dataType === 'enum'
164
+ ? def.allowedValuesInput
165
+ .split(',')
166
+ .map((v) => v.trim())
167
+ .filter(Boolean)
168
+ : undefined,
169
+ helpText: def.helpText?.trim() || undefined,
170
+ plainLanguage: def.plainLanguage?.trim() || undefined,
171
+ required: def.required,
172
+ filterable: def.filterable,
173
+ comparable: def.comparable,
174
+ showInSpecTable: def.showInSpecTable,
175
+ group: def.group?.trim() || undefined,
176
+ position: def.position,
177
+ };
178
+ if (def.id) {
179
+ await firstValueFrom(this.definitionSource.update(def.id, payload));
180
+ }
181
+ else {
182
+ const res = await firstValueFrom(this.definitionSource.create(payload));
183
+ def.id = res?.data?.id;
184
+ }
185
+ notify('Attribute saved.', undefined, 'success');
186
+ }
187
+ catch {
188
+ notify('Could not save the attribute.', undefined, 'error');
189
+ }
190
+ finally {
191
+ def.saving = false;
192
+ }
193
+ }
194
+ async removeDefinition(def) {
195
+ if (def.id) {
196
+ try {
197
+ await firstValueFrom(this.definitionSource.remove(def.id));
198
+ notify('Attribute deleted.', undefined, 'success');
199
+ }
200
+ catch {
201
+ notify('Could not delete the attribute.', undefined, 'error');
202
+ return;
203
+ }
204
+ }
205
+ this.definitions = this.definitions.filter((d) => d !== def);
206
+ }
207
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AttributeTemplatesComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
208
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: AttributeTemplatesComponent, isStandalone: false, selector: "x-attribute-templates", usesInheritance: true, ngImport: i0, template: "<div class=\"x-at-head\">\n <div class=\"x-at-title\">Attribute templates</div>\n <div class=\"x-at-sub\">Ordered spec-field sets \u2014 a category's template drives its product-editor attribute grid, storefront facets and PDP spec table.</div>\n</div>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-at-layout\">\n <aside class=\"x-at-list\">\n <div class=\"x-at-new\">\n <input type=\"text\" placeholder=\"New template name\" [(ngModel)]=\"newTemplateName\" />\n <button type=\"button\" (click)=\"createTemplate()\">Add template</button>\n </div>\n @for (t of templates; track t.id) {\n <button\n type=\"button\"\n class=\"x-at-list-item\"\n [class.x-at-list-active]=\"selected?.id === t.id\"\n (click)=\"select(t)\"\n >\n {{ t.name }}\n </button>\n } @empty {\n <div class=\"x-at-empty\">No templates yet.</div>\n }\n </aside>\n\n <section class=\"x-at-editor\">\n @if (!selected) {\n <div class=\"x-at-empty\">Pick a template, or add one.</div>\n } @else {\n <div class=\"x-at-f-row\">\n <label class=\"x-at-f\">\n Name\n <input type=\"text\" [(ngModel)]=\"selected.name\" />\n </label>\n <label class=\"x-at-f\">\n Authored for category (optional)\n <select [(ngModel)]=\"selected.categoryId\">\n <option value=\"\">\u2014 none \u2014</option>\n @for (c of categories; track c.id) { <option [value]=\"c.id\">{{ c.name }}</option> }\n </select>\n </label>\n </div>\n <div class=\"x-at-actions\">\n <button type=\"button\" class=\"x-at-btn x-at-btn-primary\" (click)=\"saveTemplate()\">Save template</button>\n <button type=\"button\" class=\"x-at-btn x-at-danger\" (click)=\"removeTemplate()\">Delete template</button>\n </div>\n <div class=\"x-at-hint\">\n To actually apply this template to a category (so its products render these\n fields), open the category in <a routerLink=\"/admin/categories\">Categories</a>\n and set its attribute template \u2014 a template can be authored ahead of that link.\n </div>\n\n <div class=\"x-at-defs\">\n <div class=\"x-at-defs-head\">Attributes ({{ definitions.length }})</div>\n @for (def of definitions; track $index) {\n <div class=\"x-at-def-row\">\n <div class=\"x-at-def-grid\">\n <label class=\"x-at-f\">\n Key\n <input type=\"text\" [(ngModel)]=\"def.key\" placeholder=\"e.g. voc\" />\n </label>\n <label class=\"x-at-f\">\n Label\n <input type=\"text\" [(ngModel)]=\"def.label\" placeholder=\"e.g. Open-circuit voltage\" />\n </label>\n <label class=\"x-at-f\">\n Unit\n <input type=\"text\" [(ngModel)]=\"def.unit\" placeholder=\"V\" />\n </label>\n <label class=\"x-at-f\">\n Data type\n <select [(ngModel)]=\"def.dataType\">\n @for (dt of dataTypes; track dt) { <option [value]=\"dt\">{{ dt }}</option> }\n </select>\n </label>\n <label class=\"x-at-f\">\n Group\n <input type=\"text\" [(ngModel)]=\"def.group\" placeholder=\"Electrical\" />\n </label>\n <label class=\"x-at-f\">\n Position\n <input type=\"number\" [(ngModel)]=\"def.position\" />\n </label>\n </div>\n @if (def.dataType === 'enum') {\n <label class=\"x-at-f x-at-f-wide\">\n Allowed values (comma-separated)\n <input type=\"text\" [(ngModel)]=\"def.allowedValuesInput\" placeholder=\"pure_sine, modified_sine\" />\n </label>\n }\n <label class=\"x-at-f x-at-f-wide\">\n Help text (shown next to the input in the editor)\n <input type=\"text\" [(ngModel)]=\"def.helpText\" />\n </label>\n <label class=\"x-at-f x-at-f-wide\">\n Plain-language gloss (shown to buyers on the PDP spec table)\n <input type=\"text\" [(ngModel)]=\"def.plainLanguage\" />\n </label>\n <div class=\"x-at-def-flags\">\n <label><input type=\"checkbox\" [(ngModel)]=\"def.required\" /> Required</label>\n <label><input type=\"checkbox\" [(ngModel)]=\"def.filterable\" /> Filterable</label>\n <label><input type=\"checkbox\" [(ngModel)]=\"def.comparable\" /> Comparable</label>\n <label><input type=\"checkbox\" [(ngModel)]=\"def.showInSpecTable\" /> Show in spec table</label>\n </div>\n <div class=\"x-at-def-actions\">\n <button type=\"button\" class=\"x-at-btn x-at-btn-primary\" [disabled]=\"def.saving\" (click)=\"saveDefinition(def)\">\n {{ def.saving ? 'Saving\u2026' : 'Save' }}\n </button>\n <button type=\"button\" class=\"x-at-btn x-at-danger\" (click)=\"removeDefinition(def)\">Remove</button>\n </div>\n </div>\n }\n <button type=\"button\" class=\"x-at-btn\" (click)=\"addDefinition()\">+ Add attribute</button>\n </div>\n }\n </section>\n </div>\n}\n", styles: [".x-at-head{margin-bottom:20px}.x-at-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-at-sub{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}.x-at-empty{font-size:14px;color:var(--color-text-muted);padding:12px 0}.x-at-layout{display:grid;grid-template-columns:260px 1fr;gap:20px;align-items:start}.x-at-list{display:flex;flex-direction:column;gap:6px}.x-at-new{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}.x-at-new input,.x-at-f input,.x-at-f select{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)}.x-at-new button,.x-at-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-at-btn:disabled{opacity:.5;cursor:default}.x-at-btn-primary{border-color:var(--color-accent);color:var(--color-accent-hover)}.x-at-danger{color:var(--color-danger, #c0362c)}.x-at-list-item{text-align:left;border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);padding:8px 10px;cursor:pointer;font-size:13px;font-weight:700;color:var(--color-text)}.x-at-list-active{border-color:var(--color-accent)}.x-at-editor{min-width:0;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;display:flex;flex-direction:column;gap:12px}.x-at-f{display:flex;flex-direction:column;gap:4px;flex:1;font-size:11.5px;color:var(--color-text-muted);font-weight:700}.x-at-f-row{display:flex;gap:10px}.x-at-f-wide{width:100%}.x-at-actions{display:flex;gap:8px}.x-at-hint{font-size:12px;color:var(--color-text-muted);font-style:italic}.x-at-hint a{color:var(--color-brand)}.x-at-defs{margin-top:8px;display:flex;flex-direction:column;gap:10px}.x-at-defs-head{font-size:13px;font-weight:800;color:var(--color-text);text-transform:uppercase;letter-spacing:.02em}.x-at-def-row{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:12px;display:flex;flex-direction:column;gap:10px;background:var(--color-surface-alt, rgba(0, 0, 0, .02))}.x-at-def-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:10px}.x-at-def-flags{display:flex;flex-wrap:wrap;gap:14px;font-size:12px;color:var(--color-text-muted)}.x-at-def-flags label{display:flex;align-items:center;gap:6px;font-weight:500}.x-at-def-actions{display:flex;gap:8px}\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: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: i2.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }] }); }
209
+ }
210
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AttributeTemplatesComponent, decorators: [{
211
+ type: Component,
212
+ args: [{ standalone: false, selector: 'x-attribute-templates', template: "<div class=\"x-at-head\">\n <div class=\"x-at-title\">Attribute templates</div>\n <div class=\"x-at-sub\">Ordered spec-field sets \u2014 a category's template drives its product-editor attribute grid, storefront facets and PDP spec table.</div>\n</div>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-at-layout\">\n <aside class=\"x-at-list\">\n <div class=\"x-at-new\">\n <input type=\"text\" placeholder=\"New template name\" [(ngModel)]=\"newTemplateName\" />\n <button type=\"button\" (click)=\"createTemplate()\">Add template</button>\n </div>\n @for (t of templates; track t.id) {\n <button\n type=\"button\"\n class=\"x-at-list-item\"\n [class.x-at-list-active]=\"selected?.id === t.id\"\n (click)=\"select(t)\"\n >\n {{ t.name }}\n </button>\n } @empty {\n <div class=\"x-at-empty\">No templates yet.</div>\n }\n </aside>\n\n <section class=\"x-at-editor\">\n @if (!selected) {\n <div class=\"x-at-empty\">Pick a template, or add one.</div>\n } @else {\n <div class=\"x-at-f-row\">\n <label class=\"x-at-f\">\n Name\n <input type=\"text\" [(ngModel)]=\"selected.name\" />\n </label>\n <label class=\"x-at-f\">\n Authored for category (optional)\n <select [(ngModel)]=\"selected.categoryId\">\n <option value=\"\">\u2014 none \u2014</option>\n @for (c of categories; track c.id) { <option [value]=\"c.id\">{{ c.name }}</option> }\n </select>\n </label>\n </div>\n <div class=\"x-at-actions\">\n <button type=\"button\" class=\"x-at-btn x-at-btn-primary\" (click)=\"saveTemplate()\">Save template</button>\n <button type=\"button\" class=\"x-at-btn x-at-danger\" (click)=\"removeTemplate()\">Delete template</button>\n </div>\n <div class=\"x-at-hint\">\n To actually apply this template to a category (so its products render these\n fields), open the category in <a routerLink=\"/admin/categories\">Categories</a>\n and set its attribute template \u2014 a template can be authored ahead of that link.\n </div>\n\n <div class=\"x-at-defs\">\n <div class=\"x-at-defs-head\">Attributes ({{ definitions.length }})</div>\n @for (def of definitions; track $index) {\n <div class=\"x-at-def-row\">\n <div class=\"x-at-def-grid\">\n <label class=\"x-at-f\">\n Key\n <input type=\"text\" [(ngModel)]=\"def.key\" placeholder=\"e.g. voc\" />\n </label>\n <label class=\"x-at-f\">\n Label\n <input type=\"text\" [(ngModel)]=\"def.label\" placeholder=\"e.g. Open-circuit voltage\" />\n </label>\n <label class=\"x-at-f\">\n Unit\n <input type=\"text\" [(ngModel)]=\"def.unit\" placeholder=\"V\" />\n </label>\n <label class=\"x-at-f\">\n Data type\n <select [(ngModel)]=\"def.dataType\">\n @for (dt of dataTypes; track dt) { <option [value]=\"dt\">{{ dt }}</option> }\n </select>\n </label>\n <label class=\"x-at-f\">\n Group\n <input type=\"text\" [(ngModel)]=\"def.group\" placeholder=\"Electrical\" />\n </label>\n <label class=\"x-at-f\">\n Position\n <input type=\"number\" [(ngModel)]=\"def.position\" />\n </label>\n </div>\n @if (def.dataType === 'enum') {\n <label class=\"x-at-f x-at-f-wide\">\n Allowed values (comma-separated)\n <input type=\"text\" [(ngModel)]=\"def.allowedValuesInput\" placeholder=\"pure_sine, modified_sine\" />\n </label>\n }\n <label class=\"x-at-f x-at-f-wide\">\n Help text (shown next to the input in the editor)\n <input type=\"text\" [(ngModel)]=\"def.helpText\" />\n </label>\n <label class=\"x-at-f x-at-f-wide\">\n Plain-language gloss (shown to buyers on the PDP spec table)\n <input type=\"text\" [(ngModel)]=\"def.plainLanguage\" />\n </label>\n <div class=\"x-at-def-flags\">\n <label><input type=\"checkbox\" [(ngModel)]=\"def.required\" /> Required</label>\n <label><input type=\"checkbox\" [(ngModel)]=\"def.filterable\" /> Filterable</label>\n <label><input type=\"checkbox\" [(ngModel)]=\"def.comparable\" /> Comparable</label>\n <label><input type=\"checkbox\" [(ngModel)]=\"def.showInSpecTable\" /> Show in spec table</label>\n </div>\n <div class=\"x-at-def-actions\">\n <button type=\"button\" class=\"x-at-btn x-at-btn-primary\" [disabled]=\"def.saving\" (click)=\"saveDefinition(def)\">\n {{ def.saving ? 'Saving\u2026' : 'Save' }}\n </button>\n <button type=\"button\" class=\"x-at-btn x-at-danger\" (click)=\"removeDefinition(def)\">Remove</button>\n </div>\n </div>\n }\n <button type=\"button\" class=\"x-at-btn\" (click)=\"addDefinition()\">+ Add attribute</button>\n </div>\n }\n </section>\n </div>\n}\n", styles: [".x-at-head{margin-bottom:20px}.x-at-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-at-sub{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}.x-at-empty{font-size:14px;color:var(--color-text-muted);padding:12px 0}.x-at-layout{display:grid;grid-template-columns:260px 1fr;gap:20px;align-items:start}.x-at-list{display:flex;flex-direction:column;gap:6px}.x-at-new{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}.x-at-new input,.x-at-f input,.x-at-f select{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)}.x-at-new button,.x-at-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-at-btn:disabled{opacity:.5;cursor:default}.x-at-btn-primary{border-color:var(--color-accent);color:var(--color-accent-hover)}.x-at-danger{color:var(--color-danger, #c0362c)}.x-at-list-item{text-align:left;border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);padding:8px 10px;cursor:pointer;font-size:13px;font-weight:700;color:var(--color-text)}.x-at-list-active{border-color:var(--color-accent)}.x-at-editor{min-width:0;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;display:flex;flex-direction:column;gap:12px}.x-at-f{display:flex;flex-direction:column;gap:4px;flex:1;font-size:11.5px;color:var(--color-text-muted);font-weight:700}.x-at-f-row{display:flex;gap:10px}.x-at-f-wide{width:100%}.x-at-actions{display:flex;gap:8px}.x-at-hint{font-size:12px;color:var(--color-text-muted);font-style:italic}.x-at-hint a{color:var(--color-brand)}.x-at-defs{margin-top:8px;display:flex;flex-direction:column;gap:10px}.x-at-defs-head{font-size:13px;font-weight:800;color:var(--color-text);text-transform:uppercase;letter-spacing:.02em}.x-at-def-row{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:12px;display:flex;flex-direction:column;gap:10px;background:var(--color-surface-alt, rgba(0, 0, 0, .02))}.x-at-def-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:10px}.x-at-def-flags{display:flex;flex-wrap:wrap;gap:14px;font-size:12px;color:var(--color-text-muted)}.x-at-def-flags label{display:flex;align-items:center;gap:6px;font-weight:500}.x-at-def-actions{display:flex;gap:8px}\n"] }]
213
+ }] });
214
+
215
+ class AttributeTemplatesModule {
216
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AttributeTemplatesModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
217
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: AttributeTemplatesModule, declarations: [AttributeTemplatesComponent], imports: [SharedModule,
218
+ ElementsModule,
219
+ WebgetsModule, i1$1.RouterModule] }); }
220
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AttributeTemplatesModule, imports: [SharedModule,
221
+ ElementsModule,
222
+ WebgetsModule,
223
+ RouterModule.forChild([{ path: '', component: AttributeTemplatesComponent }])] }); }
224
+ }
225
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AttributeTemplatesModule, decorators: [{
226
+ type: NgModule,
227
+ args: [{
228
+ declarations: [AttributeTemplatesComponent],
229
+ imports: [
230
+ SharedModule,
231
+ ElementsModule,
232
+ WebgetsModule,
233
+ RouterModule.forChild([{ path: '', component: AttributeTemplatesComponent }]),
234
+ ],
235
+ }]
236
+ }] });
237
+
238
+ export { AttributeTemplatesModule };
239
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-attribute-templates.module-CwQnW1cH.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-attribute-templates.module-CwQnW1cH.mjs","sources":["../../../projects/admin/src/lib/pages/attribute-templates/attribute-templates.component.ts","../../../projects/admin/src/lib/pages/attribute-templates/attribute-templates.component.html","../../../projects/admin/src/lib/pages/attribute-templates/attribute-templates.module.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { BaseDirective, notify, using } from '@thescaffold/ngx-core';\nimport { firstValueFrom, takeUntil } from 'rxjs';\n\ninterface TemplateVm {\n id: string;\n name: string;\n categoryId?: string;\n}\n\ninterface DefinitionVm {\n id?: string;\n key: string;\n label: string;\n unit?: string;\n dataType: string;\n allowedValuesInput: string;\n helpText?: string;\n plainLanguage?: string;\n required: boolean;\n filterable: boolean;\n comparable: boolean;\n showInSpecTable: boolean;\n group?: string;\n position: number;\n saving?: boolean;\n}\n\nconst DATA_TYPES = ['number', 'range', 'enum', 'boolean', 'text'];\n\n// R1 production-readiness pass — CAT-04. Previously missing entirely:\n// the Catalogue product editor resolves and reads a category's\n// attribute template, but there was no page to author one — every spec\n// field ops needed had to be seeded via a migration. This is a\n// template-level list + a nested, inline-editable table of its\n// definitions (add/edit/remove a row directly — no separate save step\n// per field beyond that row's own \"Save\").\n@Component({\n standalone: false,\n selector: 'x-attribute-templates',\n templateUrl: './attribute-templates.component.html',\n styleUrl: './attribute-templates.component.scss',\n})\nexport class AttributeTemplatesComponent extends BaseDirective {\n private readonly templateSource = this.apiService.on('apps/commerce/attribute-template');\n private readonly definitionSource = this.apiService.on('apps/commerce/attribute-definition');\n private readonly categorySource = this.apiService.on('apps/commerce/category');\n\n readonly dataTypes = DATA_TYPES;\n\n loading = true;\n templates: TemplateVm[] = [];\n categories: { id: string; name: string }[] = [];\n selected: TemplateVm | null = null;\n definitions: DefinitionVm[] = [];\n\n newTemplateName = '';\n\n override init = () => {\n this.load();\n this.loadCategories();\n };\n\n load(): void {\n this.loading = true;\n this.templateSource\n .getMany({ page: 1, perPage: 200 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((response: any) => {\n this.templates = (response?.data ?? []).sort((a: any, b: any) =>\n String(a.name).localeCompare(String(b.name)),\n );\n this.loading = false;\n if (this.selected) {\n this.selected = this.templates.find((t) => t.id === this.selected!.id) ?? null;\n }\n }),\n );\n }\n\n loadCategories(): void {\n this.categorySource\n .getMany({ page: 1, perPage: 300 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((response: any) => {\n this.categories = (response?.data ?? []).sort((a: any, b: any) =>\n String(a.name).localeCompare(String(b.name)),\n );\n }),\n );\n }\n\n async createTemplate(): Promise<void> {\n const name = this.newTemplateName.trim();\n if (!name) return;\n try {\n const res: any = await firstValueFrom(this.templateSource.create({ name } as any));\n this.newTemplateName = '';\n notify('Template created.', undefined, 'success');\n this.load();\n if (res?.data) setTimeout(() => this.select({ ...res.data }), 200);\n } catch {\n notify('Could not create the template.', undefined, 'error');\n }\n }\n\n async saveTemplate(): Promise<void> {\n if (!this.selected) return;\n try {\n await firstValueFrom(\n this.templateSource.update(this.selected.id, {\n name: this.selected.name,\n categoryId: this.selected.categoryId || undefined,\n } as any),\n );\n notify('Template saved.', undefined, 'success');\n this.load();\n } catch {\n notify('Could not save the template.', undefined, 'error');\n }\n }\n\n async removeTemplate(): Promise<void> {\n if (!this.selected) return;\n try {\n await firstValueFrom(this.templateSource.remove(this.selected.id));\n this.selected = null;\n this.definitions = [];\n notify('Template deleted.', undefined, 'success');\n this.load();\n } catch {\n notify('Could not delete — it may still be attached to a category.', undefined, 'error');\n }\n }\n\n select(template: TemplateVm): void {\n this.selected = template;\n this.definitions = [];\n this.definitionSource\n .getMany({ templateId: template.id, page: 1, perPage: 200 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((response: any) => {\n this.definitions = ((response?.data ?? []) as any[])\n .sort((a, b) => (a.position ?? 0) - (b.position ?? 0))\n .map((d) => this.toVm(d));\n }),\n );\n }\n\n private toVm(d: any): DefinitionVm {\n return {\n id: d.id,\n key: d.key ?? '',\n label: d.label ?? '',\n unit: d.unit ?? '',\n dataType: d.dataType ?? 'number',\n allowedValuesInput: (d.allowedValues ?? []).join(', '),\n helpText: d.helpText ?? '',\n plainLanguage: d.plainLanguage ?? '',\n required: !!d.required,\n filterable: !!d.filterable,\n comparable: !!d.comparable,\n showInSpecTable: d.showInSpecTable !== false,\n group: d.group ?? '',\n position: d.position ?? this.definitions.length,\n };\n }\n\n addDefinition(): void {\n this.definitions.push({\n key: '',\n label: '',\n unit: '',\n dataType: 'number',\n allowedValuesInput: '',\n helpText: '',\n plainLanguage: '',\n required: false,\n filterable: false,\n comparable: false,\n showInSpecTable: true,\n group: '',\n position: this.definitions.length,\n });\n }\n\n async saveDefinition(def: DefinitionVm): Promise<void> {\n if (!this.selected || !def.key.trim() || !def.label.trim()) {\n notify('Key and label are required.', undefined, 'error');\n return;\n }\n def.saving = true;\n try {\n const payload = {\n templateId: this.selected.id,\n key: def.key.trim(),\n label: def.label.trim(),\n unit: def.unit?.trim() || undefined,\n dataType: def.dataType,\n allowedValues:\n def.dataType === 'enum'\n ? def.allowedValuesInput\n .split(',')\n .map((v) => v.trim())\n .filter(Boolean)\n : undefined,\n helpText: def.helpText?.trim() || undefined,\n plainLanguage: def.plainLanguage?.trim() || undefined,\n required: def.required,\n filterable: def.filterable,\n comparable: def.comparable,\n showInSpecTable: def.showInSpecTable,\n group: def.group?.trim() || undefined,\n position: def.position,\n };\n\n if (def.id) {\n await firstValueFrom(this.definitionSource.update(def.id, payload as any));\n } else {\n const res: any = await firstValueFrom(this.definitionSource.create(payload as any));\n def.id = res?.data?.id;\n }\n notify('Attribute saved.', undefined, 'success');\n } catch {\n notify('Could not save the attribute.', undefined, 'error');\n } finally {\n def.saving = false;\n }\n }\n\n async removeDefinition(def: DefinitionVm): Promise<void> {\n if (def.id) {\n try {\n await firstValueFrom(this.definitionSource.remove(def.id));\n notify('Attribute deleted.', undefined, 'success');\n } catch {\n notify('Could not delete the attribute.', undefined, 'error');\n return;\n }\n }\n this.definitions = this.definitions.filter((d) => d !== def);\n }\n}\n","<div class=\"x-at-head\">\n <div class=\"x-at-title\">Attribute templates</div>\n <div class=\"x-at-sub\">Ordered spec-field sets — a category's template drives its product-editor attribute grid, storefront facets and PDP spec table.</div>\n</div>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-at-layout\">\n <aside class=\"x-at-list\">\n <div class=\"x-at-new\">\n <input type=\"text\" placeholder=\"New template name\" [(ngModel)]=\"newTemplateName\" />\n <button type=\"button\" (click)=\"createTemplate()\">Add template</button>\n </div>\n @for (t of templates; track t.id) {\n <button\n type=\"button\"\n class=\"x-at-list-item\"\n [class.x-at-list-active]=\"selected?.id === t.id\"\n (click)=\"select(t)\"\n >\n {{ t.name }}\n </button>\n } @empty {\n <div class=\"x-at-empty\">No templates yet.</div>\n }\n </aside>\n\n <section class=\"x-at-editor\">\n @if (!selected) {\n <div class=\"x-at-empty\">Pick a template, or add one.</div>\n } @else {\n <div class=\"x-at-f-row\">\n <label class=\"x-at-f\">\n Name\n <input type=\"text\" [(ngModel)]=\"selected.name\" />\n </label>\n <label class=\"x-at-f\">\n Authored for category (optional)\n <select [(ngModel)]=\"selected.categoryId\">\n <option value=\"\">— none —</option>\n @for (c of categories; track c.id) { <option [value]=\"c.id\">{{ c.name }}</option> }\n </select>\n </label>\n </div>\n <div class=\"x-at-actions\">\n <button type=\"button\" class=\"x-at-btn x-at-btn-primary\" (click)=\"saveTemplate()\">Save template</button>\n <button type=\"button\" class=\"x-at-btn x-at-danger\" (click)=\"removeTemplate()\">Delete template</button>\n </div>\n <div class=\"x-at-hint\">\n To actually apply this template to a category (so its products render these\n fields), open the category in <a routerLink=\"/admin/categories\">Categories</a>\n and set its attribute template — a template can be authored ahead of that link.\n </div>\n\n <div class=\"x-at-defs\">\n <div class=\"x-at-defs-head\">Attributes ({{ definitions.length }})</div>\n @for (def of definitions; track $index) {\n <div class=\"x-at-def-row\">\n <div class=\"x-at-def-grid\">\n <label class=\"x-at-f\">\n Key\n <input type=\"text\" [(ngModel)]=\"def.key\" placeholder=\"e.g. voc\" />\n </label>\n <label class=\"x-at-f\">\n Label\n <input type=\"text\" [(ngModel)]=\"def.label\" placeholder=\"e.g. Open-circuit voltage\" />\n </label>\n <label class=\"x-at-f\">\n Unit\n <input type=\"text\" [(ngModel)]=\"def.unit\" placeholder=\"V\" />\n </label>\n <label class=\"x-at-f\">\n Data type\n <select [(ngModel)]=\"def.dataType\">\n @for (dt of dataTypes; track dt) { <option [value]=\"dt\">{{ dt }}</option> }\n </select>\n </label>\n <label class=\"x-at-f\">\n Group\n <input type=\"text\" [(ngModel)]=\"def.group\" placeholder=\"Electrical\" />\n </label>\n <label class=\"x-at-f\">\n Position\n <input type=\"number\" [(ngModel)]=\"def.position\" />\n </label>\n </div>\n @if (def.dataType === 'enum') {\n <label class=\"x-at-f x-at-f-wide\">\n Allowed values (comma-separated)\n <input type=\"text\" [(ngModel)]=\"def.allowedValuesInput\" placeholder=\"pure_sine, modified_sine\" />\n </label>\n }\n <label class=\"x-at-f x-at-f-wide\">\n Help text (shown next to the input in the editor)\n <input type=\"text\" [(ngModel)]=\"def.helpText\" />\n </label>\n <label class=\"x-at-f x-at-f-wide\">\n Plain-language gloss (shown to buyers on the PDP spec table)\n <input type=\"text\" [(ngModel)]=\"def.plainLanguage\" />\n </label>\n <div class=\"x-at-def-flags\">\n <label><input type=\"checkbox\" [(ngModel)]=\"def.required\" /> Required</label>\n <label><input type=\"checkbox\" [(ngModel)]=\"def.filterable\" /> Filterable</label>\n <label><input type=\"checkbox\" [(ngModel)]=\"def.comparable\" /> Comparable</label>\n <label><input type=\"checkbox\" [(ngModel)]=\"def.showInSpecTable\" /> Show in spec table</label>\n </div>\n <div class=\"x-at-def-actions\">\n <button type=\"button\" class=\"x-at-btn x-at-btn-primary\" [disabled]=\"def.saving\" (click)=\"saveDefinition(def)\">\n {{ def.saving ? 'Saving…' : 'Save' }}\n </button>\n <button type=\"button\" class=\"x-at-btn x-at-danger\" (click)=\"removeDefinition(def)\">Remove</button>\n </div>\n </div>\n }\n <button type=\"button\" class=\"x-at-btn\" (click)=\"addDefinition()\">+ Add attribute</button>\n </div>\n }\n </section>\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 { AttributeTemplatesComponent } from './attribute-templates.component';\n\n@NgModule({\n declarations: [AttributeTemplatesComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: AttributeTemplatesComponent }]),\n ],\n})\nexport class AttributeTemplatesModule {}\n"],"names":["i2","i3","i1"],"mappings":"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"}
@@ -1,13 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Component, NgModule } from '@angular/core';
3
- import * as i1$2 from '@angular/router';
3
+ import * as i1$1 from '@angular/router';
4
4
  import { 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.6, ADM-14 / SEC-07 — the audit-log viewer. `ntx-apps-audit` records
13
13
  // every write that carries a request actor: all CrudControllerFactory
@@ -53,7 +53,7 @@ class AuditComponent extends BaseDirective {
53
53
  return Math.max(1, Math.ceil(this.total / this.perPage));
54
54
  }
55
55
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AuditComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
56
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: AuditComponent, isStandalone: false, selector: "x-audit", usesInheritance: true, ngImport: i0, template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">Audit log</div>\n <div class=\"x-page-subtitle\">\n Every privileged action, with its actor. Immutable (SEC-07).\n </div>\n</div>\n\n<input\n class=\"x-au-filter\"\n type=\"search\"\n placeholder=\"Filter this page\u2026\"\n [(ngModel)]=\"filter\"\n/>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-au-table-wrap\">\n <table class=\"x-au-table\">\n <thead>\n <tr>\n <th>When</th>\n <th>Action</th>\n <th>Entity</th>\n <th>Description</th>\n <th>Actor</th>\n </tr>\n </thead>\n <tbody>\n @for (l of shown; track l.id) {\n <tr>\n <td>{{ l.createdAt | date: 'short' }}</td>\n <td><span class=\"x-au-action\">{{ l.action || '\u2014' }}</span></td>\n <td>{{ l.entityName || l.service }}</td>\n <td>{{ l.desc || '\u2014' }}</td>\n <td class=\"x-au-actor\">{{ l.userId || '\u2014' }}</td>\n </tr>\n } @empty {\n <tr><td colspan=\"5\" class=\"x-au-empty\">No entries.</td></tr>\n }\n </tbody>\n </table>\n </div>\n\n <div class=\"x-au-pager\">\n <button type=\"button\" [disabled]=\"page <= 1\" (click)=\"load(page - 1)\">Prev</button>\n <span>Page {{ page }} of {{ pages }}</span>\n <button type=\"button\" [disabled]=\"page >= pages\" (click)=\"load(page + 1)\">Next</button>\n </div>\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; }\n .x-au-filter { width: 100%; max-width: 320px; font: inherit; padding: 8px 10px; margin-bottom: 14px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); }\n .x-au-table-wrap { overflow-x: auto; }\n .x-au-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }\n .x-au-table th, .x-au-table td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--color-border); vertical-align: top; }\n .x-au-table th { color: var(--color-text-muted); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; }\n .x-au-action { font-family: var(--font-mono, monospace); font-size: 11px; }\n .x-au-actor { font-family: var(--font-mono, monospace); font-size: 11px; color: var(--color-text-muted); }\n .x-au-empty { text-align: center; color: var(--color-text-muted); padding: 24px; }\n .x-au-pager { display: flex; gap: 12px; align-items: center; margin-top: 14px; font-size: 12.5px; color: var(--color-text-muted); }\n .x-au-pager button { font: inherit; font-weight: 600; padding: 5px 12px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); cursor: pointer; }\n .x-au-pager button:disabled { opacity: 0.4; cursor: not-allowed; }\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}.x-au-filter{width:100%;max-width:320px;font:inherit;padding:8px 10px;margin-bottom:14px;border:1px solid var(--color-border);border-radius:6px;background:var(--color-surface, #fff);color:var(--color-text)}.x-au-table-wrap{overflow-x:auto}.x-au-table{width:100%;border-collapse:collapse;font-size:12.5px}.x-au-table th,.x-au-table td{text-align:left;padding:7px 10px;border-bottom:1px solid var(--color-border);vertical-align:top}.x-au-table th{color:var(--color-text-muted);font-size:10.5px;text-transform:uppercase;letter-spacing:.03em}.x-au-action{font-family:var(--font-mono, monospace);font-size:11px}.x-au-actor{font-family:var(--font-mono, monospace);font-size:11px;color:var(--color-text-muted)}.x-au-empty{text-align:center;color:var(--color-text-muted);padding:24px}.x-au-pager{display:flex;gap:12px;align-items:center;margin-top:14px;font-size:12.5px;color:var(--color-text-muted)}.x-au-pager button{font:inherit;font-weight:600;padding:5px 12px;border:1px solid var(--color-border);border-radius:6px;background:var(--color-surface, #fff);color:var(--color-text);cursor:pointer}.x-au-pager button:disabled{opacity:.4;cursor:not-allowed}\n"], dependencies: [{ 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i1$1.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: AuditComponent, isStandalone: false, selector: "x-audit", usesInheritance: true, ngImport: i0, template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">Audit log</div>\n <div class=\"x-page-subtitle\">\n Every privileged action, with its actor. Immutable (SEC-07).\n </div>\n</div>\n\n<input\n class=\"x-au-filter\"\n type=\"search\"\n placeholder=\"Filter this page\u2026\"\n [(ngModel)]=\"filter\"\n/>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-au-table-wrap\">\n <table class=\"x-au-table\">\n <thead>\n <tr>\n <th>When</th>\n <th>Action</th>\n <th>Entity</th>\n <th>Description</th>\n <th>Actor</th>\n </tr>\n </thead>\n <tbody>\n @for (l of shown; track l.id) {\n <tr>\n <td>{{ l.createdAt | date: 'short' }}</td>\n <td><span class=\"x-au-action\">{{ l.action || '\u2014' }}</span></td>\n <td>{{ l.entityName || l.service }}</td>\n <td>{{ l.desc || '\u2014' }}</td>\n <td class=\"x-au-actor\">{{ l.userId || '\u2014' }}</td>\n </tr>\n } @empty {\n <tr><td colspan=\"5\" class=\"x-au-empty\">No entries.</td></tr>\n }\n </tbody>\n </table>\n </div>\n\n <div class=\"x-au-pager\">\n <button type=\"button\" [disabled]=\"page <= 1\" (click)=\"load(page - 1)\">Prev</button>\n <span>Page {{ page }} of {{ pages }}</span>\n <button type=\"button\" [disabled]=\"page >= pages\" (click)=\"load(page + 1)\">Next</button>\n </div>\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; }\n .x-au-filter { width: 100%; max-width: 320px; font: inherit; padding: 8px 10px; margin-bottom: 14px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); }\n .x-au-table-wrap { overflow-x: auto; }\n .x-au-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }\n .x-au-table th, .x-au-table td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--color-border); vertical-align: top; }\n .x-au-table th { color: var(--color-text-muted); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; }\n .x-au-action { font-family: var(--font-mono, monospace); font-size: 11px; }\n .x-au-actor { font-family: var(--font-mono, monospace); font-size: 11px; color: var(--color-text-muted); }\n .x-au-empty { text-align: center; color: var(--color-text-muted); padding: 24px; }\n .x-au-pager { display: flex; gap: 12px; align-items: center; margin-top: 14px; font-size: 12.5px; color: var(--color-text-muted); }\n .x-au-pager button { font: inherit; font-weight: 600; padding: 5px 12px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); cursor: pointer; }\n .x-au-pager button:disabled { opacity: 0.4; cursor: not-allowed; }\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}.x-au-filter{width:100%;max-width:320px;font:inherit;padding:8px 10px;margin-bottom:14px;border:1px solid var(--color-border);border-radius:6px;background:var(--color-surface, #fff);color:var(--color-text)}.x-au-table-wrap{overflow-x:auto}.x-au-table{width:100%;border-collapse:collapse;font-size:12.5px}.x-au-table th,.x-au-table td{text-align:left;padding:7px 10px;border-bottom:1px solid var(--color-border);vertical-align:top}.x-au-table th{color:var(--color-text-muted);font-size:10.5px;text-transform:uppercase;letter-spacing:.03em}.x-au-action{font-family:var(--font-mono, monospace);font-size:11px}.x-au-actor{font-family:var(--font-mono, monospace);font-size:11px;color:var(--color-text-muted)}.x-au-empty{text-align:center;color:var(--color-text-muted);padding:24px}.x-au-pager{display:flex;gap:12px;align-items:center;margin-top:14px;font-size:12.5px;color:var(--color-text-muted)}.x-au-pager button{font:inherit;font-weight:600;padding:5px 12px;border:1px solid var(--color-border);border-radius:6px;background:var(--color-surface, #fff);color:var(--color-text);cursor:pointer}.x-au-pager button:disabled{opacity:.4;cursor:not-allowed}\n"], dependencies: [{ 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.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: "pipe", type: i3.DatePipe, name: "date" }] }); }
57
57
  }
58
58
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AuditComponent, decorators: [{
59
59
  type: Component,
@@ -63,7 +63,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
63
63
  class AuditModule {
64
64
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AuditModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
65
65
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: AuditModule, declarations: [AuditComponent], imports: [SharedModule,
66
- ElementsModule, i1$2.RouterModule] }); }
66
+ ElementsModule, i1$1.RouterModule] }); }
67
67
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: AuditModule, imports: [SharedModule,
68
68
  ElementsModule,
69
69
  RouterModule.forChild([{ path: '', component: AuditComponent }])] }); }
@@ -81,4 +81,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
81
81
  }] });
82
82
 
83
83
  export { AuditModule };
84
- //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-audit.module-C9gvw8X2.mjs.map
84
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-audit.module-D5YaCThD.mjs.map