@isaiahiroko/tilmart-ngx-apps-admin 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +9 -0
  2. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-articles.module-OeLRnPwN.mjs +154 -0
  3. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-articles.module-OeLRnPwN.mjs.map +1 -0
  4. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-audit.module-C9gvw8X2.mjs +84 -0
  5. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-audit.module-C9gvw8X2.mjs.map +1 -0
  6. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-baskets.module-_lHPl1dT.mjs +294 -0
  7. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-baskets.module-_lHPl1dT.mjs.map +1 -0
  8. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-Bvx2tVOi.mjs +224 -0
  9. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-catalog.module-Bvx2tVOi.mjs.map +1 -0
  10. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Ccsd8Yx3.mjs +106 -0
  11. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-claims.module-Ccsd8Yx3.mjs.map +1 -0
  12. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-xN-JUWwp.mjs +261 -0
  13. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-compatibility-rules.module-xN-JUWwp.mjs.map +1 -0
  14. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-content.module-DLdKAUqc.mjs +365 -0
  15. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-content.module-DLdKAUqc.mjs.map +1 -0
  16. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CQNCSlsd.mjs +106 -0
  17. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-coupons.module-CQNCSlsd.mjs.map +1 -0
  18. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-12XdV9zo.mjs +106 -0
  19. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-customers.module-12XdV9zo.mjs.map +1 -0
  20. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-Bfgh3zE5.mjs +286 -0
  21. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-data-quality.module-Bfgh3zE5.mjs.map +1 -0
  22. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-C6tPNlup.mjs +104 -0
  23. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-data-requests.module-C6tPNlup.mjs.map +1 -0
  24. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-disputes.module-DmUoAS0Z.mjs +73 -0
  25. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-disputes.module-DmUoAS0Z.mjs.map +1 -0
  26. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-homepage.module-BZ3UmxE3.mjs +404 -0
  27. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-homepage.module-BZ3UmxE3.mjs.map +1 -0
  28. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-inbox.module-BZDTa1L-.mjs +290 -0
  29. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-inbox.module-BZDTa1L-.mjs.map +1 -0
  30. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-macros.module-BO4bdER2.mjs +112 -0
  31. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-macros.module-BO4bdER2.mjs.map +1 -0
  32. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-moderation.module-CZOCrias.mjs +116 -0
  33. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-moderation.module-CZOCrias.mjs.map +1 -0
  34. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-navigation.module-DoKzzRgn.mjs +281 -0
  35. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-navigation.module-DoKzzRgn.mjs.map +1 -0
  36. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-order-board.module-DIOjhptn.mjs +115 -0
  37. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-order-board.module-DIOjhptn.mjs.map +1 -0
  38. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-orders.module-Ddw49QW0.mjs +185 -0
  39. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-orders.module-Ddw49QW0.mjs.map +1 -0
  40. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-BhqAtTZs.mjs +106 -0
  41. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payments.module-BhqAtTZs.mjs.map +1 -0
  42. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payouts.module-CoiscPIZ.mjs +70 -0
  43. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-payouts.module-CoiscPIZ.mjs.map +1 -0
  44. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-procurement.module-qXW-g0pf.mjs +274 -0
  45. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-procurement.module-qXW-g0pf.mjs.map +1 -0
  46. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG9uTDqX.mjs +106 -0
  47. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-promotions.module-DG9uTDqX.mjs.map +1 -0
  48. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-queues.module-K-zYYVFy.mjs +84 -0
  49. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-queues.module-K-zYYVFy.mjs.map +1 -0
  50. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-Hm2dO_bC.mjs +106 -0
  51. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-redirects.module-Hm2dO_bC.mjs.map +1 -0
  52. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-referrals.module-QLNhuh7q.mjs +106 -0
  53. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-referrals.module-QLNhuh7q.mjs.map +1 -0
  54. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-reports.module-DBZov0We.mjs +104 -0
  55. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-reports.module-DBZov0We.mjs.map +1 -0
  56. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-Bd2gdo6_.mjs +106 -0
  57. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-returns.module-Bd2gdo6_.mjs.map +1 -0
  58. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-rich-editor.module-DW9Gy_QQ.mjs +81 -0
  59. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-rich-editor.module-DW9Gy_QQ.mjs.map +1 -0
  60. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-BkVY06ci.mjs +105 -0
  61. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-routes.module-BkVY06ci.mjs.map +1 -0
  62. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-DKeZTldO.mjs +222 -0
  63. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-settings.module-DKeZTldO.mjs.map +1 -0
  64. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-shipments.module-XuaOKr8v.mjs +150 -0
  65. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-shipments.module-XuaOKr8v.mjs.map +1 -0
  66. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-DYiAkF7E.mjs +107 -0
  67. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-stock.module-DYiAkF7E.mjs.map +1 -0
  68. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-trace.module-Pwr-82Rc.mjs +92 -0
  69. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-trace.module-Pwr-82Rc.mjs.map +1 -0
  70. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-users.module-Bx_7fI2g.mjs +111 -0
  71. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin-users.module-Bx_7fI2g.mjs.map +1 -0
  72. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin.mjs +475 -0
  73. package/fesm2022/isaiahiroko-tilmart-ngx-apps-admin.mjs.map +1 -0
  74. package/package.json +34 -0
  75. package/types/isaiahiroko-tilmart-ngx-apps-admin.d.ts +32 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-homepage.module-BZ3UmxE3.mjs","sources":["../../../projects/admin/src/lib/pages/homepage/homepage.component.ts","../../../projects/admin/src/lib/pages/homepage/homepage.component.html","../../../projects/admin/src/lib/pages/homepage/homepage.module.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { BaseDirective, notify, using } from '@thescaffold/ngx-core';\nimport { catchError, firstValueFrom, forkJoin, of, takeUntil } from 'rxjs';\n\ninterface ModuleVm {\n id: string;\n page: string;\n type: string;\n title?: string;\n position: number;\n config: Record<string, any>;\n channelIds?: string[];\n segmentIds?: string[];\n startsAt?: string;\n endsAt?: string;\n enabled: boolean;\n}\n\ninterface TypeDef {\n value: string;\n label: string;\n // Config fields this type carries. `ids` = comma-separated id list,\n // `id` = single id, `num` = number, `text` = free text / html.\n fields: { key: string; label: string; kind: 'ids' | 'id' | 'num' | 'text' }[];\n}\n\n// P6.7, PROMO-06 / TRD §9.2 — the homepage composer. A drag-ordered list\n// of `MerchandisingModule` rows for a page, each with a type-aware config\n// editor, schedule and targeting, plus a resolved preview line (ids →\n// names) and a page-shape preview mode. The storefront renderer is P6.8.\nconst TYPES: TypeDef[] = [\n {\n value: 'hero_carousel',\n label: 'Hero carousel',\n fields: [{ key: 'bannerIds', label: 'Banner IDs', kind: 'ids' }],\n },\n {\n value: 'promo_banner',\n label: 'Promo banner',\n fields: [{ key: 'bannerIds', label: 'Banner IDs', kind: 'ids' }],\n },\n {\n value: 'product_rail',\n label: 'Product rail',\n fields: [\n { key: 'categoryId', label: 'From category ID', kind: 'id' },\n { key: 'productIds', label: 'Or explicit product IDs', kind: 'ids' },\n { key: 'limit', label: 'Max items', kind: 'num' },\n ],\n },\n {\n value: 'category_grid',\n label: 'Category grid',\n fields: [{ key: 'categoryIds', label: 'Category IDs', kind: 'ids' }],\n },\n {\n value: 'brand_strip',\n label: 'Brand strip',\n fields: [{ key: 'brandIds', label: 'Brand IDs', kind: 'ids' }],\n },\n {\n value: 'flash_sale_rail',\n label: 'Flash sale rail',\n fields: [{ key: 'flashSaleId', label: 'Flash sale ID (blank = all live)', kind: 'id' }],\n },\n {\n value: 'article_rail',\n label: 'Article rail',\n fields: [\n { key: 'articleIds', label: 'Article IDs', kind: 'ids' },\n { key: 'limit', label: 'Max items', kind: 'num' },\n ],\n },\n {\n value: 'rich_text',\n label: 'Rich text',\n fields: [{ key: 'html', label: 'HTML', kind: 'text' }],\n },\n { value: 'newsletter', label: 'Newsletter signup', fields: [] },\n { value: 'trust_bar', label: 'Trust bar', fields: [] },\n];\n\n@Component({\n standalone: false,\n selector: 'x-homepage',\n templateUrl: './homepage.component.html',\n})\nexport class HomepageComponent extends BaseDirective {\n private readonly source = this.apiService.on('apps/commerce/merchandising-module');\n\n readonly types = TYPES;\n\n page = 'home';\n loading = true;\n modules: ModuleVm[] = [];\n previewMode = false;\n\n newType = TYPES[0].value;\n\n editing?: ModuleVm;\n editModel: {\n title: string;\n startsAt: string;\n endsAt: string;\n channelIds: string;\n segmentIds: string;\n config: Record<string, string>;\n } = this.blankEditModel();\n\n private dragIndex: number | null = null;\n\n // id → name lookups for the preview line.\n private productName = new Map<string, string>();\n private categoryName = new Map<string, string>();\n private brandName = new Map<string, string>();\n private bannerName = new Map<string, string>();\n private flashSaleName = new Map<string, string>();\n\n override init = () => {\n this.loadLookups();\n this.load();\n };\n\n typeLabel(value: string): string {\n return TYPES.find((t) => t.value === value)?.label ?? value;\n }\n\n typeDef(value: string): TypeDef | undefined {\n return TYPES.find((t) => t.value === value);\n }\n\n get orderedModules(): ModuleVm[] {\n return [...this.modules].sort((a, b) => a.position - b.position);\n }\n\n get previewModules(): ModuleVm[] {\n const now = Date.now();\n return this.orderedModules.filter(\n (m) =>\n m.enabled &&\n (!m.startsAt || Date.parse(m.startsAt) <= now) &&\n (!m.endsAt || Date.parse(m.endsAt) >= now),\n );\n }\n\n load(): void {\n this.loading = true;\n this.source\n .getMany({ page: this.page, perPage: 200 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((res: any) => {\n this.modules = ((res?.data ?? []) as ModuleVm[])\n .filter((m) => m.page === this.page)\n .map((m) => ({ ...m, config: m.config ?? {} }))\n .sort((a, b) => a.position - b.position);\n this.loading = false;\n }),\n );\n }\n\n changePage(page: string): void {\n this.page = page.trim() || 'home';\n this.cancelEdit();\n this.load();\n }\n\n private loadLookups(): void {\n forkJoin({\n products: this.apiService\n .on('apps/commerce/product')\n .getMany({ perPage: 500 } as any)\n .pipe(catchError(() => of({ data: [] }))),\n categories: this.apiService\n .on('apps/commerce/category')\n .getMany({ perPage: 500 } as any)\n .pipe(catchError(() => of({ data: [] }))),\n brands: this.apiService\n .on('apps/commerce/brand')\n .getMany({ perPage: 500 } as any)\n .pipe(catchError(() => of({ data: [] }))),\n banners: this.apiService\n .on('apps/commerce/banner')\n .getMany({ perPage: 500 } as any)\n .pipe(catchError(() => of({ data: [] }))),\n flashSales: this.apiService\n .on('apps/commerce/flash-sale')\n .getMany({ perPage: 500 } as any)\n .pipe(catchError(() => of({ data: [] }))),\n })\n .pipe(takeUntil(this.$))\n .subscribe(\n using((res: any) => {\n for (const p of res?.products?.data ?? []) this.productName.set(p.id, p.name);\n for (const c of res?.categories?.data ?? []) this.categoryName.set(c.id, c.name);\n for (const b of res?.brands?.data ?? []) this.brandName.set(b.id, b.name);\n for (const b of res?.banners?.data ?? [])\n this.bannerName.set(b.id, b.title || b.slot || b.id);\n for (const f of res?.flashSales?.data ?? []) this.flashSaleName.set(f.id, f.name);\n }),\n );\n }\n\n previewLine(m: ModuleVm): string {\n const c: any = m.config ?? {};\n const names = (ids: any, map: Map<string, string>) =>\n (Array.isArray(ids) ? ids : [])\n .map((id: string) => map.get(id) ?? id)\n .filter(Boolean);\n\n switch (m.type) {\n case 'hero_carousel':\n case 'promo_banner': {\n const list = names(c.bannerIds, this.bannerName);\n return list.length ? `${list.length} banner(s): ${list.join(', ')}` : 'No banners set';\n }\n case 'product_rail': {\n if (c.categoryId) {\n const cat = this.categoryName.get(c.categoryId) ?? c.categoryId;\n return `Top ${c.limit || 12} from ${cat}`;\n }\n const list = names(c.productIds, this.productName);\n return list.length ? `${list.length} product(s): ${list.join(', ')}` : 'No products set';\n }\n case 'category_grid': {\n const list = names(c.categoryIds, this.categoryName);\n return list.length ? list.join(', ') : 'No categories set';\n }\n case 'brand_strip': {\n const list = names(c.brandIds, this.brandName);\n return list.length ? list.join(', ') : 'No brands set';\n }\n case 'flash_sale_rail':\n return c.flashSaleId\n ? this.flashSaleName.get(c.flashSaleId) ?? c.flashSaleId\n : 'All live flash sales';\n case 'article_rail': {\n const n = Array.isArray(c.articleIds) ? c.articleIds.length : 0;\n return n ? `${n} article(s)` : `Latest ${c.limit || 6} articles`;\n }\n case 'rich_text':\n return (c.html ? String(c.html).replace(/<[^>]+>/g, ' ').trim().slice(0, 80) : '') || 'Empty';\n default:\n return '—';\n }\n }\n\n scheduleLine(m: ModuleVm): string {\n if (!m.startsAt && !m.endsAt) return '';\n const fmt = (v?: string) => (v ? new Date(v).toLocaleDateString() : '…');\n return `${fmt(m.startsAt)} → ${fmt(m.endsAt)}`;\n }\n\n // ── ordering ──────────────────────────────────────────────────────\n\n onDragStart(index: number): void {\n this.dragIndex = index;\n }\n\n onDragOver(event: DragEvent): void {\n event.preventDefault();\n }\n\n onDrop(targetIndex: number): void {\n if (this.dragIndex === null || this.dragIndex === targetIndex) return;\n const ordered = this.orderedModules;\n const [moved] = ordered.splice(this.dragIndex, 1);\n ordered.splice(targetIndex, 0, moved);\n this.dragIndex = null;\n void this.persistOrder(ordered);\n }\n\n moveUp(index: number): void {\n if (index <= 0) return;\n const ordered = this.orderedModules;\n [ordered[index - 1], ordered[index]] = [ordered[index], ordered[index - 1]];\n void this.persistOrder(ordered);\n }\n\n moveDown(index: number): void {\n const ordered = this.orderedModules;\n if (index >= ordered.length - 1) return;\n [ordered[index + 1], ordered[index]] = [ordered[index], ordered[index + 1]];\n void this.persistOrder(ordered);\n }\n\n private async persistOrder(ordered: ModuleVm[]): Promise<void> {\n const changed: ModuleVm[] = [];\n ordered.forEach((m, i) => {\n if (m.position !== i) {\n m.position = i;\n changed.push(m);\n }\n });\n this.modules = ordered;\n try {\n await Promise.all(\n changed.map((m) =>\n firstValueFrom(this.source.update(m.id, { position: m.position } as any)),\n ),\n );\n } catch {\n notify('Could not save the new order.', undefined, 'error');\n this.load();\n }\n }\n\n // ── add / toggle / delete ─────────────────────────────────────────\n\n async addModule(): Promise<void> {\n try {\n await firstValueFrom(\n this.source.create({\n page: this.page,\n type: this.newType,\n position: this.modules.length,\n config: {},\n enabled: false,\n } as any),\n );\n notify('Module added.', undefined, 'success');\n this.load();\n } catch {\n notify('Could not add the module.', undefined, 'error');\n }\n }\n\n async toggleEnabled(m: ModuleVm): Promise<void> {\n const next = !m.enabled;\n try {\n await firstValueFrom(this.source.update(m.id, { enabled: next } as any));\n m.enabled = next;\n } catch {\n notify('Could not update the module.', undefined, 'error');\n }\n }\n\n async removeModule(m: ModuleVm): Promise<void> {\n try {\n await firstValueFrom(this.source.remove(m.id));\n this.modules = this.modules.filter((x) => x.id !== m.id);\n if (this.editing?.id === m.id) this.cancelEdit();\n notify('Module removed.', undefined, 'success');\n } catch {\n notify('Could not remove the module.', undefined, 'error');\n }\n }\n\n // ── edit ──────────────────────────────────────────────────────────\n\n editModule(m: ModuleVm): void {\n this.editing = m;\n const config: Record<string, string> = {};\n for (const field of this.typeDef(m.type)?.fields ?? []) {\n const value = (m.config ?? {})[field.key];\n config[field.key] = Array.isArray(value) ? value.join(', ') : value != null ? String(value) : '';\n }\n this.editModel = {\n title: m.title ?? '',\n startsAt: this.toLocalInput(m.startsAt),\n endsAt: this.toLocalInput(m.endsAt),\n channelIds: (m.channelIds ?? []).join(', '),\n segmentIds: (m.segmentIds ?? []).join(', '),\n config,\n };\n }\n\n cancelEdit(): void {\n this.editing = undefined;\n this.editModel = this.blankEditModel();\n }\n\n async saveEdit(): Promise<void> {\n if (!this.editing) return;\n const def = this.typeDef(this.editing.type);\n const config: Record<string, any> = { ...(this.editing.config ?? {}) };\n for (const field of def?.fields ?? []) {\n const raw = (this.editModel.config[field.key] ?? '').trim();\n if (field.kind === 'ids') {\n config[field.key] = raw ? raw.split(',').map((s) => s.trim()).filter(Boolean) : [];\n } else if (field.kind === 'num') {\n config[field.key] = raw ? Number(raw) : undefined;\n } else {\n config[field.key] = raw || undefined;\n }\n }\n const csv = (s: string) => s.split(',').map((x) => x.trim()).filter(Boolean);\n const body = {\n title: this.editModel.title.trim() || undefined,\n startsAt: this.editModel.startsAt ? new Date(this.editModel.startsAt).toISOString() : null,\n endsAt: this.editModel.endsAt ? new Date(this.editModel.endsAt).toISOString() : null,\n channelIds: csv(this.editModel.channelIds),\n segmentIds: csv(this.editModel.segmentIds),\n config,\n };\n try {\n await firstValueFrom(this.source.update(this.editing.id, body as any));\n Object.assign(this.editing, {\n title: body.title,\n startsAt: body.startsAt ?? undefined,\n endsAt: body.endsAt ?? undefined,\n channelIds: body.channelIds,\n segmentIds: body.segmentIds,\n config,\n });\n notify('Module saved.', undefined, 'success');\n this.cancelEdit();\n } catch {\n notify('Could not save the module.', undefined, 'error');\n }\n }\n\n private blankEditModel() {\n return {\n title: '',\n startsAt: '',\n endsAt: '',\n channelIds: '',\n segmentIds: '',\n config: {} as Record<string, string>,\n };\n }\n\n private toLocalInput(iso?: string): string {\n if (!iso) return '';\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return '';\n const pad = (n: number) => String(n).padStart(2, '0');\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;\n }\n}\n","<div class=\"x-hc-head\">\n <div>\n <div class=\"x-hc-title\">Homepage composer</div>\n <div class=\"x-hc-sub\">Drag to reorder the modules that build a storefront page.</div>\n </div>\n <div class=\"x-hc-head-controls\">\n <label class=\"x-hc-page-field\">\n Page\n <input\n type=\"text\"\n [ngModel]=\"page\"\n (ngModelChange)=\"changePage($event)\"\n placeholder=\"home\"\n />\n </label>\n <button\n type=\"button\"\n class=\"x-hc-btn\"\n [class.x-hc-btn-active]=\"previewMode\"\n (click)=\"previewMode = !previewMode\"\n >\n {{ previewMode ? 'Exit preview' : 'Preview' }}\n </button>\n </div>\n</div>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n @if (!previewMode) {\n <div class=\"x-hc-add\">\n <select [(ngModel)]=\"newType\">\n @for (t of types; track t.value) {\n <option [value]=\"t.value\">{{ t.label }}</option>\n }\n </select>\n <button type=\"button\" class=\"x-hc-btn x-hc-btn-primary\" (click)=\"addModule()\">\n Add module\n </button>\n </div>\n }\n\n @if (!orderedModules.length) {\n <div class=\"x-hc-empty\">No modules on this page yet.</div>\n }\n\n @if (previewMode) {\n <div class=\"x-hc-preview\">\n @for (m of previewModules; track m.id) {\n <div class=\"x-hc-preview-block\">\n <span class=\"x-hc-preview-type\">{{ typeLabel(m.type) }}</span>\n <span class=\"x-hc-preview-title\">{{ m.title || previewLine(m) }}</span>\n </div>\n } @empty {\n <div class=\"x-hc-empty\">Nothing live on this page right now.</div>\n }\n </div>\n } @else {\n <div class=\"x-hc-list\">\n @for (m of orderedModules; track m.id; let i = $index) {\n <div\n class=\"x-hc-item\"\n [class.x-hc-item-off]=\"!m.enabled\"\n draggable=\"true\"\n (dragstart)=\"onDragStart(i)\"\n (dragover)=\"onDragOver($event)\"\n (drop)=\"onDrop(i)\"\n >\n <div class=\"x-hc-item-main\">\n <span class=\"x-hc-grip\" title=\"Drag to reorder\">⠿</span>\n <div class=\"x-hc-order\">\n <button type=\"button\" (click)=\"moveUp(i)\" [disabled]=\"i === 0\">▲</button>\n <button type=\"button\" (click)=\"moveDown(i)\" [disabled]=\"i === orderedModules.length - 1\">▼</button>\n </div>\n <div class=\"x-hc-item-body\">\n <div class=\"x-hc-item-row\">\n <span class=\"x-hc-type\">{{ typeLabel(m.type) }}</span>\n @if (m.title) {\n <span class=\"x-hc-item-title\">{{ m.title }}</span>\n }\n @if (scheduleLine(m)) {\n <span class=\"x-hc-sched\">{{ scheduleLine(m) }}</span>\n }\n </div>\n <div class=\"x-hc-item-preview\">{{ previewLine(m) }}</div>\n </div>\n <div class=\"x-hc-item-actions\">\n <button type=\"button\" class=\"x-hc-toggle\" (click)=\"toggleEnabled(m)\">\n {{ m.enabled ? 'On' : 'Off' }}\n </button>\n <button type=\"button\" class=\"x-hc-link\" (click)=\"editModule(m)\">Edit</button>\n <button type=\"button\" class=\"x-hc-link x-hc-danger\" (click)=\"removeModule(m)\">Delete</button>\n </div>\n </div>\n\n @if (editing?.id === m.id) {\n <div class=\"x-hc-edit\">\n <label class=\"x-hc-f\">\n Title\n <input type=\"text\" [(ngModel)]=\"editModel.title\" />\n </label>\n @for (field of typeDef(m.type)?.fields ?? []; track field.key) {\n <label class=\"x-hc-f\">\n {{ field.label }}\n @if (field.kind === 'text') {\n <textarea rows=\"3\" [(ngModel)]=\"editModel.config[field.key]\"></textarea>\n } @else {\n <input\n [type]=\"field.kind === 'num' ? 'number' : 'text'\"\n [(ngModel)]=\"editModel.config[field.key]\"\n />\n }\n </label>\n }\n <div class=\"x-hc-f-row\">\n <label class=\"x-hc-f\">\n Starts\n <input type=\"datetime-local\" [(ngModel)]=\"editModel.startsAt\" />\n </label>\n <label class=\"x-hc-f\">\n Ends\n <input type=\"datetime-local\" [(ngModel)]=\"editModel.endsAt\" />\n </label>\n </div>\n <div class=\"x-hc-f-row\">\n <label class=\"x-hc-f\">\n Channel IDs\n <input type=\"text\" [(ngModel)]=\"editModel.channelIds\" placeholder=\"blank = all\" />\n </label>\n <label class=\"x-hc-f\">\n Segment IDs\n <input type=\"text\" [(ngModel)]=\"editModel.segmentIds\" placeholder=\"blank = all\" />\n </label>\n </div>\n <div class=\"x-hc-edit-actions\">\n <button type=\"button\" class=\"x-hc-btn x-hc-btn-primary\" (click)=\"saveEdit()\">Save</button>\n <button type=\"button\" class=\"x-hc-btn\" (click)=\"cancelEdit()\">Cancel</button>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n}\n\n<style>\n .x-hc-head {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 16px;\n margin-bottom: 20px;\n flex-wrap: wrap;\n }\n .x-hc-title { font-size: 24px; font-weight: 800; color: var(--color-text); }\n .x-hc-sub { font-size: 13.5px; color: var(--color-text-muted); margin-top: 2px; }\n .x-hc-head-controls { display: flex; gap: 10px; align-items: flex-end; }\n .x-hc-page-field {\n display: flex; flex-direction: column; gap: 4px;\n font-size: 11.5px; color: var(--color-text-muted); font-weight: 700;\n text-transform: uppercase; letter-spacing: 0.4px;\n }\n .x-hc-page-field input, .x-hc-add select, .x-hc-f input, .x-hc-f textarea {\n border: 1px solid var(--color-border);\n border-radius: var(--radius-md);\n padding: 6px 10px;\n font-size: 13px;\n background: var(--color-surface);\n color: var(--color-text);\n }\n .x-hc-add { display: flex; gap: 10px; margin-bottom: 16px; }\n .x-hc-btn {\n border: 1px solid var(--color-border);\n background: transparent;\n border-radius: var(--radius-md);\n padding: 6px 14px;\n font-size: 12.5px;\n font-weight: 700;\n cursor: pointer;\n color: var(--color-text-muted);\n }\n .x-hc-btn-primary { border-color: var(--color-accent); color: var(--color-accent-hover); }\n .x-hc-btn-active { border-color: var(--color-accent); color: var(--color-accent-hover); }\n .x-hc-empty { font-size: 14px; color: var(--color-text-muted); }\n .x-hc-list { display: flex; flex-direction: column; gap: 10px; max-width: 860px; }\n .x-hc-item {\n background: var(--color-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-lg);\n padding: 12px 14px;\n }\n .x-hc-item-off { opacity: 0.55; }\n .x-hc-item-main { display: flex; align-items: center; gap: 12px; }\n .x-hc-grip { cursor: grab; color: var(--color-text-muted); font-size: 16px; }\n .x-hc-order { display: flex; flex-direction: column; }\n .x-hc-order button {\n border: 0; background: transparent; cursor: pointer;\n color: var(--color-text-muted); font-size: 10px; line-height: 1.1; padding: 1px 3px;\n }\n .x-hc-order button:disabled { opacity: 0.3; cursor: default; }\n .x-hc-item-body { flex: 1; min-width: 0; }\n .x-hc-item-row { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }\n .x-hc-type {\n font-weight: 800; text-transform: uppercase; letter-spacing: 0.4px;\n font-size: 11px; color: var(--color-accent-hover);\n }\n .x-hc-item-title { font-size: 13.5px; font-weight: 600; color: var(--color-text); }\n .x-hc-sched { font-size: 11px; color: var(--color-text-muted); }\n .x-hc-item-preview { font-size: 12.5px; color: var(--color-text-muted); margin-top: 2px; }\n .x-hc-item-actions { display: flex; gap: 8px; align-items: center; }\n .x-hc-toggle {\n border: 1px solid var(--color-border); border-radius: var(--radius-sm);\n background: transparent; padding: 3px 10px; font-size: 11.5px; font-weight: 700;\n cursor: pointer; color: var(--color-text-muted);\n }\n .x-hc-link {\n border: 0; background: transparent; cursor: pointer;\n font-size: 12px; font-weight: 700; color: var(--color-accent-hover);\n }\n .x-hc-danger { color: var(--color-danger, #c0362c); }\n .x-hc-edit {\n margin-top: 12px; padding-top: 12px;\n border-top: 1px solid var(--color-border);\n display: flex; flex-direction: column; gap: 10px;\n }\n .x-hc-f {\n display: flex; flex-direction: column; gap: 4px; flex: 1;\n font-size: 11.5px; color: var(--color-text-muted); font-weight: 700;\n }\n .x-hc-f-row { display: flex; gap: 10px; }\n .x-hc-edit-actions { display: flex; gap: 8px; }\n .x-hc-preview { display: flex; flex-direction: column; gap: 6px; max-width: 860px; }\n .x-hc-preview-block {\n border: 1px dashed var(--color-border-strong);\n border-radius: var(--radius-md);\n padding: 18px 16px;\n display: flex; flex-direction: column; gap: 4px;\n background: var(--color-surface);\n }\n .x-hc-preview-type {\n font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.5px;\n color: var(--color-accent-hover);\n }\n .x-hc-preview-title { font-size: 14px; color: var(--color-text); }\n</style>\n","import { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { SharedModule } from '@thescaffold/ngx-core';\nimport { ElementsModule } from '@thescaffold/ngx-ui';\nimport { HomepageComponent } from './homepage.component';\n\n@NgModule({\n declarations: [HomepageComponent],\n imports: [\n SharedModule,\n ElementsModule,\n RouterModule.forChild([{ path: '', component: HomepageComponent }]),\n ],\n})\nexport class HomepageModule {}\n"],"names":["i2","i1"],"mappings":"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"}
@@ -0,0 +1,290 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Component, NgModule } from '@angular/core';
3
+ import * as i1$3 from '@angular/router';
4
+ import { RouterModule } from '@angular/router';
5
+ import * as i1 from '@thescaffold/ngx-core';
6
+ import { BaseDirective, using, notify, SharedModule } from '@thescaffold/ngx-core';
7
+ import * as i1$2 from '@thescaffold/ngx-ui';
8
+ import { ElementsModule, WebgetsModule } from '@thescaffold/ngx-ui';
9
+ import { takeUntil, firstValueFrom } from 'rxjs';
10
+ import * as i1$1 from '@angular/forms';
11
+ import * as i2 from '@angular/common';
12
+
13
+ const PRIORITIES = ['urgent', 'high', 'normal', 'low'];
14
+ const STATUSES = ['open', 'in_progress', 'resolved', 'closed'];
15
+ const CATEGORIES = [
16
+ 'order-issue',
17
+ 'delivery',
18
+ 'payment-refund',
19
+ 'product-question',
20
+ 'warranty-return',
21
+ 'account',
22
+ 'complaint',
23
+ 'other',
24
+ ];
25
+ const RESOLUTION_CODES = [
26
+ 'resolved-info-given',
27
+ 'resolved-order-action',
28
+ 'resolved-refund',
29
+ 'resolved-replacement',
30
+ 'resolved-no-fault',
31
+ 'duplicate',
32
+ 'no-response',
33
+ ];
34
+ const FILTERS = ['all', 'unassigned', 'mine', 'open', 'breaching'];
35
+ // P7.5, SUP-02/SUP-03 — the unified support inbox. One queue over every
36
+ // ComplaintLog regardless of channel (WhatsApp / phone / email / web
37
+ // form), with a ticket detail pane: customer + linked-order context
38
+ // (GET complaint-log/:id/context), priority / category / assignment /
39
+ // status / resolution coding, an internal-notes thread, canned responses
40
+ // (SupportMacro), an SLA countdown, and the inline WhatsApp thread when
41
+ // the ticket came in over WhatsApp.
42
+ class InboxComponent extends BaseDirective {
43
+ constructor(controlService) {
44
+ super();
45
+ this.controlService = controlService;
46
+ this.tickets = this.apiService.on('apps/commerce/complaint-log');
47
+ this.macros = this.apiService.on('apps/commerce/support-macro');
48
+ this.waSessions = this.apiService.on('apps/whatsapp/conversation-session');
49
+ this.waMessages = this.apiService.on('apps/whatsapp/message-log');
50
+ this.priorities = PRIORITIES;
51
+ this.statuses = STATUSES;
52
+ this.categories = CATEGORIES;
53
+ this.resolutionCodes = RESOLUTION_CODES;
54
+ this.filters = FILTERS;
55
+ this.loading = true;
56
+ this.all = [];
57
+ this.filter = 'all';
58
+ this.context = {};
59
+ this.contextLoading = false;
60
+ this.macroList = [];
61
+ this.reply = '';
62
+ this.newNote = '';
63
+ this.waThread = [];
64
+ // agent identity for "mine" / assignment
65
+ this.agent = this.controlService?.getMeta?.()?.ref ?? 'agent';
66
+ this.init = () => {
67
+ this.load();
68
+ this.macros
69
+ .getMany({ perPage: 200 })
70
+ .pipe(takeUntil(this.$))
71
+ .subscribe(using((res) => {
72
+ this.macroList = (res?.data ?? []).filter((m) => m.enabled !== false);
73
+ }));
74
+ };
75
+ // --- log a non-WhatsApp inbound (phone call, forwarded email) ---
76
+ this.newOpen = false;
77
+ this.draft = { channel: 'phone', subject: '', note: '', contactPhone: '', contactEmail: '' };
78
+ }
79
+ load() {
80
+ this.loading = true;
81
+ this.tickets
82
+ .getMany({ perPage: 200 })
83
+ .pipe(takeUntil(this.$))
84
+ .subscribe(using((res) => {
85
+ this.all = (res?.data ?? []).sort((a, b) => this.rank(a) - this.rank(b) ||
86
+ new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
87
+ this.loading = false;
88
+ if (this.selected) {
89
+ this.selected = this.all.find((t) => t.id === this.selected.id);
90
+ }
91
+ }));
92
+ }
93
+ rank(t) {
94
+ if (t.resolvedAt || t.status === 'resolved' || t.status === 'closed')
95
+ return 5;
96
+ const p = { urgent: 0, high: 1, normal: 2, low: 3 }[t.priority ?? 'normal'];
97
+ return p ?? 2;
98
+ }
99
+ get queue() {
100
+ switch (this.filter) {
101
+ case 'unassigned':
102
+ return this.all.filter((t) => !t.assignedTo && !this.isDone(t));
103
+ case 'mine':
104
+ return this.all.filter((t) => t.assignedTo === this.agent);
105
+ case 'open':
106
+ return this.all.filter((t) => !this.isDone(t));
107
+ case 'breaching':
108
+ return this.all.filter((t) => this.isBreaching(t));
109
+ default:
110
+ return this.all;
111
+ }
112
+ }
113
+ isDone(t) {
114
+ return !!t.resolvedAt || t.status === 'resolved' || t.status === 'closed';
115
+ }
116
+ isBreaching(t) {
117
+ if (this.isDone(t) || t.firstResponseAt || !t.slaDueAt)
118
+ return false;
119
+ return new Date(t.slaDueAt).getTime() < Date.now();
120
+ }
121
+ slaLabel(t) {
122
+ if (this.isDone(t))
123
+ return 'done';
124
+ if (t.firstResponseAt)
125
+ return 'responded';
126
+ if (!t.slaDueAt)
127
+ return '';
128
+ const mins = Math.round((new Date(t.slaDueAt).getTime() - Date.now()) / 60000);
129
+ if (mins < 0)
130
+ return `${Math.abs(Math.round(mins / 60))}h over`;
131
+ if (mins < 60)
132
+ return `${mins}m left`;
133
+ return `${Math.round(mins / 60)}h left`;
134
+ }
135
+ channelLabel(t) {
136
+ return (t.channel ?? 'web').replace(/_/g, ' ');
137
+ }
138
+ async select(t) {
139
+ this.selected = t;
140
+ this.reply = '';
141
+ this.newNote = '';
142
+ this.context = {};
143
+ this.waThread = [];
144
+ this.contextLoading = true;
145
+ try {
146
+ const res = await firstValueFrom(this.tickets.getOne(`${t.id}/context`));
147
+ const data = res?.data?.data ?? res?.data ?? {};
148
+ this.context = { customer: data.customer, order: data.order };
149
+ }
150
+ catch {
151
+ this.context = {};
152
+ }
153
+ finally {
154
+ this.contextLoading = false;
155
+ }
156
+ if (t.channel === 'whatsapp' && t.contactPhone) {
157
+ this.loadWaThread(t.contactPhone);
158
+ }
159
+ }
160
+ loadWaThread(phone) {
161
+ this.waSessions
162
+ .getMany({ perPage: 100 })
163
+ .pipe(takeUntil(this.$))
164
+ .subscribe(using((res) => {
165
+ const session = (res?.data ?? []).find((s) => (s.phone ?? '').replace(/\D/g, '') === phone.replace(/\D/g, ''));
166
+ if (!session)
167
+ return;
168
+ this.waMessages
169
+ .getMany({ perPage: 200, sessionId: session.id })
170
+ .pipe(takeUntil(this.$))
171
+ .subscribe(using((mres) => {
172
+ this.waThread = (mres?.data ?? [])
173
+ .slice()
174
+ .sort((a, b) => new Date(a.createdAt).getTime() -
175
+ new Date(b.createdAt).getTime())
176
+ .map((m) => ({
177
+ id: m.id,
178
+ direction: m.direction,
179
+ text: m.text ?? m.templateName ?? '',
180
+ createdAt: m.createdAt,
181
+ }));
182
+ }));
183
+ }));
184
+ }
185
+ async patch(fields) {
186
+ if (!this.selected)
187
+ return;
188
+ try {
189
+ await firstValueFrom(this.tickets.update(this.selected.id, fields));
190
+ this.load();
191
+ }
192
+ catch {
193
+ notify('Could not update the ticket.', undefined, 'error');
194
+ }
195
+ }
196
+ assignToMe() {
197
+ void this.patch({ assignedTo: this.agent, status: 'in_progress' });
198
+ }
199
+ async addNote() {
200
+ if (!this.selected || !this.newNote.trim())
201
+ return;
202
+ try {
203
+ await firstValueFrom(this.apiService.post(`apps/commerce/complaint-log/${this.selected.id}/note`, { text: this.newNote.trim(), by: this.agent }));
204
+ this.newNote = '';
205
+ this.load();
206
+ }
207
+ catch {
208
+ notify('Could not add the note.', undefined, 'error');
209
+ }
210
+ }
211
+ applyMacro(macro) {
212
+ this.reply = this.reply ? `${this.reply}\n\n${macro.body}` : macro.body;
213
+ }
214
+ async markReplied() {
215
+ if (!this.selected)
216
+ return;
217
+ try {
218
+ await firstValueFrom(this.apiService.post(`apps/commerce/complaint-log/${this.selected.id}/first-response`, {}));
219
+ notify('Marked as first-responded — SLA clock stopped.', undefined, 'success');
220
+ this.load();
221
+ }
222
+ catch {
223
+ notify('Could not mark the reply.', undefined, 'error');
224
+ }
225
+ }
226
+ async resolve(code) {
227
+ if (!this.selected || !code)
228
+ return;
229
+ await this.patch({
230
+ status: 'resolved',
231
+ resolutionCode: code,
232
+ resolvedAt: new Date().toISOString(),
233
+ });
234
+ }
235
+ async createTicket() {
236
+ if (!this.draft.note.trim())
237
+ return;
238
+ try {
239
+ await firstValueFrom(this.tickets.create({
240
+ type: 'support_request',
241
+ channel: this.draft.channel,
242
+ subject: this.draft.subject || undefined,
243
+ note: this.draft.note.trim(),
244
+ priority: 'normal',
245
+ contactPhone: this.draft.contactPhone || undefined,
246
+ contactEmail: this.draft.contactEmail || undefined,
247
+ loggedBy: this.agent,
248
+ }));
249
+ this.newOpen = false;
250
+ this.draft = { channel: 'phone', subject: '', note: '', contactPhone: '', contactEmail: '' };
251
+ notify('Ticket logged.', undefined, 'success');
252
+ this.load();
253
+ }
254
+ catch {
255
+ notify('Could not log the ticket.', undefined, 'error');
256
+ }
257
+ }
258
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: InboxComponent, deps: [{ token: i1.ControlService }], target: i0.ɵɵFactoryTarget.Component }); }
259
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: InboxComponent, isStandalone: false, selector: "x-inbox", usesInheritance: true, ngImport: i0, template: "<div class=\"x-page-head\">\n <div>\n <div class=\"x-page-title\">Support inbox</div>\n <div class=\"x-page-subtitle\">\n Every contact channel in one queue \u2014 WhatsApp, phone, email, web form.\n </div>\n </div>\n <button type=\"button\" class=\"x-ib-new-btn\" (click)=\"newOpen = !newOpen\">\n {{ newOpen ? 'Cancel' : 'Log a ticket' }}\n </button>\n</div>\n\n@if (newOpen) {\n <form class=\"x-ib-new\" (ngSubmit)=\"createTicket()\">\n <div class=\"x-ib-new-row\">\n <select [(ngModel)]=\"draft.channel\" name=\"channel\">\n <option value=\"phone\">Phone</option>\n <option value=\"email\">Email</option>\n <option value=\"web\">Web form</option>\n <option value=\"walk_in\">Walk-in</option>\n </select>\n <input placeholder=\"Subject\" [(ngModel)]=\"draft.subject\" name=\"subject\" />\n <input placeholder=\"Contact phone\" [(ngModel)]=\"draft.contactPhone\" name=\"phone\" />\n <input placeholder=\"Contact email\" [(ngModel)]=\"draft.contactEmail\" name=\"email\" />\n </div>\n <textarea placeholder=\"What did the customer contact us about?\" rows=\"2\" [(ngModel)]=\"draft.note\" name=\"note\"></textarea>\n <button type=\"submit\" class=\"x-ib-new-btn\" [disabled]=\"!draft.note.trim()\">Log ticket</button>\n </form>\n}\n\n<nav class=\"x-ib-filters\">\n @for (f of filters; track f) {\n <button type=\"button\" class=\"x-ib-filter\" [class.is-active]=\"filter === f\" (click)=\"filter = f\">\n {{ f }}\n </button>\n }\n</nav>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-ib-layout\">\n <div class=\"x-ib-queue\">\n @for (t of queue; track t.id) {\n <button\n type=\"button\"\n class=\"x-ib-ticket\"\n [class.is-active]=\"selected?.id === t.id\"\n [class.is-breaching]=\"isBreaching(t)\"\n (click)=\"select(t)\"\n >\n <div class=\"x-ib-ticket-top\">\n <span class=\"x-ib-chan\">{{ channelLabel(t) }}</span>\n @if (t.priority) { <span class=\"x-ib-prio x-ib-prio-{{ t.priority }}\">{{ t.priority }}</span> }\n <span class=\"x-ib-sla\" [class.is-bad]=\"isBreaching(t)\">{{ slaLabel(t) }}</span>\n </div>\n <div class=\"x-ib-ticket-subject\">{{ t.subject || t.type }}</div>\n <div class=\"x-ib-ticket-note\">{{ t.note }}</div>\n <div class=\"x-ib-ticket-meta\">\n <span>{{ t.status.replace('_', ' ') }}</span>\n @if (t.assignedTo) { <span>\u00B7 {{ t.assignedTo }}</span> }\n </div>\n </button>\n } @empty {\n <div class=\"x-ib-empty\">Nothing in this view.</div>\n }\n </div>\n\n <div class=\"x-ib-detail\">\n @if (!selected) {\n <div class=\"x-ib-empty\">Select a ticket.</div>\n } @else {\n <div class=\"x-ib-d-head\">\n <div class=\"x-ib-d-title\">{{ selected.subject || selected.type }}</div>\n <div class=\"x-ib-d-sub\">\n {{ channelLabel(selected) }} \u00B7 opened {{ selected.createdAt | date: 'short' }}\n </div>\n </div>\n\n <!-- context -->\n <div class=\"x-ib-context\">\n @if (contextLoading) {\n <span class=\"x-ib-muted\">Loading context\u2026</span>\n } @else {\n <div class=\"x-ib-ctx-block\">\n <div class=\"x-ib-ctx-label\">Customer</div>\n @if (context.customer) {\n <div>{{ context.customer.name || '\u2014' }}</div>\n <div class=\"x-ib-muted\">{{ context.customer.email }} {{ context.customer.phone }}</div>\n } @else {\n <div class=\"x-ib-muted\">{{ selected.contactEmail || selected.contactPhone || 'Not linked' }}</div>\n }\n </div>\n @if (context.order) {\n <div class=\"x-ib-ctx-block\">\n <div class=\"x-ib-ctx-label\">Linked order</div>\n <div><strong>{{ context.order.orderNumber }}</strong> \u00B7 {{ context.order.status.replace('_', ' ') }}</div>\n <div class=\"x-ib-muted\">\n \u20A6{{ context.order.grandTotal | number: '1.0-0' }} \u00B7\n {{ context.order.lines.length }} line(s)\n </div>\n </div>\n }\n }\n </div>\n\n <!-- controls -->\n <div class=\"x-ib-controls\">\n <label>Priority\n <select [ngModel]=\"selected.priority || 'normal'\" (ngModelChange)=\"patch({ priority: $event })\">\n @for (p of priorities; track p) { <option [value]=\"p\">{{ p }}</option> }\n </select>\n </label>\n <label>Category\n <select [ngModel]=\"selected.category || ''\" (ngModelChange)=\"patch({ category: $event })\">\n <option value=\"\">\u2014</option>\n @for (c of categories; track c) { <option [value]=\"c\">{{ c }}</option> }\n </select>\n </label>\n <label>Status\n <select [ngModel]=\"selected.status\" (ngModelChange)=\"patch({ status: $event })\">\n @for (s of statuses; track s) { <option [value]=\"s\">{{ s.replace('_', ' ') }}</option> }\n </select>\n </label>\n @if (selected.assignedTo !== agent) {\n <button type=\"button\" class=\"x-ib-btn\" (click)=\"assignToMe()\">Assign to me</button>\n } @else {\n <span class=\"x-ib-muted\">Assigned to you</span>\n }\n </div>\n\n <!-- whatsapp thread -->\n @if (waThread.length) {\n <div class=\"x-ib-ctx-label\">WhatsApp thread</div>\n <div class=\"x-ib-wa\">\n @for (m of waThread; track m.id) {\n <div class=\"x-ib-wa-row\" [class.is-out]=\"m.direction === 'outbound'\">\n <div class=\"x-ib-wa-bubble\" [class.is-out]=\"m.direction === 'outbound'\">{{ m.text }}</div>\n </div>\n }\n </div>\n }\n\n <!-- reply + macros -->\n <div class=\"x-ib-ctx-label\">Reply</div>\n @if (macroList.length) {\n <div class=\"x-ib-macros\">\n @for (m of macroList; track m.id) {\n <button type=\"button\" class=\"x-ib-macro\" (click)=\"applyMacro(m)\">{{ m.title }}</button>\n }\n </div>\n }\n <textarea class=\"x-ib-reply\" rows=\"4\" [(ngModel)]=\"reply\" placeholder=\"Draft a reply (copy into WhatsApp / email), then mark it sent.\"></textarea>\n <div class=\"x-ib-reply-actions\">\n <button type=\"button\" class=\"x-ib-btn\" (click)=\"markReplied()\">Mark replied (stops SLA)</button>\n @if (selected.firstResponseAt) {\n <span class=\"x-ib-muted\">First response {{ selected.firstResponseAt | date: 'short' }}</span>\n }\n </div>\n\n <!-- internal notes -->\n <div class=\"x-ib-ctx-label\">Internal notes</div>\n <div class=\"x-ib-notes\">\n @for (n of selected.internalNotes || []; track n.at) {\n <div class=\"x-ib-note\">\n <div class=\"x-ib-muted\">{{ n.by || 'agent' }} \u00B7 {{ n.at | date: 'short' }}</div>\n <div>{{ n.text }}</div>\n </div>\n } @empty {\n <div class=\"x-ib-muted\">No notes yet.</div>\n }\n </div>\n <div class=\"x-ib-note-add\">\n <input [(ngModel)]=\"newNote\" placeholder=\"Add an internal note\u2026\" (keyup.enter)=\"addNote()\" />\n <button type=\"button\" class=\"x-ib-btn\" (click)=\"addNote()\">Add</button>\n </div>\n\n <!-- resolve -->\n <div class=\"x-ib-resolve\">\n <div class=\"x-ib-ctx-label\">Resolve</div>\n <div class=\"x-ib-resolve-codes\">\n @for (code of resolutionCodes; track code) {\n <button type=\"button\" class=\"x-ib-macro\" (click)=\"resolve(code)\">{{ code }}</button>\n }\n </div>\n @if (selected.resolutionCode) {\n <span class=\"x-ib-muted\">Resolved: {{ selected.resolutionCode }}</span>\n }\n </div>\n }\n </div>\n </div>\n}\n\n<style>\n .x-page-head { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 16px; }\n .x-page-title { font-size: 24px; font-weight: 800; color: var(--color-text); }\n .x-page-subtitle { font-size: 13.5px; color: var(--color-text-muted); margin-top: 2px; }\n\n .x-ib-new-btn, .x-ib-btn { appearance: none; border: 1px solid var(--color-border); background: var(--color-surface, #fff); color: var(--color-text); border-radius: 8px; padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer; }\n .x-ib-new-btn:disabled { opacity: 0.5; }\n\n .x-ib-new { border: 1px solid var(--color-border); border-radius: 10px; padding: 14px; margin-bottom: 16px; display: flex; flex-direction: column; gap: 8px; }\n .x-ib-new-row { display: flex; flex-wrap: wrap; gap: 8px; }\n .x-ib-new input, .x-ib-new select, .x-ib-new textarea { font: inherit; padding: 7px 10px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); }\n .x-ib-new input { flex: 1; min-width: 130px; }\n\n .x-ib-filters { display: flex; gap: 4px; margin-bottom: 14px; border-bottom: 1px solid var(--color-border); }\n .x-ib-filter { appearance: none; background: transparent; border: 0; border-bottom: 2px solid transparent; padding: 7px 12px; font-size: 12.5px; font-weight: 600; text-transform: capitalize; color: var(--color-text-muted); cursor: pointer; }\n .x-ib-filter.is-active { color: var(--color-text); border-bottom-color: var(--color-brand); }\n\n .x-ib-layout { display: grid; grid-template-columns: 320px 1fr; gap: 16px; align-items: start; }\n @media (max-width: 820px) { .x-ib-layout { grid-template-columns: 1fr; } }\n\n .x-ib-queue { display: flex; flex-direction: column; gap: 6px; max-height: 70vh; overflow-y: auto; }\n .x-ib-ticket { text-align: left; appearance: none; border: 1px solid var(--color-border); border-radius: 10px; background: var(--color-surface, #fff); padding: 10px 12px; cursor: pointer; }\n .x-ib-ticket.is-active { border-color: var(--color-brand); }\n .x-ib-ticket.is-breaching { border-left: 3px solid var(--color-danger, #c0392b); }\n .x-ib-ticket-top { display: flex; gap: 6px; align-items: center; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); margin-bottom: 4px; }\n .x-ib-prio { font-weight: 700; }\n .x-ib-prio-urgent, .x-ib-prio-high { color: var(--color-danger, #c0392b); }\n .x-ib-sla { margin-left: auto; }\n .x-ib-sla.is-bad { color: var(--color-danger, #c0392b); font-weight: 700; }\n .x-ib-ticket-subject { font-size: 13.5px; font-weight: 700; color: var(--color-text); }\n .x-ib-ticket-note { font-size: 12px; color: var(--color-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .x-ib-ticket-meta { font-size: 11px; color: var(--color-text-muted); margin-top: 3px; text-transform: capitalize; }\n\n .x-ib-detail { border: 1px solid var(--color-border); border-radius: 12px; padding: 16px; }\n .x-ib-d-title { font-size: 16px; font-weight: 800; color: var(--color-text); }\n .x-ib-d-sub { font-size: 12px; color: var(--color-text-muted); text-transform: capitalize; margin-bottom: 12px; }\n .x-ib-context { display: flex; flex-wrap: wrap; gap: 16px; padding: 10px; background: color-mix(in srgb, var(--color-border) 20%, transparent); border-radius: 8px; margin-bottom: 12px; font-size: 13px; }\n .x-ib-ctx-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; color: var(--color-text-muted); margin: 14px 0 6px; }\n .x-ib-muted { color: var(--color-text-muted); font-size: 12px; }\n\n .x-ib-controls { display: flex; flex-wrap: wrap; gap: 10px; align-items: end; }\n .x-ib-controls label { display: flex; flex-direction: column; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); gap: 3px; }\n .x-ib-controls select { font: inherit; padding: 5px 8px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); text-transform: capitalize; }\n\n .x-ib-wa { display: flex; flex-direction: column; gap: 5px; max-height: 240px; overflow-y: auto; padding: 8px; border: 1px solid var(--color-border); border-radius: 8px; }\n .x-ib-wa-row { display: flex; }\n .x-ib-wa-row.is-out { justify-content: flex-end; }\n .x-ib-wa-bubble { max-width: 75%; font-size: 12.5px; padding: 6px 10px; border-radius: 10px; background: color-mix(in srgb, var(--color-border) 30%, transparent); }\n .x-ib-wa-bubble.is-out { background: var(--color-brand); color: #fff; }\n\n .x-ib-macros, .x-ib-resolve-codes { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 8px; }\n .x-ib-macro { appearance: none; border: 1px solid var(--color-border); background: transparent; color: var(--color-text-muted); border-radius: 999px; padding: 3px 10px; font-size: 11.5px; cursor: pointer; }\n .x-ib-reply, .x-ib-note-add input, .x-ib-new textarea { width: 100%; font: inherit; padding: 8px 10px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); box-sizing: border-box; }\n .x-ib-reply-actions { display: flex; gap: 10px; align-items: center; margin-top: 6px; }\n\n .x-ib-notes { display: flex; flex-direction: column; gap: 6px; }\n .x-ib-note { font-size: 13px; border-left: 2px solid var(--color-border); padding-left: 8px; }\n .x-ib-note-add { display: flex; gap: 8px; margin-top: 8px; }\n .x-ib-note-add input { flex: 1; }\n\n .x-ib-resolve { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--color-border); }\n .x-ib-empty { padding: 24px; text-align: center; font-size: 13px; color: var(--color-text-muted); }\n</style>\n", styles: [".x-page-head{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:16px}.x-page-title{font-size:24px;font-weight:800;color:var(--color-text)}.x-page-subtitle{font-size:13.5px;color:var(--color-text-muted);margin-top:2px}.x-ib-new-btn,.x-ib-btn{appearance:none;border:1px solid var(--color-border);background:var(--color-surface, #fff);color:var(--color-text);border-radius:8px;padding:6px 12px;font-size:12.5px;font-weight:600;cursor:pointer}.x-ib-new-btn:disabled{opacity:.5}.x-ib-new{border:1px solid var(--color-border);border-radius:10px;padding:14px;margin-bottom:16px;display:flex;flex-direction:column;gap:8px}.x-ib-new-row{display:flex;flex-wrap:wrap;gap:8px}.x-ib-new input,.x-ib-new select,.x-ib-new textarea{font:inherit;padding:7px 10px;border:1px solid var(--color-border);border-radius:6px;background:var(--color-surface, #fff);color:var(--color-text)}.x-ib-new input{flex:1;min-width:130px}.x-ib-filters{display:flex;gap:4px;margin-bottom:14px;border-bottom:1px solid var(--color-border)}.x-ib-filter{appearance:none;background:transparent;border:0;border-bottom:2px solid transparent;padding:7px 12px;font-size:12.5px;font-weight:600;text-transform:capitalize;color:var(--color-text-muted);cursor:pointer}.x-ib-filter.is-active{color:var(--color-text);border-bottom-color:var(--color-brand)}.x-ib-layout{display:grid;grid-template-columns:320px 1fr;gap:16px;align-items:start}@media(max-width:820px){.x-ib-layout{grid-template-columns:1fr}}.x-ib-queue{display:flex;flex-direction:column;gap:6px;max-height:70vh;overflow-y:auto}.x-ib-ticket{text-align:left;appearance:none;border:1px solid var(--color-border);border-radius:10px;background:var(--color-surface, #fff);padding:10px 12px;cursor:pointer}.x-ib-ticket.is-active{border-color:var(--color-brand)}.x-ib-ticket.is-breaching{border-left:3px solid var(--color-danger, #c0392b)}.x-ib-ticket-top{display:flex;gap:6px;align-items:center;font-size:10.5px;text-transform:uppercase;letter-spacing:.03em;color:var(--color-text-muted);margin-bottom:4px}.x-ib-prio{font-weight:700}.x-ib-prio-urgent,.x-ib-prio-high{color:var(--color-danger, #c0392b)}.x-ib-sla{margin-left:auto}.x-ib-sla.is-bad{color:var(--color-danger, #c0392b);font-weight:700}.x-ib-ticket-subject{font-size:13.5px;font-weight:700;color:var(--color-text)}.x-ib-ticket-note{font-size:12px;color:var(--color-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.x-ib-ticket-meta{font-size:11px;color:var(--color-text-muted);margin-top:3px;text-transform:capitalize}.x-ib-detail{border:1px solid var(--color-border);border-radius:12px;padding:16px}.x-ib-d-title{font-size:16px;font-weight:800;color:var(--color-text)}.x-ib-d-sub{font-size:12px;color:var(--color-text-muted);text-transform:capitalize;margin-bottom:12px}.x-ib-context{display:flex;flex-wrap:wrap;gap:16px;padding:10px;background:color-mix(in srgb,var(--color-border) 20%,transparent);border-radius:8px;margin-bottom:12px;font-size:13px}.x-ib-ctx-label{font-size:10.5px;text-transform:uppercase;letter-spacing:.04em;font-weight:700;color:var(--color-text-muted);margin:14px 0 6px}.x-ib-muted{color:var(--color-text-muted);font-size:12px}.x-ib-controls{display:flex;flex-wrap:wrap;gap:10px;align-items:end}.x-ib-controls label{display:flex;flex-direction:column;font-size:10.5px;text-transform:uppercase;letter-spacing:.03em;color:var(--color-text-muted);gap:3px}.x-ib-controls select{font:inherit;padding:5px 8px;border:1px solid var(--color-border);border-radius:6px;background:var(--color-surface, #fff);color:var(--color-text);text-transform:capitalize}.x-ib-wa{display:flex;flex-direction:column;gap:5px;max-height:240px;overflow-y:auto;padding:8px;border:1px solid var(--color-border);border-radius:8px}.x-ib-wa-row{display:flex}.x-ib-wa-row.is-out{justify-content:flex-end}.x-ib-wa-bubble{max-width:75%;font-size:12.5px;padding:6px 10px;border-radius:10px;background:color-mix(in srgb,var(--color-border) 30%,transparent)}.x-ib-wa-bubble.is-out{background:var(--color-brand);color:#fff}.x-ib-macros,.x-ib-resolve-codes{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:8px}.x-ib-macro{appearance:none;border:1px solid var(--color-border);background:transparent;color:var(--color-text-muted);border-radius:999px;padding:3px 10px;font-size:11.5px;cursor:pointer}.x-ib-reply,.x-ib-note-add input,.x-ib-new textarea{width:100%;font:inherit;padding:8px 10px;border:1px solid var(--color-border);border-radius:6px;background:var(--color-surface, #fff);color:var(--color-text);box-sizing:border-box}.x-ib-reply-actions{display:flex;gap:10px;align-items:center;margin-top:6px}.x-ib-notes{display:flex;flex-direction:column;gap:6px}.x-ib-note{font-size:13px;border-left:2px solid var(--color-border);padding-left:8px}.x-ib-note-add{display:flex;gap:8px;margin-top:8px}.x-ib-note-add input{flex:1}.x-ib-resolve{margin-top:16px;padding-top:12px;border-top:1px solid var(--color-border)}.x-ib-empty{padding:24px;text-align:center;font-size:13px;color:var(--color-text-muted)}\n"], dependencies: [{ kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.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$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i1$2.LoaderComponent, selector: "x-element-loader", inputs: ["data"] }, { kind: "pipe", type: i2.DecimalPipe, name: "number" }, { kind: "pipe", type: i2.DatePipe, name: "date" }] }); }
260
+ }
261
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: InboxComponent, decorators: [{
262
+ type: Component,
263
+ args: [{ standalone: false, selector: 'x-inbox', template: "<div class=\"x-page-head\">\n <div>\n <div class=\"x-page-title\">Support inbox</div>\n <div class=\"x-page-subtitle\">\n Every contact channel in one queue \u2014 WhatsApp, phone, email, web form.\n </div>\n </div>\n <button type=\"button\" class=\"x-ib-new-btn\" (click)=\"newOpen = !newOpen\">\n {{ newOpen ? 'Cancel' : 'Log a ticket' }}\n </button>\n</div>\n\n@if (newOpen) {\n <form class=\"x-ib-new\" (ngSubmit)=\"createTicket()\">\n <div class=\"x-ib-new-row\">\n <select [(ngModel)]=\"draft.channel\" name=\"channel\">\n <option value=\"phone\">Phone</option>\n <option value=\"email\">Email</option>\n <option value=\"web\">Web form</option>\n <option value=\"walk_in\">Walk-in</option>\n </select>\n <input placeholder=\"Subject\" [(ngModel)]=\"draft.subject\" name=\"subject\" />\n <input placeholder=\"Contact phone\" [(ngModel)]=\"draft.contactPhone\" name=\"phone\" />\n <input placeholder=\"Contact email\" [(ngModel)]=\"draft.contactEmail\" name=\"email\" />\n </div>\n <textarea placeholder=\"What did the customer contact us about?\" rows=\"2\" [(ngModel)]=\"draft.note\" name=\"note\"></textarea>\n <button type=\"submit\" class=\"x-ib-new-btn\" [disabled]=\"!draft.note.trim()\">Log ticket</button>\n </form>\n}\n\n<nav class=\"x-ib-filters\">\n @for (f of filters; track f) {\n <button type=\"button\" class=\"x-ib-filter\" [class.is-active]=\"filter === f\" (click)=\"filter = f\">\n {{ f }}\n </button>\n }\n</nav>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-ib-layout\">\n <div class=\"x-ib-queue\">\n @for (t of queue; track t.id) {\n <button\n type=\"button\"\n class=\"x-ib-ticket\"\n [class.is-active]=\"selected?.id === t.id\"\n [class.is-breaching]=\"isBreaching(t)\"\n (click)=\"select(t)\"\n >\n <div class=\"x-ib-ticket-top\">\n <span class=\"x-ib-chan\">{{ channelLabel(t) }}</span>\n @if (t.priority) { <span class=\"x-ib-prio x-ib-prio-{{ t.priority }}\">{{ t.priority }}</span> }\n <span class=\"x-ib-sla\" [class.is-bad]=\"isBreaching(t)\">{{ slaLabel(t) }}</span>\n </div>\n <div class=\"x-ib-ticket-subject\">{{ t.subject || t.type }}</div>\n <div class=\"x-ib-ticket-note\">{{ t.note }}</div>\n <div class=\"x-ib-ticket-meta\">\n <span>{{ t.status.replace('_', ' ') }}</span>\n @if (t.assignedTo) { <span>\u00B7 {{ t.assignedTo }}</span> }\n </div>\n </button>\n } @empty {\n <div class=\"x-ib-empty\">Nothing in this view.</div>\n }\n </div>\n\n <div class=\"x-ib-detail\">\n @if (!selected) {\n <div class=\"x-ib-empty\">Select a ticket.</div>\n } @else {\n <div class=\"x-ib-d-head\">\n <div class=\"x-ib-d-title\">{{ selected.subject || selected.type }}</div>\n <div class=\"x-ib-d-sub\">\n {{ channelLabel(selected) }} \u00B7 opened {{ selected.createdAt | date: 'short' }}\n </div>\n </div>\n\n <!-- context -->\n <div class=\"x-ib-context\">\n @if (contextLoading) {\n <span class=\"x-ib-muted\">Loading context\u2026</span>\n } @else {\n <div class=\"x-ib-ctx-block\">\n <div class=\"x-ib-ctx-label\">Customer</div>\n @if (context.customer) {\n <div>{{ context.customer.name || '\u2014' }}</div>\n <div class=\"x-ib-muted\">{{ context.customer.email }} {{ context.customer.phone }}</div>\n } @else {\n <div class=\"x-ib-muted\">{{ selected.contactEmail || selected.contactPhone || 'Not linked' }}</div>\n }\n </div>\n @if (context.order) {\n <div class=\"x-ib-ctx-block\">\n <div class=\"x-ib-ctx-label\">Linked order</div>\n <div><strong>{{ context.order.orderNumber }}</strong> \u00B7 {{ context.order.status.replace('_', ' ') }}</div>\n <div class=\"x-ib-muted\">\n \u20A6{{ context.order.grandTotal | number: '1.0-0' }} \u00B7\n {{ context.order.lines.length }} line(s)\n </div>\n </div>\n }\n }\n </div>\n\n <!-- controls -->\n <div class=\"x-ib-controls\">\n <label>Priority\n <select [ngModel]=\"selected.priority || 'normal'\" (ngModelChange)=\"patch({ priority: $event })\">\n @for (p of priorities; track p) { <option [value]=\"p\">{{ p }}</option> }\n </select>\n </label>\n <label>Category\n <select [ngModel]=\"selected.category || ''\" (ngModelChange)=\"patch({ category: $event })\">\n <option value=\"\">\u2014</option>\n @for (c of categories; track c) { <option [value]=\"c\">{{ c }}</option> }\n </select>\n </label>\n <label>Status\n <select [ngModel]=\"selected.status\" (ngModelChange)=\"patch({ status: $event })\">\n @for (s of statuses; track s) { <option [value]=\"s\">{{ s.replace('_', ' ') }}</option> }\n </select>\n </label>\n @if (selected.assignedTo !== agent) {\n <button type=\"button\" class=\"x-ib-btn\" (click)=\"assignToMe()\">Assign to me</button>\n } @else {\n <span class=\"x-ib-muted\">Assigned to you</span>\n }\n </div>\n\n <!-- whatsapp thread -->\n @if (waThread.length) {\n <div class=\"x-ib-ctx-label\">WhatsApp thread</div>\n <div class=\"x-ib-wa\">\n @for (m of waThread; track m.id) {\n <div class=\"x-ib-wa-row\" [class.is-out]=\"m.direction === 'outbound'\">\n <div class=\"x-ib-wa-bubble\" [class.is-out]=\"m.direction === 'outbound'\">{{ m.text }}</div>\n </div>\n }\n </div>\n }\n\n <!-- reply + macros -->\n <div class=\"x-ib-ctx-label\">Reply</div>\n @if (macroList.length) {\n <div class=\"x-ib-macros\">\n @for (m of macroList; track m.id) {\n <button type=\"button\" class=\"x-ib-macro\" (click)=\"applyMacro(m)\">{{ m.title }}</button>\n }\n </div>\n }\n <textarea class=\"x-ib-reply\" rows=\"4\" [(ngModel)]=\"reply\" placeholder=\"Draft a reply (copy into WhatsApp / email), then mark it sent.\"></textarea>\n <div class=\"x-ib-reply-actions\">\n <button type=\"button\" class=\"x-ib-btn\" (click)=\"markReplied()\">Mark replied (stops SLA)</button>\n @if (selected.firstResponseAt) {\n <span class=\"x-ib-muted\">First response {{ selected.firstResponseAt | date: 'short' }}</span>\n }\n </div>\n\n <!-- internal notes -->\n <div class=\"x-ib-ctx-label\">Internal notes</div>\n <div class=\"x-ib-notes\">\n @for (n of selected.internalNotes || []; track n.at) {\n <div class=\"x-ib-note\">\n <div class=\"x-ib-muted\">{{ n.by || 'agent' }} \u00B7 {{ n.at | date: 'short' }}</div>\n <div>{{ n.text }}</div>\n </div>\n } @empty {\n <div class=\"x-ib-muted\">No notes yet.</div>\n }\n </div>\n <div class=\"x-ib-note-add\">\n <input [(ngModel)]=\"newNote\" placeholder=\"Add an internal note\u2026\" (keyup.enter)=\"addNote()\" />\n <button type=\"button\" class=\"x-ib-btn\" (click)=\"addNote()\">Add</button>\n </div>\n\n <!-- resolve -->\n <div class=\"x-ib-resolve\">\n <div class=\"x-ib-ctx-label\">Resolve</div>\n <div class=\"x-ib-resolve-codes\">\n @for (code of resolutionCodes; track code) {\n <button type=\"button\" class=\"x-ib-macro\" (click)=\"resolve(code)\">{{ code }}</button>\n }\n </div>\n @if (selected.resolutionCode) {\n <span class=\"x-ib-muted\">Resolved: {{ selected.resolutionCode }}</span>\n }\n </div>\n }\n </div>\n </div>\n}\n\n<style>\n .x-page-head { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 16px; }\n .x-page-title { font-size: 24px; font-weight: 800; color: var(--color-text); }\n .x-page-subtitle { font-size: 13.5px; color: var(--color-text-muted); margin-top: 2px; }\n\n .x-ib-new-btn, .x-ib-btn { appearance: none; border: 1px solid var(--color-border); background: var(--color-surface, #fff); color: var(--color-text); border-radius: 8px; padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer; }\n .x-ib-new-btn:disabled { opacity: 0.5; }\n\n .x-ib-new { border: 1px solid var(--color-border); border-radius: 10px; padding: 14px; margin-bottom: 16px; display: flex; flex-direction: column; gap: 8px; }\n .x-ib-new-row { display: flex; flex-wrap: wrap; gap: 8px; }\n .x-ib-new input, .x-ib-new select, .x-ib-new textarea { font: inherit; padding: 7px 10px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); }\n .x-ib-new input { flex: 1; min-width: 130px; }\n\n .x-ib-filters { display: flex; gap: 4px; margin-bottom: 14px; border-bottom: 1px solid var(--color-border); }\n .x-ib-filter { appearance: none; background: transparent; border: 0; border-bottom: 2px solid transparent; padding: 7px 12px; font-size: 12.5px; font-weight: 600; text-transform: capitalize; color: var(--color-text-muted); cursor: pointer; }\n .x-ib-filter.is-active { color: var(--color-text); border-bottom-color: var(--color-brand); }\n\n .x-ib-layout { display: grid; grid-template-columns: 320px 1fr; gap: 16px; align-items: start; }\n @media (max-width: 820px) { .x-ib-layout { grid-template-columns: 1fr; } }\n\n .x-ib-queue { display: flex; flex-direction: column; gap: 6px; max-height: 70vh; overflow-y: auto; }\n .x-ib-ticket { text-align: left; appearance: none; border: 1px solid var(--color-border); border-radius: 10px; background: var(--color-surface, #fff); padding: 10px 12px; cursor: pointer; }\n .x-ib-ticket.is-active { border-color: var(--color-brand); }\n .x-ib-ticket.is-breaching { border-left: 3px solid var(--color-danger, #c0392b); }\n .x-ib-ticket-top { display: flex; gap: 6px; align-items: center; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); margin-bottom: 4px; }\n .x-ib-prio { font-weight: 700; }\n .x-ib-prio-urgent, .x-ib-prio-high { color: var(--color-danger, #c0392b); }\n .x-ib-sla { margin-left: auto; }\n .x-ib-sla.is-bad { color: var(--color-danger, #c0392b); font-weight: 700; }\n .x-ib-ticket-subject { font-size: 13.5px; font-weight: 700; color: var(--color-text); }\n .x-ib-ticket-note { font-size: 12px; color: var(--color-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .x-ib-ticket-meta { font-size: 11px; color: var(--color-text-muted); margin-top: 3px; text-transform: capitalize; }\n\n .x-ib-detail { border: 1px solid var(--color-border); border-radius: 12px; padding: 16px; }\n .x-ib-d-title { font-size: 16px; font-weight: 800; color: var(--color-text); }\n .x-ib-d-sub { font-size: 12px; color: var(--color-text-muted); text-transform: capitalize; margin-bottom: 12px; }\n .x-ib-context { display: flex; flex-wrap: wrap; gap: 16px; padding: 10px; background: color-mix(in srgb, var(--color-border) 20%, transparent); border-radius: 8px; margin-bottom: 12px; font-size: 13px; }\n .x-ib-ctx-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; color: var(--color-text-muted); margin: 14px 0 6px; }\n .x-ib-muted { color: var(--color-text-muted); font-size: 12px; }\n\n .x-ib-controls { display: flex; flex-wrap: wrap; gap: 10px; align-items: end; }\n .x-ib-controls label { display: flex; flex-direction: column; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); gap: 3px; }\n .x-ib-controls select { font: inherit; padding: 5px 8px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); text-transform: capitalize; }\n\n .x-ib-wa { display: flex; flex-direction: column; gap: 5px; max-height: 240px; overflow-y: auto; padding: 8px; border: 1px solid var(--color-border); border-radius: 8px; }\n .x-ib-wa-row { display: flex; }\n .x-ib-wa-row.is-out { justify-content: flex-end; }\n .x-ib-wa-bubble { max-width: 75%; font-size: 12.5px; padding: 6px 10px; border-radius: 10px; background: color-mix(in srgb, var(--color-border) 30%, transparent); }\n .x-ib-wa-bubble.is-out { background: var(--color-brand); color: #fff; }\n\n .x-ib-macros, .x-ib-resolve-codes { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 8px; }\n .x-ib-macro { appearance: none; border: 1px solid var(--color-border); background: transparent; color: var(--color-text-muted); border-radius: 999px; padding: 3px 10px; font-size: 11.5px; cursor: pointer; }\n .x-ib-reply, .x-ib-note-add input, .x-ib-new textarea { width: 100%; font: inherit; padding: 8px 10px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); box-sizing: border-box; }\n .x-ib-reply-actions { display: flex; gap: 10px; align-items: center; margin-top: 6px; }\n\n .x-ib-notes { display: flex; flex-direction: column; gap: 6px; }\n .x-ib-note { font-size: 13px; border-left: 2px solid var(--color-border); padding-left: 8px; }\n .x-ib-note-add { display: flex; gap: 8px; margin-top: 8px; }\n .x-ib-note-add input { flex: 1; }\n\n .x-ib-resolve { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--color-border); }\n .x-ib-empty { padding: 24px; text-align: center; font-size: 13px; color: var(--color-text-muted); }\n</style>\n" }]
264
+ }], ctorParameters: () => [{ type: i1.ControlService }] });
265
+
266
+ class InboxModule {
267
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: InboxModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
268
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.16", ngImport: i0, type: InboxModule, declarations: [InboxComponent], imports: [SharedModule,
269
+ ElementsModule,
270
+ WebgetsModule, i1$3.RouterModule] }); }
271
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: InboxModule, imports: [SharedModule,
272
+ ElementsModule,
273
+ WebgetsModule,
274
+ RouterModule.forChild([{ path: '', component: InboxComponent }])] }); }
275
+ }
276
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: InboxModule, decorators: [{
277
+ type: NgModule,
278
+ args: [{
279
+ declarations: [InboxComponent],
280
+ imports: [
281
+ SharedModule,
282
+ ElementsModule,
283
+ WebgetsModule,
284
+ RouterModule.forChild([{ path: '', component: InboxComponent }]),
285
+ ],
286
+ }]
287
+ }] });
288
+
289
+ export { InboxModule };
290
+ //# sourceMappingURL=isaiahiroko-tilmart-ngx-apps-admin-inbox.module-BZDTa1L-.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isaiahiroko-tilmart-ngx-apps-admin-inbox.module-BZDTa1L-.mjs","sources":["../../../projects/admin/src/lib/pages/inbox/inbox.component.ts","../../../projects/admin/src/lib/pages/inbox/inbox.component.html","../../../projects/admin/src/lib/pages/inbox/inbox.module.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport {\n BaseDirective,\n ControlService,\n notify,\n using,\n} from '@thescaffold/ngx-core';\nimport { firstValueFrom, takeUntil } from 'rxjs';\n\ninterface TicketVm {\n id: string;\n type: string;\n subject?: string;\n note: string;\n status: string;\n channel?: string;\n priority?: string;\n category?: string;\n customerId?: string;\n orderId?: string;\n contactPhone?: string;\n contactEmail?: string;\n assignedTo?: string;\n resolutionCode?: string;\n internalNotes?: { by?: string; at: string; text: string }[];\n firstResponseAt?: string;\n slaDueAt?: string;\n resolvedAt?: string;\n createdAt: string;\n}\n\ninterface WaMessageVm {\n id: string;\n direction: string;\n text: string;\n createdAt: string;\n}\n\nconst PRIORITIES = ['urgent', 'high', 'normal', 'low'];\nconst STATUSES = ['open', 'in_progress', 'resolved', 'closed'];\nconst CATEGORIES = [\n 'order-issue',\n 'delivery',\n 'payment-refund',\n 'product-question',\n 'warranty-return',\n 'account',\n 'complaint',\n 'other',\n];\nconst RESOLUTION_CODES = [\n 'resolved-info-given',\n 'resolved-order-action',\n 'resolved-refund',\n 'resolved-replacement',\n 'resolved-no-fault',\n 'duplicate',\n 'no-response',\n];\nconst FILTERS = ['all', 'unassigned', 'mine', 'open', 'breaching'] as const;\ntype FilterKey = (typeof FILTERS)[number];\n\n// P7.5, SUP-02/SUP-03 — the unified support inbox. One queue over every\n// ComplaintLog regardless of channel (WhatsApp / phone / email / web\n// form), with a ticket detail pane: customer + linked-order context\n// (GET complaint-log/:id/context), priority / category / assignment /\n// status / resolution coding, an internal-notes thread, canned responses\n// (SupportMacro), an SLA countdown, and the inline WhatsApp thread when\n// the ticket came in over WhatsApp.\n@Component({\n standalone: false,\n selector: 'x-inbox',\n templateUrl: './inbox.component.html',\n})\nexport class InboxComponent extends BaseDirective {\n private readonly tickets = this.apiService.on('apps/commerce/complaint-log');\n private readonly macros = this.apiService.on('apps/commerce/support-macro');\n private readonly waSessions = this.apiService.on(\n 'apps/whatsapp/conversation-session',\n );\n private readonly waMessages = this.apiService.on('apps/whatsapp/message-log');\n\n readonly priorities = PRIORITIES;\n readonly statuses = STATUSES;\n readonly categories = CATEGORIES;\n readonly resolutionCodes = RESOLUTION_CODES;\n readonly filters = FILTERS;\n\n loading = true;\n all: TicketVm[] = [];\n filter: FilterKey = 'all';\n\n selected?: TicketVm;\n context: { customer?: any; order?: any } = {};\n contextLoading = false;\n macroList: any[] = [];\n reply = '';\n newNote = '';\n waThread: WaMessageVm[] = [];\n\n // agent identity for \"mine\" / assignment\n readonly agent =\n (this.controlService?.getMeta?.() as any)?.ref ?? 'agent';\n\n constructor(private readonly controlService: ControlService) {\n super();\n }\n\n override init = () => {\n this.load();\n this.macros\n .getMany({ perPage: 200 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((res: any) => {\n this.macroList = (res?.data ?? []).filter(\n (m: any) => m.enabled !== false,\n );\n }),\n );\n };\n\n load(): void {\n this.loading = true;\n this.tickets\n .getMany({ perPage: 200 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((res: any) => {\n this.all = (res?.data ?? []).sort(\n (a: TicketVm, b: TicketVm) =>\n this.rank(a) - this.rank(b) ||\n new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(),\n );\n this.loading = false;\n if (this.selected) {\n this.selected = this.all.find((t) => t.id === this.selected!.id);\n }\n }),\n );\n }\n\n private rank(t: TicketVm): number {\n if (t.resolvedAt || t.status === 'resolved' || t.status === 'closed') return 5;\n const p = { urgent: 0, high: 1, normal: 2, low: 3 }[t.priority ?? 'normal'];\n return p ?? 2;\n }\n\n get queue(): TicketVm[] {\n switch (this.filter) {\n case 'unassigned':\n return this.all.filter((t) => !t.assignedTo && !this.isDone(t));\n case 'mine':\n return this.all.filter((t) => t.assignedTo === this.agent);\n case 'open':\n return this.all.filter((t) => !this.isDone(t));\n case 'breaching':\n return this.all.filter((t) => this.isBreaching(t));\n default:\n return this.all;\n }\n }\n\n isDone(t: TicketVm): boolean {\n return !!t.resolvedAt || t.status === 'resolved' || t.status === 'closed';\n }\n\n isBreaching(t: TicketVm): boolean {\n if (this.isDone(t) || t.firstResponseAt || !t.slaDueAt) return false;\n return new Date(t.slaDueAt).getTime() < Date.now();\n }\n\n slaLabel(t: TicketVm): string {\n if (this.isDone(t)) return 'done';\n if (t.firstResponseAt) return 'responded';\n if (!t.slaDueAt) return '';\n const mins = Math.round(\n (new Date(t.slaDueAt).getTime() - Date.now()) / 60000,\n );\n if (mins < 0) return `${Math.abs(Math.round(mins / 60))}h over`;\n if (mins < 60) return `${mins}m left`;\n return `${Math.round(mins / 60)}h left`;\n }\n\n channelLabel(t: TicketVm): string {\n return (t.channel ?? 'web').replace(/_/g, ' ');\n }\n\n async select(t: TicketVm): Promise<void> {\n this.selected = t;\n this.reply = '';\n this.newNote = '';\n this.context = {};\n this.waThread = [];\n this.contextLoading = true;\n try {\n const res: any = await firstValueFrom(this.tickets.getOne(`${t.id}/context`));\n const data = res?.data?.data ?? res?.data ?? {};\n this.context = { customer: data.customer, order: data.order };\n } catch {\n this.context = {};\n } finally {\n this.contextLoading = false;\n }\n if (t.channel === 'whatsapp' && t.contactPhone) {\n this.loadWaThread(t.contactPhone);\n }\n }\n\n private loadWaThread(phone: string): void {\n this.waSessions\n .getMany({ perPage: 100 } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((res: any) => {\n const session = (res?.data ?? []).find(\n (s: any) => (s.phone ?? '').replace(/\\D/g, '') === phone.replace(/\\D/g, ''),\n );\n if (!session) return;\n this.waMessages\n .getMany({ perPage: 200, sessionId: session.id } as any)\n .pipe(takeUntil(this.$))\n .subscribe(\n using((mres: any) => {\n this.waThread = (mres?.data ?? [])\n .slice()\n .sort(\n (a: any, b: any) =>\n new Date(a.createdAt).getTime() -\n new Date(b.createdAt).getTime(),\n )\n .map((m: any) => ({\n id: m.id,\n direction: m.direction,\n text: m.text ?? m.templateName ?? '',\n createdAt: m.createdAt,\n }));\n }),\n );\n }),\n );\n }\n\n async patch(fields: Partial<TicketVm>): Promise<void> {\n if (!this.selected) return;\n try {\n await firstValueFrom(this.tickets.update(this.selected.id, fields as any));\n this.load();\n } catch {\n notify('Could not update the ticket.', undefined, 'error');\n }\n }\n\n assignToMe(): void {\n void this.patch({ assignedTo: this.agent, status: 'in_progress' });\n }\n\n async addNote(): Promise<void> {\n if (!this.selected || !this.newNote.trim()) return;\n try {\n await firstValueFrom(\n this.apiService.post(\n `apps/commerce/complaint-log/${this.selected.id}/note`,\n { text: this.newNote.trim(), by: this.agent },\n ),\n );\n this.newNote = '';\n this.load();\n } catch {\n notify('Could not add the note.', undefined, 'error');\n }\n }\n\n applyMacro(macro: any): void {\n this.reply = this.reply ? `${this.reply}\\n\\n${macro.body}` : macro.body;\n }\n\n async markReplied(): Promise<void> {\n if (!this.selected) return;\n try {\n await firstValueFrom(\n this.apiService.post(\n `apps/commerce/complaint-log/${this.selected.id}/first-response`,\n {},\n ),\n );\n notify('Marked as first-responded — SLA clock stopped.', undefined, 'success');\n this.load();\n } catch {\n notify('Could not mark the reply.', undefined, 'error');\n }\n }\n\n async resolve(code: string): Promise<void> {\n if (!this.selected || !code) return;\n await this.patch({\n status: 'resolved',\n resolutionCode: code,\n resolvedAt: new Date().toISOString() as any,\n });\n }\n\n // --- log a non-WhatsApp inbound (phone call, forwarded email) ---\n newOpen = false;\n draft = { channel: 'phone', subject: '', note: '', contactPhone: '', contactEmail: '' };\n\n async createTicket(): Promise<void> {\n if (!this.draft.note.trim()) return;\n try {\n await firstValueFrom(\n this.tickets.create({\n type: 'support_request',\n channel: this.draft.channel,\n subject: this.draft.subject || undefined,\n note: this.draft.note.trim(),\n priority: 'normal',\n contactPhone: this.draft.contactPhone || undefined,\n contactEmail: this.draft.contactEmail || undefined,\n loggedBy: this.agent,\n } as any),\n );\n this.newOpen = false;\n this.draft = { channel: 'phone', subject: '', note: '', contactPhone: '', contactEmail: '' };\n notify('Ticket logged.', undefined, 'success');\n this.load();\n } catch {\n notify('Could not log the ticket.', undefined, 'error');\n }\n }\n}\n","<div class=\"x-page-head\">\n <div>\n <div class=\"x-page-title\">Support inbox</div>\n <div class=\"x-page-subtitle\">\n Every contact channel in one queue — WhatsApp, phone, email, web form.\n </div>\n </div>\n <button type=\"button\" class=\"x-ib-new-btn\" (click)=\"newOpen = !newOpen\">\n {{ newOpen ? 'Cancel' : 'Log a ticket' }}\n </button>\n</div>\n\n@if (newOpen) {\n <form class=\"x-ib-new\" (ngSubmit)=\"createTicket()\">\n <div class=\"x-ib-new-row\">\n <select [(ngModel)]=\"draft.channel\" name=\"channel\">\n <option value=\"phone\">Phone</option>\n <option value=\"email\">Email</option>\n <option value=\"web\">Web form</option>\n <option value=\"walk_in\">Walk-in</option>\n </select>\n <input placeholder=\"Subject\" [(ngModel)]=\"draft.subject\" name=\"subject\" />\n <input placeholder=\"Contact phone\" [(ngModel)]=\"draft.contactPhone\" name=\"phone\" />\n <input placeholder=\"Contact email\" [(ngModel)]=\"draft.contactEmail\" name=\"email\" />\n </div>\n <textarea placeholder=\"What did the customer contact us about?\" rows=\"2\" [(ngModel)]=\"draft.note\" name=\"note\"></textarea>\n <button type=\"submit\" class=\"x-ib-new-btn\" [disabled]=\"!draft.note.trim()\">Log ticket</button>\n </form>\n}\n\n<nav class=\"x-ib-filters\">\n @for (f of filters; track f) {\n <button type=\"button\" class=\"x-ib-filter\" [class.is-active]=\"filter === f\" (click)=\"filter = f\">\n {{ f }}\n </button>\n }\n</nav>\n\n@if (loading) {\n <x-element-loader></x-element-loader>\n} @else {\n <div class=\"x-ib-layout\">\n <div class=\"x-ib-queue\">\n @for (t of queue; track t.id) {\n <button\n type=\"button\"\n class=\"x-ib-ticket\"\n [class.is-active]=\"selected?.id === t.id\"\n [class.is-breaching]=\"isBreaching(t)\"\n (click)=\"select(t)\"\n >\n <div class=\"x-ib-ticket-top\">\n <span class=\"x-ib-chan\">{{ channelLabel(t) }}</span>\n @if (t.priority) { <span class=\"x-ib-prio x-ib-prio-{{ t.priority }}\">{{ t.priority }}</span> }\n <span class=\"x-ib-sla\" [class.is-bad]=\"isBreaching(t)\">{{ slaLabel(t) }}</span>\n </div>\n <div class=\"x-ib-ticket-subject\">{{ t.subject || t.type }}</div>\n <div class=\"x-ib-ticket-note\">{{ t.note }}</div>\n <div class=\"x-ib-ticket-meta\">\n <span>{{ t.status.replace('_', ' ') }}</span>\n @if (t.assignedTo) { <span>· {{ t.assignedTo }}</span> }\n </div>\n </button>\n } @empty {\n <div class=\"x-ib-empty\">Nothing in this view.</div>\n }\n </div>\n\n <div class=\"x-ib-detail\">\n @if (!selected) {\n <div class=\"x-ib-empty\">Select a ticket.</div>\n } @else {\n <div class=\"x-ib-d-head\">\n <div class=\"x-ib-d-title\">{{ selected.subject || selected.type }}</div>\n <div class=\"x-ib-d-sub\">\n {{ channelLabel(selected) }} · opened {{ selected.createdAt | date: 'short' }}\n </div>\n </div>\n\n <!-- context -->\n <div class=\"x-ib-context\">\n @if (contextLoading) {\n <span class=\"x-ib-muted\">Loading context…</span>\n } @else {\n <div class=\"x-ib-ctx-block\">\n <div class=\"x-ib-ctx-label\">Customer</div>\n @if (context.customer) {\n <div>{{ context.customer.name || '—' }}</div>\n <div class=\"x-ib-muted\">{{ context.customer.email }} {{ context.customer.phone }}</div>\n } @else {\n <div class=\"x-ib-muted\">{{ selected.contactEmail || selected.contactPhone || 'Not linked' }}</div>\n }\n </div>\n @if (context.order) {\n <div class=\"x-ib-ctx-block\">\n <div class=\"x-ib-ctx-label\">Linked order</div>\n <div><strong>{{ context.order.orderNumber }}</strong> · {{ context.order.status.replace('_', ' ') }}</div>\n <div class=\"x-ib-muted\">\n ₦{{ context.order.grandTotal | number: '1.0-0' }} ·\n {{ context.order.lines.length }} line(s)\n </div>\n </div>\n }\n }\n </div>\n\n <!-- controls -->\n <div class=\"x-ib-controls\">\n <label>Priority\n <select [ngModel]=\"selected.priority || 'normal'\" (ngModelChange)=\"patch({ priority: $event })\">\n @for (p of priorities; track p) { <option [value]=\"p\">{{ p }}</option> }\n </select>\n </label>\n <label>Category\n <select [ngModel]=\"selected.category || ''\" (ngModelChange)=\"patch({ category: $event })\">\n <option value=\"\">—</option>\n @for (c of categories; track c) { <option [value]=\"c\">{{ c }}</option> }\n </select>\n </label>\n <label>Status\n <select [ngModel]=\"selected.status\" (ngModelChange)=\"patch({ status: $event })\">\n @for (s of statuses; track s) { <option [value]=\"s\">{{ s.replace('_', ' ') }}</option> }\n </select>\n </label>\n @if (selected.assignedTo !== agent) {\n <button type=\"button\" class=\"x-ib-btn\" (click)=\"assignToMe()\">Assign to me</button>\n } @else {\n <span class=\"x-ib-muted\">Assigned to you</span>\n }\n </div>\n\n <!-- whatsapp thread -->\n @if (waThread.length) {\n <div class=\"x-ib-ctx-label\">WhatsApp thread</div>\n <div class=\"x-ib-wa\">\n @for (m of waThread; track m.id) {\n <div class=\"x-ib-wa-row\" [class.is-out]=\"m.direction === 'outbound'\">\n <div class=\"x-ib-wa-bubble\" [class.is-out]=\"m.direction === 'outbound'\">{{ m.text }}</div>\n </div>\n }\n </div>\n }\n\n <!-- reply + macros -->\n <div class=\"x-ib-ctx-label\">Reply</div>\n @if (macroList.length) {\n <div class=\"x-ib-macros\">\n @for (m of macroList; track m.id) {\n <button type=\"button\" class=\"x-ib-macro\" (click)=\"applyMacro(m)\">{{ m.title }}</button>\n }\n </div>\n }\n <textarea class=\"x-ib-reply\" rows=\"4\" [(ngModel)]=\"reply\" placeholder=\"Draft a reply (copy into WhatsApp / email), then mark it sent.\"></textarea>\n <div class=\"x-ib-reply-actions\">\n <button type=\"button\" class=\"x-ib-btn\" (click)=\"markReplied()\">Mark replied (stops SLA)</button>\n @if (selected.firstResponseAt) {\n <span class=\"x-ib-muted\">First response {{ selected.firstResponseAt | date: 'short' }}</span>\n }\n </div>\n\n <!-- internal notes -->\n <div class=\"x-ib-ctx-label\">Internal notes</div>\n <div class=\"x-ib-notes\">\n @for (n of selected.internalNotes || []; track n.at) {\n <div class=\"x-ib-note\">\n <div class=\"x-ib-muted\">{{ n.by || 'agent' }} · {{ n.at | date: 'short' }}</div>\n <div>{{ n.text }}</div>\n </div>\n } @empty {\n <div class=\"x-ib-muted\">No notes yet.</div>\n }\n </div>\n <div class=\"x-ib-note-add\">\n <input [(ngModel)]=\"newNote\" placeholder=\"Add an internal note…\" (keyup.enter)=\"addNote()\" />\n <button type=\"button\" class=\"x-ib-btn\" (click)=\"addNote()\">Add</button>\n </div>\n\n <!-- resolve -->\n <div class=\"x-ib-resolve\">\n <div class=\"x-ib-ctx-label\">Resolve</div>\n <div class=\"x-ib-resolve-codes\">\n @for (code of resolutionCodes; track code) {\n <button type=\"button\" class=\"x-ib-macro\" (click)=\"resolve(code)\">{{ code }}</button>\n }\n </div>\n @if (selected.resolutionCode) {\n <span class=\"x-ib-muted\">Resolved: {{ selected.resolutionCode }}</span>\n }\n </div>\n }\n </div>\n </div>\n}\n\n<style>\n .x-page-head { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 16px; }\n .x-page-title { font-size: 24px; font-weight: 800; color: var(--color-text); }\n .x-page-subtitle { font-size: 13.5px; color: var(--color-text-muted); margin-top: 2px; }\n\n .x-ib-new-btn, .x-ib-btn { appearance: none; border: 1px solid var(--color-border); background: var(--color-surface, #fff); color: var(--color-text); border-radius: 8px; padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer; }\n .x-ib-new-btn:disabled { opacity: 0.5; }\n\n .x-ib-new { border: 1px solid var(--color-border); border-radius: 10px; padding: 14px; margin-bottom: 16px; display: flex; flex-direction: column; gap: 8px; }\n .x-ib-new-row { display: flex; flex-wrap: wrap; gap: 8px; }\n .x-ib-new input, .x-ib-new select, .x-ib-new textarea { font: inherit; padding: 7px 10px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); }\n .x-ib-new input { flex: 1; min-width: 130px; }\n\n .x-ib-filters { display: flex; gap: 4px; margin-bottom: 14px; border-bottom: 1px solid var(--color-border); }\n .x-ib-filter { appearance: none; background: transparent; border: 0; border-bottom: 2px solid transparent; padding: 7px 12px; font-size: 12.5px; font-weight: 600; text-transform: capitalize; color: var(--color-text-muted); cursor: pointer; }\n .x-ib-filter.is-active { color: var(--color-text); border-bottom-color: var(--color-brand); }\n\n .x-ib-layout { display: grid; grid-template-columns: 320px 1fr; gap: 16px; align-items: start; }\n @media (max-width: 820px) { .x-ib-layout { grid-template-columns: 1fr; } }\n\n .x-ib-queue { display: flex; flex-direction: column; gap: 6px; max-height: 70vh; overflow-y: auto; }\n .x-ib-ticket { text-align: left; appearance: none; border: 1px solid var(--color-border); border-radius: 10px; background: var(--color-surface, #fff); padding: 10px 12px; cursor: pointer; }\n .x-ib-ticket.is-active { border-color: var(--color-brand); }\n .x-ib-ticket.is-breaching { border-left: 3px solid var(--color-danger, #c0392b); }\n .x-ib-ticket-top { display: flex; gap: 6px; align-items: center; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); margin-bottom: 4px; }\n .x-ib-prio { font-weight: 700; }\n .x-ib-prio-urgent, .x-ib-prio-high { color: var(--color-danger, #c0392b); }\n .x-ib-sla { margin-left: auto; }\n .x-ib-sla.is-bad { color: var(--color-danger, #c0392b); font-weight: 700; }\n .x-ib-ticket-subject { font-size: 13.5px; font-weight: 700; color: var(--color-text); }\n .x-ib-ticket-note { font-size: 12px; color: var(--color-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .x-ib-ticket-meta { font-size: 11px; color: var(--color-text-muted); margin-top: 3px; text-transform: capitalize; }\n\n .x-ib-detail { border: 1px solid var(--color-border); border-radius: 12px; padding: 16px; }\n .x-ib-d-title { font-size: 16px; font-weight: 800; color: var(--color-text); }\n .x-ib-d-sub { font-size: 12px; color: var(--color-text-muted); text-transform: capitalize; margin-bottom: 12px; }\n .x-ib-context { display: flex; flex-wrap: wrap; gap: 16px; padding: 10px; background: color-mix(in srgb, var(--color-border) 20%, transparent); border-radius: 8px; margin-bottom: 12px; font-size: 13px; }\n .x-ib-ctx-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; color: var(--color-text-muted); margin: 14px 0 6px; }\n .x-ib-muted { color: var(--color-text-muted); font-size: 12px; }\n\n .x-ib-controls { display: flex; flex-wrap: wrap; gap: 10px; align-items: end; }\n .x-ib-controls label { display: flex; flex-direction: column; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); gap: 3px; }\n .x-ib-controls select { font: inherit; padding: 5px 8px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); text-transform: capitalize; }\n\n .x-ib-wa { display: flex; flex-direction: column; gap: 5px; max-height: 240px; overflow-y: auto; padding: 8px; border: 1px solid var(--color-border); border-radius: 8px; }\n .x-ib-wa-row { display: flex; }\n .x-ib-wa-row.is-out { justify-content: flex-end; }\n .x-ib-wa-bubble { max-width: 75%; font-size: 12.5px; padding: 6px 10px; border-radius: 10px; background: color-mix(in srgb, var(--color-border) 30%, transparent); }\n .x-ib-wa-bubble.is-out { background: var(--color-brand); color: #fff; }\n\n .x-ib-macros, .x-ib-resolve-codes { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 8px; }\n .x-ib-macro { appearance: none; border: 1px solid var(--color-border); background: transparent; color: var(--color-text-muted); border-radius: 999px; padding: 3px 10px; font-size: 11.5px; cursor: pointer; }\n .x-ib-reply, .x-ib-note-add input, .x-ib-new textarea { width: 100%; font: inherit; padding: 8px 10px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-surface, #fff); color: var(--color-text); box-sizing: border-box; }\n .x-ib-reply-actions { display: flex; gap: 10px; align-items: center; margin-top: 6px; }\n\n .x-ib-notes { display: flex; flex-direction: column; gap: 6px; }\n .x-ib-note { font-size: 13px; border-left: 2px solid var(--color-border); padding-left: 8px; }\n .x-ib-note-add { display: flex; gap: 8px; margin-top: 8px; }\n .x-ib-note-add input { flex: 1; }\n\n .x-ib-resolve { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--color-border); }\n .x-ib-empty { padding: 24px; text-align: center; font-size: 13px; color: var(--color-text-muted); }\n</style>\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 { InboxComponent } from './inbox.component';\n\n@NgModule({\n declarations: [InboxComponent],\n imports: [\n SharedModule,\n ElementsModule,\n WebgetsModule,\n RouterModule.forChild([{ path: '', component: InboxComponent }]),\n ],\n})\nexport class InboxModule {}\n"],"names":["i2","i3","i4","i1"],"mappings":"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"}