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.
- package/README.md +12 -4
- package/cli/execute/init-app.js +5 -2
- package/cli/execute/sync-app.js +14 -2
- package/cli/settings/colors-config-utils.js +43 -8
- package/cli/settings/icons-config-utils.js +62 -0
- package/cli/settings/path-utils.js +32 -2
- package/cli/settings/project-utils.js +7 -1
- package/cli/templates/app.generator.js +2 -2
- package/fesm2022/tailjng.mjs +247 -80
- package/fesm2022/tailjng.mjs.map +1 -1
- package/lib/services/static/theme.service.d.ts +39 -1
- package/lib/utils/theme/theme-variables.util.d.ts +31 -0
- package/package.json +1 -1
- package/public-api.d.ts +2 -1
- package/registry/components.json +41 -18
- package/src/colors.safelist.css +2 -2
- package/src/lib/components/.config/README.md +11 -0
- package/src/lib/components/.config/colors/README.md +38 -0
- package/src/lib/components/{colors-config → .config/colors}/colors.config.ts +5 -5
- package/src/lib/components/{colors-config → .config/colors}/colors.safelist.css +2 -2
- package/src/lib/components/.config/icons/README.md +26 -0
- package/src/lib/components/.config/icons/icons.lucide.ts +134 -0
- package/src/lib/components/.config/input/README.md +24 -0
- package/src/lib/components/.config/input/input.classes.ts +119 -0
- package/src/lib/components/alert/alert-dialog/dialog-alert.component.css +244 -2
- package/src/lib/components/alert/alert-dialog/dialog-alert.component.html +25 -38
- package/src/lib/components/alert/alert-dialog/dialog-alert.component.ts +66 -56
- package/src/lib/components/alert/alert-dialog/dialog-alert.types.ts +19 -0
- package/src/lib/components/alert/alert-toast/toast-alert.component.css +630 -12
- package/src/lib/components/alert/alert-toast/toast-alert.component.html +103 -102
- package/src/lib/components/alert/alert-toast/toast-alert.component.ts +485 -128
- package/src/lib/components/alert/alert-toast/toast-alert.types.ts +25 -0
- package/src/lib/components/badge/badge.component.html +34 -21
- package/src/lib/components/badge/badge.component.ts +140 -31
- package/src/lib/components/button/button.component.html +16 -10
- package/src/lib/components/button/button.component.ts +162 -22
- package/src/lib/components/card/card-complete/complete-card.component.html +2 -2
- package/src/lib/components/card/card-complete/complete-card.component.ts +26 -16
- package/src/lib/components/card/card-crud-complete/complete-crud-card.component.html +2 -2
- package/src/lib/components/card/card-crud-complete/complete-crud-card.component.ts +26 -16
- package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.css +97 -0
- package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.html +54 -46
- package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.ts +135 -64
- package/src/lib/components/checkbox/checkbox-input/input-checkbox.types.ts +3 -0
- package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.css +112 -0
- package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.html +28 -25
- package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.ts +67 -15
- package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.types.ts +1 -0
- package/src/lib/components/coach-mark/coach-mark.component.html +4 -22
- package/src/lib/components/coach-mark/coach-mark.component.scss +1 -1
- package/src/lib/components/coach-mark/coach-mark.component.ts +51 -18
- package/src/lib/components/coach-mark/coach-mark.directive.ts +133 -78
- package/src/lib/components/coach-mark/coach-mark.types.ts +12 -0
- package/src/lib/components/dialog/dialog.component.css +103 -1
- package/src/lib/components/dialog/dialog.component.html +46 -66
- package/src/lib/components/dialog/dialog.component.ts +136 -110
- package/src/lib/components/dialog/dialog.types.ts +19 -0
- package/src/lib/components/filter/filter-complete/complete-filter.component.html +16 -19
- package/src/lib/components/filter/filter-complete/complete-filter.component.scss +35 -0
- package/src/lib/components/filter/filter-complete/complete-filter.component.ts +58 -34
- package/src/lib/components/filter/filter-complete/complete-filter.types.ts +7 -0
- package/src/lib/components/filter/filter-complete/complete-filter.util.ts +16 -0
- package/src/lib/components/form/form-container/container-form.component.css +4 -0
- package/src/lib/components/form/form-container/container-form.component.html +2 -2
- package/src/lib/components/form/form-container/container-form.component.ts +72 -16
- package/src/lib/components/form/form-container/container-form.types.ts +42 -0
- package/src/lib/components/form/form-container/form-col-span.directive.ts +25 -0
- package/src/lib/components/form/form-sidebar/sidebar-form.component.css +276 -0
- package/src/lib/components/form/form-sidebar/sidebar-form.component.html +117 -125
- package/src/lib/components/form/form-sidebar/sidebar-form.component.ts +109 -34
- package/src/lib/components/form/form-sidebar/sidebar-form.types.ts +3 -0
- package/src/lib/components/{toggle-radio/toggle-radio.component.css → form/form-validation/validation-form.component.css} +0 -1
- package/src/lib/components/form/form-validation/validation-form.component.html +10 -6
- package/src/lib/components/form/form-validation/validation-form.component.ts +99 -12
- package/src/lib/components/form/form-validation/validation-form.types.ts +33 -0
- package/src/lib/components/icon/icon.component.html +8 -5
- package/src/lib/components/icon/icon.component.ts +111 -9
- package/src/lib/components/input/input/input.component.html +19 -16
- package/src/lib/components/input/input/input.component.ts +130 -53
- package/src/lib/components/input/input/input.types.ts +8 -0
- package/src/lib/components/input/input-file/file-input.component.html +65 -56
- package/src/lib/components/input/input-file/file-input.component.ts +276 -173
- package/src/lib/components/input/input-file/file-input.types.ts +2 -0
- package/src/lib/components/input/input-range/range-input.component.css +67 -0
- package/src/lib/components/input/input-range/range-input.component.html +50 -58
- package/src/lib/components/input/input-range/range-input.component.ts +148 -60
- package/src/lib/components/input/input-range/range-input.types.ts +7 -0
- package/src/lib/components/input/input-textarea/textarea-input.component.html +16 -7
- package/src/lib/components/input/input-textarea/textarea-input.component.ts +140 -50
- package/src/lib/components/input/input-textarea/textarea-input.types.ts +2 -0
- package/src/lib/components/label/label.component.html +17 -16
- package/src/lib/components/label/label.component.ts +70 -16
- package/src/lib/components/label/label.types.ts +2 -0
- package/src/lib/components/menu/menu-options-table/menu-options-defaults.ts +34 -0
- package/src/lib/components/menu/menu-options-table/options-table-menu.component.html +34 -20
- package/src/lib/components/menu/menu-options-table/options-table-menu.component.ts +211 -58
- package/src/lib/components/menu/menu-options-table/options-table-menu.types.ts +38 -0
- package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.html +49 -52
- package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.ts +112 -24
- package/src/lib/components/menu/options-coach-menu/options-coach-menu.types.ts +9 -0
- package/src/lib/components/mode-toggle/mode-toggle.component.html +11 -16
- package/src/lib/components/mode-toggle/mode-toggle.component.ts +69 -33
- package/src/lib/components/paginator/paginator-complete/complete-paginator.component.html +4 -4
- package/src/lib/components/paginator/paginator-complete/complete-paginator.component.ts +31 -7
- package/src/lib/components/paginator/paginator-complete/complete-paginator.types.ts +12 -0
- package/src/lib/components/paginator/paginator-complete/complete-paginator.util.ts +36 -0
- package/src/lib/components/progress-bar/progress-bar.component.css +11 -0
- package/src/lib/components/progress-bar/progress-bar.component.html +41 -40
- package/src/lib/components/progress-bar/progress-bar.component.ts +95 -11
- package/src/lib/components/progress-bar/progress-bar.types.ts +2 -0
- package/src/lib/components/select/select-dropdown/dropdown-select.component.css +6 -0
- package/src/lib/components/select/select-dropdown/dropdown-select.component.html +54 -44
- package/src/lib/components/select/select-dropdown/dropdown-select.component.ts +450 -509
- package/src/lib/components/select/select-dropdown/dropdown-select.types.ts +43 -0
- package/src/lib/components/select/select-dropdown/dropdown-select.util.ts +179 -0
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.css +6 -0
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.html +131 -42
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.ts +491 -475
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.types.ts +22 -0
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.util.ts +20 -0
- package/src/lib/components/select/select-multi-table/multi-table-select.component.css +10 -0
- package/src/lib/components/select/select-multi-table/multi-table-select.component.html +76 -60
- package/src/lib/components/select/select-multi-table/multi-table-select.component.ts +250 -313
- package/src/lib/components/select/select-multi-table/multi-table-select.types.ts +10 -0
- package/src/lib/components/select/select-multi-table/multi-table-select.util.ts +5 -0
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.css +212 -0
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.html +62 -53
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.ts +84 -27
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.types.ts +2 -0
- package/src/lib/components/table/table-complete/complete-table.component.html +15 -17
- package/src/lib/components/table/table-complete/complete-table.component.ts +190 -338
- package/src/lib/components/table/table-complete/complete-table.types.ts +28 -0
- package/src/lib/components/table/table-complete/complete-table.util.ts +236 -0
- package/src/lib/components/table/table-complete/index.ts +2 -0
- package/src/lib/components/table/table-crud-complete/complete-crud-table.animations.ts +34 -0
- package/src/lib/components/table/table-crud-complete/complete-crud-table.component.html +73 -128
- package/src/lib/components/table/table-crud-complete/complete-crud-table.component.ts +542 -829
- package/src/lib/components/table/table-crud-complete/complete-crud-table.types.ts +57 -0
- package/src/lib/components/table/table-crud-complete/complete-crud-table.util.ts +723 -0
- package/src/lib/components/table/table-crud-complete/index.ts +3 -0
- package/src/lib/components/theme-generator/theme-generator.component.css +21 -0
- package/src/lib/components/theme-generator/theme-generator.component.html +146 -116
- package/src/lib/components/theme-generator/theme-generator.component.ts +44 -24
- package/src/lib/components/toggle-radio/shared/toggle-options.types.ts +8 -0
- package/src/lib/components/toggle-radio/shared/toggle-options.util.ts +44 -0
- package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.css +135 -0
- package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.html +52 -0
- package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.ts +198 -0
- package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.types.ts +1 -0
- package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.css +108 -0
- package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.html +37 -0
- package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.ts +193 -0
- package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.types.ts +1 -0
- package/src/lib/components/tooltip/tooltip.directive.ts +12 -9
- package/src/lib/components/tooltip/tooltip.service.ts +331 -133
- package/src/lib/components/tooltip/tooltip.types.ts +9 -0
- package/src/lib/components/viewer/viewer-image/image-viewer.component.css +90 -4
- package/src/lib/components/viewer/viewer-image/image-viewer.component.html +52 -103
- package/src/lib/components/viewer/viewer-image/image-viewer.component.ts +182 -177
- package/src/lib/components/viewer/viewer-image/image-viewer.types.ts +3 -0
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.css +177 -0
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.html +74 -24
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.ts +168 -15
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.types.ts +1 -0
- package/src/styles.css +2 -2
- package/lib/services/static/icons.service.d.ts +0 -65
- package/src/lib/components/colors-config/README.md +0 -38
- package/src/lib/components/form/form-sidebar/sidebar-form.component.scss +0 -0
- package/src/lib/components/form/form-validation/validation-form.component.scss +0 -0
- package/src/lib/components/menu/menu-options-table/options-table-menu.component.scss +0 -0
- package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.scss +0 -12
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.scss +0 -0
- package/src/lib/components/toggle-radio/toggle-radio.component.html +0 -51
- package/src/lib/components/toggle-radio/toggle-radio.component.ts +0 -222
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.scss +0 -0
- package/tailjng-0.1.6.tgz +0 -0
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, TemplateRef } from '@angular/core';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import { FormsModule } from '@angular/forms';
|
|
4
|
-
import { LucideAngularModule } from 'lucide-angular';
|
|
5
4
|
import { Params } from '@angular/router';
|
|
6
|
-
import { DomSanitizer,
|
|
7
|
-
import { animate, state, style, transition, trigger } from '@angular/animations';
|
|
5
|
+
import { DomSanitizer, SafeHtml, SafeUrl } from '@angular/platform-browser';
|
|
8
6
|
import { forkJoin } from 'rxjs';
|
|
9
7
|
|
|
10
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
EnableBoolean,
|
|
10
|
+
FilterButton,
|
|
11
|
+
FilterSelect,
|
|
12
|
+
JAlertToastService,
|
|
13
|
+
JConverterCrudService,
|
|
14
|
+
JDialogShared,
|
|
15
|
+
JFormShared,
|
|
16
|
+
JGenericCrudService,
|
|
17
|
+
JTransformService,
|
|
18
|
+
JUploadFilterService,
|
|
19
|
+
LoadingState,
|
|
20
|
+
LoadingStates,
|
|
21
|
+
OptionsTable,
|
|
22
|
+
SortDirection,
|
|
23
|
+
TableColumn} from 'tailjng';
|
|
11
24
|
|
|
12
25
|
import { JCompletePaginatorComponent } from '../../paginator/paginator-complete/complete-paginator.component';
|
|
13
26
|
import { JCompleteFilterComponent } from '../../filter/filter-complete/complete-filter.component';
|
|
@@ -19,68 +32,150 @@ import { JDropdownSelectComponent } from '../../select/select-dropdown/dropdown-
|
|
|
19
32
|
import { JInputComponent } from '../../input/input/input.component';
|
|
20
33
|
import { JImageViewerComponent } from '../../viewer/viewer-image/image-viewer.component';
|
|
21
34
|
import { JOptionsCoachMenuComponent } from '../../menu/options-coach-menu/options-coach-menu.component';
|
|
22
|
-
|
|
35
|
+
import { CRUD_TABLE_ANIMATIONS } from './complete-crud-table.animations';
|
|
36
|
+
import type {
|
|
37
|
+
CrudTableEquivalenceConfigs,
|
|
38
|
+
CrudTableEquivalenceMaps,
|
|
39
|
+
CrudTableGroupTableConfig} from './complete-crud-table.types';
|
|
40
|
+
import { Icons } from '../../.config/icons/icons.lucide';
|
|
41
|
+
import { JIconComponent } from '../../icon/icon.component';
|
|
42
|
+
import {
|
|
43
|
+
applyColumnDefaults,
|
|
44
|
+
applyNestedBooleanToggle,
|
|
45
|
+
bindFilterSelectHandlers,
|
|
46
|
+
buildCrudTableQueryParams,
|
|
47
|
+
buildRowNgClass,
|
|
48
|
+
cacheExpandedRowKeys,
|
|
49
|
+
calculateStartIndex,
|
|
50
|
+
calculateTotalPages,
|
|
51
|
+
countVisibleColumns,
|
|
52
|
+
deepCopy,
|
|
53
|
+
formatCellDisplayValue,
|
|
54
|
+
generatePaginationPages,
|
|
55
|
+
getColumnColStyles,
|
|
56
|
+
getEquivalenceData as resolveEquivalenceData,
|
|
57
|
+
getGroupCollapsedHeaderColspan,
|
|
58
|
+
getGroupExpandColspan,
|
|
59
|
+
getGroupMobileStudentHeaderColumns,
|
|
60
|
+
getGroupMobileStudentIndex,
|
|
61
|
+
getGroupMobileStudentTitle,
|
|
62
|
+
getMobileHeaderColumns,
|
|
63
|
+
getMobileMainDescription,
|
|
64
|
+
getMobileMainTitle,
|
|
65
|
+
hasEquivalenceConfig,
|
|
66
|
+
isBooleanValue,
|
|
67
|
+
isDateValue,
|
|
68
|
+
mergeOptionNgClasses,
|
|
69
|
+
normalizeGroupMetaTitles,
|
|
70
|
+
parseCellEditValue,
|
|
71
|
+
resolveCheckboxToggleTarget,
|
|
72
|
+
resolveMainEndpoint,
|
|
73
|
+
resolveNextSortState,
|
|
74
|
+
resolveOptionDisabled,
|
|
75
|
+
resolveOptionIcon,
|
|
76
|
+
resolveOptionTooltip,
|
|
77
|
+
resolveOptionVisible,
|
|
78
|
+
restoreExpandedRowsAfterLoad,
|
|
79
|
+
setNestedValue,
|
|
80
|
+
shouldStopCellClickPropagation,
|
|
81
|
+
syncCheckedFilterSelects,
|
|
82
|
+
trackGroupRowKey} from './complete-crud-table.util';
|
|
83
|
+
|
|
84
|
+
export type {
|
|
85
|
+
BuildCrudTableQueryParamsOptions,
|
|
86
|
+
CrudTableCheckboxToggleTarget,
|
|
87
|
+
CrudTableEquivalenceConfig,
|
|
88
|
+
CrudTableEquivalenceConfigs,
|
|
89
|
+
CrudTableEquivalenceData,
|
|
90
|
+
CrudTableEquivalenceMaps,
|
|
91
|
+
CrudTableEquivalenceOption,
|
|
92
|
+
CrudTableGetCellValueFn,
|
|
93
|
+
CrudTableGroupTableConfig,
|
|
94
|
+
CrudTableRowColorSemantic} from './complete-crud-table.types';
|
|
95
|
+
export {
|
|
96
|
+
applyColumnDefaults,
|
|
97
|
+
applyNestedBooleanToggle,
|
|
98
|
+
bindFilterSelectHandlers,
|
|
99
|
+
buildCrudTableQueryParams,
|
|
100
|
+
buildRowNgClass,
|
|
101
|
+
cacheExpandedRowKeys,
|
|
102
|
+
calculateStartIndex,
|
|
103
|
+
calculateTotalPages,
|
|
104
|
+
collectSearchFieldKeys,
|
|
105
|
+
countVisibleColumns,
|
|
106
|
+
CRUD_TABLE_ROW_COLOR_CLASS_MAP,
|
|
107
|
+
deepCopy,
|
|
108
|
+
formatCellDisplayValue,
|
|
109
|
+
generatePaginationPages,
|
|
110
|
+
getColumnColStyles,
|
|
111
|
+
getEquivalenceData,
|
|
112
|
+
getExpandedRowCacheKey,
|
|
113
|
+
getGroupCollapsedHeaderColspan,
|
|
114
|
+
getGroupExpandColspan,
|
|
115
|
+
getGroupMobileStudentHeaderColumns,
|
|
116
|
+
getGroupMobileStudentIndex,
|
|
117
|
+
getGroupMobileStudentTitle,
|
|
118
|
+
getMobileHeaderColumns,
|
|
119
|
+
getMobileMainDescription,
|
|
120
|
+
getMobileMainTitle,
|
|
121
|
+
hasEquivalenceConfig,
|
|
122
|
+
isBooleanValue,
|
|
123
|
+
isDateValue,
|
|
124
|
+
mergeOptionNgClasses,
|
|
125
|
+
normalizeGroupMetaTitles,
|
|
126
|
+
parseCellEditValue,
|
|
127
|
+
resolveCheckboxToggleTarget,
|
|
128
|
+
resolveMainEndpoint,
|
|
129
|
+
resolveNextSortState,
|
|
130
|
+
resolveOptionDisabled,
|
|
131
|
+
resolveOptionIcon,
|
|
132
|
+
resolveOptionTooltip,
|
|
133
|
+
resolveOptionVisible,
|
|
134
|
+
restoreExpandedRowsAfterLoad,
|
|
135
|
+
setNestedValue,
|
|
136
|
+
shouldStopCellClickPropagation,
|
|
137
|
+
syncCheckedFilterSelects,
|
|
138
|
+
trackGroupRowKey} from './complete-crud-table.util';
|
|
139
|
+
export { CRUD_TABLE_ANIMATIONS } from './complete-crud-table.animations';
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Full CRUD data table: filters, pagination, sorting, inline edit, expand rows, group mode.
|
|
143
|
+
*
|
|
144
|
+
* Install: `npx tailjng add table-crud-complete`
|
|
145
|
+
*
|
|
146
|
+
* Heavy logic lives in `complete-crud-table.util.ts`; expand grid in `expand-grid/`.
|
|
147
|
+
*/
|
|
23
148
|
@Component({
|
|
24
149
|
selector: 'JCompleteCrudTable',
|
|
25
150
|
standalone: true,
|
|
26
|
-
imports: [
|
|
151
|
+
imports: [
|
|
152
|
+
CommonModule,
|
|
153
|
+
FormsModule,
|
|
154
|
+
JCompletePaginatorComponent,
|
|
155
|
+
JCompleteFilterComponent,
|
|
156
|
+
JIconComponent,
|
|
157
|
+
JButtonComponent,
|
|
158
|
+
JInputCheckboxComponent,
|
|
159
|
+
JDialogComponent,
|
|
160
|
+
JImageViewerComponent,
|
|
161
|
+
JDropdownSelectComponent,
|
|
162
|
+
JInputComponent,
|
|
163
|
+
JOptionsCoachMenuComponent,
|
|
164
|
+
],
|
|
27
165
|
templateUrl: './complete-crud-table.component.html',
|
|
28
166
|
styleUrl: './complete-crud-table.component.scss',
|
|
29
|
-
animations:
|
|
30
|
-
trigger('slideToggle', [
|
|
31
|
-
state('collapsed', style({
|
|
32
|
-
height: '0',
|
|
33
|
-
opacity: 0,
|
|
34
|
-
})),
|
|
35
|
-
state('expanded', style({
|
|
36
|
-
height: '*',
|
|
37
|
-
opacity: 1,
|
|
38
|
-
})),
|
|
39
|
-
transition('collapsed <=> expanded', [
|
|
40
|
-
animate('0.3s ease-in-out')
|
|
41
|
-
])
|
|
42
|
-
]),
|
|
43
|
-
trigger('groupSlideToggle', [
|
|
44
|
-
state('collapsed', style({
|
|
45
|
-
height: '0',
|
|
46
|
-
opacity: 0,
|
|
47
|
-
overflow: 'hidden',
|
|
48
|
-
})),
|
|
49
|
-
state('expanded', style({
|
|
50
|
-
height: '*',
|
|
51
|
-
opacity: 1,
|
|
52
|
-
overflow: 'visible',
|
|
53
|
-
})),
|
|
54
|
-
transition('collapsed => expanded', [
|
|
55
|
-
animate('380ms cubic-bezier(0.4, 0, 0.2, 1)'),
|
|
56
|
-
]),
|
|
57
|
-
transition('expanded => collapsed', [
|
|
58
|
-
animate('300ms cubic-bezier(0.4, 0, 0.6, 1)'),
|
|
59
|
-
]),
|
|
60
|
-
]),
|
|
61
|
-
trigger('groupRowReveal', [
|
|
62
|
-
transition(':enter', [
|
|
63
|
-
style({ opacity: 0, transform: 'translateY(-4px)' }),
|
|
64
|
-
animate(
|
|
65
|
-
'280ms cubic-bezier(0.4, 0, 0.2, 1)',
|
|
66
|
-
style({ opacity: 1, transform: 'translateY(0)' }),
|
|
67
|
-
),
|
|
68
|
-
]),
|
|
69
|
-
transition(':leave', [
|
|
70
|
-
animate(
|
|
71
|
-
'220ms cubic-bezier(0.4, 0, 0.6, 1)',
|
|
72
|
-
style({ opacity: 0, transform: 'translateY(-4px)' }),
|
|
73
|
-
),
|
|
74
|
-
]),
|
|
75
|
-
]),
|
|
76
|
-
]
|
|
77
|
-
})
|
|
167
|
+
animations: CRUD_TABLE_ANIMATIONS})
|
|
78
168
|
export class JCompleteCrudTableComponent implements OnInit {
|
|
169
|
+
readonly Icons = Icons;
|
|
79
170
|
|
|
80
171
|
Math = Math;
|
|
81
172
|
|
|
82
|
-
//
|
|
173
|
+
// =====================================================
|
|
174
|
+
// Loading
|
|
175
|
+
// =====================================================
|
|
176
|
+
|
|
83
177
|
@Output() dataLoaded = new EventEmitter<void>();
|
|
178
|
+
|
|
84
179
|
loadingStates: LoadingStates = {
|
|
85
180
|
initialLoad: 'idle',
|
|
86
181
|
search: 'idle',
|
|
@@ -89,131 +184,173 @@ export class JCompleteCrudTableComponent implements OnInit {
|
|
|
89
184
|
sort: 'idle',
|
|
90
185
|
aditionalButtons: {},
|
|
91
186
|
checked: 'idle',
|
|
92
|
-
action: 'idle'
|
|
93
|
-
};
|
|
187
|
+
action: 'idle'};
|
|
94
188
|
|
|
95
|
-
|
|
189
|
+
// =====================================================
|
|
190
|
+
// Data / endpoint
|
|
191
|
+
// =====================================================
|
|
192
|
+
|
|
193
|
+
@Input() urlBase = '';
|
|
96
194
|
@Input() endpoint!: string;
|
|
97
195
|
mainEndpoint!: string;
|
|
196
|
+
|
|
197
|
+
@Input() staticData: any[] = [];
|
|
98
198
|
@Input() columns: TableColumn<any>[] = [];
|
|
99
|
-
|
|
100
|
-
|
|
199
|
+
|
|
200
|
+
data: any[] = [];
|
|
201
|
+
|
|
202
|
+
@Output() loadedData = new EventEmitter<any[]>();
|
|
203
|
+
@Output() loadedParams = new EventEmitter<any>();
|
|
204
|
+
|
|
205
|
+
displayData: any[] = [];
|
|
206
|
+
metadata: any = {};
|
|
207
|
+
|
|
208
|
+
@Input() defaultFilters: Record<string, unknown> = {};
|
|
209
|
+
|
|
210
|
+
// =====================================================
|
|
211
|
+
// Table chrome
|
|
212
|
+
// =====================================================
|
|
213
|
+
|
|
214
|
+
@Input() isNumbering = true;
|
|
101
215
|
@Input() isPaginator = true;
|
|
102
216
|
@Input() isFilter = true;
|
|
103
217
|
@Input() isbackground = false;
|
|
104
218
|
@Input() isSearch = true;
|
|
105
219
|
@Input() isEyeColumn = true;
|
|
106
|
-
@Input() hideRowCondition?: (row: any) => boolean
|
|
107
|
-
|
|
108
|
-
data: any[] = [];
|
|
220
|
+
@Input() hideRowCondition?: (row: any) => boolean;
|
|
109
221
|
|
|
110
|
-
|
|
111
|
-
|
|
222
|
+
// =====================================================
|
|
223
|
+
// Options column
|
|
224
|
+
// =====================================================
|
|
112
225
|
|
|
113
|
-
// Type options
|
|
114
226
|
@Input() optionsType: 'button' | 'dropdown' = 'button';
|
|
115
|
-
@Input() isOptions
|
|
227
|
+
@Input() isOptions = true;
|
|
228
|
+
|
|
229
|
+
// =====================================================
|
|
230
|
+
// Expand rows
|
|
231
|
+
// =====================================================
|
|
116
232
|
|
|
117
|
-
// Expansion
|
|
118
233
|
expandTemplate?: (row: any) => string;
|
|
119
|
-
expandedRows
|
|
234
|
+
expandedRows = new Set<any>();
|
|
120
235
|
|
|
236
|
+
// =====================================================
|
|
121
237
|
// Paginator
|
|
238
|
+
// =====================================================
|
|
239
|
+
|
|
122
240
|
currentPage = 1;
|
|
123
|
-
|
|
241
|
+
|
|
242
|
+
@Input() isItemsPerPage = true;
|
|
124
243
|
@Input() itemsPerPageOptions: number[] = [10, 25, 50, 100];
|
|
125
244
|
@Input() itemsPerPage = this.itemsPerPageOptions[0];
|
|
245
|
+
|
|
126
246
|
totalItems = 0;
|
|
247
|
+
pages: number[] = [];
|
|
248
|
+
|
|
249
|
+
get startIndex(): number {
|
|
250
|
+
return calculateStartIndex(this.currentPage, this.itemsPerPage);
|
|
251
|
+
}
|
|
127
252
|
|
|
253
|
+
get totalPages(): number {
|
|
254
|
+
return calculateTotalPages(this.totalItems, this.itemsPerPage);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
get params(): Params {
|
|
258
|
+
return this.getQueryParams();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// =====================================================
|
|
128
262
|
// Sorting
|
|
263
|
+
// =====================================================
|
|
264
|
+
|
|
129
265
|
sortColumn: string | null = null;
|
|
130
266
|
sortDirection: SortDirection = 'none';
|
|
131
267
|
sortingColumn: string | null = null;
|
|
132
268
|
|
|
133
|
-
// Search
|
|
134
|
-
searchQuery = '';
|
|
135
|
-
@Input() searchPlaceholder = 'Buscar...';
|
|
136
|
-
|
|
137
|
-
// Filters
|
|
138
|
-
filters: any = {};
|
|
139
|
-
|
|
140
269
|
@Input() initialSortColumn: string | null = null;
|
|
141
270
|
@Input() initialSortDirection: SortDirection = 'asc';
|
|
142
271
|
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
272
|
+
// =====================================================
|
|
273
|
+
// Search / filters
|
|
274
|
+
// =====================================================
|
|
146
275
|
|
|
147
|
-
|
|
148
|
-
|
|
276
|
+
searchQuery = '';
|
|
277
|
+
@Input() searchPlaceholder = 'Buscar...';
|
|
149
278
|
|
|
150
|
-
|
|
151
|
-
@Input() checked: boolean = false;
|
|
152
|
-
@Input() checkedColumn: string = 'id_status';
|
|
153
|
-
@Input() checkedValues: any[][] = [[true], [false]];
|
|
154
|
-
@Input() checkedTitles: string[] = ["Activos", "Inactivos"];
|
|
155
|
-
isChecked!: boolean;
|
|
156
|
-
titleChecked!: string;
|
|
279
|
+
filters: Record<string, unknown> = {};
|
|
157
280
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return (this.currentPage - 1) * this.itemsPerPage;
|
|
161
|
-
}
|
|
281
|
+
@Input() filtersButton: FilterButton[] = [];
|
|
282
|
+
@Input() filtersSelect: FilterSelect[] = [];
|
|
162
283
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
284
|
+
// =====================================================
|
|
285
|
+
// Active / inactive toggle
|
|
286
|
+
// =====================================================
|
|
166
287
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
288
|
+
@Input() checked = false;
|
|
289
|
+
@Input() checkedColumn = 'id_status';
|
|
290
|
+
@Input() checkedValues: any[][] = [[true], [false]];
|
|
291
|
+
@Input() checkedTitles = ['Activos', 'Inactivos'];
|
|
170
292
|
|
|
171
|
-
|
|
172
|
-
|
|
293
|
+
isChecked!: boolean;
|
|
294
|
+
titleChecked!: string;
|
|
173
295
|
|
|
174
|
-
//
|
|
175
|
-
|
|
296
|
+
// =====================================================
|
|
297
|
+
// Row options
|
|
298
|
+
// =====================================================
|
|
176
299
|
|
|
177
|
-
// Button options
|
|
178
300
|
@Input() optionsTable: OptionsTable[] = [];
|
|
301
|
+
selectOptions: Record<string, { key: unknown; label: string }[]> = {};
|
|
179
302
|
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
303
|
+
// =====================================================
|
|
304
|
+
// Group table
|
|
305
|
+
// =====================================================
|
|
306
|
+
|
|
307
|
+
@Input() isGroupTable = false;
|
|
308
|
+
@Input() groupTable: CrudTableGroupTableConfig = {};
|
|
309
|
+
@Output() loadDataGroup = new EventEmitter<{ data: any[]; metadata: any }>();
|
|
184
310
|
@Input() groupsTemplate!: TemplateRef<any>;
|
|
185
311
|
|
|
186
|
-
//
|
|
312
|
+
// =====================================================
|
|
313
|
+
// Image dialog
|
|
314
|
+
// =====================================================
|
|
315
|
+
|
|
187
316
|
keyDialog!: string;
|
|
188
317
|
mediaDialog!: SafeUrl | string;
|
|
189
318
|
titleDialog!: string;
|
|
190
319
|
|
|
191
|
-
//
|
|
320
|
+
// =====================================================
|
|
321
|
+
// Inline row editing
|
|
322
|
+
// =====================================================
|
|
323
|
+
|
|
192
324
|
editingRowItem: any = null;
|
|
193
325
|
originalRowItem: any = null;
|
|
194
|
-
|
|
195
|
-
@Input()
|
|
326
|
+
|
|
327
|
+
@Input() equivalenceConfigs: CrudTableEquivalenceConfigs = {};
|
|
328
|
+
@Input() equivalences: CrudTableEquivalenceMaps = {};
|
|
329
|
+
|
|
330
|
+
// =====================================================
|
|
331
|
+
// Mobile nested expand
|
|
332
|
+
// =====================================================
|
|
333
|
+
|
|
334
|
+
mobileInnerExpandedRows = new Set<any>();
|
|
196
335
|
|
|
197
336
|
constructor(
|
|
198
337
|
public readonly form: JFormShared,
|
|
199
338
|
public readonly dialog: JDialogShared,
|
|
200
339
|
public readonly transformService: JTransformService,
|
|
201
|
-
public readonly iconsService: JIconsService,
|
|
202
340
|
private readonly sanitizer: DomSanitizer,
|
|
203
341
|
private readonly genericService: JGenericCrudService,
|
|
204
342
|
private readonly alertToastService: JAlertToastService,
|
|
205
343
|
private readonly converterService: JConverterCrudService,
|
|
206
344
|
private readonly uploadFilterService: JUploadFilterService,
|
|
207
|
-
private cd: ChangeDetectorRef
|
|
208
|
-
) {
|
|
345
|
+
private readonly cd: ChangeDetectorRef,
|
|
346
|
+
) {}
|
|
209
347
|
|
|
210
|
-
ngOnInit() {
|
|
211
|
-
this.mainEndpoint = this.endpoint
|
|
348
|
+
ngOnInit(): void {
|
|
349
|
+
this.mainEndpoint = resolveMainEndpoint(this.endpoint);
|
|
212
350
|
this.isChecked = this.checkedValues[0][0];
|
|
213
351
|
this.titleChecked = this.checkedTitles[0];
|
|
214
352
|
this.columnDefaults();
|
|
215
353
|
|
|
216
|
-
// Apply initial sort if passed from parent
|
|
217
354
|
if (this.initialSortColumn) {
|
|
218
355
|
this.sortColumn = this.initialSortColumn;
|
|
219
356
|
this.sortDirection = this.initialSortDirection || 'asc';
|
|
@@ -221,296 +358,239 @@ export class JCompleteCrudTableComponent implements OnInit {
|
|
|
221
358
|
|
|
222
359
|
this.loadData();
|
|
223
360
|
this.overrideFilterEvents();
|
|
224
|
-
|
|
225
|
-
// Normalize buttons
|
|
226
361
|
this.optionsTable = this.normalizeOptionsTable(this.optionsTable);
|
|
227
362
|
}
|
|
228
363
|
|
|
229
|
-
|
|
230
|
-
overrideFilterEvents() {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const originalOnSelected = filter.onSelected;
|
|
237
|
-
|
|
238
|
-
filter.onSelected = (value: any) => {
|
|
239
|
-
const selectedValue = value?.[key] ?? value;
|
|
240
|
-
|
|
241
|
-
if (selectedValue === null || selectedValue === undefined) {
|
|
242
|
-
delete this.filters[deepKey];
|
|
243
|
-
} else {
|
|
244
|
-
this.filters[deepKey] = selectedValue;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (typeof originalOnSelected === 'function') {
|
|
248
|
-
originalOnSelected(value);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
this.currentPage = 1;
|
|
252
|
-
this.loadData('search');
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
}
|
|
364
|
+
/** Wraps filter dropdown handlers to sync `this.filters` and reload. */
|
|
365
|
+
overrideFilterEvents(): void {
|
|
366
|
+
bindFilterSelectHandlers(this.filtersSelect, this.filters, () => {
|
|
367
|
+
this.currentPage = 1;
|
|
368
|
+
this.loadData('search');
|
|
369
|
+
});
|
|
256
370
|
}
|
|
257
371
|
|
|
258
|
-
|
|
259
|
-
// After file upload
|
|
372
|
+
/** Reload after file upload from filter actions. */
|
|
260
373
|
onAfterUpload(): void {
|
|
261
374
|
this.loadData('initialLoad');
|
|
262
375
|
}
|
|
263
376
|
|
|
264
377
|
// =====================================================
|
|
265
|
-
//
|
|
378
|
+
// Data loading
|
|
266
379
|
// =====================================================
|
|
267
380
|
|
|
268
|
-
|
|
269
|
-
|
|
381
|
+
/**
|
|
382
|
+
* Loads rows from API or `staticData`, preserving expanded row state when possible.
|
|
383
|
+
* @param loadingType Which loading indicator to show.
|
|
384
|
+
* @param onFinally Optional callback after the request completes.
|
|
385
|
+
*/
|
|
386
|
+
loadData(loadingType: keyof LoadingStates = 'initialLoad', onFinally?: () => void): void {
|
|
270
387
|
this.setLoadingState(loadingType, 'loading');
|
|
271
388
|
|
|
272
389
|
const params = this.getQueryParams();
|
|
390
|
+
const expandedKeys = cacheExpandedRowKeys(this.expandedRows, this.isGroupTable);
|
|
273
391
|
|
|
274
|
-
// Store references to expanded rows before updating
|
|
275
|
-
const expandedItemsCache = new Map();
|
|
276
|
-
this.expandedRows.forEach(item => {
|
|
277
|
-
// Use a unique identifier for each row (title + description for groups)
|
|
278
|
-
const key = this.isGroupTable ?
|
|
279
|
-
`${item.title}-${item.description}` :
|
|
280
|
-
(item.id || JSON.stringify(item));
|
|
281
|
-
expandedItemsCache.set(key, true);
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
// Simulating API wait
|
|
285
|
-
// setTimeout(() => {
|
|
286
|
-
|
|
287
|
-
// Emit search parameters
|
|
288
392
|
this.loadedParams.emit(params);
|
|
289
393
|
|
|
290
|
-
|
|
291
|
-
|
|
394
|
+
if (this.staticData.length > 0) {
|
|
395
|
+
this.data = this.staticData.map((item) => ({ ...item }));
|
|
396
|
+
this.totalItems = this.data.length;
|
|
397
|
+
this.loadedData.emit(this.data);
|
|
398
|
+
this.updateDisplayData();
|
|
399
|
+
this.generatePagination();
|
|
400
|
+
this.setLoadingState(loadingType, 'success');
|
|
401
|
+
this.dataLoaded.emit();
|
|
292
402
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
(item.id || JSON.stringify(item));
|
|
302
|
-
if (expandedItemsCache.has(key)) {
|
|
303
|
-
this.expandedRows.add(item);
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
}
|
|
403
|
+
if (loadingType === 'sort') {
|
|
404
|
+
this.sortingColumn = null;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
onFinally?.();
|
|
408
|
+
this.cd.detectChanges();
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
307
411
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
412
|
+
this.genericService
|
|
413
|
+
.findAll<any>({ urlBase: this.urlBase, endpoint: this.endpoint, params })
|
|
414
|
+
.subscribe({
|
|
415
|
+
next: (response) => {
|
|
416
|
+
this.data = response.data[this.mainEndpoint] ?? [];
|
|
417
|
+
|
|
418
|
+
if (expandedKeys.size > 0) {
|
|
419
|
+
this.expandedRows = restoreExpandedRowsAfterLoad(
|
|
420
|
+
this.data,
|
|
421
|
+
expandedKeys,
|
|
422
|
+
this.isGroupTable,
|
|
423
|
+
);
|
|
311
424
|
}
|
|
312
|
-
});
|
|
313
425
|
|
|
314
|
-
|
|
426
|
+
normalizeGroupMetaTitles(this.data);
|
|
427
|
+
this.loadedData.emit(this.data);
|
|
315
428
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
});
|
|
429
|
+
this.loadDataGroup.emit({
|
|
430
|
+
data: this.data,
|
|
431
|
+
metadata: response.meta});
|
|
320
432
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
433
|
+
if (response.meta?.page) {
|
|
434
|
+
this.totalItems = response.meta.page.totalRecords;
|
|
435
|
+
this.currentPage = response.meta.page.currentPage;
|
|
436
|
+
} else {
|
|
437
|
+
this.totalItems = this.data.length;
|
|
438
|
+
}
|
|
327
439
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
440
|
+
if (response.meta?.sort) {
|
|
441
|
+
this.sortColumn = response.meta.sort.by;
|
|
442
|
+
this.sortDirection = response.meta.sort.order.toLowerCase() as SortDirection;
|
|
443
|
+
}
|
|
332
444
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
445
|
+
this.updateDisplayData();
|
|
446
|
+
this.generatePagination();
|
|
447
|
+
this.setLoadingState(loadingType, 'success');
|
|
448
|
+
this.cd.detectChanges();
|
|
449
|
+
},
|
|
450
|
+
error: (error) => {
|
|
451
|
+
console.error('Error fetching data:', error);
|
|
452
|
+
this.setLoadingState(loadingType, 'error');
|
|
453
|
+
}})
|
|
454
|
+
.add(() => {
|
|
455
|
+
this.dataLoaded.emit();
|
|
456
|
+
|
|
457
|
+
if (loadingType === 'sort') {
|
|
458
|
+
this.sortingColumn = null;
|
|
459
|
+
}
|
|
347
460
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
351
|
-
});
|
|
352
|
-
// }, 2000);
|
|
461
|
+
onFinally?.();
|
|
462
|
+
});
|
|
353
463
|
}
|
|
354
464
|
|
|
355
|
-
|
|
356
|
-
updateDisplayData() {
|
|
465
|
+
/** Copies `data` into `displayData` (hook point for future client-side transforms). */
|
|
466
|
+
updateDisplayData(): void {
|
|
357
467
|
this.displayData = this.data;
|
|
358
468
|
}
|
|
359
469
|
|
|
360
470
|
// =====================================================
|
|
361
|
-
// Table options
|
|
471
|
+
// Table options normalization
|
|
362
472
|
// =====================================================
|
|
363
473
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
// Normalize table options
|
|
474
|
+
/** Enriches `editRow` options with equivalence maps and dynamic icons. */
|
|
367
475
|
normalizeOptionsTable(buttons: OptionsTable[]): OptionsTable[] {
|
|
368
476
|
return buttons.map((button) => {
|
|
477
|
+
if (button.type !== 'editRow') {
|
|
478
|
+
return button;
|
|
479
|
+
}
|
|
369
480
|
|
|
370
|
-
|
|
371
|
-
if (button.type === 'editRow') {
|
|
372
|
-
|
|
373
|
-
this.equivalenceConfigs = button?.searchListUpload ?? {};
|
|
374
|
-
|
|
375
|
-
// Process selected IDs
|
|
376
|
-
// In the normalizeOptionsTable method, replace this part:
|
|
377
|
-
forkJoin(this.uploadFilterService.getIdsSelectedData(button)).subscribe({
|
|
378
|
-
next: (results) => {
|
|
379
|
-
// Initialize the equivalences object if it doesn't exist
|
|
380
|
-
if (!this.equivalences) this.equivalences = {};
|
|
481
|
+
this.equivalenceConfigs = button?.searchListUpload ?? {};
|
|
381
482
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
483
|
+
forkJoin(this.uploadFilterService.getIdsSelectedData(button)).subscribe({
|
|
484
|
+
next: (results) => {
|
|
485
|
+
if (!this.equivalences) {
|
|
486
|
+
this.equivalences = {};
|
|
487
|
+
}
|
|
387
488
|
|
|
388
|
-
|
|
489
|
+
results.forEach((result) => {
|
|
490
|
+
const { data, mappingInfo } = result;
|
|
491
|
+
const keyReturn = mappingInfo.keyReturn;
|
|
492
|
+
const keyAlternate = mappingInfo.keyAlternate;
|
|
493
|
+
const keyColumnSearch = mappingInfo.keyColumnSearch;
|
|
389
494
|
|
|
390
|
-
|
|
391
|
-
|
|
495
|
+
if (!keyReturn || !keyColumnSearch) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
392
498
|
|
|
499
|
+
if (!this.equivalences[keyReturn]) {
|
|
500
|
+
this.equivalences[keyReturn] = {};
|
|
501
|
+
}
|
|
393
502
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
503
|
+
data.forEach((item: any) => {
|
|
504
|
+
const idKey = keyAlternate ?? keyReturn;
|
|
505
|
+
const searchKey = keyColumnSearch.includes('.')
|
|
506
|
+
? keyColumnSearch.split('.').pop()!
|
|
507
|
+
: keyColumnSearch;
|
|
397
508
|
|
|
398
|
-
|
|
399
|
-
});
|
|
509
|
+
this.equivalences[keyReturn][item[idKey]] = item[searchKey];
|
|
400
510
|
});
|
|
401
|
-
|
|
511
|
+
});
|
|
512
|
+
}});
|
|
513
|
+
|
|
514
|
+
return {
|
|
515
|
+
...button,
|
|
516
|
+
icon: (data: any) =>
|
|
517
|
+
this.editingRowItem === data
|
|
518
|
+
? Icons.Pencil
|
|
519
|
+
: Icons.PencilLine,
|
|
520
|
+
tooltip: (data: any) =>
|
|
521
|
+
this.optionsType === 'button'
|
|
522
|
+
? this.editingRowItem === data
|
|
523
|
+
? 'Guardar edición'
|
|
524
|
+
: 'Editar en tabla'
|
|
525
|
+
: '',
|
|
526
|
+
text: (data: any) =>
|
|
527
|
+
this.optionsType === 'dropdown'
|
|
528
|
+
? this.editingRowItem === data
|
|
529
|
+
? 'Guardar edición'
|
|
530
|
+
: 'Editar en tabla'
|
|
531
|
+
: '',
|
|
532
|
+
clicked: (data) => {
|
|
533
|
+
if (
|
|
534
|
+
this.editingRowItem &&
|
|
535
|
+
this.editingRowItem[`id_${this.mainEndpoint}`] === data[`id_${this.mainEndpoint}`]
|
|
536
|
+
) {
|
|
537
|
+
this.onSaveRow(data);
|
|
538
|
+
} else {
|
|
539
|
+
this.onEditRow(data);
|
|
402
540
|
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
...button,
|
|
407
|
-
icon: (data: any) => this.editingRowItem === data ? this.iconsService.icons.editRow : this.iconsService.icons.editRowLine,
|
|
408
|
-
tooltip: (data: any) => this.optionsType === 'button' ? (this.editingRowItem === data ? 'Guardar edición' : 'Editar en tabla') : '',
|
|
409
|
-
text: (data: any) => this.optionsType === 'dropdown' ? (this.editingRowItem === data ? 'Guardar edición' : 'Editar en tabla') : '',
|
|
410
|
-
clicked: (data) => {
|
|
411
|
-
if (this.editingRowItem && this.editingRowItem[`id_${this.mainEndpoint}`] === data[`id_${this.mainEndpoint}`]) {
|
|
412
|
-
this.onSaveRow(data);
|
|
413
|
-
} else {
|
|
414
|
-
this.onEditRow(data);
|
|
415
|
-
}
|
|
416
|
-
},
|
|
417
|
-
ngClass: (data: any) => this.editingRowItem === data ? 'success_secondary' : 'warning_secondary',
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
return button;
|
|
541
|
+
},
|
|
542
|
+
ngClass: (data: any) =>
|
|
543
|
+
this.editingRowItem === data ? 'success_secondary' : 'warning_secondary'};
|
|
422
544
|
});
|
|
423
545
|
}
|
|
424
546
|
|
|
425
547
|
// =====================================================
|
|
426
|
-
//
|
|
548
|
+
// Boolean checkbox column
|
|
427
549
|
// =====================================================
|
|
428
|
-
onCheckboxChange(item: any, column: TableColumn<any>) {
|
|
429
|
-
|
|
430
|
-
let key = column.key;
|
|
431
|
-
let recordId: any;
|
|
432
|
-
let endpoint = this.mainEndpoint;
|
|
433
|
-
|
|
434
|
-
// Verificar si es key compuesta
|
|
435
|
-
if (key.includes('.')) {
|
|
436
|
-
const parts = key.split('.');
|
|
437
|
-
const parent = parts[0];
|
|
438
|
-
const lastKey = parts[parts.length - 1];
|
|
439
|
-
|
|
440
|
-
key = lastKey;
|
|
441
|
-
|
|
442
|
-
// El id corresponde a la tabla padre
|
|
443
|
-
recordId = item[`id_${parent}`];
|
|
444
|
-
|
|
445
|
-
// El endpoint también debe ser la tabla padre
|
|
446
|
-
endpoint = parent;
|
|
447
|
-
} else {
|
|
448
|
-
// key simple
|
|
449
|
-
recordId = item[`id_${this.mainEndpoint}`];
|
|
450
|
-
}
|
|
451
550
|
|
|
452
|
-
|
|
551
|
+
/** Toggles a boolean field via API (supports nested composite keys). */
|
|
552
|
+
onCheckboxChange(item: any, column: TableColumn<any>): void {
|
|
553
|
+
const { key, recordId, endpoint } = resolveCheckboxToggleTarget(
|
|
554
|
+
item,
|
|
555
|
+
column,
|
|
556
|
+
this.mainEndpoint,
|
|
557
|
+
);
|
|
453
558
|
const currentValue = this.getValue(item, column);
|
|
454
559
|
|
|
455
560
|
const formData: EnableBoolean<any> = {
|
|
456
|
-
key
|
|
561
|
+
key,
|
|
457
562
|
value: currentValue,
|
|
458
|
-
values: [currentValue, !currentValue]
|
|
459
|
-
};
|
|
563
|
+
values: [currentValue, !currentValue]};
|
|
460
564
|
|
|
461
|
-
// Llamar al servicio
|
|
462
565
|
this.genericService.toggle<any>({ endpoint, id: recordId, data: formData }).subscribe({
|
|
463
566
|
next: (response) => {
|
|
464
|
-
|
|
465
|
-
if (column.key.includes('.')) {
|
|
466
|
-
const parts = column.key.split('.');
|
|
467
|
-
const parent = parts[0];
|
|
468
|
-
const lastKey = parts[parts.length - 1];
|
|
469
|
-
|
|
470
|
-
// Actualizar objeto anidado
|
|
471
|
-
item[parent] = { ...item[parent], [lastKey]: !currentValue };
|
|
472
|
-
} else {
|
|
473
|
-
item[key] = !currentValue;
|
|
474
|
-
}
|
|
567
|
+
applyNestedBooleanToggle(item, column.key, currentValue as boolean);
|
|
475
568
|
|
|
476
569
|
this.alertToastService.AlertToast({
|
|
477
|
-
type:
|
|
478
|
-
title:
|
|
479
|
-
description: response.message
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
});
|
|
570
|
+
type: 'success',
|
|
571
|
+
title: 'Registro actualizado!',
|
|
572
|
+
description: response.message});
|
|
573
|
+
}});
|
|
483
574
|
}
|
|
484
575
|
|
|
485
|
-
|
|
576
|
+
/** Switches active/inactive filter without clearing other filters. */
|
|
486
577
|
checkActiveInactive(isChecked: boolean): void {
|
|
487
578
|
this.isChecked = !isChecked;
|
|
488
579
|
|
|
489
580
|
const index = this.isChecked ? 0 : 1;
|
|
490
581
|
this.titleChecked = this.checkedTitles[index];
|
|
491
|
-
|
|
492
|
-
// ONLY update the id_status property without touching the other filters
|
|
493
582
|
this.filters[this.checkedColumn] = this.checkedValues[index];
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
defaultFilters: {
|
|
500
|
-
...(filter.hasOwnProperty('defaultFilters') ? (filter as any).defaultFilters : {}),
|
|
501
|
-
[this.checkedColumn]: this.filters[this.checkedColumn]
|
|
502
|
-
},
|
|
503
|
-
selected: null
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
return filter;
|
|
507
|
-
});
|
|
583
|
+
this.filtersSelect = syncCheckedFilterSelects(
|
|
584
|
+
this.filtersSelect,
|
|
585
|
+
this.checkedColumn,
|
|
586
|
+
this.filters[this.checkedColumn],
|
|
587
|
+
);
|
|
508
588
|
|
|
509
589
|
this.currentPage = 1;
|
|
510
590
|
this.loadData('checked');
|
|
511
591
|
}
|
|
512
592
|
|
|
513
|
-
|
|
593
|
+
/** Clears filters and reloads (used by filter bar clear action). */
|
|
514
594
|
onClearFilters(buttonType: string): void {
|
|
515
595
|
this.setAditionalButtonLoading(buttonType);
|
|
516
596
|
|
|
@@ -523,60 +603,24 @@ export class JCompleteCrudTableComponent implements OnInit {
|
|
|
523
603
|
// Columns
|
|
524
604
|
// =====================================================
|
|
525
605
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
// Default values for columns
|
|
529
|
-
this.columns.forEach(column => {
|
|
530
|
-
if (column.visible === undefined) {
|
|
531
|
-
column.visible = true;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
if (column.sortable === undefined) {
|
|
535
|
-
column.sortable = true;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
if (column.isSearchable === undefined) {
|
|
539
|
-
column.isSearchable = true;
|
|
540
|
-
}
|
|
541
|
-
});
|
|
606
|
+
columnDefaults(): void {
|
|
607
|
+
applyColumnDefaults(this.columns);
|
|
542
608
|
}
|
|
543
609
|
|
|
544
|
-
// Get the count of visible columns for colspan in empty state
|
|
545
610
|
getVisibleColumnsCount(): number {
|
|
546
|
-
return this.columns
|
|
611
|
+
return countVisibleColumns(this.columns);
|
|
547
612
|
}
|
|
548
613
|
|
|
549
|
-
/** En tablas `table-layout: fixed`, el ancho debe ir en `<col>`; minWidth solo en `<td>` no aplica. */
|
|
550
614
|
getColumnColStyles(column: TableColumn<any>): Record<string, string> | undefined {
|
|
551
|
-
|
|
552
|
-
return undefined;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
const styles = { ...column.styles };
|
|
556
|
-
|
|
557
|
-
if (styles['minWidth'] && !styles['width']) {
|
|
558
|
-
styles['width'] = styles['minWidth'];
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
return styles;
|
|
615
|
+
return getColumnColStyles(column);
|
|
562
616
|
}
|
|
563
617
|
|
|
564
618
|
getGroupExpandColspan(): number {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
if (this.isNumbering) {
|
|
568
|
-
colspan += 1;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
if (this.isOptions) {
|
|
572
|
-
colspan += 1;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
return colspan;
|
|
619
|
+
return getGroupExpandColspan(this.columns, this.isNumbering, this.isOptions);
|
|
576
620
|
}
|
|
577
621
|
|
|
578
622
|
getGroupCollapsedHeaderColspan(): number {
|
|
579
|
-
return
|
|
623
|
+
return getGroupCollapsedHeaderColspan(this.columns, this.isOptions);
|
|
580
624
|
}
|
|
581
625
|
|
|
582
626
|
getGroupExpansionState(item: any): 'expanded' | 'collapsed' {
|
|
@@ -584,20 +628,19 @@ export class JCompleteCrudTableComponent implements OnInit {
|
|
|
584
628
|
}
|
|
585
629
|
|
|
586
630
|
trackGroupRow(group: any, index: number): string | number {
|
|
587
|
-
return group
|
|
631
|
+
return trackGroupRowKey(group, index);
|
|
588
632
|
}
|
|
589
633
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
634
|
+
handleCellClick(event: MouseEvent, row: any, column: TableColumn<any>): void {
|
|
635
|
+
if (!column.onCellClick) {
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
594
638
|
|
|
595
|
-
|
|
596
|
-
|
|
639
|
+
if (column.isCellClickable && !column.isCellClickable(row)) {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
597
642
|
|
|
598
|
-
|
|
599
|
-
const shouldStop = column.stopRowClickOnCellClick !== false; // default true
|
|
600
|
-
if (shouldStop) {
|
|
643
|
+
if (shouldStopCellClickPropagation(column)) {
|
|
601
644
|
event.stopPropagation();
|
|
602
645
|
}
|
|
603
646
|
|
|
@@ -606,136 +649,71 @@ export class JCompleteCrudTableComponent implements OnInit {
|
|
|
606
649
|
}
|
|
607
650
|
|
|
608
651
|
// =====================================================
|
|
609
|
-
//
|
|
652
|
+
// Cell values
|
|
610
653
|
// =====================================================
|
|
611
654
|
|
|
612
|
-
// Get the cell value to identify a boolean field
|
|
613
655
|
isBoolean(value: any): boolean {
|
|
614
|
-
return
|
|
656
|
+
return isBooleanValue(value);
|
|
615
657
|
}
|
|
616
658
|
|
|
617
|
-
// Get the cell value to identify a date field
|
|
618
659
|
isDate(value: any): boolean {
|
|
619
|
-
return
|
|
660
|
+
return isDateValue(value);
|
|
620
661
|
}
|
|
621
662
|
|
|
622
|
-
// MMethod to get the cell value dynamically
|
|
623
663
|
getValue(item: any, column: TableColumn<any>, row = false): any {
|
|
624
|
-
let value: any;
|
|
625
|
-
|
|
626
|
-
// If a valueGetter exists, use it directly
|
|
627
|
-
if (typeof column.valueGetter === 'function') {
|
|
628
|
-
value = column.valueGetter(item);
|
|
629
|
-
} else {
|
|
630
|
-
// If not, look for the value by key (with support for nested keys)
|
|
631
|
-
const keys = column.key.split('.');
|
|
632
|
-
value = item;
|
|
633
|
-
|
|
634
|
-
for (const key of keys) {
|
|
635
|
-
if (value != null) {
|
|
636
|
-
value = value[key];
|
|
637
|
-
} else {
|
|
638
|
-
value = null;
|
|
639
|
-
break;
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
644
664
|
if (row) {
|
|
645
|
-
|
|
646
|
-
return this.converterService.parseData(value, column);
|
|
647
|
-
} else {
|
|
648
|
-
|
|
649
|
-
// If value is null or undefined, return 'S/N'
|
|
650
|
-
if (value === null || value === undefined) {
|
|
651
|
-
return 'S/N';
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// If formatData returns nothing (unformatted value), return the original value
|
|
655
|
-
return this.converterService.formatData(value, column) ?? value;
|
|
665
|
+
return parseCellEditValue(item, column, this.converterService);
|
|
656
666
|
}
|
|
667
|
+
|
|
668
|
+
return formatCellDisplayValue(item, column, this.converterService);
|
|
657
669
|
}
|
|
658
670
|
|
|
659
671
|
// =====================================================
|
|
660
|
-
//
|
|
672
|
+
// Query params
|
|
661
673
|
// =====================================================
|
|
662
674
|
|
|
663
|
-
// Get the query parameters for the data request
|
|
664
675
|
getQueryParams(): Params {
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
direction: this.sortDirection,
|
|
671
|
-
},
|
|
676
|
+
return buildCrudTableQueryParams(this.genericService, {
|
|
677
|
+
currentPage: this.currentPage,
|
|
678
|
+
itemsPerPage: this.itemsPerPage,
|
|
679
|
+
sortColumn: this.sortColumn,
|
|
680
|
+
sortDirection: this.sortDirection,
|
|
672
681
|
filters: this.filters,
|
|
673
682
|
defaultFilters: this.defaultFilters,
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
const extraSearchKeys = this.columns.flatMap(col => col.extraSearchFields || []);
|
|
679
|
-
const allSearchKeys = [...baseSearchKeys, ...extraSearchKeys];
|
|
680
|
-
params['search'] = this.searchQuery;
|
|
681
|
-
params['searchFields'] = allSearchKeys;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
// Add group pagination if defined
|
|
685
|
-
if (this.isGroupTable && typeof this.groupTable['getGroupParams'] === 'function') {
|
|
686
|
-
Object.assign(params, this.groupTable['getGroupParams']());
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
return params;
|
|
683
|
+
searchQuery: this.searchQuery,
|
|
684
|
+
columns: this.columns,
|
|
685
|
+
isGroupTable: this.isGroupTable,
|
|
686
|
+
groupTable: this.groupTable});
|
|
690
687
|
}
|
|
691
688
|
|
|
692
689
|
// =====================================================
|
|
693
690
|
// Sorting
|
|
694
691
|
// =====================================================
|
|
695
692
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
// Prevent multiple simultaneous sort requests
|
|
699
|
-
if (this.isLoading('sort')) {
|
|
693
|
+
onSort(column: TableColumn<any>): void {
|
|
694
|
+
if (this.isLoading('sort') || !column.sortable) {
|
|
700
695
|
return;
|
|
701
696
|
}
|
|
702
697
|
|
|
703
|
-
if (!column.sortable) return;
|
|
704
|
-
|
|
705
|
-
// Save the column being sorted
|
|
706
698
|
this.sortingColumn = column.key;
|
|
707
699
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
// Toggle sort direction
|
|
715
|
-
if (this.sortDirection === 'asc') {
|
|
716
|
-
this.sortDirection = 'desc';
|
|
717
|
-
} else if (this.sortDirection === 'desc') {
|
|
718
|
-
this.sortDirection = 'none';
|
|
719
|
-
} else {
|
|
720
|
-
this.sortDirection = 'asc';
|
|
721
|
-
}
|
|
722
|
-
} else {
|
|
723
|
-
// If a new column is selected for sorting, start with ascending order
|
|
724
|
-
this.sortColumn = column.key;
|
|
725
|
-
this.sortDirection = 'asc';
|
|
726
|
-
}
|
|
700
|
+
const next = resolveNextSortState(
|
|
701
|
+
column,
|
|
702
|
+
this.sortColumn,
|
|
703
|
+
this.sortDirection,
|
|
704
|
+
(value) => this.converterService.getSortKey(value),
|
|
705
|
+
);
|
|
727
706
|
|
|
707
|
+
this.sortColumn = next.sortColumn;
|
|
708
|
+
this.sortDirection = next.sortDirection;
|
|
728
709
|
this.loadData('sort');
|
|
729
710
|
}
|
|
730
711
|
|
|
731
|
-
// Get the sort key
|
|
732
712
|
getSortKey(value: any): string {
|
|
733
713
|
return this.converterService.getSortKey(value);
|
|
734
714
|
}
|
|
735
715
|
|
|
736
|
-
|
|
737
|
-
onSortKeyPress(event: KeyboardEvent, column: TableColumn<any>) {
|
|
738
|
-
// If a sort is already in progress, do not allow another
|
|
716
|
+
onSortKeyPress(event: KeyboardEvent, column: TableColumn<any>): void {
|
|
739
717
|
if (this.isLoading('sort')) {
|
|
740
718
|
return;
|
|
741
719
|
}
|
|
@@ -747,146 +725,97 @@ export class JCompleteCrudTableComponent implements OnInit {
|
|
|
747
725
|
}
|
|
748
726
|
|
|
749
727
|
// =====================================================
|
|
750
|
-
// Search
|
|
728
|
+
// Search / pagination
|
|
751
729
|
// =====================================================
|
|
752
730
|
|
|
753
|
-
|
|
754
|
-
onSearch() {
|
|
731
|
+
onSearch(): void {
|
|
755
732
|
this.currentPage = 1;
|
|
756
733
|
this.loadData('search');
|
|
757
734
|
}
|
|
758
735
|
|
|
759
|
-
|
|
760
|
-
onItemsPerPageChange() {
|
|
736
|
+
onItemsPerPageChange(): void {
|
|
761
737
|
this.currentPage = 1;
|
|
762
738
|
this.loadData('itemsPerPage');
|
|
763
739
|
}
|
|
764
740
|
|
|
765
|
-
|
|
766
|
-
// Paginator
|
|
767
|
-
// =====================================================
|
|
768
|
-
|
|
769
|
-
// Generate pagination numbers
|
|
770
|
-
generatePagination() {
|
|
741
|
+
generatePagination(): void {
|
|
771
742
|
const totalPages = this.totalPages;
|
|
772
743
|
const currentPage = this.currentPage;
|
|
773
|
-
|
|
774
|
-
// Show 3 pagination numbers
|
|
775
744
|
const maxPagesToShow = 3;
|
|
776
745
|
let startPage = Math.max(1, currentPage - Math.floor(maxPagesToShow / 2));
|
|
777
746
|
let endPage = Math.min(totalPages, startPage + maxPagesToShow - 1);
|
|
778
747
|
|
|
779
|
-
// Adjust if we're near the end
|
|
780
748
|
if (endPage - startPage + 1 < maxPagesToShow) {
|
|
781
749
|
startPage = Math.max(1, endPage - maxPagesToShow + 1);
|
|
782
750
|
}
|
|
783
751
|
|
|
784
|
-
this.pages = Array.from({ length: endPage - startPage + 1 }, (_,
|
|
752
|
+
this.pages = Array.from({ length: endPage - startPage + 1 }, (_, index) => startPage + index);
|
|
785
753
|
}
|
|
786
754
|
|
|
787
|
-
|
|
788
|
-
handlePageChange(page: number) {
|
|
755
|
+
handlePageChange(page: number): void {
|
|
789
756
|
this.currentPage = page;
|
|
790
757
|
this.loadData('pagination');
|
|
791
758
|
}
|
|
792
759
|
|
|
793
760
|
// =====================================================
|
|
794
|
-
// Image
|
|
761
|
+
// Image dialog
|
|
795
762
|
// =====================================================
|
|
796
763
|
|
|
797
|
-
|
|
798
|
-
showDialogImg(id: string, name: string, url: string) {
|
|
764
|
+
showDialogImg(id: string, name: string, url: string): void {
|
|
799
765
|
this.keyDialog = id;
|
|
800
766
|
this.titleDialog = name;
|
|
801
|
-
|
|
802
|
-
const finalUrl = url.trim();
|
|
803
|
-
|
|
804
|
-
this.mediaDialog = this.sanitizer.bypassSecurityTrustUrl(finalUrl);
|
|
767
|
+
this.mediaDialog = this.sanitizer.bypassSecurityTrustUrl(url.trim());
|
|
805
768
|
}
|
|
806
769
|
|
|
807
770
|
// =====================================================
|
|
808
|
-
//
|
|
771
|
+
// Row options
|
|
809
772
|
// =====================================================
|
|
810
773
|
|
|
811
|
-
// MMethod to handle button click
|
|
812
774
|
onButtonClick(button: OptionsTable, element: any): void {
|
|
813
|
-
|
|
814
|
-
if (button.clicked) {
|
|
815
|
-
button.clicked(element);
|
|
816
|
-
}
|
|
775
|
+
button.clicked?.(element);
|
|
817
776
|
}
|
|
818
777
|
|
|
819
|
-
// MMethod to get a tooltip
|
|
820
778
|
getTooltip(tooltip: string | ((data?: any) => string), data: any): string {
|
|
821
|
-
|
|
822
|
-
return tooltip(data);
|
|
823
|
-
}
|
|
824
|
-
return tooltip ?? '';
|
|
779
|
+
return resolveOptionTooltip(tooltip, data);
|
|
825
780
|
}
|
|
826
781
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
if (typeof icon === 'function') {
|
|
830
|
-
return icon(data);
|
|
831
|
-
}
|
|
832
|
-
return icon;
|
|
782
|
+
getIcon(icon: ((data?: any) => any) | undefined, data: any): any {
|
|
783
|
+
return resolveOptionIcon(icon, data);
|
|
833
784
|
}
|
|
834
785
|
|
|
835
|
-
// Evaluate if a button is disabled
|
|
836
786
|
getDisabled(option: OptionsTable, data: any): boolean {
|
|
837
|
-
|
|
838
|
-
return option.disabled(data);
|
|
839
|
-
}
|
|
840
|
-
return !!option.disabled;
|
|
787
|
+
return resolveOptionDisabled(option, data);
|
|
841
788
|
}
|
|
842
789
|
|
|
843
|
-
// Evaluate if a button is visible
|
|
844
790
|
getIsVisible(option: OptionsTable, data: any): boolean {
|
|
845
|
-
|
|
846
|
-
return option.isVisible(data);
|
|
847
|
-
}
|
|
848
|
-
// If not defined, default is visible
|
|
849
|
-
return option.isVisible !== false;
|
|
791
|
+
return resolveOptionVisible(option, data);
|
|
850
792
|
}
|
|
851
793
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
const baseClass = {
|
|
855
|
-
'min-w-auto p-[1px]! pl-[5px]! pr-[5px]!': true
|
|
856
|
-
};
|
|
857
|
-
|
|
858
|
-
let dynamicClass: any = {};
|
|
859
|
-
if (typeof optionNgClass === 'function') {
|
|
860
|
-
dynamicClass = optionNgClass(data);
|
|
861
|
-
} else {
|
|
862
|
-
dynamicClass = optionNgClass ?? {};
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
return {
|
|
866
|
-
...baseClass,
|
|
867
|
-
...(typeof dynamicClass === 'string' ? { [dynamicClass]: true } : dynamicClass)
|
|
868
|
-
};
|
|
794
|
+
mergeNgClasses(optionNgClass: ((data?: any) => any) | undefined, data: any): any {
|
|
795
|
+
return mergeOptionNgClasses(optionNgClass, data);
|
|
869
796
|
}
|
|
870
797
|
|
|
871
798
|
// =====================================================
|
|
872
|
-
//
|
|
799
|
+
// Loading state
|
|
873
800
|
// =====================================================
|
|
874
801
|
|
|
875
|
-
// MMethod to check if a specific state is loading
|
|
876
802
|
isLoading(state: keyof LoadingStates): boolean {
|
|
877
803
|
return this.loadingStates[state] === 'loading';
|
|
878
804
|
}
|
|
879
805
|
|
|
880
|
-
// MMethod to check if any state is loading
|
|
881
806
|
isAnyLoading(): boolean {
|
|
882
|
-
return Object.values(this.loadingStates).some(state => state === 'loading');
|
|
807
|
+
return Object.values(this.loadingStates).some((state) => state === 'loading');
|
|
883
808
|
}
|
|
884
809
|
|
|
885
|
-
|
|
886
|
-
|
|
810
|
+
setLoadingState(
|
|
811
|
+
state: keyof LoadingStates,
|
|
812
|
+
value: LoadingState | { [buttonType: string]: LoadingState },
|
|
813
|
+
): void {
|
|
887
814
|
if (state === 'aditionalButtons') {
|
|
888
815
|
if (typeof value === 'string') {
|
|
889
|
-
console.warn(
|
|
816
|
+
console.warn(
|
|
817
|
+
`No puedes asignar '${value}' directamente a aditionalButtons. Usa setAditionalButtonLoading en su lugar.`,
|
|
818
|
+
);
|
|
890
819
|
} else {
|
|
891
820
|
this.loadingStates.aditionalButtons = value;
|
|
892
821
|
}
|
|
@@ -895,34 +824,29 @@ export class JCompleteCrudTableComponent implements OnInit {
|
|
|
895
824
|
}
|
|
896
825
|
}
|
|
897
826
|
|
|
898
|
-
// Activate loading
|
|
899
827
|
setAditionalButtonLoading(buttonType: string, id?: number | string): void {
|
|
900
828
|
const key = id !== undefined ? `${buttonType}_${id}` : buttonType;
|
|
901
829
|
this.loadingStates.aditionalButtons[key] = 'loading';
|
|
902
830
|
}
|
|
903
831
|
|
|
904
|
-
// Clear loading
|
|
905
832
|
clearAditionalButtonLoading(buttonType: string, id?: number | string): void {
|
|
906
833
|
const key = id !== undefined ? `${buttonType}_${id}` : buttonType;
|
|
907
834
|
this.loadingStates.aditionalButtons[key] = 'idle';
|
|
908
835
|
}
|
|
909
836
|
|
|
910
|
-
// Verify loading
|
|
911
837
|
isAditionalButtonLoading(buttonType: string, id?: number | string): boolean {
|
|
912
838
|
const key = id !== undefined ? `${buttonType}_${id}` : buttonType;
|
|
913
839
|
return this.loadingStates.aditionalButtons[key] === 'loading';
|
|
914
840
|
}
|
|
915
841
|
|
|
916
|
-
//
|
|
842
|
+
// =====================================================
|
|
917
843
|
// Expand rows
|
|
918
|
-
//
|
|
844
|
+
// =====================================================
|
|
919
845
|
|
|
920
|
-
// Method to check if the table has expandable rows
|
|
921
846
|
hasExpandable(): boolean {
|
|
922
847
|
return hasExpandableColumn(this.columns);
|
|
923
848
|
}
|
|
924
849
|
|
|
925
|
-
// Method to check if a row is expanded
|
|
926
850
|
toggleRow(row: any): void {
|
|
927
851
|
if (this.expandedRows.has(row)) {
|
|
928
852
|
this.expandedRows.delete(row);
|
|
@@ -931,240 +855,105 @@ export class JCompleteCrudTableComponent implements OnInit {
|
|
|
931
855
|
}
|
|
932
856
|
}
|
|
933
857
|
|
|
934
|
-
// Method to get expanded content from a row
|
|
935
858
|
getExpandedContent(row: any): SafeHtml {
|
|
936
859
|
const html = resolveExpandContent(this.columns, row);
|
|
937
|
-
|
|
938
860
|
return this.sanitizer.bypassSecurityTrustHtml(html);
|
|
939
861
|
}
|
|
940
862
|
|
|
941
|
-
// Method to get the expansion state of a row
|
|
942
863
|
getExpansionState(row: any): 'expanded' | 'collapsed' {
|
|
943
864
|
return this.expandedRows.has(row) ? 'expanded' : 'collapsed';
|
|
944
865
|
}
|
|
945
866
|
|
|
946
867
|
// =====================================================
|
|
947
|
-
//
|
|
868
|
+
// Inline row editing
|
|
948
869
|
// =====================================================
|
|
949
870
|
|
|
950
|
-
// Method to edit an item
|
|
951
|
-
// Replaces the onEditRow method
|
|
952
871
|
onEditRow(item: any): void {
|
|
953
872
|
if (this.editingRowItem === item) {
|
|
954
873
|
this.editingRowItem = null;
|
|
955
874
|
this.originalRowItem = null;
|
|
956
|
-
|
|
957
|
-
// If we are editing a different row, cancel the editing of the previous one
|
|
958
|
-
if (this.editingRowItem !== null) {
|
|
959
|
-
// Restore the original value of the previous row
|
|
960
|
-
this.restoreOriginalData();
|
|
961
|
-
}
|
|
962
|
-
this.editingRowItem = item;
|
|
963
|
-
|
|
964
|
-
this.originalRowItem = this.deepCopy(item);
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
// Replaces the restoreOriginalData method
|
|
969
|
-
restoreOriginalData() {
|
|
970
|
-
if (this.editingRowItem && this.originalRowItem) {
|
|
971
|
-
const restoredData = this.deepCopy(this.originalRowItem);
|
|
972
|
-
Object.assign(this.editingRowItem, restoredData);
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
// Adds this new method for deep copying
|
|
977
|
-
private deepCopy(obj: any): any {
|
|
978
|
-
if (obj === null || typeof obj !== 'object') {
|
|
979
|
-
return obj;
|
|
875
|
+
return;
|
|
980
876
|
}
|
|
981
877
|
|
|
982
|
-
if (
|
|
983
|
-
|
|
878
|
+
if (this.editingRowItem !== null) {
|
|
879
|
+
this.restoreOriginalData();
|
|
984
880
|
}
|
|
985
881
|
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
882
|
+
this.editingRowItem = item;
|
|
883
|
+
this.originalRowItem = deepCopy(item);
|
|
884
|
+
}
|
|
989
885
|
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
copy[key] = this.deepCopy(obj[key]);
|
|
994
|
-
}
|
|
886
|
+
restoreOriginalData(): void {
|
|
887
|
+
if (this.editingRowItem && this.originalRowItem) {
|
|
888
|
+
Object.assign(this.editingRowItem, deepCopy(this.originalRowItem));
|
|
995
889
|
}
|
|
996
|
-
|
|
997
|
-
return copy;
|
|
998
890
|
}
|
|
999
891
|
|
|
1000
|
-
// Method to save the editing of the item
|
|
1001
892
|
onSaveRow(item: any): void {
|
|
1002
|
-
|
|
1003
|
-
// Get the name of the entity's id
|
|
1004
893
|
const idField = `id_${this.mainEndpoint}`;
|
|
1005
894
|
|
|
1006
|
-
// If the item being edited exists, save it
|
|
1007
895
|
if (this.editingRowItem && this.editingRowItem[idField] === item[idField]) {
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
this.
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
} else {
|
|
1025
|
-
this.alertToastService.AlertToast({
|
|
1026
|
-
type: "error",
|
|
1027
|
-
title: "Error al guardar",
|
|
1028
|
-
description: "No se pudo guardar el registro. Por favor, inténtelo de nuevo.",
|
|
1029
|
-
});
|
|
896
|
+
this.genericService
|
|
897
|
+
.update<any>({
|
|
898
|
+
endpoint: this.mainEndpoint,
|
|
899
|
+
id: this.editingRowItem[idField],
|
|
900
|
+
data: this.editingRowItem})
|
|
901
|
+
.subscribe({
|
|
902
|
+
next: (response) => {
|
|
903
|
+
this.alertToastService.AlertToast({
|
|
904
|
+
type: 'success',
|
|
905
|
+
title: 'Registro actualizado!',
|
|
906
|
+
description: response.message});
|
|
907
|
+
|
|
908
|
+
this.editingRowItem = null;
|
|
909
|
+
}});
|
|
910
|
+
return;
|
|
1030
911
|
}
|
|
912
|
+
|
|
913
|
+
this.alertToastService.AlertToast({
|
|
914
|
+
type: 'error',
|
|
915
|
+
title: 'Error al guardar',
|
|
916
|
+
description: 'No se pudo guardar el registro. Por favor, inténtelo de nuevo.'});
|
|
1031
917
|
}
|
|
1032
918
|
|
|
1033
|
-
// Checks if a column has equivalence configuration
|
|
1034
919
|
hasEquivalence(columnKey: string): boolean {
|
|
1035
|
-
|
|
1036
|
-
if (conf && conf.keyColumnSearch === columnKey) {
|
|
1037
|
-
return true;
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
return false;
|
|
920
|
+
return hasEquivalenceConfig(this.equivalenceConfigs, columnKey);
|
|
1041
921
|
}
|
|
1042
922
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
keyReturn: string;
|
|
1046
|
-
options: { key: any; label: string }[];
|
|
1047
|
-
optional: boolean
|
|
1048
|
-
} | null {
|
|
1049
|
-
|
|
1050
|
-
for (const [name, conf] of Object.entries(this.equivalenceConfigs || {})) {
|
|
1051
|
-
if (conf && conf.keyColumnSearch === columnKey) {
|
|
1052
|
-
const keyReturn = conf.keyReturn;
|
|
1053
|
-
const idMap = (this.equivalences as any)[keyReturn];
|
|
1054
|
-
|
|
1055
|
-
if (!idMap) return null;
|
|
1056
|
-
|
|
1057
|
-
const options = Object.entries(idMap).map(([id, label]) => ({
|
|
1058
|
-
key: +id,
|
|
1059
|
-
label: String(label),
|
|
1060
|
-
}));
|
|
1061
|
-
|
|
1062
|
-
return {
|
|
1063
|
-
keyReturn,
|
|
1064
|
-
options,
|
|
1065
|
-
optional: conf.optional || false,
|
|
1066
|
-
};
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
return null;
|
|
923
|
+
getEquivalenceData(columnKey: string): ReturnType<typeof resolveEquivalenceData> {
|
|
924
|
+
return resolveEquivalenceData(this.equivalenceConfigs, this.equivalences, columnKey);
|
|
1070
925
|
}
|
|
1071
926
|
|
|
1072
|
-
// Sets a nested value in an object based on the column key
|
|
1073
927
|
setNestedValue(item: any, columnKey: string, value: any): void {
|
|
1074
|
-
|
|
1075
|
-
let current = item;
|
|
1076
|
-
|
|
1077
|
-
for (let i = 0; i < keys.length; i++) {
|
|
1078
|
-
const key = keys[i];
|
|
1079
|
-
if (i === keys.length - 1) {
|
|
1080
|
-
current[key] = value;
|
|
1081
|
-
} else {
|
|
1082
|
-
if (!current[key]) {
|
|
1083
|
-
current[key] = {};
|
|
1084
|
-
}
|
|
1085
|
-
current = current[key];
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
928
|
+
setNestedValue(item, columnKey, value);
|
|
1088
929
|
}
|
|
1089
930
|
|
|
1090
|
-
// =====================================================
|
|
1091
|
-
// Update table editing
|
|
1092
|
-
// =====================================================
|
|
1093
|
-
|
|
1094
|
-
// Handles the equivalence change in an item
|
|
1095
931
|
onEquivalenceChange(item: any, columnKey: string, newId: any): void {
|
|
1096
932
|
const eqData = this.getEquivalenceData(columnKey);
|
|
1097
|
-
if (!eqData)
|
|
933
|
+
if (!eqData) {
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
1098
936
|
|
|
1099
|
-
// Change the value in the edited object (directly in the same object)
|
|
1100
937
|
const newLabel = eqData.options.find((opt) => opt.key == newId)?.label ?? null;
|
|
1101
|
-
|
|
938
|
+
setNestedValue(this.editingRowItem, columnKey, newLabel);
|
|
1102
939
|
this.editingRowItem[eqData.keyReturn] = newId;
|
|
1103
940
|
}
|
|
1104
941
|
|
|
1105
|
-
// Handles the change of value in an editable field
|
|
1106
942
|
onFieldChange(item: any, columnKey: string, value: any): void {
|
|
1107
|
-
|
|
1108
|
-
this.setNestedValue(this.editingRowItem, columnKey, value);
|
|
943
|
+
setNestedValue(this.editingRowItem, columnKey, value);
|
|
1109
944
|
}
|
|
1110
945
|
|
|
946
|
+
// =====================================================
|
|
947
|
+
// Row styling
|
|
948
|
+
// =====================================================
|
|
1111
949
|
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
// Apply colors to row based on conditions
|
|
1116
|
-
private readonly rowColorMap: { [key: string]: string } = {
|
|
1117
|
-
primary: 'bg-primary/20 hover:bg-dark-primary/30! dark:hover:bg-dark-primary/20',
|
|
1118
|
-
success: 'bg-green-500/20 hover:bg-green-500/30! dark:hover:bg-green-500/20',
|
|
1119
|
-
error: ' bg-red-500/20 hover:bg-red-500/30! dark:hover:bg-red-500/20',
|
|
1120
|
-
warning: 'bg-yellow-500/20 hover:bg-yellow-500/30! dark:hover:bg-yellow-500/20',
|
|
1121
|
-
info: ' bg-blue-500/20 hover:bg-blue-500/30! dark:hover:bg-blue-500/20',
|
|
1122
|
-
};
|
|
1123
|
-
|
|
1124
|
-
getRowNgClass(row: any): any {
|
|
1125
|
-
|
|
1126
|
-
const base: any = {
|
|
1127
|
-
'cursor-pointer': this.hasExpandable(),
|
|
1128
|
-
};
|
|
1129
|
-
|
|
1130
|
-
const columnWithColor = this.columns.find(c => !!c.rowColorCondition);
|
|
1131
|
-
if (!columnWithColor) return base;
|
|
1132
|
-
|
|
1133
|
-
const keys = columnWithColor.key.split('.');
|
|
1134
|
-
let value = row;
|
|
1135
|
-
for (const key of keys) {
|
|
1136
|
-
value = value?.[key];
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
const normalizedValue =
|
|
1140
|
-
value === null || value === undefined
|
|
1141
|
-
? 'null'
|
|
1142
|
-
: String(value).toUpperCase();
|
|
1143
|
-
const semanticClass = columnWithColor.rowColorCondition?.[normalizedValue];
|
|
1144
|
-
|
|
1145
|
-
if (semanticClass && this.rowColorMap[semanticClass]) {
|
|
1146
|
-
base[this.rowColorMap[semanticClass]] = true;
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
return base;
|
|
950
|
+
getRowNgClass(row: any): Record<string, boolean> {
|
|
951
|
+
return buildRowNgClass(row, this.columns, this.hasExpandable());
|
|
1150
952
|
}
|
|
1151
953
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
mobileInnerExpandedRows: Set<any> = new Set();
|
|
954
|
+
// =====================================================
|
|
955
|
+
// Mobile layout
|
|
956
|
+
// =====================================================
|
|
1168
957
|
|
|
1169
958
|
toggleMobileInnerRow(row: any): void {
|
|
1170
959
|
if (this.mobileInnerExpandedRows.has(row)) {
|
|
@@ -1175,106 +964,30 @@ export class JCompleteCrudTableComponent implements OnInit {
|
|
|
1175
964
|
}
|
|
1176
965
|
|
|
1177
966
|
getMobileMainTitle(item: any): string {
|
|
1178
|
-
|
|
1179
|
-
column.visible &&
|
|
1180
|
-
!column.isDecorator &&
|
|
1181
|
-
!column.isDecoratorArray &&
|
|
1182
|
-
!column.isLink &&
|
|
1183
|
-
!column.isLinkImage &&
|
|
1184
|
-
!this.isBoolean(this.getValue(item, column))
|
|
1185
|
-
);
|
|
1186
|
-
|
|
1187
|
-
return firstTextColumn ? this.getValue(item, firstTextColumn) : 'Registro';
|
|
967
|
+
return getMobileMainTitle(item, this.columns, (row, column) => this.getValue(row, column));
|
|
1188
968
|
}
|
|
1189
969
|
|
|
1190
970
|
getMobileMainDescription(item: any): string | null {
|
|
1191
|
-
|
|
1192
|
-
column.visible &&
|
|
1193
|
-
(column as any).isDescription
|
|
1194
|
-
);
|
|
1195
|
-
|
|
1196
|
-
if (!descriptionColumn) return null;
|
|
1197
|
-
|
|
1198
|
-
const value = this.getValue(item, descriptionColumn);
|
|
1199
|
-
|
|
1200
|
-
if (
|
|
1201
|
-
value === null ||
|
|
1202
|
-
value === undefined ||
|
|
1203
|
-
value === '' ||
|
|
1204
|
-
value === 'S/N'
|
|
1205
|
-
) {
|
|
1206
|
-
return null;
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
return value;
|
|
971
|
+
return getMobileMainDescription(item, this.columns, (row, column) => this.getValue(row, column));
|
|
1210
972
|
}
|
|
1211
973
|
|
|
1212
974
|
getMobileHeaderColumns(): TableColumn<any>[] {
|
|
1213
|
-
return this.columns
|
|
1214
|
-
.filter(column => column.visible)
|
|
1215
|
-
.filter(column => column.isDecorator || column.colorDecorator)
|
|
1216
|
-
.slice(0, 2);
|
|
975
|
+
return getMobileHeaderColumns(this.columns);
|
|
1217
976
|
}
|
|
1218
977
|
|
|
1219
978
|
getGroupMobileStudentTitle(student: any): string {
|
|
1220
|
-
|
|
1221
|
-
(column)
|
|
1222
|
-
);
|
|
1223
|
-
const lastNameCol = this.columns.find(
|
|
1224
|
-
(column) => column.visible && column.key === 'student.lastname_student',
|
|
1225
|
-
);
|
|
1226
|
-
|
|
1227
|
-
const parts: string[] = [];
|
|
1228
|
-
|
|
1229
|
-
if (firstNameCol) {
|
|
1230
|
-
const firstName = this.getValue(student, firstNameCol);
|
|
1231
|
-
if (firstName) {
|
|
1232
|
-
parts.push(firstName);
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
if (lastNameCol) {
|
|
1237
|
-
const lastName = this.getValue(student, lastNameCol);
|
|
1238
|
-
if (lastName) {
|
|
1239
|
-
parts.push(lastName);
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
if (parts.length > 0) {
|
|
1244
|
-
return parts.join(' ');
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
const fallbackColumn = this.columns.find(
|
|
1248
|
-
(column) =>
|
|
1249
|
-
column.visible &&
|
|
1250
|
-
!column.isDecorator &&
|
|
1251
|
-
!column.isDecoratorArray &&
|
|
1252
|
-
!column.isLink &&
|
|
1253
|
-
!column.isLinkImage &&
|
|
1254
|
-
!this.isBoolean(this.getValue(student, column)),
|
|
979
|
+
return getGroupMobileStudentTitle(student, this.columns, (row, column) =>
|
|
980
|
+
this.getValue(row, column),
|
|
1255
981
|
);
|
|
1256
|
-
|
|
1257
|
-
return fallbackColumn
|
|
1258
|
-
? this.getValue(student, fallbackColumn)
|
|
1259
|
-
: 'Estudiante';
|
|
1260
982
|
}
|
|
1261
983
|
|
|
1262
984
|
getGroupMobileStudentHeaderColumns(student: any): TableColumn<any>[] {
|
|
1263
|
-
return this.columns
|
|
1264
|
-
.
|
|
1265
|
-
|
|
1266
|
-
column.visible &&
|
|
1267
|
-
(column.isDecorator || column.colorDecorator) &&
|
|
1268
|
-
this.getValue(student, column),
|
|
1269
|
-
)
|
|
1270
|
-
.slice(0, 3);
|
|
985
|
+
return getGroupMobileStudentHeaderColumns(student, this.columns, (row, column) =>
|
|
986
|
+
this.getValue(row, column),
|
|
987
|
+
);
|
|
1271
988
|
}
|
|
1272
989
|
|
|
1273
990
|
getGroupMobileStudentIndex(groupItem: any, index: number): number {
|
|
1274
|
-
|
|
1275
|
-
const limit = groupItem.meta?.page?.limit ?? 0;
|
|
1276
|
-
|
|
1277
|
-
return (page - 1) * limit + index + 1;
|
|
991
|
+
return getGroupMobileStudentIndex(groupItem, index);
|
|
1278
992
|
}
|
|
1279
|
-
|
|
1280
993
|
}
|