@sankhyalabs/core 3.1.2 → 4.0.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.
- package/.docs/classes/Change.md +11 -11
- package/.docs/classes/DataUnit.md +385 -104
- package/.docs/classes/DataUnitStorage.md +116 -0
- package/.docs/classes/DateUtils.md +87 -10
- package/.docs/classes/MaskFormatter-1.md +25 -14
- package/.docs/classes/NumberUtils.md +45 -0
- package/.docs/enums/Action.md +11 -0
- package/.docs/enums/ChangeOperation.md +4 -4
- package/.docs/enums/DependencyType.md +3 -3
- package/.docs/enums/SortMode.md +2 -2
- package/.docs/enums/UserInterface.md +17 -17
- package/.docs/interfaces/ChildDescriptor.md +41 -0
- package/.docs/interfaces/ChildLink.md +30 -0
- package/.docs/interfaces/DUActionInterceptor.md +1 -1
- package/.docs/interfaces/FieldDescriptor.md +11 -11
- package/.docs/interfaces/Filter.md +3 -3
- package/.docs/interfaces/PageRequest.md +3 -3
- package/.docs/interfaces/QuickFilter.md +2 -2
- package/.docs/interfaces/Record.md +24 -2
- package/.docs/interfaces/SavedRecord.md +33 -3
- package/.docs/interfaces/Sort.md +3 -3
- package/.docs/interfaces/SortingProvider.md +1 -1
- package/.docs/interfaces/UnitMetadata.md +1 -1
- package/.docs/interfaces/WaitingChange.md +3 -3
- package/.docs/modules/MaskFormatter.md +4 -4
- package/.docs/modules.md +3 -0
- package/dist/dataunit/DataUnit.d.ts +83 -4
- package/dist/dataunit/DataUnit.js +216 -20
- package/dist/dataunit/DataUnit.js.map +1 -1
- package/dist/dataunit/DataUnitStorage.d.ts +24 -0
- package/dist/dataunit/DataUnitStorage.js +39 -0
- package/dist/dataunit/DataUnitStorage.js.map +1 -0
- package/dist/dataunit/formatting/PrettyFormatter.d.ts +2 -0
- package/dist/dataunit/formatting/PrettyFormatter.js +88 -0
- package/dist/dataunit/formatting/PrettyFormatter.js.map +1 -0
- package/dist/dataunit/metadata/UnitMetadata.d.ts +1 -0
- package/dist/dataunit/metadata/UnitMetadata.js.map +1 -1
- package/dist/dataunit/state/action/DataUnitAction.d.ts +2 -1
- package/dist/dataunit/state/action/DataUnitAction.js +1 -0
- package/dist/dataunit/state/action/DataUnitAction.js.map +1 -1
- package/dist/dataunit/state/slice/ChangesSlice.js +2 -1
- package/dist/dataunit/state/slice/ChangesSlice.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/DateUtils.d.ts +23 -0
- package/dist/utils/DateUtils.js +30 -0
- package/dist/utils/DateUtils.js.map +1 -1
- package/dist/utils/MaskFormatter.d.ts +1 -0
- package/dist/utils/MaskFormatter.js +6 -0
- package/dist/utils/MaskFormatter.js.map +1 -1
- package/dist/utils/NumberUtils.d.ts +25 -0
- package/dist/utils/NumberUtils.js +28 -0
- package/dist/utils/NumberUtils.js.map +1 -1
- package/package.json +4 -2
- package/src/dataunit/DataUnit.ts +244 -28
- package/src/dataunit/DataUnitStorage.ts +43 -0
- package/src/dataunit/formatting/PrettyFormatter.ts +106 -0
- package/src/dataunit/metadata/UnitMetadata.ts +1 -0
- package/src/dataunit/state/action/DataUnitAction.ts +3 -1
- package/src/dataunit/state/slice/ChangesSlice.ts +2 -1
- package/src/index.ts +6 -1
- package/src/utils/DateUtils.ts +34 -1
- package/src/utils/MaskFormatter.ts +8 -0
- package/src/utils/NumberUtils.ts +29 -0
- package/test/util/DataUnitStorage.spec.ts +63 -0
- package/test/util/ElementIDUtils.spec.ts +127 -85
- package/test/util/NumberUtils.spec.ts +22 -0
package/src/dataunit/DataUnit.ts
CHANGED
|
@@ -7,6 +7,7 @@ import StateManager from "./state/StateManager.js";
|
|
|
7
7
|
import ErrorException from "../exceptions/ErrorException.js";
|
|
8
8
|
import WaitingChangeException from "../exceptions/WaitingChangeException.js";
|
|
9
9
|
import { StringUtils } from "../utils/StringUtils.js";
|
|
10
|
+
import ObjectUtils from "../utils/ObjectUtils.js";
|
|
10
11
|
import { LoadDataRequest } from "./loading/LoadDataRequest.js";
|
|
11
12
|
import { LoadDataResponse } from "./loading/LoadDataResponse.js";
|
|
12
13
|
import { PaginationInfo } from "./loading/PaginationInfo.js";
|
|
@@ -14,18 +15,23 @@ import { AddedRecordsReducer, getAddedRecords, prepareAddedRecordId, prepareCopi
|
|
|
14
15
|
import { ChangesReducer, getChangesToSave, isDirty, hasDirtyRecords } from "./state/slice/ChangesSlice.js";
|
|
15
16
|
import { CurrentRecordsReducer, getCurrentRecords, getFieldValue, getModifiedRecords } from "./state/slice/CurrentRecordsSlice.js";
|
|
16
17
|
import { getCurrentPage, getLastPage, getPaginationInfo, getCurrentRequest, LoadingControlReducer, hasMorePages, hasPreviousPages } from "./state/slice/LoadingControlSlice.js";
|
|
17
|
-
import { RecordsReducer } from "./state/slice/RecordsSlice.js";
|
|
18
|
+
import { getRecords, RecordsReducer } from "./state/slice/RecordsSlice.js";
|
|
18
19
|
import { RemovedRecordsReducer } from "./state/slice/RemovedRecordsSlice.js";
|
|
19
20
|
import { getSelection, hasNext, hasPrevious, SelectionReducer } from "./state/slice/SelectionSlice.js";
|
|
20
21
|
import { getField, getMetadata, UnitMetadataReducer } from "./state/slice/UnitMetadataSlice.js";
|
|
21
22
|
import { getBlockingWaitingChanges, getWaitingChangePromisses, isWaiting, WaitingChangesReducer } from "./state/slice/WaitingChangesSlice.js";
|
|
22
23
|
import { canRedo, canUndo, HistReducer } from "./state/HistReducer.js";
|
|
24
|
+
import { getFormattedValue } from "./formatting/PrettyFormatter.js";
|
|
25
|
+
import { v4 as uuid } from "uuid"
|
|
26
|
+
import { DataUnitStorage } from "./DataUnitStorage.js";
|
|
27
|
+
|
|
28
|
+
const DEFAULT_DATAUNIT_NAME = "dataunit";
|
|
23
29
|
|
|
24
30
|
/***
|
|
25
31
|
* `DataUnit`: Atua como uma camada de abstração entre o back-end e a interface do usuário.
|
|
26
32
|
*/
|
|
27
33
|
export default class DataUnit {
|
|
28
|
-
|
|
34
|
+
private _uuid: string;
|
|
29
35
|
private _name: string;
|
|
30
36
|
private _observers: Array<(action: DataUnitAction) => void>;
|
|
31
37
|
private _sortingProvider?: SortingProvider;
|
|
@@ -35,16 +41,16 @@ export default class DataUnit {
|
|
|
35
41
|
private _pageSize: number;
|
|
36
42
|
private _childByName = new Map<string, DataUnit>();
|
|
37
43
|
private _parentDataUnit: DataUnit | undefined;
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
|
|
40
45
|
public metadataLoader?: (dataUnit: DataUnit) => Promise<UnitMetadata>;
|
|
41
46
|
public dataLoader?: (dataUnit: DataUnit, request: LoadDataRequest) => Promise<LoadDataResponse>;
|
|
42
47
|
public saveLoader?: (dataUnit: DataUnit, changes: Array<Change>) => Promise<Array<SavedRecord>>;
|
|
43
48
|
public removeLoader?: (dataUnit: DataUnit, recordIds: Array<string>) => Promise<Array<string>>;
|
|
44
49
|
public recordLoader?: (dataUnit: DataUnit, recordIds: Array<string>) => Promise<Array<Record>>;
|
|
45
50
|
|
|
46
|
-
constructor(name: string, parentDataUnit?: DataUnit) {
|
|
47
|
-
this.
|
|
51
|
+
constructor(name: string = DEFAULT_DATAUNIT_NAME, parentDataUnit?: DataUnit) {
|
|
52
|
+
this._uuid = uuid()
|
|
53
|
+
this._name = `${name}?uuid=${this._uuid}`;
|
|
48
54
|
this._pageSize = 0;
|
|
49
55
|
this._stateManager = new StateManager(
|
|
50
56
|
[
|
|
@@ -66,7 +72,22 @@ export default class DataUnit {
|
|
|
66
72
|
this._interceptors = [];
|
|
67
73
|
this._parentDataUnit = parentDataUnit;
|
|
68
74
|
this._parentDataUnit?.subscribe(this.onDataUnitParentEvent);
|
|
75
|
+
DataUnitStorage.put(this);
|
|
76
|
+
}
|
|
69
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Desfaz vinculos do DataUnit. Chamado quando o DU não é mais necessário.
|
|
80
|
+
*/
|
|
81
|
+
public release(){
|
|
82
|
+
DataUnitStorage.remove(this._name);
|
|
83
|
+
if(this._parentDataUnit){
|
|
84
|
+
this._parentDataUnit.unsubscribe(this.onDataUnitParentEvent);
|
|
85
|
+
this._parentDataUnit.removeChildDataunit(this._name);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public get dataUnitId(): string{
|
|
90
|
+
return this._uuid;
|
|
70
91
|
}
|
|
71
92
|
|
|
72
93
|
/**
|
|
@@ -104,14 +125,14 @@ export default class DataUnit {
|
|
|
104
125
|
private onDataUnitParentEvent = (action: DataUnitAction) => {
|
|
105
126
|
switch(action.type){
|
|
106
127
|
case Action.SELECTION_CHANGED:
|
|
107
|
-
this.
|
|
128
|
+
if(this._parentDataUnit?.hasCopiedRecord() || this._parentDataUnit?.hasNewRecord()) return;
|
|
108
129
|
this.loadData();
|
|
109
130
|
break;
|
|
110
131
|
case Action.DATA_LOADED:
|
|
111
132
|
if(this._parentDataUnit?.getSelectedRecord() == undefined){
|
|
112
133
|
this.clearDataUnit();
|
|
113
134
|
}
|
|
114
|
-
break;
|
|
135
|
+
break;
|
|
115
136
|
}
|
|
116
137
|
}
|
|
117
138
|
|
|
@@ -137,11 +158,24 @@ export default class DataUnit {
|
|
|
137
158
|
*
|
|
138
159
|
* @param request - Dados da requisição para carregamento dos registros.
|
|
139
160
|
* @param executionCtx - Contexto de execução do carregamento dos registros do DataUnit.
|
|
161
|
+
* @param checkLastFilter - Habilita a verificação da última requisição, evitando carga desnecessária.
|
|
140
162
|
*
|
|
141
163
|
* @returns - Registros do DataUnit.
|
|
142
164
|
*
|
|
143
165
|
*/
|
|
144
|
-
private executeLoadData(request: LoadDataRequest, executionCtx?: ExecutionContext): Promise<LoadDataResponse> {
|
|
166
|
+
private executeLoadData(request: LoadDataRequest, executionCtx?: ExecutionContext, checkLastFilter?: boolean): Promise<LoadDataResponse> {
|
|
167
|
+
|
|
168
|
+
if(checkLastFilter && this.isSameRequest(request)){
|
|
169
|
+
const paginationInfo = getPaginationInfo(this._stateManager);
|
|
170
|
+
if(paginationInfo){
|
|
171
|
+
const response: LoadDataResponse = {
|
|
172
|
+
records: getRecords(this._stateManager),
|
|
173
|
+
paginationInfo
|
|
174
|
+
};
|
|
175
|
+
return Promise.resolve(response);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
145
179
|
return new Promise(async (resolve, fail) => {
|
|
146
180
|
if (await this.dispatchAction(Action.LOADING_DATA, request, executionCtx)) {
|
|
147
181
|
if (this.dataLoader) {
|
|
@@ -159,6 +193,11 @@ export default class DataUnit {
|
|
|
159
193
|
});
|
|
160
194
|
}
|
|
161
195
|
|
|
196
|
+
private isSameRequest(request: LoadDataRequest): boolean{
|
|
197
|
+
const lastRequest = getCurrentRequest(this._stateManager);
|
|
198
|
+
return ObjectUtils.objectToString(request) === ObjectUtils.objectToString(lastRequest || {});
|
|
199
|
+
}
|
|
200
|
+
|
|
162
201
|
// Loaders
|
|
163
202
|
/**
|
|
164
203
|
*
|
|
@@ -193,11 +232,12 @@ export default class DataUnit {
|
|
|
193
232
|
*
|
|
194
233
|
* @param quickFilter - Filtros a serem aplicados.
|
|
195
234
|
* @param executionCtx - Contexto de execução do carregamento dos registros.
|
|
235
|
+
* @param checkLastFilter - Habilita a verificação da última requisição, evitando carga desnecessária.
|
|
196
236
|
*
|
|
197
237
|
* @returns - Registros requisitados.
|
|
198
238
|
*
|
|
199
239
|
*/
|
|
200
|
-
public async loadData(quickFilter?: QuickFilter, executionCtx?: ExecutionContext): Promise<LoadDataResponse> {
|
|
240
|
+
public async loadData(quickFilter?: QuickFilter, executionCtx?: ExecutionContext, checkLastFilter?: boolean): Promise<LoadDataResponse> {
|
|
201
241
|
|
|
202
242
|
if(this._parentDataUnit && !this._parentDataUnit.getSelectedRecord()){
|
|
203
243
|
if(this.records){
|
|
@@ -207,12 +247,11 @@ export default class DataUnit {
|
|
|
207
247
|
records: []
|
|
208
248
|
});
|
|
209
249
|
}
|
|
210
|
-
|
|
211
250
|
const loadDataRequest: LoadDataRequest = {
|
|
212
251
|
quickFilter,
|
|
213
252
|
filters: this.getFilters(),
|
|
214
253
|
sort: this.getSort(),
|
|
215
|
-
parentRecordId: this.
|
|
254
|
+
parentRecordId: this.getParentRecordId()
|
|
216
255
|
};
|
|
217
256
|
|
|
218
257
|
if (this._pageSize > 0) {
|
|
@@ -220,7 +259,7 @@ export default class DataUnit {
|
|
|
220
259
|
loadDataRequest.offset = 0;
|
|
221
260
|
}
|
|
222
261
|
|
|
223
|
-
return this.executeLoadData(loadDataRequest, executionCtx);
|
|
262
|
+
return this.executeLoadData(loadDataRequest, executionCtx, checkLastFilter);
|
|
224
263
|
}
|
|
225
264
|
|
|
226
265
|
/**
|
|
@@ -279,6 +318,22 @@ export default class DataUnit {
|
|
|
279
318
|
return this.gotoPage(getCurrentPage(this._stateManager) - 1, executionCtx);
|
|
280
319
|
}
|
|
281
320
|
|
|
321
|
+
|
|
322
|
+
private async notifySavingData(executionCtx?: ExecutionContext): Promise<boolean>{
|
|
323
|
+
const notifyPromises: Array<Promise<boolean>> = [];
|
|
324
|
+
Array.from(this._childByName.values()).forEach(
|
|
325
|
+
child => notifyPromises.push(child.notifySavingData(executionCtx))
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
if(isDirty(this._stateManager)){
|
|
329
|
+
notifyPromises.push(this.dispatchAction(Action.SAVING_DATA, undefined, executionCtx));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return new Promise(accept => Promise.all(notifyPromises).then(responses => {
|
|
333
|
+
accept(responses.reduce((previous, current) => previous && current, true));
|
|
334
|
+
}));
|
|
335
|
+
}
|
|
336
|
+
|
|
282
337
|
/**
|
|
283
338
|
*
|
|
284
339
|
* Salva o estado do registro do DataUnit.
|
|
@@ -292,23 +347,32 @@ export default class DataUnit {
|
|
|
292
347
|
const blockingWaitingChanges = getBlockingWaitingChanges(this._stateManager);
|
|
293
348
|
|
|
294
349
|
if (blockingWaitingChanges && blockingWaitingChanges.size > 0) {
|
|
295
|
-
const [
|
|
350
|
+
const [_, waitingChange] = blockingWaitingChanges.entries().next().value;
|
|
296
351
|
return Promise.reject(new WaitingChangeException("Aguardando alteração de campo", (waitingChange as WaitingChange).waitmessage));
|
|
297
352
|
} else {
|
|
298
|
-
if (isDirty(
|
|
353
|
+
if (this.isDirty()) {
|
|
299
354
|
|
|
300
|
-
if (await this.
|
|
355
|
+
if (await this.notifySavingData(executionCtx)) {
|
|
301
356
|
const promisses = getWaitingChangePromisses(this._stateManager);
|
|
302
357
|
return new Promise((resolve, fail) => {
|
|
303
358
|
Promise.all(promisses || []).then(() => {
|
|
304
359
|
if (this.saveLoader) {
|
|
305
|
-
const changes: Array<Change> =
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
360
|
+
const changes: Array<Change> = this.getAllChangesToSave();
|
|
361
|
+
|
|
362
|
+
this.saveLoader(this, changes).then((records) => {
|
|
363
|
+
const recordsByDataUnit = this.getRecordsByDataUnit(records);
|
|
364
|
+
|
|
365
|
+
const dispatchPromisses = [];
|
|
366
|
+
for (const [ownerDataUnitName, splittedRecords] of recordsByDataUnit) {
|
|
367
|
+
const ownerDataUnit = DataUnitStorage.get(ownerDataUnitName);
|
|
368
|
+
const changesByDataUnit = changes.filter(change => change.dataUnit === ownerDataUnitName);
|
|
369
|
+
|
|
370
|
+
if(ownerDataUnit){
|
|
371
|
+
dispatchPromisses.push(ownerDataUnit.dispatchAction(Action.DATA_SAVED, { changes: changesByDataUnit, records: splittedRecords }, executionCtx));
|
|
372
|
+
}
|
|
310
373
|
}
|
|
311
|
-
|
|
374
|
+
Promise.all(dispatchPromisses).then(() => resolve())
|
|
375
|
+
}).catch(cause => {
|
|
312
376
|
const {errorCode} = cause;
|
|
313
377
|
fail(new ErrorException("Erro salvando alterações", cause, errorCode));
|
|
314
378
|
});
|
|
@@ -324,6 +388,44 @@ export default class DataUnit {
|
|
|
324
388
|
return Promise.resolve();
|
|
325
389
|
}
|
|
326
390
|
|
|
391
|
+
/**
|
|
392
|
+
*
|
|
393
|
+
* Retorna as alterações a serem salvas no DataUnit atual.
|
|
394
|
+
*
|
|
395
|
+
*
|
|
396
|
+
*
|
|
397
|
+
* @returns - Mudanças realizadas no DataUnit atual
|
|
398
|
+
*
|
|
399
|
+
*/
|
|
400
|
+
public buildChangesToSave(): Array<Change>{
|
|
401
|
+
return getChangesToSave(this._name, this._stateManager);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
public buildChangesToSaveFromChild(allChanges: Array<Change>, dataUnit: DataUnit): void {
|
|
405
|
+
dataUnit._childByName?.forEach((dataUnitChild: DataUnit) => {
|
|
406
|
+
if (dataUnitChild.isDirty()) {
|
|
407
|
+
allChanges.push(...getChangesToSave(dataUnitChild.name, dataUnitChild._stateManager));
|
|
408
|
+
this.buildChangesToSaveFromChild(allChanges, dataUnitChild);
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
*
|
|
415
|
+
* Retorna todas as alterações do DataUnit a serem salvas, incluindo no nível Master e Detail.
|
|
416
|
+
*
|
|
417
|
+
*
|
|
418
|
+
*
|
|
419
|
+
* @returns - Todas as mudanças realizadas no DataUnit, tanto Master quanto Detail;
|
|
420
|
+
*
|
|
421
|
+
*/
|
|
422
|
+
public getAllChangesToSave(): Array<Change> {
|
|
423
|
+
const allChanges: Array<Change> = [];
|
|
424
|
+
allChanges.push(...this.buildChangesToSave());
|
|
425
|
+
this.buildChangesToSaveFromChild(allChanges, this);
|
|
426
|
+
return allChanges;
|
|
427
|
+
}
|
|
428
|
+
|
|
327
429
|
/**
|
|
328
430
|
*
|
|
329
431
|
* Remove o registro selecionado.
|
|
@@ -423,6 +525,29 @@ export default class DataUnit {
|
|
|
423
525
|
return toString(descriptor?.dataType, value);
|
|
424
526
|
}
|
|
425
527
|
|
|
528
|
+
/**
|
|
529
|
+
*
|
|
530
|
+
* Formata o valor do campo considerando as informações do descriptor.
|
|
531
|
+
* Diferente do método "valueToString" que retorna o dado em valor textual,
|
|
532
|
+
* getFormattedValue retorna uma informação amigável ao usuário, geralmente
|
|
533
|
+
* usada na interface.
|
|
534
|
+
*
|
|
535
|
+
* @param fieldName - Nome do campo utilizado do qual se quer obter valor.
|
|
536
|
+
*
|
|
537
|
+
* @returns - Valor formatado.
|
|
538
|
+
*
|
|
539
|
+
*/
|
|
540
|
+
public getFormattedValue(fieldName: string): string {
|
|
541
|
+
|
|
542
|
+
const descriptor = this.getField(fieldName);
|
|
543
|
+
const value = this.getFieldValue(fieldName);
|
|
544
|
+
if(value == undefined){
|
|
545
|
+
return "";
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return getFormattedValue(value, descriptor);
|
|
549
|
+
}
|
|
550
|
+
|
|
426
551
|
/**
|
|
427
552
|
*
|
|
428
553
|
* Adiciona um interceptor correspondente a uma ação do DataUnit para fazer um processamento customizado.
|
|
@@ -588,7 +713,7 @@ export default class DataUnit {
|
|
|
588
713
|
*
|
|
589
714
|
*/
|
|
590
715
|
public addRecord(executionCtx?: ExecutionContext): void {
|
|
591
|
-
this.dispatchAction(Action.RECORDS_ADDED, prepareAddedRecordId(this._stateManager, [{}], this.
|
|
716
|
+
this.dispatchAction(Action.RECORDS_ADDED, prepareAddedRecordId(this._stateManager, [{}], this.getParentRecordId()), executionCtx);
|
|
592
717
|
}
|
|
593
718
|
|
|
594
719
|
/**
|
|
@@ -601,9 +726,35 @@ export default class DataUnit {
|
|
|
601
726
|
public copySelected(executionCtx?: ExecutionContext): void {
|
|
602
727
|
const selectedRecords = this.getSelectedRecords();
|
|
603
728
|
if (selectedRecords) {
|
|
604
|
-
this.dispatchAction(Action.RECORDS_COPIED, prepareCopiedRecord(this._stateManager, selectedRecords, this.
|
|
729
|
+
this.dispatchAction(Action.RECORDS_COPIED, prepareCopiedRecord(this._stateManager, selectedRecords, this.getParentRecordId()), executionCtx);
|
|
605
730
|
}
|
|
606
731
|
}
|
|
732
|
+
|
|
733
|
+
private getRecordsByDataUnit(records: Array<Record>): Map<string, Array<Record>> {
|
|
734
|
+
const recordsMap = new Map<string, Array<Record>>();
|
|
735
|
+
|
|
736
|
+
records.forEach(record => {
|
|
737
|
+
const { __owner__dataunit__name__: ownerDataUnitName } = record;
|
|
738
|
+
|
|
739
|
+
if(ownerDataUnitName){
|
|
740
|
+
let changes = recordsMap.get(ownerDataUnitName);
|
|
741
|
+
|
|
742
|
+
if(!changes){
|
|
743
|
+
changes = [];
|
|
744
|
+
recordsMap.set(ownerDataUnitName, changes);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
changes.push(record);
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
return recordsMap;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
private getParentRecordId(){
|
|
755
|
+
const parentRecord = this._parentDataUnit?.getSelectedRecord();
|
|
756
|
+
return parentRecord?.__record__id__;
|
|
757
|
+
}
|
|
607
758
|
|
|
608
759
|
/**
|
|
609
760
|
*
|
|
@@ -836,8 +987,13 @@ export default class DataUnit {
|
|
|
836
987
|
* @param executionCtx - Contexto de execução do cancelamento da seleção dos registros.
|
|
837
988
|
*
|
|
838
989
|
*/
|
|
839
|
-
public cancelEdition(executionCtx?: ExecutionContext): void {
|
|
840
|
-
|
|
990
|
+
public async cancelEdition(executionCtx?: ExecutionContext, fromParent?: boolean): Promise<void> {
|
|
991
|
+
await this.dispatchAction(Action.EDITION_CANCELED, {fromParent}, executionCtx);
|
|
992
|
+
for (const [key, dataUnit] of this._childByName) {
|
|
993
|
+
if(dataUnit.isDirty()){
|
|
994
|
+
dataUnit.cancelEdition(undefined, true);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
841
997
|
}
|
|
842
998
|
|
|
843
999
|
/**
|
|
@@ -846,9 +1002,23 @@ export default class DataUnit {
|
|
|
846
1002
|
*
|
|
847
1003
|
* @returns Verdadeiro se existir alterações pendentes.
|
|
848
1004
|
*
|
|
849
|
-
*/
|
|
1005
|
+
*/
|
|
850
1006
|
public isDirty(): boolean {
|
|
851
|
-
return isDirty(this._stateManager);
|
|
1007
|
+
return isDirty(this._stateManager) || this.childrenIsDirty();
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
*
|
|
1012
|
+
* Retorna se existe algum DataUnit detail com alterações pendentes.
|
|
1013
|
+
*
|
|
1014
|
+
* @returns Verdadeiro se existir alterações pendentes em algum DataUnit detail.
|
|
1015
|
+
*
|
|
1016
|
+
*/
|
|
1017
|
+
private childrenIsDirty():boolean {
|
|
1018
|
+
for (let [key, dataUnitChild] of this._childByName) {
|
|
1019
|
+
if(dataUnitChild.isDirty()) return true;
|
|
1020
|
+
}
|
|
1021
|
+
return false;
|
|
852
1022
|
}
|
|
853
1023
|
|
|
854
1024
|
/**
|
|
@@ -909,6 +1079,38 @@ export default class DataUnit {
|
|
|
909
1079
|
return false;
|
|
910
1080
|
}
|
|
911
1081
|
|
|
1082
|
+
/**
|
|
1083
|
+
*
|
|
1084
|
+
* Retorna se existe pelo menos um registro novo.
|
|
1085
|
+
*
|
|
1086
|
+
* @returns Verdadeiro se algum registro foi adicionado.
|
|
1087
|
+
*
|
|
1088
|
+
*/
|
|
1089
|
+
public hasNewRecord(): boolean {
|
|
1090
|
+
const newRecords = getAddedRecords(this._stateManager);
|
|
1091
|
+
return newRecords && newRecords.length > 0;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
*
|
|
1096
|
+
* Retorna se existe pelo menos um registro novo.
|
|
1097
|
+
*
|
|
1098
|
+
* @returns Verdadeiro se algum registro foi adicionado.
|
|
1099
|
+
*
|
|
1100
|
+
*/
|
|
1101
|
+
public hasCopiedRecord(): boolean {
|
|
1102
|
+
const newRecords = getAddedRecords(this._stateManager);
|
|
1103
|
+
if(newRecords == undefined){
|
|
1104
|
+
return false;
|
|
1105
|
+
}
|
|
1106
|
+
for(let record of newRecords){
|
|
1107
|
+
if (record['__record__source__id__'] != undefined) {
|
|
1108
|
+
return true
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return false;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
912
1114
|
/**
|
|
913
1115
|
*
|
|
914
1116
|
* Retorna se a informação do estado anterior está salva, permitindo desfazer a ação.
|
|
@@ -1027,6 +1229,7 @@ export default class DataUnit {
|
|
|
1027
1229
|
*/
|
|
1028
1230
|
private doDispatchAction(action: DataUnitAction): void {
|
|
1029
1231
|
this._stateManager.process(action);
|
|
1232
|
+
this?._parentDataUnit?.dispatchAction(Action.CHILD_CHANGED, { srcAction: action, srcDataUnit: this });
|
|
1030
1233
|
this._observers.forEach(f => {
|
|
1031
1234
|
/*
|
|
1032
1235
|
if some observer throws exceptions,
|
|
@@ -1052,6 +1255,17 @@ export default class DataUnit {
|
|
|
1052
1255
|
this._childByName.set(name, detail);
|
|
1053
1256
|
return detail;
|
|
1054
1257
|
}
|
|
1258
|
+
|
|
1259
|
+
/**
|
|
1260
|
+
*
|
|
1261
|
+
* Remove um dataunit filho.
|
|
1262
|
+
*
|
|
1263
|
+
* @param name - Nome do dataunit filho.
|
|
1264
|
+
*
|
|
1265
|
+
*/
|
|
1266
|
+
public removeChildDataunit(name:string){
|
|
1267
|
+
this._childByName.delete(name);
|
|
1268
|
+
}
|
|
1055
1269
|
|
|
1056
1270
|
/**
|
|
1057
1271
|
*
|
|
@@ -1196,7 +1410,9 @@ export interface DUActionInterceptor {
|
|
|
1196
1410
|
|
|
1197
1411
|
export interface Record {
|
|
1198
1412
|
__record__id__: string;
|
|
1413
|
+
__record__label__?: string;
|
|
1199
1414
|
__parent__record__id__?: string;
|
|
1415
|
+
__owner__dataunit__name__?: string;
|
|
1200
1416
|
[key: string]: any;
|
|
1201
1417
|
}
|
|
1202
1418
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import DataUnit from "./DataUnit.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Classe responsável por armazenar instâncias de DataUnit.
|
|
5
|
+
*/
|
|
6
|
+
export class DataUnitStorage {
|
|
7
|
+
private static dataUnitMap: WeakMap<{ key: string }, DataUnit> = new WeakMap();
|
|
8
|
+
private static keyMap: Map<string, { key: string }> = new Map();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Retorna uma instância de DataUnit armazenada no DataUnitStorage.
|
|
12
|
+
* @param {string} dataUnitName - O nome do DataUnit.
|
|
13
|
+
* @returns {DataUnit | undefined} - A instância do DataUnit ou undefined caso não seja encontrada.
|
|
14
|
+
*/
|
|
15
|
+
static get(dataUnitName: string): DataUnit | undefined {
|
|
16
|
+
const key = this.keyMap.get(dataUnitName);
|
|
17
|
+
if (key) return this.dataUnitMap.get(key);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Armazena uma instância de DataUnit no DataUnitStorage.
|
|
22
|
+
* @param {DataUnit} dataUnit - A instância de DataUnit a ser armazenada.
|
|
23
|
+
*/
|
|
24
|
+
static put(dataUnit: DataUnit): void {
|
|
25
|
+
const key = { key: dataUnit.name };
|
|
26
|
+
this.dataUnitMap.set(key, dataUnit);
|
|
27
|
+
this.keyMap.set(dataUnit.name, key);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Remove uma instância de DataUnit do DataUnitStorage.
|
|
32
|
+
* @param {DataUnit | string} dataUnit - A instância de DataUnit ou o nome do DataUnit a ser removido.
|
|
33
|
+
*/
|
|
34
|
+
static remove(dataUnit: DataUnit | string): void {
|
|
35
|
+
const key = typeof dataUnit === 'string' ? this.keyMap.get(dataUnit) : this.keyMap.get(dataUnit.name);
|
|
36
|
+
const dataUnitKey = typeof dataUnit === 'string' ? dataUnit : dataUnit.name;
|
|
37
|
+
|
|
38
|
+
if (key) {
|
|
39
|
+
this.dataUnitMap.delete(key);
|
|
40
|
+
this.keyMap.delete(dataUnitKey);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import DateUtils from "../../utils/DateUtils.js";
|
|
2
|
+
import { MaskFormatter } from "../../utils/MaskFormatter.js";
|
|
3
|
+
import { NumberUtils } from "../../utils/NumberUtils.js";
|
|
4
|
+
import { DataType, toString } from "../metadata/DataType.js";
|
|
5
|
+
import { FieldDescriptor, UserInterface } from "../metadata/UnitMetadata.js";
|
|
6
|
+
|
|
7
|
+
export const getFormattedValue = (value: any, descriptor?: FieldDescriptor) => {
|
|
8
|
+
if(descriptor?.dataType === DataType.OBJECT && Object.hasOwn(value, "value")){
|
|
9
|
+
return getSearchFormat(value, descriptor);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if(descriptor?.userInterface === UserInterface.OPTIONSELECTOR){
|
|
13
|
+
return getOptionFormat(value, descriptor);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if(descriptor?.dataType === DataType.BOOLEAN){
|
|
17
|
+
return value ? "Sim" : "Não";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if(descriptor?.dataType === DataType.NUMBER){
|
|
21
|
+
return getNumberFormat(value, descriptor);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if(descriptor?.userInterface === UserInterface.DATE){
|
|
25
|
+
return DateUtils.formatDate(value);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if(descriptor?.userInterface === UserInterface.TIME){
|
|
29
|
+
return DateUtils.formatTime(value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if(descriptor?.userInterface === UserInterface.DATETIME){
|
|
33
|
+
return DateUtils.formatDateTime(value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const mask = getMask(value, descriptor);
|
|
37
|
+
if(mask != undefined){
|
|
38
|
+
try{
|
|
39
|
+
return new MaskFormatter(mask).format(value);
|
|
40
|
+
} catch(error){
|
|
41
|
+
console.warn(`Erro ao formatar valor: ${value}. Mascara: ${mask}. Erro: ${error}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return toString(descriptor?.dataType, value);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const getNumberFormat = (value: any, descriptor: FieldDescriptor) => {
|
|
49
|
+
if(descriptor.userInterface === UserInterface.INTEGERNUMBER){
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
const precision = Number(descriptor.properties?.precision || 2);
|
|
53
|
+
const prettyPrecision = Number(descriptor.properties?.prettyPrecision || precision);
|
|
54
|
+
|
|
55
|
+
return NumberUtils.format(value, precision, prettyPrecision);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const getSearchFormat = (value: any, descriptor: FieldDescriptor) => {
|
|
59
|
+
const {value: codeValue, label} = value;
|
|
60
|
+
if(descriptor.userInterface === UserInterface.SEARCH){
|
|
61
|
+
return label ? `${codeValue} - ${label}` : codeValue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return label ? label : codeValue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const getOptionFormat = (value: any, descriptor: FieldDescriptor) => {
|
|
68
|
+
const prop: string | Array<{label: string, value: string}> = descriptor.properties?.options;
|
|
69
|
+
let options: any;
|
|
70
|
+
|
|
71
|
+
if (typeof prop === "string") {
|
|
72
|
+
options = JSON.parse(prop);
|
|
73
|
+
} else {
|
|
74
|
+
options = {};
|
|
75
|
+
prop.forEach(opt => options[opt.label] = opt.value);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if(options && Object.hasOwn(options, value)){
|
|
79
|
+
return options[value];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
const getMask = (value: string, descriptor: FieldDescriptor | undefined): string | undefined => {
|
|
87
|
+
|
|
88
|
+
if(descriptor == undefined){
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const mask = descriptor.properties?.mask;
|
|
93
|
+
if(mask == undefined){
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if(mask === "cgc_cpf"){
|
|
98
|
+
return value.length === 11 ? MaskFormatter.DEFAULT_MASKS.cpf : MaskFormatter.DEFAULT_MASKS.cnpj;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if(Object.hasOwn(MaskFormatter.DEFAULT_MASKS, mask)){
|
|
102
|
+
return MaskFormatter.DEFAULT_MASKS[mask];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return mask;
|
|
106
|
+
}
|
|
@@ -25,6 +25,7 @@ class ChangesReducerImpl implements ActionReducer{
|
|
|
25
25
|
delete rChanges.__record__id__;
|
|
26
26
|
delete rChanges.__parent__record__id__;
|
|
27
27
|
delete rChanges.__record__source__id__;
|
|
28
|
+
delete rChanges.__record__label__;
|
|
28
29
|
delete rChanges.__copy__;
|
|
29
30
|
changes.set(r.__record__id__, rChanges);
|
|
30
31
|
}
|
|
@@ -89,7 +90,7 @@ export const getChangesToSave = (dataUnit: string, stateManager: StateManager):
|
|
|
89
90
|
if(changes){
|
|
90
91
|
const c = changes.get(r.__record__id__);
|
|
91
92
|
if (c) {
|
|
92
|
-
result.push(new Change(dataUnit, r, c, ChangeOperation.UPDATE));
|
|
93
|
+
result.push(new Change(dataUnit, r, c, ChangeOperation.UPDATE, r.__record__source__id__));
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
});
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { RequestMetadata } from "./http/RequestMetadata.js";
|
|
|
11
11
|
import { AuthorizedServiceCaller } from "./http/AuthorizedServiceCaller.js";
|
|
12
12
|
import DataUnit, {SavedRecord, Record, Change, ChangeOperation, DUActionInterceptor, WaitingChange, PageRequest, QuickFilter} from "./dataunit/DataUnit.js";
|
|
13
13
|
import { DataType } from "./dataunit/metadata/DataType.js";
|
|
14
|
-
import { UnitMetadata, FieldDescriptor, UserInterface, Sort, SortMode, SortingProvider, Filter, DependencyType } from "./dataunit/metadata/UnitMetadata.js";
|
|
14
|
+
import { UnitMetadata, FieldDescriptor, UserInterface, Sort, SortMode, SortingProvider, Filter, DependencyType, ChildDescriptor, ChildLink } from "./dataunit/metadata/UnitMetadata.js";
|
|
15
15
|
import { DataUnitAction, Action, ExecutionContext } from "./dataunit/state/action/DataUnitAction.js";
|
|
16
16
|
import ApplicationContext from "./utils/ApplicationContext.js";
|
|
17
17
|
import ReadyUtil from "./utils/ReadyUtil.js";
|
|
@@ -26,6 +26,8 @@ import { LoadDataResponse } from "./dataunit/loading/LoadDataResponse.js";
|
|
|
26
26
|
import { ElementIDUtils, IElementIDInfo } from "./utils/ElementIDUtils.js";
|
|
27
27
|
import { UserAgentUtils } from "./utils/UserAgentUtils/index.js";
|
|
28
28
|
import { JSUtils } from "./utils/JSUtils.js";
|
|
29
|
+
import { DataUnitStorage } from "./dataunit/DataUnitStorage.js";
|
|
30
|
+
|
|
29
31
|
|
|
30
32
|
/*Classes públicas no pacote*/
|
|
31
33
|
export {
|
|
@@ -41,6 +43,7 @@ export {
|
|
|
41
43
|
RequestMetadata,
|
|
42
44
|
AuthorizedServiceCaller,
|
|
43
45
|
DataUnit,
|
|
46
|
+
DataUnitStorage,
|
|
44
47
|
Record,
|
|
45
48
|
SavedRecord,
|
|
46
49
|
DataType,
|
|
@@ -48,6 +51,8 @@ export {
|
|
|
48
51
|
FieldDescriptor,
|
|
49
52
|
UserInterface,
|
|
50
53
|
DependencyType,
|
|
54
|
+
ChildDescriptor,
|
|
55
|
+
ChildLink,
|
|
51
56
|
DataUnitAction,
|
|
52
57
|
Action,
|
|
53
58
|
Change,
|