@snteam/amplify-angular-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -0
- package/fesm2022/snteam-amplify-angular-core.mjs +1764 -0
- package/fesm2022/snteam-amplify-angular-core.mjs.map +1 -0
- package/index.d.ts +406 -0
- package/package.json +52 -0
|
@@ -0,0 +1,1764 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Component, Injectable, Pipe, inject, Input, ChangeDetectionStrategy, model, signal, EventEmitter, Output } from '@angular/core';
|
|
3
|
+
import * as i3$1 from '@angular/common';
|
|
4
|
+
import { CommonModule } from '@angular/common';
|
|
5
|
+
import * as i1 from '@angular/forms';
|
|
6
|
+
import { FormGroup, Validators, FormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
7
|
+
import * as i4 from '@angular/material/input';
|
|
8
|
+
import { MatInputModule } from '@angular/material/input';
|
|
9
|
+
import * as i3 from '@angular/material/form-field';
|
|
10
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
11
|
+
import * as i5 from '@angular/material/select';
|
|
12
|
+
import { MatSelectModule } from '@angular/material/select';
|
|
13
|
+
import * as i6 from '@angular/material/datepicker';
|
|
14
|
+
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
15
|
+
import { provideNativeDateAdapter } from '@angular/material/core';
|
|
16
|
+
import * as i3$2 from '@angular/material/divider';
|
|
17
|
+
import { MatDividerModule } from '@angular/material/divider';
|
|
18
|
+
import * as i2 from '@angular/material/toolbar';
|
|
19
|
+
import { MatToolbarModule } from '@angular/material/toolbar';
|
|
20
|
+
import * as i3$3 from '@angular/material/button';
|
|
21
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
22
|
+
import * as i4$1 from '@angular/material/icon';
|
|
23
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
24
|
+
import * as i5$1 from '@angular/material/list';
|
|
25
|
+
import { MatListModule } from '@angular/material/list';
|
|
26
|
+
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose, MatDialog } from '@angular/material/dialog';
|
|
27
|
+
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
|
28
|
+
|
|
29
|
+
class AmplifyAngularCore {
|
|
30
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AmplifyAngularCore, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
31
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: AmplifyAngularCore, isStandalone: true, selector: "snteam-amplify-angular-core", ngImport: i0, template: `
|
|
32
|
+
<p>
|
|
33
|
+
amplify-angular-core works!
|
|
34
|
+
</p>
|
|
35
|
+
`, isInline: true, styles: [""] });
|
|
36
|
+
}
|
|
37
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AmplifyAngularCore, decorators: [{
|
|
38
|
+
type: Component,
|
|
39
|
+
args: [{ selector: 'snteam-amplify-angular-core', imports: [], template: `
|
|
40
|
+
<p>
|
|
41
|
+
amplify-angular-core works!
|
|
42
|
+
</p>
|
|
43
|
+
` }]
|
|
44
|
+
}] });
|
|
45
|
+
|
|
46
|
+
class AmplifyFormBuilderService {
|
|
47
|
+
fb;
|
|
48
|
+
skipFields = ['createdAt', 'updatedAt'];
|
|
49
|
+
materialInputs = [
|
|
50
|
+
'textbox',
|
|
51
|
+
'int',
|
|
52
|
+
'number',
|
|
53
|
+
'color',
|
|
54
|
+
'email',
|
|
55
|
+
'month',
|
|
56
|
+
'number',
|
|
57
|
+
'password',
|
|
58
|
+
'search',
|
|
59
|
+
'tel',
|
|
60
|
+
'text',
|
|
61
|
+
'time',
|
|
62
|
+
'url',
|
|
63
|
+
'week'
|
|
64
|
+
];
|
|
65
|
+
switchInputs = ['dropdown'];
|
|
66
|
+
refInputs = ['async-dropdown'];
|
|
67
|
+
formGroupInputs = ['formgroup']; // we skip these and handle them in the form
|
|
68
|
+
datePickerInputs = ['date', 'datepicker', 'datetime-local'];
|
|
69
|
+
outputs;
|
|
70
|
+
constructor(fb) {
|
|
71
|
+
this.fb = fb;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Initialize the service with Amplify outputs
|
|
75
|
+
* This should be called by the consuming application after Amplify.configure()
|
|
76
|
+
*/
|
|
77
|
+
initializeOutputs(outputs) {
|
|
78
|
+
this.outputs = outputs;
|
|
79
|
+
}
|
|
80
|
+
getInputTypeFromFieldType(type) {
|
|
81
|
+
if (this.materialInputs.includes(type))
|
|
82
|
+
return 'materialInputs';
|
|
83
|
+
if (this.switchInputs.includes(type))
|
|
84
|
+
return 'switchInputs';
|
|
85
|
+
if (this.refInputs.includes(type))
|
|
86
|
+
return 'refInputs';
|
|
87
|
+
if (this.formGroupInputs.includes(type))
|
|
88
|
+
return 'formGroupInputs';
|
|
89
|
+
if (this.datePickerInputs.includes(type))
|
|
90
|
+
return 'datePickerInputs';
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
getMaterialInputs() {
|
|
94
|
+
return this.materialInputs;
|
|
95
|
+
}
|
|
96
|
+
getSwitchInputs() {
|
|
97
|
+
return this.switchInputs;
|
|
98
|
+
}
|
|
99
|
+
getRefInputs() {
|
|
100
|
+
return this.refInputs;
|
|
101
|
+
}
|
|
102
|
+
getErrorStrForControl(ctrl, label) {
|
|
103
|
+
const errorMap = {
|
|
104
|
+
email: 'Please enter a valid email address',
|
|
105
|
+
invalidPhoneNumber: 'Please enter a phone number in (111) 222-3333 format.',
|
|
106
|
+
required: (label || 'This field') + ' is required.'
|
|
107
|
+
};
|
|
108
|
+
if (!ctrl.errors)
|
|
109
|
+
return '';
|
|
110
|
+
let errStrs = [];
|
|
111
|
+
let keys = Object.keys(ctrl.errors);
|
|
112
|
+
for (let err of keys) {
|
|
113
|
+
if (errorMap[err]) {
|
|
114
|
+
errStrs.push(errorMap[err]);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
errStrs.push(`Invalid ${err}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return errStrs.join(', ');
|
|
121
|
+
}
|
|
122
|
+
/** Shared Methods for FormControls **/
|
|
123
|
+
getPathFromRoot(searchKey, controls, pathArr) {
|
|
124
|
+
if (!pathArr)
|
|
125
|
+
pathArr = [];
|
|
126
|
+
const isInControls = Object.keys(controls).find(name => name === searchKey);
|
|
127
|
+
if (isInControls) {
|
|
128
|
+
pathArr.push(searchKey);
|
|
129
|
+
return pathArr.join('.');
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
const tempArr = [];
|
|
133
|
+
const entries = Object.entries(controls);
|
|
134
|
+
for (let e of entries) {
|
|
135
|
+
let controlName = e[0];
|
|
136
|
+
let controlObj = e[1];
|
|
137
|
+
if (controlObj instanceof FormGroup) {
|
|
138
|
+
console.log('getPathFromRoot controlName', typeof controlName, controlName, 'controlObj', controlObj);
|
|
139
|
+
tempArr.push(controlName);
|
|
140
|
+
console.log('getPathFromRoot', controlName, 'is a FormGroup in path tempArr', tempArr);
|
|
141
|
+
const childControls = controls[controlName].controls;
|
|
142
|
+
console.log('getPathFromRoot childControls', childControls);
|
|
143
|
+
let childPath = this.getPathFromRoot(searchKey, childControls, [...pathArr, ...tempArr]);
|
|
144
|
+
if (childPath !== '')
|
|
145
|
+
return childPath;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return '';
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** FormBuilder Methods **/
|
|
152
|
+
_getFormGroupFromFieldsArr(fields) {
|
|
153
|
+
if (!this.outputs) {
|
|
154
|
+
console.error('AmplifyFormBuilderService: Outputs not initialized. Call initializeOutputs() first.');
|
|
155
|
+
return this.fb.group({});
|
|
156
|
+
}
|
|
157
|
+
console.log('_getFormGroupFromFieldsArr fields', fields);
|
|
158
|
+
const formGroup = this.fb.group({});
|
|
159
|
+
const fieldsArr = Object.values(fields);
|
|
160
|
+
fieldsArr.forEach((field) => {
|
|
161
|
+
if (this.skipFields.indexOf(field.name) > -1)
|
|
162
|
+
return;
|
|
163
|
+
if (typeof field.type === 'object') {
|
|
164
|
+
if (field.type.model) {
|
|
165
|
+
formGroup.addControl(field.name, this._getControlForField(field));
|
|
166
|
+
}
|
|
167
|
+
if (field.type.nonModel) {
|
|
168
|
+
formGroup.addControl(field.name, this._getGroupControlForNonModel(field));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
formGroup.addControl(field.name, this._getControlForField(field));
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
return formGroup;
|
|
176
|
+
}
|
|
177
|
+
_getGroupControlForNonModel(field) {
|
|
178
|
+
if (!this.outputs) {
|
|
179
|
+
console.error('AmplifyFormBuilderService: Outputs not initialized. Call initializeOutputs() first.');
|
|
180
|
+
return this.fb.group({});
|
|
181
|
+
}
|
|
182
|
+
const nonModel = field.type.nonModel;
|
|
183
|
+
const fields = this.outputs.data.model_introspection.nonModels[nonModel].fields;
|
|
184
|
+
return this._getFormGroupFromFieldsArr(fields);
|
|
185
|
+
}
|
|
186
|
+
_getControlForField(field) {
|
|
187
|
+
const validators = field.required ? [Validators.required] : [];
|
|
188
|
+
return new FormControl(field.value || '', validators);
|
|
189
|
+
}
|
|
190
|
+
buildFormForModel(model) {
|
|
191
|
+
if (!this.outputs) {
|
|
192
|
+
console.error('AmplifyFormBuilderService: Outputs not initialized. Call initializeOutputs() first.');
|
|
193
|
+
return this.fb.group({});
|
|
194
|
+
}
|
|
195
|
+
const config = this.outputs.data.model_introspection.models[model].fields;
|
|
196
|
+
const formGroup = this._getFormGroupFromFieldsArr(config);
|
|
197
|
+
return formGroup;
|
|
198
|
+
}
|
|
199
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AmplifyFormBuilderService, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
200
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AmplifyFormBuilderService, providedIn: 'root' });
|
|
201
|
+
}
|
|
202
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AmplifyFormBuilderService, decorators: [{
|
|
203
|
+
type: Injectable,
|
|
204
|
+
args: [{
|
|
205
|
+
providedIn: 'root'
|
|
206
|
+
}]
|
|
207
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }] });
|
|
208
|
+
|
|
209
|
+
class ValToTitlePipe {
|
|
210
|
+
transform(value) {
|
|
211
|
+
if (!value) {
|
|
212
|
+
return '';
|
|
213
|
+
}
|
|
214
|
+
let fixed = value.replace(new RegExp('_', 'g'), ' ').split(' ')
|
|
215
|
+
.map(w => w.length > 0 ? w[0].toUpperCase() + w.substring(1).toLowerCase() : '')
|
|
216
|
+
.join(' ');
|
|
217
|
+
return fixed;
|
|
218
|
+
}
|
|
219
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ValToTitlePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
220
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: ValToTitlePipe, isStandalone: true, name: "valToTitle" });
|
|
221
|
+
}
|
|
222
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ValToTitlePipe, decorators: [{
|
|
223
|
+
type: Pipe,
|
|
224
|
+
args: [{
|
|
225
|
+
name: 'valToTitle',
|
|
226
|
+
standalone: true
|
|
227
|
+
}]
|
|
228
|
+
}] });
|
|
229
|
+
|
|
230
|
+
class AmplifyModelService {
|
|
231
|
+
client;
|
|
232
|
+
constructor() {
|
|
233
|
+
// Client will be initialized when Amplify is configured by the consuming application
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Initialize the service with the Amplify client
|
|
237
|
+
* This should be called by the consuming application after Amplify.configure()
|
|
238
|
+
*/
|
|
239
|
+
initializeClient(schema) {
|
|
240
|
+
// Dynamic import to avoid build-time dependency
|
|
241
|
+
try {
|
|
242
|
+
// This will be provided by the consuming application
|
|
243
|
+
this.client = globalThis.amplifyClient;
|
|
244
|
+
if (!this.client) {
|
|
245
|
+
console.warn('AmplifyModelService: No client provided. Call with generateClient result.');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
console.error('Failed to initialize Amplify client:', error);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Set the client directly (for consuming applications)
|
|
254
|
+
*/
|
|
255
|
+
setClient(client) {
|
|
256
|
+
this.client = client;
|
|
257
|
+
}
|
|
258
|
+
getMenuLinksFromModels(outputs, config) {
|
|
259
|
+
const defaultConfig = { skipModels: ['ServiceCustomer', 'ServiceProduct', 'Todo'] };
|
|
260
|
+
const finalConfig = { ...defaultConfig, ...config };
|
|
261
|
+
let pageButtons = [{ label: 'Home', route: '/' }];
|
|
262
|
+
let models = outputs.data.model_introspection.models;
|
|
263
|
+
Object.values(models).forEach((model) => {
|
|
264
|
+
if (finalConfig.skipModels.includes(model.name))
|
|
265
|
+
return;
|
|
266
|
+
console.log('model', model);
|
|
267
|
+
pageButtons.push({ label: model.name, route: '/list/' + model.name });
|
|
268
|
+
});
|
|
269
|
+
console.log('models', models);
|
|
270
|
+
return pageButtons;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* TODO: Make this generate dynamically using refModel Names and types
|
|
274
|
+
* TODO: Bring the refModel generation in here.
|
|
275
|
+
* Edit this function with form fields...
|
|
276
|
+
*
|
|
277
|
+
* ... probably don't need this...
|
|
278
|
+
*/
|
|
279
|
+
getEmptyObjectForModel(model) {
|
|
280
|
+
switch (model) {
|
|
281
|
+
case 'Customer':
|
|
282
|
+
return {
|
|
283
|
+
name: '',
|
|
284
|
+
contact: {},
|
|
285
|
+
services: [],
|
|
286
|
+
contact_preference: '',
|
|
287
|
+
notes: [],
|
|
288
|
+
};
|
|
289
|
+
case 'Service':
|
|
290
|
+
return {
|
|
291
|
+
cost: 0.00,
|
|
292
|
+
price: 0.00,
|
|
293
|
+
customers: [],
|
|
294
|
+
products: [],
|
|
295
|
+
id: '',
|
|
296
|
+
title: ''
|
|
297
|
+
};
|
|
298
|
+
default:
|
|
299
|
+
return {};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
getRelationshipChoices(config, selectedOptions) {
|
|
303
|
+
if (!this.client) {
|
|
304
|
+
console.error('AmplifyModelService: Client not initialized. Call initializeClient() first.');
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
let filter = {};
|
|
308
|
+
/*if (selectedOptions){ //TODO: FILTER OUT SELECTED ITEMS
|
|
309
|
+
filter = {id: {nin: [...selectedOptions]}};
|
|
310
|
+
}*/
|
|
311
|
+
console.log('getRelationshipChoices filter', filter);
|
|
312
|
+
try {
|
|
313
|
+
const modelName = config.partnerModelName;
|
|
314
|
+
if (this.client.models[modelName]) {
|
|
315
|
+
return this.client.models[modelName].observeQuery({ filter: filter });
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
console.error(`Model ${modelName} not found in client`);
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
console.error('error fetching ' + config.partnerModelName + 'items', error);
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
getRefChoices(config, itemId, selectedOptions) {
|
|
328
|
+
if (!this.client) {
|
|
329
|
+
console.error('AmplifyModelService: Client not initialized. Call initializeClient() first.');
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
let filter = this.getRelationshipFilter(config, itemId, selectedOptions);
|
|
333
|
+
console.log('getRefChoices getRelationshipFilter model', config.partnerModelName, 'filter', filter);
|
|
334
|
+
try {
|
|
335
|
+
const modelName = config.partnerModelName;
|
|
336
|
+
if (this.client.models[modelName]) {
|
|
337
|
+
return this.client.models[modelName].observeQuery({ filter: filter });
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
console.error(`Model ${modelName} not found in client`);
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
console.error('error fetching ' + config.partnerModelName + 'items', error);
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async deleteRelationship(relModel, relId) {
|
|
350
|
+
if (!this.client) {
|
|
351
|
+
console.error('AmplifyModelService: Client not initialized. Call initializeClient() first.');
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
let obj = { id: relId };
|
|
355
|
+
if (this.client.models[relModel]) {
|
|
356
|
+
return this.client.models[relModel].delete(obj);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
console.error(`Relationship model ${relModel} not found in client`);
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
createRelationship(relModel, relItem) {
|
|
364
|
+
if (!this.client) {
|
|
365
|
+
console.error('AmplifyModelService: Client not initialized. Call initializeClient() first.');
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
console.log('createRelationship starting for relModel', relModel, 'relItem', relItem);
|
|
369
|
+
if (this.client.models[relModel]) {
|
|
370
|
+
return this.client.models[relModel].create(relItem);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
console.error(`Relationship model ${relModel} not found in client`);
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
getRelationshipFilter(config, baseId, removeIds) {
|
|
378
|
+
let condition = {};
|
|
379
|
+
condition[config.associatedWith] = { eq: baseId };
|
|
380
|
+
if (removeIds && removeIds.length > 0) {
|
|
381
|
+
let conditionArr = [
|
|
382
|
+
condition,
|
|
383
|
+
{ id: { ne: removeIds } }
|
|
384
|
+
];
|
|
385
|
+
let andCondition = { and: conditionArr };
|
|
386
|
+
console.log('getRelationshipFilter has andCondition', andCondition);
|
|
387
|
+
return andCondition;
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
condition[config.associatedWith] = { eq: baseId };
|
|
391
|
+
console.log('getRelationshipFilter config', config, 'condition', condition);
|
|
392
|
+
return condition;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
getAmplifySelectionSet(config, baseId) {
|
|
396
|
+
switch (config.fieldName) {
|
|
397
|
+
case 'service':
|
|
398
|
+
return ['id', 'service.title', 'service.price', 'service.id'];
|
|
399
|
+
case 'customer':
|
|
400
|
+
return ['id', 'customer.*'];
|
|
401
|
+
default:
|
|
402
|
+
return [];
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
setRelatedSub(config, baseId) {
|
|
406
|
+
if (!this.client) {
|
|
407
|
+
console.error('AmplifyModelService: Client not initialized. Call initializeClient() first.');
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
let filter = this.getRelationshipFilter(config, baseId);
|
|
411
|
+
const modelName = config.relationshipModelName;
|
|
412
|
+
if (this.client.models[modelName]) {
|
|
413
|
+
return this.client.models[modelName].onCreate({ filter: filter });
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
console.error(`Relationship model ${modelName} not found in client`);
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* @param config {object} - see dynamic-relationship-builder.component getDetailsForManyToMany() for example of building config
|
|
422
|
+
*/
|
|
423
|
+
getRelatedItems(config, baseId) {
|
|
424
|
+
if (!this.client) {
|
|
425
|
+
console.error('AmplifyModelService: Client not initialized. Call initializeClient() first.');
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
let filter = this.getRelationshipFilter(config, baseId);
|
|
429
|
+
let selectionSet = this.getAmplifySelectionSet(config, baseId);
|
|
430
|
+
const modelName = config.relationshipModelName;
|
|
431
|
+
if (this.client.models[modelName]) {
|
|
432
|
+
return this.client.models[modelName].observeQuery({
|
|
433
|
+
filter: filter,
|
|
434
|
+
selectionSet: [...selectionSet]
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
console.error(`Relationship model ${modelName} not found in client`);
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
createItemPromise(model, payload) {
|
|
443
|
+
if (!this.client) {
|
|
444
|
+
console.error('AmplifyModelService: Client not initialized. Call initializeClient() first.');
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
if (this.client.models[model]) {
|
|
448
|
+
return this.client.models[model].create(payload);
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
console.error(`Model ${model} not found in client`);
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
updateItemForModel(model, payload) {
|
|
456
|
+
if (!this.client) {
|
|
457
|
+
console.error('AmplifyModelService: Client not initialized. Call initializeClient() first.');
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
if (this.client.models[model]) {
|
|
461
|
+
return this.client.models[model].update(payload);
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
console.error(`Model ${model} not found in client`);
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
getItemForModel(model, id) {
|
|
469
|
+
if (!this.client) {
|
|
470
|
+
console.error('AmplifyModelService: Client not initialized. Call initializeClient() first.');
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
if (this.client.models[model]) {
|
|
474
|
+
return this.client.models[model].get({ id: id });
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
console.error(`Model ${model} not found in client`);
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
listItemsForModel(model) {
|
|
482
|
+
if (!this.client) {
|
|
483
|
+
console.error('AmplifyModelService: Client not initialized. Call initializeClient() first.');
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
console.log('this.listItems starting');
|
|
487
|
+
try {
|
|
488
|
+
if (this.client.models[model]) {
|
|
489
|
+
return this.client.models[model].observeQuery();
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
console.error(`Model ${model} not found in client`);
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
catch (error) {
|
|
497
|
+
console.error('error fetching ' + model, error);
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AmplifyModelService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
502
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AmplifyModelService, providedIn: 'root' });
|
|
503
|
+
}
|
|
504
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AmplifyModelService, decorators: [{
|
|
505
|
+
type: Injectable,
|
|
506
|
+
args: [{
|
|
507
|
+
providedIn: 'root'
|
|
508
|
+
}]
|
|
509
|
+
}], ctorParameters: () => [] });
|
|
510
|
+
|
|
511
|
+
class DynamicFormQuestionComponent {
|
|
512
|
+
rootFormGroup;
|
|
513
|
+
ams;
|
|
514
|
+
question; //QuestionBase<string>;
|
|
515
|
+
formGroup;
|
|
516
|
+
formControlPath = '';
|
|
517
|
+
materialInputs = [];
|
|
518
|
+
switchInputs = [];
|
|
519
|
+
refInputs = [];
|
|
520
|
+
form;
|
|
521
|
+
ctrl;
|
|
522
|
+
items$;
|
|
523
|
+
afbs = inject(AmplifyFormBuilderService);
|
|
524
|
+
constructor(rootFormGroup, ams) {
|
|
525
|
+
this.rootFormGroup = rootFormGroup;
|
|
526
|
+
this.ams = ams;
|
|
527
|
+
}
|
|
528
|
+
ngOnInit() {
|
|
529
|
+
console.log('DynamicFormQuestionComponent question', this.question);
|
|
530
|
+
this.form = this.rootFormGroup.control;
|
|
531
|
+
this.ctrl = this.rootFormGroup.control.get(this.question.key);
|
|
532
|
+
if (this.question.controlType == 'async-dropdown') {
|
|
533
|
+
console.log('DynamicFormQuestionComponent has async-dropdown!');
|
|
534
|
+
//this.items$ = this.ams.getRefChoices(this.question.targetModel);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
showError() {
|
|
538
|
+
return this.afbs.getErrorStrForControl(this.ctrl, this.question.label);
|
|
539
|
+
}
|
|
540
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DynamicFormQuestionComponent, deps: [{ token: i1.FormGroupDirective }, { token: AmplifyModelService }], target: i0.ɵɵFactoryTarget.Component });
|
|
541
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DynamicFormQuestionComponent, isStandalone: true, selector: "snteam-dynamic-form-question", inputs: { question: "question", formGroup: "formGroup" }, providers: [provideNativeDateAdapter()], ngImport: i0, template: "<form [formGroup]=\"form\">\n @switch (afbs.getInputTypeFromFieldType(question.controlType)) {\n @case ('materialInputs'){\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <input matInput [formControlName]=\"question.key\" [id]=\"question.key\" [type]=\"question.type\">\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n\n @case ('datePickerInputs') {\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <input matInput [matDatepicker]=\"picker\">\n <mat-datepicker-toggle matSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n\n @case ('switchInputs') {\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <mat-select [id]=\"question.key\" [formControlName]=\"question.key\">\n @for (opt of question.options; track opt) {\n <mat-option [value]=\"opt.key\">{{opt.value | valToTitle}}</mat-option>\n }\n </mat-select>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n\n @case ('refInputs') {\n @if (question.multiple){\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <mat-select [id]=\"question.key\" [formControlName]=\"question.key\" multiple>\n @if (items$ | async; as items) {\n @for (item of items.items; track item?.id) {\n <mat-option [value]=\"item\">{{item.title | valToTitle}}</mat-option>\n }\n } @else {\n <p>Loading Items...</p>\n }\n </mat-select>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n @else {\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <mat-select [id]=\"question.key\" [formControlName]=\"question.key\">\n @if (items$ | async; as items) {\n @for (item of items.items; track item?.id) {\n <mat-option [value]=\"item\">{{item.title | valToTitle}}</mat-option>\n }\n } @else {\n <p>Loading Items...</p>\n }\n </mat-select>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n }\n }\n</form>", styles: ["form,.full-width{width:100%}\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: 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.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { 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: MatDatepickerModule }, { kind: "component", type: i6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i6.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: ValToTitlePipe, name: "valToTitle" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
542
|
+
}
|
|
543
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DynamicFormQuestionComponent, decorators: [{
|
|
544
|
+
type: Component,
|
|
545
|
+
args: [{ selector: 'snteam-dynamic-form-question', standalone: true, providers: [provideNativeDateAdapter()], imports: [CommonModule, ReactiveFormsModule, ValToTitlePipe, MatFormFieldModule, MatInputModule, MatSelectModule, MatDatepickerModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<form [formGroup]=\"form\">\n @switch (afbs.getInputTypeFromFieldType(question.controlType)) {\n @case ('materialInputs'){\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <input matInput [formControlName]=\"question.key\" [id]=\"question.key\" [type]=\"question.type\">\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n\n @case ('datePickerInputs') {\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <input matInput [matDatepicker]=\"picker\">\n <mat-datepicker-toggle matSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n\n @case ('switchInputs') {\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <mat-select [id]=\"question.key\" [formControlName]=\"question.key\">\n @for (opt of question.options; track opt) {\n <mat-option [value]=\"opt.key\">{{opt.value | valToTitle}}</mat-option>\n }\n </mat-select>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n\n @case ('refInputs') {\n @if (question.multiple){\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <mat-select [id]=\"question.key\" [formControlName]=\"question.key\" multiple>\n @if (items$ | async; as items) {\n @for (item of items.items; track item?.id) {\n <mat-option [value]=\"item\">{{item.title | valToTitle}}</mat-option>\n }\n } @else {\n <p>Loading Items...</p>\n }\n </mat-select>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n @else {\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <mat-select [id]=\"question.key\" [formControlName]=\"question.key\">\n @if (items$ | async; as items) {\n @for (item of items.items; track item?.id) {\n <mat-option [value]=\"item\">{{item.title | valToTitle}}</mat-option>\n }\n } @else {\n <p>Loading Items...</p>\n }\n </mat-select>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n }\n }\n</form>", styles: ["form,.full-width{width:100%}\n"] }]
|
|
546
|
+
}], ctorParameters: () => [{ type: i1.FormGroupDirective }, { type: AmplifyModelService }], propDecorators: { question: [{
|
|
547
|
+
type: Input
|
|
548
|
+
}], formGroup: [{
|
|
549
|
+
type: Input
|
|
550
|
+
}] } });
|
|
551
|
+
|
|
552
|
+
class DynamicNestedFormQuestionComponent {
|
|
553
|
+
rootFormGroup;
|
|
554
|
+
groupName;
|
|
555
|
+
question; //QuestionBase<string>;
|
|
556
|
+
formGroup;
|
|
557
|
+
formControlPath = '';
|
|
558
|
+
form;
|
|
559
|
+
ctrl;
|
|
560
|
+
items$;
|
|
561
|
+
afbs = inject(AmplifyFormBuilderService);
|
|
562
|
+
constructor(rootFormGroup) {
|
|
563
|
+
this.rootFormGroup = rootFormGroup;
|
|
564
|
+
}
|
|
565
|
+
ngOnInit() {
|
|
566
|
+
console.log('DynamicNestedFormQuestionComponent question', this.question);
|
|
567
|
+
var searchKey = this.groupName;
|
|
568
|
+
var controls = this.rootFormGroup.control.controls;
|
|
569
|
+
var pathStr = this.afbs.getPathFromRoot(searchKey, controls); //this.getPathFromRoot();
|
|
570
|
+
this.form = this.rootFormGroup.control;
|
|
571
|
+
this.formControlPath = pathStr + '.' + this.question.key;
|
|
572
|
+
this.ctrl = this.rootFormGroup.control.get(this.formControlPath);
|
|
573
|
+
if (this.question.key == 'services')
|
|
574
|
+
console.log('DynamicNestedFormQuestionComponent has services');
|
|
575
|
+
}
|
|
576
|
+
showError() {
|
|
577
|
+
return this.afbs.getErrorStrForControl(this.ctrl, this.question.label);
|
|
578
|
+
}
|
|
579
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DynamicNestedFormQuestionComponent, deps: [{ token: i1.FormGroupDirective }], target: i0.ɵɵFactoryTarget.Component });
|
|
580
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DynamicNestedFormQuestionComponent, isStandalone: true, selector: "snteam-dynamic-nested-form-question", inputs: { groupName: "groupName", question: "question", formGroup: "formGroup" }, providers: [provideNativeDateAdapter()], ngImport: i0, template: "<form autocomplete=\"off\" [formGroup]=\"formGroup\">\n <div [formGroupName]=\"groupName\">\n\n @switch (afbs.getInputTypeFromFieldType(question.controlType)) {\n @case ('materialInputs'){\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <input matInput [formControlName]=\"question.key\" [id]=\"question.key\" [type]=\"question.type\">\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n\n @case ('datePickerInputs') {\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <input matInput [matDatepicker]=\"picker\">\n <mat-datepicker-toggle matSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n\n @case ('switchInputs') {\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <mat-select [id]=\"question.key\" [formControlName]=\"question.key\">\n @for (opt of question.options; track opt) {\n <mat-option [value]=\"opt.key\">{{opt.value | valToTitle}}</mat-option>\n }\n </mat-select>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n\n @case ('refInputs') {\n @if (question.multiple){\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <mat-select [id]=\"question.key\" [formControlName]=\"question.key\" multiple>\n @if (items$ | async; as items) {\n @for (item of items.items; track item?.id) {\n <mat-option [value]=\"item\">{{item.title | valToTitle}}</mat-option>\n }\n } @else {\n <p>Loading Items...</p>\n }\n </mat-select>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n @else {\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <mat-select [id]=\"question.key\" [formControlName]=\"question.key\">\n @if (items$ | async; as items) {\n @for (item of items.items; track item?.id) {\n <mat-option [value]=\"item\">{{item.title | valToTitle}}</mat-option>\n }\n } @else {\n <p>Loading Items...</p>\n }\n </mat-select>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n }\n }\n\n\n </div>\n</form>", styles: [".full-width{width:100%}\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: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { 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.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { 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: MatDatepickerModule }, { kind: "component", type: i6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i6.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: ValToTitlePipe, name: "valToTitle" }] });
|
|
581
|
+
}
|
|
582
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DynamicNestedFormQuestionComponent, decorators: [{
|
|
583
|
+
type: Component,
|
|
584
|
+
args: [{ selector: 'snteam-dynamic-nested-form-question', standalone: true, providers: [provideNativeDateAdapter()], imports: [CommonModule, ReactiveFormsModule, ValToTitlePipe, MatFormFieldModule, MatInputModule, MatSelectModule, MatDatepickerModule], template: "<form autocomplete=\"off\" [formGroup]=\"formGroup\">\n <div [formGroupName]=\"groupName\">\n\n @switch (afbs.getInputTypeFromFieldType(question.controlType)) {\n @case ('materialInputs'){\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <input matInput [formControlName]=\"question.key\" [id]=\"question.key\" [type]=\"question.type\">\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n\n @case ('datePickerInputs') {\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <input matInput [matDatepicker]=\"picker\">\n <mat-datepicker-toggle matSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n\n @case ('switchInputs') {\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <mat-select [id]=\"question.key\" [formControlName]=\"question.key\">\n @for (opt of question.options; track opt) {\n <mat-option [value]=\"opt.key\">{{opt.value | valToTitle}}</mat-option>\n }\n </mat-select>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n\n @case ('refInputs') {\n @if (question.multiple){\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <mat-select [id]=\"question.key\" [formControlName]=\"question.key\" multiple>\n @if (items$ | async; as items) {\n @for (item of items.items; track item?.id) {\n <mat-option [value]=\"item\">{{item.title | valToTitle}}</mat-option>\n }\n } @else {\n <p>Loading Items...</p>\n }\n </mat-select>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n @else {\n <mat-form-field class=\"full-width\" appearance=\"outline\">\n <mat-label [attr.for]=\"question.key\">{{ question.label }}</mat-label>\n <mat-select [id]=\"question.key\" [formControlName]=\"question.key\">\n @if (items$ | async; as items) {\n @for (item of items.items; track item?.id) {\n <mat-option [value]=\"item\">{{item.title | valToTitle}}</mat-option>\n }\n } @else {\n <p>Loading Items...</p>\n }\n </mat-select>\n @if (ctrl.errors) {\n <mat-error>{{showError()}}</mat-error>\n }\n </mat-form-field>\n }\n }\n }\n\n\n </div>\n</form>", styles: [".full-width{width:100%}\n"] }]
|
|
585
|
+
}], ctorParameters: () => [{ type: i1.FormGroupDirective }], propDecorators: { groupName: [{
|
|
586
|
+
type: Input
|
|
587
|
+
}], question: [{
|
|
588
|
+
type: Input
|
|
589
|
+
}], formGroup: [{
|
|
590
|
+
type: Input
|
|
591
|
+
}] } });
|
|
592
|
+
|
|
593
|
+
class DynamicFormGroupComponent {
|
|
594
|
+
rootFormGroup;
|
|
595
|
+
afbs;
|
|
596
|
+
formGroupName;
|
|
597
|
+
question; //QuestionBase<string>;
|
|
598
|
+
formGroup;
|
|
599
|
+
materialInputs = [];
|
|
600
|
+
switchInputs = [];
|
|
601
|
+
refInputs = [];
|
|
602
|
+
formGroupInputs = ['formgroup'];
|
|
603
|
+
form;
|
|
604
|
+
ctrl;
|
|
605
|
+
constructor(rootFormGroup, afbs) {
|
|
606
|
+
this.rootFormGroup = rootFormGroup;
|
|
607
|
+
this.afbs = afbs;
|
|
608
|
+
}
|
|
609
|
+
getControlName(c) {
|
|
610
|
+
if (c.parent) {
|
|
611
|
+
const formGroup = c.parent.controls;
|
|
612
|
+
return Object.keys(formGroup).find(name => c === formGroup[name]) || null;
|
|
613
|
+
}
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
getPathFromRoot(controls, pathArr) {
|
|
617
|
+
if (!controls)
|
|
618
|
+
controls = this.rootFormGroup.control.controls;
|
|
619
|
+
if (!pathArr)
|
|
620
|
+
pathArr = [];
|
|
621
|
+
var isInControls = Object.keys(controls).find(name => name === this.question.key);
|
|
622
|
+
if (isInControls) {
|
|
623
|
+
pathArr.push(this.question.key);
|
|
624
|
+
return pathArr.join('.');
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
var tempArr = [];
|
|
628
|
+
var entries = Object.entries(controls);
|
|
629
|
+
for (let e of entries) {
|
|
630
|
+
let controlName = e[0];
|
|
631
|
+
let controlObj = e[1];
|
|
632
|
+
if (controlObj instanceof FormGroup) {
|
|
633
|
+
//if (tempArr.length > 10) return '';
|
|
634
|
+
tempArr.push(controlName);
|
|
635
|
+
var childControls = controls[controlName].controls;
|
|
636
|
+
let childPath = this.getPathFromRoot(childControls, tempArr);
|
|
637
|
+
if (childPath != '')
|
|
638
|
+
return childPath;
|
|
639
|
+
}
|
|
640
|
+
;
|
|
641
|
+
}
|
|
642
|
+
return '';
|
|
643
|
+
}
|
|
644
|
+
return '';
|
|
645
|
+
}
|
|
646
|
+
ngOnInit() {
|
|
647
|
+
//console.log('DynamicFormGroupComponent key starting:', this.question.key);
|
|
648
|
+
this.materialInputs = this.afbs.getMaterialInputs();
|
|
649
|
+
this.switchInputs = this.afbs.getSwitchInputs();
|
|
650
|
+
this.refInputs = this.afbs.getRefInputs();
|
|
651
|
+
if (this.formGroupName) {
|
|
652
|
+
var searchKey = this.question.key;
|
|
653
|
+
var controls = this.rootFormGroup.control.controls;
|
|
654
|
+
var pathStr = this.afbs.getPathFromRoot(searchKey, controls); //this.getPathFromRoot();
|
|
655
|
+
this.form = this.rootFormGroup.control.get(pathStr);
|
|
656
|
+
var controlName = this.getControlName(this.form);
|
|
657
|
+
this.ctrl = this.rootFormGroup.control.get(this.question.key);
|
|
658
|
+
console.log('DynamicFormGroupComponent init vals:', {
|
|
659
|
+
formGroupName: this.formGroupName,
|
|
660
|
+
question: this.question,
|
|
661
|
+
key: this.question.key,
|
|
662
|
+
searchKey: searchKey,
|
|
663
|
+
controls: controls,
|
|
664
|
+
pathStr: pathStr,
|
|
665
|
+
controlName: controlName,
|
|
666
|
+
ctrl: this.ctrl
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
//console.log('DynamicFormGroupComponent will get main control form');
|
|
671
|
+
this.form = this.rootFormGroup.control;
|
|
672
|
+
}
|
|
673
|
+
//console.log('DynamicFormGroupComponent question', this.question, 'form', this.form, 'formGroupName', typeof this.formGroupName, this.formGroupName);
|
|
674
|
+
}
|
|
675
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DynamicFormGroupComponent, deps: [{ token: i1.FormGroupDirective }, { token: AmplifyFormBuilderService }], target: i0.ɵɵFactoryTarget.Component });
|
|
676
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DynamicFormGroupComponent, isStandalone: true, selector: "snteam-dynamic-form-group", inputs: { formGroupName: "formGroupName", question: "question", formGroup: "formGroup" }, ngImport: i0, template: "<form autocomplete=\"off\" [formGroup]=\"form\">\n <h2>{{question.key | valToTitle}}</h2>\n <mat-divider></mat-divider>\n @for (q of $any(question).questions; track q) {\n @if (q.controlType != 'formgroup') {\n <div class=\"form-row\">\n <snteam-dynamic-nested-form-question [question]=\"q\" [formGroup]=\"formGroup\" [groupName]=\"question.key\"></snteam-dynamic-nested-form-question>\n </div>\n }\n @if (q.controlType == 'formgroup') {\n <div class=\"group-row\">\n <snteam-dynamic-form-group [question]=\"q\" [formGroup]=\"form\" [formGroupName]=\"q.key\"></snteam-dynamic-form-group>\n </div>\n }\n }\n <mat-divider></mat-divider>\n</form>", styles: [".form-row{margin-top:20px}\n"], dependencies: [{ kind: "component", type: DynamicFormGroupComponent, selector: "snteam-dynamic-form-group", inputs: ["formGroupName", "question", "formGroup"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i3$2.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "component", type: DynamicNestedFormQuestionComponent, selector: "snteam-dynamic-nested-form-question", inputs: ["groupName", "question", "formGroup"] }, { kind: "pipe", type: ValToTitlePipe, name: "valToTitle" }] });
|
|
677
|
+
}
|
|
678
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DynamicFormGroupComponent, decorators: [{
|
|
679
|
+
type: Component,
|
|
680
|
+
args: [{ selector: 'snteam-dynamic-form-group', standalone: true, imports: [CommonModule, ReactiveFormsModule,
|
|
681
|
+
MatDividerModule,
|
|
682
|
+
ValToTitlePipe, DynamicNestedFormQuestionComponent], template: "<form autocomplete=\"off\" [formGroup]=\"form\">\n <h2>{{question.key | valToTitle}}</h2>\n <mat-divider></mat-divider>\n @for (q of $any(question).questions; track q) {\n @if (q.controlType != 'formgroup') {\n <div class=\"form-row\">\n <snteam-dynamic-nested-form-question [question]=\"q\" [formGroup]=\"formGroup\" [groupName]=\"question.key\"></snteam-dynamic-nested-form-question>\n </div>\n }\n @if (q.controlType == 'formgroup') {\n <div class=\"group-row\">\n <snteam-dynamic-form-group [question]=\"q\" [formGroup]=\"form\" [formGroupName]=\"q.key\"></snteam-dynamic-form-group>\n </div>\n }\n }\n <mat-divider></mat-divider>\n</form>", styles: [".form-row{margin-top:20px}\n"] }]
|
|
683
|
+
}], ctorParameters: () => [{ type: i1.FormGroupDirective }, { type: AmplifyFormBuilderService }], propDecorators: { formGroupName: [{
|
|
684
|
+
type: Input
|
|
685
|
+
}], question: [{
|
|
686
|
+
type: Input
|
|
687
|
+
}], formGroup: [{
|
|
688
|
+
type: Input
|
|
689
|
+
}] } });
|
|
690
|
+
|
|
691
|
+
class AddRelationshipDialogComponent {
|
|
692
|
+
dialogRef = inject((MatDialogRef));
|
|
693
|
+
data = inject(MAT_DIALOG_DATA);
|
|
694
|
+
modelItem = model(this.data.modelItem, ...(ngDevMode ? [{ debugName: "modelItem" }] : []));
|
|
695
|
+
ams = inject(AmplifyModelService);
|
|
696
|
+
items$;
|
|
697
|
+
ngOnInit() {
|
|
698
|
+
console.log('AddRelationshipDialogComponent init this.data', this.data);
|
|
699
|
+
let selectedIdsArr = [];
|
|
700
|
+
if (this.data.selectedItems && this.data.selectedItems.length > 0) {
|
|
701
|
+
this.data.selectedItems.forEach(item => {
|
|
702
|
+
console.log('item', item, 'this.data.rel', this.data.rel);
|
|
703
|
+
selectedIdsArr.push(item[this.data.rel.fieldName].id);
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
console.log('AddRelationshipDialogComponent selectedIdsArr', selectedIdsArr);
|
|
707
|
+
const relationshipChoices = this.ams.getRelationshipChoices(this.data.rel, selectedIdsArr);
|
|
708
|
+
if (relationshipChoices) {
|
|
709
|
+
this.items$ = relationshipChoices;
|
|
710
|
+
}
|
|
711
|
+
console.log('AddRelationshipDialogComponent this.items$', this.items$);
|
|
712
|
+
}
|
|
713
|
+
onNoClick() {
|
|
714
|
+
this.dialogRef.close();
|
|
715
|
+
}
|
|
716
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AddRelationshipDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
717
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: AddRelationshipDialogComponent, isStandalone: true, selector: "snteam-add-relationship-dialog", inputs: { modelItem: { classPropertyName: "modelItem", publicName: "modelItem", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { modelItem: "modelItemChange" }, ngImport: i0, template: "<h2 mat-dialog-title>Add New {{data.rel.partnerModelName}}</h2>\n<mat-dialog-content>\n <mat-select [(ngModel)]=\"modelItem\">\n @if (items$ | async; as items) {\n @for (item of items.items; track item?.id) {\n <mat-option [value]=\"item\">{{item.title}}</mat-option>\n }\n } @else {\n <p>Loading Items...</p>\n }\n </mat-select>\n</mat-dialog-content>\n<mat-dialog-actions>\n <button mat-button (click)=\"onNoClick()\">Cancel</button>\n <button mat-button [mat-dialog-close]=\"modelItem()\" cdkFocusInitial>Add</button>\n</mat-dialog-actions>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: 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: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }] });
|
|
718
|
+
}
|
|
719
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AddRelationshipDialogComponent, decorators: [{
|
|
720
|
+
type: Component,
|
|
721
|
+
args: [{ selector: 'snteam-add-relationship-dialog', standalone: true, imports: [
|
|
722
|
+
CommonModule,
|
|
723
|
+
MatFormFieldModule,
|
|
724
|
+
MatInputModule,
|
|
725
|
+
FormsModule,
|
|
726
|
+
MatSelectModule,
|
|
727
|
+
MatButtonModule,
|
|
728
|
+
MatDialogTitle,
|
|
729
|
+
MatDialogContent,
|
|
730
|
+
MatDialogActions,
|
|
731
|
+
MatDialogClose,
|
|
732
|
+
], template: "<h2 mat-dialog-title>Add New {{data.rel.partnerModelName}}</h2>\n<mat-dialog-content>\n <mat-select [(ngModel)]=\"modelItem\">\n @if (items$ | async; as items) {\n @for (item of items.items; track item?.id) {\n <mat-option [value]=\"item\">{{item.title}}</mat-option>\n }\n } @else {\n <p>Loading Items...</p>\n }\n </mat-select>\n</mat-dialog-content>\n<mat-dialog-actions>\n <button mat-button (click)=\"onNoClick()\">Cancel</button>\n <button mat-button [mat-dialog-close]=\"modelItem()\" cdkFocusInitial>Add</button>\n</mat-dialog-actions>" }]
|
|
733
|
+
}], propDecorators: { modelItem: [{ type: i0.Input, args: [{ isSignal: true, alias: "modelItem", required: false }] }, { type: i0.Output, args: ["modelItemChange"] }] } });
|
|
734
|
+
|
|
735
|
+
// Note: These imports will need to be updated based on the actual Amplify setup in the consuming application
|
|
736
|
+
// import type { Schema } from '../../../../amplify/data/resource';
|
|
737
|
+
// import outputs from '../../../../amplify_outputs.json';
|
|
738
|
+
class DynamicRelationshipBuilderComponent {
|
|
739
|
+
ams;
|
|
740
|
+
m2m;
|
|
741
|
+
item;
|
|
742
|
+
model;
|
|
743
|
+
m2mItems;
|
|
744
|
+
constructor(ams) {
|
|
745
|
+
this.ams = ams;
|
|
746
|
+
}
|
|
747
|
+
modelItem; // = signal('');
|
|
748
|
+
dialog = inject(MatDialog);
|
|
749
|
+
ngOnInit() {
|
|
750
|
+
console.log('DynamicRelationshipBuilderComponent model', this.m2m);
|
|
751
|
+
this.listItems();
|
|
752
|
+
this.setCreateSubscription();
|
|
753
|
+
}
|
|
754
|
+
async perhapsAddM2MItem(item) {
|
|
755
|
+
let m2mItem = { id: item.id };
|
|
756
|
+
const { data: retItem } = await item[this.m2m.fieldName]();
|
|
757
|
+
m2mItem[this.m2m.fieldName] = retItem;
|
|
758
|
+
let existingInd = this.m2mItems.findIndex((x) => x.id == item.id);
|
|
759
|
+
if (existingInd > -1) {
|
|
760
|
+
if (!this.m2mItems[existingInd][this.m2m.fieldName]) {
|
|
761
|
+
console.log('would delete existing', this.m2mItems[existingInd]);
|
|
762
|
+
this.m2mItems.splice(existingInd, 1);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
this.m2mItems.push(m2mItem);
|
|
766
|
+
//console.log('perhapsAddM2MItem', item, 'm2m', this.m2m);
|
|
767
|
+
}
|
|
768
|
+
setCreateSubscription() {
|
|
769
|
+
this.ams.setRelatedSub(this.m2m, this.item.id)?.subscribe({
|
|
770
|
+
next: (data) => this.perhapsAddM2MItem(data), //(console.log('setRelatedSub m2mItems', this.m2mItems, 'data', data),
|
|
771
|
+
error: (error) => console.warn(error),
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
listItems() {
|
|
775
|
+
this.ams.getRelatedItems(this.m2m, this.item.id)?.subscribe({
|
|
776
|
+
next: ({ items, isSynced }) => {
|
|
777
|
+
this.m2mItems = items;
|
|
778
|
+
},
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
Customize this function to include all desired models from your file
|
|
783
|
+
*/
|
|
784
|
+
setModelItem(model) {
|
|
785
|
+
let mdl = model || this.model;
|
|
786
|
+
console.log('setModelItem mdl', mdl);
|
|
787
|
+
this.modelItem = signal(this.ams.getEmptyObjectForModel(mdl), ...(ngDevMode ? [{ debugName: "modelItem" }] : []));
|
|
788
|
+
}
|
|
789
|
+
async deleteRelatedItem(item) {
|
|
790
|
+
console.log('deleteRelatedItem for item', item, 'm2m', this.m2m);
|
|
791
|
+
//this.ams.deleteRelationship(this.m2m.relationshipModelName, item.id);
|
|
792
|
+
await this.ams.deleteRelationship(this.m2m.relationshipModelName, item.id);
|
|
793
|
+
}
|
|
794
|
+
openDialog(rel) {
|
|
795
|
+
console.log('openDialog clicked for rel', rel);
|
|
796
|
+
this.setModelItem(rel.partnerModelName);
|
|
797
|
+
const dialogRef = this.dialog.open(AddRelationshipDialogComponent, {
|
|
798
|
+
data: { rel: rel, modelItem: this.modelItem(), selectedItems: this.m2mItems },
|
|
799
|
+
});
|
|
800
|
+
dialogRef.afterClosed().subscribe(result => {
|
|
801
|
+
if (result)
|
|
802
|
+
this.handleDialogResult(result, rel);
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
handleDialogResult(result, rel) {
|
|
806
|
+
if (!result)
|
|
807
|
+
return;
|
|
808
|
+
console.log('handleDialogResult', result, 'rel', rel);
|
|
809
|
+
if (result !== undefined) {
|
|
810
|
+
this.modelItem.set(result);
|
|
811
|
+
}
|
|
812
|
+
let relIdName = rel.partnerModelName.toLowerCase() + 'Id';
|
|
813
|
+
let thisIdName = rel.baseModelName.toLowerCase() + 'Id';
|
|
814
|
+
let newRelObj = {};
|
|
815
|
+
newRelObj[relIdName] = result.id;
|
|
816
|
+
newRelObj[thisIdName] = this.item.id;
|
|
817
|
+
this.ams.createRelationship(rel.relationshipModelName, newRelObj);
|
|
818
|
+
}
|
|
819
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DynamicRelationshipBuilderComponent, deps: [{ token: AmplifyModelService }], target: i0.ɵɵFactoryTarget.Component });
|
|
820
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DynamicRelationshipBuilderComponent, isStandalone: true, selector: "snteam-dynamic-relationship-builder", inputs: { m2m: "m2m", item: "item", model: "model" }, ngImport: i0, template: "<div class=\"record-relationship-holder\">\n <div class=\"relationship-type\">\n <mat-toolbar>\n <span>{{m2m.partnerModelName}} Management</span>\n <span class=\"rel-title-spacer\"></span>\n <button mat-icon-button attr.aria-label=\"Add {{m2m.name}}\" (click)=\"openDialog(m2m)\">\n <mat-icon>add</mat-icon>\n </button>\n </mat-toolbar>\n @if(m2mItems && m2mItems.length > 0){\n <mat-list>\n @for (item of m2mItems; track item?.id) {\n @if(item[m2m.fieldName]){\n <mat-list-item>\n <span matListItemTitle>{{item[m2m.fieldName].name || item[m2m.fieldName].title}}</span>\n <button mat-icon-button (click)=\"deleteRelatedItem(item)\" matListItemMeta>\n <mat-icon>delete</mat-icon>\n </button>\n </mat-list-item>\n }\n }\n </mat-list>\n }\n @else {\n Add your first {{m2m.partnerModelName}}\n }\n </div>\n</div>", styles: [".relationship-type{margin-top:20px}.rel-title-spacer{flex:1 1 auto}mat-toolbar button{background:transparent;color:#000}\n"], dependencies: [{ kind: "ngmodule", type: MatToolbarModule }, { kind: "component", type: i2.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "ngmodule", type: MatButtonModule }, { 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: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i5$1.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i5$1.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "directive", type: i5$1.MatListItemTitle, selector: "[matListItemTitle]" }, { kind: "directive", type: i5$1.MatListItemMeta, selector: "[matListItemMeta]" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: CommonModule }] });
|
|
821
|
+
}
|
|
822
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DynamicRelationshipBuilderComponent, decorators: [{
|
|
823
|
+
type: Component,
|
|
824
|
+
args: [{ selector: 'snteam-dynamic-relationship-builder', standalone: true, imports: [MatToolbarModule, MatButtonModule, MatIconModule, MatFormFieldModule, MatInputModule, MatListModule,
|
|
825
|
+
FormsModule,
|
|
826
|
+
CommonModule], template: "<div class=\"record-relationship-holder\">\n <div class=\"relationship-type\">\n <mat-toolbar>\n <span>{{m2m.partnerModelName}} Management</span>\n <span class=\"rel-title-spacer\"></span>\n <button mat-icon-button attr.aria-label=\"Add {{m2m.name}}\" (click)=\"openDialog(m2m)\">\n <mat-icon>add</mat-icon>\n </button>\n </mat-toolbar>\n @if(m2mItems && m2mItems.length > 0){\n <mat-list>\n @for (item of m2mItems; track item?.id) {\n @if(item[m2m.fieldName]){\n <mat-list-item>\n <span matListItemTitle>{{item[m2m.fieldName].name || item[m2m.fieldName].title}}</span>\n <button mat-icon-button (click)=\"deleteRelatedItem(item)\" matListItemMeta>\n <mat-icon>delete</mat-icon>\n </button>\n </mat-list-item>\n }\n }\n </mat-list>\n }\n @else {\n Add your first {{m2m.partnerModelName}}\n }\n </div>\n</div>", styles: [".relationship-type{margin-top:20px}.rel-title-spacer{flex:1 1 auto}mat-toolbar button{background:transparent;color:#000}\n"] }]
|
|
827
|
+
}], ctorParameters: () => [{ type: AmplifyModelService }], propDecorators: { m2m: [{
|
|
828
|
+
type: Input
|
|
829
|
+
}], item: [{
|
|
830
|
+
type: Input
|
|
831
|
+
}], model: [{
|
|
832
|
+
type: Input
|
|
833
|
+
}] } });
|
|
834
|
+
|
|
835
|
+
// Note: These imports will need to be updated based on the actual Amplify setup in the consuming application
|
|
836
|
+
// import type { Schema } from '../../../../amplify/data/resource';
|
|
837
|
+
// import outputs from '../../../../amplify_outputs.json';
|
|
838
|
+
class RecordRelationshipsComponent {
|
|
839
|
+
ams;
|
|
840
|
+
model = '';
|
|
841
|
+
id;
|
|
842
|
+
item;
|
|
843
|
+
amplifyOutputs; // Allow injection of amplify outputs
|
|
844
|
+
constructor(ams) {
|
|
845
|
+
this.ams = ams;
|
|
846
|
+
}
|
|
847
|
+
m2mDetailArrays = [];
|
|
848
|
+
//readonly animal = signal('');
|
|
849
|
+
modelItem; // = signal('');
|
|
850
|
+
dialog = inject(MatDialog);
|
|
851
|
+
ngOnInit() {
|
|
852
|
+
console.log('DynamicRelationshipBuilderComponent model', this.model, 'id', this.id, 'item', this.item);
|
|
853
|
+
//this.setModelItem();
|
|
854
|
+
if (!this.amplifyOutputs) {
|
|
855
|
+
console.warn('RecordRelationshipsComponent: amplifyOutputs not provided. Component may not function correctly.');
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
console.log('DynamicRelationshipBuilderComponent outputs.data.model_introspection.models', this.amplifyOutputs.data.model_introspection.models);
|
|
859
|
+
var fields = this.amplifyOutputs.data.model_introspection.models[this.model].fields;
|
|
860
|
+
var values = Object.values(fields);
|
|
861
|
+
for (var v in values) {
|
|
862
|
+
var fieldObj = values[v];
|
|
863
|
+
if (typeof fieldObj.type != 'object')
|
|
864
|
+
continue;
|
|
865
|
+
if (!fieldObj.type.model)
|
|
866
|
+
continue;
|
|
867
|
+
let m2mDetails = this.getDetailsForManyToMany(fieldObj, this.model);
|
|
868
|
+
if (m2mDetails) {
|
|
869
|
+
this.m2mDetailArrays.push(m2mDetails);
|
|
870
|
+
}
|
|
871
|
+
console.log('DynamicRelationshipBuilderComponent ref field', fieldObj);
|
|
872
|
+
console.log('DynamicRelationshipBuilderComponent m2mDetails', m2mDetails);
|
|
873
|
+
}
|
|
874
|
+
console.log('DynamicRelationshipBuilderComponent m2mDetailArrays', this.m2mDetailArrays);
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
Customize this function to include all desired models from your file
|
|
878
|
+
*/
|
|
879
|
+
setModelItem(model) {
|
|
880
|
+
let mdl = model || this.model;
|
|
881
|
+
console.log('setModelItem mdl', mdl);
|
|
882
|
+
signal(this.ams.getEmptyObjectForModel(mdl));
|
|
883
|
+
}
|
|
884
|
+
getFieldNameForAssociatedWithModel(relationshipModelName, associatedWith) {
|
|
885
|
+
if (!this.amplifyOutputs)
|
|
886
|
+
return;
|
|
887
|
+
var relationshipModel = this.amplifyOutputs.data.model_introspection.models[relationshipModelName].fields;
|
|
888
|
+
console.log('getFieldNameForAssociatedWithModel relationshipModel', relationshipModel);
|
|
889
|
+
var fieldVals = Object.values(relationshipModel);
|
|
890
|
+
for (let field of fieldVals) {
|
|
891
|
+
const fieldAny = field;
|
|
892
|
+
console.log('getFieldNameForAssociatedWithModel field', field);
|
|
893
|
+
if (!fieldAny.association)
|
|
894
|
+
continue;
|
|
895
|
+
if (fieldAny.association.targetNames.indexOf(associatedWith) > -1)
|
|
896
|
+
continue;
|
|
897
|
+
// there should only be two fields with associations.
|
|
898
|
+
// if we get here we are in the field we want
|
|
899
|
+
return fieldAny.name;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
getDetailsForManyToMany(fieldObj, baseModelName) {
|
|
903
|
+
if (!this.amplifyOutputs)
|
|
904
|
+
return null;
|
|
905
|
+
let relationshipModelName = fieldObj.type.model;
|
|
906
|
+
let associatedWith = fieldObj.association.associatedWith[0]; //TODO this may not work for more complex Schemas!!!
|
|
907
|
+
console.log('getDetailsForManyToMany relationshipModelName', relationshipModelName, 'baseModelName', baseModelName);
|
|
908
|
+
this.getFieldNameForAssociatedWithModel(relationshipModelName, associatedWith);
|
|
909
|
+
// TODO: MAKE THIS AUTO REFRESH... maybe make this a signal?
|
|
910
|
+
let ret = {
|
|
911
|
+
relationshipModelName: relationshipModelName,
|
|
912
|
+
baseModelName: baseModelName,
|
|
913
|
+
fieldName: this.getFieldNameForAssociatedWithModel(relationshipModelName, associatedWith),
|
|
914
|
+
associatedWith: associatedWith
|
|
915
|
+
};
|
|
916
|
+
var relationshipModel = this.amplifyOutputs.data.model_introspection.models[relationshipModelName].fields;
|
|
917
|
+
var vals = Object.values(relationshipModel);
|
|
918
|
+
for (var i in vals) {
|
|
919
|
+
const val = vals[i];
|
|
920
|
+
if (typeof val.type != 'object')
|
|
921
|
+
continue;
|
|
922
|
+
if (!val.type.model)
|
|
923
|
+
continue;
|
|
924
|
+
if (val.type.model == baseModelName)
|
|
925
|
+
continue;
|
|
926
|
+
console.log('vals[i]', vals[i]);
|
|
927
|
+
var modelName = val.type.model;
|
|
928
|
+
let refModel = this.amplifyOutputs.data.model_introspection.models[modelName].fields;
|
|
929
|
+
ret.partnerModelName = modelName;
|
|
930
|
+
//this.ams.getRelatedItems(ret, this.item.id);
|
|
931
|
+
}
|
|
932
|
+
console.log('getDetailsForManyToMany ret', ret);
|
|
933
|
+
return ret;
|
|
934
|
+
}
|
|
935
|
+
deleteRelatedItem(item) {
|
|
936
|
+
console.log('deleteRelatedItem for item', item);
|
|
937
|
+
}
|
|
938
|
+
openDialog(rel) {
|
|
939
|
+
console.log('openDialog clicked for rel', rel);
|
|
940
|
+
this.setModelItem(rel.partnerModelName);
|
|
941
|
+
const dialogRef = this.dialog.open(AddRelationshipDialogComponent, {
|
|
942
|
+
data: { rel: rel, modelItem: this.modelItem() },
|
|
943
|
+
});
|
|
944
|
+
dialogRef.afterClosed().subscribe(result => {
|
|
945
|
+
console.log('The dialog was closed');
|
|
946
|
+
this.handleDialogResult(result, rel);
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
handleDialogResult(result, rel) {
|
|
950
|
+
console.log('handleDialogResult', result, 'rel', rel);
|
|
951
|
+
if (result !== undefined) {
|
|
952
|
+
this.modelItem.set(result);
|
|
953
|
+
}
|
|
954
|
+
let relIdName = rel.partnerModelName.toLowerCase() + 'Id';
|
|
955
|
+
let thisIdName = rel.baseModelName.toLowerCase() + 'Id';
|
|
956
|
+
let newRelObj = {};
|
|
957
|
+
newRelObj[relIdName] = result.id;
|
|
958
|
+
newRelObj[thisIdName] = this.id || this.item.id;
|
|
959
|
+
this.ams.createRelationship(rel.relationshipModelName, newRelObj);
|
|
960
|
+
}
|
|
961
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: RecordRelationshipsComponent, deps: [{ token: AmplifyModelService }], target: i0.ɵɵFactoryTarget.Component });
|
|
962
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: RecordRelationshipsComponent, isStandalone: true, selector: "snteam-record-relationships", inputs: { model: "model", id: "id", item: "item", amplifyOutputs: "amplifyOutputs" }, ngImport: i0, template: "<div class=\"relationship-builder-holder\">\n @if(item){\n <div class=\"relationship-builder\">\n @for (m2m of m2mDetailArrays; track m2m) {\n <snteam-dynamic-relationship-builder [m2m]=\"m2m\" [item]=\"item\" [model]=\"model\"></snteam-dynamic-relationship-builder>\n }\n </div>\n }\n @else {\n <div class=\"blank-relationship-builder\">Create the {{model}} first, then add relationships</div>\n }\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: MatToolbarModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: DynamicRelationshipBuilderComponent, selector: "snteam-dynamic-relationship-builder", inputs: ["m2m", "item", "model"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: CommonModule }] });
|
|
963
|
+
}
|
|
964
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: RecordRelationshipsComponent, decorators: [{
|
|
965
|
+
type: Component,
|
|
966
|
+
args: [{ selector: 'snteam-record-relationships', standalone: true, imports: [MatToolbarModule, MatButtonModule, MatIconModule, MatFormFieldModule, MatInputModule, MatListModule,
|
|
967
|
+
DynamicRelationshipBuilderComponent,
|
|
968
|
+
FormsModule,
|
|
969
|
+
CommonModule], template: "<div class=\"relationship-builder-holder\">\n @if(item){\n <div class=\"relationship-builder\">\n @for (m2m of m2mDetailArrays; track m2m) {\n <snteam-dynamic-relationship-builder [m2m]=\"m2m\" [item]=\"item\" [model]=\"model\"></snteam-dynamic-relationship-builder>\n }\n </div>\n }\n @else {\n <div class=\"blank-relationship-builder\">Create the {{model}} first, then add relationships</div>\n }\n</div>" }]
|
|
970
|
+
}], ctorParameters: () => [{ type: AmplifyModelService }], propDecorators: { model: [{
|
|
971
|
+
type: Input
|
|
972
|
+
}], id: [{
|
|
973
|
+
type: Input
|
|
974
|
+
}], item: [{
|
|
975
|
+
type: Input
|
|
976
|
+
}], amplifyOutputs: [{
|
|
977
|
+
type: Input
|
|
978
|
+
}] } });
|
|
979
|
+
|
|
980
|
+
class QuestionBase {
|
|
981
|
+
model;
|
|
982
|
+
value;
|
|
983
|
+
key;
|
|
984
|
+
label;
|
|
985
|
+
required;
|
|
986
|
+
order;
|
|
987
|
+
controlType;
|
|
988
|
+
type;
|
|
989
|
+
options;
|
|
990
|
+
tooltip;
|
|
991
|
+
validators;
|
|
992
|
+
constructor(options = {}) {
|
|
993
|
+
this.model = options.model || '';
|
|
994
|
+
this.value = options.value;
|
|
995
|
+
this.key = options.key || '';
|
|
996
|
+
this.label = options.label || '';
|
|
997
|
+
this.required = !!options.required;
|
|
998
|
+
this.order = options.order === undefined ? 1 : options.order;
|
|
999
|
+
this.controlType = options.controlType || '';
|
|
1000
|
+
this.type = options.type || '';
|
|
1001
|
+
this.options = options.options || [];
|
|
1002
|
+
this.tooltip = options.tooltip || '';
|
|
1003
|
+
this.validators = options.validators;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
class TimePickerQuestion extends QuestionBase {
|
|
1007
|
+
controlType = 'time';
|
|
1008
|
+
constructor(options = {}) {
|
|
1009
|
+
super(options);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
class DatePickerQuestion extends QuestionBase {
|
|
1013
|
+
controlType = 'date';
|
|
1014
|
+
min;
|
|
1015
|
+
max;
|
|
1016
|
+
startDate;
|
|
1017
|
+
constructor(options = {}) {
|
|
1018
|
+
super(options);
|
|
1019
|
+
this.min = new Date(options['min']) ?? new Date('January 01, 1900 00:00:00');
|
|
1020
|
+
var thisYear = new Date().getUTCFullYear;
|
|
1021
|
+
this.max = new Date(options['max']) ?? new Date('December 31 ' + thisYear + ' 23:59:59');
|
|
1022
|
+
this.startDate = new Date(options['startDate']) ?? new Date();
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
class DateTimePickerQuestion extends QuestionBase {
|
|
1026
|
+
controlType = 'date-time';
|
|
1027
|
+
min;
|
|
1028
|
+
max;
|
|
1029
|
+
startDate;
|
|
1030
|
+
constructor(options = {}) {
|
|
1031
|
+
super(options);
|
|
1032
|
+
this.min = new Date(options['min']) ?? new Date('January 01, 1900 00:00:00');
|
|
1033
|
+
var thisYear = new Date().getUTCFullYear;
|
|
1034
|
+
this.max = new Date(options['max']) ?? new Date('December 31 ' + thisYear + ' 23:59:59');
|
|
1035
|
+
this.startDate = new Date(options['startDate']) ?? new Date();
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
class SliderQuestion extends QuestionBase {
|
|
1039
|
+
controlType = 'slider';
|
|
1040
|
+
min;
|
|
1041
|
+
max;
|
|
1042
|
+
constructor(options = {}) {
|
|
1043
|
+
super(options);
|
|
1044
|
+
this.min = options['min'] || 0;
|
|
1045
|
+
this.max = options['max'] || 100;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
class NumberQuestion extends QuestionBase {
|
|
1049
|
+
controlType = 'number';
|
|
1050
|
+
}
|
|
1051
|
+
class TextboxQuestion extends QuestionBase {
|
|
1052
|
+
controlType = 'textbox';
|
|
1053
|
+
}
|
|
1054
|
+
class EmailQuestion extends QuestionBase {
|
|
1055
|
+
controlType = 'email';
|
|
1056
|
+
}
|
|
1057
|
+
class PhoneQuestion extends QuestionBase {
|
|
1058
|
+
controlType = 'tel';
|
|
1059
|
+
}
|
|
1060
|
+
class DropdownQuestion extends QuestionBase {
|
|
1061
|
+
controlType = 'dropdown';
|
|
1062
|
+
}
|
|
1063
|
+
class AsyncDropdownQuestion extends QuestionBase {
|
|
1064
|
+
controlType = 'async-dropdown';
|
|
1065
|
+
targetModel;
|
|
1066
|
+
multiple;
|
|
1067
|
+
ddbKey;
|
|
1068
|
+
constructor(options = {}) {
|
|
1069
|
+
super(options);
|
|
1070
|
+
this.targetModel = options['targetModel'];
|
|
1071
|
+
this.multiple = options['multiple'];
|
|
1072
|
+
this.ddbKey = options['ddbKey'];
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
class FormGroupQuestion extends QuestionBase {
|
|
1076
|
+
controlType = 'formgroup';
|
|
1077
|
+
formGroup;
|
|
1078
|
+
questions = [];
|
|
1079
|
+
constructor(options = {}) {
|
|
1080
|
+
super(options);
|
|
1081
|
+
this.formGroup = options['formGroup'];
|
|
1082
|
+
this.questions = options['questions'];
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// Simple phone number validator (can be customized further)
|
|
1087
|
+
function phoneNumberValidator() {
|
|
1088
|
+
return (control) => {
|
|
1089
|
+
const phoneNumber = control.value;
|
|
1090
|
+
// Use a regular expression to validate the phone number format
|
|
1091
|
+
const regex = /^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/;
|
|
1092
|
+
if (!phoneNumber || regex.test(phoneNumber)) {
|
|
1093
|
+
return null; // Valid phone number
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
return { invalidPhoneNumber: true }; // Invalid phone number
|
|
1097
|
+
}
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
class QuestionControlService {
|
|
1102
|
+
ams;
|
|
1103
|
+
outputs;
|
|
1104
|
+
constructor(ams) {
|
|
1105
|
+
this.ams = ams;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Initialize the service with Amplify outputs
|
|
1109
|
+
* This should be called by the consuming application after Amplify.configure()
|
|
1110
|
+
*/
|
|
1111
|
+
initializeOutputs(outputs) {
|
|
1112
|
+
this.outputs = outputs;
|
|
1113
|
+
}
|
|
1114
|
+
getFieldForFieldObj(fieldObj, model) {
|
|
1115
|
+
if (!this.outputs) {
|
|
1116
|
+
console.error('QuestionControlService: Outputs not initialized. Call initializeOutputs() first.');
|
|
1117
|
+
return null;
|
|
1118
|
+
}
|
|
1119
|
+
console.log('getFieldForFieldObj starting for type', fieldObj.type, 'fieldObj', fieldObj);
|
|
1120
|
+
switch (fieldObj.type) {
|
|
1121
|
+
case 'String':
|
|
1122
|
+
return this._addStringQuestion(fieldObj, model);
|
|
1123
|
+
case 'Int':
|
|
1124
|
+
case 'Float':
|
|
1125
|
+
return this._addNumberQuestion(fieldObj, model);
|
|
1126
|
+
case 'AWSDate':
|
|
1127
|
+
case 'AWSDateTime':
|
|
1128
|
+
return this._addDatePickerQuestion(fieldObj, model);
|
|
1129
|
+
case 'AWSEmail':
|
|
1130
|
+
return this._addEmailQuestion(fieldObj, model);
|
|
1131
|
+
case 'AWSPhone':
|
|
1132
|
+
return this._addPhoneQuestion(fieldObj, model);
|
|
1133
|
+
}
|
|
1134
|
+
if (typeof fieldObj.type === 'object') {
|
|
1135
|
+
if (fieldObj.type.model) {
|
|
1136
|
+
return this.getRefModel(fieldObj, model);
|
|
1137
|
+
}
|
|
1138
|
+
if (fieldObj.type.nonModel) {
|
|
1139
|
+
let groupQuestions = this.getNonModelQuestions(fieldObj, model);
|
|
1140
|
+
var formGroup = this.toFormGroup(groupQuestions);
|
|
1141
|
+
return this._addFormGroupQuestion(fieldObj, formGroup, groupQuestions);
|
|
1142
|
+
}
|
|
1143
|
+
if (fieldObj.type.enum) {
|
|
1144
|
+
return this.getEnumQuestion(fieldObj, model);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
return null;
|
|
1148
|
+
}
|
|
1149
|
+
getLabelForFieldName(name) {
|
|
1150
|
+
return name.toLowerCase().split('_').map((word) => {
|
|
1151
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
1152
|
+
}).join(' ');
|
|
1153
|
+
}
|
|
1154
|
+
getQuestionsForModelFields(model) {
|
|
1155
|
+
if (!this.outputs) {
|
|
1156
|
+
console.error('QuestionControlService: Outputs not initialized. Call initializeOutputs() first.');
|
|
1157
|
+
return [];
|
|
1158
|
+
}
|
|
1159
|
+
console.log('outputs.data.model_introspection', this.outputs.data.model_introspection);
|
|
1160
|
+
const fields = this.outputs.data.model_introspection.models[model].fields;
|
|
1161
|
+
const questions = [];
|
|
1162
|
+
const values = Object.values(fields);
|
|
1163
|
+
const skipFields = ['createdAt', 'updatedAt', 'id'];
|
|
1164
|
+
for (const fieldObj of values) {
|
|
1165
|
+
if (skipFields.indexOf(fieldObj.name) > -1)
|
|
1166
|
+
continue;
|
|
1167
|
+
if (typeof fieldObj.type === 'object' && fieldObj.type.model) {
|
|
1168
|
+
continue; // skip model relationship fields
|
|
1169
|
+
}
|
|
1170
|
+
const q = this.getFieldForFieldObj(fieldObj, model);
|
|
1171
|
+
if (q) {
|
|
1172
|
+
questions.push(q);
|
|
1173
|
+
}
|
|
1174
|
+
else {
|
|
1175
|
+
console.log('no question returned for fieldObj', fieldObj, 'model', model);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
console.log('getQuestionsForModelFields questions', questions);
|
|
1179
|
+
return questions;
|
|
1180
|
+
}
|
|
1181
|
+
/** nonModel, form section Methods **/
|
|
1182
|
+
getNonModelQuestions(fieldObj, model) {
|
|
1183
|
+
if (!this.outputs) {
|
|
1184
|
+
console.error('QuestionControlService: Outputs not initialized. Call initializeOutputs() first.');
|
|
1185
|
+
return [];
|
|
1186
|
+
}
|
|
1187
|
+
const nonModel = fieldObj.type.nonModel;
|
|
1188
|
+
const fields = this.outputs.data.model_introspection.nonModels[nonModel].fields;
|
|
1189
|
+
const questions = [];
|
|
1190
|
+
const values = Object.values(fields);
|
|
1191
|
+
const skipFields = ['createdAt', 'updatedAt'];
|
|
1192
|
+
for (const nestedFieldObj of values) {
|
|
1193
|
+
const question = this.getFieldForFieldObj(nestedFieldObj, model);
|
|
1194
|
+
if (question) {
|
|
1195
|
+
questions.push(question);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
console.log('getNonModelQuestions questions', questions);
|
|
1199
|
+
return questions;
|
|
1200
|
+
}
|
|
1201
|
+
getEnumQuestion(fieldObj, model) {
|
|
1202
|
+
if (!this.outputs) {
|
|
1203
|
+
console.error('QuestionControlService: Outputs not initialized. Call initializeOutputs() first.');
|
|
1204
|
+
return null;
|
|
1205
|
+
}
|
|
1206
|
+
const enumName = fieldObj.type.enum;
|
|
1207
|
+
const e = this.outputs.data.model_introspection.enums[enumName];
|
|
1208
|
+
console.log('getEnumQuestion e', e);
|
|
1209
|
+
const choices = [];
|
|
1210
|
+
e.values.forEach((val) => {
|
|
1211
|
+
choices.push({ key: val, value: val });
|
|
1212
|
+
});
|
|
1213
|
+
return this._addDropdownQuestion(fieldObj, choices, model);
|
|
1214
|
+
}
|
|
1215
|
+
/** Reference Field Methods **/
|
|
1216
|
+
getRefModel(fieldObj, callingModel) {
|
|
1217
|
+
if (!this.outputs) {
|
|
1218
|
+
console.error('QuestionControlService: Outputs not initialized. Call initializeOutputs() first.');
|
|
1219
|
+
return null;
|
|
1220
|
+
}
|
|
1221
|
+
console.log('getRefModel starting for fieldObj', fieldObj);
|
|
1222
|
+
const referenceModel = fieldObj.type.model;
|
|
1223
|
+
const relModel = this.outputs.data.model_introspection.models[referenceModel].fields;
|
|
1224
|
+
const vals = Object.values(relModel);
|
|
1225
|
+
for (const val of vals) {
|
|
1226
|
+
const fieldVal = val;
|
|
1227
|
+
if (typeof fieldVal.type !== 'object')
|
|
1228
|
+
continue;
|
|
1229
|
+
if (!fieldVal.type.model)
|
|
1230
|
+
continue;
|
|
1231
|
+
if (fieldVal.type.model === callingModel)
|
|
1232
|
+
continue;
|
|
1233
|
+
const modelName = fieldVal.type.model;
|
|
1234
|
+
return this._addRefDropdownQuestion(fieldObj, callingModel, modelName);
|
|
1235
|
+
}
|
|
1236
|
+
return null;
|
|
1237
|
+
}
|
|
1238
|
+
_addFormGroupQuestion(fieldObj, formGroup, questions) {
|
|
1239
|
+
const name = fieldObj.name.toString();
|
|
1240
|
+
return new FormGroupQuestion({
|
|
1241
|
+
key: name,
|
|
1242
|
+
label: this.getLabelForFieldName(fieldObj.name),
|
|
1243
|
+
required: fieldObj.isRequired,
|
|
1244
|
+
type: 'formgroup',
|
|
1245
|
+
formGroup: formGroup,
|
|
1246
|
+
questions: questions
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
_addDropdownQuestion(fieldObj, items, model) {
|
|
1250
|
+
console.log('_addDropdownQuestion for fieldObj', fieldObj, 'items', items);
|
|
1251
|
+
const name = fieldObj.name.toString();
|
|
1252
|
+
return new DropdownQuestion({
|
|
1253
|
+
key: name,
|
|
1254
|
+
label: this.getLabelForFieldName(fieldObj.name),
|
|
1255
|
+
required: fieldObj.isRequired,
|
|
1256
|
+
options: items,
|
|
1257
|
+
type: 'dropdown',
|
|
1258
|
+
model: model
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
_addRefDropdownQuestion(fieldObj, model, targetModel) {
|
|
1262
|
+
console.log('_addRefDropdownQuestion for fieldObj', fieldObj);
|
|
1263
|
+
const name = fieldObj.name.toString();
|
|
1264
|
+
return new AsyncDropdownQuestion({
|
|
1265
|
+
key: name,
|
|
1266
|
+
label: this.getLabelForFieldName(fieldObj.name),
|
|
1267
|
+
required: fieldObj.isRequired,
|
|
1268
|
+
type: 'dropdown',
|
|
1269
|
+
model: model,
|
|
1270
|
+
targetModel: targetModel,
|
|
1271
|
+
multiple: fieldObj.association?.connectionType === "HAS_MANY"
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
_addDatePickerQuestion(fieldObj, model) {
|
|
1275
|
+
const name = fieldObj.name.toString();
|
|
1276
|
+
return new DatePickerQuestion({
|
|
1277
|
+
key: name,
|
|
1278
|
+
label: this.getLabelForFieldName(fieldObj.name),
|
|
1279
|
+
required: fieldObj.isRequired,
|
|
1280
|
+
type: 'date',
|
|
1281
|
+
model: model
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
_addNumberQuestion(fieldObj, model) {
|
|
1285
|
+
const name = fieldObj.name.toString();
|
|
1286
|
+
return new NumberQuestion({
|
|
1287
|
+
key: name,
|
|
1288
|
+
label: this.getLabelForFieldName(fieldObj.name),
|
|
1289
|
+
required: fieldObj.isRequired,
|
|
1290
|
+
type: 'number',
|
|
1291
|
+
model: model
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
_addStringQuestion(fieldObj, model) {
|
|
1295
|
+
const name = fieldObj.name.toString();
|
|
1296
|
+
return new TextboxQuestion({
|
|
1297
|
+
key: name,
|
|
1298
|
+
label: this.getLabelForFieldName(fieldObj.name),
|
|
1299
|
+
required: fieldObj.isRequired,
|
|
1300
|
+
type: 'textbox',
|
|
1301
|
+
model: model
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
_addEmailQuestion(fieldObj, model) {
|
|
1305
|
+
const name = fieldObj.name.toString();
|
|
1306
|
+
return new EmailQuestion({
|
|
1307
|
+
key: name,
|
|
1308
|
+
label: this.getLabelForFieldName(fieldObj.name),
|
|
1309
|
+
required: fieldObj.isRequired,
|
|
1310
|
+
type: 'email',
|
|
1311
|
+
validators: [Validators.email],
|
|
1312
|
+
model: model
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
_addPhoneQuestion(fieldObj, model) {
|
|
1316
|
+
const name = fieldObj.name.toString();
|
|
1317
|
+
return new PhoneQuestion({
|
|
1318
|
+
key: name,
|
|
1319
|
+
label: this.getLabelForFieldName(fieldObj.name),
|
|
1320
|
+
required: fieldObj.isRequired,
|
|
1321
|
+
type: 'tel',
|
|
1322
|
+
validators: [phoneNumberValidator()],
|
|
1323
|
+
model: model
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
toFormGroup(questions) {
|
|
1327
|
+
const group = {};
|
|
1328
|
+
questions.forEach(question => {
|
|
1329
|
+
if (!question)
|
|
1330
|
+
return;
|
|
1331
|
+
if ('questions' in question && question.questions) {
|
|
1332
|
+
group[question.key] = this.toFormGroup(question.questions);
|
|
1333
|
+
}
|
|
1334
|
+
else {
|
|
1335
|
+
const validators = [];
|
|
1336
|
+
if (question.required) {
|
|
1337
|
+
validators.push(Validators.required);
|
|
1338
|
+
}
|
|
1339
|
+
if (question.validators) {
|
|
1340
|
+
validators.push(...question.validators);
|
|
1341
|
+
}
|
|
1342
|
+
group[question.key] = new FormControl(question.value || '', validators);
|
|
1343
|
+
}
|
|
1344
|
+
});
|
|
1345
|
+
return new FormGroup(group);
|
|
1346
|
+
}
|
|
1347
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: QuestionControlService, deps: [{ token: AmplifyModelService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1348
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: QuestionControlService, providedIn: 'root' });
|
|
1349
|
+
}
|
|
1350
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: QuestionControlService, decorators: [{
|
|
1351
|
+
type: Injectable,
|
|
1352
|
+
args: [{
|
|
1353
|
+
providedIn: 'root'
|
|
1354
|
+
}]
|
|
1355
|
+
}], ctorParameters: () => [{ type: AmplifyModelService }] });
|
|
1356
|
+
|
|
1357
|
+
/** Error when invalid control is dirty, touched, or submitted. */
|
|
1358
|
+
class MyErrorStateMatcher {
|
|
1359
|
+
isErrorState(control, form) {
|
|
1360
|
+
const isSubmitted = form && form.submitted;
|
|
1361
|
+
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
class DynamicFormComponent {
|
|
1365
|
+
qcs;
|
|
1366
|
+
afb;
|
|
1367
|
+
location;
|
|
1368
|
+
model = "";
|
|
1369
|
+
itemId = "";
|
|
1370
|
+
valueMap;
|
|
1371
|
+
hideRelationships = false;
|
|
1372
|
+
hideSaveButton = false;
|
|
1373
|
+
itemOutput = new EventEmitter();
|
|
1374
|
+
submitOutput = new EventEmitter();
|
|
1375
|
+
formLoaded = new EventEmitter();
|
|
1376
|
+
mdl = '';
|
|
1377
|
+
id = '';
|
|
1378
|
+
item;
|
|
1379
|
+
payLoad;
|
|
1380
|
+
form;
|
|
1381
|
+
questions = [];
|
|
1382
|
+
materialInputs = [];
|
|
1383
|
+
switchInputs = [];
|
|
1384
|
+
formGroupInputs = ['formgroup'];
|
|
1385
|
+
refInputs = [];
|
|
1386
|
+
ams = inject(AmplifyModelService);
|
|
1387
|
+
_snackBar = inject(MatSnackBar);
|
|
1388
|
+
constructor(qcs, afb, location) {
|
|
1389
|
+
this.qcs = qcs;
|
|
1390
|
+
this.afb = afb;
|
|
1391
|
+
this.location = location;
|
|
1392
|
+
}
|
|
1393
|
+
ngOnInit() {
|
|
1394
|
+
this.materialInputs = this.afb.getMaterialInputs();
|
|
1395
|
+
this.switchInputs = this.afb.getSwitchInputs();
|
|
1396
|
+
this.refInputs = this.afb.getRefInputs();
|
|
1397
|
+
// Use inputs instead of route params
|
|
1398
|
+
this.mdl = this.model;
|
|
1399
|
+
this.id = this.itemId;
|
|
1400
|
+
if (this.id == '-1' || !this.id) {
|
|
1401
|
+
this.setQuestionsAndForm();
|
|
1402
|
+
}
|
|
1403
|
+
else {
|
|
1404
|
+
this.getItem();
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
setFormBuilderForm() {
|
|
1408
|
+
this.form = this.afb.buildFormForModel(this.mdl);
|
|
1409
|
+
}
|
|
1410
|
+
async getItem() {
|
|
1411
|
+
const { data: item } = await this.ams.getItemForModel(this.mdl, this.id);
|
|
1412
|
+
this.item = item;
|
|
1413
|
+
this.itemOutput.emit(this.item);
|
|
1414
|
+
this.setQuestionsAndForm();
|
|
1415
|
+
}
|
|
1416
|
+
setQuestionsAndForm() {
|
|
1417
|
+
if (!this.questions || this.questions.length == 0) {
|
|
1418
|
+
this.questions = this.qcs.getQuestionsForModelFields(this.mdl);
|
|
1419
|
+
}
|
|
1420
|
+
if (this.item) {
|
|
1421
|
+
for (let question of this.questions) {
|
|
1422
|
+
this.setQuestionValueFromItem(question);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
// Apply valueMap if provided
|
|
1426
|
+
if (this.valueMap) {
|
|
1427
|
+
for (let question of this.questions) {
|
|
1428
|
+
this.setQuestionValueFromValueMap(question, this.valueMap);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
this.form = this.qcs.toFormGroup(this.questions);
|
|
1432
|
+
this.formLoaded.emit(this.form);
|
|
1433
|
+
console.log('this.item', this.item);
|
|
1434
|
+
}
|
|
1435
|
+
setQuestionValueFromItem(question, item) {
|
|
1436
|
+
if (!item)
|
|
1437
|
+
item = this.item;
|
|
1438
|
+
if (question.controlType == 'formgroup') {
|
|
1439
|
+
for (let q of question.questions) {
|
|
1440
|
+
this.setQuestionValueFromItem(q, item[question.key]);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
else if (item[question.key]) {
|
|
1444
|
+
question.value = item[question.key];
|
|
1445
|
+
}
|
|
1446
|
+
else {
|
|
1447
|
+
console.log('setQuestionValueFromItem no item found and not formgroup');
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
setQuestionValueFromValueMap(question, valueMap) {
|
|
1451
|
+
if (question.controlType == 'formgroup') {
|
|
1452
|
+
for (let q of question.questions) {
|
|
1453
|
+
this.setQuestionValueFromValueMap(q, valueMap);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
else if (valueMap[question.key] !== undefined) {
|
|
1457
|
+
question.value = valueMap[question.key];
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
getValueForKey(obj, key) {
|
|
1461
|
+
if (obj === null || typeof obj !== 'object') {
|
|
1462
|
+
return null;
|
|
1463
|
+
}
|
|
1464
|
+
if (key in obj) {
|
|
1465
|
+
return obj[key];
|
|
1466
|
+
}
|
|
1467
|
+
for (const k in obj) {
|
|
1468
|
+
const result = this.getValueForKey(obj[k], key);
|
|
1469
|
+
if (result !== null) {
|
|
1470
|
+
return result;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
return null;
|
|
1474
|
+
}
|
|
1475
|
+
setValueForKey(obj, key, val) {
|
|
1476
|
+
if (obj === null || typeof obj !== 'object') {
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
if (key in obj) {
|
|
1480
|
+
obj[key] = val;
|
|
1481
|
+
}
|
|
1482
|
+
for (const k in obj) {
|
|
1483
|
+
if (typeof obj[k] == 'object') {
|
|
1484
|
+
this.setValueForKey(obj[k], key, val);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
setRefQuestionValuesInPayload(questions, payLoadObj) {
|
|
1489
|
+
let qs = questions || this.questions;
|
|
1490
|
+
let refQs = qs.filter((x) => x.controlType == 'async-dropdown');
|
|
1491
|
+
if (refQs) {
|
|
1492
|
+
refQs.forEach((refQ) => {
|
|
1493
|
+
let keyVal = this.getValueForKey(this.payLoad, refQ.key);
|
|
1494
|
+
if (Array.isArray(keyVal)) {
|
|
1495
|
+
let relArr = [];
|
|
1496
|
+
for (var v in keyVal) {
|
|
1497
|
+
let valObj = {};
|
|
1498
|
+
valObj[refQ.targetModel.toLowerCase() + 'Id'] = keyVal[v];
|
|
1499
|
+
valObj[this.mdl.toLowerCase() + 'Id'] = this.id;
|
|
1500
|
+
relArr.push(valObj);
|
|
1501
|
+
}
|
|
1502
|
+
this.setValueForKey(this.payLoad, refQ.key, relArr);
|
|
1503
|
+
}
|
|
1504
|
+
else {
|
|
1505
|
+
console.log('qs keyVal is not array');
|
|
1506
|
+
}
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
let groupQs = qs.filter((x) => x.controlType == 'formgroup');
|
|
1510
|
+
if (groupQs) {
|
|
1511
|
+
for (let groupQ of groupQs) {
|
|
1512
|
+
console.log('qs checking groupQ', groupQ);
|
|
1513
|
+
this.setRefQuestionValuesInPayload(groupQ.questions);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
console.log('qs payload', this.payLoad);
|
|
1517
|
+
}
|
|
1518
|
+
questionsIncludesRefType(questions) {
|
|
1519
|
+
let qs = questions || this.questions;
|
|
1520
|
+
let refQ = qs.find((x) => x.controlType == 'async-dropdown');
|
|
1521
|
+
if (refQ)
|
|
1522
|
+
return true;
|
|
1523
|
+
let groupQs = qs.filter((x) => x.controlType == 'formgroup');
|
|
1524
|
+
if (groupQs) {
|
|
1525
|
+
for (let groupQ of groupQs) {
|
|
1526
|
+
if (this.questionsIncludesRefType(groupQ.questions)) {
|
|
1527
|
+
return true;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
return false;
|
|
1532
|
+
}
|
|
1533
|
+
formHasRelationshipQuestions() {
|
|
1534
|
+
return this.questionsIncludesRefType();
|
|
1535
|
+
}
|
|
1536
|
+
onSubmit() {
|
|
1537
|
+
this.payLoad = this.form.getRawValue();
|
|
1538
|
+
if (this.item && this.item.id) {
|
|
1539
|
+
this.payLoad.id = this.item.id;
|
|
1540
|
+
console.log('will update because this.item.id', this.item.id);
|
|
1541
|
+
this.updateItem();
|
|
1542
|
+
}
|
|
1543
|
+
else {
|
|
1544
|
+
console.log('will create because !this.item.id', this.item);
|
|
1545
|
+
this.createItem();
|
|
1546
|
+
}
|
|
1547
|
+
this.submitOutput.emit(this.payLoad);
|
|
1548
|
+
console.log('onSubmit payload', this.payLoad, 'this.item', this.item, 'this.form', this.form);
|
|
1549
|
+
}
|
|
1550
|
+
async updateItem() {
|
|
1551
|
+
const { data: item } = await this.ams.updateItemForModel(this.mdl, this.payLoad);
|
|
1552
|
+
this.item = item;
|
|
1553
|
+
this.itemOutput.emit(this.item);
|
|
1554
|
+
this.setQuestionsAndForm();
|
|
1555
|
+
}
|
|
1556
|
+
getQuestionForKey(key, questions) {
|
|
1557
|
+
let searchQs = questions || this.questions;
|
|
1558
|
+
let q = searchQs.find((x) => x.key == key);
|
|
1559
|
+
if (q)
|
|
1560
|
+
return q;
|
|
1561
|
+
let groupQs = searchQs.filter((x) => x.controlType == 'formgroup');
|
|
1562
|
+
if (groupQs) {
|
|
1563
|
+
for (let groupQ of groupQs) {
|
|
1564
|
+
console.log('searchQs checking groupQ', groupQ);
|
|
1565
|
+
q = this.getQuestionForKey(key, groupQ.questions);
|
|
1566
|
+
if (q)
|
|
1567
|
+
return q;
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
prepPayloadObj(obj) {
|
|
1573
|
+
let prepObj = obj || this.payLoad;
|
|
1574
|
+
console.log('prepPayloadObj starting for prepObj', prepObj);
|
|
1575
|
+
for (let prop in prepObj) {
|
|
1576
|
+
let q = this.getQuestionForKey(prop);
|
|
1577
|
+
if (q) {
|
|
1578
|
+
console.log('q', q);
|
|
1579
|
+
if (q.controlType == "formgroup") {
|
|
1580
|
+
this.prepPayloadObj(prepObj[prop]);
|
|
1581
|
+
}
|
|
1582
|
+
else {
|
|
1583
|
+
if (!q.required && prepObj[prop] == '') {
|
|
1584
|
+
delete prepObj[prop];
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
else {
|
|
1589
|
+
console.log('prepPayloadObj no q for prop', prop);
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
async createItem() {
|
|
1594
|
+
this.prepPayloadObj();
|
|
1595
|
+
console.log('createItem prepped payload', this.payLoad);
|
|
1596
|
+
try {
|
|
1597
|
+
let res = await this.ams.createItemPromise(this.mdl, this.payLoad);
|
|
1598
|
+
console.log('createItem finished res', res);
|
|
1599
|
+
if (!res) {
|
|
1600
|
+
this._snackBar.open('Error creating ' + this.mdl, 'Dismiss');
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
if (res.errors && res.errors.length > 0) {
|
|
1604
|
+
let messages = [];
|
|
1605
|
+
res.errors.forEach((err) => {
|
|
1606
|
+
messages.push(err.message);
|
|
1607
|
+
});
|
|
1608
|
+
this._snackBar.open(messages.join('\n'), 'Dismiss');
|
|
1609
|
+
}
|
|
1610
|
+
else {
|
|
1611
|
+
this.item = res.data;
|
|
1612
|
+
this.itemOutput.emit(this.item);
|
|
1613
|
+
this.setQuestionsAndForm();
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
catch (error) {
|
|
1617
|
+
console.error('error in createItem', error);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
changePathIdParam(newParamValue) {
|
|
1621
|
+
const currentUrl = this.location.path();
|
|
1622
|
+
let paramValArr = currentUrl.split('/');
|
|
1623
|
+
let currentId = paramValArr[paramValArr.length - 1];
|
|
1624
|
+
const newUrl = currentUrl.replace(currentId, newParamValue);
|
|
1625
|
+
this.location.replaceState(newUrl);
|
|
1626
|
+
}
|
|
1627
|
+
isValid(question, q, q2) {
|
|
1628
|
+
if (question && q && q2)
|
|
1629
|
+
return this.form.get(question.key + '.' + q.key + '.' + q2.key);
|
|
1630
|
+
if (question && q && !q2)
|
|
1631
|
+
return this.form.get(question.key + '.' + q.key);
|
|
1632
|
+
if (question && !q && !q2)
|
|
1633
|
+
return this.form.get(question.key);
|
|
1634
|
+
return true;
|
|
1635
|
+
}
|
|
1636
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DynamicFormComponent, deps: [{ token: QuestionControlService }, { token: AmplifyFormBuilderService }, { token: i3$1.Location }], target: i0.ɵɵFactoryTarget.Component });
|
|
1637
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DynamicFormComponent, isStandalone: true, selector: "snteam-dynamic-form", inputs: { model: "model", itemId: "itemId", valueMap: "valueMap", hideRelationships: "hideRelationships", hideSaveButton: "hideSaveButton" }, outputs: { itemOutput: "itemOutput", submitOutput: "submitOutput", formLoaded: "formLoaded" }, ngImport: i0, template: "<div class=\"dynamic-form-holder\">\n @if (form && questions) {\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"form\">\n @for (question of questions; track question) {\n @if (this.switchInputs.includes(question.controlType) || this.materialInputs.includes(question.controlType) || this.refInputs.includes(question.controlType)) {\n <div class=\"form-row\">\n <snteam-dynamic-form-question [question]=\"question\" [formGroup]=\"form\"></snteam-dynamic-form-question>\n </div>\n }\n @if (this.formGroupInputs.includes(question.controlType)) {\n <div class=\"group-row\">\n <snteam-dynamic-form-group [question]=\"question\" [formGroup]=\"form\" [formGroupName]=\"question.key\"></snteam-dynamic-form-group>\n </div>\n }\n }\n @if (!hideSaveButton) {\n <div class=\"form-row\">\n <button mat-raised-button color=\"primary\" type=\"submit\" [disabled]=\"!form.valid\">Save</button>\n </div>\n }\n </form>\n } @else {\n <div class=\"loading\">Loading...</div>\n }\n\n @if(item && !hideRelationships){\n <div class=\"relationship-holder\">\n <snteam-record-relationships [model]=\"mdl\" [id]=\"id\" [item]=\"item\"></snteam-record-relationships>\n </div>\n }\n</div>", styles: [".dynamic-form-holder{padding:20px;width:100%;display:flex;justify-content:space-between;gap:20px}form{width:100%;flex-basis:40%}.relationship-holder{flex-basis:40%}.full-width{width:100%}.form-row,.group-row{margin-top:20px}.loading{padding:20px;text-align:center;color:#666}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DynamicFormQuestionComponent, selector: "snteam-dynamic-form-question", inputs: ["question", "formGroup"] }, { kind: "component", type: DynamicFormGroupComponent, selector: "snteam-dynamic-form-group", inputs: ["formGroupName", "question", "formGroup"] }, { kind: "component", type: RecordRelationshipsComponent, selector: "snteam-record-relationships", inputs: ["model", "id", "item", "amplifyOutputs"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatDividerModule }, { 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"] }] });
|
|
1638
|
+
}
|
|
1639
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DynamicFormComponent, decorators: [{
|
|
1640
|
+
type: Component,
|
|
1641
|
+
args: [{ standalone: true, selector: 'snteam-dynamic-form', imports: [
|
|
1642
|
+
CommonModule,
|
|
1643
|
+
DynamicFormQuestionComponent,
|
|
1644
|
+
DynamicNestedFormQuestionComponent,
|
|
1645
|
+
DynamicFormGroupComponent,
|
|
1646
|
+
RecordRelationshipsComponent,
|
|
1647
|
+
ReactiveFormsModule,
|
|
1648
|
+
MatFormFieldModule,
|
|
1649
|
+
MatInputModule,
|
|
1650
|
+
MatSelectModule,
|
|
1651
|
+
MatSnackBarModule,
|
|
1652
|
+
MatDividerModule,
|
|
1653
|
+
MatButtonModule
|
|
1654
|
+
], template: "<div class=\"dynamic-form-holder\">\n @if (form && questions) {\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"form\">\n @for (question of questions; track question) {\n @if (this.switchInputs.includes(question.controlType) || this.materialInputs.includes(question.controlType) || this.refInputs.includes(question.controlType)) {\n <div class=\"form-row\">\n <snteam-dynamic-form-question [question]=\"question\" [formGroup]=\"form\"></snteam-dynamic-form-question>\n </div>\n }\n @if (this.formGroupInputs.includes(question.controlType)) {\n <div class=\"group-row\">\n <snteam-dynamic-form-group [question]=\"question\" [formGroup]=\"form\" [formGroupName]=\"question.key\"></snteam-dynamic-form-group>\n </div>\n }\n }\n @if (!hideSaveButton) {\n <div class=\"form-row\">\n <button mat-raised-button color=\"primary\" type=\"submit\" [disabled]=\"!form.valid\">Save</button>\n </div>\n }\n </form>\n } @else {\n <div class=\"loading\">Loading...</div>\n }\n\n @if(item && !hideRelationships){\n <div class=\"relationship-holder\">\n <snteam-record-relationships [model]=\"mdl\" [id]=\"id\" [item]=\"item\"></snteam-record-relationships>\n </div>\n }\n</div>", styles: [".dynamic-form-holder{padding:20px;width:100%;display:flex;justify-content:space-between;gap:20px}form{width:100%;flex-basis:40%}.relationship-holder{flex-basis:40%}.full-width{width:100%}.form-row,.group-row{margin-top:20px}.loading{padding:20px;text-align:center;color:#666}\n"] }]
|
|
1655
|
+
}], ctorParameters: () => [{ type: QuestionControlService }, { type: AmplifyFormBuilderService }, { type: i3$1.Location }], propDecorators: { model: [{
|
|
1656
|
+
type: Input
|
|
1657
|
+
}], itemId: [{
|
|
1658
|
+
type: Input
|
|
1659
|
+
}], valueMap: [{
|
|
1660
|
+
type: Input
|
|
1661
|
+
}], hideRelationships: [{
|
|
1662
|
+
type: Input
|
|
1663
|
+
}], hideSaveButton: [{
|
|
1664
|
+
type: Input
|
|
1665
|
+
}], itemOutput: [{
|
|
1666
|
+
type: Output
|
|
1667
|
+
}], submitOutput: [{
|
|
1668
|
+
type: Output
|
|
1669
|
+
}], formLoaded: [{
|
|
1670
|
+
type: Output
|
|
1671
|
+
}] } });
|
|
1672
|
+
|
|
1673
|
+
class ListViewComponent {
|
|
1674
|
+
ams = inject(AmplifyModelService);
|
|
1675
|
+
modelName = '';
|
|
1676
|
+
customItemTemplate;
|
|
1677
|
+
hideNewButton = false;
|
|
1678
|
+
title;
|
|
1679
|
+
itemClick = new EventEmitter();
|
|
1680
|
+
newClick = new EventEmitter();
|
|
1681
|
+
itemsLoaded = new EventEmitter();
|
|
1682
|
+
itemsArr = [];
|
|
1683
|
+
loading = true;
|
|
1684
|
+
error = null;
|
|
1685
|
+
ngOnInit() {
|
|
1686
|
+
if (this.modelName) {
|
|
1687
|
+
this.listItems();
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
listItems() {
|
|
1691
|
+
console.log('ListViewComponent listItems starting for model:', this.modelName);
|
|
1692
|
+
if (this.modelName) {
|
|
1693
|
+
this.loading = true;
|
|
1694
|
+
this.error = null;
|
|
1695
|
+
const observable = this.ams.listItemsForModel(this.modelName);
|
|
1696
|
+
if (observable) {
|
|
1697
|
+
observable.subscribe({
|
|
1698
|
+
next: ({ items, isSynced }) => {
|
|
1699
|
+
this.itemsArr = items;
|
|
1700
|
+
this.loading = false;
|
|
1701
|
+
this.itemsLoaded.emit(this.itemsArr);
|
|
1702
|
+
console.log('ListViewComponent items loaded:', this.itemsArr);
|
|
1703
|
+
},
|
|
1704
|
+
error: (err) => {
|
|
1705
|
+
console.error('Error loading items:', err);
|
|
1706
|
+
this.error = 'Failed to load items';
|
|
1707
|
+
this.loading = false;
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
else {
|
|
1712
|
+
this.error = 'Service not initialized';
|
|
1713
|
+
this.loading = false;
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
onItemClick(item) {
|
|
1718
|
+
console.log('ListViewComponent itemClick', item);
|
|
1719
|
+
this.itemClick.emit(item);
|
|
1720
|
+
}
|
|
1721
|
+
onNewClick() {
|
|
1722
|
+
console.log('ListViewComponent newClick');
|
|
1723
|
+
this.newClick.emit();
|
|
1724
|
+
}
|
|
1725
|
+
getDisplayText(item) {
|
|
1726
|
+
// Try common display fields
|
|
1727
|
+
return item.name || item.title || item.label || item.id || 'Unnamed Item';
|
|
1728
|
+
}
|
|
1729
|
+
refresh() {
|
|
1730
|
+
this.listItems();
|
|
1731
|
+
}
|
|
1732
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ListViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1733
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ListViewComponent, isStandalone: true, selector: "snteam-list-view", inputs: { modelName: "modelName", customItemTemplate: "customItemTemplate", hideNewButton: "hideNewButton", title: "title" }, outputs: { itemClick: "itemClick", newClick: "newClick", itemsLoaded: "itemsLoaded" }, ngImport: i0, template: "<div class=\"list-view-container\">\n <div class=\"header\">\n <h2>{{ title || modelName }}</h2>\n @if (!hideNewButton) {\n <button mat-raised-button color=\"primary\" (click)=\"onNewClick()\">\n <mat-icon>add</mat-icon>\n New {{ modelName }}\n </button>\n }\n </div>\n\n @if (loading) {\n <div class=\"loading\">Loading {{ modelName }}...</div>\n }\n\n @if (error) {\n <div class=\"error\">\n {{ error }}\n <button mat-button (click)=\"refresh()\">Retry</button>\n </div>\n }\n\n @if (!loading && !error && itemsArr.length === 0) {\n <div class=\"empty-state\">\n <p>No {{ modelName }} found</p>\n @if (!hideNewButton) {\n <button mat-raised-button color=\"primary\" (click)=\"onNewClick()\">\n Create First {{ modelName }}\n </button>\n }\n </div>\n }\n\n @if (!loading && !error && itemsArr.length > 0) {\n <mat-list class=\"items-list\">\n @for (item of itemsArr; track item.id) {\n <mat-list-item (click)=\"onItemClick(item)\" class=\"clickable-item\">\n @if (customItemTemplate) {\n <ng-container [ngTemplateOutlet]=\"customItemTemplate\" [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n } @else {\n <span matListItemTitle>{{ getDisplayText(item) }}</span>\n <span matListItemLine>{{ item.description || item.createdAt | date }}</span>\n }\n </mat-list-item>\n }\n </mat-list>\n }\n</div>", styles: [".list-view-container{padding:20px;width:100%}.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.header h2{margin:0;color:#333}.loading,.error,.empty-state{padding:40px 20px;text-align:center;color:#666}.error{color:#d32f2f}.empty-state p{margin-bottom:20px;font-size:16px}.items-list{border:1px solid #e0e0e0;border-radius:4px}.clickable-item{cursor:pointer;transition:background-color .2s}.clickable-item:hover{background-color:#f5f5f5}.clickable-item:not(:last-child){border-bottom:1px solid #e0e0e0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i5$1.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i5$1.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "directive", type: i5$1.MatListItemLine, selector: "[matListItemLine]" }, { kind: "directive", type: i5$1.MatListItemTitle, selector: "[matListItemTitle]" }, { 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: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i3$1.DatePipe, name: "date" }] });
|
|
1734
|
+
}
|
|
1735
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ListViewComponent, decorators: [{
|
|
1736
|
+
type: Component,
|
|
1737
|
+
args: [{ selector: 'snteam-list-view', standalone: true, imports: [CommonModule, MatListModule, MatButtonModule, MatIconModule], template: "<div class=\"list-view-container\">\n <div class=\"header\">\n <h2>{{ title || modelName }}</h2>\n @if (!hideNewButton) {\n <button mat-raised-button color=\"primary\" (click)=\"onNewClick()\">\n <mat-icon>add</mat-icon>\n New {{ modelName }}\n </button>\n }\n </div>\n\n @if (loading) {\n <div class=\"loading\">Loading {{ modelName }}...</div>\n }\n\n @if (error) {\n <div class=\"error\">\n {{ error }}\n <button mat-button (click)=\"refresh()\">Retry</button>\n </div>\n }\n\n @if (!loading && !error && itemsArr.length === 0) {\n <div class=\"empty-state\">\n <p>No {{ modelName }} found</p>\n @if (!hideNewButton) {\n <button mat-raised-button color=\"primary\" (click)=\"onNewClick()\">\n Create First {{ modelName }}\n </button>\n }\n </div>\n }\n\n @if (!loading && !error && itemsArr.length > 0) {\n <mat-list class=\"items-list\">\n @for (item of itemsArr; track item.id) {\n <mat-list-item (click)=\"onItemClick(item)\" class=\"clickable-item\">\n @if (customItemTemplate) {\n <ng-container [ngTemplateOutlet]=\"customItemTemplate\" [ngTemplateOutletContext]=\"{ $implicit: item }\"></ng-container>\n } @else {\n <span matListItemTitle>{{ getDisplayText(item) }}</span>\n <span matListItemLine>{{ item.description || item.createdAt | date }}</span>\n }\n </mat-list-item>\n }\n </mat-list>\n }\n</div>", styles: [".list-view-container{padding:20px;width:100%}.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.header h2{margin:0;color:#333}.loading,.error,.empty-state{padding:40px 20px;text-align:center;color:#666}.error{color:#d32f2f}.empty-state p{margin-bottom:20px;font-size:16px}.items-list{border:1px solid #e0e0e0;border-radius:4px}.clickable-item{cursor:pointer;transition:background-color .2s}.clickable-item:hover{background-color:#f5f5f5}.clickable-item:not(:last-child){border-bottom:1px solid #e0e0e0}\n"] }]
|
|
1738
|
+
}], propDecorators: { modelName: [{
|
|
1739
|
+
type: Input
|
|
1740
|
+
}], customItemTemplate: [{
|
|
1741
|
+
type: Input
|
|
1742
|
+
}], hideNewButton: [{
|
|
1743
|
+
type: Input
|
|
1744
|
+
}], title: [{
|
|
1745
|
+
type: Input
|
|
1746
|
+
}], itemClick: [{
|
|
1747
|
+
type: Output
|
|
1748
|
+
}], newClick: [{
|
|
1749
|
+
type: Output
|
|
1750
|
+
}], itemsLoaded: [{
|
|
1751
|
+
type: Output
|
|
1752
|
+
}] } });
|
|
1753
|
+
|
|
1754
|
+
/*
|
|
1755
|
+
* Public API Surface of @snteam/amplify-angular-core
|
|
1756
|
+
*/
|
|
1757
|
+
// Legacy exports for backward compatibility
|
|
1758
|
+
|
|
1759
|
+
/**
|
|
1760
|
+
* Generated bundle index. Do not edit.
|
|
1761
|
+
*/
|
|
1762
|
+
|
|
1763
|
+
export { AddRelationshipDialogComponent, AmplifyAngularCore, AmplifyFormBuilderService, AmplifyModelService, AsyncDropdownQuestion, DatePickerQuestion, DateTimePickerQuestion, DropdownQuestion, DynamicFormComponent, DynamicFormGroupComponent, DynamicFormQuestionComponent, DynamicNestedFormQuestionComponent, DynamicRelationshipBuilderComponent, EmailQuestion, FormGroupQuestion, ListViewComponent, MyErrorStateMatcher, NumberQuestion, PhoneQuestion, QuestionBase, QuestionControlService, RecordRelationshipsComponent, SliderQuestion, TextboxQuestion, TimePickerQuestion, ValToTitlePipe, phoneNumberValidator };
|
|
1764
|
+
//# sourceMappingURL=snteam-amplify-angular-core.mjs.map
|