@snteam/amplify-angular-core 1.0.40 → 1.0.42
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.
|
@@ -3,7 +3,7 @@ import { Injectable, Component, Pipe, inject, Input, ChangeDetectionStrategy, mo
|
|
|
3
3
|
import * as i3$1 from '@angular/common';
|
|
4
4
|
import { CommonModule } from '@angular/common';
|
|
5
5
|
import * as i1 from '@angular/forms';
|
|
6
|
-
import { FormGroup, Validators, FormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
6
|
+
import { FormGroup, Validators, FormControl, ReactiveFormsModule, FormsModule, FormBuilder } from '@angular/forms';
|
|
7
7
|
import * as i4 from '@angular/material/input';
|
|
8
8
|
import { MatInputModule } from '@angular/material/input';
|
|
9
9
|
import * as i3 from '@angular/material/form-field';
|
|
@@ -30,9 +30,9 @@ import * as i4$1 from '@angular/router';
|
|
|
30
30
|
import { ActivatedRoute, Router } from '@angular/router';
|
|
31
31
|
import * as i5$2 from '@angular/material/tooltip';
|
|
32
32
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
33
|
-
import * as
|
|
33
|
+
import * as i2 from '@angular/material/expansion';
|
|
34
34
|
import { MatExpansionModule } from '@angular/material/expansion';
|
|
35
|
-
import * as
|
|
35
|
+
import * as i4$2 from '@angular/material/card';
|
|
36
36
|
import { MatCardModule } from '@angular/material/card';
|
|
37
37
|
|
|
38
38
|
// Dynamic Relationship Loader Interfaces
|
|
@@ -2296,11 +2296,29 @@ class AmplifyModelService {
|
|
|
2296
2296
|
selectionSetGenerator;
|
|
2297
2297
|
errorHandler;
|
|
2298
2298
|
client;
|
|
2299
|
+
relationshipConfig = null;
|
|
2299
2300
|
constructor(selectionSetGenerator, errorHandler) {
|
|
2300
2301
|
this.selectionSetGenerator = selectionSetGenerator;
|
|
2301
2302
|
this.errorHandler = errorHandler;
|
|
2302
2303
|
// Client will be initialized when Amplify is configured by the consuming application
|
|
2303
2304
|
}
|
|
2305
|
+
/**
|
|
2306
|
+
* Configure the service with relationship selection sets
|
|
2307
|
+
* This should be called by the consuming application to define how relationships should be queried
|
|
2308
|
+
*/
|
|
2309
|
+
configure(config) {
|
|
2310
|
+
if (config.relationshipSelectionSets) {
|
|
2311
|
+
this.relationshipConfig = config.relationshipSelectionSets;
|
|
2312
|
+
console.log('AmplifyModelService: Configured with relationship selection sets:', this.relationshipConfig);
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
/**
|
|
2316
|
+
* Set relationship selection set configuration directly
|
|
2317
|
+
*/
|
|
2318
|
+
setRelationshipConfig(config) {
|
|
2319
|
+
this.relationshipConfig = config;
|
|
2320
|
+
console.log('AmplifyModelService: Updated relationship selection sets configuration:', this.relationshipConfig);
|
|
2321
|
+
}
|
|
2304
2322
|
/**
|
|
2305
2323
|
* Initialize the service with the Amplify client
|
|
2306
2324
|
* This should be called by the consuming application after Amplify.configure()
|
|
@@ -2486,9 +2504,8 @@ class AmplifyModelService {
|
|
|
2486
2504
|
}
|
|
2487
2505
|
}
|
|
2488
2506
|
/**
|
|
2489
|
-
*
|
|
2490
|
-
*
|
|
2491
|
-
* Enhanced with comprehensive error handling and logging
|
|
2507
|
+
* Get selection set for relationship queries using configuration
|
|
2508
|
+
* This replaces the complex dynamic generation with simple configuration lookup
|
|
2492
2509
|
* @param config Relationship configuration object
|
|
2493
2510
|
* @param baseId Base record ID (for compatibility, not used in generation)
|
|
2494
2511
|
* @returns Array of GraphQL field selectors
|
|
@@ -2496,64 +2513,88 @@ class AmplifyModelService {
|
|
|
2496
2513
|
getAmplifySelectionSet(config, baseId) {
|
|
2497
2514
|
try {
|
|
2498
2515
|
if (!config) {
|
|
2499
|
-
|
|
2500
|
-
method: 'getAmplifySelectionSet',
|
|
2501
|
-
baseId,
|
|
2502
|
-
clientAvailable: !!this.client
|
|
2503
|
-
});
|
|
2504
|
-
// Return minimal fallback
|
|
2516
|
+
console.warn('AmplifyModelService: Configuration is null or undefined, using minimal fallback');
|
|
2505
2517
|
return ['id'];
|
|
2506
2518
|
}
|
|
2507
|
-
console.log('AmplifyModelService:
|
|
2519
|
+
console.log('AmplifyModelService: Getting selection set for relationship:', {
|
|
2508
2520
|
relationshipModel: config.relationshipModelName,
|
|
2509
2521
|
fieldName: config.fieldName,
|
|
2510
2522
|
baseModelName: config.baseModelName,
|
|
2511
2523
|
associatedWith: config.associatedWith,
|
|
2512
2524
|
baseId
|
|
2513
2525
|
});
|
|
2514
|
-
//
|
|
2515
|
-
const
|
|
2516
|
-
if (
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
baseModelName: config.baseModelName,
|
|
2533
|
-
fieldName: config.fieldName,
|
|
2534
|
-
associatedWith: config.associatedWith
|
|
2526
|
+
// Try to get selection set from configuration first
|
|
2527
|
+
const configuredSelectionSet = this.getConfiguredSelectionSet(config);
|
|
2528
|
+
if (configuredSelectionSet && configuredSelectionSet.length > 0) {
|
|
2529
|
+
console.log('AmplifyModelService: Using configured selection set:', configuredSelectionSet);
|
|
2530
|
+
return configuredSelectionSet;
|
|
2531
|
+
}
|
|
2532
|
+
// Fall back to default selection set if configured
|
|
2533
|
+
if (this.relationshipConfig?.defaultSelectionSet && this.relationshipConfig.defaultSelectionSet.length > 0) {
|
|
2534
|
+
console.log('AmplifyModelService: Using default configured selection set:', this.relationshipConfig.defaultSelectionSet);
|
|
2535
|
+
return this.relationshipConfig.defaultSelectionSet;
|
|
2536
|
+
}
|
|
2537
|
+
// Fall back to dynamic generation if enabled and no configuration found
|
|
2538
|
+
if (this.relationshipConfig?.enableFallback !== false) {
|
|
2539
|
+
console.log('AmplifyModelService: No configuration found, falling back to dynamic generation');
|
|
2540
|
+
const dynamicSelectionSet = this.selectionSetGenerator.generateSelectionSet(config);
|
|
2541
|
+
if (dynamicSelectionSet && dynamicSelectionSet.length > 0) {
|
|
2542
|
+
console.log('AmplifyModelService: Using dynamic selection set:', dynamicSelectionSet);
|
|
2543
|
+
return dynamicSelectionSet;
|
|
2535
2544
|
}
|
|
2536
|
-
}
|
|
2537
|
-
|
|
2545
|
+
}
|
|
2546
|
+
// Final fallback - use a simple, predictable selection set
|
|
2547
|
+
const fallbackSelectionSet = this.generateSimpleFallbackSelectionSet(config);
|
|
2548
|
+
console.log('AmplifyModelService: Using simple fallback selection set:', fallbackSelectionSet);
|
|
2549
|
+
return fallbackSelectionSet;
|
|
2538
2550
|
}
|
|
2539
2551
|
catch (error) {
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2552
|
+
console.error('AmplifyModelService: Error getting selection set, using minimal fallback:', error);
|
|
2553
|
+
return ['id'];
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
/**
|
|
2557
|
+
* Get configured selection set for a specific relationship
|
|
2558
|
+
*/
|
|
2559
|
+
getConfiguredSelectionSet(config) {
|
|
2560
|
+
if (!this.relationshipConfig?.relationships) {
|
|
2561
|
+
return null;
|
|
2562
|
+
}
|
|
2563
|
+
// Look for exact match first
|
|
2564
|
+
const exactMatch = this.relationshipConfig.relationships.find(rel => rel.relationshipModel === config.relationshipModelName &&
|
|
2565
|
+
rel.fieldName === config.fieldName);
|
|
2566
|
+
if (exactMatch) {
|
|
2567
|
+
console.log('AmplifyModelService: Found exact configuration match:', exactMatch);
|
|
2568
|
+
return exactMatch.selectionSet;
|
|
2569
|
+
}
|
|
2570
|
+
// Look for relationship model match
|
|
2571
|
+
const modelMatch = this.relationshipConfig.relationships.find(rel => rel.relationshipModel === config.relationshipModelName);
|
|
2572
|
+
if (modelMatch) {
|
|
2573
|
+
console.log('AmplifyModelService: Found relationship model match:', modelMatch);
|
|
2574
|
+
return modelMatch.selectionSet;
|
|
2575
|
+
}
|
|
2576
|
+
// Look for field name match
|
|
2577
|
+
const fieldMatch = this.relationshipConfig.relationships.find(rel => rel.fieldName === config.fieldName);
|
|
2578
|
+
if (fieldMatch) {
|
|
2579
|
+
console.log('AmplifyModelService: Found field name match:', fieldMatch);
|
|
2580
|
+
return fieldMatch.selectionSet;
|
|
2556
2581
|
}
|
|
2582
|
+
return null;
|
|
2583
|
+
}
|
|
2584
|
+
/**
|
|
2585
|
+
* Generate a simple, predictable fallback selection set
|
|
2586
|
+
*/
|
|
2587
|
+
generateSimpleFallbackSelectionSet(config) {
|
|
2588
|
+
const selectionSet = ['id'];
|
|
2589
|
+
if (config.fieldName) {
|
|
2590
|
+
// Add the relationship field itself
|
|
2591
|
+
selectionSet.push(config.fieldName);
|
|
2592
|
+
// Add common nested fields
|
|
2593
|
+
selectionSet.push(`${config.fieldName}.id`);
|
|
2594
|
+
selectionSet.push(`${config.fieldName}.name`);
|
|
2595
|
+
selectionSet.push(`${config.fieldName}.title`);
|
|
2596
|
+
}
|
|
2597
|
+
return selectionSet;
|
|
2557
2598
|
}
|
|
2558
2599
|
setRelatedSub(config, baseId) {
|
|
2559
2600
|
if (!this.client) {
|
|
@@ -2757,178 +2798,44 @@ class AmplifyModelService {
|
|
|
2757
2798
|
}
|
|
2758
2799
|
/**
|
|
2759
2800
|
* Validate that relationship field data is populated in query results
|
|
2760
|
-
*
|
|
2801
|
+
* Simplified validation that's more predictable and easier to debug
|
|
2761
2802
|
*/
|
|
2762
2803
|
validateRelationshipFieldPopulation(result, config) {
|
|
2763
2804
|
try {
|
|
2764
2805
|
if (!result || !result.data) {
|
|
2765
|
-
|
|
2766
|
-
method: 'validateRelationshipFieldPopulation',
|
|
2767
|
-
hasResult: !!result,
|
|
2768
|
-
hasData: !!(result?.data),
|
|
2769
|
-
resultType: typeof result,
|
|
2770
|
-
resultKeys: result ? Object.keys(result) : []
|
|
2771
|
-
});
|
|
2806
|
+
console.warn('AmplifyModelService: Query result is missing or has no data property');
|
|
2772
2807
|
return false;
|
|
2773
2808
|
}
|
|
2774
|
-
// Check if any records have the relationship field populated
|
|
2775
2809
|
const records = Array.isArray(result.data) ? result.data : [result.data];
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2810
|
+
if (records.length === 0) {
|
|
2811
|
+
console.warn('AmplifyModelService: No records returned from query');
|
|
2812
|
+
return false;
|
|
2813
|
+
}
|
|
2779
2814
|
console.log(`AmplifyModelService: Validating relationship field '${config.fieldName}' in ${records.length} records`);
|
|
2815
|
+
// Simple validation: if we have records, consider it successful
|
|
2816
|
+
// The actual relationship data validation will happen at the UI level
|
|
2817
|
+
let hasAnyData = false;
|
|
2780
2818
|
for (let i = 0; i < records.length; i++) {
|
|
2781
2819
|
const record = records[i];
|
|
2782
|
-
if (
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
fieldName: config.fieldName,
|
|
2787
|
-
fieldValue: record[config.fieldName],
|
|
2788
|
-
fieldType: typeof record[config.fieldName]
|
|
2789
|
-
});
|
|
2790
|
-
const relationshipData = record[config.fieldName];
|
|
2791
|
-
// Strategy 1: Check for fully populated relationship (object with data)
|
|
2792
|
-
if (relationshipData !== undefined && relationshipData !== null) {
|
|
2793
|
-
console.log(`AmplifyModelService: Record ${i} has relationship data:`, relationshipData);
|
|
2794
|
-
// If it's an object with properties, consider it populated
|
|
2795
|
-
if (typeof relationshipData === 'object' && Object.keys(relationshipData).length > 0) {
|
|
2796
|
-
populatedCount++;
|
|
2797
|
-
recordsWithAnyRelationshipData++;
|
|
2798
|
-
}
|
|
2799
|
-
// If it's a non-null primitive value, also consider it populated
|
|
2800
|
-
else if (typeof relationshipData !== 'object') {
|
|
2801
|
-
populatedCount++;
|
|
2802
|
-
recordsWithAnyRelationshipData++;
|
|
2803
|
-
}
|
|
2804
|
-
// If it's an empty object or array, consider it partially populated
|
|
2805
|
-
else {
|
|
2806
|
-
partiallyPopulatedCount++;
|
|
2807
|
-
recordsWithAnyRelationshipData++;
|
|
2808
|
-
}
|
|
2809
|
-
}
|
|
2810
|
-
// Strategy 2: Check if the record has any nested relationship data
|
|
2811
|
-
// This handles cases where the selection set includes nested fields like "table.id", "table.name"
|
|
2812
|
-
const hasNestedRelationshipData = this.hasNestedRelationshipData(record, config.fieldName);
|
|
2813
|
-
if (hasNestedRelationshipData) {
|
|
2814
|
-
console.log(`AmplifyModelService: Record ${i} has nested relationship data for field '${config.fieldName}'`);
|
|
2815
|
-
populatedCount++;
|
|
2816
|
-
recordsWithAnyRelationshipData++;
|
|
2817
|
-
}
|
|
2818
|
-
// Strategy 3: Check for any field that contains the relationship name
|
|
2819
|
-
// This is a very permissive check for cases where the field structure is different
|
|
2820
|
-
const hasAnyRelatedField = this.hasAnyRelatedField(record, config.fieldName);
|
|
2821
|
-
if (hasAnyRelatedField) {
|
|
2822
|
-
console.log(`AmplifyModelService: Record ${i} has some related field data for '${config.fieldName}'`);
|
|
2823
|
-
recordsWithAnyRelationshipData++;
|
|
2820
|
+
if (record && typeof record === 'object' && Object.keys(record).length > 0) {
|
|
2821
|
+
hasAnyData = true;
|
|
2822
|
+
console.log(`AmplifyModelService: Record ${i} has data:`, Object.keys(record));
|
|
2823
|
+
break;
|
|
2824
2824
|
}
|
|
2825
2825
|
}
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
const totalValidRecords = Math.max(populatedCount + partiallyPopulatedCount, recordsWithAnyRelationshipData);
|
|
2829
|
-
console.log(`AmplifyModelService: Relationship validation summary:`, {
|
|
2830
|
-
fieldName: config.fieldName,
|
|
2831
|
-
totalRecords: records.length,
|
|
2832
|
-
populatedCount,
|
|
2833
|
-
partiallyPopulatedCount,
|
|
2834
|
-
recordsWithAnyRelationshipData,
|
|
2835
|
-
totalValidRecords,
|
|
2836
|
-
validationPassed: totalValidRecords > 0
|
|
2837
|
-
});
|
|
2838
|
-
if (totalValidRecords === 0) {
|
|
2839
|
-
// Log very detailed information about what we found
|
|
2840
|
-
const sampleRecord = records[0];
|
|
2841
|
-
const detailedError = {
|
|
2842
|
-
method: 'validateRelationshipFieldPopulation',
|
|
2843
|
-
recordCount: records.length,
|
|
2844
|
-
populatedCount,
|
|
2845
|
-
partiallyPopulatedCount,
|
|
2846
|
-
recordsWithAnyRelationshipData,
|
|
2847
|
-
fieldName: config.fieldName,
|
|
2848
|
-
sampleRecord: sampleRecord ? {
|
|
2849
|
-
keys: Object.keys(sampleRecord),
|
|
2850
|
-
values: sampleRecord,
|
|
2851
|
-
fieldValue: sampleRecord[config.fieldName],
|
|
2852
|
-
fieldType: typeof sampleRecord[config.fieldName]
|
|
2853
|
-
} : null,
|
|
2854
|
-
allRecordKeys: records.map((r) => r ? Object.keys(r) : []),
|
|
2855
|
-
config: {
|
|
2856
|
-
relationshipModelName: config.relationshipModelName,
|
|
2857
|
-
baseModelName: config.baseModelName,
|
|
2858
|
-
fieldName: config.fieldName,
|
|
2859
|
-
associatedWith: config.associatedWith
|
|
2860
|
-
}
|
|
2861
|
-
};
|
|
2862
|
-
this.errorHandler.logSelectionSetError(SelectionSetErrorType.FIELD_NOT_FOUND, `No records have relationship field '${config.fieldName}' populated - detailed analysis shows no relationship data found`, config, undefined, detailedError);
|
|
2863
|
-
console.error(`AmplifyModelService: DETAILED VALIDATION FAILURE for field '${config.fieldName}':`, detailedError);
|
|
2864
|
-
// TEMPORARY: For debugging, let's be even more permissive and return true if we have any records at all
|
|
2865
|
-
// This will help us see what data is actually being processed
|
|
2866
|
-
if (records.length > 0) {
|
|
2867
|
-
console.warn(`AmplifyModelService: TEMPORARY DEBUG MODE - Allowing validation to pass despite no relationship data found`);
|
|
2868
|
-
return true;
|
|
2869
|
-
}
|
|
2826
|
+
if (!hasAnyData) {
|
|
2827
|
+
console.warn('AmplifyModelService: No valid records found in query result');
|
|
2870
2828
|
return false;
|
|
2871
2829
|
}
|
|
2872
|
-
console.log(`AmplifyModelService: Relationship
|
|
2830
|
+
console.log(`AmplifyModelService: Relationship validation successful - found ${records.length} records with data`);
|
|
2873
2831
|
return true;
|
|
2874
2832
|
}
|
|
2875
2833
|
catch (error) {
|
|
2876
|
-
this.errorHandler.logSelectionSetError(SelectionSetErrorType.GRAPHQL_ERROR, `Error validating relationship field population: ${error instanceof Error ? error.message : String(error)}`, config, undefined, {
|
|
2877
|
-
method: 'validateRelationshipFieldPopulation',
|
|
2878
|
-
originalError: {
|
|
2879
|
-
message: error instanceof Error ? error.message : String(error),
|
|
2880
|
-
stack: error instanceof Error ? error.stack : undefined
|
|
2881
|
-
}
|
|
2882
|
-
});
|
|
2883
2834
|
console.error('AmplifyModelService: Error validating relationship field population:', error);
|
|
2884
|
-
//
|
|
2885
|
-
console.warn('AmplifyModelService: Validation error occurred, allowing relationship to proceed');
|
|
2835
|
+
// Be permissive on validation errors
|
|
2886
2836
|
return true;
|
|
2887
2837
|
}
|
|
2888
2838
|
}
|
|
2889
|
-
/**
|
|
2890
|
-
* Check if a record has nested relationship data based on field patterns
|
|
2891
|
-
* This helps validate cases where selection sets use nested field access like "table.id", "table.name"
|
|
2892
|
-
*/
|
|
2893
|
-
hasNestedRelationshipData(record, fieldName) {
|
|
2894
|
-
if (!record || typeof record !== 'object') {
|
|
2895
|
-
return false;
|
|
2896
|
-
}
|
|
2897
|
-
// Look for any fields that start with the relationship field name followed by a dot
|
|
2898
|
-
const nestedFieldPattern = `${fieldName}.`;
|
|
2899
|
-
for (const key of Object.keys(record)) {
|
|
2900
|
-
if (key.startsWith(nestedFieldPattern)) {
|
|
2901
|
-
const value = record[key];
|
|
2902
|
-
if (value !== undefined && value !== null) {
|
|
2903
|
-
console.log(`AmplifyModelService: Found nested relationship data: ${key} = ${value}`);
|
|
2904
|
-
return true;
|
|
2905
|
-
}
|
|
2906
|
-
}
|
|
2907
|
-
}
|
|
2908
|
-
return false;
|
|
2909
|
-
}
|
|
2910
|
-
/**
|
|
2911
|
-
* Check if a record has any field that might be related to the relationship field
|
|
2912
|
-
* This is a very permissive check for debugging purposes
|
|
2913
|
-
*/
|
|
2914
|
-
hasAnyRelatedField(record, fieldName) {
|
|
2915
|
-
if (!record || typeof record !== 'object') {
|
|
2916
|
-
return false;
|
|
2917
|
-
}
|
|
2918
|
-
const fieldNameLower = fieldName.toLowerCase();
|
|
2919
|
-
for (const key of Object.keys(record)) {
|
|
2920
|
-
const keyLower = key.toLowerCase();
|
|
2921
|
-
// Check if the key contains the field name or vice versa
|
|
2922
|
-
if (keyLower.includes(fieldNameLower) || fieldNameLower.includes(keyLower)) {
|
|
2923
|
-
const value = record[key];
|
|
2924
|
-
if (value !== undefined && value !== null) {
|
|
2925
|
-
console.log(`AmplifyModelService: Found potentially related field: ${key} = ${value}`);
|
|
2926
|
-
return true;
|
|
2927
|
-
}
|
|
2928
|
-
}
|
|
2929
|
-
}
|
|
2930
|
-
return false;
|
|
2931
|
-
}
|
|
2932
2839
|
/**
|
|
2933
2840
|
* Log query attempt details
|
|
2934
2841
|
*/
|
|
@@ -5243,13 +5150,31 @@ class ConfigurationsComponent {
|
|
|
5243
5150
|
qcs = inject(QuestionControlService);
|
|
5244
5151
|
ams = inject(AmplifyModelService);
|
|
5245
5152
|
snackBar = inject(MatSnackBar);
|
|
5153
|
+
fb = inject(FormBuilder);
|
|
5246
5154
|
// Track which models exist in the schema
|
|
5247
5155
|
hasTableConfig = false;
|
|
5248
5156
|
hasFormView = false;
|
|
5249
5157
|
hasListView = false;
|
|
5250
5158
|
hasFieldConfig = false;
|
|
5159
|
+
// Relationship configuration management
|
|
5160
|
+
relationshipConfigs = [];
|
|
5161
|
+
showAddRelationshipConfig = false;
|
|
5162
|
+
editingRelationshipConfig = null;
|
|
5163
|
+
relationshipConfigForm;
|
|
5164
|
+
availableModels = [];
|
|
5165
|
+
availableFields = [];
|
|
5166
|
+
constructor() {
|
|
5167
|
+
this.relationshipConfigForm = this.fb.group({
|
|
5168
|
+
relationshipModel: ['', Validators.required],
|
|
5169
|
+
fieldName: ['', Validators.required],
|
|
5170
|
+
selectionSet: ['', Validators.required],
|
|
5171
|
+
description: ['']
|
|
5172
|
+
});
|
|
5173
|
+
}
|
|
5251
5174
|
ngOnInit() {
|
|
5252
5175
|
this.checkAvailableModels();
|
|
5176
|
+
this.loadRelationshipConfigs();
|
|
5177
|
+
this.loadAvailableModels();
|
|
5253
5178
|
}
|
|
5254
5179
|
get outputs() {
|
|
5255
5180
|
return this.amplifyOutputs || this.qcs.outputs;
|
|
@@ -5540,21 +5465,256 @@ return fields;
|
|
|
5540
5465
|
}
|
|
5541
5466
|
return 'text'; // Default fallback
|
|
5542
5467
|
}
|
|
5468
|
+
// Relationship Configuration Management Methods
|
|
5469
|
+
loadAvailableModels() {
|
|
5470
|
+
if (!this.outputs || !this.outputs.data || !this.outputs.data.model_introspection) {
|
|
5471
|
+
console.warn('ConfigurationsComponent: No model introspection data available for relationship configs');
|
|
5472
|
+
return;
|
|
5473
|
+
}
|
|
5474
|
+
const models = this.outputs.data.model_introspection.models;
|
|
5475
|
+
this.availableModels = Object.keys(models).sort();
|
|
5476
|
+
console.log('ConfigurationsComponent: Available models for relationship configs:', this.availableModels);
|
|
5477
|
+
}
|
|
5478
|
+
onRelationshipModelChange(event) {
|
|
5479
|
+
const selectedModel = event.value;
|
|
5480
|
+
this.loadAvailableFields(selectedModel);
|
|
5481
|
+
// Reset field name when model changes
|
|
5482
|
+
this.relationshipConfigForm.patchValue({ fieldName: '' });
|
|
5483
|
+
}
|
|
5484
|
+
loadAvailableFields(modelName) {
|
|
5485
|
+
if (!this.outputs || !this.outputs.data || !this.outputs.data.model_introspection) {
|
|
5486
|
+
this.availableFields = [];
|
|
5487
|
+
return;
|
|
5488
|
+
}
|
|
5489
|
+
const models = this.outputs.data.model_introspection.models;
|
|
5490
|
+
const modelData = models[modelName];
|
|
5491
|
+
if (!modelData || !modelData.fields) {
|
|
5492
|
+
this.availableFields = [];
|
|
5493
|
+
return;
|
|
5494
|
+
}
|
|
5495
|
+
// Get relationship fields from the selected model
|
|
5496
|
+
this.availableFields = Object.values(modelData.fields)
|
|
5497
|
+
.filter((field) => {
|
|
5498
|
+
// Include relationship fields (fields that reference other models)
|
|
5499
|
+
return field.association && field.association.connectionType;
|
|
5500
|
+
})
|
|
5501
|
+
.map((field) => ({
|
|
5502
|
+
name: field.name,
|
|
5503
|
+
label: this.formatFieldLabel(field.name),
|
|
5504
|
+
type: field.type
|
|
5505
|
+
}))
|
|
5506
|
+
.sort((a, b) => a.label.localeCompare(b.label));
|
|
5507
|
+
console.log('ConfigurationsComponent: Available relationship fields for', modelName, ':', this.availableFields);
|
|
5508
|
+
}
|
|
5509
|
+
loadRelationshipConfigs() {
|
|
5510
|
+
// Load from localStorage for now (could be extended to use Amplify model later)
|
|
5511
|
+
const stored = localStorage.getItem('snteam-relationship-configs');
|
|
5512
|
+
if (stored) {
|
|
5513
|
+
try {
|
|
5514
|
+
const config = JSON.parse(stored);
|
|
5515
|
+
this.relationshipConfigs = (config.relationships || []).map(rel => ({
|
|
5516
|
+
...rel,
|
|
5517
|
+
id: rel.id || this.generateId()
|
|
5518
|
+
}));
|
|
5519
|
+
console.log('ConfigurationsComponent: Loaded relationship configs:', this.relationshipConfigs);
|
|
5520
|
+
}
|
|
5521
|
+
catch (error) {
|
|
5522
|
+
console.error('ConfigurationsComponent: Error loading relationship configs:', error);
|
|
5523
|
+
this.relationshipConfigs = [];
|
|
5524
|
+
}
|
|
5525
|
+
}
|
|
5526
|
+
else {
|
|
5527
|
+
this.relationshipConfigs = [];
|
|
5528
|
+
}
|
|
5529
|
+
}
|
|
5530
|
+
saveRelationshipConfigsToStorage() {
|
|
5531
|
+
const config = {
|
|
5532
|
+
relationships: this.relationshipConfigs.map(({ id, ...config }) => config),
|
|
5533
|
+
defaultSelectionSet: ['id'],
|
|
5534
|
+
enableFallback: true
|
|
5535
|
+
};
|
|
5536
|
+
localStorage.setItem('snteam-relationship-configs', JSON.stringify(config));
|
|
5537
|
+
// Update the AmplifyModelService with the new configuration
|
|
5538
|
+
this.ams.setRelationshipConfig(config);
|
|
5539
|
+
console.log('ConfigurationsComponent: Saved relationship configs to storage and updated service');
|
|
5540
|
+
}
|
|
5541
|
+
saveRelationshipConfig() {
|
|
5542
|
+
if (!this.relationshipConfigForm.valid) {
|
|
5543
|
+
this.snackBar.open('Please fill in all required fields', 'Dismiss', { duration: 3000 });
|
|
5544
|
+
return;
|
|
5545
|
+
}
|
|
5546
|
+
const formValue = this.relationshipConfigForm.value;
|
|
5547
|
+
// Parse selection set from textarea (one field per line)
|
|
5548
|
+
const selectionSet = formValue.selectionSet
|
|
5549
|
+
.split('\n')
|
|
5550
|
+
.map((line) => line.trim())
|
|
5551
|
+
.filter((line) => line.length > 0);
|
|
5552
|
+
const config = {
|
|
5553
|
+
relationshipModel: formValue.relationshipModel,
|
|
5554
|
+
fieldName: formValue.fieldName,
|
|
5555
|
+
selectionSet: selectionSet,
|
|
5556
|
+
description: formValue.description || undefined,
|
|
5557
|
+
id: this.editingRelationshipConfig?.id || this.generateId()
|
|
5558
|
+
};
|
|
5559
|
+
if (this.editingRelationshipConfig) {
|
|
5560
|
+
// Update existing configuration
|
|
5561
|
+
const index = this.relationshipConfigs.findIndex(c => c.id === this.editingRelationshipConfig.id);
|
|
5562
|
+
if (index >= 0) {
|
|
5563
|
+
this.relationshipConfigs[index] = config;
|
|
5564
|
+
this.snackBar.open('Relationship configuration updated successfully', 'Dismiss', { duration: 3000 });
|
|
5565
|
+
}
|
|
5566
|
+
}
|
|
5567
|
+
else {
|
|
5568
|
+
// Check for duplicate configuration
|
|
5569
|
+
const existing = this.relationshipConfigs.find(c => c.relationshipModel === config.relationshipModel &&
|
|
5570
|
+
c.fieldName === config.fieldName);
|
|
5571
|
+
if (existing) {
|
|
5572
|
+
this.snackBar.open('Configuration for this relationship already exists', 'Dismiss', { duration: 3000 });
|
|
5573
|
+
return;
|
|
5574
|
+
}
|
|
5575
|
+
// Add new configuration
|
|
5576
|
+
this.relationshipConfigs.push(config);
|
|
5577
|
+
this.snackBar.open('Relationship configuration added successfully', 'Dismiss', { duration: 3000 });
|
|
5578
|
+
}
|
|
5579
|
+
this.saveRelationshipConfigsToStorage();
|
|
5580
|
+
this.cancelRelationshipConfigEdit();
|
|
5581
|
+
}
|
|
5582
|
+
editRelationshipConfig(config) {
|
|
5583
|
+
this.editingRelationshipConfig = config;
|
|
5584
|
+
this.showAddRelationshipConfig = true;
|
|
5585
|
+
// Load fields for the selected model
|
|
5586
|
+
this.loadAvailableFields(config.relationshipModel);
|
|
5587
|
+
// Populate form with existing values
|
|
5588
|
+
this.relationshipConfigForm.patchValue({
|
|
5589
|
+
relationshipModel: config.relationshipModel,
|
|
5590
|
+
fieldName: config.fieldName,
|
|
5591
|
+
selectionSet: config.selectionSet.join('\n'),
|
|
5592
|
+
description: config.description || ''
|
|
5593
|
+
});
|
|
5594
|
+
}
|
|
5595
|
+
deleteRelationshipConfig(config) {
|
|
5596
|
+
const index = this.relationshipConfigs.findIndex(c => c.id === config.id);
|
|
5597
|
+
if (index >= 0) {
|
|
5598
|
+
this.relationshipConfigs.splice(index, 1);
|
|
5599
|
+
this.saveRelationshipConfigsToStorage();
|
|
5600
|
+
this.snackBar.open('Relationship configuration deleted', 'Dismiss', { duration: 3000 });
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
generateId() {
|
|
5604
|
+
return 'config_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
5605
|
+
}
|
|
5606
|
+
cancelRelationshipConfigEdit() {
|
|
5607
|
+
this.showAddRelationshipConfig = false;
|
|
5608
|
+
this.editingRelationshipConfig = null;
|
|
5609
|
+
this.relationshipConfigForm.reset();
|
|
5610
|
+
this.availableFields = [];
|
|
5611
|
+
}
|
|
5612
|
+
loadDefaultRelationshipConfigs() {
|
|
5613
|
+
// Load some sensible default configurations
|
|
5614
|
+
const defaultConfigs = [
|
|
5615
|
+
{
|
|
5616
|
+
id: this.generateId(),
|
|
5617
|
+
relationshipModel: 'ListView',
|
|
5618
|
+
fieldName: 'table',
|
|
5619
|
+
selectionSet: [
|
|
5620
|
+
'id',
|
|
5621
|
+
'table',
|
|
5622
|
+
'table.id',
|
|
5623
|
+
'table.name',
|
|
5624
|
+
'table.title',
|
|
5625
|
+
'table.description'
|
|
5626
|
+
],
|
|
5627
|
+
description: 'Configuration for ListView -> Table relationship'
|
|
5628
|
+
},
|
|
5629
|
+
{
|
|
5630
|
+
id: this.generateId(),
|
|
5631
|
+
relationshipModel: 'FormViewField',
|
|
5632
|
+
fieldName: 'formView',
|
|
5633
|
+
selectionSet: [
|
|
5634
|
+
'id',
|
|
5635
|
+
'formView',
|
|
5636
|
+
'formView.id',
|
|
5637
|
+
'formView.name',
|
|
5638
|
+
'formView.title',
|
|
5639
|
+
'formView.description'
|
|
5640
|
+
],
|
|
5641
|
+
description: 'Configuration for FormViewField -> FormView relationship'
|
|
5642
|
+
},
|
|
5643
|
+
{
|
|
5644
|
+
id: this.generateId(),
|
|
5645
|
+
relationshipModel: 'ServiceCustomer',
|
|
5646
|
+
fieldName: 'service',
|
|
5647
|
+
selectionSet: [
|
|
5648
|
+
'id',
|
|
5649
|
+
'service',
|
|
5650
|
+
'service.id',
|
|
5651
|
+
'service.title',
|
|
5652
|
+
'service.cost',
|
|
5653
|
+
'service.price'
|
|
5654
|
+
],
|
|
5655
|
+
description: 'Configuration for ServiceCustomer -> Service relationship'
|
|
5656
|
+
},
|
|
5657
|
+
{
|
|
5658
|
+
id: this.generateId(),
|
|
5659
|
+
relationshipModel: 'ServiceCustomer',
|
|
5660
|
+
fieldName: 'customer',
|
|
5661
|
+
selectionSet: [
|
|
5662
|
+
'id',
|
|
5663
|
+
'customer',
|
|
5664
|
+
'customer.id',
|
|
5665
|
+
'customer.name',
|
|
5666
|
+
'customer.contact_preference'
|
|
5667
|
+
],
|
|
5668
|
+
description: 'Configuration for ServiceCustomer -> Customer relationship'
|
|
5669
|
+
}
|
|
5670
|
+
];
|
|
5671
|
+
// Only add configs that don't already exist
|
|
5672
|
+
let addedCount = 0;
|
|
5673
|
+
for (const defaultConfig of defaultConfigs) {
|
|
5674
|
+
const existing = this.relationshipConfigs.find(c => c.relationshipModel === defaultConfig.relationshipModel &&
|
|
5675
|
+
c.fieldName === defaultConfig.fieldName);
|
|
5676
|
+
if (!existing) {
|
|
5677
|
+
this.relationshipConfigs.push(defaultConfig);
|
|
5678
|
+
addedCount++;
|
|
5679
|
+
}
|
|
5680
|
+
}
|
|
5681
|
+
if (addedCount > 0) {
|
|
5682
|
+
this.saveRelationshipConfigsToStorage();
|
|
5683
|
+
this.snackBar.open(`Added ${addedCount} default relationship configurations`, 'Dismiss', { duration: 3000 });
|
|
5684
|
+
}
|
|
5685
|
+
else {
|
|
5686
|
+
this.snackBar.open('All default configurations already exist', 'Dismiss', { duration: 3000 });
|
|
5687
|
+
}
|
|
5688
|
+
}
|
|
5689
|
+
clearAllRelationshipConfigs() {
|
|
5690
|
+
if (this.relationshipConfigs.length === 0) {
|
|
5691
|
+
this.snackBar.open('No relationship configurations to clear', 'Dismiss', { duration: 3000 });
|
|
5692
|
+
return;
|
|
5693
|
+
}
|
|
5694
|
+
this.relationshipConfigs = [];
|
|
5695
|
+
this.saveRelationshipConfigsToStorage();
|
|
5696
|
+
this.snackBar.open('All relationship configurations cleared', 'Dismiss', { duration: 3000 });
|
|
5697
|
+
}
|
|
5543
5698
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigurationsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5544
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ConfigurationsComponent, isStandalone: true, selector: "snteam-configurations", inputs: { amplifyOutputs: "amplifyOutputs" }, ngImport: i0, template: "<div class=\"configurations-container\">\n <h2>System Configurations</h2>\n \n <mat-accordion multi=\"true\">\n \n <!-- Table Configs Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>table_chart</mat-icon>\n <span class=\"panel-title\">Table Configs</span>\n </mat-panel-title>\n <mat-panel-description>\n Manage table configurations and settings\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasTableConfig) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"accent\" (click)=\"populateDefaultTableConfigs()\" class=\"populate-button\">\n <mat-icon>auto_fix_high</mat-icon>\n Populate Default Table Configs\n </button>\n <button mat-raised-button color=\"warn\" (click)=\"deleteTableConfigs()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All Table Configs\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'TableConfig'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>TableConfig Model Not Found</h3>\n <p>The TableConfig model is not available in your Amplify schema. Please add it to your data model to manage table configurations.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- Form Views Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>dynamic_form</mat-icon>\n <span class=\"panel-title\">Form Views</span>\n </mat-panel-title>\n <mat-panel-description>\n Configure custom form layouts and field selections\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasFormView) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"warn\" (click)=\"deleteFormViews()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All Form Views\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'FormView'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>FormView Model Not Found</h3>\n <p>The FormView model is not available in your Amplify schema. Please add it to your data model to create custom form configurations.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- List Views Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>view_list</mat-icon>\n <span class=\"panel-title\">List Views</span>\n </mat-panel-title>\n <mat-panel-description>\n Customize list displays and column configurations\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasListView) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"warn\" (click)=\"deleteListViews()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All List Views\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'ListView'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>ListView Model Not Found</h3>\n <p>The ListView model is not available in your Amplify schema. Please add it to your data model to create custom list view configurations.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- Field Configs Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>settings</mat-icon>\n <span class=\"panel-title\">Field Configs</span>\n </mat-panel-title>\n <mat-panel-description>\n Configure field behavior, validation, and choices\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasFieldConfig) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"accent\" (click)=\"populateDefaultFieldConfigs()\" class=\"populate-button\">\n <mat-icon>auto_fix_high</mat-icon>\n Populate Default Field Configs\n </button>\n <button mat-raised-button color=\"warn\" (click)=\"deleteFieldConfigs()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All Field Configs\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'FieldConfig'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>FieldConfig Model Not Found</h3>\n <p>The FieldConfig model is not available in your Amplify schema. Please add it to your data model to configure field behavior and validation.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n </mat-accordion>\n</div>", styles: [".configurations-container{padding:20px;max-width:1200px;margin:0 auto}.configurations-container h2{margin-bottom:24px;color:#333;font-weight:500}.mat-expansion-panel{margin-bottom:16px;border-radius:8px;box-shadow:0 2px 4px #0000001a}.mat-expansion-panel-header{padding:16px 24px}.panel-title{margin-left:12px;font-weight:500;font-size:16px}.mat-panel-description{color:#666;font-size:14px}.panel-content{padding:16px 24px 24px;background-color:#fafafa}.error-card{text-align:center;padding:24px;background-color:#fff;border:2px dashed #ddd;border-radius:8px}.error-card mat-card-content{padding:0}.error-icon{font-size:48px;width:48px;height:48px;color:#ff9800;margin-bottom:16px}.error-card h3{margin:0 0 12px;color:#333;font-weight:500}.error-card p{color:#666;line-height:1.5;max-width:500px;margin:0 auto 20px}.help-button{margin-top:8px}.help-button mat-icon{margin-right:8px}.section-actions{margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid #e0e0e0;display:flex;gap:12px;flex-wrap:wrap}.generate-button{background-color:#2196f3;color:#fff}.generate-button mat-icon{margin-right:8px}.delete-button{background-color:#f44336;color:#fff}.delete-button mat-icon{margin-right:8px}.populate-button mat-icon{margin-right:8px}@media (max-width: 768px){.configurations-container{padding:16px}.panel-content{padding:12px 16px 16px}.error-card{padding:16px}.error-icon{font-size:36px;width:36px;height:36px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i1$1.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i1$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i1$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i1$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i1$1.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3$5.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i3$5.MatCardContent, selector: "mat-card-content" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "component", type: ListViewComponent, selector: "snteam-list-view", inputs: ["modelName", "customItemTemplate", "hideNewButton", "title", "useRouter", "showRowActions", "showDeleteAction", "customRowActions"], outputs: ["itemClick", "newClick", "itemsLoaded", "itemDeleted"] }] });
|
|
5699
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ConfigurationsComponent, isStandalone: true, selector: "snteam-configurations", inputs: { amplifyOutputs: "amplifyOutputs" }, ngImport: i0, template: "<div class=\"configurations-container\">\n <h2>System Configurations</h2>\n \n <mat-accordion multi=\"true\">\n \n <!-- Table Configs Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>table_chart</mat-icon>\n <span class=\"panel-title\">Table Configs</span>\n </mat-panel-title>\n <mat-panel-description>\n Manage table configurations and settings\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasTableConfig) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"accent\" (click)=\"populateDefaultTableConfigs()\" class=\"populate-button\">\n <mat-icon>auto_fix_high</mat-icon>\n Populate Default Table Configs\n </button>\n <button mat-raised-button color=\"warn\" (click)=\"deleteTableConfigs()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All Table Configs\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'TableConfig'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>TableConfig Model Not Found</h3>\n <p>The TableConfig model is not available in your Amplify schema. Please add it to your data model to manage table configurations.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- Form Views Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>dynamic_form</mat-icon>\n <span class=\"panel-title\">Form Views</span>\n </mat-panel-title>\n <mat-panel-description>\n Configure custom form layouts and field selections\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasFormView) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"warn\" (click)=\"deleteFormViews()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All Form Views\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'FormView'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>FormView Model Not Found</h3>\n <p>The FormView model is not available in your Amplify schema. Please add it to your data model to create custom form configurations.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- List Views Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>view_list</mat-icon>\n <span class=\"panel-title\">List Views</span>\n </mat-panel-title>\n <mat-panel-description>\n Customize list displays and column configurations\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasListView) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"warn\" (click)=\"deleteListViews()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All List Views\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'ListView'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>ListView Model Not Found</h3>\n <p>The ListView model is not available in your Amplify schema. Please add it to your data model to create custom list view configurations.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- Field Configs Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>settings</mat-icon>\n <span class=\"panel-title\">Field Configs</span>\n </mat-panel-title>\n <mat-panel-description>\n Configure field behavior, validation, and choices\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasFieldConfig) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"accent\" (click)=\"populateDefaultFieldConfigs()\" class=\"populate-button\">\n <mat-icon>auto_fix_high</mat-icon>\n Populate Default Field Configs\n </button>\n <button mat-raised-button color=\"warn\" (click)=\"deleteFieldConfigs()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All Field Configs\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'FieldConfig'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>FieldConfig Model Not Found</h3>\n <p>The FieldConfig model is not available in your Amplify schema. Please add it to your data model to configure field behavior and validation.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- Relationship Configs Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>link</mat-icon>\n <span class=\"panel-title\">Relationship Configs</span>\n </mat-panel-title>\n <mat-panel-description>\n Configure GraphQL selection sets for relationship queries\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n <div class=\"section-actions\">\n <button mat-raised-button color=\"primary\" (click)=\"showAddRelationshipConfig = !showAddRelationshipConfig\" class=\"add-button\">\n <mat-icon>{{ showAddRelationshipConfig ? 'close' : 'add' }}</mat-icon>\n {{ showAddRelationshipConfig ? 'Cancel' : 'Add Relationship Config' }}\n </button>\n <button mat-raised-button color=\"accent\" (click)=\"loadDefaultRelationshipConfigs()\" class=\"populate-button\">\n <mat-icon>auto_fix_high</mat-icon>\n Load Default Configs\n </button>\n <button mat-raised-button color=\"warn\" (click)=\"clearAllRelationshipConfigs()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Clear All Configs\n </button>\n </div>\n\n <!-- Add/Edit Relationship Config Form -->\n @if (showAddRelationshipConfig) {\n <mat-card class=\"config-form-card\">\n <mat-card-header>\n <mat-card-title>\n <mat-icon>{{ editingRelationshipConfig ? 'edit' : 'add' }}</mat-icon>\n {{ editingRelationshipConfig ? 'Edit' : 'Add' }} Relationship Configuration\n </mat-card-title>\n </mat-card-header>\n <mat-card-content>\n <form [formGroup]=\"relationshipConfigForm\" (ngSubmit)=\"saveRelationshipConfig()\">\n <div class=\"form-row\">\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>Relationship Model</mat-label>\n <mat-select formControlName=\"relationshipModel\" (selectionChange)=\"onRelationshipModelChange($event)\">\n @for (model of availableModels; track model) {\n <mat-option [value]=\"model\">{{ model }}</mat-option>\n }\n </mat-select>\n <mat-hint>The model that contains the relationship field</mat-hint>\n </mat-form-field>\n </div>\n\n <div class=\"form-row\">\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>Field Name</mat-label>\n <mat-select formControlName=\"fieldName\">\n @for (field of availableFields; track field.name) {\n <mat-option [value]=\"field.name\">{{ field.label }}</mat-option>\n }\n </mat-select>\n <mat-hint>The relationship field in the selected model</mat-hint>\n </mat-form-field>\n </div>\n\n <div class=\"form-row\">\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>Selection Set</mat-label>\n <textarea \n matInput \n formControlName=\"selectionSet\" \n rows=\"6\"\n placeholder=\"Enter GraphQL selection fields, one per line: id table table.id table.name table.title\">\n </textarea>\n <mat-hint>GraphQL fields to select for this relationship (one per line)</mat-hint>\n </mat-form-field>\n </div>\n\n <div class=\"form-row\">\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>Description</mat-label>\n <input matInput formControlName=\"description\" placeholder=\"Optional description for this configuration\">\n <mat-hint>Optional description to document this configuration</mat-hint>\n </mat-form-field>\n </div>\n\n <div class=\"form-actions\">\n <button mat-raised-button color=\"primary\" type=\"submit\" [disabled]=\"!relationshipConfigForm.valid\">\n <mat-icon>save</mat-icon>\n {{ editingRelationshipConfig ? 'Update' : 'Save' }} Configuration\n </button>\n <button mat-button type=\"button\" (click)=\"cancelRelationshipConfigEdit()\">\n <mat-icon>cancel</mat-icon>\n Cancel\n </button>\n </div>\n </form>\n </mat-card-content>\n </mat-card>\n }\n\n <!-- Current Relationship Configurations -->\n <div class=\"current-configs\">\n <h3>Current Relationship Configurations</h3>\n @if (relationshipConfigs.length === 0) {\n <mat-card class=\"empty-state-card\">\n <mat-card-content>\n <mat-icon class=\"empty-icon\">link_off</mat-icon>\n <h4>No Relationship Configurations</h4>\n <p>Add relationship configurations to control GraphQL selection sets for relationship queries.</p>\n </mat-card-content>\n </mat-card>\n } @else {\n <div class=\"configs-list\">\n @for (config of relationshipConfigs; track config.id) {\n <mat-card class=\"config-card\">\n <mat-card-header>\n <mat-card-title>{{ config.relationshipModel }} \u2192 {{ config.fieldName }}</mat-card-title>\n <mat-card-subtitle>{{ config.description || 'No description' }}</mat-card-subtitle>\n <div class=\"card-actions\">\n <button mat-icon-button (click)=\"editRelationshipConfig(config)\" matTooltip=\"Edit Configuration\">\n <mat-icon>edit</mat-icon>\n </button>\n <button mat-icon-button color=\"warn\" (click)=\"deleteRelationshipConfig(config)\" matTooltip=\"Delete Configuration\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </mat-card-header>\n <mat-card-content>\n <div class=\"selection-set\">\n <strong>Selection Set:</strong>\n <div class=\"selection-fields\">\n @for (field of config.selectionSet; track field) {\n <span class=\"field-chip\">{{ field }}</span>\n }\n </div>\n </div>\n </mat-card-content>\n </mat-card>\n }\n </div>\n }\n </div>\n\n <!-- Configuration Status -->\n <div class=\"config-status\">\n <mat-card class=\"status-card\">\n <mat-card-content>\n <div class=\"status-info\">\n <mat-icon class=\"status-icon\">info</mat-icon>\n <div class=\"status-text\">\n <strong>Active Configurations: {{ relationshipConfigs.length }}</strong>\n <p>These configurations control how relationship data is queried from your GraphQL API.</p>\n </div>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </div>\n </mat-expansion-panel>\n\n </mat-accordion>\n</div>", styles: [".configurations-container{padding:20px;max-width:1200px;margin:0 auto}.configurations-container h2{margin-bottom:24px;color:#333;font-weight:500}.mat-expansion-panel{margin-bottom:16px;border-radius:8px;box-shadow:0 2px 4px #0000001a}.mat-expansion-panel-header{padding:16px 24px}.panel-title{margin-left:12px;font-weight:500;font-size:16px}.mat-panel-description{color:#666;font-size:14px}.panel-content{padding:16px 24px 24px;background-color:#fafafa}.error-card{text-align:center;padding:24px;background-color:#fff;border:2px dashed #ddd;border-radius:8px}.error-card mat-card-content{padding:0}.error-icon{font-size:48px;width:48px;height:48px;color:#ff9800;margin-bottom:16px}.error-card h3{margin:0 0 12px;color:#333;font-weight:500}.error-card p{color:#666;line-height:1.5;max-width:500px;margin:0 auto 20px}.help-button{margin-top:8px}.help-button mat-icon{margin-right:8px}.section-actions{margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid #e0e0e0;display:flex;gap:12px;flex-wrap:wrap}.generate-button{background-color:#2196f3;color:#fff}.generate-button mat-icon{margin-right:8px}.delete-button{background-color:#f44336;color:#fff}.delete-button mat-icon{margin-right:8px}.populate-button mat-icon{margin-right:8px}@media (max-width: 768px){.configurations-container{padding:16px}.panel-content{padding:12px 16px 16px}.error-card{padding:16px}.error-icon{font-size:36px;width:36px;height:36px}}.add-button{background-color:#4caf50;color:#fff}.add-button mat-icon{margin-right:8px}.config-form-card{margin:16px 0;background-color:#fff;border-radius:8px;box-shadow:0 2px 8px #0000001a}.config-form-card mat-card-header{background-color:#f5f5f5;border-radius:8px 8px 0 0;padding:16px 24px}.config-form-card mat-card-title{display:flex;align-items:center;font-size:18px;font-weight:500;color:#333}.config-form-card mat-card-title mat-icon{margin-right:12px;color:#2196f3}.config-form-card mat-card-content{padding:24px}.form-row{margin-bottom:20px}.full-width{width:100%}.form-actions{display:flex;gap:12px;margin-top:24px;padding-top:16px;border-top:1px solid #e0e0e0}.form-actions button mat-icon{margin-right:8px}.current-configs{margin-top:24px}.current-configs h3{margin-bottom:16px;color:#333;font-weight:500}.empty-state-card{text-align:center;padding:32px;background-color:#fff;border:2px dashed #ddd;border-radius:8px}.empty-state-card mat-card-content{padding:0}.empty-icon{font-size:48px;width:48px;height:48px;color:#bbb;margin-bottom:16px}.empty-state-card h4{margin:0 0 12px;color:#333;font-weight:500}.empty-state-card p{margin:0;color:#666;line-height:1.5}.configs-list{display:grid;gap:16px}.config-card{background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a;transition:box-shadow .2s ease}.config-card:hover{box-shadow:0 4px 8px #00000026}.config-card mat-card-header{padding:16px 20px;border-bottom:1px solid #f0f0f0;display:flex;align-items:center;justify-content:space-between}.config-card mat-card-title{font-size:16px;font-weight:500;color:#333;margin:0}.config-card mat-card-subtitle{font-size:14px;color:#666;margin:4px 0 0}.card-actions{display:flex;gap:4px}.config-card mat-card-content{padding:16px 20px}.selection-set{margin:0}.selection-set strong{display:block;margin-bottom:8px;color:#333;font-size:14px}.selection-fields{display:flex;flex-wrap:wrap;gap:6px}.field-chip{display:inline-block;padding:4px 8px;background-color:#e3f2fd;color:#1976d2;border-radius:12px;font-size:12px;font-family:Roboto Mono,monospace;border:1px solid #bbdefb}.config-status{margin-top:24px}.status-card{background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.status-card mat-card-content{padding:16px 20px}.status-info{display:flex;align-items:flex-start;gap:12px}.status-icon{color:#2196f3;margin-top:2px}.status-text strong{display:block;margin-bottom:4px;color:#333;font-size:16px}.status-text p{margin:0;color:#666;font-size:14px;line-height:1.5}@media (max-width: 768px){.config-form-card mat-card-content{padding:16px}.form-actions{flex-direction:column}.form-actions button{width:100%}.config-card mat-card-header{flex-direction:column;align-items:flex-start;gap:8px}.card-actions{align-self:flex-end}.selection-fields{gap:4px}.field-chip{font-size:11px;padding:3px 6px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i2.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i2.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i2.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i2.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i4$2.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i4$2.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i4$2.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i4$2.MatCardSubtitle, selector: "mat-card-subtitle, [mat-card-subtitle], [matCardSubtitle]" }, { kind: "directive", type: i4$2.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3$3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "component", type: ListViewComponent, selector: "snteam-list-view", inputs: ["modelName", "customItemTemplate", "hideNewButton", "title", "useRouter", "showRowActions", "showDeleteAction", "customRowActions"], outputs: ["itemClick", "newClick", "itemsLoaded", "itemDeleted"] }] });
|
|
5545
5700
|
}
|
|
5546
5701
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigurationsComponent, decorators: [{
|
|
5547
5702
|
type: Component,
|
|
5548
5703
|
args: [{ selector: 'snteam-configurations', standalone: true, imports: [
|
|
5549
5704
|
CommonModule,
|
|
5705
|
+
ReactiveFormsModule,
|
|
5550
5706
|
MatExpansionModule,
|
|
5551
5707
|
MatIconModule,
|
|
5552
5708
|
MatCardModule,
|
|
5553
5709
|
MatButtonModule,
|
|
5710
|
+
MatFormFieldModule,
|
|
5711
|
+
MatInputModule,
|
|
5712
|
+
MatSelectModule,
|
|
5713
|
+
MatTooltipModule,
|
|
5554
5714
|
MatSnackBarModule,
|
|
5555
5715
|
ListViewComponent
|
|
5556
|
-
], template: "<div class=\"configurations-container\">\n <h2>System Configurations</h2>\n \n <mat-accordion multi=\"true\">\n \n <!-- Table Configs Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>table_chart</mat-icon>\n <span class=\"panel-title\">Table Configs</span>\n </mat-panel-title>\n <mat-panel-description>\n Manage table configurations and settings\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasTableConfig) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"accent\" (click)=\"populateDefaultTableConfigs()\" class=\"populate-button\">\n <mat-icon>auto_fix_high</mat-icon>\n Populate Default Table Configs\n </button>\n <button mat-raised-button color=\"warn\" (click)=\"deleteTableConfigs()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All Table Configs\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'TableConfig'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>TableConfig Model Not Found</h3>\n <p>The TableConfig model is not available in your Amplify schema. Please add it to your data model to manage table configurations.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- Form Views Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>dynamic_form</mat-icon>\n <span class=\"panel-title\">Form Views</span>\n </mat-panel-title>\n <mat-panel-description>\n Configure custom form layouts and field selections\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasFormView) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"warn\" (click)=\"deleteFormViews()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All Form Views\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'FormView'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>FormView Model Not Found</h3>\n <p>The FormView model is not available in your Amplify schema. Please add it to your data model to create custom form configurations.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- List Views Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>view_list</mat-icon>\n <span class=\"panel-title\">List Views</span>\n </mat-panel-title>\n <mat-panel-description>\n Customize list displays and column configurations\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasListView) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"warn\" (click)=\"deleteListViews()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All List Views\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'ListView'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>ListView Model Not Found</h3>\n <p>The ListView model is not available in your Amplify schema. Please add it to your data model to create custom list view configurations.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- Field Configs Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>settings</mat-icon>\n <span class=\"panel-title\">Field Configs</span>\n </mat-panel-title>\n <mat-panel-description>\n Configure field behavior, validation, and choices\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasFieldConfig) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"accent\" (click)=\"populateDefaultFieldConfigs()\" class=\"populate-button\">\n <mat-icon>auto_fix_high</mat-icon>\n Populate Default Field Configs\n </button>\n <button mat-raised-button color=\"warn\" (click)=\"deleteFieldConfigs()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All Field Configs\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'FieldConfig'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>FieldConfig Model Not Found</h3>\n <p>The FieldConfig model is not available in your Amplify schema. Please add it to your data model to configure field behavior and validation.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n </mat-accordion>\n</div>", styles: [".configurations-container{padding:20px;max-width:1200px;margin:0 auto}.configurations-container h2{margin-bottom:24px;color:#333;font-weight:500}.mat-expansion-panel{margin-bottom:16px;border-radius:8px;box-shadow:0 2px 4px #0000001a}.mat-expansion-panel-header{padding:16px 24px}.panel-title{margin-left:12px;font-weight:500;font-size:16px}.mat-panel-description{color:#666;font-size:14px}.panel-content{padding:16px 24px 24px;background-color:#fafafa}.error-card{text-align:center;padding:24px;background-color:#fff;border:2px dashed #ddd;border-radius:8px}.error-card mat-card-content{padding:0}.error-icon{font-size:48px;width:48px;height:48px;color:#ff9800;margin-bottom:16px}.error-card h3{margin:0 0 12px;color:#333;font-weight:500}.error-card p{color:#666;line-height:1.5;max-width:500px;margin:0 auto 20px}.help-button{margin-top:8px}.help-button mat-icon{margin-right:8px}.section-actions{margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid #e0e0e0;display:flex;gap:12px;flex-wrap:wrap}.generate-button{background-color:#2196f3;color:#fff}.generate-button mat-icon{margin-right:8px}.delete-button{background-color:#f44336;color:#fff}.delete-button mat-icon{margin-right:8px}.populate-button mat-icon{margin-right:8px}@media (max-width: 768px){.configurations-container{padding:16px}.panel-content{padding:12px 16px 16px}.error-card{padding:16px}.error-icon{font-size:36px;width:36px;height:36px}}\n"] }]
|
|
5557
|
-
}], propDecorators: { amplifyOutputs: [{
|
|
5716
|
+
], template: "<div class=\"configurations-container\">\n <h2>System Configurations</h2>\n \n <mat-accordion multi=\"true\">\n \n <!-- Table Configs Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>table_chart</mat-icon>\n <span class=\"panel-title\">Table Configs</span>\n </mat-panel-title>\n <mat-panel-description>\n Manage table configurations and settings\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasTableConfig) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"accent\" (click)=\"populateDefaultTableConfigs()\" class=\"populate-button\">\n <mat-icon>auto_fix_high</mat-icon>\n Populate Default Table Configs\n </button>\n <button mat-raised-button color=\"warn\" (click)=\"deleteTableConfigs()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All Table Configs\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'TableConfig'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>TableConfig Model Not Found</h3>\n <p>The TableConfig model is not available in your Amplify schema. Please add it to your data model to manage table configurations.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- Form Views Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>dynamic_form</mat-icon>\n <span class=\"panel-title\">Form Views</span>\n </mat-panel-title>\n <mat-panel-description>\n Configure custom form layouts and field selections\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasFormView) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"warn\" (click)=\"deleteFormViews()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All Form Views\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'FormView'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>FormView Model Not Found</h3>\n <p>The FormView model is not available in your Amplify schema. Please add it to your data model to create custom form configurations.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- List Views Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>view_list</mat-icon>\n <span class=\"panel-title\">List Views</span>\n </mat-panel-title>\n <mat-panel-description>\n Customize list displays and column configurations\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasListView) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"warn\" (click)=\"deleteListViews()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All List Views\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'ListView'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>ListView Model Not Found</h3>\n <p>The ListView model is not available in your Amplify schema. Please add it to your data model to create custom list view configurations.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- Field Configs Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>settings</mat-icon>\n <span class=\"panel-title\">Field Configs</span>\n </mat-panel-title>\n <mat-panel-description>\n Configure field behavior, validation, and choices\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n @if (hasFieldConfig) {\n <div class=\"section-actions\">\n <button mat-raised-button color=\"accent\" (click)=\"populateDefaultFieldConfigs()\" class=\"populate-button\">\n <mat-icon>auto_fix_high</mat-icon>\n Populate Default Field Configs\n </button>\n <button mat-raised-button color=\"warn\" (click)=\"deleteFieldConfigs()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Delete All Field Configs\n </button>\n </div>\n <snteam-list-view \n [modelName]=\"'FieldConfig'\"\n [useRouter]=\"true\"\n [showDeleteAction]=\"true\">\n </snteam-list-view>\n } @else {\n <mat-card class=\"error-card\">\n <mat-card-content>\n <mat-icon class=\"error-icon\">error_outline</mat-icon>\n <h3>FieldConfig Model Not Found</h3>\n <p>The FieldConfig model is not available in your Amplify schema. Please add it to your data model to configure field behavior and validation.</p>\n <button mat-raised-button color=\"primary\" class=\"help-button\">\n <mat-icon>help</mat-icon>\n View Documentation\n </button>\n </mat-card-content>\n </mat-card>\n }\n </div>\n </mat-expansion-panel>\n\n <!-- Relationship Configs Section -->\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon>link</mat-icon>\n <span class=\"panel-title\">Relationship Configs</span>\n </mat-panel-title>\n <mat-panel-description>\n Configure GraphQL selection sets for relationship queries\n </mat-panel-description>\n </mat-expansion-panel-header>\n \n <div class=\"panel-content\">\n <div class=\"section-actions\">\n <button mat-raised-button color=\"primary\" (click)=\"showAddRelationshipConfig = !showAddRelationshipConfig\" class=\"add-button\">\n <mat-icon>{{ showAddRelationshipConfig ? 'close' : 'add' }}</mat-icon>\n {{ showAddRelationshipConfig ? 'Cancel' : 'Add Relationship Config' }}\n </button>\n <button mat-raised-button color=\"accent\" (click)=\"loadDefaultRelationshipConfigs()\" class=\"populate-button\">\n <mat-icon>auto_fix_high</mat-icon>\n Load Default Configs\n </button>\n <button mat-raised-button color=\"warn\" (click)=\"clearAllRelationshipConfigs()\" class=\"delete-button\">\n <mat-icon>delete</mat-icon>\n Clear All Configs\n </button>\n </div>\n\n <!-- Add/Edit Relationship Config Form -->\n @if (showAddRelationshipConfig) {\n <mat-card class=\"config-form-card\">\n <mat-card-header>\n <mat-card-title>\n <mat-icon>{{ editingRelationshipConfig ? 'edit' : 'add' }}</mat-icon>\n {{ editingRelationshipConfig ? 'Edit' : 'Add' }} Relationship Configuration\n </mat-card-title>\n </mat-card-header>\n <mat-card-content>\n <form [formGroup]=\"relationshipConfigForm\" (ngSubmit)=\"saveRelationshipConfig()\">\n <div class=\"form-row\">\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>Relationship Model</mat-label>\n <mat-select formControlName=\"relationshipModel\" (selectionChange)=\"onRelationshipModelChange($event)\">\n @for (model of availableModels; track model) {\n <mat-option [value]=\"model\">{{ model }}</mat-option>\n }\n </mat-select>\n <mat-hint>The model that contains the relationship field</mat-hint>\n </mat-form-field>\n </div>\n\n <div class=\"form-row\">\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>Field Name</mat-label>\n <mat-select formControlName=\"fieldName\">\n @for (field of availableFields; track field.name) {\n <mat-option [value]=\"field.name\">{{ field.label }}</mat-option>\n }\n </mat-select>\n <mat-hint>The relationship field in the selected model</mat-hint>\n </mat-form-field>\n </div>\n\n <div class=\"form-row\">\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>Selection Set</mat-label>\n <textarea \n matInput \n formControlName=\"selectionSet\" \n rows=\"6\"\n placeholder=\"Enter GraphQL selection fields, one per line: id table table.id table.name table.title\">\n </textarea>\n <mat-hint>GraphQL fields to select for this relationship (one per line)</mat-hint>\n </mat-form-field>\n </div>\n\n <div class=\"form-row\">\n <mat-form-field appearance=\"outline\" class=\"full-width\">\n <mat-label>Description</mat-label>\n <input matInput formControlName=\"description\" placeholder=\"Optional description for this configuration\">\n <mat-hint>Optional description to document this configuration</mat-hint>\n </mat-form-field>\n </div>\n\n <div class=\"form-actions\">\n <button mat-raised-button color=\"primary\" type=\"submit\" [disabled]=\"!relationshipConfigForm.valid\">\n <mat-icon>save</mat-icon>\n {{ editingRelationshipConfig ? 'Update' : 'Save' }} Configuration\n </button>\n <button mat-button type=\"button\" (click)=\"cancelRelationshipConfigEdit()\">\n <mat-icon>cancel</mat-icon>\n Cancel\n </button>\n </div>\n </form>\n </mat-card-content>\n </mat-card>\n }\n\n <!-- Current Relationship Configurations -->\n <div class=\"current-configs\">\n <h3>Current Relationship Configurations</h3>\n @if (relationshipConfigs.length === 0) {\n <mat-card class=\"empty-state-card\">\n <mat-card-content>\n <mat-icon class=\"empty-icon\">link_off</mat-icon>\n <h4>No Relationship Configurations</h4>\n <p>Add relationship configurations to control GraphQL selection sets for relationship queries.</p>\n </mat-card-content>\n </mat-card>\n } @else {\n <div class=\"configs-list\">\n @for (config of relationshipConfigs; track config.id) {\n <mat-card class=\"config-card\">\n <mat-card-header>\n <mat-card-title>{{ config.relationshipModel }} \u2192 {{ config.fieldName }}</mat-card-title>\n <mat-card-subtitle>{{ config.description || 'No description' }}</mat-card-subtitle>\n <div class=\"card-actions\">\n <button mat-icon-button (click)=\"editRelationshipConfig(config)\" matTooltip=\"Edit Configuration\">\n <mat-icon>edit</mat-icon>\n </button>\n <button mat-icon-button color=\"warn\" (click)=\"deleteRelationshipConfig(config)\" matTooltip=\"Delete Configuration\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </mat-card-header>\n <mat-card-content>\n <div class=\"selection-set\">\n <strong>Selection Set:</strong>\n <div class=\"selection-fields\">\n @for (field of config.selectionSet; track field) {\n <span class=\"field-chip\">{{ field }}</span>\n }\n </div>\n </div>\n </mat-card-content>\n </mat-card>\n }\n </div>\n }\n </div>\n\n <!-- Configuration Status -->\n <div class=\"config-status\">\n <mat-card class=\"status-card\">\n <mat-card-content>\n <div class=\"status-info\">\n <mat-icon class=\"status-icon\">info</mat-icon>\n <div class=\"status-text\">\n <strong>Active Configurations: {{ relationshipConfigs.length }}</strong>\n <p>These configurations control how relationship data is queried from your GraphQL API.</p>\n </div>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </div>\n </mat-expansion-panel>\n\n </mat-accordion>\n</div>", styles: [".configurations-container{padding:20px;max-width:1200px;margin:0 auto}.configurations-container h2{margin-bottom:24px;color:#333;font-weight:500}.mat-expansion-panel{margin-bottom:16px;border-radius:8px;box-shadow:0 2px 4px #0000001a}.mat-expansion-panel-header{padding:16px 24px}.panel-title{margin-left:12px;font-weight:500;font-size:16px}.mat-panel-description{color:#666;font-size:14px}.panel-content{padding:16px 24px 24px;background-color:#fafafa}.error-card{text-align:center;padding:24px;background-color:#fff;border:2px dashed #ddd;border-radius:8px}.error-card mat-card-content{padding:0}.error-icon{font-size:48px;width:48px;height:48px;color:#ff9800;margin-bottom:16px}.error-card h3{margin:0 0 12px;color:#333;font-weight:500}.error-card p{color:#666;line-height:1.5;max-width:500px;margin:0 auto 20px}.help-button{margin-top:8px}.help-button mat-icon{margin-right:8px}.section-actions{margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid #e0e0e0;display:flex;gap:12px;flex-wrap:wrap}.generate-button{background-color:#2196f3;color:#fff}.generate-button mat-icon{margin-right:8px}.delete-button{background-color:#f44336;color:#fff}.delete-button mat-icon{margin-right:8px}.populate-button mat-icon{margin-right:8px}@media (max-width: 768px){.configurations-container{padding:16px}.panel-content{padding:12px 16px 16px}.error-card{padding:16px}.error-icon{font-size:36px;width:36px;height:36px}}.add-button{background-color:#4caf50;color:#fff}.add-button mat-icon{margin-right:8px}.config-form-card{margin:16px 0;background-color:#fff;border-radius:8px;box-shadow:0 2px 8px #0000001a}.config-form-card mat-card-header{background-color:#f5f5f5;border-radius:8px 8px 0 0;padding:16px 24px}.config-form-card mat-card-title{display:flex;align-items:center;font-size:18px;font-weight:500;color:#333}.config-form-card mat-card-title mat-icon{margin-right:12px;color:#2196f3}.config-form-card mat-card-content{padding:24px}.form-row{margin-bottom:20px}.full-width{width:100%}.form-actions{display:flex;gap:12px;margin-top:24px;padding-top:16px;border-top:1px solid #e0e0e0}.form-actions button mat-icon{margin-right:8px}.current-configs{margin-top:24px}.current-configs h3{margin-bottom:16px;color:#333;font-weight:500}.empty-state-card{text-align:center;padding:32px;background-color:#fff;border:2px dashed #ddd;border-radius:8px}.empty-state-card mat-card-content{padding:0}.empty-icon{font-size:48px;width:48px;height:48px;color:#bbb;margin-bottom:16px}.empty-state-card h4{margin:0 0 12px;color:#333;font-weight:500}.empty-state-card p{margin:0;color:#666;line-height:1.5}.configs-list{display:grid;gap:16px}.config-card{background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a;transition:box-shadow .2s ease}.config-card:hover{box-shadow:0 4px 8px #00000026}.config-card mat-card-header{padding:16px 20px;border-bottom:1px solid #f0f0f0;display:flex;align-items:center;justify-content:space-between}.config-card mat-card-title{font-size:16px;font-weight:500;color:#333;margin:0}.config-card mat-card-subtitle{font-size:14px;color:#666;margin:4px 0 0}.card-actions{display:flex;gap:4px}.config-card mat-card-content{padding:16px 20px}.selection-set{margin:0}.selection-set strong{display:block;margin-bottom:8px;color:#333;font-size:14px}.selection-fields{display:flex;flex-wrap:wrap;gap:6px}.field-chip{display:inline-block;padding:4px 8px;background-color:#e3f2fd;color:#1976d2;border-radius:12px;font-size:12px;font-family:Roboto Mono,monospace;border:1px solid #bbdefb}.config-status{margin-top:24px}.status-card{background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.status-card mat-card-content{padding:16px 20px}.status-info{display:flex;align-items:flex-start;gap:12px}.status-icon{color:#2196f3;margin-top:2px}.status-text strong{display:block;margin-bottom:4px;color:#333;font-size:16px}.status-text p{margin:0;color:#666;font-size:14px;line-height:1.5}@media (max-width: 768px){.config-form-card mat-card-content{padding:16px}.form-actions{flex-direction:column}.form-actions button{width:100%}.config-card mat-card-header{flex-direction:column;align-items:flex-start;gap:8px}.card-actions{align-self:flex-end}.selection-fields{gap:4px}.field-chip{font-size:11px;padding:3px 6px}}\n"] }]
|
|
5717
|
+
}], ctorParameters: () => [], propDecorators: { amplifyOutputs: [{
|
|
5558
5718
|
type: Input
|
|
5559
5719
|
}] } });
|
|
5560
5720
|
|