tuain-ng-forms-lib 17.3.6 → 17.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +280 -335
- package/esm2022/lib/classes/forms/action.mjs +3 -3
- package/esm2022/lib/classes/forms/element.mjs +2 -2
- package/esm2022/lib/classes/forms/field.mjs +4 -7
- package/esm2022/lib/classes/forms/form.mjs +4 -6
- package/esm2022/lib/classes/forms/piece-propagate.mjs +1 -1
- package/esm2022/lib/classes/forms/piece.mjs +2 -2
- package/esm2022/lib/classes/forms/section.mjs +2 -2
- package/esm2022/lib/classes/forms/subsection.mjs +1 -1
- package/esm2022/lib/classes/forms/table/action.mjs +1 -1
- package/esm2022/lib/classes/forms/table/column.mjs +1 -1
- package/esm2022/lib/classes/forms/table/row-data.mjs +3 -5
- package/esm2022/lib/classes/forms/table/table.mjs +2 -2
- package/esm2022/lib/components/elements/layout/piece.component.mjs +3 -3
- package/esm2022/lib/components/elements/tables/table-record-action.component.mjs +1 -1
- package/esm2022/lib/components/elements/tables/table-record-field.component.mjs +1 -1
- package/esm2022/lib/components/forms/basic-form.mjs +23 -24
- package/esm2022/lib/interfaces/action.interface.mjs +2 -0
- package/esm2022/lib/interfaces/field.interface.mjs +2 -0
- package/esm2022/lib/interfaces/form-config.interface.mjs +1 -1
- package/esm2022/lib/interfaces/form.interface.mjs +2 -0
- package/esm2022/lib/interfaces/index.mjs +9 -1
- package/esm2022/lib/interfaces/piece.interface.mjs +2 -0
- package/esm2022/lib/interfaces/section.interface.mjs +2 -0
- package/esm2022/lib/interfaces/sse-live-connection.interface.mjs +2 -0
- package/esm2022/lib/interfaces/table.interface.mjs +2 -0
- package/esm2022/lib/services/file-manager.service.mjs +5 -5
- package/esm2022/lib/services/form-manager.service.mjs +5 -5
- package/esm2022/lib/services/icon-dictionary.service.mjs +1 -1
- package/esm2022/lib/services/sse-live-connection.service.mjs +165 -0
- package/esm2022/lib/tokens/sse-live-connection.token.mjs +7 -0
- package/esm2022/public-api.mjs +3 -1
- package/fesm2022/tuain-ng-forms-lib.mjs +215 -55
- package/fesm2022/tuain-ng-forms-lib.mjs.map +1 -1
- package/lib/classes/forms/action.d.ts +3 -2
- package/lib/classes/forms/element.d.ts +2 -1
- package/lib/classes/forms/field.d.ts +5 -4
- package/lib/classes/forms/form.d.ts +4 -2
- package/lib/classes/forms/piece-propagate.d.ts +2 -1
- package/lib/classes/forms/piece.d.ts +4 -3
- package/lib/classes/forms/section.d.ts +3 -2
- package/lib/classes/forms/subsection.d.ts +2 -1
- package/lib/classes/forms/table/action.d.ts +2 -1
- package/lib/classes/forms/table/column.d.ts +2 -1
- package/lib/classes/forms/table/row-data.d.ts +3 -2
- package/lib/classes/forms/table/table.d.ts +3 -1
- package/lib/components/elements/layout/piece.component.d.ts +2 -2
- package/lib/components/forms/basic-form.d.ts +4 -4
- package/lib/interfaces/action.interface.d.ts +23 -0
- package/lib/interfaces/field.interface.d.ts +70 -0
- package/lib/interfaces/form-config.interface.d.ts +24 -16
- package/lib/interfaces/form.interface.d.ts +106 -0
- package/lib/interfaces/index.d.ts +7 -0
- package/lib/interfaces/piece.interface.d.ts +67 -0
- package/lib/interfaces/section.interface.d.ts +56 -0
- package/lib/interfaces/sse-live-connection.interface.d.ts +59 -0
- package/lib/interfaces/table.interface.d.ts +139 -0
- package/lib/services/file-manager.service.d.ts +4 -4
- package/lib/services/form-manager.service.d.ts +3 -3
- package/lib/services/icon-dictionary.service.d.ts +1 -1
- package/lib/services/sse-live-connection.service.d.ts +48 -0
- package/lib/tokens/sse-live-connection.token.d.ts +7 -0
- package/package.json +2 -1
- package/public-api.d.ts +2 -0
|
@@ -4,9 +4,16 @@ import { Subject, takeUntil, BehaviorSubject, ReplaySubject } from 'rxjs';
|
|
|
4
4
|
import yn from 'yn';
|
|
5
5
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
6
6
|
import { nanoid } from 'nanoid';
|
|
7
|
+
import { fetchEventSource } from '@microsoft/fetch-event-source';
|
|
7
8
|
|
|
8
9
|
const ICON_RESOLVER = new InjectionToken('ICON_RESOLVER');
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Token con el que cada app provee su configuración del stream SSE.
|
|
13
|
+
* Sigue el mismo patrón que `ICON_RESOLVER`.
|
|
14
|
+
*/
|
|
15
|
+
const SSE_LIVE_CONNECTION_CONFIG = new InjectionToken('SSE_LIVE_CONNECTION_CONFIG');
|
|
16
|
+
|
|
10
17
|
class FormPiece {
|
|
11
18
|
destroy$ = new Subject();
|
|
12
19
|
_formState = '';
|
|
@@ -117,7 +124,7 @@ class FormPiece {
|
|
|
117
124
|
set disabled(disabled) { this.enabled = !disabled; }
|
|
118
125
|
enable() { this.enabled = true; }
|
|
119
126
|
disable() { this.enabled = false; }
|
|
120
|
-
formStateChangeCustomSubscribe(
|
|
127
|
+
formStateChangeCustomSubscribe(_form, _formChangeSubject) { }
|
|
121
128
|
formStateChange(state) {
|
|
122
129
|
if (state) {
|
|
123
130
|
this._formState = state;
|
|
@@ -249,7 +256,7 @@ class FormElement extends FormPiecePropagate {
|
|
|
249
256
|
}
|
|
250
257
|
}
|
|
251
258
|
catch (e) {
|
|
252
|
-
console.
|
|
259
|
+
console.error(`Atributo ${attrName} no presente o valor ${value} inconsistente. ${e}`);
|
|
253
260
|
}
|
|
254
261
|
}
|
|
255
262
|
isField() { return this.elementType === ElementType.Field; }
|
|
@@ -566,7 +573,7 @@ class FieldDescriptor extends FormElement {
|
|
|
566
573
|
/**
|
|
567
574
|
* @deprecated Since v17. Use options instead. Will be removed in v19.
|
|
568
575
|
*/
|
|
569
|
-
setFieldOptions(newOptions) {
|
|
576
|
+
setFieldOptions(newOptions) { this.options = newOptions; }
|
|
570
577
|
/**
|
|
571
578
|
* @deprecated Since v17. Use intrinsicErrorMessage instead. Will be removed in v19.
|
|
572
579
|
*/
|
|
@@ -592,7 +599,6 @@ class FieldDescriptor extends FormElement {
|
|
|
592
599
|
&& Array.isArray(fieldCurrentValue) && fieldCurrentValue.length === 0) {
|
|
593
600
|
return true;
|
|
594
601
|
}
|
|
595
|
-
;
|
|
596
602
|
if (this._fieldType === this._formConfig.fieldTypes.phone) {
|
|
597
603
|
if (!Array.isArray(fieldCurrentValue)) {
|
|
598
604
|
return true;
|
|
@@ -602,7 +608,6 @@ class FieldDescriptor extends FormElement {
|
|
|
602
608
|
}
|
|
603
609
|
return false;
|
|
604
610
|
}
|
|
605
|
-
;
|
|
606
611
|
const arrayFieldTypes = this._formConfig.arrayFieldTypes ?? null;
|
|
607
612
|
if (arrayFieldTypes && Object.keys(arrayFieldTypes).includes(this._fieldType)) {
|
|
608
613
|
let arraySize = arrayFieldTypes[this._fieldType];
|
|
@@ -612,7 +617,6 @@ class FieldDescriptor extends FormElement {
|
|
|
612
617
|
}
|
|
613
618
|
return (Array.isArray(fieldCurrentValue) && fieldCurrentValue.length < arraySize);
|
|
614
619
|
}
|
|
615
|
-
;
|
|
616
620
|
return fieldCurrentValue === '';
|
|
617
621
|
}
|
|
618
622
|
notifyEditionPartial() {
|
|
@@ -646,14 +650,14 @@ class FieldDescriptor extends FormElement {
|
|
|
646
650
|
if (intrinsicValidation && fieldValue && this._minValue && fieldValue < this._minValue) {
|
|
647
651
|
intrinsicValidation = false;
|
|
648
652
|
const formatedMinValue = !isNaN(+this._minValue)
|
|
649
|
-
? this._minValue.toLocaleString('es-CO')
|
|
653
|
+
? Number(this._minValue).toLocaleString('es-CO')
|
|
650
654
|
: this._minValue;
|
|
651
655
|
this.setError('99', `El valor de ${this.title} no puede ser inferior a ${formatedMinValue}`);
|
|
652
656
|
}
|
|
653
657
|
if (intrinsicValidation && fieldValue && this._maxValue && fieldValue > this._maxValue) {
|
|
654
658
|
intrinsicValidation = false;
|
|
655
659
|
const formatedMaxValue = !isNaN(+this._maxValue)
|
|
656
|
-
? this._maxValue.toLocaleString('es-CO')
|
|
660
|
+
? Number(this._maxValue).toLocaleString('es-CO')
|
|
657
661
|
: this._maxValue;
|
|
658
662
|
this.setError('99', `El valor de ${this.title} no puede ser superior a ${formatedMaxValue}`);
|
|
659
663
|
}
|
|
@@ -812,7 +816,7 @@ class FormAction extends FormElement {
|
|
|
812
816
|
}
|
|
813
817
|
}
|
|
814
818
|
}
|
|
815
|
-
updateRestrictedVisibility(
|
|
819
|
+
updateRestrictedVisibility(_event) {
|
|
816
820
|
const newVisible = this._absoluteVisible && this.viewOnState(this._formState);
|
|
817
821
|
(this._visible !== newVisible) && this.setVisibility(newVisible);
|
|
818
822
|
}
|
|
@@ -839,7 +843,7 @@ class FormAction extends FormElement {
|
|
|
839
843
|
this[propertyName] = receivedAction[propertyName];
|
|
840
844
|
}
|
|
841
845
|
catch (e) {
|
|
842
|
-
console.
|
|
846
|
+
console.error(`Error actualizando la propiedad ${propertyName} de la acción ${this.actionCode}. ${e}`);
|
|
843
847
|
}
|
|
844
848
|
}
|
|
845
849
|
}
|
|
@@ -1008,7 +1012,7 @@ class RecordFormSection extends FormPiecePropagate {
|
|
|
1008
1012
|
}
|
|
1009
1013
|
get title() { return this._sectionTitle; }
|
|
1010
1014
|
set title(title) { this._sectionTitle = title; }
|
|
1011
|
-
getVisibleSubsections(
|
|
1015
|
+
getVisibleSubsections(_state) {
|
|
1012
1016
|
return this._subSections.filter(subSection => subSection.visible);
|
|
1013
1017
|
}
|
|
1014
1018
|
getSubsection(subSectionCode) {
|
|
@@ -1223,7 +1227,6 @@ class TableRecordData {
|
|
|
1223
1227
|
const rawRecordData = recordData.filter(fieldData => fieldData.fieldCode && fieldNames.includes(fieldData.fieldCode));
|
|
1224
1228
|
rawRecordData.forEach(fieldData => {
|
|
1225
1229
|
const { fieldCode, fieldValue } = fieldData;
|
|
1226
|
-
const fieldDef = recordDefinition.find(column => column.fieldCode === fieldCode);
|
|
1227
1230
|
this.recordData[fieldCode] = fieldValue ?? '';
|
|
1228
1231
|
if (fieldCode === selectionFieldName) {
|
|
1229
1232
|
this.selected = fieldValue;
|
|
@@ -1234,7 +1237,6 @@ class TableRecordData {
|
|
|
1234
1237
|
const fields = Object.keys(recordData);
|
|
1235
1238
|
fields.forEach(fieldCode => {
|
|
1236
1239
|
const fieldValue = recordData[fieldCode];
|
|
1237
|
-
const fieldDef = recordDefinition.find(column => column.fieldCode === fieldCode);
|
|
1238
1240
|
this.recordData[fieldCode] = fieldValue ?? '';
|
|
1239
1241
|
});
|
|
1240
1242
|
}
|
|
@@ -1257,7 +1259,7 @@ class TableRecordData {
|
|
|
1257
1259
|
let wordIsPresent = false;
|
|
1258
1260
|
for (const fieldCode in this.recordData) {
|
|
1259
1261
|
const columnDef = columnObj?.[fieldCode];
|
|
1260
|
-
if (columnDef?.searchable &&
|
|
1262
|
+
if (columnDef?.searchable && Object.prototype.hasOwnProperty.call(this.recordData, fieldCode)) {
|
|
1261
1263
|
const term = word.toUpperCase();
|
|
1262
1264
|
let fieldValue;
|
|
1263
1265
|
if (columnDef.fieldType.toUpperCase().includes('DATE')) {
|
|
@@ -1284,7 +1286,7 @@ class TableRecordData {
|
|
|
1284
1286
|
}
|
|
1285
1287
|
for (const condition of columnFilters) {
|
|
1286
1288
|
const { fieldCode, operator, values } = condition;
|
|
1287
|
-
if (
|
|
1289
|
+
if (Object.prototype.hasOwnProperty.call(this.recordData, fieldCode)) {
|
|
1288
1290
|
const fieldValue = this.recordData[fieldCode];
|
|
1289
1291
|
const stringValue = fieldValue.toString().toUpperCase();
|
|
1290
1292
|
if (operator === ComparisonOperator.G && fieldValue <= values[0]) {
|
|
@@ -1465,6 +1467,7 @@ class RecordTable extends FormElement {
|
|
|
1465
1467
|
get selectionField() { return this._selectionField; }
|
|
1466
1468
|
get allSelected() { return this._allSelected; }
|
|
1467
1469
|
get tableCode() { return this._tableCode; }
|
|
1470
|
+
get code() { return this._tableCode; }
|
|
1468
1471
|
get tableTitle() { return this._tableTitle; }
|
|
1469
1472
|
get currentPage() { return this._currentPage; }
|
|
1470
1473
|
get totalPages() { return this._totalPages; }
|
|
@@ -1715,7 +1718,6 @@ class RecordTable extends FormElement {
|
|
|
1715
1718
|
this.updateVisibleRecords();
|
|
1716
1719
|
}
|
|
1717
1720
|
getTableRecord(recordId) {
|
|
1718
|
-
const recordIdKey = (typeof recordId === 'object') ? JSON.stringify(recordId) : recordId;
|
|
1719
1721
|
return (this._tableRecordObj && recordId && this._tableRecordObj[recordId])
|
|
1720
1722
|
? this._tableRecordObj[recordId] : null;
|
|
1721
1723
|
}
|
|
@@ -1860,9 +1862,7 @@ const HIDE = 'hide';
|
|
|
1860
1862
|
const ENABLE = 'enable';
|
|
1861
1863
|
const DISABLE = 'disable';
|
|
1862
1864
|
const CLEAN = 'clean';
|
|
1863
|
-
const alwaysVisible = 'ALWAYS';
|
|
1864
1865
|
const neverVisible = 'NONE';
|
|
1865
|
-
const onStatesVisible = 'ONSTATES';
|
|
1866
1866
|
class FormStructureAndData {
|
|
1867
1867
|
_stateChange = new Subject();
|
|
1868
1868
|
_immutableData = {};
|
|
@@ -2091,7 +2091,7 @@ class FormStructureAndData {
|
|
|
2091
2091
|
field && (field.required = required);
|
|
2092
2092
|
}
|
|
2093
2093
|
catch (e) {
|
|
2094
|
-
console.
|
|
2094
|
+
console.error(`Error modificando campo ${code}: ${e}`);
|
|
2095
2095
|
}
|
|
2096
2096
|
}
|
|
2097
2097
|
}
|
|
@@ -2159,7 +2159,7 @@ class FormStructureAndData {
|
|
|
2159
2159
|
processedFields += 1;
|
|
2160
2160
|
}
|
|
2161
2161
|
catch (e) {
|
|
2162
|
-
console.
|
|
2162
|
+
console.error(`Error procesando funcion en campo ${field}: ${e}`);
|
|
2163
2163
|
}
|
|
2164
2164
|
}
|
|
2165
2165
|
}
|
|
@@ -2222,7 +2222,7 @@ class FormStructureAndData {
|
|
|
2222
2222
|
for (let index = 0; index < codes.length; index++) {
|
|
2223
2223
|
const code = codes[index];
|
|
2224
2224
|
if (code) {
|
|
2225
|
-
resultObject[code] = this.fields?.[code]?.
|
|
2225
|
+
resultObject[code] = this.fields?.[code]?.value ?? null;
|
|
2226
2226
|
}
|
|
2227
2227
|
}
|
|
2228
2228
|
return resultObject;
|
|
@@ -2445,7 +2445,7 @@ class PieceComponent {
|
|
|
2445
2445
|
enabled = computed(() => !this.disabled());
|
|
2446
2446
|
customAttributes = signal({});
|
|
2447
2447
|
setForm(form) { this.form = form; }
|
|
2448
|
-
propagatedAttributeChange(
|
|
2448
|
+
propagatedAttributeChange(_attribute, _value) { }
|
|
2449
2449
|
updatePieceAttribute(signaledAttributes, signaledAttribute, value) {
|
|
2450
2450
|
if (!signaledAttributes.includes(signaledAttribute)) {
|
|
2451
2451
|
return;
|
|
@@ -2468,7 +2468,7 @@ class PieceComponent {
|
|
|
2468
2468
|
}
|
|
2469
2469
|
}
|
|
2470
2470
|
}
|
|
2471
|
-
customAttributeChange(
|
|
2471
|
+
customAttributeChange(_subAttribute, _value) { }
|
|
2472
2472
|
updateCustomAttribute(attrName, attrValue) {
|
|
2473
2473
|
this.customAttributes.update(old => ({ ...old, [attrName]: attrValue }));
|
|
2474
2474
|
this.customAttributeChange(attrName, attrValue);
|
|
@@ -3048,9 +3048,9 @@ class LibFormManagerService {
|
|
|
3048
3048
|
this.cleanStack();
|
|
3049
3049
|
}
|
|
3050
3050
|
// Métodos virtuales para las aplicaciones
|
|
3051
|
-
getFormDefinition(
|
|
3052
|
-
execServerAction(
|
|
3053
|
-
goToForm(
|
|
3051
|
+
getFormDefinition(_formCode) { }
|
|
3052
|
+
execServerAction(_actionDetail) { }
|
|
3053
|
+
goToForm(_formCode, _token, _subject) { }
|
|
3054
3054
|
async loadStack() { }
|
|
3055
3055
|
saveStack() { }
|
|
3056
3056
|
cleanStack() { this.pageStack = []; }
|
|
@@ -3089,7 +3089,7 @@ class LibFormManagerService {
|
|
|
3089
3089
|
return token;
|
|
3090
3090
|
}
|
|
3091
3091
|
unstack(token = null) {
|
|
3092
|
-
|
|
3092
|
+
const index = (token) ? this.findFormInStack(token).index : this.pageStack.length - 2;
|
|
3093
3093
|
let formInfo = null;
|
|
3094
3094
|
if (index >= 0) {
|
|
3095
3095
|
formInfo = this.pageStack[index];
|
|
@@ -3182,10 +3182,10 @@ class LibEventManagerService {
|
|
|
3182
3182
|
}
|
|
3183
3183
|
|
|
3184
3184
|
class LibFileManagementService {
|
|
3185
|
-
openFile(
|
|
3186
|
-
saveFileFromURL(
|
|
3187
|
-
saveFile(
|
|
3188
|
-
printPdfFile(
|
|
3185
|
+
openFile(_fileBase64Data, _fileName, _fileType) { }
|
|
3186
|
+
saveFileFromURL(_fileUrl, _fileName = null, _fileType = null) { }
|
|
3187
|
+
saveFile(_fileBase64Data, _fileName, _fileType) { }
|
|
3188
|
+
printPdfFile(_pdfBufferData) { }
|
|
3189
3189
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LibFileManagementService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
3190
3190
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LibFileManagementService });
|
|
3191
3191
|
}
|
|
@@ -3194,7 +3194,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
3194
3194
|
}] });
|
|
3195
3195
|
|
|
3196
3196
|
const PAYLOAD_VERSION = 'TUAINEXCHANGE_1.0';
|
|
3197
|
-
const INLINE_ACTION = 'INLINE';
|
|
3198
3197
|
const GLOBAL_ACTION = 'GLOBAL';
|
|
3199
3198
|
const GET_DATA_ACTION = 'GETDATA';
|
|
3200
3199
|
const SUBJECT = 'subject';
|
|
@@ -3314,9 +3313,9 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
3314
3313
|
displayActionServerError() { }
|
|
3315
3314
|
displayValidationServerError() { }
|
|
3316
3315
|
displayTableServerError() { }
|
|
3317
|
-
showFieldInfo(
|
|
3318
|
-
showModalDialog(
|
|
3319
|
-
openUploadDialog(
|
|
3316
|
+
showFieldInfo(_code, _detail) { }
|
|
3317
|
+
showModalDialog(_title, _body, _options, _callback, _params) { }
|
|
3318
|
+
openUploadDialog(_title, _body, _options, _callback, _params) { }
|
|
3320
3319
|
subscribeAppEvent(eventName, callback) {
|
|
3321
3320
|
this._eventEmiter.subscribe(eventName, callback);
|
|
3322
3321
|
}
|
|
@@ -3666,7 +3665,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
3666
3665
|
const clientSectionMethods = this._formSectionsCanDeactivate[code];
|
|
3667
3666
|
if (clientSectionMethods) {
|
|
3668
3667
|
for (const clientSectionMethod of clientSectionMethods) {
|
|
3669
|
-
const { callback
|
|
3668
|
+
const { callback } = clientSectionMethod;
|
|
3670
3669
|
const canActivate = callback(sectionObject);
|
|
3671
3670
|
if (canActivate === false) {
|
|
3672
3671
|
return false;
|
|
@@ -3684,7 +3683,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
3684
3683
|
const clientSectionMethods = this._formSectionsActivate[code];
|
|
3685
3684
|
if (clientSectionMethods) {
|
|
3686
3685
|
for (const clientSectionMethod of clientSectionMethods) {
|
|
3687
|
-
const { callback
|
|
3686
|
+
const { callback } = clientSectionMethod;
|
|
3688
3687
|
callback(sectionObject);
|
|
3689
3688
|
}
|
|
3690
3689
|
}
|
|
@@ -3698,7 +3697,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
3698
3697
|
const clientSectionMethods = this._formSectionsInactivate[code];
|
|
3699
3698
|
if (clientSectionMethods) {
|
|
3700
3699
|
for (const clientSectionMethod of clientSectionMethods) {
|
|
3701
|
-
const { callback
|
|
3700
|
+
const { callback } = clientSectionMethod;
|
|
3702
3701
|
callback(sectionObject);
|
|
3703
3702
|
}
|
|
3704
3703
|
}
|
|
@@ -3711,7 +3710,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
3711
3710
|
}
|
|
3712
3711
|
if (this._actionsInProgress[code]) {
|
|
3713
3712
|
const { sent } = this._actionsInProgress[code];
|
|
3714
|
-
console.
|
|
3713
|
+
console.error(`Reingreso sobre acción ${code} con ejecución previa ${sent}`);
|
|
3715
3714
|
return;
|
|
3716
3715
|
}
|
|
3717
3716
|
this._actionsInProgress[code] = { sent: new Date() };
|
|
@@ -3721,7 +3720,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
3721
3720
|
if (clientActionMethods) {
|
|
3722
3721
|
const clientActionPromises = [];
|
|
3723
3722
|
for (const clientActionMethod of clientActionMethods) {
|
|
3724
|
-
const { callback
|
|
3723
|
+
const { callback } = clientActionMethod;
|
|
3725
3724
|
const continueActionPromise = callback(actionObject);
|
|
3726
3725
|
clientActionPromises.push(continueActionPromise);
|
|
3727
3726
|
}
|
|
@@ -3750,7 +3749,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
3750
3749
|
if (backend) {
|
|
3751
3750
|
if (this._serverActionsInProgress[code]) {
|
|
3752
3751
|
const { sent } = this._serverActionsInProgress[code];
|
|
3753
|
-
console.
|
|
3752
|
+
console.error(`Reingreso server en acción ${code} con ejecución previa ${sent}`);
|
|
3754
3753
|
return;
|
|
3755
3754
|
}
|
|
3756
3755
|
this._serverActionsInProgress[code] = { sent: new Date() };
|
|
@@ -3764,7 +3763,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
3764
3763
|
}
|
|
3765
3764
|
else {
|
|
3766
3765
|
for (let index = 0; index < this._actionServerError.length; index++) {
|
|
3767
|
-
const { callback
|
|
3766
|
+
const { callback } = this._actionServerError[index];
|
|
3768
3767
|
callback(action);
|
|
3769
3768
|
}
|
|
3770
3769
|
}
|
|
@@ -3822,7 +3821,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
3822
3821
|
this.removeEventHandler(this._fieldValidationsStart, codes);
|
|
3823
3822
|
this.removeEventHandler(this._fieldValidationsFinish, codes);
|
|
3824
3823
|
}
|
|
3825
|
-
async startFieldInputValidation(code,
|
|
3824
|
+
async startFieldInputValidation(code, _intrinsicValidation = true) {
|
|
3826
3825
|
this.notifyFormActivity();
|
|
3827
3826
|
const fieldToValidate = this.getField(code);
|
|
3828
3827
|
if (!fieldToValidate) {
|
|
@@ -3832,7 +3831,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
3832
3831
|
if (validationCallbacks) {
|
|
3833
3832
|
const clientValidationPromises = [];
|
|
3834
3833
|
for (const validationMethod of validationCallbacks) {
|
|
3835
|
-
const { callback
|
|
3834
|
+
const { callback } = validationMethod;
|
|
3836
3835
|
const continueValidationPromise = callback(fieldToValidate);
|
|
3837
3836
|
clientValidationPromises.push(continueValidationPromise);
|
|
3838
3837
|
}
|
|
@@ -3850,7 +3849,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
3850
3849
|
if (eventHandlerCallbacks) {
|
|
3851
3850
|
const clientEventPromises = [];
|
|
3852
3851
|
for (const eventHandlerMethod of eventHandlerCallbacks) {
|
|
3853
|
-
const { callback
|
|
3852
|
+
const { callback } = eventHandlerMethod;
|
|
3854
3853
|
const clientEventPromise = callback(eventName, eventData, fieldToTrigger);
|
|
3855
3854
|
clientEventPromises.push(clientEventPromise);
|
|
3856
3855
|
}
|
|
@@ -3866,7 +3865,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
3866
3865
|
if (validationCallbacks) {
|
|
3867
3866
|
const clientValidationPromises = [];
|
|
3868
3867
|
for (const validationMethod of validationCallbacks) {
|
|
3869
|
-
const { callback
|
|
3868
|
+
const { callback } = validationMethod;
|
|
3870
3869
|
const clientValidationPromise = callback(fieldToValidate);
|
|
3871
3870
|
clientValidationPromises.push(clientValidationPromise);
|
|
3872
3871
|
}
|
|
@@ -3898,7 +3897,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
3898
3897
|
fieldObj?.setErrorCode(this.errorCode);
|
|
3899
3898
|
fieldObj?.setErrorMessage(this.errorMessage);
|
|
3900
3899
|
for (let index = 0; index < this._fieldServerError.length; index++) {
|
|
3901
|
-
const { callback
|
|
3900
|
+
const { callback } = this._fieldServerError[index];
|
|
3902
3901
|
callback(fieldObj);
|
|
3903
3902
|
}
|
|
3904
3903
|
}
|
|
@@ -4052,7 +4051,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
4052
4051
|
if (tableActionMethods) {
|
|
4053
4052
|
const clientActionPromises = [];
|
|
4054
4053
|
for (const tableActionMethod of tableActionMethods) {
|
|
4055
|
-
const { callback
|
|
4054
|
+
const { callback } = tableActionMethod;
|
|
4056
4055
|
const clientActionPromise = callback(tableActionDetail);
|
|
4057
4056
|
clientActionPromises.push(clientActionPromise);
|
|
4058
4057
|
}
|
|
@@ -4088,7 +4087,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
4088
4087
|
}
|
|
4089
4088
|
else {
|
|
4090
4089
|
for (let index = 0; index < this._tableServerError.length; index++) {
|
|
4091
|
-
const { callback
|
|
4090
|
+
const { callback } = this._tableServerError[index];
|
|
4092
4091
|
callback(tableObject);
|
|
4093
4092
|
}
|
|
4094
4093
|
}
|
|
@@ -4136,7 +4135,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
4136
4135
|
if (tableActionMethods) {
|
|
4137
4136
|
const clientActionPromises = [];
|
|
4138
4137
|
for (const tableActionMethod of tableActionMethods) {
|
|
4139
|
-
const { callback
|
|
4138
|
+
const { callback } = tableActionMethod;
|
|
4140
4139
|
const clientActionPromise = callback(tableActionDetail);
|
|
4141
4140
|
clientActionPromises.push(clientActionPromise);
|
|
4142
4141
|
}
|
|
@@ -4215,7 +4214,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
4215
4214
|
if (tableEventHandlers) {
|
|
4216
4215
|
const clientActionPromises = [];
|
|
4217
4216
|
for (const tableSelectionMethod of tableEventHandlers) {
|
|
4218
|
-
const { callback
|
|
4217
|
+
const { callback } = tableSelectionMethod;
|
|
4219
4218
|
const clientActionPromise = callback(tableSelectionDetail);
|
|
4220
4219
|
clientActionPromises.push(clientActionPromise);
|
|
4221
4220
|
}
|
|
@@ -4293,7 +4292,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
4293
4292
|
if (tableActionMethods) {
|
|
4294
4293
|
const clientActionPromises = [];
|
|
4295
4294
|
for (const tableActionMethod of tableActionMethods) {
|
|
4296
|
-
const { callback
|
|
4295
|
+
const { callback } = tableActionMethod;
|
|
4297
4296
|
const clientActionPromise = callback(tableActionDetail);
|
|
4298
4297
|
clientActionPromises.push(clientActionPromise);
|
|
4299
4298
|
}
|
|
@@ -4362,7 +4361,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
4362
4361
|
if (tableEventHandlers) {
|
|
4363
4362
|
const clientActionPromises = [];
|
|
4364
4363
|
for (const tableActionMethod of tableEventHandlers) {
|
|
4365
|
-
const { callback
|
|
4364
|
+
const { callback } = tableActionMethod;
|
|
4366
4365
|
const clientActionPromise = callback(tableActionDetail);
|
|
4367
4366
|
clientActionPromises.push(clientActionPromise);
|
|
4368
4367
|
}
|
|
@@ -4463,7 +4462,7 @@ class BasicFormComponent extends FormStructureAndData {
|
|
|
4463
4462
|
}
|
|
4464
4463
|
notifyFormActivity() {
|
|
4465
4464
|
if (this._notifyFormActivity) {
|
|
4466
|
-
this._eventEmiter.next('formActivity', { code: this.
|
|
4465
|
+
this._eventEmiter.next('formActivity', { code: this.name });
|
|
4467
4466
|
}
|
|
4468
4467
|
}
|
|
4469
4468
|
/**
|
|
@@ -4793,6 +4792,167 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
4793
4792
|
type: Injectable
|
|
4794
4793
|
}] });
|
|
4795
4794
|
|
|
4795
|
+
const DEFAULT_STREAM_PATH = '/liveconnect/stream';
|
|
4796
|
+
const DEFAULT_SESSION_ENDED_EVENT = 'sessionEnded';
|
|
4797
|
+
const SESSION_EVENT_CLASS = 'session';
|
|
4798
|
+
const SESSION_REPLACED_EVENT_TYPE = 'sessionReplaced';
|
|
4799
|
+
/** Error que detiene los reintentos de `fetchEventSource` (handshake rechazado). */
|
|
4800
|
+
class FatalSseError extends Error {
|
|
4801
|
+
}
|
|
4802
|
+
/**
|
|
4803
|
+
* Canal de eventos servidor→cliente sobre Server-Sent Events (SSE).
|
|
4804
|
+
*
|
|
4805
|
+
* Reemplaza al `LiveConnectionService` basado en Socket.IO que vivía duplicado
|
|
4806
|
+
* en cada app. El uso siempre fue unidireccional (un único handshake y luego
|
|
4807
|
+
* solo recepción), así que SSE encaja sin perder funcionalidad y además gana
|
|
4808
|
+
* reconexión automática nativa.
|
|
4809
|
+
*
|
|
4810
|
+
* El servicio es agnóstico de la app: toda la integración (URL, token de auth,
|
|
4811
|
+
* allow-list, re-emisión al bus, logger) llega por `SSE_LIVE_CONNECTION_CONFIG`.
|
|
4812
|
+
* El ciclo de vida (cuándo abrir/cerrar) lo controla cada app llamando
|
|
4813
|
+
* `open()`/`close()` — típicamente atado a `sessionEstablished`/`sessionEnded`,
|
|
4814
|
+
* y en móvil además al `appStateChange` de Capacitor.
|
|
4815
|
+
*/
|
|
4816
|
+
class SseLiveConnectionService {
|
|
4817
|
+
_config = inject(SSE_LIVE_CONNECTION_CONFIG);
|
|
4818
|
+
_abort = null;
|
|
4819
|
+
_currentSessionId = null;
|
|
4820
|
+
_connected = false;
|
|
4821
|
+
isConnected() {
|
|
4822
|
+
return this._connected;
|
|
4823
|
+
}
|
|
4824
|
+
/**
|
|
4825
|
+
* Abre el stream SSE para la sesión dada (cierra cualquier stream anterior).
|
|
4826
|
+
* El handshake viaja en el header `Authorization`, no en la URL.
|
|
4827
|
+
*/
|
|
4828
|
+
async open(sessionData) {
|
|
4829
|
+
this.close();
|
|
4830
|
+
if (!sessionData) {
|
|
4831
|
+
return;
|
|
4832
|
+
}
|
|
4833
|
+
this._currentSessionId = sessionData?.sessionId ?? null;
|
|
4834
|
+
const logger = this._config.logger;
|
|
4835
|
+
const allowed = this._allowedSet();
|
|
4836
|
+
const token = await this._config.getAuthToken(sessionData);
|
|
4837
|
+
const scheme = this._config.authScheme ?? 'Bearer';
|
|
4838
|
+
const authorization = scheme ? `${scheme} ${token}` : token;
|
|
4839
|
+
const abort = new AbortController();
|
|
4840
|
+
this._abort = abort;
|
|
4841
|
+
this.openEventSource(this._buildStreamUrl(), {
|
|
4842
|
+
signal: abort.signal,
|
|
4843
|
+
openWhenHidden: this._config.keepOpenWhenHidden ?? true,
|
|
4844
|
+
headers: { Authorization: authorization },
|
|
4845
|
+
onopen: async (response) => {
|
|
4846
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
4847
|
+
if (response.ok && contentType.includes('text/event-stream')) {
|
|
4848
|
+
this._connected = true;
|
|
4849
|
+
return;
|
|
4850
|
+
}
|
|
4851
|
+
// 4xx (p. ej. 401) => fatal, no reintentar. 5xx/otros => reintentar.
|
|
4852
|
+
if (response.status >= 400 && response.status < 500) {
|
|
4853
|
+
throw new FatalSseError(`handshake rechazado: ${response.status}`);
|
|
4854
|
+
}
|
|
4855
|
+
throw new Error(`handshake inesperado: ${response.status}`);
|
|
4856
|
+
},
|
|
4857
|
+
onmessage: msg => {
|
|
4858
|
+
if (!msg.data) {
|
|
4859
|
+
return; // comentarios/heartbeats no traen `data`
|
|
4860
|
+
}
|
|
4861
|
+
let eventData;
|
|
4862
|
+
try {
|
|
4863
|
+
eventData = JSON.parse(msg.data);
|
|
4864
|
+
}
|
|
4865
|
+
catch {
|
|
4866
|
+
logger?.warn?.('[SseLiveConnection] Payload no-JSON descartado');
|
|
4867
|
+
return;
|
|
4868
|
+
}
|
|
4869
|
+
logger?.debug?.(`[SseLiveConnection] Evento recibido: ${msg.data}`);
|
|
4870
|
+
if (this._handleSessionReplaced(eventData)) {
|
|
4871
|
+
return;
|
|
4872
|
+
}
|
|
4873
|
+
const eventClass = eventData?.eventClass;
|
|
4874
|
+
if (typeof eventClass !== 'string' || !allowed.has(eventClass)) {
|
|
4875
|
+
logger?.warn?.('[SseLiveConnection] Evento no permitido rechazado:', eventClass);
|
|
4876
|
+
return;
|
|
4877
|
+
}
|
|
4878
|
+
this._config.emit(eventClass, eventData);
|
|
4879
|
+
},
|
|
4880
|
+
onerror: err => {
|
|
4881
|
+
this._connected = false;
|
|
4882
|
+
if (err instanceof FatalSseError) {
|
|
4883
|
+
logger?.warn?.('[SseLiveConnection]', err.message);
|
|
4884
|
+
throw err; // detiene los reintentos
|
|
4885
|
+
}
|
|
4886
|
+
logger?.warn?.('[SseLiveConnection] error de stream, reintentando', err?.message ?? err);
|
|
4887
|
+
// devolver undefined => fetch-event-source reintenta con backoff propio
|
|
4888
|
+
},
|
|
4889
|
+
}).catch(err => {
|
|
4890
|
+
this._connected = false;
|
|
4891
|
+
if (!(err instanceof FatalSseError) && err?.name !== 'AbortError') {
|
|
4892
|
+
logger?.warn?.('[SseLiveConnection] stream finalizado', err?.message ?? err);
|
|
4893
|
+
}
|
|
4894
|
+
});
|
|
4895
|
+
}
|
|
4896
|
+
/**
|
|
4897
|
+
* Punto de indirección (seam) sobre `fetchEventSource`: permite sustituirlo en
|
|
4898
|
+
* tests (`fetchEventSource` es un binding ESM de solo lectura, no espiable).
|
|
4899
|
+
* En producción delega directamente en la librería.
|
|
4900
|
+
*/
|
|
4901
|
+
openEventSource(url, options) {
|
|
4902
|
+
return fetchEventSource(url, options);
|
|
4903
|
+
}
|
|
4904
|
+
/** Cierra el stream actual (idempotente). */
|
|
4905
|
+
close() {
|
|
4906
|
+
if (this._abort) {
|
|
4907
|
+
this._abort.abort();
|
|
4908
|
+
this._abort = null;
|
|
4909
|
+
}
|
|
4910
|
+
this._connected = false;
|
|
4911
|
+
}
|
|
4912
|
+
_buildStreamUrl() {
|
|
4913
|
+
const base = (this._config.buildUrl() ?? '').replace(/\/+$/, '');
|
|
4914
|
+
const path = this._config.streamPath ?? DEFAULT_STREAM_PATH;
|
|
4915
|
+
return `${base}${path.startsWith('/') ? path : '/' + path}`;
|
|
4916
|
+
}
|
|
4917
|
+
_allowedSet() {
|
|
4918
|
+
const allowed = this._config.allowedEvents;
|
|
4919
|
+
return allowed instanceof Set ? allowed : new Set(allowed);
|
|
4920
|
+
}
|
|
4921
|
+
/**
|
|
4922
|
+
* Maneja el evento de lifecycle `sessionReplaced` (otra apertura de sesión
|
|
4923
|
+
* para el mismo usuario). Si el `newSessionId` difiere de la sesión local,
|
|
4924
|
+
* emite el evento de fin de sesión para forzar logout. Se ejecuta antes del
|
|
4925
|
+
* filtro de allow-list porque no se re-emite al bus genérico.
|
|
4926
|
+
* Devuelve `true` cuando consume el evento.
|
|
4927
|
+
*/
|
|
4928
|
+
_handleSessionReplaced(eventData) {
|
|
4929
|
+
if ((this._config.handleSessionReplaced ?? true) === false) {
|
|
4930
|
+
return false;
|
|
4931
|
+
}
|
|
4932
|
+
if (eventData?.eventClass !== SESSION_EVENT_CLASS ||
|
|
4933
|
+
eventData?.eventType !== SESSION_REPLACED_EVENT_TYPE) {
|
|
4934
|
+
return false;
|
|
4935
|
+
}
|
|
4936
|
+
const newSessionId = eventData?.eventDetail?.newSessionId ?? null;
|
|
4937
|
+
if (!this._currentSessionId) {
|
|
4938
|
+
return true; // no hay sesión local que cerrar
|
|
4939
|
+
}
|
|
4940
|
+
if (newSessionId && newSessionId === this._currentSessionId) {
|
|
4941
|
+
return true; // corresponde a la propia sesión recién abierta
|
|
4942
|
+
}
|
|
4943
|
+
this._config.logger?.warn?.('[SseLiveConnection] Sesión reemplazada por otra apertura para el mismo usuario', { currentSessionId: this._currentSessionId, newSessionId });
|
|
4944
|
+
const sessionEndedName = this._config.sessionEndedEventName ?? DEFAULT_SESSION_ENDED_EVENT;
|
|
4945
|
+
this._config.emit(sessionEndedName, null);
|
|
4946
|
+
return true;
|
|
4947
|
+
}
|
|
4948
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SseLiveConnectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4949
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SseLiveConnectionService, providedIn: 'root' });
|
|
4950
|
+
}
|
|
4951
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SseLiveConnectionService, decorators: [{
|
|
4952
|
+
type: Injectable,
|
|
4953
|
+
args: [{ providedIn: 'root' }]
|
|
4954
|
+
}] });
|
|
4955
|
+
|
|
4796
4956
|
const COMPONENTS = [
|
|
4797
4957
|
ActionComponent,
|
|
4798
4958
|
FieldComponent,
|
|
@@ -4845,5 +5005,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
4845
5005
|
* Generated bundle index. Do not edit.
|
|
4846
5006
|
*/
|
|
4847
5007
|
|
|
4848
|
-
export { ActionComponent, BaseIconResolverService, BasicFormComponent, ComparisonOperator, ElementComponent, ElementType, FieldComponent, FieldDescriptor, FormAction, FormActionType, FormElement, FormErrorComponent, FormHeaderComponent, FormPiece, FormPiecePropagate, FormStructureAndData, HEADER, ICON_RESOLVER, IconDictionaryService, LibEventManagerService, LibFileManagementService, LibFormManagerService, LibTableComponent, LibTableRecordActionComponent, LibTableRecordFieldComponent, NO_ERROR, PieceComponent, RecordFormSection, RecordFormSubSection, RecordTable, RecordTableColumn, SectionComponent, SubSectionComponent, TableAction, TableRecordData, TuainNgFormsLibModule, elementTypes, formActions, iconDictionary, operators };
|
|
5008
|
+
export { ActionComponent, BaseIconResolverService, BasicFormComponent, ComparisonOperator, ElementComponent, ElementType, FieldComponent, FieldDescriptor, FormAction, FormActionType, FormElement, FormErrorComponent, FormHeaderComponent, FormPiece, FormPiecePropagate, FormStructureAndData, HEADER, ICON_RESOLVER, IconDictionaryService, LibEventManagerService, LibFileManagementService, LibFormManagerService, LibTableComponent, LibTableRecordActionComponent, LibTableRecordFieldComponent, NO_ERROR, PieceComponent, RecordFormSection, RecordFormSubSection, RecordTable, RecordTableColumn, SSE_LIVE_CONNECTION_CONFIG, SectionComponent, SseLiveConnectionService, SubSectionComponent, TableAction, TableRecordData, TuainNgFormsLibModule, elementTypes, formActions, iconDictionary, operators };
|
|
4849
5009
|
//# sourceMappingURL=tuain-ng-forms-lib.mjs.map
|