@meshmakers/octo-ui 3.4.1050 → 3.4.1080
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 +1 -1
- package/fesm2022/meshmakers-octo-ui-branding.mjs.map +1 -1
- package/fesm2022/meshmakers-octo-ui.mjs +259 -235
- package/fesm2022/meshmakers-octo-ui.mjs.map +1 -1
- package/lib/runtime-browser/styles/_theme.scss +484 -0
- package/package.json +1 -1
- package/types/meshmakers-octo-ui-branding.d.ts +2 -3
- package/types/meshmakers-octo-ui.d.ts +4 -3
|
@@ -2085,8 +2085,15 @@ class PropertyValueDisplayComponent {
|
|
|
2085
2085
|
recordArrayRows = [];
|
|
2086
2086
|
/** Hard cap so we don't render a horizontally-unmanageable table. */
|
|
2087
2087
|
static MAX_TABLE_COLUMNS = 12;
|
|
2088
|
-
|
|
2089
|
-
|
|
2088
|
+
// Exposed to the template through a getter, not a `readonly X = X` field: Vite's
|
|
2089
|
+
// module-runner transform (used by the Vitest unit-test builder) snapshots a class
|
|
2090
|
+
// field whose initialiser is a bare imported identifier at module-evaluation time,
|
|
2091
|
+
// which is before esbuild's lazily-initialised shared chunk has assigned the export,
|
|
2092
|
+
// so the field would be `undefined`.
|
|
2093
|
+
// A getter body is rewritten to a live read. Test-runner-only: production and
|
|
2094
|
+
// ng-packagr builds do not use the module runner and are unaffected.
|
|
2095
|
+
get PropertyDisplayMode() { return PropertyDisplayMode; }
|
|
2096
|
+
get AttributeValueTypeDto() { return AttributeValueTypeDto; }
|
|
2090
2097
|
Array = Array;
|
|
2091
2098
|
chevronRightIcon = chevronRightIcon;
|
|
2092
2099
|
chevronDownIcon = chevronDownIcon;
|
|
@@ -2834,7 +2841,14 @@ class PropertyGridComponent {
|
|
|
2834
2841
|
checkboxCheckedIcon = checkboxCheckedIcon;
|
|
2835
2842
|
listUnorderedIcon = listUnorderedIcon;
|
|
2836
2843
|
// Enum reference
|
|
2837
|
-
|
|
2844
|
+
// Exposed to the template through a getter, not a `readonly X = X` field: Vite's
|
|
2845
|
+
// module-runner transform (used by the Vitest unit-test builder) snapshots a class
|
|
2846
|
+
// field whose initialiser is a bare imported identifier at module-evaluation time,
|
|
2847
|
+
// which is before esbuild's lazily-initialised shared chunk has assigned the export,
|
|
2848
|
+
// so the field would be `undefined`.
|
|
2849
|
+
// A getter body is rewritten to a live read. Test-runner-only: production and
|
|
2850
|
+
// ng-packagr builds do not use the module runner and are unaffected.
|
|
2851
|
+
get AttributeValueTypeDto() { return AttributeValueTypeDto; }
|
|
2838
2852
|
ngOnInit() {
|
|
2839
2853
|
this.updateFilteredData();
|
|
2840
2854
|
}
|
|
@@ -5649,9 +5663,9 @@ class OctoGraphQlDataSource extends DataSourceTyped {
|
|
|
5649
5663
|
case 'isnotnull':
|
|
5650
5664
|
return { operator: FieldFilterOperatorsDto.NotEqualsDto, value: null };
|
|
5651
5665
|
case 'isempty':
|
|
5652
|
-
return { operator: FieldFilterOperatorsDto.EqualsDto, value:
|
|
5666
|
+
return { operator: FieldFilterOperatorsDto.EqualsDto, value: '' };
|
|
5653
5667
|
case 'isnotempty':
|
|
5654
|
-
return { operator: FieldFilterOperatorsDto.NotEqualsDto, value:
|
|
5668
|
+
return { operator: FieldFilterOperatorsDto.NotEqualsDto, value: '' };
|
|
5655
5669
|
default:
|
|
5656
5670
|
throw new Error('The filter operator is not supported');
|
|
5657
5671
|
}
|
|
@@ -6198,7 +6212,7 @@ const swagger_communication = {
|
|
|
6198
6212
|
viewBox: '0 0 192 192'
|
|
6199
6213
|
};
|
|
6200
6214
|
const swagger = {
|
|
6201
|
-
name:
|
|
6215
|
+
name: 'swagger',
|
|
6202
6216
|
content: '<path id="path1" class="st0" d="M96,0C43.1,0,0,43,0,96s43.1,96,96,96,96-43,96-96S148.9,0,96,0ZM96,9.2c48,0,86.8,38.9,86.8,86.8s-38.9,86.8-86.8,86.8S9.2,143.9,9.2,96,48,9.2,96,9.2ZM67,46.9h-3.4c-7.9.5-12.5,4.1-13.9,11.8-.9,5.4-.7,10.7-1.1,16.1,0,2.8-.4,5.5-.9,8.3-1,4.9-3.2,6.4-8.2,6.6-.7,0-1.3,0-2,.3v11.6c9.1.5,10.2,3.7,10.9,13,.3,3.4,0,6.9,0,10.3s.6,6.4,1.3,9.6c2,8.6,10.5,11.5,20.6,9.8v-10.3h-4.5c-3.4,0-4.7-.9-5-4.3-.4-4.3-.3-8.6-.6-13-.6-8-1.4-15.9-9.2-21,4-2.9,6.9-6.4,7.9-11.2.7-3.2,1-6.6,1.3-10s0-6.7,0-10c.4-5.4.9-7.5,7.2-7.3h2.9v-10.4h-3.6.4ZM127.6,47c-2.2,0-4.3,0-6.5.5v10.3h5c2.6,0,4.6,1.1,4.9,4,.3,2.6.3,5.4.6,8.1.6,5.4.9,10.7,1.7,16.1.9,4.3,3.7,7.7,7.5,10.3-6.6,4.4-8.5,10.7-8.8,17.8,0,4.9-.3,9.6-.6,14.5-.3,4.4-1.7,5.8-6.2,6h-3.9v10.4h7.5c7.5-.5,11.9-4.1,13.5-11.3.6-4,1-8,1.1-12.1.3-3.7.3-7.3.6-11,.6-5.7,3.2-8.1,8.9-8.4.6,0,1,0,1.6-.3v-11.6c-1,0-1.6,0-2.3-.3-4.3,0-6.5-1.7-7.5-5.7-.7-2.6-1-5.2-1.3-8-.3-4.9-.3-10-.6-14.9-.6-9.5-6.3-14.1-15.1-14.2,0,0-.3,0-.3,0ZM72.6,89.3c-3.7,0-6.8,3.1-6.8,6.7s2.6,6.4,6.2,6.7h.4c3.6,0,6.8-2.6,7-6.3v-.3c0-3.7-2.9-6.7-6.6-6.9,0,0-.3,0-.3,0ZM95.9,89.3c-3.6,0-6.6,2.6-6.6,6.3v.6c0,4,2.7,6.6,6.9,6.6s6.6-2.6,6.6-6.9-2.7-6.7-6.9-6.6h0ZM119.6,89.3c-3.7,0-6.9,2.9-7,6.7,0,3.8,3,6.7,6.8,6.7h0c3.4.6,6.8-2.8,7-6.6,0-3.7-3.2-6.9-6.9-6.9h.1Z"/>',
|
|
6203
6217
|
viewBox: '0 0 192 192'
|
|
6204
6218
|
};
|
|
@@ -10030,7 +10044,7 @@ class EntityDetailViewComponent {
|
|
|
10030
10044
|
propertyGridConfig = {
|
|
10031
10045
|
readOnlyMode: false,
|
|
10032
10046
|
showTypeIcons: true,
|
|
10033
|
-
height:
|
|
10047
|
+
height: '500px',
|
|
10034
10048
|
showSearch: true,
|
|
10035
10049
|
};
|
|
10036
10050
|
// Direction filter - use getter for current messages
|
|
@@ -10092,7 +10106,7 @@ class EntityDetailViewComponent {
|
|
|
10092
10106
|
});
|
|
10093
10107
|
}
|
|
10094
10108
|
ngOnChanges(changes) {
|
|
10095
|
-
if (changes[
|
|
10109
|
+
if (changes['entity'] && this.entity) {
|
|
10096
10110
|
this.updatePropertyGrid();
|
|
10097
10111
|
}
|
|
10098
10112
|
}
|
|
@@ -10112,7 +10126,7 @@ class EntityDetailViewComponent {
|
|
|
10112
10126
|
this.propertyGridItems = items;
|
|
10113
10127
|
},
|
|
10114
10128
|
error: (err) => {
|
|
10115
|
-
console.error(
|
|
10129
|
+
console.error('Error converting entity to properties:', err);
|
|
10116
10130
|
this.propertyGridItems = [];
|
|
10117
10131
|
},
|
|
10118
10132
|
});
|
|
@@ -10129,7 +10143,7 @@ class EntityDetailViewComponent {
|
|
|
10129
10143
|
};
|
|
10130
10144
|
}
|
|
10131
10145
|
normalizeDateValue(value) {
|
|
10132
|
-
if (typeof value ===
|
|
10146
|
+
if (typeof value === 'string') {
|
|
10133
10147
|
return value;
|
|
10134
10148
|
}
|
|
10135
10149
|
if (value instanceof Date) {
|
|
@@ -10184,7 +10198,7 @@ class EntityDetailViewComponent {
|
|
|
10184
10198
|
};
|
|
10185
10199
|
getEntityIdentifier() {
|
|
10186
10200
|
if (!this.entity?.ckTypeId || !this.entity?.rtId) {
|
|
10187
|
-
return
|
|
10201
|
+
return 'Unknown';
|
|
10188
10202
|
}
|
|
10189
10203
|
return `${this.entity.ckTypeId}@${this.entity.rtId}`;
|
|
10190
10204
|
}
|
|
@@ -10213,20 +10227,20 @@ class EntityDetailViewComponent {
|
|
|
10213
10227
|
.then(() => {
|
|
10214
10228
|
this.notificationService.show({
|
|
10215
10229
|
content: `${label} ${this._messages.copiedToClipboard}`,
|
|
10216
|
-
type: { style:
|
|
10217
|
-
position: { horizontal:
|
|
10230
|
+
type: { style: 'success', icon: true },
|
|
10231
|
+
position: { horizontal: 'right', vertical: 'top' },
|
|
10218
10232
|
hideAfter: 2000,
|
|
10219
|
-
animation: { type:
|
|
10233
|
+
animation: { type: 'fade', duration: 400 },
|
|
10220
10234
|
});
|
|
10221
10235
|
})
|
|
10222
10236
|
.catch((err) => {
|
|
10223
|
-
console.error(
|
|
10237
|
+
console.error('Failed to copy:', err);
|
|
10224
10238
|
this.notificationService.show({
|
|
10225
10239
|
content: this._messages.failedToCopyToClipboard,
|
|
10226
|
-
type: { style:
|
|
10227
|
-
position: { horizontal:
|
|
10240
|
+
type: { style: 'error', icon: true },
|
|
10241
|
+
position: { horizontal: 'right', vertical: 'top' },
|
|
10228
10242
|
hideAfter: 3000,
|
|
10229
|
-
animation: { type:
|
|
10243
|
+
animation: { type: 'fade', duration: 400 },
|
|
10230
10244
|
});
|
|
10231
10245
|
});
|
|
10232
10246
|
}
|
|
@@ -10236,7 +10250,7 @@ class EntityDetailViewComponent {
|
|
|
10236
10250
|
*/
|
|
10237
10251
|
async onBinaryDownload(_event) {
|
|
10238
10252
|
if (!this.entity?.rtId) {
|
|
10239
|
-
console.warn(
|
|
10253
|
+
console.warn('Cannot download: no entity rtId available');
|
|
10240
10254
|
return;
|
|
10241
10255
|
}
|
|
10242
10256
|
try {
|
|
@@ -10247,27 +10261,27 @@ class EntityDetailViewComponent {
|
|
|
10247
10261
|
const item = result.data?.runtime?.systemReportingFileSystemItem?.items?.[0];
|
|
10248
10262
|
if (item?.content?.downloadUri) {
|
|
10249
10263
|
// Open download in new tab
|
|
10250
|
-
window.open(item.content.downloadUri,
|
|
10264
|
+
window.open(item.content.downloadUri, '_blank', 'noopener,noreferrer');
|
|
10251
10265
|
}
|
|
10252
10266
|
else {
|
|
10253
|
-
console.warn(
|
|
10267
|
+
console.warn('No downloadUri found for binary');
|
|
10254
10268
|
this.notificationService.show({
|
|
10255
10269
|
content: this._messages.downloadNotAvailable,
|
|
10256
|
-
type: { style:
|
|
10257
|
-
position: { horizontal:
|
|
10270
|
+
type: { style: 'warning', icon: true },
|
|
10271
|
+
position: { horizontal: 'right', vertical: 'top' },
|
|
10258
10272
|
hideAfter: 3000,
|
|
10259
|
-
animation: { type:
|
|
10273
|
+
animation: { type: 'fade', duration: 400 },
|
|
10260
10274
|
});
|
|
10261
10275
|
}
|
|
10262
10276
|
}
|
|
10263
10277
|
catch (error) {
|
|
10264
|
-
console.error(
|
|
10278
|
+
console.error('Failed to load binary info:', error);
|
|
10265
10279
|
this.notificationService.show({
|
|
10266
10280
|
content: this._messages.failedToLoadDownloadInfo,
|
|
10267
|
-
type: { style:
|
|
10268
|
-
position: { horizontal:
|
|
10281
|
+
type: { style: 'error', icon: true },
|
|
10282
|
+
position: { horizontal: 'right', vertical: 'top' },
|
|
10269
10283
|
hideAfter: 3000,
|
|
10270
|
-
animation: { type:
|
|
10284
|
+
animation: { type: 'fade', duration: 400 },
|
|
10271
10285
|
});
|
|
10272
10286
|
}
|
|
10273
10287
|
}
|
|
@@ -10512,7 +10526,7 @@ class EntityDetailViewComponent {
|
|
|
10512
10526
|
}
|
|
10513
10527
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: EntityDetailViewComponent, decorators: [{
|
|
10514
10528
|
type: Component,
|
|
10515
|
-
args: [{ selector:
|
|
10529
|
+
args: [{ selector: 'mm-entity-detail-view', standalone: true, imports: [
|
|
10516
10530
|
CommonModule,
|
|
10517
10531
|
TabStripModule,
|
|
10518
10532
|
ButtonModule,
|
|
@@ -10799,7 +10813,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
10799
10813
|
type: Output
|
|
10800
10814
|
}], associationsDataSource: [{
|
|
10801
10815
|
type: ViewChild,
|
|
10802
|
-
args: [
|
|
10816
|
+
args: ['associationsDir', { static: false }]
|
|
10803
10817
|
}] } });
|
|
10804
10818
|
|
|
10805
10819
|
class EntityDetailDataSource {
|
|
@@ -11706,11 +11720,11 @@ class AttributeCoordinatorService {
|
|
|
11706
11720
|
this.structureReady.set(true);
|
|
11707
11721
|
}
|
|
11708
11722
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeCoordinatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
11709
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeCoordinatorService, providedIn:
|
|
11723
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeCoordinatorService, providedIn: 'root' });
|
|
11710
11724
|
}
|
|
11711
11725
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeCoordinatorService, decorators: [{
|
|
11712
11726
|
type: Injectable,
|
|
11713
|
-
args: [{ providedIn:
|
|
11727
|
+
args: [{ providedIn: 'root' }]
|
|
11714
11728
|
}] });
|
|
11715
11729
|
|
|
11716
11730
|
const GetCkAttributesDetailedDocumentDto = gql `
|
|
@@ -11883,51 +11897,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
11883
11897
|
|
|
11884
11898
|
class AttributeRecognitionService {
|
|
11885
11899
|
isString(attributeValueType) {
|
|
11886
|
-
return attributeValueType ===
|
|
11900
|
+
return attributeValueType === 'STRING';
|
|
11887
11901
|
}
|
|
11888
11902
|
isNumber(attributeValueType) {
|
|
11889
|
-
return [
|
|
11903
|
+
return ['INT', 'INTEGER', 'INTEGER_64', 'DOUBLE', 'NUMBER', 'LONG'].includes(attributeValueType);
|
|
11890
11904
|
}
|
|
11891
11905
|
isEnum(attributeValueType) {
|
|
11892
|
-
return attributeValueType ===
|
|
11906
|
+
return attributeValueType === 'ENUM';
|
|
11893
11907
|
}
|
|
11894
11908
|
isRecord(attributeValueType) {
|
|
11895
|
-
return attributeValueType ===
|
|
11909
|
+
return attributeValueType === 'RECORD';
|
|
11896
11910
|
}
|
|
11897
11911
|
isArray(attributeValueType) {
|
|
11898
|
-
return (attributeValueType.endsWith(
|
|
11899
|
-
attributeValueType !==
|
|
11912
|
+
return (attributeValueType.endsWith('_ARRAY') &&
|
|
11913
|
+
attributeValueType !== 'RECORD_ARRAY');
|
|
11900
11914
|
}
|
|
11901
11915
|
isDate(attributeValueType) {
|
|
11902
|
-
return [
|
|
11916
|
+
return ['DATE', 'DATE_TIME', 'DATE_TIME_OFFSET'].includes(attributeValueType);
|
|
11903
11917
|
}
|
|
11904
11918
|
isTime(attributeValueType) {
|
|
11905
|
-
return [
|
|
11919
|
+
return ['TIME_SPAN'].includes(attributeValueType);
|
|
11906
11920
|
}
|
|
11907
11921
|
isGeoSpatialPoint(attributeValueType) {
|
|
11908
|
-
return [
|
|
11922
|
+
return ['GEOSPATIAL_POINT'].includes(attributeValueType);
|
|
11909
11923
|
}
|
|
11910
11924
|
isBinary(attributeValueType) {
|
|
11911
|
-
return [
|
|
11925
|
+
return ['BINARY', 'BINARY_LINKED'].includes(attributeValueType);
|
|
11912
11926
|
}
|
|
11913
11927
|
isRecordArray(attributeValueType) {
|
|
11914
|
-
return attributeValueType ===
|
|
11928
|
+
return attributeValueType === 'RECORD_ARRAY';
|
|
11915
11929
|
}
|
|
11916
11930
|
isBoolean(attributeValueType) {
|
|
11917
|
-
return attributeValueType ===
|
|
11931
|
+
return attributeValueType === 'BOOLEAN';
|
|
11918
11932
|
}
|
|
11919
11933
|
isIntArray(attributeValueType) {
|
|
11920
|
-
return [
|
|
11934
|
+
return ['INT_ARRAY', 'INTEGER_ARRAY'].includes(attributeValueType);
|
|
11921
11935
|
}
|
|
11922
11936
|
isStringArray(attributeValueType) {
|
|
11923
|
-
return attributeValueType ===
|
|
11937
|
+
return attributeValueType === 'STRING_ARRAY';
|
|
11924
11938
|
}
|
|
11925
11939
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeRecognitionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
11926
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeRecognitionService, providedIn:
|
|
11940
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeRecognitionService, providedIn: 'root' });
|
|
11927
11941
|
}
|
|
11928
11942
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeRecognitionService, decorators: [{
|
|
11929
11943
|
type: Injectable,
|
|
11930
|
-
args: [{ providedIn:
|
|
11944
|
+
args: [{ providedIn: 'root' }]
|
|
11931
11945
|
}] });
|
|
11932
11946
|
|
|
11933
11947
|
/**
|
|
@@ -11935,7 +11949,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
11935
11949
|
* Provides convertGeospatialPointToGeoJSON, fileToByteArray, getFileFromValue, base64ToByteArray.
|
|
11936
11950
|
*/
|
|
11937
11951
|
function isRecordValue(value) {
|
|
11938
|
-
return typeof value ===
|
|
11952
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
11939
11953
|
}
|
|
11940
11954
|
/**
|
|
11941
11955
|
* Converts a GeoJSON-like point control ({ longitude, latitude } or { type, longitude, latitude })
|
|
@@ -11945,16 +11959,16 @@ function convertGeospatialPointToGeoJSON(point) {
|
|
|
11945
11959
|
if (!isRecordValue(point)) {
|
|
11946
11960
|
return point;
|
|
11947
11961
|
}
|
|
11948
|
-
if (
|
|
11962
|
+
if ('longitude' in point && 'latitude' in point) {
|
|
11949
11963
|
return {
|
|
11950
|
-
type:
|
|
11951
|
-
coordinates: [point[
|
|
11964
|
+
type: 'Point',
|
|
11965
|
+
coordinates: [point['longitude'], point['latitude']],
|
|
11952
11966
|
};
|
|
11953
11967
|
}
|
|
11954
|
-
if (point[
|
|
11968
|
+
if (point['type'] === 'Point' && 'longitude' in point && 'latitude' in point) {
|
|
11955
11969
|
return {
|
|
11956
|
-
type:
|
|
11957
|
-
coordinates: [point[
|
|
11970
|
+
type: 'Point',
|
|
11971
|
+
coordinates: [point['longitude'], point['latitude']],
|
|
11958
11972
|
};
|
|
11959
11973
|
}
|
|
11960
11974
|
return point;
|
|
@@ -11987,9 +12001,9 @@ function base64ToByteArray(base64) {
|
|
|
11987
12001
|
}
|
|
11988
12002
|
|
|
11989
12003
|
/** Property set on synthetic BINARY_LINKED File objects (reference/mockup without content). Used by UI to show a preview indicator. */
|
|
11990
|
-
const BINARY_LINKED_REFERENCE_FLAG =
|
|
12004
|
+
const BINARY_LINKED_REFERENCE_FLAG = '__isBinaryLinkedReference';
|
|
11991
12005
|
/** Property set on BINARY File objects restored from base64 (content is real; filename is a placeholder). Used by UI to show a reference hint. */
|
|
11992
|
-
const BINARY_REFERENCE_FLAG =
|
|
12006
|
+
const BINARY_REFERENCE_FLAG = '__isBinaryFromBase64';
|
|
11993
12007
|
/**
|
|
11994
12008
|
* Maps between form model and GraphQL API: builds form attribute definitions from CK metadata and initial/default
|
|
11995
12009
|
* values, and maps form values to create/update mutation payloads (including RECORD, BINARY, GEOSPATIAL_POINT, etc.).
|
|
@@ -11998,10 +12012,10 @@ class AttributeMapperService {
|
|
|
11998
12012
|
recognition = inject(AttributeRecognitionService);
|
|
11999
12013
|
metadataResolver = inject(AttributeMetadataResolverService);
|
|
12000
12014
|
isRecordValue(value) {
|
|
12001
|
-
return typeof value ===
|
|
12015
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
12002
12016
|
}
|
|
12003
12017
|
isCkAttributeMetadata(attr) {
|
|
12004
|
-
return
|
|
12018
|
+
return 'attribute' in attr || 'ckAttributeId' in attr;
|
|
12005
12019
|
}
|
|
12006
12020
|
// ─── Form ← API: attribute definition + initial/default value ─────────────────────
|
|
12007
12021
|
/**
|
|
@@ -12009,8 +12023,8 @@ class AttributeMapperService {
|
|
|
12009
12023
|
* Value priority: 1. Initial (entity data), 2. Default (CK defaultValues), 3. Empty (placeholder).
|
|
12010
12024
|
*/
|
|
12011
12025
|
mapToFormAttribute(attr, rawInitialValue) {
|
|
12012
|
-
const attributeName = attr?.attributeName ??
|
|
12013
|
-
const type = attr?.attributeValueType ??
|
|
12026
|
+
const attributeName = attr?.attributeName ?? '';
|
|
12027
|
+
const type = attr?.attributeValueType ?? '';
|
|
12014
12028
|
let parsedValue;
|
|
12015
12029
|
const hasInitial = rawInitialValue !== undefined && rawInitialValue !== null;
|
|
12016
12030
|
if (hasInitial) {
|
|
@@ -12084,20 +12098,20 @@ class AttributeMapperService {
|
|
|
12084
12098
|
async mapSingleAttributeToInput(key, value, metadata) {
|
|
12085
12099
|
const type = metadata?.attributeValueType;
|
|
12086
12100
|
// type is set only when metadata exists, so the non-null assertion is safe inside these branches.
|
|
12087
|
-
if (type ===
|
|
12101
|
+
if (type === 'BINARY_LINKED') {
|
|
12088
12102
|
return this.mapBinaryLinkedAttribute(key, value, metadata);
|
|
12089
12103
|
}
|
|
12090
12104
|
if (value === null || value === undefined) {
|
|
12091
12105
|
return metadata?.isOptional ? { attributeName: key, value: null } : null;
|
|
12092
12106
|
}
|
|
12093
|
-
if (type ===
|
|
12107
|
+
if (type === 'BINARY' && this.isOptionalEmptyBinary(metadata, value)) {
|
|
12094
12108
|
return { attributeName: key, value: null };
|
|
12095
12109
|
}
|
|
12096
12110
|
// Empty RECORD (empty object) and RECORD_ARRAY (empty array): send null when optional, skip otherwise.
|
|
12097
|
-
if (type ===
|
|
12111
|
+
if (type === 'RECORD' && this.isValueEmpty(value)) {
|
|
12098
12112
|
return metadata?.isOptional ? { attributeName: key, value: null } : null;
|
|
12099
12113
|
}
|
|
12100
|
-
if (type ===
|
|
12114
|
+
if (type === 'RECORD_ARRAY' && Array.isArray(value) && value.length === 0) {
|
|
12101
12115
|
return metadata?.isOptional ? { attributeName: key, value: null } : null;
|
|
12102
12116
|
}
|
|
12103
12117
|
if (metadata?.isOptional &&
|
|
@@ -12219,26 +12233,26 @@ class AttributeMapperService {
|
|
|
12219
12233
|
async processAttributeValue(value, metadata) {
|
|
12220
12234
|
const attributeType = metadata?.attributeValueType;
|
|
12221
12235
|
switch (attributeType) {
|
|
12222
|
-
case
|
|
12236
|
+
case 'RECORD':
|
|
12223
12237
|
return await this.mapRecordValueToGraphQL(value, metadata?.id?.ckId);
|
|
12224
|
-
case
|
|
12238
|
+
case 'RECORD_ARRAY':
|
|
12225
12239
|
return await this.mapRecordArrayValueToGraphQL(value, metadata?.id?.ckId);
|
|
12226
|
-
case
|
|
12240
|
+
case 'GEOSPATIAL_POINT':
|
|
12227
12241
|
return convertGeospatialPointToGeoJSON(value);
|
|
12228
|
-
case
|
|
12242
|
+
case 'TIME_SPAN':
|
|
12229
12243
|
return this.convertTimeSpanToSeconds(value);
|
|
12230
|
-
case
|
|
12244
|
+
case 'BINARY':
|
|
12231
12245
|
// MeshMakers expects byte[] (array of numbers). Accept File, ArrayBuffer, base64 string, or byte[].
|
|
12232
12246
|
return await this.convertBinaryToByteArray(value);
|
|
12233
|
-
case
|
|
12247
|
+
case 'BINARY_LINKED':
|
|
12234
12248
|
// MeshMakers: prefer File for multipart upload; also accept base64 string, ArrayBuffer or byte[].
|
|
12235
12249
|
return this.convertBinaryLinkedValue(value);
|
|
12236
|
-
case
|
|
12237
|
-
return this.normalizePrimitiveArray(value,
|
|
12238
|
-
case
|
|
12239
|
-
return this.normalizePrimitiveArray(value,
|
|
12240
|
-
case
|
|
12241
|
-
case
|
|
12250
|
+
case 'INTEGER_ARRAY':
|
|
12251
|
+
return this.normalizePrimitiveArray(value, 'number');
|
|
12252
|
+
case 'STRING_ARRAY':
|
|
12253
|
+
return this.normalizePrimitiveArray(value, 'string');
|
|
12254
|
+
case 'DATE_TIME':
|
|
12255
|
+
case 'DATE_TIME_OFFSET':
|
|
12242
12256
|
if (value instanceof Date) {
|
|
12243
12257
|
if (isNaN(value.getTime())) {
|
|
12244
12258
|
return null;
|
|
@@ -12255,17 +12269,17 @@ class AttributeMapperService {
|
|
|
12255
12269
|
normalizePrimitiveArray(value, mode) {
|
|
12256
12270
|
if (!Array.isArray(value) || value.length === 0)
|
|
12257
12271
|
return null;
|
|
12258
|
-
if (mode ===
|
|
12272
|
+
if (mode === 'number') {
|
|
12259
12273
|
return value.map((v) => {
|
|
12260
|
-
if (this.isRecordValue(v) &&
|
|
12261
|
-
return Number(v[
|
|
12274
|
+
if (this.isRecordValue(v) && 'key' in v) {
|
|
12275
|
+
return Number(v['key']);
|
|
12262
12276
|
}
|
|
12263
12277
|
return Number(v);
|
|
12264
12278
|
});
|
|
12265
12279
|
}
|
|
12266
12280
|
return value.map((v) => {
|
|
12267
|
-
if (this.isRecordValue(v) &&
|
|
12268
|
-
return String(v[
|
|
12281
|
+
if (this.isRecordValue(v) && 'key' in v) {
|
|
12282
|
+
return String(v['key']);
|
|
12269
12283
|
}
|
|
12270
12284
|
return String(v);
|
|
12271
12285
|
});
|
|
@@ -12278,7 +12292,7 @@ class AttributeMapperService {
|
|
|
12278
12292
|
normalizeGeospatialPointForForm(value) {
|
|
12279
12293
|
if (value == null)
|
|
12280
12294
|
return { longitude: null, latitude: null };
|
|
12281
|
-
if (typeof value ===
|
|
12295
|
+
if (typeof value === 'string') {
|
|
12282
12296
|
try {
|
|
12283
12297
|
value = JSON.parse(value);
|
|
12284
12298
|
}
|
|
@@ -12289,17 +12303,17 @@ class AttributeMapperService {
|
|
|
12289
12303
|
if (!this.isRecordValue(value)) {
|
|
12290
12304
|
return { longitude: null, latitude: null };
|
|
12291
12305
|
}
|
|
12292
|
-
if (
|
|
12306
|
+
if ('longitude' in value && 'latitude' in value) {
|
|
12293
12307
|
return {
|
|
12294
|
-
longitude: value[
|
|
12295
|
-
latitude: value[
|
|
12308
|
+
longitude: value['longitude'] != null ? Number(value['longitude']) : null,
|
|
12309
|
+
latitude: value['latitude'] != null ? Number(value['latitude']) : null,
|
|
12296
12310
|
};
|
|
12297
12311
|
}
|
|
12298
|
-
if (Array.isArray(value[
|
|
12299
|
-
value[
|
|
12312
|
+
if (Array.isArray(value['coordinates']) &&
|
|
12313
|
+
value['coordinates'].length >= 2) {
|
|
12300
12314
|
return {
|
|
12301
|
-
longitude: Number(value[
|
|
12302
|
-
latitude: Number(value[
|
|
12315
|
+
longitude: Number(value['coordinates'][0]),
|
|
12316
|
+
latitude: Number(value['coordinates'][1]),
|
|
12303
12317
|
};
|
|
12304
12318
|
}
|
|
12305
12319
|
return { longitude: null, latitude: null };
|
|
@@ -12314,7 +12328,7 @@ class AttributeMapperService {
|
|
|
12314
12328
|
const seconds = value.getSeconds();
|
|
12315
12329
|
return hours * 3600 + minutes * 60 + seconds;
|
|
12316
12330
|
}
|
|
12317
|
-
if (typeof value ===
|
|
12331
|
+
if (typeof value === 'number') {
|
|
12318
12332
|
return value;
|
|
12319
12333
|
}
|
|
12320
12334
|
return 0;
|
|
@@ -12326,7 +12340,7 @@ class AttributeMapperService {
|
|
|
12326
12340
|
* Note: A base64 string carries only the raw bytes; it does not contain filename or other metadata.
|
|
12327
12341
|
* The filename (and contentType) must be supplied by the caller when known (e.g. from a BINARY_LINKED payload); for plain BINARY we use a default name.
|
|
12328
12342
|
*/
|
|
12329
|
-
base64ToFile(base64, filename =
|
|
12343
|
+
base64ToFile(base64, filename = 'file.bin', contentType = 'application/octet-stream') {
|
|
12330
12344
|
try {
|
|
12331
12345
|
const bytes = base64ToByteArray(base64);
|
|
12332
12346
|
const uint8 = new Uint8Array(bytes);
|
|
@@ -12343,7 +12357,7 @@ class AttributeMapperService {
|
|
|
12343
12357
|
*/
|
|
12344
12358
|
async convertBinaryToByteArray(value) {
|
|
12345
12359
|
if (Array.isArray(value) &&
|
|
12346
|
-
(value.length === 0 || typeof value[0] ===
|
|
12360
|
+
(value.length === 0 || typeof value[0] === 'number')) {
|
|
12347
12361
|
return value;
|
|
12348
12362
|
}
|
|
12349
12363
|
const fileValue = getFileFromValue(value);
|
|
@@ -12352,16 +12366,16 @@ class AttributeMapperService {
|
|
|
12352
12366
|
return await fileToByteArray(fileValue);
|
|
12353
12367
|
}
|
|
12354
12368
|
catch (e) {
|
|
12355
|
-
console.error(
|
|
12369
|
+
console.error('Error converting File to byte array:', e);
|
|
12356
12370
|
return value;
|
|
12357
12371
|
}
|
|
12358
12372
|
}
|
|
12359
|
-
if (typeof value ===
|
|
12373
|
+
if (typeof value === 'string') {
|
|
12360
12374
|
try {
|
|
12361
12375
|
return base64ToByteArray(value);
|
|
12362
12376
|
}
|
|
12363
12377
|
catch (e) {
|
|
12364
|
-
console.error(
|
|
12378
|
+
console.error('Error converting Base64 to byte array:', e);
|
|
12365
12379
|
return value;
|
|
12366
12380
|
}
|
|
12367
12381
|
}
|
|
@@ -12377,7 +12391,7 @@ class AttributeMapperService {
|
|
|
12377
12391
|
return value;
|
|
12378
12392
|
if (Array.isArray(value) && value[0] instanceof File)
|
|
12379
12393
|
return value[0];
|
|
12380
|
-
if (typeof value ===
|
|
12394
|
+
if (typeof value === 'string') {
|
|
12381
12395
|
try {
|
|
12382
12396
|
return base64ToByteArray(value);
|
|
12383
12397
|
}
|
|
@@ -12385,43 +12399,43 @@ class AttributeMapperService {
|
|
|
12385
12399
|
// Re-throw instead of returning null: a malformed base64 must not silently degrade
|
|
12386
12400
|
// to null, which the backend may interpret as "clear/detach" the linked binary.
|
|
12387
12401
|
// Surfacing the error lets the caller present a validation message to the user.
|
|
12388
|
-
console.error(
|
|
12389
|
-
throw new Error(
|
|
12402
|
+
console.error('Error converting BINARY_LINKED base64 to byte array:', e);
|
|
12403
|
+
throw new Error('Invalid base64 value for BINARY_LINKED attribute', { cause: e });
|
|
12390
12404
|
}
|
|
12391
12405
|
}
|
|
12392
12406
|
if (value instanceof ArrayBuffer) {
|
|
12393
12407
|
return this.arrayBufferToByteArray(value);
|
|
12394
12408
|
}
|
|
12395
|
-
if (Array.isArray(value) && typeof value[0] ===
|
|
12409
|
+
if (Array.isArray(value) && typeof value[0] === 'number') {
|
|
12396
12410
|
return value;
|
|
12397
12411
|
}
|
|
12398
12412
|
return null;
|
|
12399
12413
|
}
|
|
12400
12414
|
// ─── Type recognition (single responsibility: attribute type checks) ─────────────
|
|
12401
12415
|
isEnum(attr) {
|
|
12402
|
-
return attr.attributeValueType ===
|
|
12416
|
+
return attr.attributeValueType === 'ENUM';
|
|
12403
12417
|
}
|
|
12404
12418
|
/**
|
|
12405
12419
|
* Checks whether the attribute is a record type.
|
|
12406
12420
|
*/
|
|
12407
12421
|
isRecord(attr) {
|
|
12408
|
-
return attr.attributeValueType ===
|
|
12422
|
+
return attr.attributeValueType === 'RECORD';
|
|
12409
12423
|
}
|
|
12410
12424
|
/**
|
|
12411
12425
|
* Checks whether the attribute is a record array type.
|
|
12412
12426
|
*/
|
|
12413
12427
|
isRecordArray(attr) {
|
|
12414
|
-
return attr.attributeValueType ===
|
|
12428
|
+
return attr.attributeValueType === 'RECORD_ARRAY';
|
|
12415
12429
|
}
|
|
12416
12430
|
/**
|
|
12417
12431
|
* Checks whether the attribute is a geospatial point type.
|
|
12418
12432
|
*/
|
|
12419
12433
|
isGeospatialPoint(attr) {
|
|
12420
|
-
return attr.attributeValueType ===
|
|
12434
|
+
return attr.attributeValueType === 'GEOSPATIAL_POINT';
|
|
12421
12435
|
}
|
|
12422
12436
|
/** True when optional scalar/array is "cleared" (send null to remove value). */
|
|
12423
12437
|
isOptionalEmptyScalarOrArray(value, metadata) {
|
|
12424
|
-
if (value ===
|
|
12438
|
+
if (value === '' || value == null)
|
|
12425
12439
|
return true;
|
|
12426
12440
|
const t = metadata?.attributeValueType;
|
|
12427
12441
|
if (this.recognition.isArray(t) && Array.isArray(value) && value.length === 0)
|
|
@@ -12455,7 +12469,7 @@ class AttributeMapperService {
|
|
|
12455
12469
|
const val = value[key];
|
|
12456
12470
|
return (val === null ||
|
|
12457
12471
|
val === undefined ||
|
|
12458
|
-
val ===
|
|
12472
|
+
val === '' ||
|
|
12459
12473
|
(this.isRecordValue(val) && Object.keys(val).length === 0) ||
|
|
12460
12474
|
(Array.isArray(val) && val.length === 0));
|
|
12461
12475
|
});
|
|
@@ -12475,7 +12489,7 @@ class AttributeMapperService {
|
|
|
12475
12489
|
if (Array.isArray(value))
|
|
12476
12490
|
return value;
|
|
12477
12491
|
if (this.isRecordValue(value)) {
|
|
12478
|
-
const attrs = value[
|
|
12492
|
+
const attrs = value['attributes'];
|
|
12479
12493
|
return Array.isArray(attrs) ? attrs : [];
|
|
12480
12494
|
}
|
|
12481
12495
|
return [];
|
|
@@ -12488,7 +12502,7 @@ class AttributeMapperService {
|
|
|
12488
12502
|
if (Array.isArray(item))
|
|
12489
12503
|
return item; // Already parsed
|
|
12490
12504
|
if (this.isRecordValue(item)) {
|
|
12491
|
-
const attrs = item[
|
|
12505
|
+
const attrs = item['attributes'];
|
|
12492
12506
|
return Array.isArray(attrs) ? attrs : [];
|
|
12493
12507
|
}
|
|
12494
12508
|
return [];
|
|
@@ -12509,8 +12523,8 @@ class AttributeMapperService {
|
|
|
12509
12523
|
return this.parseTimeSpanForForm(value);
|
|
12510
12524
|
}
|
|
12511
12525
|
if (this.recognition.isDate(type)) {
|
|
12512
|
-
if (typeof value ===
|
|
12513
|
-
typeof value ===
|
|
12526
|
+
if (typeof value === 'string' ||
|
|
12527
|
+
typeof value === 'number' ||
|
|
12514
12528
|
value instanceof Date) {
|
|
12515
12529
|
const date = new Date(value);
|
|
12516
12530
|
return isNaN(date.getTime()) ? null : date;
|
|
@@ -12520,10 +12534,10 @@ class AttributeMapperService {
|
|
|
12520
12534
|
if (this.recognition.isGeoSpatialPoint(type)) {
|
|
12521
12535
|
return this.normalizeGeospatialPointForForm(value);
|
|
12522
12536
|
}
|
|
12523
|
-
if (type ===
|
|
12537
|
+
if (type === 'BINARY') {
|
|
12524
12538
|
return this.parseBinaryForForm(value);
|
|
12525
12539
|
}
|
|
12526
|
-
if (type ===
|
|
12540
|
+
if (type === 'BINARY_LINKED') {
|
|
12527
12541
|
return this.parseBinaryLinkedForForm(value);
|
|
12528
12542
|
}
|
|
12529
12543
|
return value;
|
|
@@ -12537,7 +12551,7 @@ class AttributeMapperService {
|
|
|
12537
12551
|
return null;
|
|
12538
12552
|
if (value instanceof Date)
|
|
12539
12553
|
return isNaN(value.getTime()) ? null : value;
|
|
12540
|
-
if (typeof value ===
|
|
12554
|
+
if (typeof value === 'number') {
|
|
12541
12555
|
const d = new Date(0);
|
|
12542
12556
|
d.setUTCHours(0, 0, 0, 0);
|
|
12543
12557
|
d.setUTCSeconds(value % 86400);
|
|
@@ -12559,15 +12573,15 @@ class AttributeMapperService {
|
|
|
12559
12573
|
* When a dot is present, the segment before the dot is always days (e.g. "44682.00:00:00" = 44682 days).
|
|
12560
12574
|
*/
|
|
12561
12575
|
parseTimeSpanStringToSeconds(s) {
|
|
12562
|
-
const parts = s.split(
|
|
12563
|
-
let timePart = parts[0] ??
|
|
12576
|
+
const parts = s.split('.');
|
|
12577
|
+
let timePart = parts[0] ?? '';
|
|
12564
12578
|
let days = 0;
|
|
12565
12579
|
if (parts.length >= 2) {
|
|
12566
12580
|
const firstNum = parseInt(parts[0], 10);
|
|
12567
12581
|
if (!Number.isNaN(firstNum)) {
|
|
12568
12582
|
days = firstNum;
|
|
12569
12583
|
}
|
|
12570
|
-
timePart = parts[1] ??
|
|
12584
|
+
timePart = parts[1] ?? '00:00:00';
|
|
12571
12585
|
}
|
|
12572
12586
|
const timeMatch = timePart.match(/^(\d+):(\d+):(\d+)(?:\.(\d+))?$/);
|
|
12573
12587
|
if (!timeMatch) {
|
|
@@ -12593,8 +12607,8 @@ class AttributeMapperService {
|
|
|
12593
12607
|
return [value];
|
|
12594
12608
|
if (Array.isArray(value) && value[0] instanceof File)
|
|
12595
12609
|
return value;
|
|
12596
|
-
if (typeof value ===
|
|
12597
|
-
const file = this.base64ToFile(value,
|
|
12610
|
+
if (typeof value === 'string') {
|
|
12611
|
+
const file = this.base64ToFile(value, 'file.bin', 'application/octet-stream');
|
|
12598
12612
|
if (file) {
|
|
12599
12613
|
file[BINARY_REFERENCE_FLAG] =
|
|
12600
12614
|
true;
|
|
@@ -12615,27 +12629,27 @@ class AttributeMapperService {
|
|
|
12615
12629
|
return [];
|
|
12616
12630
|
if (value instanceof File)
|
|
12617
12631
|
return [value];
|
|
12618
|
-
const filename = (typeof value[
|
|
12619
|
-
? value[
|
|
12620
|
-
: typeof value[
|
|
12621
|
-
? value[
|
|
12622
|
-
: null) ??
|
|
12623
|
-
const contentType = (typeof value[
|
|
12624
|
-
? value[
|
|
12625
|
-
: null) ??
|
|
12626
|
-
const base64 = typeof value[
|
|
12627
|
-
? value[
|
|
12628
|
-
: typeof value[
|
|
12629
|
-
? value[
|
|
12630
|
-
: typeof value[
|
|
12631
|
-
? value[
|
|
12632
|
+
const filename = (typeof value['filename'] === 'string'
|
|
12633
|
+
? value['filename']
|
|
12634
|
+
: typeof value['fileName'] === 'string'
|
|
12635
|
+
? value['fileName']
|
|
12636
|
+
: null) ?? 'document.bin';
|
|
12637
|
+
const contentType = (typeof value['contentType'] === 'string'
|
|
12638
|
+
? value['contentType']
|
|
12639
|
+
: null) ?? 'application/octet-stream';
|
|
12640
|
+
const base64 = typeof value['contentBase64'] === 'string'
|
|
12641
|
+
? value['contentBase64']
|
|
12642
|
+
: typeof value['data'] === 'string'
|
|
12643
|
+
? value['data']
|
|
12644
|
+
: typeof value['base64'] === 'string'
|
|
12645
|
+
? value['base64']
|
|
12632
12646
|
: null;
|
|
12633
12647
|
if (base64) {
|
|
12634
12648
|
const file = this.base64ToFile(base64, filename, contentType);
|
|
12635
12649
|
if (file)
|
|
12636
12650
|
return [file];
|
|
12637
12651
|
}
|
|
12638
|
-
const size = typeof value[
|
|
12652
|
+
const size = typeof value['size'] === 'number' ? Math.max(0, value['size']) : 0;
|
|
12639
12653
|
const blob = size > 0
|
|
12640
12654
|
? new Blob([new ArrayBuffer(size)], { type: contentType })
|
|
12641
12655
|
: new Blob([], { type: contentType });
|
|
@@ -12652,7 +12666,7 @@ class AttributeMapperService {
|
|
|
12652
12666
|
return [];
|
|
12653
12667
|
if (this.recognition.isGeoSpatialPoint(type))
|
|
12654
12668
|
return { longitude: null, latitude: null };
|
|
12655
|
-
if (type ===
|
|
12669
|
+
if (type === 'BINARY' || type === 'BINARY_LINKED')
|
|
12656
12670
|
return []; // FileSelect expects File[]
|
|
12657
12671
|
return null;
|
|
12658
12672
|
}
|
|
@@ -12665,22 +12679,22 @@ class AttributeMapperService {
|
|
|
12665
12679
|
}
|
|
12666
12680
|
if (this.recognition.isEnum(type)) {
|
|
12667
12681
|
const options = this.getEnumOptions(attrMetadata);
|
|
12668
|
-
const key = typeof rawDefault ===
|
|
12682
|
+
const key = typeof rawDefault === 'number' ? rawDefault : Number(rawDefault);
|
|
12669
12683
|
const found = options.find((opt) => opt.key === key);
|
|
12670
12684
|
return found ? found.key : key;
|
|
12671
12685
|
}
|
|
12672
12686
|
if (this.recognition.isNumber(type)) {
|
|
12673
|
-
return typeof rawDefault ===
|
|
12687
|
+
return typeof rawDefault === 'number' ? rawDefault : Number(rawDefault);
|
|
12674
12688
|
}
|
|
12675
12689
|
if (this.recognition.isString(type)) {
|
|
12676
12690
|
return String(rawDefault);
|
|
12677
12691
|
}
|
|
12678
12692
|
if (this.recognition.isBoolean(type)) {
|
|
12679
|
-
return rawDefault === true || rawDefault ===
|
|
12693
|
+
return rawDefault === true || rawDefault === 'true' || rawDefault === 1;
|
|
12680
12694
|
}
|
|
12681
12695
|
if (this.recognition.isDate(type)) {
|
|
12682
|
-
if (typeof rawDefault ===
|
|
12683
|
-
typeof rawDefault ===
|
|
12696
|
+
if (typeof rawDefault === 'string' ||
|
|
12697
|
+
typeof rawDefault === 'number' ||
|
|
12684
12698
|
rawDefault instanceof Date) {
|
|
12685
12699
|
const date = new Date(rawDefault);
|
|
12686
12700
|
return isNaN(date.getTime()) ? null : date;
|
|
@@ -12688,14 +12702,14 @@ class AttributeMapperService {
|
|
|
12688
12702
|
return null;
|
|
12689
12703
|
}
|
|
12690
12704
|
if (this.recognition.isTime(type)) {
|
|
12691
|
-
if (typeof rawDefault ===
|
|
12705
|
+
if (typeof rawDefault === 'number') {
|
|
12692
12706
|
const d = new Date(0);
|
|
12693
12707
|
d.setUTCHours(0, 0, 0, 0);
|
|
12694
12708
|
d.setSeconds(rawDefault);
|
|
12695
12709
|
return d;
|
|
12696
12710
|
}
|
|
12697
|
-
if (typeof rawDefault ===
|
|
12698
|
-
typeof rawDefault ===
|
|
12711
|
+
if (typeof rawDefault === 'string' ||
|
|
12712
|
+
typeof rawDefault === 'number' ||
|
|
12699
12713
|
rawDefault instanceof Date) {
|
|
12700
12714
|
const date = new Date(rawDefault);
|
|
12701
12715
|
return isNaN(date.getTime()) ? null : date;
|
|
@@ -12720,16 +12734,16 @@ class AttributeMapperService {
|
|
|
12720
12734
|
/** Returns primitive arrays for Kendo MultiSelect (valuePrimitive: true). */
|
|
12721
12735
|
parseArrayType(type, value) {
|
|
12722
12736
|
if (this.recognition.isStringArray(type)) {
|
|
12723
|
-
const source = this.isRecordValue(value) &&
|
|
12737
|
+
const source = this.isRecordValue(value) && '_v' in value ? value['_v'] : value;
|
|
12724
12738
|
if (!Array.isArray(source))
|
|
12725
12739
|
return [];
|
|
12726
|
-
return source.map((v) => this.isRecordValue(v) &&
|
|
12740
|
+
return source.map((v) => this.isRecordValue(v) && 'key' in v ? String(v['key']) : String(v));
|
|
12727
12741
|
}
|
|
12728
12742
|
if (this.recognition.isIntArray(type)) {
|
|
12729
|
-
const source = this.isRecordValue(value) &&
|
|
12743
|
+
const source = this.isRecordValue(value) && '_v' in value ? value['_v'] : value; // Handle _v wrapper format from upstream
|
|
12730
12744
|
if (!Array.isArray(source))
|
|
12731
12745
|
return [];
|
|
12732
|
-
return source.map((v) => this.isRecordValue(v) &&
|
|
12746
|
+
return source.map((v) => this.isRecordValue(v) && 'key' in v ? Number(v['key']) : Number(v));
|
|
12733
12747
|
}
|
|
12734
12748
|
return [];
|
|
12735
12749
|
}
|
|
@@ -12739,19 +12753,19 @@ class AttributeMapperService {
|
|
|
12739
12753
|
}
|
|
12740
12754
|
if (this.isEnum(attr)) {
|
|
12741
12755
|
return {
|
|
12742
|
-
ckId: attr.attribute?.ckEnum?.ckEnumId?.fullName ??
|
|
12756
|
+
ckId: attr.attribute?.ckEnum?.ckEnumId?.fullName ?? '',
|
|
12743
12757
|
rtId: null,
|
|
12744
12758
|
};
|
|
12745
12759
|
}
|
|
12746
12760
|
else if (this.isRecord(attr) || this.isRecordArray(attr)) {
|
|
12747
12761
|
return {
|
|
12748
|
-
ckId: attr.attribute?.ckRecord?.ckRecordId?.fullName ??
|
|
12762
|
+
ckId: attr.attribute?.ckRecord?.ckRecordId?.fullName ?? '',
|
|
12749
12763
|
rtId: null,
|
|
12750
12764
|
};
|
|
12751
12765
|
}
|
|
12752
12766
|
else {
|
|
12753
12767
|
return {
|
|
12754
|
-
ckId: attr.ckAttributeId?.fullName ??
|
|
12768
|
+
ckId: attr.ckAttributeId?.fullName ?? '',
|
|
12755
12769
|
rtId: null,
|
|
12756
12770
|
};
|
|
12757
12771
|
}
|
|
@@ -12769,12 +12783,12 @@ class AttributeMapperService {
|
|
|
12769
12783
|
return [];
|
|
12770
12784
|
}
|
|
12771
12785
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeMapperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
12772
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeMapperService, providedIn:
|
|
12786
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeMapperService, providedIn: 'root' });
|
|
12773
12787
|
}
|
|
12774
12788
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeMapperService, decorators: [{
|
|
12775
12789
|
type: Injectable,
|
|
12776
12790
|
args: [{
|
|
12777
|
-
providedIn:
|
|
12791
|
+
providedIn: 'root',
|
|
12778
12792
|
}]
|
|
12779
12793
|
}] });
|
|
12780
12794
|
|
|
@@ -12850,15 +12864,15 @@ class AttributeFieldComponent {
|
|
|
12850
12864
|
/** Value to restore on UNDO. If not set, UNDO resets to null. */
|
|
12851
12865
|
baselineValue = input(/* @ts-ignore */
|
|
12852
12866
|
...(ngDevMode ? [undefined, { debugName: "baselineValue" }] : /* istanbul ignore next */ []));
|
|
12853
|
-
fieldId = input(
|
|
12867
|
+
fieldId = input('', /* @ts-ignore */
|
|
12854
12868
|
...(ngDevMode ? [{ debugName: "fieldId" }] : /* istanbul ignore next */ []));
|
|
12855
12869
|
overrideLabelText = input(/* @ts-ignore */
|
|
12856
12870
|
...(ngDevMode ? [undefined, { debugName: "overrideLabelText" }] : /* istanbul ignore next */ []));
|
|
12857
12871
|
showUndoButton = input(true, /* @ts-ignore */
|
|
12858
12872
|
...(ngDevMode ? [{ debugName: "showUndoButton" }] : /* istanbul ignore next */ []));
|
|
12859
|
-
undoButtonSize = input(
|
|
12873
|
+
undoButtonSize = input('medium', /* @ts-ignore */
|
|
12860
12874
|
...(ngDevMode ? [{ debugName: "undoButtonSize" }] : /* istanbul ignore next */ []));
|
|
12861
|
-
errorMessage = input(
|
|
12875
|
+
errorMessage = input('This field is required or invalid.', /* @ts-ignore */
|
|
12862
12876
|
...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
|
|
12863
12877
|
hintText = input(/* @ts-ignore */
|
|
12864
12878
|
...(ngDevMode ? [undefined, { debugName: "hintText" }] : /* istanbul ignore next */ []));
|
|
@@ -12880,7 +12894,7 @@ class AttributeFieldComponent {
|
|
|
12880
12894
|
this.undo.emit();
|
|
12881
12895
|
}
|
|
12882
12896
|
hasHint() {
|
|
12883
|
-
return this.hintText() != null && this.hintText() !==
|
|
12897
|
+
return this.hintText() != null && this.hintText() !== '';
|
|
12884
12898
|
}
|
|
12885
12899
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12886
12900
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: AttributeFieldComponent, isStandalone: true, selector: "mm-attribute-field", inputs: { attribute: { classPropertyName: "attribute", publicName: "attribute", isSignal: true, isRequired: true, transformFunction: null }, control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: true, transformFunction: null }, baselineValue: { classPropertyName: "baselineValue", publicName: "baselineValue", isSignal: true, isRequired: false, transformFunction: null }, fieldId: { classPropertyName: "fieldId", publicName: "fieldId", isSignal: true, isRequired: false, transformFunction: null }, overrideLabelText: { classPropertyName: "overrideLabelText", publicName: "overrideLabelText", isSignal: true, isRequired: false, transformFunction: null }, showUndoButton: { classPropertyName: "showUndoButton", publicName: "showUndoButton", isSignal: true, isRequired: false, transformFunction: null }, undoButtonSize: { classPropertyName: "undoButtonSize", publicName: "undoButtonSize", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, hintText: { classPropertyName: "hintText", publicName: "hintText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { undo: "undo" }, ngImport: i0, template: `
|
|
@@ -12922,7 +12936,7 @@ class AttributeFieldComponent {
|
|
|
12922
12936
|
}
|
|
12923
12937
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributeFieldComponent, decorators: [{
|
|
12924
12938
|
type: Component,
|
|
12925
|
-
args: [{ selector:
|
|
12939
|
+
args: [{ selector: 'mm-attribute-field', standalone: true, imports: [CommonModule, ReactiveFormsModule, KENDO_LABEL, KENDO_BUTTONS], template: `
|
|
12926
12940
|
<div class="k-form-field">
|
|
12927
12941
|
<kendo-label
|
|
12928
12942
|
[for]="fieldId()"
|
|
@@ -12976,7 +12990,7 @@ class AttributesGroupComponent {
|
|
|
12976
12990
|
recognition = inject(AttributeRecognitionService);
|
|
12977
12991
|
mapper = inject(AttributeMapperService);
|
|
12978
12992
|
isRecordValue(value) {
|
|
12979
|
-
return typeof value ===
|
|
12993
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
12980
12994
|
}
|
|
12981
12995
|
// ─── Inputs (public API for parent / template) ─────────────────────────────────────
|
|
12982
12996
|
ckId = input.required(/* @ts-ignore */
|
|
@@ -13090,8 +13104,8 @@ class AttributesGroupComponent {
|
|
|
13090
13104
|
}
|
|
13091
13105
|
const geoValue = this.isRecordValue(initialValue) ? initialValue : {};
|
|
13092
13106
|
const geoGroup = new FormGroup({
|
|
13093
|
-
longitude: new FormControl(geoValue[
|
|
13094
|
-
latitude: new FormControl(geoValue[
|
|
13107
|
+
longitude: new FormControl(geoValue['longitude'] ?? null, longitudeValidators),
|
|
13108
|
+
latitude: new FormControl(geoValue['latitude'] ?? null, latitudeValidators),
|
|
13095
13109
|
});
|
|
13096
13110
|
form.addControl(attr.attributeName, geoGroup);
|
|
13097
13111
|
this.setBaselineForAttr(attr, initialValue);
|
|
@@ -13111,8 +13125,8 @@ class AttributesGroupComponent {
|
|
|
13111
13125
|
const key = attr.attributeName;
|
|
13112
13126
|
if (this.recognition.isGeoSpatialPoint(attr.attributeValueType)) {
|
|
13113
13127
|
const v = this.isRecordValue(value) ? value : {};
|
|
13114
|
-
this.baselineValues.set(key +
|
|
13115
|
-
this.baselineValues.set(key +
|
|
13128
|
+
this.baselineValues.set(key + '.longitude', this.cloneForBaseline(v['longitude'] ?? null));
|
|
13129
|
+
this.baselineValues.set(key + '.latitude', this.cloneForBaseline(v['latitude'] ?? null));
|
|
13116
13130
|
}
|
|
13117
13131
|
else if (!this.recognition.isRecord(attr.attributeValueType) &&
|
|
13118
13132
|
!this.recognition.isRecordArray(attr.attributeValueType)) {
|
|
@@ -13124,7 +13138,7 @@ class AttributesGroupComponent {
|
|
|
13124
13138
|
return v;
|
|
13125
13139
|
if (v instanceof Date)
|
|
13126
13140
|
return new Date(v.getTime());
|
|
13127
|
-
if (typeof v ===
|
|
13141
|
+
if (typeof v === 'object' && v instanceof File)
|
|
13128
13142
|
return v;
|
|
13129
13143
|
if (Array.isArray(v))
|
|
13130
13144
|
return v.map((x) => this.cloneForBaseline(x));
|
|
@@ -13154,7 +13168,7 @@ class AttributesGroupComponent {
|
|
|
13154
13168
|
: !this.isRecordValue(value) || Object.keys(value).length === 0;
|
|
13155
13169
|
if (this.recognition.isGeoSpatialPoint(type)) {
|
|
13156
13170
|
const v = this.isRecordValue(value) ? value : {};
|
|
13157
|
-
return v[
|
|
13171
|
+
return v['longitude'] == null && v['latitude'] == null;
|
|
13158
13172
|
}
|
|
13159
13173
|
if (this.recognition.isArray(type))
|
|
13160
13174
|
return Array.isArray(value) && value.length === 0;
|
|
@@ -13162,12 +13176,12 @@ class AttributesGroupComponent {
|
|
|
13162
13176
|
}
|
|
13163
13177
|
isControlValueEmpty(control, type) {
|
|
13164
13178
|
const value = control.value;
|
|
13165
|
-
if (value === null || value === undefined || value ===
|
|
13179
|
+
if (value === null || value === undefined || value === '')
|
|
13166
13180
|
return true;
|
|
13167
13181
|
if (this.recognition.isRecordArray(type) || this.recognition.isRecord(type)) {
|
|
13168
13182
|
if (Array.isArray(value))
|
|
13169
13183
|
return value.length === 0;
|
|
13170
|
-
const obj = value && typeof value ===
|
|
13184
|
+
const obj = value && typeof value === 'object' ? value : {};
|
|
13171
13185
|
return Object.keys(obj).length === 0;
|
|
13172
13186
|
}
|
|
13173
13187
|
if (this.recognition.isGeoSpatialPoint(type)) {
|
|
@@ -13274,8 +13288,8 @@ class AttributesGroupComponent {
|
|
|
13274
13288
|
const item = raw[index]?.attributes ?? EMPTY_INITIAL_VALUES;
|
|
13275
13289
|
return Array.isArray(item) ? item : EMPTY_INITIAL_VALUES;
|
|
13276
13290
|
}
|
|
13277
|
-
if (this.isRecordValue(raw) && Array.isArray(raw[
|
|
13278
|
-
return raw[
|
|
13291
|
+
if (this.isRecordValue(raw) && Array.isArray(raw['attributes'])) {
|
|
13292
|
+
return raw['attributes'];
|
|
13279
13293
|
}
|
|
13280
13294
|
if (raw !== undefined && raw !== null && Array.isArray(raw)) {
|
|
13281
13295
|
return raw;
|
|
@@ -13589,7 +13603,7 @@ class AttributesGroupComponent {
|
|
|
13589
13603
|
}
|
|
13590
13604
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: AttributesGroupComponent, decorators: [{
|
|
13591
13605
|
type: Component,
|
|
13592
|
-
args: [{ selector:
|
|
13606
|
+
args: [{ selector: 'mm-attributes-group', standalone: true, imports: [
|
|
13593
13607
|
CommonModule,
|
|
13594
13608
|
ReactiveFormsModule,
|
|
13595
13609
|
KENDO_LAYOUT,
|
|
@@ -13938,9 +13952,9 @@ class SharedEditor {
|
|
|
13938
13952
|
this.notificationService.show({
|
|
13939
13953
|
content: message,
|
|
13940
13954
|
hideAfter: 3000,
|
|
13941
|
-
position: { horizontal:
|
|
13942
|
-
animation: { type:
|
|
13943
|
-
type: { style:
|
|
13955
|
+
position: { horizontal: 'right', vertical: 'top' },
|
|
13956
|
+
animation: { type: 'fade', duration: 400 },
|
|
13957
|
+
type: { style: 'error', icon: true },
|
|
13944
13958
|
});
|
|
13945
13959
|
}
|
|
13946
13960
|
/** Wraps variables with optional multipart context based on whether a File is present anywhere in the payload. */
|
|
@@ -13964,18 +13978,18 @@ class SharedEditor {
|
|
|
13964
13978
|
if (Array.isArray(value)) {
|
|
13965
13979
|
return value.some((item) => this.containsFile(item));
|
|
13966
13980
|
}
|
|
13967
|
-
if (typeof value ===
|
|
13981
|
+
if (typeof value === 'object') {
|
|
13968
13982
|
return Object.values(value).some((v) => this.containsFile(v));
|
|
13969
13983
|
}
|
|
13970
13984
|
return false;
|
|
13971
13985
|
}
|
|
13972
13986
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SharedEditor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
13973
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SharedEditor, providedIn:
|
|
13987
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SharedEditor, providedIn: 'root' });
|
|
13974
13988
|
}
|
|
13975
13989
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SharedEditor, decorators: [{
|
|
13976
13990
|
type: Injectable,
|
|
13977
13991
|
args: [{
|
|
13978
|
-
providedIn:
|
|
13992
|
+
providedIn: 'root',
|
|
13979
13993
|
}]
|
|
13980
13994
|
}] });
|
|
13981
13995
|
|
|
@@ -14025,7 +14039,7 @@ class CreateEditorComponent {
|
|
|
14025
14039
|
const isDirty = this.formIsDifferentFromInitial();
|
|
14026
14040
|
return (!this.isCreating() &&
|
|
14027
14041
|
!this.isCanceling() &&
|
|
14028
|
-
status ===
|
|
14042
|
+
status === 'VALID' &&
|
|
14029
14043
|
isLoaded &&
|
|
14030
14044
|
isDirty);
|
|
14031
14045
|
}, /* @ts-ignore */
|
|
@@ -14044,7 +14058,7 @@ class CreateEditorComponent {
|
|
|
14044
14058
|
...(ngDevMode ? [{ debugName: "formIsDifferentFromInitial" }] : /* istanbul ignore next */ []));
|
|
14045
14059
|
formStatusSignal = toSignal(toObservable(this.form).pipe(switchMap$1((f) => {
|
|
14046
14060
|
if (!f)
|
|
14047
|
-
return of(
|
|
14061
|
+
return of('INVALID');
|
|
14048
14062
|
return f.statusChanges.pipe(startWith(f.status));
|
|
14049
14063
|
})));
|
|
14050
14064
|
formValueSignal = toSignal(toObservable(this.form).pipe(switchMap$1((f) => {
|
|
@@ -14097,7 +14111,7 @@ class CreateEditorComponent {
|
|
|
14097
14111
|
}
|
|
14098
14112
|
}
|
|
14099
14113
|
catch (error) {
|
|
14100
|
-
console.error(
|
|
14114
|
+
console.error('Error saving entity:', error);
|
|
14101
14115
|
this.sharedEditor.showErrorNotification(this.resolvedMessages().failedToCreateEntity);
|
|
14102
14116
|
}
|
|
14103
14117
|
finally {
|
|
@@ -14132,7 +14146,7 @@ class CreateEditorComponent {
|
|
|
14132
14146
|
if (parent?.ckTypeId && parent?.rtId) {
|
|
14133
14147
|
entity.associations = [
|
|
14134
14148
|
{
|
|
14135
|
-
roleName: input?.parentRoleName ??
|
|
14149
|
+
roleName: input?.parentRoleName ?? 'parent',
|
|
14136
14150
|
targets: [
|
|
14137
14151
|
{
|
|
14138
14152
|
modOption: AssociationModOptionsDto.CreateDto,
|
|
@@ -14241,7 +14255,7 @@ class CreateEditorComponent {
|
|
|
14241
14255
|
}
|
|
14242
14256
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CreateEditorComponent, decorators: [{
|
|
14243
14257
|
type: Component,
|
|
14244
|
-
args: [{ selector:
|
|
14258
|
+
args: [{ selector: 'mm-create-editor-component', imports: [
|
|
14245
14259
|
ReactiveFormsModule,
|
|
14246
14260
|
CardModule,
|
|
14247
14261
|
KENDO_INPUTS,
|
|
@@ -14368,7 +14382,7 @@ class UpdateEditorComponent {
|
|
|
14368
14382
|
const isDirty = this.formIsDifferentFromInitial();
|
|
14369
14383
|
return (!this.isUpdating() &&
|
|
14370
14384
|
!this.isCanceling() &&
|
|
14371
|
-
status ===
|
|
14385
|
+
status === 'VALID' &&
|
|
14372
14386
|
isLoaded &&
|
|
14373
14387
|
isDirty);
|
|
14374
14388
|
}, /* @ts-ignore */
|
|
@@ -14379,7 +14393,7 @@ class UpdateEditorComponent {
|
|
|
14379
14393
|
...(ngDevMode ? [{ debugName: "isCancelButtonEnabled" }] : /* istanbul ignore next */ []));
|
|
14380
14394
|
formStatusSignal = toSignal(toObservable(this.form).pipe(switchMap$1((f) => {
|
|
14381
14395
|
if (!f)
|
|
14382
|
-
return of(
|
|
14396
|
+
return of('INVALID');
|
|
14383
14397
|
return f.statusChanges.pipe(startWith(f.status));
|
|
14384
14398
|
})));
|
|
14385
14399
|
formValueSignal = toSignal(toObservable(this.form).pipe(switchMap$1((f) => {
|
|
@@ -14468,7 +14482,7 @@ class UpdateEditorComponent {
|
|
|
14468
14482
|
}
|
|
14469
14483
|
}
|
|
14470
14484
|
catch (error) {
|
|
14471
|
-
console.error(
|
|
14485
|
+
console.error('Error updating entity:', error);
|
|
14472
14486
|
this.sharedEditor.showErrorNotification(this.resolvedMessages().failedToUpdateEntity);
|
|
14473
14487
|
}
|
|
14474
14488
|
finally {
|
|
@@ -14578,7 +14592,7 @@ class UpdateEditorComponent {
|
|
|
14578
14592
|
}
|
|
14579
14593
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: UpdateEditorComponent, decorators: [{
|
|
14580
14594
|
type: Component,
|
|
14581
|
-
args: [{ selector:
|
|
14595
|
+
args: [{ selector: 'mm-update-editor-component', imports: [
|
|
14582
14596
|
ReactiveFormsModule,
|
|
14583
14597
|
CardModule,
|
|
14584
14598
|
KENDO_INPUTS,
|
|
@@ -14704,38 +14718,38 @@ class RuntimeBrowserDetailsComponent {
|
|
|
14704
14718
|
get ckTypeColumns() {
|
|
14705
14719
|
return [
|
|
14706
14720
|
{
|
|
14707
|
-
field:
|
|
14721
|
+
field: 'rtId',
|
|
14708
14722
|
displayName: this._messages.runtimeId,
|
|
14709
|
-
dataType:
|
|
14723
|
+
dataType: 'text',
|
|
14710
14724
|
},
|
|
14711
14725
|
{
|
|
14712
|
-
field:
|
|
14726
|
+
field: 'ckTypeId',
|
|
14713
14727
|
displayName: this._messages.typeId,
|
|
14714
|
-
dataType:
|
|
14728
|
+
dataType: 'text',
|
|
14715
14729
|
},
|
|
14716
14730
|
{
|
|
14717
|
-
field:
|
|
14731
|
+
field: 'rtWellKnownName',
|
|
14718
14732
|
displayName: this._messages.wellKnownName,
|
|
14719
|
-
dataType:
|
|
14733
|
+
dataType: 'text',
|
|
14720
14734
|
},
|
|
14721
14735
|
{
|
|
14722
|
-
field:
|
|
14736
|
+
field: 'rtCreationDateTime',
|
|
14723
14737
|
displayName: this._messages.created,
|
|
14724
|
-
format:
|
|
14725
|
-
dataType:
|
|
14738
|
+
format: 'short',
|
|
14739
|
+
dataType: 'iso8601',
|
|
14726
14740
|
},
|
|
14727
14741
|
{
|
|
14728
|
-
field:
|
|
14742
|
+
field: 'rtChangedDateTime',
|
|
14729
14743
|
displayName: this._messages.modified,
|
|
14730
|
-
format:
|
|
14731
|
-
dataType:
|
|
14744
|
+
format: 'short',
|
|
14745
|
+
dataType: 'iso8601',
|
|
14732
14746
|
},
|
|
14733
14747
|
];
|
|
14734
14748
|
}
|
|
14735
14749
|
get ckTypeViewDetailsCommand() {
|
|
14736
14750
|
return {
|
|
14737
|
-
id:
|
|
14738
|
-
type:
|
|
14751
|
+
id: 'view',
|
|
14752
|
+
type: 'link',
|
|
14739
14753
|
text: this._messages.viewDetails,
|
|
14740
14754
|
svgIcon: this.detailsIcon,
|
|
14741
14755
|
onClick: this.onViewEntityDetails,
|
|
@@ -14751,26 +14765,26 @@ class RuntimeBrowserDetailsComponent {
|
|
|
14751
14765
|
ngAfterViewInit() {
|
|
14752
14766
|
// If we have a pending CK type ID, set it now
|
|
14753
14767
|
if (this.pendingRtCkTypeId && this.dataSourceDirective) {
|
|
14754
|
-
console.debug(
|
|
14768
|
+
console.debug('AfterViewInit: Setting pending CK Type ID:', this.pendingRtCkTypeId);
|
|
14755
14769
|
this.dataSourceDirective.setRtCkTypeId(this.pendingRtCkTypeId);
|
|
14756
14770
|
this.pendingRtCkTypeId = null;
|
|
14757
14771
|
}
|
|
14758
14772
|
}
|
|
14759
14773
|
async ngOnChanges(changes) {
|
|
14760
|
-
if (changes[
|
|
14761
|
-
console.debug(
|
|
14774
|
+
if (changes['selectedItem'] && this.selectedItem) {
|
|
14775
|
+
console.debug('Selected item changed:', this.selectedItem);
|
|
14762
14776
|
// If it's a CK Type, update the data source with the type ID
|
|
14763
14777
|
if (this.isCkType(this.selectedItem.item)) {
|
|
14764
14778
|
const rtCkTypeId = this.getRtCkTypeId(this.selectedItem.item);
|
|
14765
|
-
console.debug(
|
|
14779
|
+
console.debug('Setting CK Type ID:', rtCkTypeId);
|
|
14766
14780
|
// Use setTimeout to ensure ViewChild is initialized
|
|
14767
14781
|
setTimeout(() => {
|
|
14768
14782
|
if (this.dataSourceDirective) {
|
|
14769
|
-
console.debug(
|
|
14783
|
+
console.debug('ngOnChanges: ViewChild ready, setting CK Type ID:', rtCkTypeId);
|
|
14770
14784
|
this.dataSourceDirective.setRtCkTypeId(rtCkTypeId);
|
|
14771
14785
|
}
|
|
14772
14786
|
else {
|
|
14773
|
-
console.debug(
|
|
14787
|
+
console.debug('ngOnChanges: ViewChild not ready, storing pending CK Type ID:', rtCkTypeId);
|
|
14774
14788
|
this.pendingRtCkTypeId = rtCkTypeId;
|
|
14775
14789
|
}
|
|
14776
14790
|
}, 0);
|
|
@@ -14784,7 +14798,7 @@ class RuntimeBrowserDetailsComponent {
|
|
|
14784
14798
|
this.pendingRtCkTypeId = null;
|
|
14785
14799
|
}
|
|
14786
14800
|
}
|
|
14787
|
-
else if (changes[
|
|
14801
|
+
else if (changes['selectedItem'] && !this.selectedItem) {
|
|
14788
14802
|
this.fullEntity = null;
|
|
14789
14803
|
this.error = null;
|
|
14790
14804
|
this.pendingRtCkTypeId = null;
|
|
@@ -14821,7 +14835,7 @@ class RuntimeBrowserDetailsComponent {
|
|
|
14821
14835
|
if (token !== this.loadRequestToken) {
|
|
14822
14836
|
return;
|
|
14823
14837
|
}
|
|
14824
|
-
console.error(
|
|
14838
|
+
console.error('Failed to load full entity details:', error);
|
|
14825
14839
|
this.error = this._messages.failedToLoadEntityDetails;
|
|
14826
14840
|
// Fall back to the basic entity data
|
|
14827
14841
|
this.fullEntity = runtimeEntity;
|
|
@@ -14841,16 +14855,16 @@ class RuntimeBrowserDetailsComponent {
|
|
|
14841
14855
|
// Save current state before navigating
|
|
14842
14856
|
this.stateService.saveState(this.selectedItem);
|
|
14843
14857
|
const encodedId = RtEntityIdHelper.encode(runtimeEntity.rtId, runtimeEntity.ckTypeId);
|
|
14844
|
-
await this.router.navigate([
|
|
14858
|
+
await this.router.navigate(['entity', encodedId], {
|
|
14845
14859
|
relativeTo: this.route,
|
|
14846
14860
|
});
|
|
14847
14861
|
}
|
|
14848
14862
|
async navigateToEntity(rtId, ckTypeId) {
|
|
14849
|
-
if (!rtId || !ckTypeId || rtId ===
|
|
14863
|
+
if (!rtId || !ckTypeId || rtId === 'Unknown' || ckTypeId === 'Unknown') {
|
|
14850
14864
|
return;
|
|
14851
14865
|
}
|
|
14852
14866
|
const encodedId = RtEntityIdHelper.encode(rtId, ckTypeId);
|
|
14853
|
-
await this.router.navigate([
|
|
14867
|
+
await this.router.navigate(['entity', encodedId], {
|
|
14854
14868
|
relativeTo: this.route,
|
|
14855
14869
|
});
|
|
14856
14870
|
}
|
|
@@ -14865,50 +14879,50 @@ class RuntimeBrowserDetailsComponent {
|
|
|
14865
14879
|
return null;
|
|
14866
14880
|
}
|
|
14867
14881
|
isCkModel(item) {
|
|
14868
|
-
return !!item &&
|
|
14882
|
+
return !!item && 'id' in item && !('rtId' in item) && !('ckTypeId' in item);
|
|
14869
14883
|
}
|
|
14870
14884
|
isCkType(item) {
|
|
14871
|
-
return !!item &&
|
|
14885
|
+
return !!item && 'ckTypeId' in item && !('rtId' in item) && !('id' in item);
|
|
14872
14886
|
}
|
|
14873
14887
|
isCkModelsRoot(item) {
|
|
14874
|
-
return !!item &&
|
|
14888
|
+
return !!item && 'isCkModelsRoot' in item;
|
|
14875
14889
|
}
|
|
14876
14890
|
// Helper methods to get CK Model properties
|
|
14877
14891
|
getCkModelIdFullName(item) {
|
|
14878
14892
|
if (!this.isCkModel(item))
|
|
14879
|
-
return
|
|
14880
|
-
return item.id.fullName ||
|
|
14893
|
+
return 'Unknown';
|
|
14894
|
+
return item.id.fullName || 'Unknown';
|
|
14881
14895
|
}
|
|
14882
14896
|
getCkModelIdName(item) {
|
|
14883
14897
|
if (!this.isCkModel(item))
|
|
14884
|
-
return
|
|
14885
|
-
return item.id.name ||
|
|
14898
|
+
return 'Unknown';
|
|
14899
|
+
return item.id.name || 'Unknown';
|
|
14886
14900
|
}
|
|
14887
14901
|
getCkModelIdVersion(item) {
|
|
14888
14902
|
if (!this.isCkModel(item))
|
|
14889
|
-
return
|
|
14890
|
-
return item.id.version ||
|
|
14903
|
+
return 'Unknown';
|
|
14904
|
+
return item.id.version || 'Unknown';
|
|
14891
14905
|
}
|
|
14892
14906
|
getCkModelIdSemanticName(item) {
|
|
14893
14907
|
if (!this.isCkModel(item))
|
|
14894
|
-
return
|
|
14895
|
-
return item.id.semanticVersionedFullName ||
|
|
14908
|
+
return 'Unknown';
|
|
14909
|
+
return item.id.semanticVersionedFullName || 'Unknown';
|
|
14896
14910
|
}
|
|
14897
14911
|
getCkModelState(item) {
|
|
14898
14912
|
if (!this.isCkModel(item))
|
|
14899
|
-
return
|
|
14900
|
-
return item.modelState ||
|
|
14913
|
+
return 'Unknown';
|
|
14914
|
+
return item.modelState || 'Unknown';
|
|
14901
14915
|
}
|
|
14902
14916
|
// Helper methods to get CK Type properties
|
|
14903
14917
|
getCkTypeId(item) {
|
|
14904
14918
|
if (!this.isCkType(item))
|
|
14905
|
-
return
|
|
14906
|
-
return item.ckTypeId.fullName ||
|
|
14919
|
+
return 'Unknown';
|
|
14920
|
+
return item.ckTypeId.fullName || 'Unknown';
|
|
14907
14921
|
}
|
|
14908
14922
|
getRtCkTypeId(item) {
|
|
14909
14923
|
if (!this.isCkType(item))
|
|
14910
|
-
return
|
|
14911
|
-
return item.rtCkTypeId ||
|
|
14924
|
+
return 'Unknown';
|
|
14925
|
+
return item.rtCkTypeId || 'Unknown';
|
|
14912
14926
|
}
|
|
14913
14927
|
isCkTypeAbstract(item) {
|
|
14914
14928
|
return this.isCkType(item) && item.isAbstract === true;
|
|
@@ -14938,7 +14952,7 @@ class RuntimeBrowserDetailsComponent {
|
|
|
14938
14952
|
parent: {
|
|
14939
14953
|
ckTypeId: rtEntityDto?.item.ckTypeId,
|
|
14940
14954
|
rtId: rtEntityDto?.item.rtId,
|
|
14941
|
-
name: parentNode?.text ??
|
|
14955
|
+
name: parentNode?.text ?? '',
|
|
14942
14956
|
},
|
|
14943
14957
|
derivedFromRtCkTypeId,
|
|
14944
14958
|
};
|
|
@@ -14948,13 +14962,13 @@ class RuntimeBrowserDetailsComponent {
|
|
|
14948
14962
|
}
|
|
14949
14963
|
enterEditMode(selectedItem, rtCkTypeId) {
|
|
14950
14964
|
const rtEntityDto = selectedItem;
|
|
14951
|
-
const rtId = rtEntityDto?.item?.rtId ??
|
|
14965
|
+
const rtId = rtEntityDto?.item?.rtId ?? '';
|
|
14952
14966
|
const ckTypeId = rtEntityDto?.item?.ckTypeId;
|
|
14953
14967
|
if (!rtId || !ckTypeId || !rtCkTypeId) {
|
|
14954
|
-
console.warn(
|
|
14968
|
+
console.warn('Enter edit mode skipped: missing rtId, ckTypeId or rtCkTypeId.', { rtId, ckTypeId, rtCkTypeId });
|
|
14955
14969
|
return;
|
|
14956
14970
|
}
|
|
14957
|
-
const name = rtEntityDto?.text ??
|
|
14971
|
+
const name = rtEntityDto?.text ?? '';
|
|
14958
14972
|
this.updateInput = {
|
|
14959
14973
|
name,
|
|
14960
14974
|
rtId,
|
|
@@ -14963,7 +14977,7 @@ class RuntimeBrowserDetailsComponent {
|
|
|
14963
14977
|
};
|
|
14964
14978
|
this.isCreateModeEnabled = false;
|
|
14965
14979
|
this.isUpdateModeEnabled = true;
|
|
14966
|
-
console.debug(
|
|
14980
|
+
console.debug('Entered update mode with input:', this.updateInput);
|
|
14967
14981
|
}
|
|
14968
14982
|
/**
|
|
14969
14983
|
* Handles the result of the save operation from the child component
|
|
@@ -15009,7 +15023,7 @@ class RuntimeBrowserDetailsComponent {
|
|
|
15009
15023
|
this.loadFullEntityDetails();
|
|
15010
15024
|
}
|
|
15011
15025
|
onCancel() {
|
|
15012
|
-
console.debug(
|
|
15026
|
+
console.debug('Creation or update cancelled, resetting state.');
|
|
15013
15027
|
this.isCreateModeEnabled = false;
|
|
15014
15028
|
this.isUpdateModeEnabled = false;
|
|
15015
15029
|
this.createInput = undefined;
|
|
@@ -15448,7 +15462,7 @@ class RuntimeBrowserDetailsComponent {
|
|
|
15448
15462
|
}
|
|
15449
15463
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RuntimeBrowserDetailsComponent, decorators: [{
|
|
15450
15464
|
type: Component,
|
|
15451
|
-
args: [{ selector:
|
|
15465
|
+
args: [{ selector: 'mm-runtime-browser-details', imports: [
|
|
15452
15466
|
CommonModule,
|
|
15453
15467
|
ButtonModule,
|
|
15454
15468
|
SVGIconModule,
|
|
@@ -15608,7 +15622,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
15608
15622
|
type: Output
|
|
15609
15623
|
}], dataSourceDirective: [{
|
|
15610
15624
|
type: ViewChild,
|
|
15611
|
-
args: [
|
|
15625
|
+
args: ['dir', { static: false }]
|
|
15612
15626
|
}], expressionValidator: [{
|
|
15613
15627
|
type: Input
|
|
15614
15628
|
}] } });
|
|
@@ -19390,7 +19404,14 @@ class MappingCoverageTreeComponent {
|
|
|
19390
19404
|
dataSource = inject(MappingCoverageTreeDataSource);
|
|
19391
19405
|
treeView;
|
|
19392
19406
|
/** Optional override for non-default hierarchies / mapping roles. */
|
|
19393
|
-
|
|
19407
|
+
// Assigned in the constructor rather than as a field initialiser: Vite's module-runner
|
|
19408
|
+
// transform (used by the Vitest unit-test builder) snapshots a class field whose
|
|
19409
|
+
// initialiser is a bare imported identifier at module-evaluation time, which is before
|
|
19410
|
+
// esbuild's lazily-initialised shared chunk has assigned the export, so the field would
|
|
19411
|
+
// be `undefined`. A constructor assignment is rewritten to a live read; a getter is not
|
|
19412
|
+
// an option here because Angular has to be able to set an @Input. Test-runner-only:
|
|
19413
|
+
// production and ng-packagr builds do not use the module runner and are unaffected.
|
|
19414
|
+
config;
|
|
19394
19415
|
/** Pre-select a root on first show (e.g. via route param). */
|
|
19395
19416
|
initialRoot = null;
|
|
19396
19417
|
/**
|
|
@@ -19669,6 +19690,9 @@ class MappingCoverageTreeComponent {
|
|
|
19669
19690
|
}
|
|
19670
19691
|
}
|
|
19671
19692
|
}
|
|
19693
|
+
constructor() {
|
|
19694
|
+
this.config = DEFAULT_MAPPING_COVERAGE_TREE_CONFIG;
|
|
19695
|
+
}
|
|
19672
19696
|
/**
|
|
19673
19697
|
* The Studio loads the per-tenant source-candidate CK types ASYNCHRONOUSLY
|
|
19674
19698
|
* (System.UI singleton) and re-emits the `config` input when they arrive —
|
|
@@ -20769,7 +20793,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
20769
20793
|
TreeComponent,
|
|
20770
20794
|
PerspectiveSwitcherComponent,
|
|
20771
20795
|
], providers: [MappingCoverageTreeDataSource], template: "<div class=\"coverage-root\">\n @if (config.sourceCandidateCkTypeIds.length > 0) {\n <div class=\"coverage-tabs\" role=\"tablist\">\n <button class=\"coverage-tab\"\n [class.active]=\"activeTab() === 'coverage'\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab() === 'coverage'\"\n (click)=\"selectTab('coverage')\">Coverage Tree</button>\n <button class=\"coverage-tab\"\n [class.active]=\"activeTab() === 'orphans'\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab() === 'orphans'\"\n (click)=\"selectTab('orphans')\">\n Orphan Sources\n @if (orphanStats(); as s) {\n @if (s.unmapped > 0) { <span class=\"tab-badge\">{{ s.unmapped }}</span> }\n }\n </button>\n </div>\n }\n\n @if (activeTab() === 'coverage') {\n <div class=\"coverage-toolbar\">\n <mm-perspective-switcher\n [perspectives]=\"perspectives()\"\n [activeKey]=\"activePerspectiveKey()\"\n (perspectiveChange)=\"onPerspectiveChange($event)\"></mm-perspective-switcher>\n <label class=\"toolbar-label\">Tree:</label>\n <select class=\"root-select\"\n [value]=\"selectedRoot()?.rtId ?? ''\"\n (change)=\"onRootSelectChange($any($event.target).value)\">\n <option value=\"\" disabled>\u2014 Pick a {{ activeRootCkTypeId() }} \u2014</option>\n @for (root of rootCandidates(); track root.rtId) {\n <option [value]=\"root.rtId\" [title]=\"root.description\">{{ root.name }}</option>\n }\n </select>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.refresh\"\n (click)=\"refreshRoots()\" title=\"Refresh tree list\"></button>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.folderOpen\"\n (click)=\"pickRoot()\" title=\"Browse for entity\">Browse...</button>\n\n @if (selectedRoot(); as root) {\n <div class=\"toolbar-spacer\"></div>\n <span class=\"toolbar-meta\">\n <span class=\"meta-name\">{{ root.name }}</span>\n <span class=\"meta-type\">{{ root.ckTypeId }}</span>\n </span>\n }\n </div>\n\n <div class=\"coverage-toolbar coverage-toolbar-validation\">\n <label class=\"toolbar-label\">Validation:</label>\n <select class=\"root-select\"\n [value]=\"selectedPipeline()?.rtId ?? ''\"\n (change)=\"onPipelineSelectChange($any($event.target).value)\">\n <option value=\"\" disabled>\u2014 Pick a {{ config.validationPipelineCkTypeId }} \u2014</option>\n @for (p of validationPipelines(); track p.rtId) {\n <option [value]=\"p.rtId\">{{ p.name }}</option>\n }\n </select>\n @if (tenantId) {\n <button kendoButton themeColor=\"primary\" size=\"small\"\n [disabled]=\"!selectedPipeline() || validationRunning() || validationLoading()\"\n (click)=\"runValidation()\"\n [title]=\"validationRunning() ? 'Validation running\u2026' : 'Run pipeline + load report'\">\n @if (validationRunning()) { Running\u2026 } @else { Run }\n </button>\n }\n <button kendoButton fillMode=\"solid\" size=\"small\" [svgIcon]=\"icons.refresh\"\n [disabled]=\"!selectedPipeline() || validationLoading() || validationRunning()\"\n (click)=\"refreshValidation()\">Load Report</button>\n @if (validationSummary()) {\n <button kendoButton fillMode=\"flat\" size=\"small\"\n (click)=\"clearValidation()\">Clear</button>\n }\n\n <div class=\"toolbar-spacer\"></div>\n\n @if (validationLoading()) {\n <span class=\"toolbar-meta\">Loading\u2026</span>\n } @else if (validationError(); as err) {\n <span class=\"toolbar-meta validation-error-text\">{{ err }}</span>\n } @else if (validationSummary(); as s) {\n <span class=\"validation-summary\">\n <span class=\"badge badge-error\" title=\"error\">{{ s.error }} err</span>\n <span class=\"badge badge-warning\" title=\"warning\">{{ s.warning }} warn</span>\n <span class=\"badge badge-ok\" title=\"ok\">{{ s.ok }} ok</span>\n <span class=\"badge badge-info\" title=\"info / unscoped\">{{ s.info }} info</span>\n <span class=\"badge\">{{ s.total }} total</span>\n @if (validationExecutedAt(); as ts) {\n <span class=\"validation-timestamp\">{{ ts }}</span>\n }\n </span>\n }\n </div>\n\n @if (tenantId) {\n <div class=\"coverage-toolbar coverage-toolbar-validation\">\n <label class=\"toolbar-label\">Generation:</label>\n <select class=\"root-select\"\n [value]=\"selectedGenerationPipeline()?.rtId ?? ''\"\n (change)=\"onGenerationSelectChange($any($event.target).value)\">\n <option value=\"\" disabled>\u2014 Pick an auto-mapping pipeline \u2014</option>\n @for (p of validationPipelines(); track p.rtId) {\n <option [value]=\"p.rtId\">{{ p.name }}</option>\n }\n </select>\n <button kendoButton themeColor=\"primary\" size=\"small\"\n [disabled]=\"!selectedGenerationPipeline() || generationRunning()\"\n (click)=\"runGeneration()\"\n [title]=\"generationRunning() ? 'Generation running\u2026' : 'Run the auto-mapping pipeline and reload counts'\">\n @if (generationRunning()) { Running\u2026 } @else { Generate }\n </button>\n\n <div class=\"toolbar-spacer\"></div>\n\n @if (generationRunning()) {\n <span class=\"toolbar-meta\">Generating mappings\u2026</span>\n } @else if (generationError(); as err) {\n <span class=\"toolbar-meta validation-error-text\">{{ err }}</span>\n } @else if (generationStats(); as s) {\n <span class=\"validation-summary\">\n @if (s.totalSuggestions !== undefined) {\n <span class=\"badge badge-ok\" title=\"mapping suggestions produced\">{{ s.totalSuggestions }} mappings</span>\n }\n @if (s.matchedContainers !== undefined && s.totalContainers !== undefined) {\n <span class=\"badge\" title=\"containers (e.g. rooms) matched to a target\">\n {{ s.matchedContainers }}/{{ s.totalContainers }} matched\n </span>\n }\n @if (s.unmatchedContainers) {\n <span class=\"badge badge-warning\"\n [attr.title]=\"'Unmatched: ' + (s.unmatchedContainerNames ?? []).join(', ')\">\n {{ s.unmatchedContainers }} unmatched\n </span>\n }\n @if (generationCompletedAt(); as ts) {\n <span class=\"validation-timestamp\">{{ ts }}</span>\n }\n </span>\n } @else if (generationCompletedAt(); as ts) {\n <span class=\"toolbar-meta\">Completed {{ ts }} (no statistics in OutputData)</span>\n }\n </div>\n }\n\n @if (tenantId && (exportBackupPipeline() || importBackupPipeline())) {\n <div class=\"coverage-toolbar coverage-toolbar-validation\">\n <label class=\"toolbar-label\">Backup:</label>\n <button kendoButton size=\"small\" [svgIcon]=\"icons.download\"\n [disabled]=\"!exportBackupPipeline() || backupRunning() !== null\"\n (click)=\"exportMappings()\"\n title=\"Run the export pipeline and download the mapping document as JSON\">\n @if (backupRunning() === 'export') { Exporting\u2026 } @else { Export }\n </button>\n <button kendoButton size=\"small\" [svgIcon]=\"icons.upload\"\n [disabled]=\"!importBackupPipeline() || backupRunning() !== null\"\n (click)=\"importFileInput.click()\"\n title=\"Import a previously exported mapping document (JSON)\">\n @if (backupRunning() === 'import') { Importing\u2026 } @else { Import\u2026 }\n </button>\n <input #importFileInput type=\"file\" accept=\".json,application/json\" hidden\n (change)=\"onImportFileSelected($event)\" />\n\n <div class=\"toolbar-spacer\"></div>\n\n @if (backupRunning() === 'export') {\n <span class=\"toolbar-meta\">Exporting mappings\u2026</span>\n } @else if (backupRunning() === 'import') {\n <span class=\"toolbar-meta\">Importing mappings\u2026</span>\n } @else if (backupError(); as err) {\n <span class=\"toolbar-meta validation-error-text\">{{ err }}</span>\n } @else if (importStats(); as s) {\n <span class=\"validation-summary\">\n @if (s.resolved !== undefined) {\n <span class=\"badge badge-ok\" title=\"mappings resolved and imported\">{{ s.resolved }} imported</span>\n }\n @if (s.unresolved) {\n <span class=\"badge badge-warning\"\n [attr.title]=\"'Unresolved: ' + unresolvedImportNames()\">\n {{ s.unresolved }} unresolved\n </span>\n }\n @if (s.total !== undefined) {\n <span class=\"badge\">{{ s.total }} total</span>\n }\n @if (importCompletedAt(); as ts) {\n <span class=\"validation-timestamp\">{{ ts }}</span>\n }\n </span>\n } @else if (importCompletedAt(); as ts) {\n <span class=\"toolbar-meta\">Import completed {{ ts }} (no statistics in OutputData)</span>\n }\n </div>\n }\n\n <div class=\"coverage-body\">\n <div class=\"coverage-tree-pane\">\n @if (selectedRoot()) {\n <mm-tree-view\n [dataSource]=\"dataSource\"\n (nodeSelected)=\"onNodeSelected($event)\">\n </mm-tree-view>\n } @else {\n <div class=\"empty-hint\">\n Select a tree to start. Counts in brackets show how many mappings\n point to that node.\n </div>\n }\n </div>\n\n <div class=\"coverage-detail-pane\">\n @if (selectedNode(); as node) {\n <div class=\"detail-header\">\n <div class=\"detail-titles\">\n <h3 class=\"detail-name\">{{ node.name }}</h3>\n <div class=\"detail-subtitle\">{{ node.ckTypeId }}@{{ node.rtId }}</div>\n </div>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.refresh\"\n (click)=\"refreshSelected()\" title=\"Refresh\"></button>\n </div>\n\n <div class=\"detail-section-title\">\n <span>Mappings</span>\n @if (summaryLine(); as s) {\n <span class=\"detail-summary\">{{ s }}</span>\n }\n <span class=\"toolbar-spacer\"></span>\n <button kendoButton themeColor=\"primary\" size=\"small\" [svgIcon]=\"icons.plus\"\n (click)=\"addMapping()\">Add</button>\n </div>\n\n @if (mappingsLoading()) {\n <div class=\"empty-hint\">Loading mappings\u2026</div>\n } @else if (mappingsError(); as err) {\n <div class=\"error-hint\">{{ err }}</div>\n } @else if (mappings().length === 0) {\n <div class=\"empty-hint\">\n No mappings reference this node yet. Add one using the button above.\n </div>\n } @else {\n <ul class=\"mapping-list\">\n @for (mapping of mappings(); track trackByRtId($index, mapping)) {\n <li class=\"mapping-row\" [class.mapping-disabled]=\"!mapping.enabled\">\n <div class=\"mapping-line\">\n <span class=\"mapping-name\">\n {{ mapping.name || 'Mapping ' + ($index + 1) }}\n </span>\n @if (!mapping.enabled) {\n <span class=\"mapping-badge mapping-badge-off\">disabled</span>\n }\n </div>\n <div class=\"mapping-details\">\n <span class=\"kv\">\n <span class=\"kv-key\">Source:</span>\n <span class=\"kv-val\">\n {{ mapping.sourceName || mapping.sourceRtId || '\u2014 not linked \u2014' }}\n </span>\n </span>\n @if (mapping.sourceAttributePath) {\n <span class=\"kv\">\n <span class=\"kv-key\">Attr:</span>\n <span class=\"kv-val\">{{ mapping.sourceAttributePath }}</span>\n </span>\n }\n @if (mapping.mappingExpression) {\n <span class=\"kv\">\n <span class=\"kv-key\">Expr:</span>\n <code class=\"kv-val\">{{ mapping.mappingExpression }}</code>\n </span>\n }\n @if (mapping.targetAttributePath) {\n <span class=\"kv\">\n <span class=\"kv-key\">\u2192</span>\n <span class=\"kv-val\">{{ mapping.targetAttributePath }}</span>\n </span>\n }\n </div>\n <div class=\"mapping-actions\">\n <button kendoButton themeColor=\"primary\" fillMode=\"flat\" size=\"small\"\n [svgIcon]=\"icons.pencil\"\n (click)=\"editMapping(mapping)\"\n title=\"Edit mapping\">Edit\u2026</button>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.trash\"\n (click)=\"deleteMapping(mapping)\"\n title=\"Delete mapping\"></button>\n </div>\n </li>\n }\n </ul>\n }\n } @else {\n <div class=\"empty-hint\">\n Select a node from the tree to view and edit its mappings.\n </div>\n }\n </div>\n </div>\n }\n\n @if (activeTab() === 'orphans') {\n <div class=\"coverage-toolbar\">\n <label class=\"toolbar-label\">Source type:</label>\n <select class=\"root-select\"\n [value]=\"orphanCkType() ?? ''\"\n (change)=\"onOrphanCkTypeChange($any($event.target).value)\">\n <option value=\"\" disabled>\u2014 Pick a source type \u2014</option>\n @for (ckType of config.sourceCandidateCkTypeIds; track ckType) {\n <option [value]=\"ckType\">{{ ckType }}</option>\n }\n </select>\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.refresh\"\n [disabled]=\"!orphanCkType() || orphanLoading()\"\n (click)=\"refreshOrphans()\" title=\"Reload\"></button>\n <span class=\"orphan-search-wrap\">\n <input type=\"text\" class=\"root-select orphan-search\"\n placeholder=\"Search name, room, description\u2026\"\n [value]=\"orphanSearchText()\"\n (input)=\"orphanSearchText.set($any($event.target).value)\" />\n @if (orphanSearchText()) {\n <button kendoButton fillMode=\"flat\" size=\"small\" [svgIcon]=\"icons.x\"\n class=\"orphan-search-clear\"\n (click)=\"orphanSearchText.set('')\"\n title=\"Clear search\" aria-label=\"Clear search\"></button>\n }\n </span>\n <label class=\"orphan-toggle\">\n <input type=\"checkbox\" [checked]=\"orphanHideMapped()\" (change)=\"toggleOrphanHideMapped()\" />\n Hide already-mapped\n </label>\n <label class=\"toolbar-label\">Group by:</label>\n <select class=\"root-select\"\n [value]=\"orphanGroupParentType() ?? ''\"\n (change)=\"onOrphanGroupParentTypeChange($any($event.target).value)\">\n <option value=\"\">(no grouping)</option>\n @for (t of orphanAvailableParentTypes(); track t) {\n <option [value]=\"t\">{{ t }}</option>\n }\n </select>\n @if (orphanGroupParentType()) {\n <kendo-multiselect class=\"orphan-group-filter\"\n [data]=\"orphanAvailableGroupNames()\"\n [value]=\"orphanSelectedGroupNames()\"\n (valueChange)=\"onOrphanGroupNamesChange($event)\"\n [autoClose]=\"false\"\n [clearButton]=\"true\"\n size=\"small\"\n placeholder=\"Filter groups\u2026\"\n title=\"Show only the selected groups (e.g. specific categories)\">\n </kendo-multiselect>\n }\n\n <div class=\"toolbar-spacer\"></div>\n\n <button kendoButton size=\"small\" fillMode=\"flat\"\n [disabled]=\"orphanVisibleList().length === 0\"\n (click)=\"selectAllVisibleOrphans()\"\n title=\"Select every row visible under the current filter\">Select all</button>\n <button kendoButton size=\"small\" fillMode=\"flat\"\n [disabled]=\"orphanSelectedCount() === 0\"\n (click)=\"clearOrphanSelection()\">Clear</button>\n <button kendoButton themeColor=\"primary\" size=\"small\"\n [disabled]=\"orphanSelectedCount() === 0\"\n (click)=\"bulkMapSelected()\"\n title=\"Create one mapping per selected source (shared target + paths)\">\n Map {{ orphanSelectedCount() }} selected\u2026\n </button>\n\n @if (orphanStats(); as s) {\n <span class=\"validation-summary\">\n @if (orphanSearchText().trim() || orphanSelectedGroupNames().length > 0) {\n <span class=\"badge badge-info\" title=\"rows matching search + group filter\">{{ orphanVisibleList().length }} shown</span>\n }\n <span class=\"badge badge-error\" title=\"unmapped \u2014 no DataPointMapping yet\">{{ s.unmapped }} unmapped</span>\n <span class=\"badge badge-ok\" title=\"already covered by at least one mapping\">{{ s.mapped }} mapped</span>\n <span class=\"badge\">{{ s.total }} total</span>\n </span>\n }\n </div>\n\n <div class=\"orphan-body\">\n @if (orphanLoading()) {\n <div class=\"empty-hint\">Loading source candidates\u2026</div>\n } @else if (orphanError(); as err) {\n <div class=\"error-hint\">{{ err }}</div>\n } @else if (orphanVisibleList().length === 0) {\n <div class=\"empty-hint\">\n @if (orphanSelectedGroupNames().length > 0) {\n No entries in the selected group(s) match the current filters.\n } @else if (orphanSearchText().trim()) {\n No entries match \"{{ orphanSearchText() }}\".\n } @else if (orphanHideMapped()) {\n All {{ orphanCkType() }} entities are mapped at least once. \uD83C\uDF89\n } @else {\n No entities of type {{ orphanCkType() }} found.\n }\n </div>\n } @else if (orphanGroupParentType()) {\n @for (group of orphanGroupedList(); track group.key) {\n <div class=\"orphan-group\">\n <div class=\"orphan-group-header\">\n <span class=\"orphan-group-label\">{{ group.label }}</span>\n <span class=\"orphan-group-count\">{{ group.items.length }}</span>\n </div>\n <ul class=\"orphan-list\">\n @for (item of group.items; track trackOrphanByRtId($index, item)) {\n <ng-container *ngTemplateOutlet=\"orphanRowTpl; context: { $implicit: item }\"></ng-container>\n }\n </ul>\n </div>\n }\n } @else {\n <ul class=\"orphan-list\">\n @for (item of orphanVisibleList(); track trackOrphanByRtId($index, item)) {\n <ng-container *ngTemplateOutlet=\"orphanRowTpl; context: { $implicit: item }\"></ng-container>\n }\n </ul>\n }\n\n <ng-template #orphanRowTpl let-item>\n <li class=\"orphan-row\" [class.orphan-mapped]=\"item.mappingCount > 0\">\n <div class=\"orphan-line\">\n <input type=\"checkbox\" class=\"orphan-check\"\n [checked]=\"isOrphanSelected(item.rtId)\"\n (change)=\"toggleOrphanSelected(item.rtId)\"\n [attr.aria-label]=\"'Select ' + item.name\" />\n <span class=\"orphan-name\">{{ item.name }}</span>\n @if (item.mappingCount > 0) {\n <span class=\"orphan-badge orphan-badge-mapped\">\n {{ item.mappingCount }} mapping{{ item.mappingCount === 1 ? '' : 's' }}\n </span>\n } @else {\n <span class=\"orphan-badge orphan-badge-unmapped\">unmapped</span>\n }\n </div>\n @if (item.parentPath.length > 0) {\n <div class=\"orphan-breadcrumb\" [attr.title]=\"'Parent chain (root-first)'\">\n @for (parent of breadcrumbFor(item); track parent.rtId; let last = $last) {\n <span class=\"crumb\">{{ parent.name }}</span>\n @if (!last) { <span class=\"crumb-sep\">\u203A</span> }\n }\n </div>\n }\n <div class=\"orphan-meta\">\n <span class=\"kv-val\">{{ item.ckTypeId }}@{{ item.rtId }}</span>\n </div>\n @if (item.description) {\n <div class=\"orphan-description\">{{ item.description }}</div>\n }\n <div class=\"orphan-actions\">\n <button kendoButton themeColor=\"primary\" fillMode=\"flat\" size=\"small\"\n [svgIcon]=\"icons.plus\"\n (click)=\"createMappingFromOrphan(item)\"\n title=\"Create mapping pre-linked to this source\">\n Map\u2026\n </button>\n </div>\n </li>\n </ng-template>\n </div>\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0}.coverage-root{display:flex;flex-direction:column;flex:1;min-height:0;gap:12px}.coverage-toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 12px;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;background:color-mix(in srgb,var(--theme-bg-surface, var(--kendo-color-surface-alt, #f8f9fa)) 70%,transparent)}.coverage-toolbar .toolbar-label{flex:0 0 96px;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d))}.coverage-toolbar .root-select{flex:0 0 280px;min-width:0;padding:4px 8px;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:4px;background:var(--theme-bg-elevated, var(--kendo-color-surface, #fff));color:var(--theme-text-primary, inherit);font-size:.9rem;height:30px}.coverage-toolbar .orphan-search-wrap{display:inline-flex;align-items:center;gap:2px}.coverage-toolbar .orphan-search-wrap .orphan-search{flex:1 1 220px;min-width:140px}.coverage-toolbar .orphan-group-filter{flex:0 1 320px;min-width:160px}.coverage-toolbar .toolbar-spacer{flex:1}.coverage-toolbar .toolbar-meta{display:flex;flex-direction:column;align-items:flex-end;line-height:1.1}.coverage-toolbar .toolbar-meta .meta-name{font-size:.85rem;font-weight:600}.coverage-toolbar .toolbar-meta .meta-type{font-size:.7rem;font-family:monospace;color:var(--kendo-color-subtle, #6c757d)}.coverage-body{flex:1;display:flex;gap:12px;min-height:0}@media(max-width:900px){.coverage-body{flex-direction:column}.coverage-tree-pane{flex:0 0 38vh;min-width:0;min-height:180px}.coverage-detail-pane{min-height:180px}.coverage-toolbar .toolbar-label{flex:0 0 auto}.coverage-toolbar .root-select{flex:1 1 200px;max-width:100%}}.coverage-tree-pane{flex:0 0 40%;min-width:240px;display:flex;flex-direction:column;min-height:0;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;background:color-mix(in srgb,var(--theme-bg-surface, var(--kendo-color-surface, #fff)) 60%,transparent);color:var(--theme-text-primary, inherit);overflow:auto}.coverage-detail-pane{flex:1;min-width:0;display:flex;flex-direction:column;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;background:color-mix(in srgb,var(--theme-bg-surface, var(--kendo-color-surface, #fff)) 60%,transparent);color:var(--theme-text-primary, inherit);padding:12px 14px;gap:12px;overflow:auto}.detail-header{display:flex;align-items:flex-start;gap:8px}.detail-header .detail-titles{flex:1;min-width:0}.detail-header .detail-name{margin:0;font-size:1.05rem;font-weight:600;word-break:break-word}.detail-header .detail-subtitle{font-size:.72rem;font-family:monospace;color:var(--kendo-color-subtle, #6c757d);word-break:break-all}.detail-section-title{display:flex;align-items:center;gap:10px;font-size:.78rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #6c757d)}.detail-section-title .toolbar-spacer{flex:1}.detail-section-title .detail-summary{text-transform:none;letter-spacing:0;font-weight:500;color:var(--kendo-color-on-app-surface, inherit);font-size:.78rem}.mapping-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.mapping-row{border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;padding:8px 10px;display:flex;flex-direction:column;gap:6px;background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f8f9fa)) 65%,transparent)}.mapping-row.mapping-disabled{opacity:.55}.mapping-line{display:flex;align-items:center;gap:8px}.mapping-line .mapping-name{font-weight:600;font-size:.9rem}.mapping-badge{font-size:.65rem;text-transform:uppercase;letter-spacing:.5px;padding:1px 6px;border-radius:8px;border:1px solid var(--kendo-color-border, #dee2e6)}.mapping-badge.mapping-badge-off{color:var(--kendo-color-subtle, #6c757d)}.mapping-details{display:flex;flex-wrap:wrap;gap:10px;font-size:.8rem}.mapping-details .kv{display:inline-flex;align-items:baseline;gap:4px}.mapping-details .kv-key{font-weight:600;color:var(--kendo-color-subtle, #6c757d)}.mapping-details code.kv-val{font-family:monospace;background:var(--kendo-color-surface, transparent);padding:1px 4px;border-radius:3px}.mapping-actions{display:flex;gap:6px;justify-content:flex-end}.empty-hint,.error-hint{padding:12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d);text-align:center}.error-hint{color:var(--kendo-color-error, #dc3545)}.coverage-tabs{display:flex;gap:0;border-bottom:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6))}.coverage-tab{padding:8px 16px;border:none;background:transparent;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));font-size:.85rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;display:inline-flex;align-items:center;gap:6px}.coverage-tab:hover{color:var(--theme-text-primary, inherit)}.coverage-tab.active{color:var(--theme-text-accent, var(--kendo-color-primary, #ff6358));border-bottom-color:var(--theme-text-accent, var(--kendo-color-primary, #ff6358))}.coverage-tab .tab-badge{background:color-mix(in srgb,var(--kendo-color-error, #dc3545) 22%,transparent);color:var(--kendo-color-error, #dc3545);border-radius:10px;padding:1px 8px;font-size:.7rem;font-weight:700}.orphan-toggle{display:inline-flex;align-items:center;gap:6px;font-size:.8rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));cursor:pointer}.orphan-body{flex:1;min-height:0;overflow:auto;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:6px;background:color-mix(in srgb,var(--theme-bg-surface, var(--kendo-color-surface, #fff)) 60%,transparent);padding:8px}.orphan-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.orphan-row{display:flex;flex-direction:column;gap:4px;padding:8px 10px;border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-left-width:3px;border-left-color:var(--kendo-color-error, #dc3545);border-radius:6px;background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f8f9fa)) 65%,transparent)}.orphan-row.orphan-mapped{border-left-color:var(--kendo-color-success, #28a745);opacity:.7}.orphan-row .orphan-line{display:flex;align-items:center;gap:8px}.orphan-row .orphan-line .orphan-check{flex-shrink:0;margin:0;cursor:pointer;accent-color:var(--kendo-color-primary, #ff6358)}.orphan-row .orphan-line .orphan-name{flex:1;font-weight:600;font-size:.9rem}.orphan-row .orphan-badge{font-size:.7rem;padding:1px 8px;border-radius:10px;text-transform:uppercase;letter-spacing:.5px;font-weight:600}.orphan-row .orphan-badge-unmapped{background:color-mix(in srgb,var(--kendo-color-error, #dc3545) 22%,transparent);color:var(--kendo-color-error, #dc3545);border:1px solid var(--kendo-color-error, #dc3545)}.orphan-row .orphan-badge-mapped{background:color-mix(in srgb,var(--kendo-color-success, #28a745) 18%,transparent);color:var(--kendo-color-success, #28a745);border:1px solid var(--kendo-color-success, #28a745)}.orphan-row .orphan-meta{font-family:monospace;font-size:.7rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));word-break:break-all}.orphan-row .orphan-breadcrumb{display:flex;flex-wrap:wrap;align-items:center;gap:4px;font-size:.75rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d))}.orphan-row .orphan-breadcrumb .crumb{line-height:1.3}.orphan-row .orphan-breadcrumb .crumb-sep{opacity:.55;font-weight:600}.orphan-row .orphan-description{font-size:.78rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));font-style:italic}.orphan-row .orphan-actions{display:flex;gap:6px;justify-content:flex-end}.orphan-group{display:flex;flex-direction:column;gap:6px}.orphan-group+.orphan-group{margin-top:14px}.orphan-group .orphan-group-header{display:flex;align-items:baseline;gap:8px;padding:4px 8px;border-bottom:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));font-size:.85rem;font-weight:600;color:var(--theme-text-primary, inherit)}.orphan-group .orphan-group-header .orphan-group-label{flex:1}.orphan-group .orphan-group-header .orphan-group-count{font-size:.7rem;color:var(--theme-text-secondary, var(--kendo-color-subtle, #6c757d));background:color-mix(in srgb,var(--theme-bg-elevated, var(--kendo-color-surface-alt, #f1f3f5)) 80%,transparent);border:1px solid var(--theme-border-subtle, var(--kendo-color-border, #dee2e6));border-radius:10px;padding:0 8px}.validation-summary{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}.badge{font-size:.7rem;padding:2px 8px;border-radius:10px;background:var(--kendo-color-surface, #f1f3f5);border:1px solid var(--kendo-color-border, #dee2e6)}.badge-error{background:color-mix(in srgb,var(--kendo-color-error, #dc3545) 18%,transparent);border-color:var(--kendo-color-error, #dc3545);color:var(--kendo-color-error, #dc3545);font-weight:600}.badge-warning{background:color-mix(in srgb,var(--kendo-color-warning, #ffc107) 22%,transparent);border-color:var(--kendo-color-warning, #ffc107);color:var(--kendo-color-warning-on, #6b5500);font-weight:600}.badge-ok{background:color-mix(in srgb,var(--kendo-color-success, #28a745) 18%,transparent);border-color:var(--kendo-color-success, #28a745);color:var(--kendo-color-success, #28a745);font-weight:600}.badge-info{background:color-mix(in srgb,var(--kendo-color-info, #0dcaf0) 18%,transparent);border-color:var(--kendo-color-info, #0dcaf0);color:var(--kendo-color-info, #0dcaf0)}.validation-error-text{color:var(--kendo-color-error, #dc3545);font-size:.8rem}.validation-timestamp{font-size:.7rem;color:var(--kendo-color-subtle, #6c757d);margin-left:4px}\n"] }]
|
|
20772
|
-
}], propDecorators: { treeView: [{
|
|
20796
|
+
}], ctorParameters: () => [], propDecorators: { treeView: [{
|
|
20773
20797
|
type: ViewChild,
|
|
20774
20798
|
args: [TreeComponent, { static: false }]
|
|
20775
20799
|
}], config: [{
|