@zeedhi/teknisa-components-common 1.106.0 → 1.107.1

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.
@@ -1,5 +1,5 @@
1
1
  import { KeyMap, I18n, FormatterParserProvider, Messages, Metadata, DatasourceFactory, MemoryDatasource, URL, Utils, RestDatasource, Config, Accessor, Loader, DateHelper, Http, Singleton, VersionService } from '@zeedhi/core';
2
- import { Form, Button, Tooltip, GridEditable, Grid, ComponentRender, Iterable, Carousel, IterableComponentRender as IterableComponentRender$1, Loading as Loading$1, GridColumnEditable, Report, ColumnNotFoundError, IterableColumnsButtonController, IterableColumnsButton, ModalService, SelectMultiple, TreeGridEditable, List } from '@zeedhi/common';
2
+ import { Form, Button, Tooltip, GridEditable, Grid, ComponentRender, Iterable, Carousel, IterableComponentRender as IterableComponentRender$1, Loading as Loading$1, GridColumnEditable, Report, EmptyDataError, AlertService, ColumnNotFoundError, IterableColumnsButtonController, IterableColumnsButton, ModalService, SelectMultiple, TreeGridEditable, List } from '@zeedhi/common';
3
3
  import debounce from 'lodash.debounce';
4
4
  import merge from 'lodash.merge';
5
5
  import { UserInfo } from '@zeedhi/zd-user-info-common';
@@ -1254,6 +1254,7 @@ Messages.add({
1254
1254
  TEKGRID_HELPERVALUE_CURRENT_YEAR: 'Ano Atual',
1255
1255
  TEKGRID_WITH_GROUPS: '(Com grupos)',
1256
1256
  TEKGRID_GRID_MIRROR: '(Espelho do grid)',
1257
+ TEKGRID_NO_DATA: 'Não há dados para gerar o relatório',
1257
1258
  },
1258
1259
  },
1259
1260
  'en-US': {
@@ -1326,6 +1327,7 @@ Messages.add({
1326
1327
  TEKGRID_HELPERVALUE_CURRENT_YEAR: 'Current Year',
1327
1328
  TEKGRID_WITH_GROUPS: '(With groups)',
1328
1329
  TEKGRID_GRID_MIRROR: '(Grid mirror)',
1330
+ TEKGRID_NO_DATA: 'There is no data to generate the report',
1329
1331
  },
1330
1332
  },
1331
1333
  'es-CL': {
@@ -1398,6 +1400,7 @@ Messages.add({
1398
1400
  TEKGRID_HELPERVALUE_CURRENT_YEAR: 'Año actual',
1399
1401
  TEKGRID_WITH_GROUPS: '(Con grupos)',
1400
1402
  TEKGRID_GRID_MIRROR: '(Espejo de grid)',
1403
+ TEKGRID_NO_DATA: 'No hay datos para generar el reporte',
1401
1404
  },
1402
1405
  },
1403
1406
  });
@@ -1505,6 +1508,22 @@ class GridController {
1505
1508
  }
1506
1509
  }
1507
1510
 
1511
+ /**
1512
+ * Extracts properties from an object based on a list of properties
1513
+ * @param obj object whose properties will be extracted
1514
+ * @param props array of strings with the properties to be extracted
1515
+ * @returns object containing the extracted properties
1516
+ */
1517
+ const extractProperties = (obj, props) => {
1518
+ const result = {};
1519
+ props.forEach((prop) => {
1520
+ if (obj[prop] !== undefined) {
1521
+ result[prop] = obj[prop];
1522
+ }
1523
+ });
1524
+ return result;
1525
+ };
1526
+
1508
1527
  const DynamicFilterOperations = {
1509
1528
  CONTAINS: true,
1510
1529
  NOT_CONTAINS: true,
@@ -1594,6 +1613,14 @@ class TekMemoryDatasource extends MemoryDatasource {
1594
1613
  }
1595
1614
  return superQueryString + dynamicFilterQuerystring + searchJoinQuerystring;
1596
1615
  }
1616
+ /**
1617
+ * Allows the comunication between the base filter and the dynamic filter
1618
+ * @param filtroDinamico
1619
+ * @returns
1620
+ */
1621
+ setBaseFilter(filtroDinamico) {
1622
+ return this.setDynamicFilter(filtroDinamico);
1623
+ }
1597
1624
  /**
1598
1625
  * Adds a new dynamic filter position or replace if exists
1599
1626
  * @param column Dynamic Filter column name
@@ -1671,12 +1698,15 @@ class TekMemoryDatasource extends MemoryDatasource {
1671
1698
  * @returns Is valid filter value
1672
1699
  */
1673
1700
  isValidDynamicFilterValue(column, value) {
1701
+ if (!Array.isArray(value))
1702
+ return false;
1674
1703
  return !this.reservedKeys[column]
1675
- && value
1676
1704
  && value.length > 0
1677
- && value.every((filterValue) => this.dynamicFilterOperations[filterValue.operation]
1705
+ && value.every((filterValue) => filterValue
1706
+ && this.dynamicFilterOperations[filterValue.operation]
1678
1707
  && this.dynamicFilterRelations[filterValue.relation]
1679
- && filterValue.value !== '' && filterValue.value !== null);
1708
+ && filterValue.value !== ''
1709
+ && filterValue.value !== null);
1680
1710
  }
1681
1711
  clone() {
1682
1712
  return Object.assign(Object.assign({}, super.clone()), { dynamicFilter: this.dynamicFilter, searchJoin: this.searchJoin, type: 'tek-memory' });
@@ -1863,6 +1893,14 @@ class TekRestDatasource extends RestDatasource {
1863
1893
  }
1864
1894
  return superQueryString + dynamicFilterQuerystring + searchJoinQuerystring;
1865
1895
  }
1896
+ /**
1897
+ * Allows the comunication between the base filter and the dynamic filter
1898
+ * @param filtroDinamico
1899
+ * @returns
1900
+ */
1901
+ setBaseFilter(filtroDinamico) {
1902
+ return this.setDynamicFilter(filtroDinamico);
1903
+ }
1866
1904
  /**
1867
1905
  * Adds a new dynamic filter position or replace if exists
1868
1906
  * @param column Dynamic Filter column name
@@ -1940,12 +1978,15 @@ class TekRestDatasource extends RestDatasource {
1940
1978
  * @returns Is valid filter value
1941
1979
  */
1942
1980
  isValidDynamicFilterValue(column, value) {
1981
+ if (!Array.isArray(value))
1982
+ return false;
1943
1983
  return !this.reservedKeys[column]
1944
- && value
1945
1984
  && value.length > 0
1946
- && value.every((filterValue) => this.dynamicFilterOperations[filterValue.operation]
1985
+ && value.every((filterValue) => filterValue
1986
+ && this.dynamicFilterOperations[filterValue.operation]
1947
1987
  && this.dynamicFilterRelations[filterValue.relation]
1948
- && filterValue.value !== '' && filterValue.value !== null);
1988
+ && filterValue.value !== ''
1989
+ && filterValue.value !== null);
1949
1990
  }
1950
1991
  /**
1951
1992
  * Retrieves request params
@@ -2154,6 +2195,19 @@ class TekGridColumn extends GridColumnEditable {
2154
2195
  }
2155
2196
  }
2156
2197
 
2198
+ /**
2199
+ * Thrown when a row has incomplete group information.
2200
+ * Provides details about the missing groups and the problematic row.
2201
+ */
2202
+ class IncompleteGroupsError extends Error {
2203
+ constructor(row, missingGroups) {
2204
+ const rowPreview = JSON.stringify(row);
2205
+ const message = `Row groups are incomplete. Missing groups: ${missingGroups.join(', ')}.\nRow data:\n${rowPreview}`;
2206
+ super(message);
2207
+ this.name = 'IncompleteGroupsError';
2208
+ }
2209
+ }
2210
+
2157
2211
  /* TekGrid Class */
2158
2212
  class TekGrid extends GridEditable {
2159
2213
  /**
@@ -2490,16 +2544,25 @@ class TekGrid extends GridEditable {
2490
2544
  if (typeof this.events.beforeOpenReport === 'function') {
2491
2545
  beforeOpen = this.events.beforeOpenReport;
2492
2546
  }
2493
- return report
2494
- .getReport(typeValue, portrait, {
2495
- metaData: merge(rowObj, {
2496
- filter,
2497
- groups: reportGroups,
2498
- columns: reportAggregations,
2499
- xlsDefaultType: this.xlsDefaultType,
2500
- }),
2501
- }, beforeOpen)
2502
- .then((reportUrl) => window.open(reportUrl));
2547
+ try {
2548
+ const reportUrl = yield report
2549
+ .getReport(typeValue, portrait, {
2550
+ metaData: merge(rowObj, {
2551
+ filter,
2552
+ groups: reportGroups,
2553
+ columns: reportAggregations,
2554
+ xlsDefaultType: this.xlsDefaultType,
2555
+ }),
2556
+ }, beforeOpen);
2557
+ window.open(reportUrl);
2558
+ }
2559
+ catch (e) {
2560
+ if (e instanceof EmptyDataError) {
2561
+ AlertService.show({ name: 'no-data-warning', text: I18n.translate('TEKGRID_NO_DATA'), color: 'warning' });
2562
+ return;
2563
+ }
2564
+ throw e;
2565
+ }
2503
2566
  });
2504
2567
  }
2505
2568
  registerTask(task) {
@@ -2590,9 +2653,10 @@ class TekGrid extends GridEditable {
2590
2653
  this.groups.forEach((group) => {
2591
2654
  group.lastGroupHeaderRow.children.push(row);
2592
2655
  });
2593
- this.groupedData.push(Object.assign(Object.assign({}, row), { groupHeaders: this.groups
2594
- .map((group) => group.lastGroupHeaderRow)
2595
- .filter(Boolean) }));
2656
+ const groupHeaders = this.groups
2657
+ .map((group) => group.lastGroupHeaderRow)
2658
+ .filter(Boolean);
2659
+ this.groupedData.push(Object.assign(Object.assign({}, row), { groupHeaders }));
2596
2660
  this.calcSummary(row);
2597
2661
  });
2598
2662
  if (this.groupedData.length > 0) {
@@ -2648,14 +2712,14 @@ class TekGrid extends GridEditable {
2648
2712
  return;
2649
2713
  }
2650
2714
  for (let i = this.groups.length - 1; i >= groupIndex; i -= 1) {
2651
- if (this.groups[i].initialized) {
2652
- const groupFooterRow = Object.assign({ groupFooter: true, groupIndex: i, groupHeaders: [], groupLabel: this.groups[i].lastGroupHeaderRow.groupLabel, groupValue: this.groups[i].lastGroupHeaderRow.groupValue }, this.summaryData(this.groups[i].footer));
2653
- // add header for outer groups
2654
- for (let g = 0; g < i; g += 1) {
2655
- groupFooterRow.groupHeaders.push(this.groups[g].lastGroupHeaderRow);
2656
- }
2657
- this.groupedData.push(groupFooterRow);
2715
+ if (!this.groups[i].initialized)
2716
+ return;
2717
+ const groupFooterRow = Object.assign({ groupFooter: true, groupIndex: i, groupHeaders: [], groupLabel: this.groups[i].lastGroupHeaderRow.groupLabel, groupValue: this.groups[i].lastGroupHeaderRow.groupValue }, this.summaryData(this.groups[i].footer));
2718
+ // add header for outer groups
2719
+ for (let g = 0; g < i; g += 1) {
2720
+ groupFooterRow.groupHeaders.push(this.groups[g].lastGroupHeaderRow);
2658
2721
  }
2722
+ this.groupedData.push(groupFooterRow);
2659
2723
  }
2660
2724
  }
2661
2725
  resetFooterVariables(group) {
@@ -2905,6 +2969,7 @@ class TekGrid extends GridEditable {
2905
2969
  */
2906
2970
  groupRowClick(row, event, element) {
2907
2971
  if (!this.preventRowClick) {
2972
+ this.datasource.currentRow = row;
2908
2973
  this.callEvent('groupRowClick', {
2909
2974
  event,
2910
2975
  element,
@@ -2917,6 +2982,7 @@ class TekGrid extends GridEditable {
2917
2982
  }
2918
2983
  groupRowDoubleClick(row, event, element) {
2919
2984
  if (!this.preventRowDoubleClick) {
2985
+ this.datasource.currentRow = row;
2920
2986
  this.callEvent('groupRowDoubleClick', {
2921
2987
  event,
2922
2988
  element,
@@ -2966,6 +3032,157 @@ class TekGrid extends GridEditable {
2966
3032
  getColumn(name) {
2967
3033
  return super.getColumn(name);
2968
3034
  }
3035
+ /**
3036
+ * Adds new row to the datasource data and pushes it to the editedRows
3037
+ * @param row Row
3038
+ * @param position whether the new Row will be inserted at the beginning or end of the data array
3039
+ */
3040
+ addNewRow(row, position = 'end') {
3041
+ const _super = Object.create(null, {
3042
+ addNewRow: { get: () => super.addNewRow }
3043
+ });
3044
+ return __awaiter(this, void 0, void 0, function* () {
3045
+ if (!this.groupColumns.length) {
3046
+ yield _super.addNewRow.call(this, row, position);
3047
+ return;
3048
+ }
3049
+ this.insertRowInDatasource(row, position);
3050
+ this.saveRowReference(row);
3051
+ this.addGroupedRow(row, position);
3052
+ this.editing = true;
3053
+ });
3054
+ }
3055
+ /**
3056
+ * Takes a row and adds it to the selected group (datasource.currentRow)
3057
+ * @param row the new row to be added
3058
+ * @param position the position, at the beginning of the group or at the end
3059
+ */
3060
+ addToSelection(row, position = 'end') {
3061
+ const { currentRow } = this.datasource;
3062
+ // if the currentRow is a group, use the groupRow as reference
3063
+ const referenceRow = this.isGroupHeader(currentRow) ? currentRow.groupRow : currentRow;
3064
+ // extract the group properties from the referenceRow to add to the new row
3065
+ const groupProperties = extractProperties(referenceRow, this.groupColumnNames);
3066
+ const rowWithGroups = Object.assign(Object.assign({}, row), groupProperties);
3067
+ return this.addNewRow(rowWithGroups, position);
3068
+ }
3069
+ /**
3070
+ * Adds a new row to the groupedData array
3071
+ * @param row the new row to be added
3072
+ * @param position the position, at the beginning of the group or at the end
3073
+ */
3074
+ addGroupedRow(row, position) {
3075
+ const missingGroups = this.findMissingGroups(row);
3076
+ if (missingGroups.length > 0) {
3077
+ throw new IncompleteGroupsError(row, missingGroups);
3078
+ }
3079
+ const { index, group } = this.findLastGroupingIndex(row);
3080
+ const groupHeaders = [...group.groupHeaders, group];
3081
+ const newGroupedRow = Object.assign(Object.assign({}, row), { groupHeaders });
3082
+ if (position === 'end') {
3083
+ const childrenLength = group.children.length;
3084
+ this.groupedData.splice(index + childrenLength + 1, 0, newGroupedRow);
3085
+ }
3086
+ else {
3087
+ this.groupedData.splice(index + 1, 0, newGroupedRow);
3088
+ }
3089
+ groupHeaders.forEach((groupHeader) => {
3090
+ // open all groups, making the new row visible to the user
3091
+ groupHeader.groupOpened = true;
3092
+ this.addRowToGroupChildren(row, groupHeader, position);
3093
+ });
3094
+ }
3095
+ findMissingGroups(row) {
3096
+ return this.groupColumnNames.filter((group) => row[group] === undefined);
3097
+ }
3098
+ /**
3099
+ * Finds the last group (most internal group) where a row should be inserted
3100
+ * @param row to be inserted
3101
+ * @returns the index and the group where the row should be inserted
3102
+ */
3103
+ findLastGroupingIndex(row) {
3104
+ const index = this.groupedData.findIndex((groupRow) => {
3105
+ // ignore rows that are not headers or not the last grouping (most internal groping)
3106
+ if (!this.isGroupHeader(groupRow) || !this.isLastGrouping(groupRow))
3107
+ return false;
3108
+ // if the new added row is not part of the group, ignore it
3109
+ if (groupRow.groupValue !== row[groupRow.groupColumnName])
3110
+ return false;
3111
+ if (this.groupColumns.length === 1)
3112
+ return true;
3113
+ const matchHeaders = groupRow.groupHeaders.every((groupHeader) => (groupHeader.groupValue === row[groupHeader.groupColumnName]));
3114
+ return matchHeaders;
3115
+ });
3116
+ if (index === -1) {
3117
+ // No existing group found, so we create the full group hierarchy
3118
+ return this.createGroupHierarchyForRow(row);
3119
+ }
3120
+ const group = this.groupedData[index];
3121
+ return { index, group };
3122
+ }
3123
+ /**
3124
+ * Creates a group hierarchy for a new row, creating intermediate groups if needed
3125
+ * @param row to be inserted
3126
+ * @returns the index and the group where the row should be inserted
3127
+ */
3128
+ createGroupHierarchyForRow(row) {
3129
+ let groupHeaders = [];
3130
+ let insertionIndex = this.groupedData.length; // default to end
3131
+ this.groups.forEach((column) => {
3132
+ const groupValue = row[column.name];
3133
+ const existingGroupIndex = this.groupedData.findIndex((groupRow) => this.isGroupHeader(groupRow)
3134
+ && groupRow.groupColumnName === column.name
3135
+ && groupRow.groupValue === groupValue
3136
+ && groupRow.groupHeaders.every((h, j) => h.groupValue === groupHeaders[j].groupValue));
3137
+ if (existingGroupIndex !== -1) {
3138
+ const existingGroup = this.groupedData[existingGroupIndex];
3139
+ groupHeaders = [...existingGroup.groupHeaders, existingGroup];
3140
+ insertionIndex = existingGroupIndex;
3141
+ return;
3142
+ }
3143
+ // Create new group header
3144
+ const newGroup = {
3145
+ group: true,
3146
+ groupHeader: true,
3147
+ groupValue,
3148
+ groupColumnName: column.name,
3149
+ groupHeaders: [...groupHeaders],
3150
+ children: [],
3151
+ groupRow: row,
3152
+ groupIndex: groupHeaders.length,
3153
+ groupLabel: I18n.translate(column.label),
3154
+ groupOpened: true,
3155
+ };
3156
+ // Insert right after last insertionIndex
3157
+ insertionIndex += 1;
3158
+ this.groupedData.splice(insertionIndex, 0, newGroup);
3159
+ groupHeaders.push(newGroup);
3160
+ });
3161
+ const group = groupHeaders[groupHeaders.length - 1]; // the most inner group
3162
+ return { index: insertionIndex, group };
3163
+ }
3164
+ /**
3165
+ * Adds a row to the children of a group
3166
+ */
3167
+ addRowToGroupChildren(row, group, position) {
3168
+ if (position === 'end') {
3169
+ group.children.push(row);
3170
+ return;
3171
+ }
3172
+ group.children.unshift(row);
3173
+ }
3174
+ /**
3175
+ * Checks if a row is a group header, adding typescript type-checking
3176
+ */
3177
+ isGroupHeader(row) {
3178
+ return row.groupHeader;
3179
+ }
3180
+ /**
3181
+ * Checks if a row is the last grouping of the grid (the most internal grouping)
3182
+ */
3183
+ isLastGrouping(group) {
3184
+ return group.groupColumnName === this.groupColumns[this.groupColumns.length - 1].name;
3185
+ }
2969
3186
  }
2970
3187
 
2971
3188
  class TekGridColumnsButtonController extends IterableColumnsButtonController {
@@ -3309,7 +3526,7 @@ class TekGridFilterButton extends Button {
3309
3526
  }
3310
3527
  });
3311
3528
  datasource.dynamicFilter = filter;
3312
- this.setFilter(filter, event, datasource.setDynamicFilter.bind(datasource));
3529
+ this.setFilter(filter, event, datasource.setBaseFilter.bind(datasource));
3313
3530
  }
3314
3531
  else {
3315
3532
  Object.keys(formValue).forEach((item) => {
@@ -3320,7 +3537,7 @@ class TekGridFilterButton extends Button {
3320
3537
  }
3321
3538
  });
3322
3539
  datasource.filter = filter;
3323
- this.setFilter(filter, event, datasource.setFilter.bind(datasource));
3540
+ this.setFilter(filter, event, datasource.setBaseFilter.bind(datasource));
3324
3541
  }
3325
3542
  this.grid.changeLayout(event);
3326
3543
  }