@caribeng/ui 22.4.0 → 22.5.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 (186) hide show
  1. package/README.md +311 -313
  2. package/fesm2022/caribeng-ui-account-picker.mjs +533 -0
  3. package/fesm2022/caribeng-ui-aging.mjs +488 -0
  4. package/fesm2022/{caribeng-ui-caribe-alert.mjs → caribeng-ui-alert.mjs} +45 -45
  5. package/fesm2022/caribeng-ui-amount.mjs +249 -0
  6. package/fesm2022/caribeng-ui-anchor.mjs +2 -2
  7. package/fesm2022/{caribeng-ui-caribe-animation-lottie.mjs → caribeng-ui-animation-lottie.mjs} +41 -41
  8. package/fesm2022/caribeng-ui-audit-log.mjs +323 -0
  9. package/fesm2022/caribeng-ui-avatar.mjs +2 -2
  10. package/fesm2022/caribeng-ui-breadcrumb.mjs +13 -3
  11. package/fesm2022/{caribeng-ui-caribe-button-group.mjs → caribeng-ui-button-group.mjs} +15 -15
  12. package/fesm2022/caribeng-ui-button.mjs +2 -2
  13. package/fesm2022/{caribeng-ui-caribe-card-content.mjs → caribeng-ui-card-content.mjs} +39 -39
  14. package/fesm2022/{caribeng-ui-caribe-card-move.mjs → caribeng-ui-card-move.mjs} +65 -65
  15. package/fesm2022/{caribeng-ui-caribe-card-primary.mjs → caribeng-ui-card-primary.mjs} +31 -31
  16. package/fesm2022/{caribeng-ui-caribe-card-select.mjs → caribeng-ui-card-select.mjs} +67 -67
  17. package/fesm2022/caribeng-ui-carousel.mjs +2 -2
  18. package/fesm2022/caribeng-ui-cascadeselect.mjs +45 -9
  19. package/fesm2022/caribeng-ui-chat.mjs +2 -2
  20. package/fesm2022/{caribeng-ui-caribe-coachmark.mjs → caribeng-ui-coachmark.mjs} +85 -85
  21. package/fesm2022/caribeng-ui-command-palette.mjs +2 -2
  22. package/fesm2022/caribeng-ui-comments.mjs +864 -0
  23. package/fesm2022/caribeng-ui-datagrid.mjs +1635 -0
  24. package/fesm2022/caribeng-ui-dataview.mjs +2 -2
  25. package/fesm2022/caribeng-ui-datepicker.mjs +2 -2
  26. package/fesm2022/caribeng-ui-document.mjs +495 -0
  27. package/fesm2022/{caribeng-ui-caribe-dots-indicator.mjs → caribeng-ui-dots-indicator.mjs} +41 -41
  28. package/fesm2022/caribeng-ui-dynamic-form.mjs +1581 -0
  29. package/fesm2022/caribeng-ui-editor.mjs +2303 -1311
  30. package/fesm2022/{caribeng-ui-caribe-expansion-panel.mjs → caribeng-ui-expansion-panel.mjs} +39 -39
  31. package/fesm2022/{caribeng-ui-caribe-fab-menu.mjs → caribeng-ui-fab-menu.mjs} +75 -75
  32. package/fesm2022/caribeng-ui-file-card.mjs +2 -2
  33. package/fesm2022/{caribeng-ui-caribe-file-download.mjs → caribeng-ui-file-download.mjs} +63 -63
  34. package/fesm2022/caribeng-ui-financial-report.mjs +380 -0
  35. package/fesm2022/{caribeng-ui-caribe-float-menu.mjs → caribeng-ui-float-menu.mjs} +31 -31
  36. package/fesm2022/caribeng-ui-floatlabel.mjs +2 -2
  37. package/fesm2022/caribeng-ui-flow.mjs +1508 -0
  38. package/fesm2022/{caribeng-ui-caribe-footer.mjs → caribeng-ui-footer.mjs} +19 -19
  39. package/fesm2022/caribeng-ui-gantt.mjs +1094 -0
  40. package/fesm2022/{caribeng-ui-caribe-header.mjs → caribeng-ui-header.mjs} +49 -49
  41. package/fesm2022/{caribeng-ui-caribe-icon-button.mjs → caribeng-ui-icon-button.mjs} +23 -23
  42. package/fesm2022/caribeng-ui-image-cropper.mjs +671 -0
  43. package/fesm2022/caribeng-ui-import-wizard.mjs +746 -0
  44. package/fesm2022/caribeng-ui-infinite-scroll.mjs +226 -0
  45. package/fesm2022/caribeng-ui-input-number.mjs +22 -6
  46. package/fesm2022/{caribeng-ui-caribe-input-search.mjs → caribeng-ui-input-search.mjs} +65 -65
  47. package/fesm2022/{caribeng-ui-caribe-input-token.mjs → caribeng-ui-input-token.mjs} +59 -59
  48. package/fesm2022/caribeng-ui-inputotp.mjs +2 -2
  49. package/fesm2022/caribeng-ui-journal-entry.mjs +340 -0
  50. package/fesm2022/caribeng-ui-kanban.mjs +6 -6
  51. package/fesm2022/caribeng-ui-kpi.mjs +189 -0
  52. package/fesm2022/{caribeng-ui-caribe-link.mjs → caribeng-ui-link.mjs} +37 -37
  53. package/fesm2022/caribeng-ui-masonry.mjs +93 -0
  54. package/fesm2022/caribeng-ui-media-player.mjs +821 -0
  55. package/fesm2022/caribeng-ui-mentions.mjs +2 -2
  56. package/fesm2022/caribeng-ui-notification-center.mjs +636 -0
  57. package/fesm2022/{caribeng-ui-caribe-off-canvas.mjs → caribeng-ui-off-canvas.mjs} +55 -55
  58. package/fesm2022/{caribeng-ui-caribe-onboarding.mjs → caribeng-ui-onboarding.mjs} +105 -105
  59. package/fesm2022/caribeng-ui-orderlist.mjs +2 -2
  60. package/fesm2022/caribeng-ui-pdfviewer.mjs +1755 -25
  61. package/fesm2022/caribeng-ui-period-picker.mjs +637 -0
  62. package/fesm2022/caribeng-ui-phone-input.mjs +2 -2
  63. package/fesm2022/caribeng-ui-picklist.mjs +2 -2
  64. package/fesm2022/caribeng-ui-pivot.mjs +419 -0
  65. package/fesm2022/caribeng-ui-pricing.mjs +2 -2
  66. package/fesm2022/caribeng-ui-rating.mjs +14 -17
  67. package/fesm2022/caribeng-ui-reconciliation.mjs +386 -0
  68. package/fesm2022/caribeng-ui-scheduler.mjs +1027 -0
  69. package/fesm2022/caribeng-ui-select.mjs +2 -2
  70. package/fesm2022/{caribeng-ui-caribe-shortcut.mjs → caribeng-ui-shortcut.mjs} +29 -29
  71. package/fesm2022/caribeng-ui-sidebar.mjs +2 -2
  72. package/fesm2022/caribeng-ui-signature-pad.mjs +654 -0
  73. package/fesm2022/{caribeng-ui-caribe-status.mjs → caribeng-ui-status.mjs} +11 -11
  74. package/fesm2022/{caribeng-ui-caribe-step-indicator.mjs → caribeng-ui-step-indicator.mjs} +53 -53
  75. package/fesm2022/caribeng-ui-stepper.mjs +2 -2
  76. package/fesm2022/caribeng-ui-steps.mjs +2 -2
  77. package/fesm2022/caribeng-ui-table.mjs +323 -15
  78. package/fesm2022/caribeng-ui-tabmenu.mjs +2 -2
  79. package/fesm2022/caribeng-ui-tabs.mjs +2 -2
  80. package/fesm2022/{caribeng-ui-caribe-tag-button.mjs → caribeng-ui-tag-button.mjs} +25 -25
  81. package/fesm2022/caribeng-ui-tag.mjs +2 -2
  82. package/fesm2022/caribeng-ui-tieredmenu.mjs +6 -6
  83. package/fesm2022/{caribeng-ui-caribe-tracker.mjs → caribeng-ui-tracker.mjs} +29 -29
  84. package/fesm2022/{caribeng-ui-caribe-transaction-status.mjs → caribeng-ui-transaction-status.mjs} +11 -11
  85. package/fesm2022/caribeng-ui-treeselect.mjs +44 -9
  86. package/fesm2022/caribeng-ui-treetable.mjs +236 -17
  87. package/fesm2022/{caribeng-ui-caribe-voucher.mjs → caribeng-ui-voucher.mjs} +45 -45
  88. package/fesm2022/caribeng-ui-xlsx.mjs +486 -0
  89. package/fesm2022/caribeng-ui.mjs +1181 -1
  90. package/package.json +217 -177
  91. package/types/caribeng-ui-account-picker.d.ts +142 -0
  92. package/types/caribeng-ui-aging.d.ts +123 -0
  93. package/types/{caribeng-ui-caribe-alert.d.ts → caribeng-ui-alert.d.ts} +2 -2
  94. package/types/caribeng-ui-amount.d.ts +82 -0
  95. package/types/{caribeng-ui-caribe-animation-lottie.d.ts → caribeng-ui-animation-lottie.d.ts} +2 -2
  96. package/types/caribeng-ui-audit-log.d.ts +86 -0
  97. package/types/caribeng-ui-breadcrumb.d.ts +2 -2
  98. package/types/{caribeng-ui-caribe-button-group.d.ts → caribeng-ui-button-group.d.ts} +2 -2
  99. package/types/{caribeng-ui-caribe-card-content.d.ts → caribeng-ui-card-content.d.ts} +2 -2
  100. package/types/{caribeng-ui-caribe-card-move.d.ts → caribeng-ui-card-move.d.ts} +1 -1
  101. package/types/{caribeng-ui-caribe-card-primary.d.ts → caribeng-ui-card-primary.d.ts} +2 -2
  102. package/types/{caribeng-ui-caribe-card-select.d.ts → caribeng-ui-card-select.d.ts} +2 -2
  103. package/types/caribeng-ui-cascadeselect.d.ts +6 -0
  104. package/types/{caribeng-ui-caribe-coachmark.d.ts → caribeng-ui-coachmark.d.ts} +2 -2
  105. package/types/caribeng-ui-comments.d.ts +214 -0
  106. package/types/caribeng-ui-datagrid.d.ts +269 -0
  107. package/types/caribeng-ui-document.d.ts +138 -0
  108. package/types/{caribeng-ui-caribe-dots-indicator.d.ts → caribeng-ui-dots-indicator.d.ts} +2 -2
  109. package/types/caribeng-ui-dynamic-form.d.ts +409 -0
  110. package/types/caribeng-ui-editor.d.ts +110 -12
  111. package/types/{caribeng-ui-caribe-expansion-panel.d.ts → caribeng-ui-expansion-panel.d.ts} +1 -1
  112. package/types/{caribeng-ui-caribe-fab-menu.d.ts → caribeng-ui-fab-menu.d.ts} +2 -2
  113. package/types/{caribeng-ui-caribe-file-download.d.ts → caribeng-ui-file-download.d.ts} +1 -1
  114. package/types/caribeng-ui-financial-report.d.ts +102 -0
  115. package/types/{caribeng-ui-caribe-float-menu.d.ts → caribeng-ui-float-menu.d.ts} +2 -2
  116. package/types/caribeng-ui-flow.d.ts +228 -0
  117. package/types/{caribeng-ui-caribe-footer.d.ts → caribeng-ui-footer.d.ts} +2 -2
  118. package/types/caribeng-ui-gantt.d.ts +244 -0
  119. package/types/{caribeng-ui-caribe-header.d.ts → caribeng-ui-header.d.ts} +2 -2
  120. package/types/{caribeng-ui-caribe-icon-button.d.ts → caribeng-ui-icon-button.d.ts} +2 -2
  121. package/types/{caribeng-ui-caribe-illustration.d.ts → caribeng-ui-illustration.d.ts} +1 -1
  122. package/types/caribeng-ui-image-cropper.d.ts +169 -0
  123. package/types/caribeng-ui-import-wizard.d.ts +149 -0
  124. package/types/caribeng-ui-infinite-scroll.d.ts +75 -0
  125. package/types/caribeng-ui-input-number.d.ts +1 -1
  126. package/types/{caribeng-ui-caribe-input-search.d.ts → caribeng-ui-input-search.d.ts} +2 -2
  127. package/types/{caribeng-ui-caribe-input-token.d.ts → caribeng-ui-input-token.d.ts} +2 -2
  128. package/types/caribeng-ui-journal-entry.d.ts +110 -0
  129. package/types/caribeng-ui-kpi.d.ts +64 -0
  130. package/types/{caribeng-ui-caribe-link.d.ts → caribeng-ui-link.d.ts} +2 -2
  131. package/types/{caribeng-ui-caribe-logo.d.ts → caribeng-ui-logo.d.ts} +1 -1
  132. package/types/caribeng-ui-masonry.d.ts +32 -0
  133. package/types/caribeng-ui-media-player.d.ts +140 -0
  134. package/types/caribeng-ui-notification-center.d.ts +153 -0
  135. package/types/{caribeng-ui-caribe-off-canvas.d.ts → caribeng-ui-off-canvas.d.ts} +2 -2
  136. package/types/{caribeng-ui-caribe-onboarding.d.ts → caribeng-ui-onboarding.d.ts} +2 -2
  137. package/types/caribeng-ui-pdfviewer.d.ts +245 -5
  138. package/types/caribeng-ui-period-picker.d.ts +148 -0
  139. package/types/caribeng-ui-pivot.d.ts +116 -0
  140. package/types/caribeng-ui-reconciliation.d.ts +109 -0
  141. package/types/caribeng-ui-scheduler.d.ts +188 -0
  142. package/types/{caribeng-ui-caribe-shortcut.d.ts → caribeng-ui-shortcut.d.ts} +2 -2
  143. package/types/caribeng-ui-signature-pad.d.ts +151 -0
  144. package/types/{caribeng-ui-caribe-status.d.ts → caribeng-ui-status.d.ts} +2 -2
  145. package/types/{caribeng-ui-caribe-step-indicator.d.ts → caribeng-ui-step-indicator.d.ts} +2 -2
  146. package/types/caribeng-ui-table.d.ts +66 -2
  147. package/types/{caribeng-ui-caribe-tag-button.d.ts → caribeng-ui-tag-button.d.ts} +2 -2
  148. package/types/{caribeng-ui-caribe-tracker.d.ts → caribeng-ui-tracker.d.ts} +2 -2
  149. package/types/{caribeng-ui-caribe-transaction-status.d.ts → caribeng-ui-transaction-status.d.ts} +2 -2
  150. package/types/caribeng-ui-treeselect.d.ts +6 -0
  151. package/types/caribeng-ui-treetable.d.ts +67 -2
  152. package/types/{caribeng-ui-caribe-voucher.d.ts → caribeng-ui-voucher.d.ts} +2 -2
  153. package/types/caribeng-ui-xlsx.d.ts +79 -0
  154. package/types/caribeng-ui.d.ts +711 -2
  155. package/fesm2022/caribeng-ui-caribe-calendar.mjs +0 -92
  156. package/fesm2022/caribeng-ui-caribe-circle-loading.mjs +0 -130
  157. package/fesm2022/caribeng-ui-caribe-data-table.mjs +0 -94
  158. package/fesm2022/caribeng-ui-caribe-file-select.mjs +0 -88
  159. package/fesm2022/caribeng-ui-caribe-inline-alert.mjs +0 -100
  160. package/fesm2022/caribeng-ui-caribe-input-date.mjs +0 -138
  161. package/fesm2022/caribeng-ui-caribe-input-select.mjs +0 -154
  162. package/fesm2022/caribeng-ui-caribe-loader.mjs +0 -48
  163. package/fesm2022/caribeng-ui-caribe-modal.mjs +0 -119
  164. package/fesm2022/caribeng-ui-caribe-pictogram.mjs +0 -52
  165. package/fesm2022/caribeng-ui-caribe-pop-over.mjs +0 -104
  166. package/fesm2022/caribeng-ui-caribe-preloader.mjs +0 -57
  167. package/fesm2022/caribeng-ui-caribe-progress-bar.mjs +0 -84
  168. package/fesm2022/caribeng-ui-caribe-stroke.mjs +0 -69
  169. package/fesm2022/caribeng-ui-caribe-switch.mjs +0 -182
  170. package/types/caribeng-ui-caribe-calendar.d.ts +0 -27
  171. package/types/caribeng-ui-caribe-circle-loading.d.ts +0 -28
  172. package/types/caribeng-ui-caribe-data-table.d.ts +0 -26
  173. package/types/caribeng-ui-caribe-file-select.d.ts +0 -31
  174. package/types/caribeng-ui-caribe-inline-alert.d.ts +0 -28
  175. package/types/caribeng-ui-caribe-input-date.d.ts +0 -40
  176. package/types/caribeng-ui-caribe-input-select.d.ts +0 -47
  177. package/types/caribeng-ui-caribe-loader.d.ts +0 -22
  178. package/types/caribeng-ui-caribe-modal.d.ts +0 -21
  179. package/types/caribeng-ui-caribe-pictogram.d.ts +0 -15
  180. package/types/caribeng-ui-caribe-pop-over.d.ts +0 -17
  181. package/types/caribeng-ui-caribe-preloader.d.ts +0 -24
  182. package/types/caribeng-ui-caribe-progress-bar.d.ts +0 -28
  183. package/types/caribeng-ui-caribe-stroke.d.ts +0 -17
  184. package/types/caribeng-ui-caribe-switch.d.ts +0 -55
  185. /package/fesm2022/{caribeng-ui-caribe-illustration.mjs → caribeng-ui-illustration.mjs} +0 -0
  186. /package/fesm2022/{caribeng-ui-caribe-logo.mjs → caribeng-ui-logo.mjs} +0 -0
@@ -0,0 +1,488 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, input, model, output, signal, computed, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { injectCbTranslation, cbFormat, injectCbAmount, CB_CONFIG } from '@caribeng/ui';
4
+ import { CbButton } from '@caribeng/ui/button';
5
+ import { CbIcon } from '@caribeng/ui/icon';
6
+ import { currencySymbol, cbDownloadXlsx } from '@caribeng/ui/xlsx';
7
+
8
+ /** Tramos habituales: por vencer, 1–30, 31–60, 61–90 y más de 90 días. */
9
+ function cbDefaultBuckets(labels) {
10
+ const range = (a, b) => labels.range.replace('{0}', String(a)).replace('{1}', String(b));
11
+ return [
12
+ { key: 'current', label: labels.current, from: null, to: 0 },
13
+ { key: 'd30', label: range(1, 30), from: 1, to: 30 },
14
+ { key: 'd60', label: range(31, 60), from: 31, to: 60 },
15
+ { key: 'd90', label: range(61, 90), from: 61, to: 90 },
16
+ { key: 'over', label: labels.over.replace('{0}', '90'), from: 91, to: null },
17
+ ];
18
+ }
19
+ function toDay(value) {
20
+ if (value instanceof Date)
21
+ return new Date(value.getFullYear(), value.getMonth(), value.getDate());
22
+ const [y, m, d] = value.slice(0, 10).split('-').map(Number);
23
+ return new Date(y, m - 1, d);
24
+ }
25
+ /** Días vencidos a la fecha de corte (negativo si aún no vence). */
26
+ function daysOverdue(due, asOf) {
27
+ return Math.round((toDay(asOf).getTime() - toDay(due).getTime()) / 86400000);
28
+ }
29
+ /** Tramo al que pertenece un número de días vencidos. */
30
+ function bucketOf(days, buckets) {
31
+ return buckets.find((b) => (b.from === null || days >= b.from) && (b.to === null || b.to === undefined || days <= b.to));
32
+ }
33
+ const round = (n) => Math.round(n * 100) / 100;
34
+ /** Agrupa los documentos por tercero y reparte sus saldos en los tramos. */
35
+ function ageDocuments(documents, buckets, asOf) {
36
+ const byParty = new Map();
37
+ for (const doc of documents) {
38
+ if (!byParty.has(doc.party))
39
+ byParty.set(doc.party, []);
40
+ byParty.get(doc.party).push(doc);
41
+ }
42
+ return [...byParty].map(([party, docs]) => {
43
+ const sums = Object.fromEntries(buckets.map((b) => [b.key, 0]));
44
+ let maxDays = -Infinity;
45
+ for (const doc of docs) {
46
+ const days = daysOverdue(doc.due, asOf);
47
+ maxDays = Math.max(maxDays, days);
48
+ const bucket = bucketOf(days, buckets);
49
+ if (bucket)
50
+ sums[bucket.key] = round(sums[bucket.key] + doc.balance);
51
+ }
52
+ return { party, documents: docs, buckets: sums, total: round(docs.reduce((t, d) => t + d.balance, 0)), maxDays };
53
+ });
54
+ }
55
+ /** Totales por tramo y general. */
56
+ function agingTotals(rows, buckets) {
57
+ const out = Object.fromEntries(buckets.map((b) => [b.key, 0]));
58
+ for (const row of rows)
59
+ for (const b of buckets)
60
+ out[b.key] = round(out[b.key] + (row.buckets[b.key] ?? 0));
61
+ return { buckets: out, total: round(rows.reduce((t, r) => t + r.total, 0)) };
62
+ }
63
+
64
+ /** Colores de los tramos, del más reciente al más vencido. */
65
+ const TONES = ['var(--cb-color-success-500, #10b981)', 'var(--cb-color-warning-400, #fbbf24)', 'var(--cb-color-warning-600, #f97316)', 'var(--cb-color-danger-500, #ef4444)', 'var(--cb-color-danger-700, #b91c1c)'];
66
+ let nextUid = 0;
67
+ /**
68
+ * Antigüedad de cartera de CaribeNG: cuentas por cobrar o por pagar repartidas
69
+ * en tramos de vencimiento (por vencer, 1–30, 31–60, 61–90, más de 90 días).
70
+ *
71
+ * - Barra de composición por tramo con importes y porcentajes.
72
+ * - Una fila por tercero, que se abre para ver sus documentos con los días vencidos.
73
+ * - Ordenar por tercero, por total, por antigüedad o por cualquier tramo; totales y Excel.
74
+ *
75
+ * @example
76
+ * ```html
77
+ * <cb-aging title="Cartera por edades" [documents]="facturas" [asOf]="corte" />
78
+ * ```
79
+ */
80
+ class CbAging {
81
+ constructor() {
82
+ this.t = injectCbTranslation();
83
+ this.fmt = cbFormat;
84
+ this.amount = injectCbAmount();
85
+ this.config = inject(CB_CONFIG);
86
+ this.uid = `cb-aging-${++nextUid}`;
87
+ this.title = input('', /* @ts-ignore */
88
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
89
+ /** Documentos pendientes (tercero, número, fecha, vencimiento y saldo). */
90
+ this.documents = input([], /* @ts-ignore */
91
+ ...(ngDevMode ? [{ debugName: "documents" }] : /* istanbul ignore next */ []));
92
+ /** Fecha de corte (hoy por defecto). */
93
+ this.asOf = input(new Date(), /* @ts-ignore */
94
+ ...(ngDevMode ? [{ debugName: "asOf" }] : /* istanbul ignore next */ []));
95
+ /** Tramos (por defecto: por vencer, 1–30, 31–60, 61–90 y más de 90 días). */
96
+ this.buckets = input(null, /* @ts-ignore */
97
+ ...(ngDevMode ? [{ debugName: "buckets" }] : /* istanbul ignore next */ []));
98
+ /** Encabezado de la columna de terceros («Cliente», «Proveedor»…). */
99
+ this.partyHeader = input('', /* @ts-ignore */
100
+ ...(ngDevMode ? [{ debugName: "partyHeader" }] : /* istanbul ignore next */ []));
101
+ /** Orden de las filas (bidireccional): `party`, `total`, `maxDays` o la clave de un tramo. */
102
+ this.sort = model({ key: 'total', order: -1 }, /* @ts-ignore */
103
+ ...(ngDevMode ? [{ debugName: "sort" }] : /* istanbul ignore next */ []));
104
+ /** Los números de documento son botones (`documentOpen`). */
105
+ this.documentLinks = input(false, /* @ts-ignore */
106
+ ...(ngDevMode ? [{ debugName: "documentLinks" }] : /* istanbul ignore next */ []));
107
+ this.currency = input(undefined, /* @ts-ignore */
108
+ ...(ngDevMode ? [{ debugName: "currency" }] : /* istanbul ignore next */ []));
109
+ this.documentOpen = output();
110
+ this.open = signal(new Set(), /* @ts-ignore */
111
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
112
+ this.bucketList = computed(() => {
113
+ const t = this.t();
114
+ return this.buckets() ?? cbDefaultBuckets({ current: t.agingCurrent, range: t.agingRange, over: t.agingOver });
115
+ }, /* @ts-ignore */
116
+ ...(ngDevMode ? [{ debugName: "bucketList" }] : /* istanbul ignore next */ []));
117
+ this.rows = computed(() => {
118
+ const rows = ageDocuments(this.documents(), this.bucketList(), this.asOf());
119
+ const { key, order } = this.sort();
120
+ const value = (r) => (key === 'party' ? r.party : key === 'total' ? r.total : key === 'maxDays' ? r.maxDays : (r.buckets[key] ?? 0));
121
+ return rows.sort((a, b) => {
122
+ const va = value(a);
123
+ const vb = value(b);
124
+ return (typeof va === 'string' ? va.localeCompare(String(vb), this.t().intlLocale) : va - vb) * order;
125
+ });
126
+ }, /* @ts-ignore */
127
+ ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
128
+ this.totals = computed(() => agingTotals(this.rows(), this.bucketList()), /* @ts-ignore */
129
+ ...(ngDevMode ? [{ debugName: "totals" }] : /* istanbul ignore next */ []));
130
+ }
131
+ money(value) {
132
+ return this.amount(value ?? 0, this.currency() ? { currency: this.currency() } : {});
133
+ }
134
+ share(key) {
135
+ const total = this.totals().total;
136
+ return total ? Math.max(0, (this.totals().buckets[key] ?? 0) / total) : 0;
137
+ }
138
+ pct(share) {
139
+ return new Intl.NumberFormat(this.t().intlLocale, { style: 'percent', maximumFractionDigits: 1 }).format(share);
140
+ }
141
+ tone(i) {
142
+ return TONES[Math.min(i, TONES.length - 1)];
143
+ }
144
+ days(doc) {
145
+ return daysOverdue(doc.due, this.asOf());
146
+ }
147
+ sortedDocs(row) {
148
+ return [...row.documents].sort((a, b) => toDay(a.due).getTime() - toDay(b.due).getTime());
149
+ }
150
+ shortDate(value) {
151
+ return new Intl.DateTimeFormat(this.t().intlLocale, { day: '2-digit', month: 'short', year: 'numeric' }).format(toDay(value)).replace(/\./g, '');
152
+ }
153
+ longDate(value) {
154
+ return new Intl.DateTimeFormat(this.t().intlLocale, { dateStyle: 'long' }).format(value);
155
+ }
156
+ isOpen(party) {
157
+ return this.open().has(party);
158
+ }
159
+ toggle(party) {
160
+ const next = new Set(this.open());
161
+ if (next.has(party))
162
+ next.delete(party);
163
+ else
164
+ next.add(party);
165
+ this.open.set(next);
166
+ }
167
+ sortBy(key) {
168
+ const s = this.sort();
169
+ this.sort.set(s.key === key ? { key, order: s.order === 1 ? -1 : 1 } : { key, order: key === 'party' ? 1 : -1 });
170
+ }
171
+ ariaSort(key) {
172
+ const s = this.sort();
173
+ return s.key !== key ? 'none' : s.order === 1 ? 'ascending' : 'descending';
174
+ }
175
+ /** Libro de Excel con el resumen por tercero y el detalle de documentos. */
176
+ toExcelSheets() {
177
+ const t = this.t();
178
+ const currency = this.currency() ?? this.config.currency;
179
+ const symbol = currency ? currencySymbol(currency, this.config.numberLocale ?? t.intlLocale) : undefined;
180
+ const money = { type: currency ? 'currency' : 'number', symbol };
181
+ const buckets = this.bucketList();
182
+ const title = [this.title() || t.agingTitle, cbFormat(t.agingAsOf, this.longDate(this.asOf()))];
183
+ return [
184
+ {
185
+ name: t.agingSummary,
186
+ title,
187
+ columns: [{ header: this.partyHeader() || t.agingParty }, ...buckets.map((b) => ({ header: b.label, ...money })), { header: t.tableTotal, ...money }, { header: t.agingMaxDays, type: 'integer' }],
188
+ rows: this.rows().map((r) => [r.party, ...buckets.map((b) => r.buckets[b.key] || null), r.total, Math.max(0, r.maxDays)]),
189
+ totals: [t.tableTotal, ...buckets.map((b) => this.totals().buckets[b.key]), this.totals().total, null],
190
+ },
191
+ {
192
+ name: t.agingDetail,
193
+ title,
194
+ columns: [{ header: this.partyHeader() || t.agingParty }, { header: t.agingNumber }, { header: t.agingDate, type: 'date' }, { header: t.agingDue, type: 'date' }, { header: t.agingOverdue, type: 'integer' }, { header: t.agingBalance, ...money }],
195
+ rows: this.rows().flatMap((r) => this.sortedDocs(r).map((d) => [r.party, d.number ?? String(d.id), d.date ?? null, d.due, this.days(d), d.balance])),
196
+ totals: [t.tableTotal, null, null, null, null, this.totals().total],
197
+ },
198
+ ];
199
+ }
200
+ /** Descarga la cartera en Excel (resumen y detalle). */
201
+ exportExcel(filename) {
202
+ cbDownloadXlsx(filename ?? (this.title() || this.t().agingTitle), this.toExcelSheets());
203
+ }
204
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbAging, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
205
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbAging, isStandalone: true, selector: "cb-aging", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, documents: { classPropertyName: "documents", publicName: "documents", isSignal: true, isRequired: false, transformFunction: null }, asOf: { classPropertyName: "asOf", publicName: "asOf", isSignal: true, isRequired: false, transformFunction: null }, buckets: { classPropertyName: "buckets", publicName: "buckets", isSignal: true, isRequired: false, transformFunction: null }, partyHeader: { classPropertyName: "partyHeader", publicName: "partyHeader", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, documentLinks: { classPropertyName: "documentLinks", publicName: "documentLinks", isSignal: true, isRequired: false, transformFunction: null }, currency: { classPropertyName: "currency", publicName: "currency", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sort: "sortChange", documentOpen: "documentOpen" }, host: { classAttribute: "cb-aging" }, ngImport: i0, template: `
206
+ <div class="cb-ag__head">
207
+ <div>
208
+ @if (title()) {
209
+ <h2 class="cb-ag__title">{{ title() }}</h2>
210
+ }
211
+ <p class="cb-ag__asof">{{ fmt(t().agingAsOf, longDate(asOf())) }}</p>
212
+ </div>
213
+ <cb-button size="sm" variant="outlined" (click)="exportExcel()">
214
+ <cb-icon name="download" [size]="15" aria-hidden="true" />
215
+ {{ t().reportExcel }}
216
+ </cb-button>
217
+ </div>
218
+
219
+ <div class="cb-ag__summary">
220
+ <div class="cb-ag__bar" aria-hidden="true">
221
+ @for (b of bucketList(); track b.key; let i = $index) {
222
+ @if (share(b.key) > 0) {
223
+ <span class="cb-ag__seg" [style.flex-grow]="share(b.key)" [style.background]="tone(i)" [title]="b.label + ': ' + money(totals().buckets[b.key])"></span>
224
+ }
225
+ }
226
+ </div>
227
+ <ul class="cb-ag__legend">
228
+ @for (b of bucketList(); track b.key; let i = $index) {
229
+ <li>
230
+ <span class="cb-ag__dot" [style.background]="tone(i)" aria-hidden="true"></span>
231
+ <span class="cb-ag__legend-label">{{ b.label }}</span>
232
+ <span class="cb-ag__legend-value">{{ money(totals().buckets[b.key]) }}</span>
233
+ <span class="cb-ag__legend-pct">{{ pct(share(b.key)) }}</span>
234
+ </li>
235
+ }
236
+ <li class="cb-ag__legend-total">
237
+ <span class="cb-ag__legend-label">{{ t().tableTotal }}</span>
238
+ <span class="cb-ag__legend-value">{{ money(totals().total) }}</span>
239
+ </li>
240
+ </ul>
241
+ </div>
242
+
243
+ <div class="cb-ag__wrap">
244
+ <table class="cb-ag__table" [attr.aria-label]="title() || t().agingTitle">
245
+ <thead>
246
+ <tr>
247
+ <th scope="col" [attr.aria-sort]="ariaSort('party')">
248
+ <button type="button" class="cb-ag__sort" (click)="sortBy('party')">{{ partyHeader() || t().agingParty }}</button>
249
+ </th>
250
+ @for (b of bucketList(); track b.key) {
251
+ <th scope="col" class="cb-ag__num" [attr.aria-sort]="ariaSort(b.key)">
252
+ <button type="button" class="cb-ag__sort" (click)="sortBy(b.key)">{{ b.label }}</button>
253
+ </th>
254
+ }
255
+ <th scope="col" class="cb-ag__num" [attr.aria-sort]="ariaSort('total')">
256
+ <button type="button" class="cb-ag__sort" (click)="sortBy('total')">{{ t().tableTotal }}</button>
257
+ </th>
258
+ <th scope="col" class="cb-ag__num" [attr.aria-sort]="ariaSort('maxDays')">
259
+ <button type="button" class="cb-ag__sort" (click)="sortBy('maxDays')">{{ t().agingMaxDays }}</button>
260
+ </th>
261
+ </tr>
262
+ </thead>
263
+ <tbody>
264
+ @for (row of rows(); track row.party; let ri = $index) {
265
+ <tr class="cb-ag__row">
266
+ <th scope="row">
267
+ <button
268
+ type="button"
269
+ class="cb-ag__toggle"
270
+ [attr.aria-expanded]="isOpen(row.party)"
271
+ [attr.aria-controls]="uid + '-docs-' + ri"
272
+ (click)="toggle(row.party)"
273
+ >
274
+ <cb-icon name="chevron-right" [size]="14" [class.cb-is-open]="isOpen(row.party)" aria-hidden="true" />
275
+ <span class="cb-ag__party">{{ row.party }} </span>
276
+ <span class="cb-ag__count">{{ fmt(t().agingDocs, row.documents.length) }}</span>
277
+ </button>
278
+ </th>
279
+ @for (b of bucketList(); track b.key; let i = $index) {
280
+ <td class="cb-ag__num" [class.cb-ag__late]="i > 0 && row.buckets[b.key] > 0">{{ row.buckets[b.key] ? money(row.buckets[b.key]) : '' }}</td>
281
+ }
282
+ <td class="cb-ag__num cb-ag__strong">{{ money(row.total) }}</td>
283
+ <td class="cb-ag__num" [class.cb-ag__late]="row.maxDays > 0">{{ row.maxDays > 0 ? fmt(t().agingDays, row.maxDays) : t().agingCurrent }}</td>
284
+ </tr>
285
+ @if (isOpen(row.party)) {
286
+ <tr class="cb-ag__docs" [id]="uid + '-docs-' + ri">
287
+ <td [attr.colspan]="bucketList().length + 3">
288
+ <table class="cb-ag__detail" [attr.aria-label]="fmt(t().agingDocsOf, row.party)">
289
+ <thead>
290
+ <tr>
291
+ <th scope="col">{{ t().agingNumber }}</th>
292
+ <th scope="col">{{ t().agingDate }}</th>
293
+ <th scope="col">{{ t().agingDue }}</th>
294
+ <th scope="col" class="cb-ag__num">{{ t().agingOverdue }}</th>
295
+ <th scope="col" class="cb-ag__num">{{ t().agingBalance }}</th>
296
+ </tr>
297
+ </thead>
298
+ <tbody>
299
+ @for (doc of sortedDocs(row); track doc.id) {
300
+ <tr>
301
+ <td>
302
+ @if (documentLinks()) {
303
+ <button type="button" class="cb-ag__doc" (click)="documentOpen.emit(doc)">{{ doc.number || doc.id }}</button>
304
+ } @else {
305
+ {{ doc.number || doc.id }}
306
+ }
307
+ </td>
308
+ <td>{{ doc.date ? shortDate(doc.date) : '' }}</td>
309
+ <td>{{ shortDate(doc.due) }}</td>
310
+ <td class="cb-ag__num" [class.cb-ag__late]="days(doc) > 0">{{ days(doc) > 0 ? fmt(t().agingDays, days(doc)) : fmt(t().agingDueIn, -days(doc)) }}</td>
311
+ <td class="cb-ag__num">{{ money(doc.balance) }}</td>
312
+ </tr>
313
+ }
314
+ </tbody>
315
+ </table>
316
+ </td>
317
+ </tr>
318
+ }
319
+ } @empty {
320
+ <tr>
321
+ <td class="cb-ag__empty" [attr.colspan]="bucketList().length + 3">{{ t().noData }}</td>
322
+ </tr>
323
+ }
324
+ </tbody>
325
+ @if (rows().length) {
326
+ <tfoot>
327
+ <tr>
328
+ <th scope="row">{{ t().tableTotal }}</th>
329
+ @for (b of bucketList(); track b.key) {
330
+ <td class="cb-ag__num">{{ money(totals().buckets[b.key]) }}</td>
331
+ }
332
+ <td class="cb-ag__num">{{ money(totals().total) }}</td>
333
+ <td></td>
334
+ </tr>
335
+ </tfoot>
336
+ }
337
+ </table>
338
+ </div>
339
+ `, isInline: true, styles: [":host{display:grid;gap:var(--cb-space-4, 1rem);width:100%;min-width:0;container-type:inline-size;color:var(--cb-color-text);font-family:var(--cb-font-sans);font-size:var(--cb-text-sm, .875rem)}.cb-ag__head{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:.75rem}.cb-ag__title{margin:0;font-size:var(--cb-text-lg, 1.125rem);font-weight:var(--cb-font-weight-semibold, 600)}.cb-ag__asof{margin:.15rem 0 0;color:var(--cb-color-text-muted)}.cb-ag__summary{display:grid;gap:.6rem}.cb-ag__bar{display:flex;gap:2px;height:.85rem;overflow:hidden;border-radius:999px;background:var(--cb-color-surface-subtle)}.cb-ag__seg{flex-basis:0;min-width:4px}.cb-ag__legend{display:flex;flex-wrap:wrap;gap:.4rem 1.25rem;margin:0;padding:0;list-style:none}.cb-ag__legend li{display:inline-flex;align-items:baseline;gap:.35rem}.cb-ag__dot{display:inline-block;width:.6rem;height:.6rem;border-radius:999px;transform:translateY(1px)}.cb-ag__legend-label{color:var(--cb-color-text-muted)}.cb-ag__legend-value{font-variant-numeric:tabular-nums;font-weight:var(--cb-font-weight-semibold, 600)}.cb-ag__legend-pct{color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem)}.cb-ag__legend-total{padding-left:1rem;border-left:1px solid var(--cb-color-border)}.cb-ag__wrap{overflow-x:auto;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-md)}.cb-ag__table{width:100%;border-collapse:collapse}.cb-ag__table th,.cb-ag__table td{padding:.55rem .75rem;border-bottom:1px solid var(--cb-color-border);text-align:start;white-space:nowrap}.cb-ag__table>thead th{background:var(--cb-color-surface-subtle);color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-semibold, 600);text-transform:uppercase;letter-spacing:.03em}.cb-ag__table .cb-ag__num{text-align:end;font-variant-numeric:tabular-nums}.cb-ag__sort{padding:0;border:0;background:none;color:inherit;font:inherit;letter-spacing:inherit;text-transform:inherit;cursor:pointer}th[aria-sort=ascending] .cb-ag__sort:after{content:\" \\2191\"}th[aria-sort=descending] .cb-ag__sort:after{content:\" \\2193\"}.cb-ag__row>th{font-weight:var(--cb-font-weight-medium, 500)}.cb-ag__toggle{display:inline-flex;align-items:center;gap:.35rem;padding:0;border:0;background:none;color:inherit;font:inherit;text-align:start;cursor:pointer}.cb-ag__toggle cb-icon{transition:transform var(--cb-duration-fast, .12s) var(--cb-ease, ease)}.cb-ag__toggle cb-icon.cb-is-open{transform:rotate(90deg)}.cb-ag__count{color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);font-weight:400}.cb-ag__late{color:var(--cb-color-danger-text, #dc2626)}.cb-ag__strong{font-weight:var(--cb-font-weight-semibold, 600)}.cb-ag__docs>td{padding:.25rem .75rem .75rem 2rem;background:var(--cb-color-surface-subtle)}.cb-ag__detail{width:100%;border-collapse:collapse}.cb-ag__detail th{color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-medium, 500)}.cb-ag__detail th,.cb-ag__detail td{padding:.35rem .5rem;border-bottom:1px solid var(--cb-color-border)}.cb-ag__doc{padding:0;border:0;background:none;color:var(--cb-color-primary-text, var(--cb-color-primary-700));font:inherit;text-decoration:underline;text-underline-offset:2px;cursor:pointer}.cb-ag__table tfoot th,.cb-ag__table tfoot td{border-top:2px solid var(--cb-color-border-strong);border-bottom:0;background:var(--cb-color-surface-subtle);font-weight:var(--cb-font-weight-semibold, 600)}.cb-ag__empty{height:5rem;color:var(--cb-color-text-muted);text-align:center!important}:is(.cb-ag__sort,.cb-ag__toggle,.cb-ag__doc):focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px;border-radius:2px}@media(forced-colors:active){.cb-ag__seg{forced-color-adjust:none}}@container (max-width: 40rem){.cb-ag__table>thead>tr>th:first-child,.cb-ag__table>tbody>tr>th[scope=row],.cb-ag__table>tfoot>tr>:first-child{position:sticky;left:0;z-index:1;min-width:8rem;max-width:10rem;white-space:normal;box-shadow:inset -1px 0 0 var(--cb-color-border)}.cb-ag__table>tbody>tr>th[scope=row]{background:var(--cb-color-bg)}.cb-ag__toggle{flex-wrap:wrap;min-height:2rem}.cb-ag__party{flex:1 1 0;min-width:0}.cb-ag__count{flex-basis:100%;padding-inline-start:calc(14px + .35rem)}}\n"], dependencies: [{ kind: "component", type: CbButton, selector: "cb-button", inputs: ["severity", "variant", "size", "disabled", "loading", "fullWidth", "type", "ariaLabel", "ariaHaspopup", "ariaExpanded", "ariaControls"] }, { kind: "component", type: CbIcon, selector: "cb-icon", inputs: ["name", "size", "spin", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
340
+ }
341
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbAging, decorators: [{
342
+ type: Component,
343
+ args: [{ selector: 'cb-aging', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [CbButton, CbIcon], host: { class: 'cb-aging' }, template: `
344
+ <div class="cb-ag__head">
345
+ <div>
346
+ @if (title()) {
347
+ <h2 class="cb-ag__title">{{ title() }}</h2>
348
+ }
349
+ <p class="cb-ag__asof">{{ fmt(t().agingAsOf, longDate(asOf())) }}</p>
350
+ </div>
351
+ <cb-button size="sm" variant="outlined" (click)="exportExcel()">
352
+ <cb-icon name="download" [size]="15" aria-hidden="true" />
353
+ {{ t().reportExcel }}
354
+ </cb-button>
355
+ </div>
356
+
357
+ <div class="cb-ag__summary">
358
+ <div class="cb-ag__bar" aria-hidden="true">
359
+ @for (b of bucketList(); track b.key; let i = $index) {
360
+ @if (share(b.key) > 0) {
361
+ <span class="cb-ag__seg" [style.flex-grow]="share(b.key)" [style.background]="tone(i)" [title]="b.label + ': ' + money(totals().buckets[b.key])"></span>
362
+ }
363
+ }
364
+ </div>
365
+ <ul class="cb-ag__legend">
366
+ @for (b of bucketList(); track b.key; let i = $index) {
367
+ <li>
368
+ <span class="cb-ag__dot" [style.background]="tone(i)" aria-hidden="true"></span>
369
+ <span class="cb-ag__legend-label">{{ b.label }}</span>
370
+ <span class="cb-ag__legend-value">{{ money(totals().buckets[b.key]) }}</span>
371
+ <span class="cb-ag__legend-pct">{{ pct(share(b.key)) }}</span>
372
+ </li>
373
+ }
374
+ <li class="cb-ag__legend-total">
375
+ <span class="cb-ag__legend-label">{{ t().tableTotal }}</span>
376
+ <span class="cb-ag__legend-value">{{ money(totals().total) }}</span>
377
+ </li>
378
+ </ul>
379
+ </div>
380
+
381
+ <div class="cb-ag__wrap">
382
+ <table class="cb-ag__table" [attr.aria-label]="title() || t().agingTitle">
383
+ <thead>
384
+ <tr>
385
+ <th scope="col" [attr.aria-sort]="ariaSort('party')">
386
+ <button type="button" class="cb-ag__sort" (click)="sortBy('party')">{{ partyHeader() || t().agingParty }}</button>
387
+ </th>
388
+ @for (b of bucketList(); track b.key) {
389
+ <th scope="col" class="cb-ag__num" [attr.aria-sort]="ariaSort(b.key)">
390
+ <button type="button" class="cb-ag__sort" (click)="sortBy(b.key)">{{ b.label }}</button>
391
+ </th>
392
+ }
393
+ <th scope="col" class="cb-ag__num" [attr.aria-sort]="ariaSort('total')">
394
+ <button type="button" class="cb-ag__sort" (click)="sortBy('total')">{{ t().tableTotal }}</button>
395
+ </th>
396
+ <th scope="col" class="cb-ag__num" [attr.aria-sort]="ariaSort('maxDays')">
397
+ <button type="button" class="cb-ag__sort" (click)="sortBy('maxDays')">{{ t().agingMaxDays }}</button>
398
+ </th>
399
+ </tr>
400
+ </thead>
401
+ <tbody>
402
+ @for (row of rows(); track row.party; let ri = $index) {
403
+ <tr class="cb-ag__row">
404
+ <th scope="row">
405
+ <button
406
+ type="button"
407
+ class="cb-ag__toggle"
408
+ [attr.aria-expanded]="isOpen(row.party)"
409
+ [attr.aria-controls]="uid + '-docs-' + ri"
410
+ (click)="toggle(row.party)"
411
+ >
412
+ <cb-icon name="chevron-right" [size]="14" [class.cb-is-open]="isOpen(row.party)" aria-hidden="true" />
413
+ <span class="cb-ag__party">{{ row.party }} </span>
414
+ <span class="cb-ag__count">{{ fmt(t().agingDocs, row.documents.length) }}</span>
415
+ </button>
416
+ </th>
417
+ @for (b of bucketList(); track b.key; let i = $index) {
418
+ <td class="cb-ag__num" [class.cb-ag__late]="i > 0 && row.buckets[b.key] > 0">{{ row.buckets[b.key] ? money(row.buckets[b.key]) : '' }}</td>
419
+ }
420
+ <td class="cb-ag__num cb-ag__strong">{{ money(row.total) }}</td>
421
+ <td class="cb-ag__num" [class.cb-ag__late]="row.maxDays > 0">{{ row.maxDays > 0 ? fmt(t().agingDays, row.maxDays) : t().agingCurrent }}</td>
422
+ </tr>
423
+ @if (isOpen(row.party)) {
424
+ <tr class="cb-ag__docs" [id]="uid + '-docs-' + ri">
425
+ <td [attr.colspan]="bucketList().length + 3">
426
+ <table class="cb-ag__detail" [attr.aria-label]="fmt(t().agingDocsOf, row.party)">
427
+ <thead>
428
+ <tr>
429
+ <th scope="col">{{ t().agingNumber }}</th>
430
+ <th scope="col">{{ t().agingDate }}</th>
431
+ <th scope="col">{{ t().agingDue }}</th>
432
+ <th scope="col" class="cb-ag__num">{{ t().agingOverdue }}</th>
433
+ <th scope="col" class="cb-ag__num">{{ t().agingBalance }}</th>
434
+ </tr>
435
+ </thead>
436
+ <tbody>
437
+ @for (doc of sortedDocs(row); track doc.id) {
438
+ <tr>
439
+ <td>
440
+ @if (documentLinks()) {
441
+ <button type="button" class="cb-ag__doc" (click)="documentOpen.emit(doc)">{{ doc.number || doc.id }}</button>
442
+ } @else {
443
+ {{ doc.number || doc.id }}
444
+ }
445
+ </td>
446
+ <td>{{ doc.date ? shortDate(doc.date) : '' }}</td>
447
+ <td>{{ shortDate(doc.due) }}</td>
448
+ <td class="cb-ag__num" [class.cb-ag__late]="days(doc) > 0">{{ days(doc) > 0 ? fmt(t().agingDays, days(doc)) : fmt(t().agingDueIn, -days(doc)) }}</td>
449
+ <td class="cb-ag__num">{{ money(doc.balance) }}</td>
450
+ </tr>
451
+ }
452
+ </tbody>
453
+ </table>
454
+ </td>
455
+ </tr>
456
+ }
457
+ } @empty {
458
+ <tr>
459
+ <td class="cb-ag__empty" [attr.colspan]="bucketList().length + 3">{{ t().noData }}</td>
460
+ </tr>
461
+ }
462
+ </tbody>
463
+ @if (rows().length) {
464
+ <tfoot>
465
+ <tr>
466
+ <th scope="row">{{ t().tableTotal }}</th>
467
+ @for (b of bucketList(); track b.key) {
468
+ <td class="cb-ag__num">{{ money(totals().buckets[b.key]) }}</td>
469
+ }
470
+ <td class="cb-ag__num">{{ money(totals().total) }}</td>
471
+ <td></td>
472
+ </tr>
473
+ </tfoot>
474
+ }
475
+ </table>
476
+ </div>
477
+ `, styles: [":host{display:grid;gap:var(--cb-space-4, 1rem);width:100%;min-width:0;container-type:inline-size;color:var(--cb-color-text);font-family:var(--cb-font-sans);font-size:var(--cb-text-sm, .875rem)}.cb-ag__head{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:.75rem}.cb-ag__title{margin:0;font-size:var(--cb-text-lg, 1.125rem);font-weight:var(--cb-font-weight-semibold, 600)}.cb-ag__asof{margin:.15rem 0 0;color:var(--cb-color-text-muted)}.cb-ag__summary{display:grid;gap:.6rem}.cb-ag__bar{display:flex;gap:2px;height:.85rem;overflow:hidden;border-radius:999px;background:var(--cb-color-surface-subtle)}.cb-ag__seg{flex-basis:0;min-width:4px}.cb-ag__legend{display:flex;flex-wrap:wrap;gap:.4rem 1.25rem;margin:0;padding:0;list-style:none}.cb-ag__legend li{display:inline-flex;align-items:baseline;gap:.35rem}.cb-ag__dot{display:inline-block;width:.6rem;height:.6rem;border-radius:999px;transform:translateY(1px)}.cb-ag__legend-label{color:var(--cb-color-text-muted)}.cb-ag__legend-value{font-variant-numeric:tabular-nums;font-weight:var(--cb-font-weight-semibold, 600)}.cb-ag__legend-pct{color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem)}.cb-ag__legend-total{padding-left:1rem;border-left:1px solid var(--cb-color-border)}.cb-ag__wrap{overflow-x:auto;border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-md)}.cb-ag__table{width:100%;border-collapse:collapse}.cb-ag__table th,.cb-ag__table td{padding:.55rem .75rem;border-bottom:1px solid var(--cb-color-border);text-align:start;white-space:nowrap}.cb-ag__table>thead th{background:var(--cb-color-surface-subtle);color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-semibold, 600);text-transform:uppercase;letter-spacing:.03em}.cb-ag__table .cb-ag__num{text-align:end;font-variant-numeric:tabular-nums}.cb-ag__sort{padding:0;border:0;background:none;color:inherit;font:inherit;letter-spacing:inherit;text-transform:inherit;cursor:pointer}th[aria-sort=ascending] .cb-ag__sort:after{content:\" \\2191\"}th[aria-sort=descending] .cb-ag__sort:after{content:\" \\2193\"}.cb-ag__row>th{font-weight:var(--cb-font-weight-medium, 500)}.cb-ag__toggle{display:inline-flex;align-items:center;gap:.35rem;padding:0;border:0;background:none;color:inherit;font:inherit;text-align:start;cursor:pointer}.cb-ag__toggle cb-icon{transition:transform var(--cb-duration-fast, .12s) var(--cb-ease, ease)}.cb-ag__toggle cb-icon.cb-is-open{transform:rotate(90deg)}.cb-ag__count{color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);font-weight:400}.cb-ag__late{color:var(--cb-color-danger-text, #dc2626)}.cb-ag__strong{font-weight:var(--cb-font-weight-semibold, 600)}.cb-ag__docs>td{padding:.25rem .75rem .75rem 2rem;background:var(--cb-color-surface-subtle)}.cb-ag__detail{width:100%;border-collapse:collapse}.cb-ag__detail th{color:var(--cb-color-text-muted);font-size:var(--cb-text-xs, .75rem);font-weight:var(--cb-font-weight-medium, 500)}.cb-ag__detail th,.cb-ag__detail td{padding:.35rem .5rem;border-bottom:1px solid var(--cb-color-border)}.cb-ag__doc{padding:0;border:0;background:none;color:var(--cb-color-primary-text, var(--cb-color-primary-700));font:inherit;text-decoration:underline;text-underline-offset:2px;cursor:pointer}.cb-ag__table tfoot th,.cb-ag__table tfoot td{border-top:2px solid var(--cb-color-border-strong);border-bottom:0;background:var(--cb-color-surface-subtle);font-weight:var(--cb-font-weight-semibold, 600)}.cb-ag__empty{height:5rem;color:var(--cb-color-text-muted);text-align:center!important}:is(.cb-ag__sort,.cb-ag__toggle,.cb-ag__doc):focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px;border-radius:2px}@media(forced-colors:active){.cb-ag__seg{forced-color-adjust:none}}@container (max-width: 40rem){.cb-ag__table>thead>tr>th:first-child,.cb-ag__table>tbody>tr>th[scope=row],.cb-ag__table>tfoot>tr>:first-child{position:sticky;left:0;z-index:1;min-width:8rem;max-width:10rem;white-space:normal;box-shadow:inset -1px 0 0 var(--cb-color-border)}.cb-ag__table>tbody>tr>th[scope=row]{background:var(--cb-color-bg)}.cb-ag__toggle{flex-wrap:wrap;min-height:2rem}.cb-ag__party{flex:1 1 0;min-width:0}.cb-ag__count{flex-basis:100%;padding-inline-start:calc(14px + .35rem)}}\n"] }]
478
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], documents: [{ type: i0.Input, args: [{ isSignal: true, alias: "documents", required: false }] }], asOf: [{ type: i0.Input, args: [{ isSignal: true, alias: "asOf", required: false }] }], buckets: [{ type: i0.Input, args: [{ isSignal: true, alias: "buckets", required: false }] }], partyHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "partyHeader", required: false }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }, { type: i0.Output, args: ["sortChange"] }], documentLinks: [{ type: i0.Input, args: [{ isSignal: true, alias: "documentLinks", required: false }] }], currency: [{ type: i0.Input, args: [{ isSignal: true, alias: "currency", required: false }] }], documentOpen: [{ type: i0.Output, args: ["documentOpen"] }] } });
479
+
480
+ /*
481
+ * Public API Surface de @caribeng/ui/aging
482
+ */
483
+
484
+ /**
485
+ * Generated bundle index. Do not edit.
486
+ */
487
+
488
+ export { CbAging, ageDocuments, agingTotals, bucketOf, cbDefaultBuckets, daysOverdue };
@@ -3,7 +3,7 @@ import { input, model, output, ViewEncapsulation, ChangeDetectionStrategy, Compo
3
3
  import { injectCbTranslation } from '@caribeng/ui';
4
4
 
5
5
  /**
6
- * Alert de Caribe Design: banner de notificación con severidad, título,
6
+ * Alert: banner de notificación con severidad, título,
7
7
  * contenido proyectado y cierre opcional.
8
8
  *
9
9
  * @example
@@ -42,54 +42,54 @@ class CbAlert {
42
42
  this.closed.emit();
43
43
  }
44
44
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbAlert, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
45
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbAlert, isStandalone: true, selector: "cb-alert", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visible: "visibleChange", closed: "closed" }, ngImport: i0, template: `
46
- @if (visible()) {
47
- <section class="cb-alert cb-alert--{{ severity() }}" role="alert">
48
- <span class="cb-alert__icon" aria-hidden="true">{{ glyph() }}</span>
49
- <div class="cb-alert__content">
50
- @if (title()) {
51
- <strong class="cb-alert__title">{{ title() }}</strong>
52
- }
53
- <div class="cb-alert__body"><ng-content /></div>
54
- </div>
55
- @if (closable()) {
56
- <button
57
- type="button"
58
- class="cb-alert__close"
59
- [attr.aria-label]="t().close"
60
- (click)="close()"
61
- >
62
- ×
63
- </button>
64
- }
65
- </section>
66
- }
45
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbAlert, isStandalone: true, selector: "cb-alert", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visible: "visibleChange", closed: "closed" }, ngImport: i0, template: `
46
+ @if (visible()) {
47
+ <section class="cb-alert cb-alert--{{ severity() }}" role="alert">
48
+ <span class="cb-alert__icon" aria-hidden="true">{{ glyph() }}</span>
49
+ <div class="cb-alert__content">
50
+ @if (title()) {
51
+ <strong class="cb-alert__title">{{ title() }}</strong>
52
+ }
53
+ <div class="cb-alert__body"><ng-content /></div>
54
+ </div>
55
+ @if (closable()) {
56
+ <button
57
+ type="button"
58
+ class="cb-alert__close"
59
+ [attr.aria-label]="t().close"
60
+ (click)="close()"
61
+ >
62
+ ×
63
+ </button>
64
+ }
65
+ </section>
66
+ }
67
67
  `, isInline: true, styles: [":host{display:block;width:100%}.cb-alert{display:flex;align-items:flex-start;gap:var(--cb-space-3, .75rem);box-sizing:border-box;width:100%;padding:var(--cb-space-4, 1rem);font-family:var(--cb-font-sans, inherit);color:var(--cb-color-text-strong, #0f172a);background-color:var(--cb-color-surface-0, #ffffff);border:1px solid var(--cb-color-border, #e2e8f0);border-left-width:4px;border-radius:var(--cb-radius-md, .5rem)}.cb-alert__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;width:1.5rem;height:1.5rem;font-weight:var(--cb-font-weight-bold, 700);color:#fff;border-radius:var(--cb-radius-full, 999px)}.cb-alert__content{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--cb-space-1, .25rem)}.cb-alert__title{font-weight:var(--cb-font-weight-semibold, 600)}.cb-alert__body{font-size:var(--cb-text-sm, .875rem);color:var(--cb-color-text, #334155)}.cb-alert__close{flex:none;display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;font-size:1.1rem;line-height:1;color:var(--cb-color-text-muted, #64748b);background:transparent;border:none;border-radius:var(--cb-radius-sm, .25rem);cursor:pointer}.cb-alert__close:hover{background-color:var(--cb-color-surface-100, #f1f5f9)}.cb-alert--success{border-left-color:var(--cb-color-success-500, #16a34a);background-color:var(--cb-color-success-50, #f0fdf4)}.cb-alert--success .cb-alert__icon{background-color:var(--cb-color-success-500, #16a34a)}.cb-alert--warning{border-left-color:var(--cb-color-warning-500, #d97706);background-color:var(--cb-color-warning-50, #fffbeb)}.cb-alert--warning .cb-alert__icon{background-color:var(--cb-color-warning-500, #d97706)}.cb-alert--error{border-left-color:var(--cb-color-danger-500, #dc2626);background-color:var(--cb-color-danger-50, #fef2f2)}.cb-alert--error .cb-alert__icon{background-color:var(--cb-color-danger-500, #dc2626)}.cb-alert--info{border-left-color:var(--cb-color-info-500, #2563eb);background-color:var(--cb-color-info-50, #eff6ff)}.cb-alert--info .cb-alert__icon{background-color:var(--cb-color-info-500, #2563eb)}:host-context([data-theme=\"dark\"]) .cb-alert{color:var(--cb-color-surface-0, #f8fafc);background-color:var(--cb-color-surface-800, #1e293b);border-color:var(--cb-color-surface-600, #475569)}:host-context([data-theme=\"dark\"]) .cb-alert__body{color:var(--cb-color-surface-200, #e2e8f0)}:host-context([data-theme=\"dark\"]) .cb-alert--success,:host-context([data-theme=\"dark\"]) .cb-alert--warning,:host-context([data-theme=\"dark\"]) .cb-alert--error,:host-context([data-theme=\"dark\"]) .cb-alert--info{background-color:var(--cb-color-surface-800, #1e293b)}:host-context([data-theme=\"dark\"]) .cb-alert__close:hover{background-color:var(--cb-color-surface-700, #334155)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
68
68
  }
69
69
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbAlert, decorators: [{
70
70
  type: Component,
71
- args: [{ selector: 'cb-alert', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, template: `
72
- @if (visible()) {
73
- <section class="cb-alert cb-alert--{{ severity() }}" role="alert">
74
- <span class="cb-alert__icon" aria-hidden="true">{{ glyph() }}</span>
75
- <div class="cb-alert__content">
76
- @if (title()) {
77
- <strong class="cb-alert__title">{{ title() }}</strong>
78
- }
79
- <div class="cb-alert__body"><ng-content /></div>
80
- </div>
81
- @if (closable()) {
82
- <button
83
- type="button"
84
- class="cb-alert__close"
85
- [attr.aria-label]="t().close"
86
- (click)="close()"
87
- >
88
- ×
89
- </button>
90
- }
91
- </section>
92
- }
71
+ args: [{ selector: 'cb-alert', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, template: `
72
+ @if (visible()) {
73
+ <section class="cb-alert cb-alert--{{ severity() }}" role="alert">
74
+ <span class="cb-alert__icon" aria-hidden="true">{{ glyph() }}</span>
75
+ <div class="cb-alert__content">
76
+ @if (title()) {
77
+ <strong class="cb-alert__title">{{ title() }}</strong>
78
+ }
79
+ <div class="cb-alert__body"><ng-content /></div>
80
+ </div>
81
+ @if (closable()) {
82
+ <button
83
+ type="button"
84
+ class="cb-alert__close"
85
+ [attr.aria-label]="t().close"
86
+ (click)="close()"
87
+ >
88
+ ×
89
+ </button>
90
+ }
91
+ </section>
92
+ }
93
93
  `, styles: [":host{display:block;width:100%}.cb-alert{display:flex;align-items:flex-start;gap:var(--cb-space-3, .75rem);box-sizing:border-box;width:100%;padding:var(--cb-space-4, 1rem);font-family:var(--cb-font-sans, inherit);color:var(--cb-color-text-strong, #0f172a);background-color:var(--cb-color-surface-0, #ffffff);border:1px solid var(--cb-color-border, #e2e8f0);border-left-width:4px;border-radius:var(--cb-radius-md, .5rem)}.cb-alert__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;width:1.5rem;height:1.5rem;font-weight:var(--cb-font-weight-bold, 700);color:#fff;border-radius:var(--cb-radius-full, 999px)}.cb-alert__content{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--cb-space-1, .25rem)}.cb-alert__title{font-weight:var(--cb-font-weight-semibold, 600)}.cb-alert__body{font-size:var(--cb-text-sm, .875rem);color:var(--cb-color-text, #334155)}.cb-alert__close{flex:none;display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;font-size:1.1rem;line-height:1;color:var(--cb-color-text-muted, #64748b);background:transparent;border:none;border-radius:var(--cb-radius-sm, .25rem);cursor:pointer}.cb-alert__close:hover{background-color:var(--cb-color-surface-100, #f1f5f9)}.cb-alert--success{border-left-color:var(--cb-color-success-500, #16a34a);background-color:var(--cb-color-success-50, #f0fdf4)}.cb-alert--success .cb-alert__icon{background-color:var(--cb-color-success-500, #16a34a)}.cb-alert--warning{border-left-color:var(--cb-color-warning-500, #d97706);background-color:var(--cb-color-warning-50, #fffbeb)}.cb-alert--warning .cb-alert__icon{background-color:var(--cb-color-warning-500, #d97706)}.cb-alert--error{border-left-color:var(--cb-color-danger-500, #dc2626);background-color:var(--cb-color-danger-50, #fef2f2)}.cb-alert--error .cb-alert__icon{background-color:var(--cb-color-danger-500, #dc2626)}.cb-alert--info{border-left-color:var(--cb-color-info-500, #2563eb);background-color:var(--cb-color-info-50, #eff6ff)}.cb-alert--info .cb-alert__icon{background-color:var(--cb-color-info-500, #2563eb)}:host-context([data-theme=\"dark\"]) .cb-alert{color:var(--cb-color-surface-0, #f8fafc);background-color:var(--cb-color-surface-800, #1e293b);border-color:var(--cb-color-surface-600, #475569)}:host-context([data-theme=\"dark\"]) .cb-alert__body{color:var(--cb-color-surface-200, #e2e8f0)}:host-context([data-theme=\"dark\"]) .cb-alert--success,:host-context([data-theme=\"dark\"]) .cb-alert--warning,:host-context([data-theme=\"dark\"]) .cb-alert--error,:host-context([data-theme=\"dark\"]) .cb-alert--info{background-color:var(--cb-color-surface-800, #1e293b)}:host-context([data-theme=\"dark\"]) .cb-alert__close:hover{background-color:var(--cb-color-surface-700, #334155)}\n"] }]
94
94
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: false }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }, { type: i0.Output, args: ["visibleChange"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
95
95