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

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-catalog.module-BwjfFXDU.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.\n// Product photo guidance — mirrored in the bulk importer and the admin hint.\nconst MAX_IMAGE_BYTES = 8 * 1024 * 1024;\nconst MIN_IMAGE_SIDE = 800;\n\nasync function readImageSize(file: File): Promise<{ width: number; height: number } | undefined> {\n try {\n const bitmap = await createImageBitmap(file);\n const size = { width: bitmap.width, height: bitmap.height };\n bitmap.close();\n return size;\n } catch {\n return undefined;\n }\n}\n\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 const file = files[0];\n if (file.size > MAX_IMAGE_BYTES) {\n notify('That image is over 8 MB — export it at 1200–2000 px (JPG/WebP) and try again.', undefined, 'error');\n input.value = '';\n return;\n }\n const size = await readImageSize(file);\n if (size && Math.min(size.width, size.height) < MIN_IMAGE_SIDE) {\n notify(\n `That image is only ${size.width} × ${size.height}px — it will look soft on large screens. 1200 × 1200 px or more is recommended.`,\n undefined,\n 'warning',\n );\n }\n\n this.uploadingMedia = true;\n try {\n const blobId = await uploadFileToBlobs(this.apiService, file, 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 <a class=\"x-cat-btn\" routerLink=\"/admin/bulk-upload\">Bulk upload (Excel)</a>\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 &amp; 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 &amp; 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 <div class=\"x-cat-hint\">\n Best results: <strong>1200 × 1200 px</strong> or larger, square (4:3 also works), product centred on a\n plain white/light background, JPG or WebP, under 8 MB. The first image is the listing thumbnail —\n use a clean front-on shot; add the rating plate, ports/back and box contents as extra images.\n The site downsizes photos for phones automatically, so upload the sharp original.\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 { 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":["i2","i3","i1"],"mappings":"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"}
@@ -5,6 +5,7 @@ import { ActivatedRoute, RouterModule } from '@angular/router';
5
5
  import { using, notify, SharedModule } from '@thescaffold/ngx-core';
6
6
  import * as i2 from '@thescaffold/ngx-ui';
7
7
  import { FlyoutService, ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';
8
+ import { downloadBlob } from '@isaiahiroko/tilmart-ngx-ui';
8
9
  import { takeUntil } from 'rxjs';
9
10
  import { L as ListPageDirective } from './isaiahiroko-tilmart-ngx-apps-admin-list-page.base-Cl7JZ-Tp.mjs';
10
11
  import * as i3 from '@angular/common';
@@ -48,6 +49,9 @@ class OrdersComponent extends ListPageDirective {
48
49
  this.query = this.route.snapshot.queryParamMap.get('q') ?? '';
49
50
  this.list();
50
51
  };
52
+ // ORD-11 — the order's VAT invoice as a PDF. Fetched as a Blob (auth
53
+ // header, not a cookie) and saved under the invoice's document number.
54
+ this.downloadingInvoice = false;
51
55
  }
52
56
  view(item) {
53
57
  this.detail = null;
@@ -99,6 +103,25 @@ class OrdersComponent extends ListPageDirective {
99
103
  }));
100
104
  }));
101
105
  }
106
+ downloadInvoice() {
107
+ if (!this.detail || this.downloadingInvoice)
108
+ return;
109
+ const { id, orderNumber } = this.detail;
110
+ this.downloadingInvoice = true;
111
+ this.apiService.requestService
112
+ .raw('get', `apps/commerce/document/order/${id}/invoice`, { format: 'pdf' }, { responseType: 'blob' })
113
+ .pipe(takeUntil(this.$))
114
+ .subscribe({
115
+ next: (blob) => {
116
+ this.downloadingInvoice = false;
117
+ downloadBlob(blob, `INV-${orderNumber}.pdf`);
118
+ },
119
+ error: () => {
120
+ this.downloadingInvoice = false;
121
+ notify('Could not generate the invoice PDF.', undefined, 'error');
122
+ },
123
+ });
124
+ }
102
125
  transition(to) {
103
126
  if (!this.detail)
104
127
  return;
@@ -130,11 +153,11 @@ class OrdersComponent extends ListPageDirective {
130
153
  };
131
154
  }
132
155
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: OrdersComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
133
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: OrdersComponent, isStandalone: false, selector: "x-orders", 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\">Orders</div>\n <div class=\"x-page-subtitle\">Every order \u2014 search, drill in, move it forward.</div>\n</div>\n\n@if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n} @else {\n <x-element-loader></x-element-loader>\n}\n\n<ng-template #detailFlyout>\n <x-webget-flyout [data]=\"{ title: 'Order detail' }\">\n <div class=\"p-4 d-flex flex-column gap-3\">\n @if (detail) {\n <div>\n <div class=\"fw-bold font-monospace\">\n {{ detail.orderNumber }}\n <a\n class=\"small ms-2\"\n [routerLink]=\"['/admin/trace']\"\n [queryParams]=\"{ ref: detail.orderNumber }\"\n >full trace \u2192</a>\n </div>\n <span class=\"badge text-bg-light border mt-1\">{{ detail.status }}</span>\n @if (detail.businessName) {\n <div class=\"text-secondary small mt-1\">Invoice to: {{ detail.businessName }}</div>\n }\n @if (detail.recipientName) {\n <div class=\"text-secondary small\">Recipient: {{ detail.recipientName }} \u00B7 {{ detail.recipientPhone }}</div>\n }\n </div>\n\n @if (detailActions.length) {\n <div class=\"d-flex flex-wrap gap-2\">\n @for (action of detailActions; track action.to) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-dark\" (click)=\"transition(action.to)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <table class=\"table table-sm\">\n <thead><tr><th>Item</th><th>Qty</th><th class=\"text-end\">Amount</th></tr></thead>\n <tbody>\n @for (line of detail.lines; track line.id) {\n <tr>\n <td>{{ line.nameSnapshot || line.variantId }}</td>\n <td>{{ line.quantity }}</td>\n <td class=\"text-end\">{{ naira(line.unitPrice * line.quantity, detail.currency) }}</td>\n </tr>\n } @empty {\n <tr><td colspan=\"3\" class=\"text-secondary\">No lines.</td></tr>\n }\n </tbody>\n </table>\n\n <div class=\"small\">\n <div class=\"d-flex justify-content-between\"><span>Subtotal</span><span>{{ naira(detail.subtotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Delivery</span><span>{{ naira(detail.deliveryTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>VAT (incl.)</span><span>{{ naira(detail.taxTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between fw-bold\"><span>Total</span><span>{{ naira(detail.grandTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Paid</span><span>{{ naira(detail.amountPaid, detail.currency) }}</span></div>\n @if (detail.codAmountDue > 0) {\n <div class=\"d-flex justify-content-between text-warning\"><span>Due on delivery</span><span>{{ naira(detail.codAmountDue, detail.currency) }}</span></div>\n }\n </div>\n\n <hr class=\"my-1\" />\n <div>\n <div class=\"fw-bold small mb-1\">Payments</div>\n @for (payment of detail.payments; track payment.id) {\n <div class=\"small text-secondary\">\n {{ payment.provider }} \u00B7 {{ payment.method || 'card' }} \u00B7 {{ payment.status }}\n \u00B7 {{ naira(payment.amount, payment.currency) }}\n </div>\n } @empty {\n <div class=\"small text-secondary\">None.</div>\n }\n </div>\n\n <div>\n <div class=\"fw-bold small mb-1\">Shipments</div>\n @for (shipment of detail.shipments; track shipment.id) {\n <div class=\"small text-secondary\">\n {{ shipment.status }}@if (shipment.trackingRef) { \u00B7 {{ shipment.trackingRef }} }\n </div>\n } @empty {\n <div class=\"small text-secondary\">None yet \u2014 create one on the Shipments page.</div>\n }\n </div>\n\n <div>\n <div class=\"fw-bold small mb-1\">History</div>\n @for (row of detail.history; track row.id) {\n <div class=\"small text-secondary\">\n {{ row.toStatus }} \u00B7 {{ row.createdAt | date: 'MMM d, h:mm a' }}\n @if (row.reason) { \u2014 {{ row.reason }} }\n </div>\n } @empty {\n <div class=\"small text-secondary\">No transitions recorded.</div>\n }\n </div>\n } @else {\n <x-element-loader></x-element-loader>\n }\n </div>\n </x-webget-flyout>\n</ng-template>\n", 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.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"] }, { kind: "component", type: i2.CollectionComponent, selector: "x-webget-collection", inputs: ["data"], outputs: ["search", "filter", "refresh", "select", "page", "show"] }, { kind: "component", type: i2.FlyoutComponent, selector: "x-webget-flyout", inputs: ["data"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.DatePipe, name: "date" }] }); }
156
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: OrdersComponent, isStandalone: false, selector: "x-orders", 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\">Orders</div>\n <div class=\"x-page-subtitle\">Every order \u2014 search, drill in, move it forward.</div>\n</div>\n\n@if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n} @else {\n <x-element-loader></x-element-loader>\n}\n\n<ng-template #detailFlyout>\n <x-webget-flyout [data]=\"{ title: 'Order detail' }\">\n <div class=\"p-4 d-flex flex-column gap-3\">\n @if (detail) {\n <div>\n <div class=\"fw-bold font-monospace\">\n {{ detail.orderNumber }}\n <a\n class=\"small ms-2\"\n [routerLink]=\"['/admin/trace']\"\n [queryParams]=\"{ ref: detail.orderNumber }\"\n >full trace \u2192</a>\n </div>\n <span class=\"badge text-bg-light border mt-1\">{{ detail.status }}</span>\n @if (detail.businessName) {\n <div class=\"text-secondary small mt-1\">Invoice to: {{ detail.businessName }}</div>\n }\n @if (detail.recipientName) {\n <div class=\"text-secondary small\">Recipient: {{ detail.recipientName }} \u00B7 {{ detail.recipientPhone }}</div>\n }\n </div>\n\n <div class=\"d-flex flex-wrap gap-2\">\n <button\n type=\"button\"\n class=\"btn btn-sm btn-outline-primary\"\n [disabled]=\"downloadingInvoice\"\n (click)=\"downloadInvoice()\"\n >\n {{ downloadingInvoice ? 'Preparing\u2026' : 'Download invoice (PDF)' }}\n </button>\n </div>\n\n @if (detailActions.length) {\n <div class=\"d-flex flex-wrap gap-2\">\n @for (action of detailActions; track action.to) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-dark\" (click)=\"transition(action.to)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <table class=\"table table-sm\">\n <thead><tr><th>Item</th><th>Qty</th><th class=\"text-end\">Amount</th></tr></thead>\n <tbody>\n @for (line of detail.lines; track line.id) {\n <tr>\n <td>{{ line.nameSnapshot || line.variantId }}</td>\n <td>{{ line.quantity }}</td>\n <td class=\"text-end\">{{ naira(line.unitPrice * line.quantity, detail.currency) }}</td>\n </tr>\n } @empty {\n <tr><td colspan=\"3\" class=\"text-secondary\">No lines.</td></tr>\n }\n </tbody>\n </table>\n\n <div class=\"small\">\n <div class=\"d-flex justify-content-between\"><span>Subtotal</span><span>{{ naira(detail.subtotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Delivery</span><span>{{ naira(detail.deliveryTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>VAT (incl.)</span><span>{{ naira(detail.taxTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between fw-bold\"><span>Total</span><span>{{ naira(detail.grandTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Paid</span><span>{{ naira(detail.amountPaid, detail.currency) }}</span></div>\n @if (detail.codAmountDue > 0) {\n <div class=\"d-flex justify-content-between text-warning\"><span>Due on delivery</span><span>{{ naira(detail.codAmountDue, detail.currency) }}</span></div>\n }\n </div>\n\n <hr class=\"my-1\" />\n <div>\n <div class=\"fw-bold small mb-1\">Payments</div>\n @for (payment of detail.payments; track payment.id) {\n <div class=\"small text-secondary\">\n {{ payment.provider }} \u00B7 {{ payment.method || 'card' }} \u00B7 {{ payment.status }}\n \u00B7 {{ naira(payment.amount, payment.currency) }}\n </div>\n } @empty {\n <div class=\"small text-secondary\">None.</div>\n }\n </div>\n\n <div>\n <div class=\"fw-bold small mb-1\">Shipments</div>\n @for (shipment of detail.shipments; track shipment.id) {\n <div class=\"small text-secondary\">\n {{ shipment.status }}@if (shipment.trackingRef) { \u00B7 {{ shipment.trackingRef }} }\n </div>\n } @empty {\n <div class=\"small text-secondary\">None yet \u2014 create one on the Shipments page.</div>\n }\n </div>\n\n <div>\n <div class=\"fw-bold small mb-1\">History</div>\n @for (row of detail.history; track row.id) {\n <div class=\"small text-secondary\">\n {{ row.toStatus }} \u00B7 {{ row.createdAt | date: 'MMM d, h:mm a' }}\n @if (row.reason) { \u2014 {{ row.reason }} }\n </div>\n } @empty {\n <div class=\"small text-secondary\">No transitions recorded.</div>\n }\n </div>\n } @else {\n <x-element-loader></x-element-loader>\n }\n </div>\n </x-webget-flyout>\n</ng-template>\n", 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.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"] }, { kind: "component", type: i2.CollectionComponent, selector: "x-webget-collection", inputs: ["data"], outputs: ["search", "filter", "refresh", "select", "page", "show"] }, { kind: "component", type: i2.FlyoutComponent, selector: "x-webget-flyout", inputs: ["data"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.DatePipe, name: "date" }] }); }
134
157
  }
135
158
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: OrdersComponent, decorators: [{
136
159
  type: Component,
137
- args: [{ standalone: false, selector: 'x-orders', template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">Orders</div>\n <div class=\"x-page-subtitle\">Every order \u2014 search, drill in, move it forward.</div>\n</div>\n\n@if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n} @else {\n <x-element-loader></x-element-loader>\n}\n\n<ng-template #detailFlyout>\n <x-webget-flyout [data]=\"{ title: 'Order detail' }\">\n <div class=\"p-4 d-flex flex-column gap-3\">\n @if (detail) {\n <div>\n <div class=\"fw-bold font-monospace\">\n {{ detail.orderNumber }}\n <a\n class=\"small ms-2\"\n [routerLink]=\"['/admin/trace']\"\n [queryParams]=\"{ ref: detail.orderNumber }\"\n >full trace \u2192</a>\n </div>\n <span class=\"badge text-bg-light border mt-1\">{{ detail.status }}</span>\n @if (detail.businessName) {\n <div class=\"text-secondary small mt-1\">Invoice to: {{ detail.businessName }}</div>\n }\n @if (detail.recipientName) {\n <div class=\"text-secondary small\">Recipient: {{ detail.recipientName }} \u00B7 {{ detail.recipientPhone }}</div>\n }\n </div>\n\n @if (detailActions.length) {\n <div class=\"d-flex flex-wrap gap-2\">\n @for (action of detailActions; track action.to) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-dark\" (click)=\"transition(action.to)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <table class=\"table table-sm\">\n <thead><tr><th>Item</th><th>Qty</th><th class=\"text-end\">Amount</th></tr></thead>\n <tbody>\n @for (line of detail.lines; track line.id) {\n <tr>\n <td>{{ line.nameSnapshot || line.variantId }}</td>\n <td>{{ line.quantity }}</td>\n <td class=\"text-end\">{{ naira(line.unitPrice * line.quantity, detail.currency) }}</td>\n </tr>\n } @empty {\n <tr><td colspan=\"3\" class=\"text-secondary\">No lines.</td></tr>\n }\n </tbody>\n </table>\n\n <div class=\"small\">\n <div class=\"d-flex justify-content-between\"><span>Subtotal</span><span>{{ naira(detail.subtotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Delivery</span><span>{{ naira(detail.deliveryTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>VAT (incl.)</span><span>{{ naira(detail.taxTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between fw-bold\"><span>Total</span><span>{{ naira(detail.grandTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Paid</span><span>{{ naira(detail.amountPaid, detail.currency) }}</span></div>\n @if (detail.codAmountDue > 0) {\n <div class=\"d-flex justify-content-between text-warning\"><span>Due on delivery</span><span>{{ naira(detail.codAmountDue, detail.currency) }}</span></div>\n }\n </div>\n\n <hr class=\"my-1\" />\n <div>\n <div class=\"fw-bold small mb-1\">Payments</div>\n @for (payment of detail.payments; track payment.id) {\n <div class=\"small text-secondary\">\n {{ payment.provider }} \u00B7 {{ payment.method || 'card' }} \u00B7 {{ payment.status }}\n \u00B7 {{ naira(payment.amount, payment.currency) }}\n </div>\n } @empty {\n <div class=\"small text-secondary\">None.</div>\n }\n </div>\n\n <div>\n <div class=\"fw-bold small mb-1\">Shipments</div>\n @for (shipment of detail.shipments; track shipment.id) {\n <div class=\"small text-secondary\">\n {{ shipment.status }}@if (shipment.trackingRef) { \u00B7 {{ shipment.trackingRef }} }\n </div>\n } @empty {\n <div class=\"small text-secondary\">None yet \u2014 create one on the Shipments page.</div>\n }\n </div>\n\n <div>\n <div class=\"fw-bold small mb-1\">History</div>\n @for (row of detail.history; track row.id) {\n <div class=\"small text-secondary\">\n {{ row.toStatus }} \u00B7 {{ row.createdAt | date: 'MMM d, h:mm a' }}\n @if (row.reason) { \u2014 {{ row.reason }} }\n </div>\n } @empty {\n <div class=\"small text-secondary\">No transitions recorded.</div>\n }\n </div>\n } @else {\n <x-element-loader></x-element-loader>\n }\n </div>\n </x-webget-flyout>\n</ng-template>\n", 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"] }]
160
+ args: [{ standalone: false, selector: 'x-orders', template: "<div class=\"x-page-head\">\n <div class=\"x-page-title\">Orders</div>\n <div class=\"x-page-subtitle\">Every order \u2014 search, drill in, move it forward.</div>\n</div>\n\n@if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n} @else {\n <x-element-loader></x-element-loader>\n}\n\n<ng-template #detailFlyout>\n <x-webget-flyout [data]=\"{ title: 'Order detail' }\">\n <div class=\"p-4 d-flex flex-column gap-3\">\n @if (detail) {\n <div>\n <div class=\"fw-bold font-monospace\">\n {{ detail.orderNumber }}\n <a\n class=\"small ms-2\"\n [routerLink]=\"['/admin/trace']\"\n [queryParams]=\"{ ref: detail.orderNumber }\"\n >full trace \u2192</a>\n </div>\n <span class=\"badge text-bg-light border mt-1\">{{ detail.status }}</span>\n @if (detail.businessName) {\n <div class=\"text-secondary small mt-1\">Invoice to: {{ detail.businessName }}</div>\n }\n @if (detail.recipientName) {\n <div class=\"text-secondary small\">Recipient: {{ detail.recipientName }} \u00B7 {{ detail.recipientPhone }}</div>\n }\n </div>\n\n <div class=\"d-flex flex-wrap gap-2\">\n <button\n type=\"button\"\n class=\"btn btn-sm btn-outline-primary\"\n [disabled]=\"downloadingInvoice\"\n (click)=\"downloadInvoice()\"\n >\n {{ downloadingInvoice ? 'Preparing\u2026' : 'Download invoice (PDF)' }}\n </button>\n </div>\n\n @if (detailActions.length) {\n <div class=\"d-flex flex-wrap gap-2\">\n @for (action of detailActions; track action.to) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-dark\" (click)=\"transition(action.to)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <table class=\"table table-sm\">\n <thead><tr><th>Item</th><th>Qty</th><th class=\"text-end\">Amount</th></tr></thead>\n <tbody>\n @for (line of detail.lines; track line.id) {\n <tr>\n <td>{{ line.nameSnapshot || line.variantId }}</td>\n <td>{{ line.quantity }}</td>\n <td class=\"text-end\">{{ naira(line.unitPrice * line.quantity, detail.currency) }}</td>\n </tr>\n } @empty {\n <tr><td colspan=\"3\" class=\"text-secondary\">No lines.</td></tr>\n }\n </tbody>\n </table>\n\n <div class=\"small\">\n <div class=\"d-flex justify-content-between\"><span>Subtotal</span><span>{{ naira(detail.subtotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Delivery</span><span>{{ naira(detail.deliveryTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>VAT (incl.)</span><span>{{ naira(detail.taxTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between fw-bold\"><span>Total</span><span>{{ naira(detail.grandTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Paid</span><span>{{ naira(detail.amountPaid, detail.currency) }}</span></div>\n @if (detail.codAmountDue > 0) {\n <div class=\"d-flex justify-content-between text-warning\"><span>Due on delivery</span><span>{{ naira(detail.codAmountDue, detail.currency) }}</span></div>\n }\n </div>\n\n <hr class=\"my-1\" />\n <div>\n <div class=\"fw-bold small mb-1\">Payments</div>\n @for (payment of detail.payments; track payment.id) {\n <div class=\"small text-secondary\">\n {{ payment.provider }} \u00B7 {{ payment.method || 'card' }} \u00B7 {{ payment.status }}\n \u00B7 {{ naira(payment.amount, payment.currency) }}\n </div>\n } @empty {\n <div class=\"small text-secondary\">None.</div>\n }\n </div>\n\n <div>\n <div class=\"fw-bold small mb-1\">Shipments</div>\n @for (shipment of detail.shipments; track shipment.id) {\n <div class=\"small text-secondary\">\n {{ shipment.status }}@if (shipment.trackingRef) { \u00B7 {{ shipment.trackingRef }} }\n </div>\n } @empty {\n <div class=\"small text-secondary\">None yet \u2014 create one on the Shipments page.</div>\n }\n </div>\n\n <div>\n <div class=\"fw-bold small mb-1\">History</div>\n @for (row of detail.history; track row.id) {\n <div class=\"small text-secondary\">\n {{ row.toStatus }} \u00B7 {{ row.createdAt | date: 'MMM d, h:mm a' }}\n @if (row.reason) { \u2014 {{ row.reason }} }\n </div>\n } @empty {\n <div class=\"small text-secondary\">No transitions recorded.</div>\n }\n </div>\n } @else {\n <x-element-loader></x-element-loader>\n }\n </div>\n </x-webget-flyout>\n</ng-template>\n", 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"] }]
138
161
  }], propDecorators: { detailFlyout: [{
139
162
  type: ViewChild,
140
163
  args: ['detailFlyout', { read: TemplateRef, static: true }]
@@ -164,4 +187,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
164
187
  }] });
165
188
 
166
189
  export { OrdersModule };
167
- //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-orders.module-Cfeb0QjH.mjs.map
190
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-orders.module-vldWwcAr.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-orders.module-vldWwcAr.mjs","sources":["../../../projects/admin/src/lib/pages/orders/orders.component.ts","../../../projects/admin/src/lib/pages/orders/orders.component.html","../../../projects/admin/src/lib/pages/orders/orders.module.ts"],"sourcesContent":["import { Component, TemplateRef, ViewChild, inject } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { notify, using } from '@thescaffold/ngx-core';\nimport { downloadBlob } from '@isaiahiroko/tilmart-ngx-ui';\nimport { FlyoutService } from '@thescaffold/ngx-ui';\nimport { takeUntil } from 'rxjs';\nimport { ListPageDirective } from '../_shared/list-page.base';\n\nfunction naira(amount: number, currency = 'NGN'): string {\n return `${currency} ${Math.round(amount).toLocaleString('en-US')}`;\n}\n\n// Ops action buttons per current status — each entry is a permitted\n// `OrderWorkflow.transition` target. Matches the PRD Appendix B matrix;\n// fulfilment-driven statuses (dispatched, delivered, …) are set by the\n// Shipments page, not here.\nconst ACTIONS: Record<string, { to: string; label: string }[]> = {\n payment_received: [{ to: 'confirmed', label: 'Confirm' }, { to: 'on_hold', label: 'Hold' }],\n on_hold: [{ to: 'confirmed', label: 'Release' }, { to: 'cancelled', label: 'Cancel' }],\n confirmed: [{ to: 'processing', label: 'Start preparing' }, { to: 'cancelled', label: 'Cancel' }],\n processing: [{ to: 'cancelled', label: 'Cancel' }],\n pending_payment: [{ to: 'cancelled', label: 'Cancel' }],\n};\n\n@Component({\n standalone: false,\n selector: 'x-orders',\n templateUrl: './orders.component.html',\n styleUrl: './orders.component.scss',\n})\nexport class OrdersComponent extends ListPageDirective {\n private readonly flyoutService = inject(FlyoutService);\n private readonly route = inject(ActivatedRoute);\n\n protected pluralName = 'orders';\n protected dataSource = this.apiService.on('apps/commerce/order');\n private readonly lineSource = this.apiService.on('apps/commerce/order-line');\n private readonly historySource = this.apiService.on('apps/commerce/order-status-history');\n private readonly paymentSource = this.apiService.on('apps/commerce/payment');\n private readonly shipmentSource = this.apiService.on('apps/commerce/shipment');\n\n protected columns = [\n { key: 'orderNumber', value: 'Order' },\n { key: 'status', value: 'Status', type: 'class', class: 'badge text-bg-light border' },\n { key: 'grandTotalLabel', value: 'Total' },\n { key: 'paidLabel', value: 'Paid' },\n { key: 'placedAt', value: 'Placed', type: 'date' },\n ];\n\n detail: any = null;\n detailActions: { to: string; label: string }[] = [];\n naira = naira;\n\n @ViewChild('detailFlyout', { read: TemplateRef, static: true })\n detailFlyout!: TemplateRef<any>;\n\n override init = () => {\n this.query = this.route.snapshot.queryParamMap.get('q') ?? '';\n this.list();\n };\n\n view(item: any) {\n this.detail = null;\n this.detailActions = ACTIONS[item.status] ?? [];\n this.flyoutService.open(this.detailFlyout);\n\n this.lineSource\n .getMany({ orderId: item.id, page: 1, perPage: 200 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using(({ data: lines }: { data: any[] }) => {\n this.detail = {\n id: item.id,\n orderNumber: item.orderNumber,\n status: item.status,\n currency: item.currency,\n businessName: item.businessName,\n recipientName: item.recipientName,\n recipientPhone: item.recipientPhone,\n subtotal: Number(item.subtotal ?? 0),\n deliveryTotal: Number(item.deliveryTotal ?? 0),\n taxTotal: Number(item.taxTotal ?? 0),\n grandTotal: Number(item.grandTotal ?? 0),\n amountPaid: Number(item.amountPaid ?? 0),\n codAmountDue: Number(item.codAmountDue ?? 0),\n lines: lines ?? [],\n history: [],\n payments: [],\n shipments: [],\n };\n\n this.historySource\n .getMany({ orderId: item.id, page: 1, perPage: 100 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(using(({ data }: { data: any[] }) => {\n if (this.detail) this.detail.history = data ?? [];\n }));\n this.paymentSource\n .getMany({ orderId: item.id, page: 1, perPage: 20 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(using(({ data }: { data: any[] }) => {\n if (this.detail) this.detail.payments = data ?? [];\n }));\n this.shipmentSource\n .getMany({ orderId: item.id, page: 1, perPage: 50 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(using(({ data }: { data: any[] }) => {\n if (this.detail) this.detail.shipments = data ?? [];\n }));\n }),\n );\n }\n\n // ORD-11 — the order's VAT invoice as a PDF. Fetched as a Blob (auth\n // header, not a cookie) and saved under the invoice's document number.\n downloadingInvoice = false;\n downloadInvoice() {\n if (!this.detail || this.downloadingInvoice) return;\n const { id, orderNumber } = this.detail;\n this.downloadingInvoice = true;\n this.apiService.requestService\n .raw<Blob>(\n 'get',\n `apps/commerce/document/order/${id}/invoice`,\n { format: 'pdf' },\n { responseType: 'blob' },\n )\n .pipe(takeUntil(this.$))\n .subscribe({\n next: (blob) => {\n this.downloadingInvoice = false;\n downloadBlob(blob, `INV-${orderNumber}.pdf`);\n },\n error: () => {\n this.downloadingInvoice = false;\n notify('Could not generate the invoice PDF.', undefined, 'error');\n },\n });\n }\n\n transition(to: string) {\n if (!this.detail) return;\n this.apiService\n .post(`apps/commerce/order/${this.detail.id}/transition`, { toStatus: to })\n .pipe(takeUntil(this.$))\n .subscribe(\n using((res: any) => {\n if (res?.status === 'error') {\n notify(res.message || 'That transition is not allowed.', undefined, 'error');\n return;\n }\n notify(`Order moved to ${to.replace(/_/g, ' ')}.`, undefined, 'success');\n this.flyoutService.dismiss();\n this.list();\n }),\n );\n }\n\n protected override mapRow(order: any) {\n return {\n ...order,\n grandTotalLabel: naira(Number(order.grandTotal ?? 0), order.currency),\n paidLabel: naira(Number(order.amountPaid ?? 0), order.currency),\n };\n }\n\n protected override extraCollectionFields() {\n return {\n links: {\n secondary: [{ title: 'View', fn: (_link: any, item: any) => this.view(item) }],\n },\n };\n }\n}\n","<div class=\"x-page-head\">\n <div class=\"x-page-title\">Orders</div>\n <div class=\"x-page-subtitle\">Every order — search, drill in, move it forward.</div>\n</div>\n\n@if (list$ | async; as list) {\n <x-webget-collection [data]=\"list\"></x-webget-collection>\n} @else {\n <x-element-loader></x-element-loader>\n}\n\n<ng-template #detailFlyout>\n <x-webget-flyout [data]=\"{ title: 'Order detail' }\">\n <div class=\"p-4 d-flex flex-column gap-3\">\n @if (detail) {\n <div>\n <div class=\"fw-bold font-monospace\">\n {{ detail.orderNumber }}\n <a\n class=\"small ms-2\"\n [routerLink]=\"['/admin/trace']\"\n [queryParams]=\"{ ref: detail.orderNumber }\"\n >full trace →</a>\n </div>\n <span class=\"badge text-bg-light border mt-1\">{{ detail.status }}</span>\n @if (detail.businessName) {\n <div class=\"text-secondary small mt-1\">Invoice to: {{ detail.businessName }}</div>\n }\n @if (detail.recipientName) {\n <div class=\"text-secondary small\">Recipient: {{ detail.recipientName }} · {{ detail.recipientPhone }}</div>\n }\n </div>\n\n <div class=\"d-flex flex-wrap gap-2\">\n <button\n type=\"button\"\n class=\"btn btn-sm btn-outline-primary\"\n [disabled]=\"downloadingInvoice\"\n (click)=\"downloadInvoice()\"\n >\n {{ downloadingInvoice ? 'Preparing…' : 'Download invoice (PDF)' }}\n </button>\n </div>\n\n @if (detailActions.length) {\n <div class=\"d-flex flex-wrap gap-2\">\n @for (action of detailActions; track action.to) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-dark\" (click)=\"transition(action.to)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <table class=\"table table-sm\">\n <thead><tr><th>Item</th><th>Qty</th><th class=\"text-end\">Amount</th></tr></thead>\n <tbody>\n @for (line of detail.lines; track line.id) {\n <tr>\n <td>{{ line.nameSnapshot || line.variantId }}</td>\n <td>{{ line.quantity }}</td>\n <td class=\"text-end\">{{ naira(line.unitPrice * line.quantity, detail.currency) }}</td>\n </tr>\n } @empty {\n <tr><td colspan=\"3\" class=\"text-secondary\">No lines.</td></tr>\n }\n </tbody>\n </table>\n\n <div class=\"small\">\n <div class=\"d-flex justify-content-between\"><span>Subtotal</span><span>{{ naira(detail.subtotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Delivery</span><span>{{ naira(detail.deliveryTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>VAT (incl.)</span><span>{{ naira(detail.taxTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between fw-bold\"><span>Total</span><span>{{ naira(detail.grandTotal, detail.currency) }}</span></div>\n <div class=\"d-flex justify-content-between\"><span>Paid</span><span>{{ naira(detail.amountPaid, detail.currency) }}</span></div>\n @if (detail.codAmountDue > 0) {\n <div class=\"d-flex justify-content-between text-warning\"><span>Due on delivery</span><span>{{ naira(detail.codAmountDue, detail.currency) }}</span></div>\n }\n </div>\n\n <hr class=\"my-1\" />\n <div>\n <div class=\"fw-bold small mb-1\">Payments</div>\n @for (payment of detail.payments; track payment.id) {\n <div class=\"small text-secondary\">\n {{ payment.provider }} · {{ payment.method || 'card' }} · {{ payment.status }}\n · {{ naira(payment.amount, payment.currency) }}\n </div>\n } @empty {\n <div class=\"small text-secondary\">None.</div>\n }\n </div>\n\n <div>\n <div class=\"fw-bold small mb-1\">Shipments</div>\n @for (shipment of detail.shipments; track shipment.id) {\n <div class=\"small text-secondary\">\n {{ shipment.status }}@if (shipment.trackingRef) { · {{ shipment.trackingRef }} }\n </div>\n } @empty {\n <div class=\"small text-secondary\">None yet — create one on the Shipments page.</div>\n }\n </div>\n\n <div>\n <div class=\"fw-bold small mb-1\">History</div>\n @for (row of detail.history; track row.id) {\n <div class=\"small text-secondary\">\n {{ row.toStatus }} · {{ row.createdAt | date: 'MMM d, h:mm a' }}\n @if (row.reason) { — {{ row.reason }} }\n </div>\n } @empty {\n <div class=\"small text-secondary\">No transitions recorded.</div>\n }\n </div>\n } @else {\n <x-element-loader></x-element-loader>\n }\n </div>\n </x-webget-flyout>\n</ng-template>\n","import { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { SharedModule } from '@thescaffold/ngx-core';\nimport { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';\nimport { OrdersComponent } from './orders.component';\n\n@NgModule({\n declarations: [OrdersComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: OrdersComponent }]),\n ],\n})\nexport class OrdersModule {}\n"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@ import { RouterModule } from '@angular/router';
5
5
  import { BaseDirective, titleCase, using, notify, SharedModule } from '@thescaffold/ngx-core';
6
6
  import * as i2 from '@thescaffold/ngx-ui';
7
7
  import { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';
8
- import { BehaviorSubject, takeUntil, map, firstValueFrom } from 'rxjs';
8
+ import { BehaviorSubject, firstValueFrom, takeUntil, map } from 'rxjs';
9
9
  import * as i1 from '@angular/forms';
10
10
  import * as i3 from '@angular/common';
11
11
 
@@ -58,8 +58,12 @@ class SettingsComponent extends BaseDirective {
58
58
  ],
59
59
  fields: [
60
60
  { key: 'name', label: 'Name', type: 'text', required: true },
61
- { key: 'state', label: 'State', type: 'text', required: true },
62
- { key: 'lga', label: 'LGA', type: 'text' },
61
+ // Populated from `apps/commerce/static-list-item` in `init()` below
62
+ // (StaticLists) — this form has no per-field dependent-options
63
+ // support, so LGA lists every LGA rather than just the picked
64
+ // state's (leave blank on the zone itself for a whole-state zone).
65
+ { key: 'state', label: 'State', type: 'select', required: true, options: [] },
66
+ { key: 'lga', label: 'LGA (blank = whole state)', type: 'select', options: [] },
63
67
  ],
64
68
  data$: new BehaviorSubject(null),
65
69
  page: 1,
@@ -163,11 +167,51 @@ class SettingsComponent extends BaseDirective {
163
167
  this.init = () => {
164
168
  for (const section of this.sections)
165
169
  this.load(section);
170
+ void this.loadZoneOptions();
166
171
  };
167
172
  }
168
173
  get activeSection() {
169
174
  return this.sections.find((s) => s.key === this.active);
170
175
  }
176
+ async loadZoneOptions() {
177
+ const zones = this.sections.find((s) => s.key === 'zones');
178
+ const stateField = zones.fields.find((f) => f.key === 'state');
179
+ const lgaField = zones.fields.find((f) => f.key === 'lga');
180
+ const [statesRes, lgaRows] = await Promise.all([
181
+ firstValueFrom(this.apiService.on('apps/commerce/static-list-item').getMany({
182
+ listKey: 'ng-state',
183
+ perPage: 200,
184
+ })),
185
+ this.fetchAllPages({ listKey: 'ng-lga' }),
186
+ ]);
187
+ stateField.options = (statesRes?.data ?? []).map((r) => ({
188
+ value: r.code,
189
+ label: r.label,
190
+ }));
191
+ lgaField.options = lgaRows.map((r) => ({
192
+ value: r.code,
193
+ label: `${r.label} (${r.parentCode})`,
194
+ }));
195
+ }
196
+ // The CRUD list endpoint caps `perPage` at 200 server-side regardless of
197
+ // what's asked for — 774 LGAs need 4 pages.
198
+ async fetchAllPages(filter) {
199
+ const rows = [];
200
+ let page = 1;
201
+ for (;;) {
202
+ const res = await firstValueFrom(this.apiService.on('apps/commerce/static-list-item').getMany({
203
+ ...filter,
204
+ page,
205
+ perPage: 200,
206
+ }));
207
+ const batch = res?.data ?? [];
208
+ rows.push(...batch);
209
+ if (batch.length < 200)
210
+ break;
211
+ page++;
212
+ }
213
+ return rows;
214
+ }
171
215
  load(section, page = section.page, query = section.query) {
172
216
  section.page = page;
173
217
  section.query = query;
@@ -315,4 +359,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
315
359
  }] });
316
360
 
317
361
  export { SettingsModule };
318
- //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-settings.module-Cz6xPNwB.mjs.map
362
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-settings.module-CFVgAQ2f.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-settings.module-CFVgAQ2f.mjs","sources":["../../../projects/admin/src/lib/pages/settings/settings.component.ts","../../../projects/admin/src/lib/pages/settings/settings.component.html","../../../projects/admin/src/lib/pages/settings/settings.module.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { BaseDirective, notify, titleCase, using } from '@thescaffold/ngx-core';\nimport { BehaviorSubject, firstValueFrom, map, takeUntil } from 'rxjs';\nimport { CrudFieldSpec } from '../_shared/simple-crud.base';\n\ninterface Section {\n key: string;\n title: string;\n singularLabel: string;\n mount: string;\n columns: { key: string; value: string; type?: string; class?: string }[];\n fields: CrudFieldSpec[];\n data$: BehaviorSubject<any>;\n page: number;\n query?: string;\n}\n\n// R1 production-readiness pass — ADM-12 / ORD-05. P7.1's original\n// version stacked these four config resources as read-only lists; ops\n// had no way to add a channel, shipping zone, shipping method or return\n// policy \"without a deploy\" (ADM-12's own words). Same field-by-field\n// form idea as `SimpleCrudDirective`, adapted for four resources sharing\n// one tabbed page rather than one resource per route.\n@Component({\n standalone: false,\n selector: 'x-settings',\n templateUrl: './settings.component.html',\n styleUrl: './settings.component.scss',\n})\nexport class SettingsComponent extends BaseDirective {\n readonly sections: Section[] = [\n {\n key: 'channels',\n title: 'Channels',\n singularLabel: 'channel',\n mount: 'apps/commerce/channel',\n columns: [\n { key: 'name', value: 'Name' },\n { key: 'code', value: 'Code' },\n { key: 'currency', value: 'Currency' },\n ],\n fields: [\n { key: 'name', label: 'Name', type: 'text', required: true },\n { key: 'code', label: 'Code', type: 'text', required: true, placeholder: 'e.g. web-ng' },\n { key: 'currency', label: 'Currency', type: 'text', placeholder: 'NGN' },\n {\n key: 'taxMode',\n label: 'Tax mode',\n type: 'select',\n options: [\n { value: 'inclusive', label: 'Inclusive' },\n { value: 'exclusive', label: 'Exclusive' },\n ],\n },\n ],\n data$: new BehaviorSubject<any>(null),\n page: 1,\n },\n {\n key: 'zones',\n title: 'Shipping zones',\n singularLabel: 'shipping zone',\n mount: 'apps/commerce/delivery-zone',\n columns: [\n { key: 'name', value: 'Name' },\n { key: 'state', value: 'State' },\n { key: 'lga', value: 'LGA' },\n ],\n fields: [\n { key: 'name', label: 'Name', type: 'text', required: true },\n // Populated from `apps/commerce/static-list-item` in `init()` below\n // (StaticLists) — this form has no per-field dependent-options\n // support, so LGA lists every LGA rather than just the picked\n // state's (leave blank on the zone itself for a whole-state zone).\n { key: 'state', label: 'State', type: 'select', required: true, options: [] },\n { key: 'lga', label: 'LGA (blank = whole state)', type: 'select', options: [] },\n ],\n data$: new BehaviorSubject<any>(null),\n page: 1,\n },\n {\n key: 'methods',\n title: 'Shipping methods',\n singularLabel: 'shipping method',\n mount: 'apps/commerce/delivery-method',\n columns: [\n { key: 'name', value: 'Name' },\n { key: 'flatFee', value: 'Flat fee' },\n { key: 'perKg', value: 'Per kg' },\n {\n key: 'enabled',\n value: 'Enabled',\n type: 'class',\n class: 'badge text-bg-light border',\n },\n ],\n fields: [\n { key: 'name', label: 'Name', type: 'text', required: true },\n {\n key: 'type',\n label: 'Type',\n type: 'select',\n options: [\n { value: 'door', label: 'Door delivery' },\n { value: 'express', label: 'Express' },\n { value: 'pickup', label: 'Pickup' },\n { value: 'scheduled', label: 'Scheduled' },\n { value: 'freight', label: 'Freight' },\n ],\n },\n { key: 'estimateDaysMin', label: 'Estimate — min days', type: 'number' },\n { key: 'estimateDaysMax', label: 'Estimate — max days', type: 'number' },\n { key: 'enabled', label: 'Enabled', type: 'boolean' },\n {\n key: 'rateRules',\n label: 'Rate rules (JSON array)',\n type: 'json',\n placeholder: '[{\"zoneId\": null, \"flatFee\": 2000, \"perKg\": 150}]',\n },\n ],\n data$: new BehaviorSubject<any>(null),\n page: 1,\n },\n {\n key: 'returns',\n title: 'Return policies',\n singularLabel: 'return policy',\n mount: 'apps/commerce/return-policy',\n columns: [\n { key: 'windowDays', value: 'Window (days)' },\n { key: 'restockingFeePct', value: 'Restock fee %' },\n { key: 'whoPaysReturn', value: 'Who pays' },\n {\n key: 'enabled',\n value: 'Enabled',\n type: 'class',\n class: 'badge text-bg-light border',\n },\n ],\n fields: [\n { key: 'windowDays', label: 'Return window (days)', type: 'number', required: true },\n {\n key: 'reasonCode',\n label: 'Reason code (blank = default policy)',\n type: 'select',\n options: [\n { value: 'damaged_in_transit', label: 'Damaged in transit' },\n { value: 'wrong_item', label: 'Wrong item' },\n { value: 'not_as_described', label: 'Not as described' },\n { value: 'faulty_on_arrival', label: 'Faulty on arrival' },\n { value: 'changed_mind', label: 'Changed mind' },\n { value: 'missing_parts', label: 'Missing parts' },\n ],\n },\n { key: 'restockingFeePct', label: 'Restocking fee %', type: 'number' },\n {\n key: 'whoPaysReturn',\n label: 'Who pays for return shipping',\n type: 'select',\n options: [\n { value: 'customer', label: 'Customer' },\n { value: 'tilmart', label: 'Tilmart' },\n { value: 'seller', label: 'Seller' },\n ],\n },\n { key: 'allowChangeOfMind', label: 'Allow change-of-mind returns', type: 'boolean' },\n { key: 'enabled', label: 'Enabled', type: 'boolean' },\n ],\n data$: new BehaviorSubject<any>(null),\n page: 1,\n },\n ];\n\n active = this.sections[0].key;\n editing = false;\n saving = false;\n draft: Record<string, any> | null = null;\n\n get activeSection(): Section {\n return this.sections.find((s) => s.key === this.active)!;\n }\n\n override init = () => {\n for (const section of this.sections) this.load(section);\n void this.loadZoneOptions();\n };\n\n private async loadZoneOptions(): Promise<void> {\n const zones = this.sections.find((s) => s.key === 'zones')!;\n const stateField = zones.fields.find((f) => f.key === 'state')!;\n const lgaField = zones.fields.find((f) => f.key === 'lga')!;\n const [statesRes, lgaRows]: any[] = await Promise.all([\n firstValueFrom(\n this.apiService.on('apps/commerce/static-list-item').getMany({\n listKey: 'ng-state',\n perPage: 200,\n } as any),\n ),\n this.fetchAllPages({ listKey: 'ng-lga' }),\n ]);\n stateField.options = (statesRes?.data ?? []).map((r: any) => ({\n value: r.code,\n label: r.label,\n }));\n lgaField.options = lgaRows.map((r: any) => ({\n value: r.code,\n label: `${r.label} (${r.parentCode})`,\n }));\n }\n\n // The CRUD list endpoint caps `perPage` at 200 server-side regardless of\n // what's asked for — 774 LGAs need 4 pages.\n private async fetchAllPages(filter: Record<string, unknown>): Promise<any[]> {\n const rows: any[] = [];\n let page = 1;\n for (;;) {\n const res: any = await firstValueFrom(\n this.apiService.on('apps/commerce/static-list-item').getMany({\n ...filter,\n page,\n perPage: 200,\n } as any),\n );\n const batch = res?.data ?? [];\n rows.push(...batch);\n if (batch.length < 200) break;\n page++;\n }\n return rows;\n }\n\n private load(section: Section, page = section.page, query = section.query): void {\n section.page = page;\n section.query = query;\n const params: any = { page, perPage: 100 };\n if (query) params.query = query;\n this.apiService\n .on(section.mount)\n .getMany(params)\n .pipe(\n takeUntil(this.$),\n map((response: any) => ({\n title: titleCase(section.title),\n columns: section.columns,\n rows: response?.data,\n hasHeaders: true,\n hasTextFilter: true,\n page: response?.meta?.current_page,\n perPage: response?.meta?.per_page,\n total: response?.meta?.total,\n onPage: (p: number) => this.load(section, p, query),\n onSearch: (q: string) => this.load(section, 1, q),\n onRefresh: () => this.load(section, page, query),\n links: {\n secondary: [{ title: 'Edit', fn: (_l: any, item: any) => this.edit(item) }],\n },\n })),\n )\n .subscribe(using((data) => section.data$.next(data)));\n }\n\n selectTab(key: string): void {\n this.active = key;\n this.editing = false;\n this.draft = null;\n }\n\n startCreate(): void {\n this.editing = true;\n const draft: Record<string, any> = {};\n for (const field of this.activeSection.fields) {\n draft[field.key] = field.type === 'boolean' ? false : '';\n }\n this.draft = draft;\n }\n\n edit(item: any): void {\n this.editing = true;\n const draft = { ...item };\n for (const field of this.activeSection.fields) {\n if (field.type === 'json' && draft[field.key] != null && typeof draft[field.key] !== 'string') {\n draft[field.key] = JSON.stringify(draft[field.key], null, 2);\n }\n }\n this.draft = draft;\n }\n\n cancelEdit(): void {\n this.editing = false;\n this.draft = null;\n }\n\n async save(): Promise<void> {\n if (!this.draft) return;\n const section = this.activeSection;\n this.saving = true;\n try {\n const payload: Record<string, any> = {};\n for (const field of section.fields) {\n const raw = this.draft[field.key];\n if (field.type === 'number') {\n payload[field.key] = raw !== null && raw !== undefined && `${raw}` !== '' ? Number(raw) : undefined;\n } else if (field.type === 'boolean') {\n payload[field.key] = !!raw;\n } else if (field.type === 'json') {\n try {\n payload[field.key] = raw && `${raw}`.trim() ? JSON.parse(raw) : undefined;\n } catch {\n payload[field.key] = undefined;\n }\n } else {\n payload[field.key] = raw || undefined;\n }\n }\n\n const source = this.apiService.on(section.mount);\n if (this.draft['id']) {\n await firstValueFrom(source.update(this.draft['id'], payload));\n } else {\n await firstValueFrom(source.create(payload));\n }\n notify(`${titleCase(section.singularLabel)} saved.`, undefined, 'success');\n this.editing = false;\n this.draft = null;\n this.load(section, section.page, section.query);\n } catch {\n notify('Could not save (check required fields).', undefined, 'error');\n } finally {\n this.saving = false;\n }\n }\n\n async remove(): Promise<void> {\n if (!this.draft?.['id']) return;\n const section = this.activeSection;\n try {\n await firstValueFrom(this.apiService.on(section.mount).remove(this.draft['id']));\n notify(`${titleCase(section.singularLabel)} deleted.`, undefined, 'success');\n this.editing = false;\n this.draft = null;\n this.load(section, section.page, section.query);\n } catch {\n notify('Could not delete — it may still be referenced elsewhere.', undefined, 'error');\n }\n }\n}\n","<div class=\"x-page-head\">\n <div class=\"x-page-title\">Settings</div>\n <div class=\"x-page-subtitle\">\n Channels, shipping and returns configuration.\n </div>\n</div>\n\n<nav class=\"x-settings-tabs\">\n @for (s of sections; track s.key) {\n <button\n type=\"button\"\n class=\"x-settings-tab\"\n [class.is-active]=\"active === s.key\"\n (click)=\"selectTab(s.key)\"\n >\n {{ s.title }}\n </button>\n }\n</nav>\n\n@for (s of sections; track s.key) {\n @if (active === s.key) {\n @if (!editing) {\n <div class=\"x-settings-toolbar\">\n <button type=\"button\" class=\"x-settings-btn x-settings-btn-primary\" (click)=\"startCreate()\">\n + New {{ s.singularLabel }}\n </button>\n </div>\n @if (s.data$ | 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-settings-editor\">\n <div class=\"x-settings-editor-head\">\n <div class=\"x-settings-editor-title\">{{ draft['id'] ? 'Edit' : 'New' }} {{ s.singularLabel }}</div>\n <div class=\"x-settings-editor-actions\">\n @if (draft['id']) {\n <button type=\"button\" class=\"x-settings-btn x-settings-danger\" (click)=\"remove()\">Delete</button>\n }\n <button type=\"button\" class=\"x-settings-btn\" (click)=\"cancelEdit()\">Close</button>\n <button type=\"button\" class=\"x-settings-btn x-settings-btn-primary\" [disabled]=\"saving\" (click)=\"save()\">\n {{ saving ? 'Saving…' : 'Save' }}\n </button>\n </div>\n </div>\n\n <div class=\"x-settings-grid\">\n @for (f of s.fields; track f.key) {\n <label class=\"x-settings-f\" [class.x-settings-f-wide]=\"f.type === 'textarea' || f.type === 'json'\">\n {{ f.label }}\n @if (f.required) {\n <span class=\"x-settings-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.options; track opt.value) {\n <option [value]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @case ('json') {\n <textarea rows=\"4\" class=\"x-settings-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 @default {\n <input type=\"text\" [(ngModel)]=\"draft[f.key]\" [placeholder]=\"f.placeholder || ''\" />\n }\n }\n </label>\n }\n </div>\n </div>\n }\n }\n}\n\n<div class=\"x-settings-more\">\n <p>\n <strong>Users &amp; roles</strong> —\n <a routerLink=\"/admin/users\">manage accounts</a>.\n </p>\n <p>\n <strong>Notifications</strong> — transactional and marketing templates\n are seeded per environment (NotificationTemplates); per-customer\n preferences are on the customer record.\n </p>\n <p class=\"x-settings-audit\">\n <strong>Audit log</strong> — every privileged action is recorded\n (ntx-apps-audit); <a routerLink=\"/admin/audit\">open the viewer</a>.\n </p>\n</div>\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 { SettingsComponent } from './settings.component';\n\n@NgModule({\n declarations: [SettingsComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: SettingsComponent }]),\n ],\n})\nexport class SettingsModule {}\n"],"names":["i2","i3","i4","i1"],"mappings":"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"}
@@ -115,7 +115,7 @@ class PagesModule {
115
115
  },
116
116
  {
117
117
  path: 'orders',
118
- loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-orders.module-Cfeb0QjH.mjs').then((m) => m.OrdersModule),
118
+ loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-orders.module-vldWwcAr.mjs').then((m) => m.OrdersModule),
119
119
  },
120
120
  {
121
121
  // P4.19 — fulfilment queue: dispatch / deliver / exception.
@@ -124,7 +124,7 @@ class PagesModule {
124
124
  },
125
125
  {
126
126
  path: 'catalog',
127
- loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-catalog.module-DoxDNaNn.mjs').then((m) => m.CatalogModule),
127
+ loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-catalog.module-BwjfFXDU.mjs').then((m) => m.CatalogModule),
128
128
  },
129
129
  {
130
130
  path: 'brands',
@@ -142,6 +142,10 @@ class PagesModule {
142
142
  path: 'compatibility-rules',
143
143
  loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-JiFnNJj_.mjs').then((m) => m.CompatibilityRulesModule),
144
144
  },
145
+ {
146
+ path: 'bulk-upload',
147
+ loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-bulk-upload.module-BGTbsYUP.mjs').then((m) => m.BulkUploadModule),
148
+ },
145
149
  {
146
150
  path: 'data-quality',
147
151
  loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-CvFmMoJe.mjs').then((m) => m.DataQualityModule),
@@ -244,7 +248,7 @@ class PagesModule {
244
248
  },
245
249
  {
246
250
  path: 'settings',
247
- loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-settings.module-Cz6xPNwB.mjs').then((m) => m.SettingsModule),
251
+ loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-settings.module-CFVgAQ2f.mjs').then((m) => m.SettingsModule),
248
252
  },
249
253
  {
250
254
  // P7.3 — queues + SLA board, cron catalogue.
@@ -301,7 +305,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
301
305
  },
302
306
  {
303
307
  path: 'orders',
304
- loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-orders.module-Cfeb0QjH.mjs').then((m) => m.OrdersModule),
308
+ loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-orders.module-vldWwcAr.mjs').then((m) => m.OrdersModule),
305
309
  },
306
310
  {
307
311
  // P4.19 — fulfilment queue: dispatch / deliver / exception.
@@ -310,7 +314,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
310
314
  },
311
315
  {
312
316
  path: 'catalog',
313
- loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-catalog.module-DoxDNaNn.mjs').then((m) => m.CatalogModule),
317
+ loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-catalog.module-BwjfFXDU.mjs').then((m) => m.CatalogModule),
314
318
  },
315
319
  {
316
320
  path: 'brands',
@@ -328,6 +332,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
328
332
  path: 'compatibility-rules',
329
333
  loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-JiFnNJj_.mjs').then((m) => m.CompatibilityRulesModule),
330
334
  },
335
+ {
336
+ path: 'bulk-upload',
337
+ loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-bulk-upload.module-BGTbsYUP.mjs').then((m) => m.BulkUploadModule),
338
+ },
331
339
  {
332
340
  path: 'data-quality',
333
341
  loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-CvFmMoJe.mjs').then((m) => m.DataQualityModule),
@@ -430,7 +438,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
430
438
  },
431
439
  {
432
440
  path: 'settings',
433
- loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-settings.module-Cz6xPNwB.mjs').then((m) => m.SettingsModule),
441
+ loadChildren: () => import('./isaiahiroko-tilmart-ngx-apps-admin-settings.module-CFVgAQ2f.mjs').then((m) => m.SettingsModule),
434
442
  },
435
443
  {
436
444
  // P7.3 — queues + SLA board, cron catalogue.