@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.
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-articles.module-OeLRnPwN.mjs → isaiahiroko-tilmart-ngx-apps-admin-articles.module-Cpx9BZcg.mjs} +5 -5
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-articles.module-OeLRnPwN.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-articles.module-Cpx9BZcg.mjs.map} +1 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-attribute-templates.module-CwQnW1cH.mjs +239 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-attribute-templates.module-CwQnW1cH.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-audit.module-C9gvw8X2.mjs → isaiahiroko-tilmart-ngx-apps-admin-audit.module-D5YaCThD.mjs} +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
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-banners.module-KSapnx6Y.mjs +76 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-banners.module-KSapnx6Y.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-brands.module-7I0ni-tQ.mjs +70 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-brands.module-7I0ni-tQ.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-DoxDNaNn.mjs +538 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-DoxDNaNn.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-categories.module-OTUfenf0.mjs +94 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-categories.module-OTUfenf0.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Dk9mz7Wj.mjs +137 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Dk9mz7Wj.mjs.map +1 -0
- 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
- 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
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-content.module-DLdKAUqc.mjs → isaiahiroko-tilmart-ngx-apps-admin-content.module-Bx6Zj0WX.mjs} +5 -5
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-content.module-DLdKAUqc.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-content.module-Bx6Zj0WX.mjs.map} +1 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CoRh6Hxq.mjs +85 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CoRh6Hxq.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-CslGiOdq.mjs +123 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-CslGiOdq.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-Bfgh3zE5.mjs → isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-CvFmMoJe.mjs} +6 -6
- 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
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-C6tPNlup.mjs → isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-CuZwNVPO.mjs} +6 -6
- 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
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-flash-sales.module-BPRnC17l.mjs +83 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-flash-sales.module-BPRnC17l.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-homepage.module-BZ3UmxE3.mjs → isaiahiroko-tilmart-ngx-apps-admin-homepage.module-vtAoIwSu.mjs} +5 -5
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-homepage.module-BZ3UmxE3.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-homepage.module-vtAoIwSu.mjs.map} +1 -1
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-inbox.module-BZDTa1L-.mjs → isaiahiroko-tilmart-ngx-apps-admin-inbox.module-CRkfjWQT.mjs} +6 -6
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-inbox.module-BZDTa1L-.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-inbox.module-CRkfjWQT.mjs.map} +1 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-list-page.base-Cl7JZ-Tp.mjs +66 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-list-page.base-Cl7JZ-Tp.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-macros.module-BO4bdER2.mjs → isaiahiroko-tilmart-ngx-apps-admin-macros.module-CElxL9W4.mjs} +5 -5
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-macros.module-BO4bdER2.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-macros.module-CElxL9W4.mjs.map} +1 -1
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-moderation.module-CZOCrias.mjs → isaiahiroko-tilmart-ngx-apps-admin-moderation.module-PGmFX7nk.mjs} +6 -6
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-moderation.module-CZOCrias.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-moderation.module-PGmFX7nk.mjs.map} +1 -1
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-navigation.module-DoKzzRgn.mjs → isaiahiroko-tilmart-ngx-apps-admin-navigation.module-De1Cvqqa.mjs} +6 -6
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-navigation.module-DoKzzRgn.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-navigation.module-De1Cvqqa.mjs.map} +1 -1
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-order-board.module-DIOjhptn.mjs → isaiahiroko-tilmart-ngx-apps-admin-order-board.module-CmLNiEEP.mjs} +4 -4
- 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
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-orders.module-Ddw49QW0.mjs → isaiahiroko-tilmart-ngx-apps-admin-orders.module-Cfeb0QjH.mjs} +15 -33
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-orders.module-Cfeb0QjH.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-DstLDYM7.mjs +114 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-DstLDYM7.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG03OY1q.mjs +94 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG03OY1q.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-queues.module-K-zYYVFy.mjs → isaiahiroko-tilmart-ngx-apps-admin-queues.module-D7iLwX0A.mjs} +6 -6
- 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
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-oQw5Xo5T.mjs +78 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-oQw5Xo5T.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-referrals.module-QLNhuh7q.mjs → isaiahiroko-tilmart-ngx-apps-admin-referrals.module-BIAY4L_u.mjs} +9 -34
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-referrals.module-BIAY4L_u.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-reports.module-DBZov0We.mjs → isaiahiroko-tilmart-ngx-apps-admin-reports.module-DORiIVLQ.mjs} +5 -5
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-reports.module-DBZov0We.mjs.map → isaiahiroko-tilmart-ngx-apps-admin-reports.module-DORiIVLQ.mjs.map} +1 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-C9Iu9CX6.mjs +163 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-C9Iu9CX6.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-CGBBHwId.mjs +81 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-CGBBHwId.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-Cz6xPNwB.mjs +318 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-Cz6xPNwB.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-shipments.module-XuaOKr8v.mjs → isaiahiroko-tilmart-ngx-apps-admin-shipments.module-Cs5CLamm.mjs} +13 -32
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-shipments.module-Cs5CLamm.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs +140 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-Bt9W1oGR.mjs +102 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-Bt9W1oGR.mjs.map +1 -0
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-trace.module-Pwr-82Rc.mjs → isaiahiroko-tilmart-ngx-apps-admin-trace.module-9sRlGZCC.mjs} +6 -6
- 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
- package/fesm2022/{isaiahiroko-tilmart-ngx-apps-admin-users.module-Bx_7fI2g.mjs → isaiahiroko-tilmart-ngx-apps-admin-users.module-C31RJJJ_.mjs} +9 -34
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-users.module-C31RJJJ_.mjs.map +1 -0
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin.mjs +96 -72
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin.mjs.map +1 -1
- package/package.json +1 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-baskets.module-_lHPl1dT.mjs +0 -294
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-baskets.module-_lHPl1dT.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-Bvx2tVOi.mjs +0 -224
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-Bvx2tVOi.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Ccsd8Yx3.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Ccsd8Yx3.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CQNCSlsd.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CQNCSlsd.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-12XdV9zo.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-12XdV9zo.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-orders.module-Ddw49QW0.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-BhqAtTZs.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-BhqAtTZs.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-procurement.module-qXW-g0pf.mjs +0 -274
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-procurement.module-qXW-g0pf.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG9uTDqX.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG9uTDqX.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-Hm2dO_bC.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-Hm2dO_bC.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-referrals.module-QLNhuh7q.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-Bd2gdo6_.mjs +0 -106
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-Bd2gdo6_.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-BkVY06ci.mjs +0 -105
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-BkVY06ci.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-DKeZTldO.mjs +0 -222
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-DKeZTldO.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-shipments.module-XuaOKr8v.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-DYiAkF7E.mjs +0 -107
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-DYiAkF7E.mjs.map +0 -1
- package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-users.module-Bx_7fI2g.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-catalog.module-DoxDNaNn.mjs","sources":["../../../projects/admin/src/lib/pages/catalog/catalog.component.ts","../../../projects/admin/src/lib/pages/catalog/catalog.component.html","../../../projects/admin/src/lib/pages/catalog/catalog.module.ts"],"sourcesContent":["import { Component, inject } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { BaseDirective, getEnv, notify, using } from '@thescaffold/ngx-core';\nimport { firstValueFrom, takeUntil } from 'rxjs';\n\n// `ProductMedia.blobId` (P1.3) resolves to `GET {API_BASE_URL}/apps/blobs/\n// download/:id` — same helper/reasoning as the buyer app's\n// `lib/data/media.ts#blobUrl` (confirmed against the real\n// `ntx-apps/libs/blobs` controller, not guessed).\nfunction resolveBlobUrl(blobId?: string | null): string {\n if (!blobId) return '';\n return `${getEnv('API_BASE_URL')}/apps/blobs/download/${blobId}`;\n}\n\n// `@thescaffold/jsx-blobs`'s own `upload()` helper is broken against this\n// platform's response envelope: its internal `init()`/`verify()` return\n// the raw `{status, title, message, data}` body but `upload()` then reads\n// `file.id` (not `file.data.id`) — so it always reports \"Failed to init\n// file upload\" even on a 201. Confirmed by driving the real upload flow\n// in a browser and reading the network log: `init` returns 201 with a\n// real file id, `upload()` still fails. Rather than depend on a broken\n// vendor convenience function, this reimplements the same three-call\n// contract (init → chunked batch → verify) directly against\n// `apiService`, which is also how P1's demo-media seed script validated\n// the contract in the first place, and gets Angular's existing\n// `JwtInterceptor` auth for free instead of hand-managing SDK config.\nasync function uploadFileToBlobs(\n apiService: { post: (path: string, body?: any) => any },\n file: File,\n parentId: string,\n tags: string[],\n): Promise<string> {\n const ext = file.name.split('.').pop()?.toLowerCase() || '';\n const mime = file.type || 'application/octet-stream';\n const meta = { type: ext, parentId, name: file.name, tags, size: file.size, mime };\n\n const initRes: any = await firstValueFrom(apiService.post('apps/blobs/upload/init', meta));\n const fileId = initRes?.data?.id;\n if (!fileId) throw new Error('Failed to init file upload');\n\n const chunkSize = 512 * 1024;\n const reader = file.stream().getReader();\n let index = 0;\n let buffer = new Uint8Array();\n\n const flushChunk = async (bytes: Uint8Array) => {\n await firstValueFrom(\n apiService.post('apps/blobs/upload/batch', {\n pages: [{ fileId, index: index++, raw: uint8ToBase64(bytes) }],\n }),\n );\n };\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n const combined = new Uint8Array(buffer.length + value.length);\n combined.set(buffer);\n combined.set(value, buffer.length);\n buffer = combined;\n while (buffer.length >= chunkSize) {\n await flushChunk(buffer.slice(0, chunkSize));\n buffer = buffer.slice(chunkSize);\n }\n }\n if (buffer.length > 0) await flushChunk(buffer);\n\n const verifyRes: any = await firstValueFrom(apiService.post('apps/blobs/upload/verify', meta));\n const blobId = verifyRes?.data?.id;\n if (!blobId) throw new Error('Failed to verify file upload');\n return blobId;\n}\n\nfunction uint8ToBase64(bytes: Uint8Array): string {\n let binary = '';\n const chunkSize = 0x8000;\n for (let i = 0; i < bytes.length; i += chunkSize) {\n binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));\n }\n return btoa(binary);\n}\n\ninterface PublishError {\n field: string;\n message: string;\n}\n\ninterface ProductRowVm {\n id: string;\n name: string;\n model: string;\n type: string;\n status: string;\n updatedAt: string;\n checking: boolean;\n checked: boolean;\n canPublish?: boolean;\n errors?: PublishError[];\n}\n\ninterface CategoryOption {\n id: string;\n name: string;\n slug: string;\n parentId?: string;\n}\n\ninterface BrandOption {\n id: string;\n name: string;\n}\n\ninterface AttributeDefVm {\n id: string;\n key: string;\n label: string;\n unit?: string;\n dataType: string;\n allowedValues?: string[];\n required: boolean;\n group?: string;\n}\n\ninterface AttributeValueVm {\n id?: string;\n valueNumber?: number | null;\n valueNumberHigh?: number | null;\n valueText?: string;\n valueBool?: boolean;\n}\n\ninterface MediaVm {\n id: string;\n blobId: string;\n altText: string;\n type: string;\n // `GET apps/blobs/download/:id` requires a bearer token, which a plain\n // `<img src>` can never send (browsers only attach cookies to image\n // requests, not custom headers) — confirmed by driving the real upload\n // flow in a browser: even a valid admin session gets a 401 loading the\n // thumbnail directly. Buyer's product-image rendering\n // (`lib/data/media.ts#blobUrl`, used the same naive way) has this same\n // bug; out of scope here, but worth flagging. This page instead fetches\n // the bytes through the authenticated `HttpClient` and renders a local\n // `blob:` object URL.\n objectUrl?: string;\n}\n\ninterface DraftProduct {\n id?: string;\n name: string;\n slug: string;\n model: string;\n brandId: string;\n primaryCategoryId: string;\n shortDesc: string;\n type: string;\n status: string;\n}\n\nconst PRODUCT_TYPES = ['simple', 'variant', 'kit'];\nconst PRODUCT_STATUSES = ['draft', 'active', 'archived', 'discontinued'];\n\n// A fixed \"folder\" id for every admin-uploaded product image.\n// `BlobsFiles.parentId` is NOT NULL in the DB despite the entity/DTO\n// marking it optional (`@IsOptional()`, `nullable: true`) — a genuine\n// vendor-package mismatch, found by running the demo-catalogue media seed\n// script against a live server and reading the resulting\n// QueryFailedError (see the commerce migrations' own notes). No FK is\n// declared on the column, so any UUID satisfies it.\nconst PRODUCT_MEDIA_PARENT_ID = '00000000-0000-4000-8000-000000000d30';\n\n// P1.15 (original slice) + R1 production-readiness pass (this rebuild).\n// The original version of this page was read-only: a product list plus\n// the CAT-03 publish-validation check, with no create/edit/media/\n// attribute/variant/pricing UI anywhere — \"no way to add items to\n// catalogue\" was the single most direct gap this session set out to\n// close. This keeps the publish-check list and adds a full create/edit\n// flow: basic info → category/brand → the category's resolved attribute\n// grid → one implicit variant (simple products; a per-variant grid for\n// `variant`-type products is real, isolated follow-up work, same\n// reasoning P1.15 originally gave for deferring it) → media upload → a\n// single default price + stock level. Search/refresh on the list are\n// real (not the generic `x-webget-collection` — this table needs a\n// bespoke \"Check\" action + inline errors the generic collection can't\n// render, so pagination/search are hand-wired here instead).\n@Component({\n standalone: false,\n selector: 'x-catalog',\n templateUrl: './catalog.component.html',\n styleUrl: './catalog.component.scss',\n})\nexport class CatalogComponent extends BaseDirective {\n private readonly http = inject(HttpClient);\n\n private readonly productSource = this.apiService.on('apps/commerce/product');\n private readonly categorySource = this.apiService.on('apps/commerce/category');\n private readonly brandSource = this.apiService.on('apps/commerce/brand');\n private readonly variantSource = this.apiService.on('apps/commerce/product-variant');\n private readonly attrValueSource = this.apiService.on('apps/commerce/product-attribute-value');\n private readonly mediaSource = this.apiService.on('apps/commerce/product-media');\n private readonly priceSource = this.apiService.on('apps/commerce/price');\n private readonly stockLevelSource = this.apiService.on('apps/commerce/stock-level');\n private readonly stockLocationSource = this.apiService.on('apps/commerce/stock-location');\n\n readonly types = PRODUCT_TYPES;\n readonly statuses = PRODUCT_STATUSES;\n\n loading = true;\n products: ProductRowVm[] = [];\n categories: CategoryOption[] = [];\n brands: BrandOption[] = [];\n stockLocations: { id: string; name: string }[] = [];\n\n query = '';\n page = 1;\n perPage = 25;\n total = 0;\n\n editing = false;\n saving = false;\n uploadingMedia = false;\n\n draft: DraftProduct | null = null;\n variant: { id?: string; sku: string; weight: number | null; handlingClass: string } | null = null;\n attributeDefs: AttributeDefVm[] = [];\n attributeValues: Record<string, AttributeValueVm> = {};\n media: MediaVm[] = [];\n price: { id?: string; amount: number | null } | null = null;\n stock: { id?: string; locationId: string; available: number } | null = null;\n\n override init = () => {\n this.loadProducts();\n this.loadCategories();\n this.loadBrands();\n this.loadStockLocations();\n };\n\n loadProducts(page = this.page): void {\n this.loading = true;\n this.page = page;\n const params: any = { page, perPage: this.perPage };\n if (this.query.trim()) params.query = this.query.trim();\n this.productSource\n .getMany(params)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((response: any) => {\n this.products = (response?.data ?? []).map(\n (p: any): ProductRowVm => ({\n id: p.id,\n name: p.name,\n model: p.model,\n type: p.type,\n status: p.status,\n updatedAt: p.updatedAt,\n checking: false,\n checked: false,\n }),\n );\n this.total = response?.meta?.total ?? this.products.length;\n this.loading = false;\n }),\n );\n }\n\n search(): void {\n this.loadProducts(1);\n }\n\n refresh(): void {\n this.loadProducts(this.page);\n }\n\n prevPage(): void {\n if (this.page > 1) this.loadProducts(this.page - 1);\n }\n\n nextPage(): void {\n if (this.page * this.perPage < this.total) this.loadProducts(this.page + 1);\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 loadBrands(): void {\n this.brandSource\n .getMany({ page: 1, perPage: 200 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((response: any) => {\n this.brands = (response?.data ?? []).sort((a: any, b: any) =>\n String(a.name).localeCompare(String(b.name)),\n );\n }),\n );\n }\n\n loadStockLocations(): void {\n this.stockLocationSource\n .getMany({ page: 1, perPage: 50 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(using((response: any) => (this.stockLocations = response?.data ?? [])));\n }\n\n checkPublish(product: ProductRowVm): void {\n product.checking = true;\n this.productSource\n .post(`apps/commerce/product/${product.id}/validate-publish`)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((response: any) => {\n product.checking = false;\n product.checked = true;\n product.canPublish = !!response?.data?.canPublish;\n product.errors = response?.data?.errors ?? [];\n }),\n );\n }\n\n startCreate(): void {\n this.editing = true;\n this.draft = {\n name: '',\n slug: '',\n model: '',\n brandId: '',\n primaryCategoryId: '',\n shortDesc: '',\n type: 'simple',\n status: 'draft',\n };\n this.variant = { sku: '', weight: null, handlingClass: 'standard' };\n this.attributeDefs = [];\n this.attributeValues = {};\n this.media = [];\n this.price = { amount: null };\n this.stock = { locationId: this.stockLocations[0]?.id ?? '', available: 0 };\n }\n\n async edit(row: ProductRowVm): Promise<void> {\n this.editing = true;\n const productRes: any = await firstValueFrom(this.productSource.getOne(row.id));\n const p = productRes?.data;\n this.draft = {\n id: p.id,\n name: p.name ?? '',\n slug: p.slug ?? '',\n model: p.model ?? '',\n brandId: p.brandId ?? '',\n primaryCategoryId: p.primaryCategoryId ?? '',\n shortDesc: p.shortDesc ?? '',\n type: p.type,\n status: p.status,\n };\n\n this.attributeValues = {};\n if (this.draft.primaryCategoryId) {\n await this.loadAttributeTemplate(this.draft.primaryCategoryId);\n } else {\n this.attributeDefs = [];\n }\n\n const [variantsRes, attrsRes, mediaRes] = await Promise.all([\n firstValueFrom(this.variantSource.getMany({ productId: p.id, page: 1, perPage: 50 } as any)),\n firstValueFrom(this.attrValueSource.getMany({ productId: p.id, page: 1, perPage: 200 } as any)),\n firstValueFrom(this.mediaSource.getMany({ productId: p.id, page: 1, perPage: 50 } as any)),\n ]);\n\n const firstVariant = (variantsRes as any)?.data?.[0];\n this.variant = firstVariant\n ? {\n id: firstVariant.id,\n sku: firstVariant.sku,\n weight: firstVariant.weight ?? null,\n handlingClass: firstVariant.handlingClass ?? 'standard',\n }\n : { sku: '', weight: null, handlingClass: 'standard' };\n\n for (const v of (attrsRes as any)?.data ?? []) {\n this.attributeValues[v.attributeDefinitionId] = {\n id: v.id,\n valueNumber: v.valueNumber,\n valueNumberHigh: v.valueNumberHigh,\n valueText: v.valueText,\n valueBool: v.valueBool,\n };\n }\n\n this.media = ((mediaRes as any)?.data ?? []).map((m: any) => ({\n id: m.id,\n blobId: m.blobId,\n altText: m.altText,\n type: m.type,\n }));\n for (const m of this.media) {\n m.objectUrl = await this.resolveMediaObjectUrl(m.blobId);\n }\n\n if (this.variant.id) {\n const priceRes: any = await firstValueFrom(\n this.priceSource.getMany({ variantId: this.variant.id, page: 1, perPage: 1 } as any),\n );\n const priceRow = priceRes?.data?.[0];\n this.price = priceRow ? { id: priceRow.id, amount: priceRow.amount } : { amount: null };\n\n const stockRes: any = await firstValueFrom(\n this.stockLevelSource.getMany({ variantId: this.variant.id, page: 1, perPage: 1 } as any),\n );\n const stockRow = stockRes?.data?.[0];\n this.stock = stockRow\n ? { id: stockRow.id, locationId: stockRow.locationId, available: stockRow.available }\n : { locationId: this.stockLocations[0]?.id ?? '', available: 0 };\n } else {\n this.price = { amount: null };\n this.stock = { locationId: this.stockLocations[0]?.id ?? '', available: 0 };\n }\n }\n\n async onCategoryChange(): Promise<void> {\n if (!this.draft?.primaryCategoryId) {\n this.attributeDefs = [];\n return;\n }\n await this.loadAttributeTemplate(this.draft.primaryCategoryId);\n }\n\n private async loadAttributeTemplate(categoryId: string): Promise<void> {\n try {\n const res: any = await firstValueFrom(\n this.apiService.get(`apps/commerce/category/${categoryId}/attribute-template`),\n );\n this.attributeDefs = (res?.data?.definitions ?? []) as AttributeDefVm[];\n for (const def of this.attributeDefs) {\n if (!this.attributeValues[def.id]) this.attributeValues[def.id] = {};\n }\n } catch {\n this.attributeDefs = [];\n }\n }\n\n async saveProduct(): Promise<void> {\n if (!this.draft || !this.draft.name.trim()) {\n notify('Name is required.', undefined, 'error');\n return;\n }\n this.saving = true;\n try {\n const payload: any = {\n name: this.draft.name.trim(),\n slug: this.draft.slug.trim() || undefined,\n brandId: this.draft.brandId || undefined,\n model: this.draft.model.trim() || undefined,\n primaryCategoryId: this.draft.primaryCategoryId || undefined,\n shortDesc: this.draft.shortDesc.trim() || undefined,\n type: this.draft.type,\n status: this.draft.status,\n };\n\n if (this.draft.id) {\n await firstValueFrom(this.productSource.update(this.draft.id, payload));\n } else {\n const res: any = await firstValueFrom(this.productSource.create(payload));\n this.draft.id = res?.data?.id;\n }\n const productId = this.draft.id!;\n\n if (this.variant && this.variant.sku.trim()) {\n const variantPayload: any = {\n productId,\n sku: this.variant.sku.trim(),\n weight: this.variant.weight != null ? Number(this.variant.weight) : undefined,\n handlingClass: this.variant.handlingClass || 'standard',\n };\n if (this.variant.id) {\n await firstValueFrom(this.variantSource.update(this.variant.id, variantPayload));\n } else {\n const vRes: any = await firstValueFrom(this.variantSource.create(variantPayload));\n this.variant.id = vRes?.data?.id;\n }\n }\n\n for (const def of this.attributeDefs) {\n const v = this.attributeValues[def.id];\n if (!v) continue;\n const hasValue =\n (v.valueNumber !== null && v.valueNumber !== undefined && `${v.valueNumber}` !== '') ||\n !!v.valueText ||\n v.valueBool !== null && v.valueBool !== undefined;\n if (!hasValue) continue;\n\n const attrPayload: any = {\n productId,\n attributeDefinitionId: def.id,\n valueNumber:\n def.dataType === 'number' || def.dataType === 'range'\n ? v.valueNumber != null && `${v.valueNumber}` !== ''\n ? Number(v.valueNumber)\n : undefined\n : undefined,\n valueNumberHigh:\n def.dataType === 'range' && v.valueNumberHigh != null && `${v.valueNumberHigh}` !== ''\n ? Number(v.valueNumberHigh)\n : undefined,\n valueText: def.dataType === 'text' || def.dataType === 'enum' ? v.valueText || undefined : undefined,\n valueBool: def.dataType === 'boolean' ? !!v.valueBool : undefined,\n };\n\n if (v.id) {\n await firstValueFrom(this.attrValueSource.update(v.id, attrPayload));\n } else {\n const aRes: any = await firstValueFrom(this.attrValueSource.create(attrPayload));\n v.id = aRes?.data?.id;\n }\n }\n\n if (this.variant?.id) {\n if (this.price && this.price.amount != null && `${this.price.amount}` !== '') {\n const pricePayload: any = {\n variantId: this.variant.id,\n amount: Number(this.price.amount),\n minQuantity: 1,\n };\n if (this.price.id) {\n await firstValueFrom(this.priceSource.update(this.price.id, pricePayload));\n } else {\n const prRes: any = await firstValueFrom(this.priceSource.create(pricePayload));\n this.price.id = prRes?.data?.id;\n }\n }\n\n if (this.stock?.locationId) {\n const stockPayload: any = {\n variantId: this.variant.id,\n locationId: this.stock.locationId,\n available: Number(this.stock.available ?? 0),\n };\n if (this.stock.id) {\n await firstValueFrom(this.stockLevelSource.update(this.stock.id, stockPayload));\n } else {\n const stRes: any = await firstValueFrom(this.stockLevelSource.create(stockPayload));\n this.stock.id = stRes?.data?.id;\n }\n }\n }\n\n notify('Product saved.', undefined, 'success');\n this.loadProducts(this.page);\n } catch {\n notify('Could not save the product (check required fields).', undefined, 'error');\n } finally {\n this.saving = false;\n }\n }\n\n async onMediaSelected(event: Event): Promise<void> {\n const input = event.target as HTMLInputElement;\n const files = input.files;\n if (!files || !files.length || !this.draft?.id) return;\n\n this.uploadingMedia = true;\n try {\n const blobId = await uploadFileToBlobs(this.apiService, files[0], PRODUCT_MEDIA_PARENT_ID, [\n 'product-media',\n ]);\n const altText = `${this.draft.name} — product photo`;\n const res: any = await firstValueFrom(\n this.mediaSource.create({\n productId: this.draft.id,\n blobId,\n type: 'gallery',\n altText,\n position: this.media.length,\n } as any),\n );\n const objectUrl = await this.resolveMediaObjectUrl(blobId);\n this.media.push({ id: res?.data?.id, blobId, altText, type: 'gallery', objectUrl });\n notify('Image uploaded.', undefined, 'success');\n } catch (e) {\n console.error('media upload failed', e);\n notify('Could not upload the image.', undefined, 'error');\n } finally {\n this.uploadingMedia = false;\n input.value = '';\n }\n }\n\n async removeMedia(item: MediaVm): Promise<void> {\n try {\n await firstValueFrom(this.mediaSource.remove(item.id));\n this.media = this.media.filter((m) => m.id !== item.id);\n } catch {\n notify('Could not remove the image.', undefined, 'error');\n }\n }\n\n // `download/:id` needs the bearer header only `HttpClient` (via the\n // app's existing `JwtInterceptor`) can attach — see the `MediaVm.\n // objectUrl` comment. Empty string on failure just leaves the\n // thumbnail blank rather than breaking the editor.\n private async resolveMediaObjectUrl(blobId: string): Promise<string> {\n try {\n const blob = await firstValueFrom(this.http.get(resolveBlobUrl(blobId), { responseType: 'blob' }));\n // Not `URL.createObjectURL` — the app's CSP `img-src` allows `data:`\n // but not `blob:`, so an object URL renders as a CSP violation\n // (confirmed by driving this in a browser). A data URI sidesteps it.\n return await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = reject;\n reader.readAsDataURL(blob);\n });\n } catch {\n return '';\n }\n }\n\n cancelEdit(): void {\n this.editing = false;\n this.draft = null;\n this.variant = null;\n this.attributeDefs = [];\n this.attributeValues = {};\n this.media = [];\n this.price = null;\n this.stock = null;\n }\n}\n","<div class=\"x-page-head\">\n <div class=\"x-page-title\">Catalogue</div>\n <div class=\"x-page-subtitle\">Products, variants, attributes, media, pricing and stock.</div>\n</div>\n\n@if (!editing) {\n <div class=\"x-cat-toolbar\">\n <input\n type=\"text\"\n class=\"x-cat-search\"\n placeholder=\"Search products…\"\n [(ngModel)]=\"query\"\n (keyup.enter)=\"search()\"\n />\n <button type=\"button\" class=\"x-cat-btn\" (click)=\"search()\">Search</button>\n <button type=\"button\" class=\"x-cat-btn\" (click)=\"refresh()\">Refresh</button>\n <div class=\"x-cat-spacer\"></div>\n <button type=\"button\" class=\"x-cat-btn x-cat-btn-primary\" (click)=\"startCreate()\">+ New product</button>\n </div>\n\n @if (loading) {\n <x-element-loader></x-element-loader>\n } @else {\n <div class=\"x-catalog-table\">\n <div class=\"x-catalog-row x-catalog-head\">\n <div class=\"x-catalog-col-name\">Product</div>\n <div class=\"x-catalog-col-type\">Type</div>\n <div class=\"x-catalog-col-status\">Status</div>\n <div class=\"x-catalog-col-check\">Publish check</div>\n <div class=\"x-catalog-col-action\"></div>\n </div>\n @for (product of products; track product.id) {\n <div class=\"x-catalog-row\">\n <div class=\"x-catalog-col-name\">\n {{ product.name }}\n @if (product.model) {\n <span class=\"x-catalog-model\">{{ product.model }}</span>\n }\n </div>\n <div class=\"x-catalog-col-type\">{{ product.type }}</div>\n <div class=\"x-catalog-col-status\">\n <span class=\"x-status-badge\" [class]=\"'x-status-' + product.status\">{{\n product.status\n }}</span>\n </div>\n <div class=\"x-catalog-col-check\">\n @if (product.checking) {\n <span class=\"x-check-pending\">Checking…</span>\n } @else if (product.checked && product.canPublish) {\n <span class=\"x-check-ok\">✓ Ready to publish</span>\n } @else if (product.checked && product.errors?.length) {\n <ul class=\"x-check-errors\">\n @for (err of product.errors; track err.field) {\n <li>{{ err.message }}</li>\n }\n </ul>\n }\n </div>\n <div class=\"x-catalog-col-action\">\n <button type=\"button\" class=\"x-cat-btn\" (click)=\"edit(product)\">Edit</button>\n <button\n type=\"button\"\n class=\"x-check-btn\"\n [disabled]=\"product.checking\"\n (click)=\"checkPublish(product)\"\n >\n Check\n </button>\n </div>\n </div>\n }\n @if (!products.length) {\n <div class=\"x-catalog-empty\">No products yet — use \"+ New product\" to add one.</div>\n }\n </div>\n\n <div class=\"x-cat-pager\">\n <button type=\"button\" class=\"x-cat-btn\" [disabled]=\"page <= 1\" (click)=\"prevPage()\">← Prev</button>\n <span class=\"x-cat-pager-label\">Page {{ page }} · {{ total }} total</span>\n <button type=\"button\" class=\"x-cat-btn\" [disabled]=\"page * perPage >= total\" (click)=\"nextPage()\">\n Next →\n </button>\n </div>\n }\n} @else if (draft) {\n <div class=\"x-cat-editor\">\n <div class=\"x-cat-editor-head\">\n <div class=\"x-cat-editor-title\">{{ draft.id ? 'Edit product' : 'New product' }}</div>\n <div class=\"x-cat-editor-actions\">\n <button type=\"button\" class=\"x-cat-btn\" (click)=\"cancelEdit()\">Close</button>\n <button type=\"button\" class=\"x-cat-btn x-cat-btn-primary\" [disabled]=\"saving\" (click)=\"saveProduct()\">\n {{ saving ? 'Saving…' : 'Save' }}\n </button>\n </div>\n </div>\n\n <section class=\"x-cat-section\">\n <div class=\"x-cat-section-title\">Basic info</div>\n <div class=\"x-cat-f-row\">\n <label class=\"x-cat-f\">\n Name\n <input type=\"text\" [(ngModel)]=\"draft.name\" />\n </label>\n <label class=\"x-cat-f\">\n Slug (optional)\n <input type=\"text\" [(ngModel)]=\"draft.slug\" placeholder=\"auto from name if blank\" />\n </label>\n </div>\n <div class=\"x-cat-f-row\">\n <label class=\"x-cat-f\">\n Model\n <input type=\"text\" [(ngModel)]=\"draft.model\" />\n </label>\n <label class=\"x-cat-f\">\n Type\n <select [(ngModel)]=\"draft.type\">\n @for (t of types; track t) { <option [value]=\"t\">{{ t }}</option> }\n </select>\n </label>\n <label class=\"x-cat-f\">\n Status\n <select [(ngModel)]=\"draft.status\">\n @for (s of statuses; track s) { <option [value]=\"s\">{{ s }}</option> }\n </select>\n </label>\n </div>\n @if (draft.type !== 'simple') {\n <div class=\"x-cat-hint\">\n \"{{ draft.type }}\" products still save through this one-variant form for now — a full\n per-variant / per-component editor is a follow-up.\n </div>\n }\n <label class=\"x-cat-f\">\n Short description\n <textarea rows=\"2\" [(ngModel)]=\"draft.shortDesc\"></textarea>\n </label>\n </section>\n\n <section class=\"x-cat-section\">\n <div class=\"x-cat-section-title\">Category & brand</div>\n <div class=\"x-cat-f-row\">\n <label class=\"x-cat-f\">\n Category\n <select [(ngModel)]=\"draft.primaryCategoryId\" (ngModelChange)=\"onCategoryChange()\">\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 <label class=\"x-cat-f\">\n Brand\n <select [(ngModel)]=\"draft.brandId\">\n <option value=\"\">— none —</option>\n @for (b of brands; track b.id) { <option [value]=\"b.id\">{{ b.name }}</option> }\n </select>\n </label>\n </div>\n </section>\n\n @if (attributeDefs.length) {\n <section class=\"x-cat-section\">\n <div class=\"x-cat-section-title\">Attributes</div>\n <div class=\"x-cat-attr-grid\">\n @for (def of attributeDefs; track def.id) {\n <label class=\"x-cat-f x-cat-attr\">\n {{ def.label }}{{ def.unit ? ' (' + def.unit + ')' : '' }}\n @if (def.required) {\n <span class=\"x-cat-required\">*</span>\n }\n @switch (def.dataType) {\n @case ('boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"attributeValues[def.id].valueBool\" />\n }\n @case ('enum') {\n <select [(ngModel)]=\"attributeValues[def.id].valueText\">\n <option value=\"\">— choose —</option>\n @for (opt of def.allowedValues; track opt) { <option [value]=\"opt\">{{ opt }}</option> }\n </select>\n }\n @case ('range') {\n <div class=\"x-cat-range\">\n <input type=\"number\" placeholder=\"min\" [(ngModel)]=\"attributeValues[def.id].valueNumber\" />\n <span>–</span>\n <input\n type=\"number\"\n placeholder=\"max\"\n [(ngModel)]=\"attributeValues[def.id].valueNumberHigh\"\n />\n </div>\n }\n @case ('number') {\n <input type=\"number\" [(ngModel)]=\"attributeValues[def.id].valueNumber\" />\n }\n @default {\n <input type=\"text\" [(ngModel)]=\"attributeValues[def.id].valueText\" />\n }\n }\n </label>\n }\n </div>\n </section>\n }\n\n <section class=\"x-cat-section\">\n <div class=\"x-cat-section-title\">Variant, price & stock</div>\n @if (variant) {\n <div class=\"x-cat-f-row\">\n <label class=\"x-cat-f\">\n SKU\n <input type=\"text\" [(ngModel)]=\"variant.sku\" />\n </label>\n <label class=\"x-cat-f\">\n Weight (kg)\n <input type=\"number\" [(ngModel)]=\"variant.weight\" />\n </label>\n <label class=\"x-cat-f\">\n Handling class\n <select [(ngModel)]=\"variant.handlingClass\">\n <option value=\"standard\">standard</option>\n <option value=\"bulky\">bulky</option>\n <option value=\"freight\">freight</option>\n <option value=\"hazmat\">hazmat</option>\n </select>\n </label>\n </div>\n }\n <div class=\"x-cat-f-row\">\n <label class=\"x-cat-f\">\n Price (₦)\n <input type=\"number\" [(ngModel)]=\"price!.amount\" [disabled]=\"!variant?.sku\" />\n </label>\n <label class=\"x-cat-f\">\n Stock location\n <select [(ngModel)]=\"stock!.locationId\" [disabled]=\"!variant?.sku\">\n @for (loc of stockLocations; track loc.id) { <option [value]=\"loc.id\">{{ loc.name }}</option> }\n </select>\n </label>\n <label class=\"x-cat-f\">\n Available\n <input type=\"number\" [(ngModel)]=\"stock!.available\" [disabled]=\"!variant?.sku\" />\n </label>\n </div>\n @if (!variant?.sku) {\n <div class=\"x-cat-hint\">Enter a SKU above, then price/stock can be saved.</div>\n }\n </section>\n\n <section class=\"x-cat-section\">\n <div class=\"x-cat-section-title\">Media</div>\n @if (!draft.id) {\n <div class=\"x-cat-hint\">Save the product once first — media needs a real product to attach to.</div>\n } @else {\n <div class=\"x-cat-media-grid\">\n @for (m of media; track m.id) {\n <div class=\"x-cat-media-item\">\n <img [src]=\"m.objectUrl\" [alt]=\"m.altText\" />\n <button type=\"button\" class=\"x-cat-media-remove\" (click)=\"removeMedia(m)\">Remove</button>\n </div>\n }\n </div>\n <label class=\"x-cat-btn x-cat-upload\">\n {{ uploadingMedia ? 'Uploading…' : '+ Add image' }}\n <input type=\"file\" accept=\"image/*\" hidden [disabled]=\"uploadingMedia\" (change)=\"onMediaSelected($event)\" />\n </label>\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 { CatalogComponent } from './catalog.component';\n\n@NgModule({\n declarations: [CatalogComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: CatalogComponent }]),\n ],\n})\nexport class CatalogModule {}\n"],"names":["i1"],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Component, NgModule } from '@angular/core';
|
|
3
|
+
import * as i1$1 from '@angular/router';
|
|
4
|
+
import { RouterModule } from '@angular/router';
|
|
5
|
+
import { using, SharedModule } from '@thescaffold/ngx-core';
|
|
6
|
+
import * as i2 from '@thescaffold/ngx-ui';
|
|
7
|
+
import { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';
|
|
8
|
+
import { takeUntil } from 'rxjs';
|
|
9
|
+
import { S as SimpleCrudDirective } from './isaiahiroko-tilmart-ngx-apps-admin-simple-crud.base-C8SPw14H.mjs';
|
|
10
|
+
import * as i1 from '@angular/forms';
|
|
11
|
+
import * as i3 from '@angular/common';
|
|
12
|
+
|
|
13
|
+
// R1 production-readiness pass — CAT-01. Previously missing entirely:
|
|
14
|
+
// the Catalogue product editor and the homepage/content pages all read
|
|
15
|
+
// categories to populate dropdowns, but there was no page to create,
|
|
16
|
+
// nest, reorder or attach an attribute template to one.
|
|
17
|
+
class CategoriesComponent extends SimpleCrudDirective {
|
|
18
|
+
constructor() {
|
|
19
|
+
super(...arguments);
|
|
20
|
+
this.templateSource = this.apiService.on('apps/commerce/attribute-template');
|
|
21
|
+
this.pageTitle = 'Categories';
|
|
22
|
+
this.pageSubtitle = 'The product taxonomy — nesting, display order, and which attribute template governs each category\'s spec fields.';
|
|
23
|
+
this.singularLabel = 'category';
|
|
24
|
+
this.pluralName = 'categories';
|
|
25
|
+
this.dataSource = this.apiService.on('apps/commerce/category');
|
|
26
|
+
this.columns = [
|
|
27
|
+
{ key: 'name', value: 'Name' },
|
|
28
|
+
{ key: 'slug', value: 'Slug' },
|
|
29
|
+
{ key: 'position', value: 'Position' },
|
|
30
|
+
];
|
|
31
|
+
this.fields = [
|
|
32
|
+
{ key: 'name', label: 'Name', type: 'text', required: true },
|
|
33
|
+
{ key: 'slug', label: 'Slug', type: 'text', required: true },
|
|
34
|
+
{ key: 'parentId', label: 'Parent category', type: 'select', optionsKey: 'categories' },
|
|
35
|
+
{ key: 'attributeTemplateId', label: 'Attribute template', type: 'select', optionsKey: 'templates' },
|
|
36
|
+
{ key: 'position', label: 'Position', type: 'number' },
|
|
37
|
+
{ key: 'description', label: 'Description', type: 'textarea' },
|
|
38
|
+
];
|
|
39
|
+
this.init = () => {
|
|
40
|
+
this.list();
|
|
41
|
+
this.loadOptions();
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
loadOptions() {
|
|
45
|
+
this.dataSource
|
|
46
|
+
.getMany({ page: 1, perPage: 300 })
|
|
47
|
+
.pipe(takeUntil(this.$))
|
|
48
|
+
.subscribe(using((response) => {
|
|
49
|
+
this.dynamicOptions['categories'] = (response?.data ?? [])
|
|
50
|
+
.sort((a, b) => String(a.name).localeCompare(String(b.name)))
|
|
51
|
+
.map((c) => ({ value: c.id, label: c.name }));
|
|
52
|
+
}));
|
|
53
|
+
this.templateSource
|
|
54
|
+
.getMany({ page: 1, perPage: 200 })
|
|
55
|
+
.pipe(takeUntil(this.$))
|
|
56
|
+
.subscribe(using((response) => {
|
|
57
|
+
this.dynamicOptions['templates'] = (response?.data ?? [])
|
|
58
|
+
.sort((a, b) => String(a.name).localeCompare(String(b.name)))
|
|
59
|
+
.map((t) => ({ value: t.id, label: t.name }));
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CategoriesComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
63
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: CategoriesComponent, isStandalone: false, selector: "x-categories", usesInheritance: true, ngImport: i0, template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">{{ pageTitle }}</div>\n <div class=\"x-page-subtitle\">{{ pageSubtitle }}</div>\n</div>\n\n@if (!editing) {\n <div class=\"x-scrud-toolbar\">\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" (click)=\"startCreate()\">+ New {{ singularLabel }}</button>\n </div>\n\n @if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n } @else {\n <x-element-loader></x-element-loader>\n }\n} @else if (draft) {\n <div class=\"x-scrud-editor\">\n <div class=\"x-scrud-editor-head\">\n <div class=\"x-scrud-editor-title\">{{ draft['id'] ? 'Edit' : 'New' }} {{ singularLabel }}</div>\n <div class=\"x-scrud-editor-actions\">\n @if (draft['id'] && !hideDelete) {\n <button type=\"button\" class=\"x-scrud-btn x-scrud-danger\" (click)=\"remove(draft); cancelEdit()\">Delete</button>\n }\n <button type=\"button\" class=\"x-scrud-btn\" (click)=\"cancelEdit()\">Close</button>\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" [disabled]=\"saving\" (click)=\"save()\">\n {{ saving ? 'Saving\u2026' : 'Save' }}\n </button>\n </div>\n </div>\n\n <div class=\"x-scrud-grid\">\n @for (f of fields; track f.key) {\n <label class=\"x-scrud-f\" [class.x-scrud-f-wide]=\"f.type === 'textarea' || f.type === 'json'\">\n {{ f.label }}\n @if (f.required) {\n <span class=\"x-scrud-required\">*</span>\n }\n @switch (f.type) {\n @case ('boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"draft[f.key]\" />\n }\n @case ('select') {\n <select [(ngModel)]=\"draft[f.key]\">\n <option value=\"\">\u2014 choose \u2014</option>\n @for (opt of (f.optionsKey ? dynamicOptions[f.optionsKey] : f.options); track opt.value) {\n <option [value]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @case ('textarea') {\n <textarea rows=\"3\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\"></textarea>\n }\n @case ('json') {\n <textarea rows=\"4\" class=\"x-scrud-json\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || '{}'\"></textarea>\n }\n @case ('number') {\n <input type=\"number\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n @case ('date') {\n <input type=\"datetime-local\" [(ngModel)]=\"draft[f.key]\" />\n }\n @default {\n <input type=\"text\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n }\n </label>\n }\n </div>\n </div>\n}\n", styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}.x-scrud-toolbar{display:flex;justify-content:flex-end;margin-bottom:14px}.x-scrud-btn{border:1px solid var(--color-border);background:transparent;border-radius:var(--radius-md);padding:6px 14px;font-size:12.5px;font-weight:700;cursor:pointer;color:var(--color-text-muted)}.x-scrud-btn:disabled{opacity:.5;cursor:default}.x-scrud-btn-primary{border-color:var(--color-accent);color:var(--color-accent-hover)}.x-scrud-danger{color:var(--color-danger, #c0362c)}.x-scrud-editor{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;display:flex;flex-direction:column;gap:14px}.x-scrud-editor-head{display:flex;align-items:center;justify-content:space-between}.x-scrud-editor-title{font-size:16px;font-weight:800;color:var(--color-text)}.x-scrud-editor-actions{display:flex;gap:8px}.x-scrud-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.x-scrud-f{display:flex;flex-direction:column;gap:4px;font-size:11.5px;color:var(--color-text-muted);font-weight:700}.x-scrud-f-wide{grid-column:1/-1}.x-scrud-f input,.x-scrud-f select,.x-scrud-f textarea{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:6px 10px;font-size:13px;background:var(--color-surface);color:var(--color-text);font-weight:400}.x-scrud-f input[type=checkbox]{width:18px;height:18px}.x-scrud-json{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.x-scrud-required{color:var(--color-danger, #c0362c)}\n"], dependencies: [{ kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i2.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "component", type: i2.CollectionComponent, selector: "x-webget-collection", inputs: ["data"], outputs: ["search", "filter", "refresh", "select", "page", "show"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }] }); }
|
|
64
|
+
}
|
|
65
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CategoriesComponent, decorators: [{
|
|
66
|
+
type: Component,
|
|
67
|
+
args: [{ standalone: false, selector: 'x-categories', template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">{{ pageTitle }}</div>\n <div class=\"x-page-subtitle\">{{ pageSubtitle }}</div>\n</div>\n\n@if (!editing) {\n <div class=\"x-scrud-toolbar\">\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" (click)=\"startCreate()\">+ New {{ singularLabel }}</button>\n </div>\n\n @if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n } @else {\n <x-element-loader></x-element-loader>\n }\n} @else if (draft) {\n <div class=\"x-scrud-editor\">\n <div class=\"x-scrud-editor-head\">\n <div class=\"x-scrud-editor-title\">{{ draft['id'] ? 'Edit' : 'New' }} {{ singularLabel }}</div>\n <div class=\"x-scrud-editor-actions\">\n @if (draft['id'] && !hideDelete) {\n <button type=\"button\" class=\"x-scrud-btn x-scrud-danger\" (click)=\"remove(draft); cancelEdit()\">Delete</button>\n }\n <button type=\"button\" class=\"x-scrud-btn\" (click)=\"cancelEdit()\">Close</button>\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" [disabled]=\"saving\" (click)=\"save()\">\n {{ saving ? 'Saving\u2026' : 'Save' }}\n </button>\n </div>\n </div>\n\n <div class=\"x-scrud-grid\">\n @for (f of fields; track f.key) {\n <label class=\"x-scrud-f\" [class.x-scrud-f-wide]=\"f.type === 'textarea' || f.type === 'json'\">\n {{ f.label }}\n @if (f.required) {\n <span class=\"x-scrud-required\">*</span>\n }\n @switch (f.type) {\n @case ('boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"draft[f.key]\" />\n }\n @case ('select') {\n <select [(ngModel)]=\"draft[f.key]\">\n <option value=\"\">\u2014 choose \u2014</option>\n @for (opt of (f.optionsKey ? dynamicOptions[f.optionsKey] : f.options); track opt.value) {\n <option [value]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @case ('textarea') {\n <textarea rows=\"3\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\"></textarea>\n }\n @case ('json') {\n <textarea rows=\"4\" class=\"x-scrud-json\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || '{}'\"></textarea>\n }\n @case ('number') {\n <input type=\"number\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n @case ('date') {\n <input type=\"datetime-local\" [(ngModel)]=\"draft[f.key]\" />\n }\n @default {\n <input type=\"text\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n }\n </label>\n }\n </div>\n </div>\n}\n", styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}.x-scrud-toolbar{display:flex;justify-content:flex-end;margin-bottom:14px}.x-scrud-btn{border:1px solid var(--color-border);background:transparent;border-radius:var(--radius-md);padding:6px 14px;font-size:12.5px;font-weight:700;cursor:pointer;color:var(--color-text-muted)}.x-scrud-btn:disabled{opacity:.5;cursor:default}.x-scrud-btn-primary{border-color:var(--color-accent);color:var(--color-accent-hover)}.x-scrud-danger{color:var(--color-danger, #c0362c)}.x-scrud-editor{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px;display:flex;flex-direction:column;gap:14px}.x-scrud-editor-head{display:flex;align-items:center;justify-content:space-between}.x-scrud-editor-title{font-size:16px;font-weight:800;color:var(--color-text)}.x-scrud-editor-actions{display:flex;gap:8px}.x-scrud-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}.x-scrud-f{display:flex;flex-direction:column;gap:4px;font-size:11.5px;color:var(--color-text-muted);font-weight:700}.x-scrud-f-wide{grid-column:1/-1}.x-scrud-f input,.x-scrud-f select,.x-scrud-f textarea{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:6px 10px;font-size:13px;background:var(--color-surface);color:var(--color-text);font-weight:400}.x-scrud-f input[type=checkbox]{width:18px;height:18px}.x-scrud-json{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.x-scrud-required{color:var(--color-danger, #c0362c)}\n"] }]
|
|
68
|
+
}] });
|
|
69
|
+
|
|
70
|
+
class CategoriesModule {
|
|
71
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CategoriesModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
72
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: CategoriesModule, declarations: [CategoriesComponent], imports: [SharedModule,
|
|
73
|
+
ElementsModule,
|
|
74
|
+
WebgetsModule, i1$1.RouterModule] }); }
|
|
75
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CategoriesModule, imports: [SharedModule,
|
|
76
|
+
ElementsModule,
|
|
77
|
+
WebgetsModule,
|
|
78
|
+
RouterModule.forChild([{ path: '', component: CategoriesComponent }])] }); }
|
|
79
|
+
}
|
|
80
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CategoriesModule, decorators: [{
|
|
81
|
+
type: NgModule,
|
|
82
|
+
args: [{
|
|
83
|
+
declarations: [CategoriesComponent],
|
|
84
|
+
imports: [
|
|
85
|
+
SharedModule,
|
|
86
|
+
ElementsModule,
|
|
87
|
+
WebgetsModule,
|
|
88
|
+
RouterModule.forChild([{ path: '', component: CategoriesComponent }]),
|
|
89
|
+
],
|
|
90
|
+
}]
|
|
91
|
+
}] });
|
|
92
|
+
|
|
93
|
+
export { CategoriesModule };
|
|
94
|
+
//# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-categories.module-OTUfenf0.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-categories.module-OTUfenf0.mjs","sources":["../../../projects/admin/src/lib/pages/categories/categories.component.ts","../../../projects/admin/src/lib/pages/_shared/simple-crud.component.html","../../../projects/admin/src/lib/pages/categories/categories.module.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { using } from '@thescaffold/ngx-core';\nimport { takeUntil } from 'rxjs';\nimport { CrudFieldSpec, SimpleCrudDirective } from '../_shared/simple-crud.base';\n\n// R1 production-readiness pass — CAT-01. Previously missing entirely:\n// the Catalogue product editor and the homepage/content pages all read\n// categories to populate dropdowns, but there was no page to create,\n// nest, reorder or attach an attribute template to one.\n@Component({\n standalone: false,\n selector: 'x-categories',\n templateUrl: '../_shared/simple-crud.component.html',\n styleUrl: '../_shared/simple-crud.component.scss',\n})\nexport class CategoriesComponent extends SimpleCrudDirective {\n private readonly templateSource = this.apiService.on('apps/commerce/attribute-template');\n\n protected pageTitle = 'Categories';\n protected pageSubtitle = 'The product taxonomy — nesting, display order, and which attribute template governs each category\\'s spec fields.';\n protected singularLabel = 'category';\n protected pluralName = 'categories';\n protected dataSource = this.apiService.on('apps/commerce/category');\n\n protected columns = [\n { key: 'name', value: 'Name' },\n { key: 'slug', value: 'Slug' },\n { key: 'position', value: 'Position' },\n ];\n\n protected fields: CrudFieldSpec[] = [\n { key: 'name', label: 'Name', type: 'text', required: true },\n { key: 'slug', label: 'Slug', type: 'text', required: true },\n { key: 'parentId', label: 'Parent category', type: 'select', optionsKey: 'categories' },\n { key: 'attributeTemplateId', label: 'Attribute template', type: 'select', optionsKey: 'templates' },\n { key: 'position', label: 'Position', type: 'number' },\n { key: 'description', label: 'Description', type: 'textarea' },\n ];\n\n override init = () => {\n this.list();\n this.loadOptions();\n };\n\n private loadOptions(): void {\n this.dataSource\n .getMany({ page: 1, perPage: 300 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((response: any) => {\n this.dynamicOptions['categories'] = (response?.data ?? [])\n .sort((a: any, b: any) => String(a.name).localeCompare(String(b.name)))\n .map((c: any) => ({ value: c.id, label: c.name }));\n }),\n );\n this.templateSource\n .getMany({ page: 1, perPage: 200 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((response: any) => {\n this.dynamicOptions['templates'] = (response?.data ?? [])\n .sort((a: any, b: any) => String(a.name).localeCompare(String(b.name)))\n .map((t: any) => ({ value: t.id, label: t.name }));\n }),\n );\n }\n}\n","<div class=\"x-page-head\">\n <div class=\"x-page-title\">{{ pageTitle }}</div>\n <div class=\"x-page-subtitle\">{{ pageSubtitle }}</div>\n</div>\n\n@if (!editing) {\n <div class=\"x-scrud-toolbar\">\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" (click)=\"startCreate()\">+ New {{ singularLabel }}</button>\n </div>\n\n @if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n } @else {\n <x-element-loader></x-element-loader>\n }\n} @else if (draft) {\n <div class=\"x-scrud-editor\">\n <div class=\"x-scrud-editor-head\">\n <div class=\"x-scrud-editor-title\">{{ draft['id'] ? 'Edit' : 'New' }} {{ singularLabel }}</div>\n <div class=\"x-scrud-editor-actions\">\n @if (draft['id'] && !hideDelete) {\n <button type=\"button\" class=\"x-scrud-btn x-scrud-danger\" (click)=\"remove(draft); cancelEdit()\">Delete</button>\n }\n <button type=\"button\" class=\"x-scrud-btn\" (click)=\"cancelEdit()\">Close</button>\n <button type=\"button\" class=\"x-scrud-btn x-scrud-btn-primary\" [disabled]=\"saving\" (click)=\"save()\">\n {{ saving ? 'Saving…' : 'Save' }}\n </button>\n </div>\n </div>\n\n <div class=\"x-scrud-grid\">\n @for (f of fields; track f.key) {\n <label class=\"x-scrud-f\" [class.x-scrud-f-wide]=\"f.type === 'textarea' || f.type === 'json'\">\n {{ f.label }}\n @if (f.required) {\n <span class=\"x-scrud-required\">*</span>\n }\n @switch (f.type) {\n @case ('boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"draft[f.key]\" />\n }\n @case ('select') {\n <select [(ngModel)]=\"draft[f.key]\">\n <option value=\"\">— choose —</option>\n @for (opt of (f.optionsKey ? dynamicOptions[f.optionsKey] : f.options); track opt.value) {\n <option [value]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @case ('textarea') {\n <textarea rows=\"3\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\"></textarea>\n }\n @case ('json') {\n <textarea rows=\"4\" class=\"x-scrud-json\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || '{}'\"></textarea>\n }\n @case ('number') {\n <input type=\"number\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n @case ('date') {\n <input type=\"datetime-local\" [(ngModel)]=\"draft[f.key]\" />\n }\n @default {\n <input type=\"text\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n }\n </label>\n }\n </div>\n </div>\n}\n","import { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { SharedModule } from '@thescaffold/ngx-core';\nimport { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';\nimport { CategoriesComponent } from './categories.component';\n\n@NgModule({\n declarations: [CategoriesComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: CategoriesComponent }]),\n ],\n})\nexport class CategoriesModule {}\n"],"names":["i1"],"mappings":"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"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, TemplateRef, ViewChild, Component, NgModule } from '@angular/core';
|
|
3
|
+
import * as i1$1 from '@angular/router';
|
|
4
|
+
import { RouterModule } from '@angular/router';
|
|
5
|
+
import { notify, SharedModule } from '@thescaffold/ngx-core';
|
|
6
|
+
import * as i2 from '@thescaffold/ngx-ui';
|
|
7
|
+
import { FlyoutService, ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';
|
|
8
|
+
import { firstValueFrom } from 'rxjs';
|
|
9
|
+
import { L as ListPageDirective } from './isaiahiroko-tilmart-ngx-apps-admin-list-page.base-Cl7JZ-Tp.mjs';
|
|
10
|
+
import * as i1 from '@angular/forms';
|
|
11
|
+
import * as i3 from '@angular/common';
|
|
12
|
+
|
|
13
|
+
// R1 production-readiness pass — RET-07. The backend workflow
|
|
14
|
+
// (`WarrantyWorkflow.triage`/`.resolve`) was already complete; ops just
|
|
15
|
+
// had no page to drive it — a bare list with zero actions.
|
|
16
|
+
class ClaimsComponent extends ListPageDirective {
|
|
17
|
+
constructor() {
|
|
18
|
+
super(...arguments);
|
|
19
|
+
this.flyoutService = inject(FlyoutService);
|
|
20
|
+
this.pluralName = 'warranty claims';
|
|
21
|
+
this.dataSource = this.apiService.on('apps/commerce/warranty-claim');
|
|
22
|
+
this.columns = [
|
|
23
|
+
{ key: 'warrantyId', value: 'Warranty' },
|
|
24
|
+
{ key: 'status', value: 'Status', type: 'class', class: 'badge text-bg-light border' },
|
|
25
|
+
{ key: 'triageOutcome', value: 'Triage' },
|
|
26
|
+
{ key: 'createdAt', value: 'Opened', type: 'date' },
|
|
27
|
+
];
|
|
28
|
+
this.detail = null;
|
|
29
|
+
this.saving = false;
|
|
30
|
+
this.triageOutcome = 'remote_fix';
|
|
31
|
+
this.triageNote = '';
|
|
32
|
+
this.resolution = 'repair';
|
|
33
|
+
this.resolveNote = '';
|
|
34
|
+
this.triageOutcomes = ['remote_fix', 'site_visit', 'repair', 'replace', 'reject', 'manufacturer'];
|
|
35
|
+
this.resolutions = ['repair', 'replace', 'reject'];
|
|
36
|
+
}
|
|
37
|
+
extraCollectionFields() {
|
|
38
|
+
return {
|
|
39
|
+
links: {
|
|
40
|
+
secondary: [{ title: 'View', fn: (_link, item) => this.view(item) }],
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async view(item) {
|
|
45
|
+
this.detail = null;
|
|
46
|
+
this.triageOutcome = 'remote_fix';
|
|
47
|
+
this.triageNote = '';
|
|
48
|
+
this.resolution = 'repair';
|
|
49
|
+
this.resolveNote = '';
|
|
50
|
+
this.flyoutService.open(this.detailFlyout);
|
|
51
|
+
const res = await firstValueFrom(this.dataSource.getOne(item.id));
|
|
52
|
+
this.detail = res?.data ?? item;
|
|
53
|
+
}
|
|
54
|
+
async saveTriage() {
|
|
55
|
+
if (!this.detail)
|
|
56
|
+
return;
|
|
57
|
+
this.saving = true;
|
|
58
|
+
try {
|
|
59
|
+
const res = await firstValueFrom(this.apiService.post(`apps/commerce/warranty-claim/${this.detail.id}/triage`, {
|
|
60
|
+
outcome: this.triageOutcome,
|
|
61
|
+
note: this.triageNote || undefined,
|
|
62
|
+
}));
|
|
63
|
+
if (res?.status === 'error') {
|
|
64
|
+
notify(res.message || 'Could not save the triage.', undefined, 'error');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
notify('Triage saved.', undefined, 'success');
|
|
68
|
+
this.detail = { ...this.detail, status: res?.data?.status ?? this.detail.status, triageOutcome: this.triageOutcome };
|
|
69
|
+
this.list(this.page, this.perPage);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
notify('Could not save the triage.', undefined, 'error');
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
this.saving = false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async resolveClaim() {
|
|
79
|
+
if (!this.detail)
|
|
80
|
+
return;
|
|
81
|
+
this.saving = true;
|
|
82
|
+
try {
|
|
83
|
+
const res = await firstValueFrom(this.apiService.post(`apps/commerce/warranty-claim/${this.detail.id}/resolve`, {
|
|
84
|
+
resolution: this.resolution,
|
|
85
|
+
note: this.resolveNote || undefined,
|
|
86
|
+
}));
|
|
87
|
+
if (res?.status === 'error') {
|
|
88
|
+
notify(res.message || 'Could not resolve the claim.', undefined, 'error');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
notify('Claim resolved.', undefined, 'success');
|
|
92
|
+
this.flyoutService.dismiss();
|
|
93
|
+
this.list(this.page, this.perPage);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
notify('Could not resolve the claim.', undefined, 'error');
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
this.saving = false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ClaimsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
103
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: ClaimsComponent, isStandalone: false, selector: "x-claims", viewQueries: [{ propertyName: "detailFlyout", first: true, predicate: ["detailFlyout"], descendants: true, read: TemplateRef, static: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">Warranty claims</div>\n <div class=\"x-page-subtitle\">Warranty claims \u2014 triage outcome, resolution and manufacturer refs.</div>\n</div>\n\n@if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n} @else {\n <x-element-loader></x-element-loader>\n}\n\n<ng-template #detailFlyout>\n <x-webget-flyout [data]=\"{ title: 'Warranty claim' }\">\n <div class=\"p-4 d-flex flex-column gap-3\">\n @if (detail) {\n <div>\n <span class=\"badge text-bg-light border\">{{ detail.status }}</span>\n @if (detail.triageOutcome) {\n <span class=\"badge text-bg-light border\">triage: {{ detail.triageOutcome }}</span>\n }\n @if (detail.installedByTilmart) {\n <span class=\"badge text-bg-light border\">Tilmart install</span>\n }\n </div>\n @if (detail.faultDescription) {\n <div class=\"small\">{{ detail.faultDescription }}</div>\n }\n @if (detail.manufacturerRef) {\n <div class=\"small text-secondary\">Manufacturer ref: {{ detail.manufacturerRef }}</div>\n }\n\n <hr class=\"my-1\" />\n\n <div>\n <div class=\"fw-bold small mb-1\">Triage</div>\n <div class=\"d-flex flex-wrap gap-2\">\n <select class=\"form-select form-select-sm\" style=\"width:auto\" [(ngModel)]=\"triageOutcome\">\n @for (o of triageOutcomes; track o) { <option [value]=\"o\">{{ o }}</option> }\n </select>\n <input type=\"text\" class=\"form-control form-control-sm\" placeholder=\"note\" [(ngModel)]=\"triageNote\" />\n </div>\n <button type=\"button\" class=\"btn btn-sm btn-outline-dark mt-2\" [disabled]=\"saving\" (click)=\"saveTriage()\">\n Save triage\n </button>\n </div>\n\n <hr class=\"my-1\" />\n\n <div>\n <div class=\"fw-bold small mb-1\">Resolve</div>\n <div class=\"d-flex flex-wrap gap-2\">\n <select class=\"form-select form-select-sm\" style=\"width:auto\" [(ngModel)]=\"resolution\">\n @for (r of resolutions; track r) { <option [value]=\"r\">{{ r }}</option> }\n </select>\n <input type=\"text\" class=\"form-control form-control-sm\" placeholder=\"note\" [(ngModel)]=\"resolveNote\" />\n </div>\n <button type=\"button\" class=\"btn btn-sm btn-dark mt-2\" [disabled]=\"saving\" (click)=\"resolveClaim()\">\n Resolve claim\n </button>\n </div>\n } @else {\n <x-element-loader></x-element-loader>\n }\n </div>\n </x-webget-flyout>\n</ng-template>\n", styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}\n"], dependencies: [{ kind: "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.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i2.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "component", type: i2.CollectionComponent, selector: "x-webget-collection", inputs: ["data"], outputs: ["search", "filter", "refresh", "select", "page", "show"] }, { kind: "component", type: i2.FlyoutComponent, selector: "x-webget-flyout", inputs: ["data"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }] }); }
|
|
104
|
+
}
|
|
105
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ClaimsComponent, decorators: [{
|
|
106
|
+
type: Component,
|
|
107
|
+
args: [{ standalone: false, selector: 'x-claims', template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">Warranty claims</div>\n <div class=\"x-page-subtitle\">Warranty claims \u2014 triage outcome, resolution and manufacturer refs.</div>\n</div>\n\n@if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n} @else {\n <x-element-loader></x-element-loader>\n}\n\n<ng-template #detailFlyout>\n <x-webget-flyout [data]=\"{ title: 'Warranty claim' }\">\n <div class=\"p-4 d-flex flex-column gap-3\">\n @if (detail) {\n <div>\n <span class=\"badge text-bg-light border\">{{ detail.status }}</span>\n @if (detail.triageOutcome) {\n <span class=\"badge text-bg-light border\">triage: {{ detail.triageOutcome }}</span>\n }\n @if (detail.installedByTilmart) {\n <span class=\"badge text-bg-light border\">Tilmart install</span>\n }\n </div>\n @if (detail.faultDescription) {\n <div class=\"small\">{{ detail.faultDescription }}</div>\n }\n @if (detail.manufacturerRef) {\n <div class=\"small text-secondary\">Manufacturer ref: {{ detail.manufacturerRef }}</div>\n }\n\n <hr class=\"my-1\" />\n\n <div>\n <div class=\"fw-bold small mb-1\">Triage</div>\n <div class=\"d-flex flex-wrap gap-2\">\n <select class=\"form-select form-select-sm\" style=\"width:auto\" [(ngModel)]=\"triageOutcome\">\n @for (o of triageOutcomes; track o) { <option [value]=\"o\">{{ o }}</option> }\n </select>\n <input type=\"text\" class=\"form-control form-control-sm\" placeholder=\"note\" [(ngModel)]=\"triageNote\" />\n </div>\n <button type=\"button\" class=\"btn btn-sm btn-outline-dark mt-2\" [disabled]=\"saving\" (click)=\"saveTriage()\">\n Save triage\n </button>\n </div>\n\n <hr class=\"my-1\" />\n\n <div>\n <div class=\"fw-bold small mb-1\">Resolve</div>\n <div class=\"d-flex flex-wrap gap-2\">\n <select class=\"form-select form-select-sm\" style=\"width:auto\" [(ngModel)]=\"resolution\">\n @for (r of resolutions; track r) { <option [value]=\"r\">{{ r }}</option> }\n </select>\n <input type=\"text\" class=\"form-control form-control-sm\" placeholder=\"note\" [(ngModel)]=\"resolveNote\" />\n </div>\n <button type=\"button\" class=\"btn btn-sm btn-dark mt-2\" [disabled]=\"saving\" (click)=\"resolveClaim()\">\n Resolve claim\n </button>\n </div>\n } @else {\n <x-element-loader></x-element-loader>\n }\n </div>\n </x-webget-flyout>\n</ng-template>\n", styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}\n"] }]
|
|
108
|
+
}], propDecorators: { detailFlyout: [{
|
|
109
|
+
type: ViewChild,
|
|
110
|
+
args: ['detailFlyout', { read: TemplateRef, static: true }]
|
|
111
|
+
}] } });
|
|
112
|
+
|
|
113
|
+
class ClaimsModule {
|
|
114
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ClaimsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
115
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: ClaimsModule, declarations: [ClaimsComponent], imports: [SharedModule,
|
|
116
|
+
ElementsModule,
|
|
117
|
+
WebgetsModule, i1$1.RouterModule] }); }
|
|
118
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ClaimsModule, imports: [SharedModule,
|
|
119
|
+
ElementsModule,
|
|
120
|
+
WebgetsModule,
|
|
121
|
+
RouterModule.forChild([{ path: '', component: ClaimsComponent }])] }); }
|
|
122
|
+
}
|
|
123
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: ClaimsModule, decorators: [{
|
|
124
|
+
type: NgModule,
|
|
125
|
+
args: [{
|
|
126
|
+
declarations: [ClaimsComponent],
|
|
127
|
+
imports: [
|
|
128
|
+
SharedModule,
|
|
129
|
+
ElementsModule,
|
|
130
|
+
WebgetsModule,
|
|
131
|
+
RouterModule.forChild([{ path: '', component: ClaimsComponent }]),
|
|
132
|
+
],
|
|
133
|
+
}]
|
|
134
|
+
}] });
|
|
135
|
+
|
|
136
|
+
export { ClaimsModule };
|
|
137
|
+
//# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-claims.module-Dk9mz7Wj.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-claims.module-Dk9mz7Wj.mjs","sources":["../../../projects/admin/src/lib/pages/claims/claims.component.ts","../../../projects/admin/src/lib/pages/claims/claims.component.html","../../../projects/admin/src/lib/pages/claims/claims.module.ts"],"sourcesContent":["import { Component, TemplateRef, ViewChild, inject } from '@angular/core';\nimport { notify } from '@thescaffold/ngx-core';\nimport { FlyoutService } from '@thescaffold/ngx-ui';\nimport { firstValueFrom } from 'rxjs';\nimport { ListPageDirective } from '../_shared/list-page.base';\n\n// R1 production-readiness pass — RET-07. The backend workflow\n// (`WarrantyWorkflow.triage`/`.resolve`) was already complete; ops just\n// had no page to drive it — a bare list with zero actions.\n@Component({\n standalone: false,\n selector: 'x-claims',\n templateUrl: './claims.component.html',\n styleUrl: './claims.component.scss',\n})\nexport class ClaimsComponent extends ListPageDirective {\n private readonly flyoutService = inject(FlyoutService);\n\n protected pluralName = 'warranty claims';\n protected dataSource = this.apiService.on('apps/commerce/warranty-claim');\n\n protected columns = [\n { key: 'warrantyId', value: 'Warranty' },\n { key: 'status', value: 'Status', type: 'class', class: 'badge text-bg-light border' },\n { key: 'triageOutcome', value: 'Triage' },\n { key: 'createdAt', value: 'Opened', type: 'date' },\n ];\n\n detail: any = null;\n saving = false;\n triageOutcome = 'remote_fix';\n triageNote = '';\n resolution: 'repair' | 'replace' | 'reject' = 'repair';\n resolveNote = '';\n\n readonly triageOutcomes = ['remote_fix', 'site_visit', 'repair', 'replace', 'reject', 'manufacturer'];\n readonly resolutions: ('repair' | 'replace' | 'reject')[] = ['repair', 'replace', 'reject'];\n\n @ViewChild('detailFlyout', { read: TemplateRef, static: true })\n detailFlyout!: TemplateRef<any>;\n\n protected override extraCollectionFields() {\n return {\n links: {\n secondary: [{ title: 'View', fn: (_link: any, item: any) => this.view(item) }],\n },\n };\n }\n\n async view(item: any): Promise<void> {\n this.detail = null;\n this.triageOutcome = 'remote_fix';\n this.triageNote = '';\n this.resolution = 'repair';\n this.resolveNote = '';\n this.flyoutService.open(this.detailFlyout);\n\n const res: any = await firstValueFrom(this.dataSource.getOne(item.id));\n this.detail = res?.data ?? item;\n }\n\n async saveTriage(): Promise<void> {\n if (!this.detail) return;\n this.saving = true;\n try {\n const res: any = await firstValueFrom(\n this.apiService.post(`apps/commerce/warranty-claim/${this.detail.id}/triage`, {\n outcome: this.triageOutcome,\n note: this.triageNote || undefined,\n }),\n );\n if (res?.status === 'error') {\n notify(res.message || 'Could not save the triage.', undefined, 'error');\n return;\n }\n notify('Triage saved.', undefined, 'success');\n this.detail = { ...this.detail, status: res?.data?.status ?? this.detail.status, triageOutcome: this.triageOutcome };\n this.list(this.page, this.perPage);\n } catch {\n notify('Could not save the triage.', undefined, 'error');\n } finally {\n this.saving = false;\n }\n }\n\n async resolveClaim(): Promise<void> {\n if (!this.detail) return;\n this.saving = true;\n try {\n const res: any = await firstValueFrom(\n this.apiService.post(`apps/commerce/warranty-claim/${this.detail.id}/resolve`, {\n resolution: this.resolution,\n note: this.resolveNote || undefined,\n }),\n );\n if (res?.status === 'error') {\n notify(res.message || 'Could not resolve the claim.', undefined, 'error');\n return;\n }\n notify('Claim resolved.', undefined, 'success');\n this.flyoutService.dismiss();\n this.list(this.page, this.perPage);\n } catch {\n notify('Could not resolve the claim.', undefined, 'error');\n } finally {\n this.saving = false;\n }\n }\n}\n","<div class=\"x-page-head\">\n <div class=\"x-page-title\">Warranty claims</div>\n <div class=\"x-page-subtitle\">Warranty claims — triage outcome, resolution and manufacturer refs.</div>\n</div>\n\n@if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n} @else {\n <x-element-loader></x-element-loader>\n}\n\n<ng-template #detailFlyout>\n <x-webget-flyout [data]=\"{ title: 'Warranty claim' }\">\n <div class=\"p-4 d-flex flex-column gap-3\">\n @if (detail) {\n <div>\n <span class=\"badge text-bg-light border\">{{ detail.status }}</span>\n @if (detail.triageOutcome) {\n <span class=\"badge text-bg-light border\">triage: {{ detail.triageOutcome }}</span>\n }\n @if (detail.installedByTilmart) {\n <span class=\"badge text-bg-light border\">Tilmart install</span>\n }\n </div>\n @if (detail.faultDescription) {\n <div class=\"small\">{{ detail.faultDescription }}</div>\n }\n @if (detail.manufacturerRef) {\n <div class=\"small text-secondary\">Manufacturer ref: {{ detail.manufacturerRef }}</div>\n }\n\n <hr class=\"my-1\" />\n\n <div>\n <div class=\"fw-bold small mb-1\">Triage</div>\n <div class=\"d-flex flex-wrap gap-2\">\n <select class=\"form-select form-select-sm\" style=\"width:auto\" [(ngModel)]=\"triageOutcome\">\n @for (o of triageOutcomes; track o) { <option [value]=\"o\">{{ o }}</option> }\n </select>\n <input type=\"text\" class=\"form-control form-control-sm\" placeholder=\"note\" [(ngModel)]=\"triageNote\" />\n </div>\n <button type=\"button\" class=\"btn btn-sm btn-outline-dark mt-2\" [disabled]=\"saving\" (click)=\"saveTriage()\">\n Save triage\n </button>\n </div>\n\n <hr class=\"my-1\" />\n\n <div>\n <div class=\"fw-bold small mb-1\">Resolve</div>\n <div class=\"d-flex flex-wrap gap-2\">\n <select class=\"form-select form-select-sm\" style=\"width:auto\" [(ngModel)]=\"resolution\">\n @for (r of resolutions; track r) { <option [value]=\"r\">{{ r }}</option> }\n </select>\n <input type=\"text\" class=\"form-control form-control-sm\" placeholder=\"note\" [(ngModel)]=\"resolveNote\" />\n </div>\n <button type=\"button\" class=\"btn btn-sm btn-dark mt-2\" [disabled]=\"saving\" (click)=\"resolveClaim()\">\n Resolve claim\n </button>\n </div>\n } @else {\n <x-element-loader></x-element-loader>\n }\n </div>\n </x-webget-flyout>\n</ng-template>\n","import { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { SharedModule } from '@thescaffold/ngx-core';\nimport { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';\nimport { ClaimsComponent } from './claims.component';\n\n@NgModule({\n declarations: [ClaimsComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: ClaimsComponent }]),\n ],\n})\nexport class ClaimsModule {}\n"],"names":["i1"],"mappings":"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"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Component, NgModule } from '@angular/core';
|
|
3
|
-
import * as i1
|
|
3
|
+
import * as i1 from '@angular/router';
|
|
4
4
|
import { RouterModule } from '@angular/router';
|
|
5
5
|
import { BaseDirective, using, notify, SharedModule } from '@thescaffold/ngx-core';
|
|
6
|
-
import * as
|
|
6
|
+
import * as i2 from '@thescaffold/ngx-ui';
|
|
7
7
|
import { ElementsModule } from '@thescaffold/ngx-ui';
|
|
8
8
|
import { BehaviorSubject, takeUntil, map } from 'rxjs';
|
|
9
|
-
import * as
|
|
9
|
+
import * as i3 from '@angular/common';
|
|
10
10
|
|
|
11
11
|
// P1.16 — CAT-10's "editable by ops without code". P1.11 deliberately
|
|
12
12
|
// implements a **fixed, named vocabulary** of rule kinds
|
|
@@ -157,7 +157,7 @@ class CompatibilityRulesComponent extends BaseDirective {
|
|
|
157
157
|
} @else {
|
|
158
158
|
<x-element-loader></x-element-loader>
|
|
159
159
|
}
|
|
160
|
-
`, isInline: true, styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px;max-width:640px}.x-rules-table{background:var(--color-surface);border-radius:var(--radius-lg);overflow:hidden}.x-rules-row{display:grid;grid-template-columns:1.8fr 1.2fr .8fr 1.4fr .6fr auto;gap:12px;align-items:start;padding:14px 16px;border-bottom:1px solid var(--color-border-muted)}.x-rules-row:last-child{border-bottom:0}.x-rules-head{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.4px;color:var(--color-text-muted);align-items:center}.x-rule-name{font-size:14px;font-weight:700;color:var(--color-text)}.x-rule-scope{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);margin-top:2px}.x-rule-message{font-size:12px;color:var(--color-text-muted);margin-top:4px;line-height:1.4}.x-rules-col-kind code{font-size:11.5px;color:var(--color-text-muted)}.x-outcome-badge{display:inline-block;font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.3px;padding:2px 8px;border-radius:var(--radius-pill, 999px);border:1px solid var(--color-border);color:var(--color-text-muted)}.x-outcome-block{color:color-mix(in srgb,var(--color-danger) 75%,black);border-color:var(--color-danger)}.x-outcome-warn{color:color-mix(in srgb,var(--color-warn) 70%,black);border-color:var(--color-warn)}.x-outcome-pass{color:color-mix(in srgb,var(--color-success) 70%,black);border-color:var(--color-success)}.x-config-input{width:100%;box-sizing:border-box;padding:6px 8px;border-radius:var(--radius-sm);border:1px solid var(--color-border);background:var(--color-bg);color:var(--color-text);font-family:var(--font-mono);font-size:11.5px;resize:vertical}.x-toggle{position:relative;display:inline-block;width:34px;height:20px}.x-toggle input{opacity:0;width:0;height:0}.x-toggle span{position:absolute;inset:0;background:var(--color-border);border-radius:999px;transition:background .15s ease}.x-toggle span:before{content:\"\";position:absolute;width:16px;height:16px;left:2px;top:2px;background:var(--color-surface);border-radius:50%;transition:transform .15s ease}.x-toggle input:checked+span{background:var(--color-accent)}.x-toggle input:checked+span:before{transform:translate(14px)}.x-save-btn{background:var(--color-accent);color:var(--color-accent-fg);border:0;border-radius:var(--radius-sm);padding:8px 12px;font-size:12.5px;font-weight:700;cursor:pointer;white-space:nowrap}.x-save-btn:disabled{opacity:.5;cursor:not-allowed}.x-rules-empty{padding:32px 16px;text-align:center;font-size:13px;color:var(--color-text-muted)}\n"], dependencies: [{ kind: "component", type:
|
|
160
|
+
`, isInline: true, styles: [".x-page-head{margin-bottom:20px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px;max-width:640px}.x-rules-table{background:var(--color-surface);border-radius:var(--radius-lg);overflow:hidden}.x-rules-row{display:grid;grid-template-columns:1.8fr 1.2fr .8fr 1.4fr .6fr auto;gap:12px;align-items:start;padding:14px 16px;border-bottom:1px solid var(--color-border-muted)}.x-rules-row:last-child{border-bottom:0}.x-rules-head{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.4px;color:var(--color-text-muted);align-items:center}.x-rule-name{font-size:14px;font-weight:700;color:var(--color-text)}.x-rule-scope{font-family:var(--font-mono);font-size:11px;color:var(--color-text-muted);margin-top:2px}.x-rule-message{font-size:12px;color:var(--color-text-muted);margin-top:4px;line-height:1.4}.x-rules-col-kind code{font-size:11.5px;color:var(--color-text-muted)}.x-outcome-badge{display:inline-block;font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.3px;padding:2px 8px;border-radius:var(--radius-pill, 999px);border:1px solid var(--color-border);color:var(--color-text-muted)}.x-outcome-block{color:color-mix(in srgb,var(--color-danger) 75%,black);border-color:var(--color-danger)}.x-outcome-warn{color:color-mix(in srgb,var(--color-warn) 70%,black);border-color:var(--color-warn)}.x-outcome-pass{color:color-mix(in srgb,var(--color-success) 70%,black);border-color:var(--color-success)}.x-config-input{width:100%;box-sizing:border-box;padding:6px 8px;border-radius:var(--radius-sm);border:1px solid var(--color-border);background:var(--color-bg);color:var(--color-text);font-family:var(--font-mono);font-size:11.5px;resize:vertical}.x-toggle{position:relative;display:inline-block;width:34px;height:20px}.x-toggle input{opacity:0;width:0;height:0}.x-toggle span{position:absolute;inset:0;background:var(--color-border);border-radius:999px;transition:background .15s ease}.x-toggle span:before{content:\"\";position:absolute;width:16px;height:16px;left:2px;top:2px;background:var(--color-surface);border-radius:50%;transition:transform .15s ease}.x-toggle input:checked+span{background:var(--color-accent)}.x-toggle input:checked+span:before{transform:translate(14px)}.x-save-btn{background:var(--color-accent);color:var(--color-accent-fg);border:0;border-radius:var(--radius-sm);padding:8px 12px;font-size:12.5px;font-weight:700;cursor:pointer;white-space:nowrap}.x-save-btn:disabled{opacity:.5;cursor:not-allowed}.x-rules-empty{padding:32px 16px;text-align:center;font-size:13px;color:var(--color-text-muted)}\n"], dependencies: [{ kind: "component", type: i2.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }] }); }
|
|
161
161
|
}
|
|
162
162
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CompatibilityRulesComponent, decorators: [{
|
|
163
163
|
type: Component,
|
|
@@ -240,7 +240,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
240
240
|
class CompatibilityRulesModule {
|
|
241
241
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CompatibilityRulesModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
242
242
|
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: CompatibilityRulesModule, declarations: [CompatibilityRulesComponent], imports: [SharedModule,
|
|
243
|
-
ElementsModule, i1
|
|
243
|
+
ElementsModule, i1.RouterModule] }); }
|
|
244
244
|
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: CompatibilityRulesModule, imports: [SharedModule,
|
|
245
245
|
ElementsModule,
|
|
246
246
|
RouterModule.forChild([{ path: '', component: CompatibilityRulesComponent }])] }); }
|
|
@@ -258,4 +258,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
|
|
|
258
258
|
}] });
|
|
259
259
|
|
|
260
260
|
export { CompatibilityRulesModule };
|
|
261
|
-
//# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-
|
|
261
|
+
//# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-JiFnNJj_.mjs.map
|