tailjng 0.1.6 → 0.1.7

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 (176) hide show
  1. package/README.md +12 -4
  2. package/cli/execute/init-app.js +5 -2
  3. package/cli/execute/sync-app.js +14 -2
  4. package/cli/settings/colors-config-utils.js +43 -8
  5. package/cli/settings/icons-config-utils.js +62 -0
  6. package/cli/settings/path-utils.js +32 -2
  7. package/cli/settings/project-utils.js +7 -1
  8. package/cli/templates/app.generator.js +2 -2
  9. package/fesm2022/tailjng.mjs +247 -80
  10. package/fesm2022/tailjng.mjs.map +1 -1
  11. package/lib/services/static/theme.service.d.ts +39 -1
  12. package/lib/utils/theme/theme-variables.util.d.ts +31 -0
  13. package/package.json +1 -1
  14. package/public-api.d.ts +2 -1
  15. package/registry/components.json +41 -18
  16. package/src/colors.safelist.css +2 -2
  17. package/src/lib/components/.config/README.md +11 -0
  18. package/src/lib/components/.config/colors/README.md +38 -0
  19. package/src/lib/components/{colors-config → .config/colors}/colors.config.ts +5 -5
  20. package/src/lib/components/{colors-config → .config/colors}/colors.safelist.css +2 -2
  21. package/src/lib/components/.config/icons/README.md +26 -0
  22. package/src/lib/components/.config/icons/icons.lucide.ts +134 -0
  23. package/src/lib/components/.config/input/README.md +24 -0
  24. package/src/lib/components/.config/input/input.classes.ts +119 -0
  25. package/src/lib/components/alert/alert-dialog/dialog-alert.component.css +244 -2
  26. package/src/lib/components/alert/alert-dialog/dialog-alert.component.html +25 -38
  27. package/src/lib/components/alert/alert-dialog/dialog-alert.component.ts +66 -56
  28. package/src/lib/components/alert/alert-dialog/dialog-alert.types.ts +19 -0
  29. package/src/lib/components/alert/alert-toast/toast-alert.component.css +630 -12
  30. package/src/lib/components/alert/alert-toast/toast-alert.component.html +103 -102
  31. package/src/lib/components/alert/alert-toast/toast-alert.component.ts +485 -128
  32. package/src/lib/components/alert/alert-toast/toast-alert.types.ts +25 -0
  33. package/src/lib/components/badge/badge.component.html +34 -21
  34. package/src/lib/components/badge/badge.component.ts +140 -31
  35. package/src/lib/components/button/button.component.html +16 -10
  36. package/src/lib/components/button/button.component.ts +162 -22
  37. package/src/lib/components/card/card-complete/complete-card.component.html +2 -2
  38. package/src/lib/components/card/card-complete/complete-card.component.ts +26 -16
  39. package/src/lib/components/card/card-crud-complete/complete-crud-card.component.html +2 -2
  40. package/src/lib/components/card/card-crud-complete/complete-crud-card.component.ts +26 -16
  41. package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.css +97 -0
  42. package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.html +54 -46
  43. package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.ts +135 -64
  44. package/src/lib/components/checkbox/checkbox-input/input-checkbox.types.ts +3 -0
  45. package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.css +112 -0
  46. package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.html +28 -25
  47. package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.ts +67 -15
  48. package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.types.ts +1 -0
  49. package/src/lib/components/coach-mark/coach-mark.component.html +4 -22
  50. package/src/lib/components/coach-mark/coach-mark.component.scss +1 -1
  51. package/src/lib/components/coach-mark/coach-mark.component.ts +51 -18
  52. package/src/lib/components/coach-mark/coach-mark.directive.ts +133 -78
  53. package/src/lib/components/coach-mark/coach-mark.types.ts +12 -0
  54. package/src/lib/components/dialog/dialog.component.css +103 -1
  55. package/src/lib/components/dialog/dialog.component.html +46 -66
  56. package/src/lib/components/dialog/dialog.component.ts +136 -110
  57. package/src/lib/components/dialog/dialog.types.ts +19 -0
  58. package/src/lib/components/filter/filter-complete/complete-filter.component.html +16 -19
  59. package/src/lib/components/filter/filter-complete/complete-filter.component.scss +35 -0
  60. package/src/lib/components/filter/filter-complete/complete-filter.component.ts +58 -34
  61. package/src/lib/components/filter/filter-complete/complete-filter.types.ts +7 -0
  62. package/src/lib/components/filter/filter-complete/complete-filter.util.ts +16 -0
  63. package/src/lib/components/form/form-container/container-form.component.css +4 -0
  64. package/src/lib/components/form/form-container/container-form.component.html +2 -2
  65. package/src/lib/components/form/form-container/container-form.component.ts +72 -16
  66. package/src/lib/components/form/form-container/container-form.types.ts +42 -0
  67. package/src/lib/components/form/form-container/form-col-span.directive.ts +25 -0
  68. package/src/lib/components/form/form-sidebar/sidebar-form.component.css +276 -0
  69. package/src/lib/components/form/form-sidebar/sidebar-form.component.html +117 -125
  70. package/src/lib/components/form/form-sidebar/sidebar-form.component.ts +109 -34
  71. package/src/lib/components/form/form-sidebar/sidebar-form.types.ts +3 -0
  72. package/src/lib/components/{toggle-radio/toggle-radio.component.css → form/form-validation/validation-form.component.css} +0 -1
  73. package/src/lib/components/form/form-validation/validation-form.component.html +10 -6
  74. package/src/lib/components/form/form-validation/validation-form.component.ts +99 -12
  75. package/src/lib/components/form/form-validation/validation-form.types.ts +33 -0
  76. package/src/lib/components/icon/icon.component.html +8 -5
  77. package/src/lib/components/icon/icon.component.ts +111 -9
  78. package/src/lib/components/input/input/input.component.html +19 -16
  79. package/src/lib/components/input/input/input.component.ts +130 -53
  80. package/src/lib/components/input/input/input.types.ts +8 -0
  81. package/src/lib/components/input/input-file/file-input.component.html +65 -56
  82. package/src/lib/components/input/input-file/file-input.component.ts +276 -173
  83. package/src/lib/components/input/input-file/file-input.types.ts +2 -0
  84. package/src/lib/components/input/input-range/range-input.component.css +67 -0
  85. package/src/lib/components/input/input-range/range-input.component.html +50 -58
  86. package/src/lib/components/input/input-range/range-input.component.ts +148 -60
  87. package/src/lib/components/input/input-range/range-input.types.ts +7 -0
  88. package/src/lib/components/input/input-textarea/textarea-input.component.html +16 -7
  89. package/src/lib/components/input/input-textarea/textarea-input.component.ts +140 -50
  90. package/src/lib/components/input/input-textarea/textarea-input.types.ts +2 -0
  91. package/src/lib/components/label/label.component.html +17 -16
  92. package/src/lib/components/label/label.component.ts +70 -16
  93. package/src/lib/components/label/label.types.ts +2 -0
  94. package/src/lib/components/menu/menu-options-table/menu-options-defaults.ts +34 -0
  95. package/src/lib/components/menu/menu-options-table/options-table-menu.component.html +34 -20
  96. package/src/lib/components/menu/menu-options-table/options-table-menu.component.ts +211 -58
  97. package/src/lib/components/menu/menu-options-table/options-table-menu.types.ts +38 -0
  98. package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.html +49 -52
  99. package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.ts +112 -24
  100. package/src/lib/components/menu/options-coach-menu/options-coach-menu.types.ts +9 -0
  101. package/src/lib/components/mode-toggle/mode-toggle.component.html +11 -16
  102. package/src/lib/components/mode-toggle/mode-toggle.component.ts +69 -33
  103. package/src/lib/components/paginator/paginator-complete/complete-paginator.component.html +4 -4
  104. package/src/lib/components/paginator/paginator-complete/complete-paginator.component.ts +31 -7
  105. package/src/lib/components/paginator/paginator-complete/complete-paginator.types.ts +12 -0
  106. package/src/lib/components/paginator/paginator-complete/complete-paginator.util.ts +36 -0
  107. package/src/lib/components/progress-bar/progress-bar.component.css +11 -0
  108. package/src/lib/components/progress-bar/progress-bar.component.html +41 -40
  109. package/src/lib/components/progress-bar/progress-bar.component.ts +95 -11
  110. package/src/lib/components/progress-bar/progress-bar.types.ts +2 -0
  111. package/src/lib/components/select/select-dropdown/dropdown-select.component.css +6 -0
  112. package/src/lib/components/select/select-dropdown/dropdown-select.component.html +54 -44
  113. package/src/lib/components/select/select-dropdown/dropdown-select.component.ts +450 -509
  114. package/src/lib/components/select/select-dropdown/dropdown-select.types.ts +43 -0
  115. package/src/lib/components/select/select-dropdown/dropdown-select.util.ts +179 -0
  116. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.css +6 -0
  117. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.html +131 -42
  118. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.ts +491 -475
  119. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.types.ts +22 -0
  120. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.util.ts +20 -0
  121. package/src/lib/components/select/select-multi-table/multi-table-select.component.css +10 -0
  122. package/src/lib/components/select/select-multi-table/multi-table-select.component.html +76 -60
  123. package/src/lib/components/select/select-multi-table/multi-table-select.component.ts +250 -313
  124. package/src/lib/components/select/select-multi-table/multi-table-select.types.ts +10 -0
  125. package/src/lib/components/select/select-multi-table/multi-table-select.util.ts +5 -0
  126. package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.css +212 -0
  127. package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.html +62 -53
  128. package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.ts +84 -27
  129. package/src/lib/components/sidebar/sidebar-static/static-sidebar.types.ts +2 -0
  130. package/src/lib/components/table/table-complete/complete-table.component.html +15 -17
  131. package/src/lib/components/table/table-complete/complete-table.component.ts +190 -338
  132. package/src/lib/components/table/table-complete/complete-table.types.ts +28 -0
  133. package/src/lib/components/table/table-complete/complete-table.util.ts +236 -0
  134. package/src/lib/components/table/table-complete/index.ts +2 -0
  135. package/src/lib/components/table/table-crud-complete/complete-crud-table.animations.ts +34 -0
  136. package/src/lib/components/table/table-crud-complete/complete-crud-table.component.html +73 -128
  137. package/src/lib/components/table/table-crud-complete/complete-crud-table.component.ts +542 -829
  138. package/src/lib/components/table/table-crud-complete/complete-crud-table.types.ts +57 -0
  139. package/src/lib/components/table/table-crud-complete/complete-crud-table.util.ts +723 -0
  140. package/src/lib/components/table/table-crud-complete/index.ts +3 -0
  141. package/src/lib/components/theme-generator/theme-generator.component.css +21 -0
  142. package/src/lib/components/theme-generator/theme-generator.component.html +146 -116
  143. package/src/lib/components/theme-generator/theme-generator.component.ts +44 -24
  144. package/src/lib/components/toggle-radio/shared/toggle-options.types.ts +8 -0
  145. package/src/lib/components/toggle-radio/shared/toggle-options.util.ts +44 -0
  146. package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.css +135 -0
  147. package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.html +52 -0
  148. package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.ts +198 -0
  149. package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.types.ts +1 -0
  150. package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.css +108 -0
  151. package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.html +37 -0
  152. package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.ts +193 -0
  153. package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.types.ts +1 -0
  154. package/src/lib/components/tooltip/tooltip.directive.ts +12 -9
  155. package/src/lib/components/tooltip/tooltip.service.ts +331 -133
  156. package/src/lib/components/tooltip/tooltip.types.ts +9 -0
  157. package/src/lib/components/viewer/viewer-image/image-viewer.component.css +90 -4
  158. package/src/lib/components/viewer/viewer-image/image-viewer.component.html +52 -103
  159. package/src/lib/components/viewer/viewer-image/image-viewer.component.ts +182 -177
  160. package/src/lib/components/viewer/viewer-image/image-viewer.types.ts +3 -0
  161. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.css +177 -0
  162. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.html +74 -24
  163. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.ts +168 -15
  164. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.types.ts +1 -0
  165. package/src/styles.css +2 -2
  166. package/lib/services/static/icons.service.d.ts +0 -65
  167. package/src/lib/components/colors-config/README.md +0 -38
  168. package/src/lib/components/form/form-sidebar/sidebar-form.component.scss +0 -0
  169. package/src/lib/components/form/form-validation/validation-form.component.scss +0 -0
  170. package/src/lib/components/menu/menu-options-table/options-table-menu.component.scss +0 -0
  171. package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.scss +0 -12
  172. package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.scss +0 -0
  173. package/src/lib/components/toggle-radio/toggle-radio.component.html +0 -51
  174. package/src/lib/components/toggle-radio/toggle-radio.component.ts +0 -222
  175. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.scss +0 -0
  176. package/tailjng-0.1.6.tgz +0 -0
@@ -1,172 +1,209 @@
1
- // TODO: BETA - IN DEVELOPMENT
2
1
  import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
3
2
  import { CommonModule } from '@angular/common';
4
3
  import { FormsModule } from '@angular/forms';
5
- import { LucideAngularModule } from 'lucide-angular';
6
- import { JAlertDialogService, JAlertToastService, JConverterCrudService, JGenericCrudService, JIconsService, JTransformService, TableColumn } from 'tailjng';
4
+ import {
5
+ JAlertDialogService,
6
+ JAlertToastService,
7
+ JConverterCrudService,
8
+ JGenericCrudService,
9
+ JTransformService,
10
+ TableColumn} from 'tailjng';
7
11
  import { JButtonComponent } from '../../button/button.component';
8
12
  import { JInputCheckboxComponent } from '../../checkbox/checkbox-input/input-checkbox.component';
9
13
  import { JInputComponent } from '../../input/input/input.component';
10
14
  import { JDropdownSelectComponent } from '../../select/select-dropdown/dropdown-select.component';
11
-
15
+ import type {
16
+ CompleteTableEquivalenceConfigs,
17
+ CompleteTableEquivalenceMaps,
18
+ CompleteTableSortDirection} from './complete-table.types';
19
+ import {
20
+ applyColumnDefaults,
21
+ filterDisplayDataBySearch,
22
+ findRowIndexByJsonSnapshot,
23
+ flattenObject,
24
+ formatCellDisplayValue,
25
+ getStaticTableEquivalenceData,
26
+ hasEquivalenceConfig,
27
+ isBooleanValue,
28
+ isDateValue,
29
+ parseCellEditValue,
30
+ rebuildObject,
31
+ resolveStaticTableSortState,
32
+ setNestedValue as assignNestedValue,
33
+ sortDisplayDataInPlace,
34
+ toggleNestedBooleanOnItem} from './complete-table.util';
35
+ import { Icons } from '../../.config/icons/icons.lucide';
36
+ import { JIconComponent } from '../../icon/icon.component';
37
+
38
+ export type {
39
+ CompleteTableEquivalenceConfig,
40
+ CompleteTableEquivalenceConfigs,
41
+ CompleteTableEquivalenceData,
42
+ CompleteTableEquivalenceMaps,
43
+ CompleteTableEquivalenceOption,
44
+ CompleteTableGetCellValueFn,
45
+ CompleteTableSortDirection} from './complete-table.types';
46
+ export {
47
+ applyColumnDefaults,
48
+ filterDisplayDataBySearch,
49
+ findRowIndexByJsonSnapshot,
50
+ flattenObject,
51
+ formatCellDisplayValue,
52
+ getStaticTableEquivalenceData,
53
+ hasEquivalenceConfig,
54
+ isBooleanValue,
55
+ isDateValue,
56
+ parseCellEditValue,
57
+ rebuildObject,
58
+ resolveStaticTableSortState,
59
+ setNestedValue,
60
+ sortDisplayDataInPlace,
61
+ toggleNestedBooleanOnItem} from './complete-table.util';
62
+
63
+ /**
64
+ * Static-data table for bulk entry dialogs (search, sort, inline edit, save many).
65
+ *
66
+ * Install: `npx tailjng add table-complete`
67
+ *
68
+ * Logic lives in `complete-table.util.ts`; shared cell helpers reuse `table-crud-complete` utils.
69
+ */
12
70
  @Component({
13
71
  selector: 'JCompleteTable',
14
72
  standalone: true,
15
- imports: [CommonModule, FormsModule, LucideAngularModule, JButtonComponent, JInputCheckboxComponent, JInputComponent, JDropdownSelectComponent],
73
+ imports: [
74
+ CommonModule,
75
+ FormsModule,
76
+ JIconComponent,
77
+ JButtonComponent,
78
+ JInputCheckboxComponent,
79
+ JInputComponent,
80
+ JDropdownSelectComponent,
81
+ ],
16
82
  templateUrl: './complete-table.component.html',
17
83
  styleUrl: './complete-table.component.scss',
18
84
  })
19
85
  export class JCompleteTableComponent implements OnInit {
86
+ readonly Icons = Icons;
20
87
 
21
88
  Math = Math;
22
89
 
90
+ // =====================================================
91
+ // Inputs / outputs
92
+ // =====================================================
93
+
23
94
  @Input() endpoint!: string;
24
95
  @Input() mainEndpoint!: string;
25
- @Output() closeDialog: EventEmitter<void> = new EventEmitter<void>();
96
+ @Output() closeDialog = new EventEmitter<void>();
26
97
 
27
98
  @Input() staticData: any[] = [];
28
99
  @Input() columns: TableColumn<any>[] = [];
29
100
 
30
- displayData: any[] = [];
101
+ @Input() isOptions = true;
31
102
 
32
- // Tipo de opciones
33
- @Input() isOptions: boolean = true;
103
+ @Input() equivalenceConfigs: CompleteTableEquivalenceConfigs = {};
104
+ @Input() equivalences: CompleteTableEquivalenceMaps = {};
34
105
 
35
- // Buscador
36
- searchQuery: string = '';
37
- searchPlaceholder: string = 'Buscar...';
38
- isLoadingSearch: boolean = false;
106
+ // =====================================================
107
+ // State
108
+ // =====================================================
109
+
110
+ displayData: any[] = [];
111
+
112
+ searchQuery = '';
113
+ searchPlaceholder = 'Buscar...';
114
+ isLoadingSearch = false;
39
115
 
40
- // Ordenamiento
41
116
  sortColumn: string | null = null;
42
- sortDirection: 'asc' | 'desc' | 'none' = 'none';
117
+ sortDirection: CompleteTableSortDirection = 'none';
43
118
  sortingColumn: string | null = null;
44
- isLoadingSort: boolean = false;
119
+ isLoadingSort = false;
45
120
 
46
- // Edicion
47
121
  editingRowIndex: number | null = null;
48
- @Input() equivalenceConfigs: { [key: string]: { keyColumnSearch: string; keyReturn: string; keyAlternate?: string } } = {};
49
- @Input() equivalences: any = {};
122
+ editingRowItem: any = null;
50
123
 
51
124
  constructor(
52
- public readonly iconsService: JIconsService,
53
125
  public readonly transformService: JTransformService,
54
126
  private readonly converterService: JConverterCrudService,
55
127
  private readonly alertDialogService: JAlertDialogService,
56
128
  private readonly alertToastService: JAlertToastService,
57
129
  private readonly genericService: JGenericCrudService,
58
- ) { }
59
-
130
+ ) {}
60
131
 
61
132
  ngOnInit(): void {
62
- this.displayData = this.staticData.map(item => this.rebuildObject(item)) ?? [];
63
-
133
+ this.displayData = this.staticData.map((item) => this.rebuildObject(item)) ?? [];
64
134
  this.columnDefaults();
65
135
  }
66
136
 
67
- // Establece valores por defecto para las columnas si no están definidos
68
- columnDefaults() {
69
- this.columns.forEach(column => {
70
- if (column.visible === undefined) {
71
- column.visible = true;
72
- }
73
-
74
- if (column.sortable === undefined) {
75
- column.sortable = true;
76
- }
77
-
78
- if (column.isSearchable === undefined) {
79
- column.isSearchable = true;
80
- }
81
- });
137
+ // =====================================================
138
+ // Columns / cell values
139
+ // =====================================================
140
+
141
+ columnDefaults(): void {
142
+ applyColumnDefaults(this.columns);
82
143
  }
83
144
 
84
- // Método para obtener el valor de las celdas dinámicamente
85
145
  getValue(item: any, column: TableColumn<any>, raw = false): any {
86
- let value: any;
87
-
88
- // Si existe un valueGetter, se usa directamente
89
- if (typeof column.valueGetter === 'function') {
90
- value = column.valueGetter(item);
91
- } else {
92
- // Si no, se busca el valor por key (con soporte para claves anidadas)
93
- const keys = column.key.split('.');
94
- value = item;
95
-
96
- for (const key of keys) {
97
- if (value != null) {
98
- value = value[key];
99
- } else {
100
- value = null;
101
- break;
102
- }
103
- }
146
+ if (raw) {
147
+ return parseCellEditValue(item, column, this.converterService);
104
148
  }
105
149
 
106
- if (raw) {
107
- // DESFORMATEA SI SE ESTÁ EDITANDO
108
- return this.converterService.parseData(value, column);
109
- } else {
150
+ return formatCellDisplayValue(item, column, this.converterService);
151
+ }
110
152
 
111
- // Si value es null o undefined, retornar 'S/N'
112
- if (value === null || value === undefined) {
113
- return 'S/N';
114
- }
153
+ isBoolean(value: any): boolean {
154
+ return isBooleanValue(value);
155
+ }
115
156
 
116
- // Devuelve el valor formateado para mostrar en tabla
117
- const formatted = this.converterService.formatData(value, column);
118
- return formatted ?? value;
119
- }
157
+ isDate(value: any): boolean {
158
+ return isDateValue(value);
120
159
  }
121
160
 
161
+ flattenObject(obj: any, prefix = ''): any {
162
+ return flattenObject(obj, prefix);
163
+ }
164
+
165
+ rebuildObject(flatObj: any): any {
166
+ return rebuildObject(flatObj);
167
+ }
122
168
 
123
169
  // =====================================================
124
- // Metodos principales de la tabla
170
+ // Save / edit / delete
125
171
  // =====================================================
126
172
 
127
- // Método para guardar los registros actuales
128
173
  saveRecords(): void {
129
-
130
174
  if (this.displayData.length > 0) {
131
-
132
175
  this.alertDialogService.AlertDialog({
133
176
  type: 'question',
134
177
  title: '¿Estás seguro de que deseas guardar los registros?',
135
- description: 'Asegúrate de verificar que los datos sean correctos, tenga en cuenta que no se tiene el control total de las validaciones como el formulario unitario.',
136
- onConfirm: async () => new Promise<void>((resolve, reject) => {
137
-
138
- this.genericService.createMany<any[]>({ endpoint: this.mainEndpoint, data: this.displayData }).subscribe({
139
- next: (response) => {
140
-
141
- this.alertToastService.AlertToast({
142
- type: 'success',
143
- title: 'Registros guardados',
144
- description: response.message,
145
- });
146
-
147
- resolve();
148
-
149
- // Cerrar dialog
150
- this.closeDialog.emit();
151
- },
152
- error: reject
153
- })
154
-
155
- }),
156
- onCancel: () => { console.log('Guardado cancelado'); }
157
- })
158
-
159
- } else {
160
- this.alertToastService.AlertToast({
161
- type: 'info',
162
- title: 'No hay registros',
163
- description: 'No hay registros para guardar.',
164
- });
178
+ description:
179
+ 'Asegúrate de verificar que los datos sean correctos, tenga en cuenta que no se tiene el control total de las validaciones como el formulario unitario.',
180
+ onConfirm: async () =>
181
+ new Promise<void>((resolve, reject) => {
182
+ this.genericService
183
+ .createMany<any[]>({ endpoint: this.mainEndpoint, data: this.displayData })
184
+ .subscribe({
185
+ next: (response) => {
186
+ this.alertToastService.AlertToast({
187
+ type: 'success',
188
+ title: 'Registros guardados',
189
+ description: response.message});
190
+
191
+ resolve();
192
+ this.closeDialog.emit();
193
+ },
194
+ error: reject});
195
+ }),
196
+ onCancel: () => {
197
+ console.log('Guardado cancelado');
198
+ }});
199
+ return;
165
200
  }
166
- }
167
201
 
168
- // Editar registro
169
- editingRowItem: any = null;
202
+ this.alertToastService.AlertToast({
203
+ type: 'info',
204
+ title: 'No hay registros',
205
+ description: 'No hay registros para guardar.'});
206
+ }
170
207
 
171
208
  onEdit(item: any): void {
172
209
  if (this.editingRowItem === item) {
@@ -176,22 +213,16 @@ export class JCompleteTableComponent implements OnInit {
176
213
  }
177
214
  }
178
215
 
179
- // Eliminar registro
180
216
  onDelete(index: number): void {
181
-
182
217
  this.alertDialogService.AlertDialog({
183
218
  type: 'question',
184
219
  title: 'Confirmar eliminación',
185
220
  description: '¿Estás seguro de que deseas eliminar este registro?',
186
221
  onConfirm: () => {
187
- // Encuentra el objeto real que corresponde en staticData
188
222
  const itemToDelete = this.displayData[index];
189
-
190
- // Borrar de displayData
191
223
  this.displayData.splice(index, 1);
192
224
 
193
- // Buscar y borrar de staticData
194
- const indexInStatic = this.staticData.findIndex(item => {
225
+ const indexInStatic = this.staticData.findIndex((item) => {
195
226
  const rebuilt = this.rebuildObject(item);
196
227
  return JSON.stringify(rebuilt) === JSON.stringify(itemToDelete);
197
228
  });
@@ -200,321 +231,142 @@ export class JCompleteTableComponent implements OnInit {
200
231
  this.staticData.splice(indexInStatic, 1);
201
232
  }
202
233
  },
203
- onCancel: () => { console.log('Eliminación cancelada'); }
204
- })
205
-
234
+ onCancel: () => {
235
+ console.log('Eliminación cancelada');
236
+ }});
206
237
  }
207
238
 
208
239
  // =====================================================
209
- // Funcionalidad de editar en tabla
240
+ // Inline editing
210
241
  // =====================================================
211
242
 
212
- // Verifica si una columna tiene configuración de equivalencia
213
243
  hasEquivalence(columnKey: string): boolean {
214
- for (const [, conf] of Object.entries(this.equivalenceConfigs || {})) {
215
- if (conf && conf.keyColumnSearch === columnKey) {
216
- return true;
217
- }
218
- }
219
- return false;
244
+ return hasEquivalenceConfig(this.equivalenceConfigs, columnKey);
220
245
  }
221
246
 
222
- // Obtiene los datos de equivalencia para una columna específica
223
- getEquivalenceData(columnKey: string): {
224
- keyReturn: string;
225
- options: { key: any; label: string }[];
226
- } | null {
227
- for (const [name, conf] of Object.entries(this.equivalenceConfigs || {})) {
228
- if (conf && conf.keyColumnSearch === columnKey) {
229
- const keyReturn = conf.keyReturn;
230
- const idMap = (this.equivalences as any)[keyReturn];
231
- if (!idMap) return null;
232
-
233
- const options = Object.entries(idMap).map(([id, label]) => ({
234
- key: +id,
235
- label: String(label),
236
- }));
237
-
238
- return {
239
- keyReturn,
240
- options,
241
- };
242
- }
243
- }
244
- return null;
247
+ getEquivalenceData(columnKey: string): ReturnType<typeof getStaticTableEquivalenceData> {
248
+ return getStaticTableEquivalenceData(this.equivalenceConfigs, this.equivalences, columnKey);
245
249
  }
246
250
 
247
-
248
-
249
- // Maneja el cambio de equivalencia en un item
250
251
  onEquivalenceChange(item: any, columnKey: string, newId: any): void {
251
252
  const eqData = this.getEquivalenceData(columnKey);
252
- if (!eqData) return;
253
+ if (!eqData) {
254
+ return;
255
+ }
253
256
 
254
257
  const newLabel = eqData.options.find((opt) => opt.key == newId)?.label ?? null;
255
-
256
- this.setNestedValue(item, columnKey, newLabel);
258
+ assignNestedValue(item, columnKey, newLabel);
257
259
  item[eqData.keyReturn] = newId;
258
260
 
259
- const index = this.displayData.findIndex(
260
- (row) => JSON.stringify(row) === JSON.stringify(item)
261
- );
261
+ const index = findRowIndexByJsonSnapshot(this.displayData, item);
262
262
  if (index !== -1) {
263
263
  this.staticData[index] = this.flattenObject(item);
264
264
  }
265
265
  }
266
266
 
267
-
268
- // Maneja el cambio de valor en un campo editable
269
267
  onFieldChange(item: any, columnKey: string, value: any): void {
270
- this.setNestedValue(item, columnKey, value);
271
- const index = this.displayData.findIndex(row => JSON.stringify(row) === JSON.stringify(item));
268
+ assignNestedValue(item, columnKey, value);
269
+
270
+ const index = findRowIndexByJsonSnapshot(this.displayData, item);
272
271
  if (index !== -1) {
273
272
  this.staticData[index] = this.flattenObject(item);
274
273
  }
275
274
  }
276
275
 
277
-
278
- // Establece un valor anidado en un objeto según la clave de columna
279
276
  setNestedValue(item: any, columnKey: string, value: any): void {
280
- const keys = columnKey.split('.');
281
- let current = item;
282
-
283
- for (let i = 0; i < keys.length; i++) {
284
- const key = keys[i];
285
- if (i === keys.length - 1) {
286
- current[key] = value;
287
- } else {
288
- if (!current[key]) {
289
- current[key] = {};
290
- }
291
- current = current[key];
292
- }
293
- }
277
+ assignNestedValue(item, columnKey, value);
294
278
  }
295
279
 
296
280
  // =====================================================
297
- // Cambiar estado en campos booleanos
281
+ // Checkbox (local toggle)
298
282
  // =====================================================
299
283
 
300
- // Método para cambiar el estado de un checkbox
301
284
  onCheckboxChange(item: any, column: TableColumn<any>): void {
302
285
  const newValue = this.getValue(item, column);
286
+ toggleNestedBooleanOnItem(item, column, newValue);
303
287
 
304
- // Invierte el valor
305
- const updatedValue = !newValue;
306
-
307
- const keys = column.key.split('.');
308
- let current = item;
309
-
310
- for (let i = 0; i < keys.length; i++) {
311
- const key = keys[i];
312
-
313
- if (i === keys.length - 1) {
314
- current[key] = updatedValue;
315
- } else {
316
- if (!current[key]) {
317
- current[key] = {};
318
- }
319
- current = current[key];
320
- }
321
- }
322
-
323
- // Buscar índice real
324
- const index = this.displayData.findIndex((row) => {
325
- return JSON.stringify(row) === JSON.stringify(item);
326
- });
327
-
288
+ const index = findRowIndexByJsonSnapshot(this.displayData, item);
328
289
  if (index !== -1) {
329
290
  this.staticData[index] = this.flattenObject(item);
330
291
  }
331
292
  }
332
293
 
333
-
334
-
335
294
  // =====================================================
336
- // Buscador
295
+ // Search
337
296
  // =====================================================
338
297
 
339
- // Ejecuta búsqueda al cambiar el valor del input.
340
298
  onSearch(): void {
341
299
  this.isLoadingSearch = true;
342
300
 
343
301
  setTimeout(() => {
344
- if (!this.searchQuery?.trim()) {
345
- // Si está vacío, mostrar todo
346
- this.displayData = this.staticData.map(item => this.rebuildObject(item));
347
- } else {
348
- // Campos sobre los que buscar
349
- const searchableKeys = this.columns
350
- .filter((col) => col.isSearchable)
351
- .map(col => col.key);
352
-
353
- this.displayData = this.staticData
354
- .map(item => this.rebuildObject(item))
355
- .filter(item => {
356
- return searchableKeys.some(key => {
357
- const column = this.columns.find(col => col.key === key);
358
- const value = column ? this.getValue(item, column) : undefined;
359
- return value?.toString().toLowerCase().includes(this.searchQuery.trim().toLowerCase());
360
- });
361
- });
362
- }
302
+ this.displayData = filterDisplayDataBySearch(
303
+ this.staticData,
304
+ this.columns,
305
+ this.searchQuery,
306
+ (row, column) => this.getValue(row, column),
307
+ );
363
308
 
364
309
  this.isLoadingSearch = false;
365
310
  }, 300);
366
311
  }
367
312
 
368
- // Limpia el campo de búsqueda y vuelve a cargar los datos originales.
369
313
  clearSearch(): void {
370
314
  this.searchQuery = '';
371
315
  this.onSearch();
372
316
  }
373
317
 
374
-
375
318
  // =====================================================
376
- // Ordenamiento
319
+ // Sort
377
320
  // =====================================================
378
321
 
379
- // Ejecuta ordenamiento al hacer clic en cabecera.
380
322
  onSort(column: TableColumn<any>): void {
381
- if (!column.sortable) return;
323
+ if (!column.sortable) {
324
+ return;
325
+ }
382
326
 
383
327
  this.sortingColumn = column.key;
384
328
  this.isLoadingSort = true;
385
329
 
386
330
  setTimeout(() => {
387
- if (this.sortColumn === column.key) {
388
- if (this.sortDirection === 'asc') {
389
- this.sortDirection = 'desc';
390
- } else if (this.sortDirection === 'desc') {
391
- this.sortDirection = 'none';
392
- this.sortColumn = null;
393
- } else {
394
- this.sortDirection = 'asc';
395
- }
396
- } else {
397
- this.sortColumn = column.key;
398
- this.sortDirection = 'asc';
399
- }
400
-
331
+ const next = resolveStaticTableSortState(column, this.sortColumn, this.sortDirection);
332
+ this.sortColumn = next.sortColumn;
333
+ this.sortDirection = next.sortDirection;
401
334
  this.sortData();
402
-
403
335
  this.isLoadingSort = false;
404
336
  }, 300);
405
337
  }
406
338
 
407
-
408
- // Ordena los datos estáticos según sortColumn y sortDirection.
409
339
  sortData(): void {
410
340
  if (!this.sortColumn || this.sortDirection === 'none') {
411
- // Restaurar orden original si no hay ordenamiento
412
341
  this.onSearch();
413
342
  return;
414
343
  }
415
344
 
416
- const column = this.columns.find(col => col.key === this.sortColumn);
417
- if (!column) return;
418
-
419
- this.displayData.sort((a, b) => {
420
- const valA = this.getValue(a, column);
421
- const valB = this.getValue(b, column);
422
-
423
- if (valA === 'S/N' && valB !== 'S/N') return -1;
424
- if (valB === 'S/N' && valA !== 'S/N') return 1;
425
-
426
- if (valA == null || valB == null) return 0;
427
-
428
- const compA = valA.toString().toLowerCase();
429
- const compB = valB.toString().toLowerCase();
430
-
431
- if (compA < compB) return this.sortDirection === 'asc' ? -1 : 1;
432
- if (compA > compB) return this.sortDirection === 'asc' ? 1 : -1;
433
- return 0;
434
- });
345
+ sortDisplayDataInPlace(
346
+ this.displayData,
347
+ this.sortColumn,
348
+ this.sortDirection,
349
+ this.columns,
350
+ (row, column) => this.getValue(row, column),
351
+ );
435
352
  }
436
353
 
437
- // Verifica si la tabla está en proceso de ordenamiento o búsqueda.
438
354
  isLoading(type: 'sort' | 'search'): boolean {
439
355
  if (type === 'sort') {
440
356
  return this.isLoadingSort;
441
- } else if (type === 'search') {
357
+ }
358
+
359
+ if (type === 'search') {
442
360
  return this.isLoadingSearch;
443
361
  }
362
+
444
363
  return false;
445
364
  }
446
365
 
447
- // Maneja el evento de ordenamiento al presionar Enter o Espacio en la cabecera.
448
366
  onSortKeyPress(event: KeyboardEvent, column: TableColumn<any>): void {
449
367
  if (event.key === 'Enter' || event.key === ' ') {
450
368
  event.preventDefault();
451
369
  this.onSort(column);
452
370
  }
453
371
  }
454
-
455
- // =====================================================
456
- // Prosesamiento de datos
457
- // =====================================================
458
-
459
- // Obtener el valor de las celdas para identificar un campo booleano
460
- isBoolean(value: any): boolean {
461
- return typeof value === 'boolean';
462
- }
463
-
464
- // Obtener el valor de las celdas para identificar un campo de fecha
465
- isDate(value: any): boolean {
466
- return value instanceof Date || (typeof value === 'string' && !isNaN(Date.parse(value)));
467
- }
468
-
469
- // Obtener los datos planos
470
- flattenObject(obj: any, prefix: string = ''): any {
471
- let result: any = {};
472
-
473
- for (const key of Object.keys(obj)) {
474
- const value = obj[key];
475
- const prefixedKey = prefix ? `${prefix}.${key}` : key;
476
-
477
- if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
478
- Object.assign(result, this.flattenObject(value, prefixedKey));
479
- } else {
480
- result[prefixedKey] = value;
481
- }
482
- }
483
-
484
- return result;
485
- }
486
-
487
- // Reconstruir el objeto a partir de los datos planos
488
- rebuildObject(flatObj: any): any {
489
- const result: any = {};
490
-
491
- for (const key in flatObj) {
492
- if (!Object.prototype.hasOwnProperty.call(flatObj, key)) continue;
493
-
494
- const value = flatObj[key];
495
-
496
- if (key.includes('.')) {
497
- const parts = key.split('.');
498
- let current = result;
499
-
500
- for (let i = 0; i < parts.length; i++) {
501
- const part = parts[i];
502
-
503
- if (i === parts.length - 1) {
504
- current[part] = value;
505
- } else {
506
- if (!current[part]) {
507
- current[part] = {};
508
- }
509
- current = current[part];
510
- }
511
- }
512
- } else {
513
- result[key] = value;
514
- }
515
- }
516
-
517
- return result;
518
- }
519
-
520
- }
372
+ }