@masterteam/form-builder 0.0.1 → 0.0.3

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.
Files changed (35) hide show
  1. package/assets/form-builder.css +2 -4
  2. package/assets/i18n/ar.json +2 -0
  3. package/assets/i18n/en.json +2 -0
  4. package/fesm2022/masterteam-form-builder.mjs +1908 -0
  5. package/fesm2022/masterteam-form-builder.mjs.map +1 -0
  6. package/package.json +16 -16
  7. package/types/masterteam-form-builder.d.ts +297 -0
  8. package/.angular/cache/21.0.2/ng-packagr/db70d8f07b5a2d2d1c3124ca92e8d56d14fb894dce4d4867ba7c0db29ba913a3 +0 -1
  9. package/.angular/cache/21.0.2/ng-packagr/tsbuildinfo/masterteam-form-builder.tsbuildinfo +0 -1
  10. package/BACKEND_API_SPEC.md +0 -338
  11. package/angular.json +0 -26
  12. package/ng-package.json +0 -13
  13. package/src/lib/fb-field-conditions/condition-constants.ts +0 -262
  14. package/src/lib/fb-field-conditions/fb-field-conditions.html +0 -35
  15. package/src/lib/fb-field-conditions/fb-field-conditions.ts +0 -123
  16. package/src/lib/fb-field-form/fb-field-form.html +0 -59
  17. package/src/lib/fb-field-form/fb-field-form.ts +0 -249
  18. package/src/lib/fb-preview-form/fb-preview-form.html +0 -31
  19. package/src/lib/fb-preview-form/fb-preview-form.ts +0 -142
  20. package/src/lib/fb-section/fb-section.html +0 -130
  21. package/src/lib/fb-section/fb-section.ts +0 -204
  22. package/src/lib/fb-section-form/fb-section-form.html +0 -38
  23. package/src/lib/fb-section-form/fb-section-form.ts +0 -128
  24. package/src/lib/form-builder.html +0 -112
  25. package/src/lib/form-builder.model.ts +0 -60
  26. package/src/lib/form-builder.scss +0 -20
  27. package/src/lib/form-builder.ts +0 -208
  28. package/src/public-api.ts +0 -6
  29. package/src/store/form-builder/api.model.ts +0 -13
  30. package/src/store/form-builder/form-builder.actions.ts +0 -98
  31. package/src/store/form-builder/form-builder.facade.ts +0 -194
  32. package/src/store/form-builder/form-builder.model.ts +0 -112
  33. package/src/store/form-builder/form-builder.state.ts +0 -575
  34. package/src/store/form-builder/index.ts +0 -5
  35. package/tsconfig.json +0 -31
@@ -1,575 +0,0 @@
1
- import { HttpClient } from '@angular/common/http';
2
- import { inject, Injectable } from '@angular/core';
3
- import { Action, Selector, State, StateContext } from '@ngxs/store';
4
- import {
5
- CrudStateBase,
6
- handleApiRequest,
7
- Response,
8
- } from '@masterteam/components';
9
-
10
- import {
11
- FormBuilderActionKey,
12
- FormBuilderStateModel,
13
- FormConfiguration,
14
- FormField,
15
- FormSection,
16
- } from './form-builder.model';
17
- import {
18
- AddField,
19
- AddSection,
20
- DeleteField,
21
- DeleteSection,
22
- GetFormConfiguration,
23
- MoveField,
24
- ResetFormBuilderState,
25
- ResetFormConfiguration,
26
- SetModuleInfo,
27
- UpdateField,
28
- UpdateSection,
29
- } from './form-builder.actions';
30
-
31
- // Default State
32
- const DEFAULT_STATE: FormBuilderStateModel = {
33
- // Module configuration
34
- moduleType: null,
35
- moduleId: null,
36
- parentModuleType: null,
37
- parentModuleId: null,
38
- parentPath: '',
39
-
40
- // Form data
41
- formConfiguration: null,
42
-
43
- // Loading state (from LoadingStateShape)
44
- loadingActive: [],
45
- errors: {},
46
- };
47
-
48
- @State<FormBuilderStateModel>({
49
- name: 'formBuilder',
50
- defaults: DEFAULT_STATE,
51
- })
52
- @Injectable()
53
- export class FormBuilderState extends CrudStateBase<
54
- FormSection,
55
- FormBuilderStateModel,
56
- FormBuilderActionKey
57
- > {
58
- private http = inject(HttpClient);
59
- private baseUrl = 'formConfigurations';
60
-
61
- // ============================================================================
62
- // Helpers
63
- // ============================================================================
64
-
65
- private getApiPath(state: FormBuilderStateModel): string {
66
- const { parentPath, moduleType, moduleId } = state;
67
- return `${this.baseUrl}${parentPath}/${moduleType}/${moduleId}`;
68
- }
69
-
70
- // ============================================================================
71
- // Selectors
72
- // ============================================================================
73
-
74
- @Selector()
75
- static getState(state: FormBuilderStateModel): FormBuilderStateModel {
76
- return state ?? DEFAULT_STATE;
77
- }
78
-
79
- @Selector()
80
- static getFormConfiguration(
81
- state: FormBuilderStateModel,
82
- ): FormConfiguration | null {
83
- return state?.formConfiguration ?? null;
84
- }
85
-
86
- @Selector()
87
- static getSections(state: FormBuilderStateModel): FormSection[] {
88
- return state?.formConfiguration?.sections ?? [];
89
- }
90
-
91
- @Selector()
92
- static getModuleType(state: FormBuilderStateModel): string | null {
93
- return state?.moduleType ?? null;
94
- }
95
-
96
- @Selector()
97
- static getModuleId(state: FormBuilderStateModel): string | number | null {
98
- return state?.moduleId ?? null;
99
- }
100
-
101
- // ============================================================================
102
- // Module Configuration Actions
103
- // ============================================================================
104
-
105
- @Action(SetModuleInfo)
106
- setModuleInfo(
107
- ctx: StateContext<FormBuilderStateModel>,
108
- action: SetModuleInfo,
109
- ) {
110
- let parentPath = '';
111
- if (action.parentModuleType && action.parentModuleId) {
112
- parentPath = `/${action.parentModuleType}/${action.parentModuleId}`;
113
- } else if (action.parentPath) {
114
- parentPath = action.parentPath;
115
- }
116
- ctx.patchState({
117
- moduleType: action.moduleType,
118
- moduleId: action.moduleId,
119
- parentModuleType: action.parentModuleType ?? null,
120
- parentModuleId: action.parentModuleId ?? null,
121
- parentPath: parentPath ?? '',
122
- });
123
- }
124
-
125
- @Action(ResetFormBuilderState)
126
- resetState(ctx: StateContext<FormBuilderStateModel>) {
127
- ctx.setState(DEFAULT_STATE);
128
- }
129
-
130
- // ============================================================================
131
- // Form Configuration Actions
132
- // ============================================================================
133
-
134
- @Action(GetFormConfiguration)
135
- getFormConfiguration(ctx: StateContext<FormBuilderStateModel>) {
136
- const state = ctx.getState();
137
- const apiPath = this.getApiPath(state);
138
- const req$ = this.http.get<Response<FormConfiguration>>(apiPath);
139
-
140
- return this.load(ctx, {
141
- key: FormBuilderActionKey.GetFormConfiguration,
142
- request$: req$,
143
- updateState: (_state, data) => ({
144
- formConfiguration: data ?? null,
145
- }),
146
- });
147
- }
148
-
149
- @Action(ResetFormConfiguration)
150
- resetFormConfiguration(ctx: StateContext<FormBuilderStateModel>) {
151
- const state = ctx.getState();
152
- const apiPath = `${this.getApiPath(state)}/reset`;
153
- const req$ = this.http.delete<Response<FormConfiguration>>(apiPath);
154
-
155
- return handleApiRequest({
156
- ctx,
157
- key: FormBuilderActionKey.ResetFormConfiguration,
158
- request$: req$,
159
- onSuccess: (res, _currentState) => ({
160
- formConfiguration: res.data ?? null,
161
- }),
162
- });
163
- }
164
-
165
- // ============================================================================
166
- // Section Actions
167
- // ============================================================================
168
-
169
- @Action(AddSection)
170
- addSection(ctx: StateContext<FormBuilderStateModel>, action: AddSection) {
171
- const state = ctx.getState();
172
- const apiPath = `${this.getApiPath(state)}/sections`;
173
- const req$ = this.http.post<Response<FormSection>>(apiPath, action.payload);
174
-
175
- return handleApiRequest({
176
- ctx,
177
- key: FormBuilderActionKey.AddSection,
178
- request$: req$,
179
- onSuccess: (res, currentState) => {
180
- const sections = this.adapter.addOne(
181
- currentState.formConfiguration?.sections ?? [],
182
- res.data,
183
- );
184
- return {
185
- formConfiguration: {
186
- ...currentState.formConfiguration!,
187
- sections,
188
- },
189
- };
190
- },
191
- });
192
- }
193
-
194
- @Action(UpdateSection)
195
- updateSection(
196
- ctx: StateContext<FormBuilderStateModel>,
197
- action: UpdateSection,
198
- ) {
199
- const state = ctx.getState();
200
- const apiPath = `${this.getApiPath(state)}/sections/${action.sectionId}`;
201
- const req$ = this.http.put<Response<FormSection>>(apiPath, action.payload);
202
-
203
- return handleApiRequest({
204
- ctx,
205
- key: FormBuilderActionKey.UpdateSection,
206
- request$: req$,
207
- onSuccess: (res, currentState) => {
208
- const sections = this.adapter.upsertOne(
209
- currentState.formConfiguration?.sections ?? [],
210
- res.data,
211
- 'id',
212
- );
213
- return {
214
- formConfiguration: {
215
- ...currentState.formConfiguration!,
216
- sections,
217
- },
218
- };
219
- },
220
- });
221
- }
222
-
223
- @Action(DeleteSection)
224
- deleteSection(
225
- ctx: StateContext<FormBuilderStateModel>,
226
- action: DeleteSection,
227
- ) {
228
- const state = ctx.getState();
229
- const apiPath = `${this.getApiPath(state)}/sections/${action.sectionId}`;
230
- const req$ = this.http.delete<Response<{ id: string }>>(apiPath);
231
-
232
- return handleApiRequest({
233
- ctx,
234
- key: FormBuilderActionKey.DeleteSection,
235
- request$: req$,
236
- onSuccess: (res, currentState) => {
237
- const sections = this.adapter.removeOne(
238
- currentState.formConfiguration?.sections ?? [],
239
- res.data.id,
240
- 'id',
241
- );
242
- return {
243
- formConfiguration: {
244
- ...currentState.formConfiguration!,
245
- sections,
246
- },
247
- };
248
- },
249
- });
250
- }
251
-
252
- // ============================================================================
253
- // Field Actions
254
- // ============================================================================
255
-
256
- @Action(AddField)
257
- addField(ctx: StateContext<FormBuilderStateModel>, action: AddField) {
258
- const state = ctx.getState();
259
- const apiPath = `${this.getApiPath(state)}/sections/${action.sectionId}/fields`;
260
- const req$ = this.http.post<Response<FormField>>(apiPath, action.payload);
261
-
262
- // Generate temp ID for optimistic update
263
- const tempId = `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
264
- const tempField: FormField = {
265
- id: tempId,
266
- sectionId: action.sectionId,
267
- propertyId: action.payload.propertyId,
268
- width: action.payload.width,
269
- order: action.payload.order ?? 0,
270
- hiddenInCreation: action.payload.hiddenInCreation,
271
- _pending: true,
272
- };
273
-
274
- // Optimistically add the temp field
275
- const sectionsWithTemp = (state.formConfiguration?.sections ?? []).map(
276
- (section) => {
277
- if (section.id === action.sectionId) {
278
- const fields = [...section.fields];
279
- const insertIndex = action.payload.order ?? fields.length;
280
- fields.splice(insertIndex, 0, tempField);
281
- return { ...section, fields };
282
- }
283
- return section;
284
- },
285
- );
286
- ctx.patchState({
287
- formConfiguration: {
288
- ...state.formConfiguration!,
289
- sections: sectionsWithTemp,
290
- },
291
- });
292
-
293
- return handleApiRequest({
294
- ctx,
295
- key: FormBuilderActionKey.AddField,
296
- request$: req$,
297
- onSuccess: (res, currentState) => {
298
- const newField = res.data;
299
- // Replace temp field with real field
300
- const sections = (currentState.formConfiguration?.sections ?? []).map(
301
- (section) => {
302
- if (section.id === action.sectionId) {
303
- return {
304
- ...section,
305
- fields: section.fields.map((f) =>
306
- f.id === tempId ? newField : f,
307
- ),
308
- };
309
- }
310
- return section;
311
- },
312
- );
313
- return {
314
- formConfiguration: {
315
- ...currentState.formConfiguration!,
316
- sections,
317
- },
318
- };
319
- },
320
- onError: (_error, currentState) => {
321
- // Remove temp field on error
322
- const sections = (currentState.formConfiguration?.sections ?? []).map(
323
- (section) => {
324
- if (section.id === action.sectionId) {
325
- return {
326
- ...section,
327
- fields: section.fields.filter((f) => f.id !== tempId),
328
- };
329
- }
330
- return section;
331
- },
332
- );
333
- return {
334
- formConfiguration: {
335
- ...currentState.formConfiguration!,
336
- sections,
337
- },
338
- };
339
- },
340
- });
341
- }
342
-
343
- @Action(UpdateField)
344
- updateField(ctx: StateContext<FormBuilderStateModel>, action: UpdateField) {
345
- const state = ctx.getState();
346
- const apiPath = `${this.getApiPath(state)}/sections/${action.sectionId}/fields/${action.fieldId}`;
347
- const req$ = this.http.put<Response<FormField>>(apiPath, action.payload);
348
-
349
- // Apply optimistic update for order changes
350
- if (action.payload.order !== undefined) {
351
- const newOrder = action.payload.order;
352
- const sections = (state.formConfiguration?.sections ?? []).map(
353
- (section) => {
354
- if (section.id === action.sectionId) {
355
- const fields = [...section.fields];
356
- const currentIndex = fields.findIndex(
357
- (f) => f.id === action.fieldId,
358
- );
359
- if (currentIndex !== -1) {
360
- const [movedField] = fields.splice(currentIndex, 1);
361
- fields.splice(newOrder, 0, movedField);
362
- }
363
- return { ...section, fields };
364
- }
365
- return section;
366
- },
367
- );
368
- ctx.patchState({
369
- formConfiguration: {
370
- ...state.formConfiguration!,
371
- sections,
372
- },
373
- });
374
- }
375
-
376
- return handleApiRequest({
377
- ctx,
378
- key: FormBuilderActionKey.UpdateField,
379
- request$: req$,
380
- onSuccess: (res, currentState) => {
381
- const updatedField = res.data;
382
- const sections = (currentState.formConfiguration?.sections ?? []).map(
383
- (section) => {
384
- if (section.id === action.sectionId) {
385
- // Update the field and sort by order
386
- const updatedFields = section.fields.map((f) =>
387
- f.id === updatedField.id ? updatedField : f,
388
- );
389
- return {
390
- ...section,
391
- fields: updatedFields.sort((a, b) => a.order - b.order),
392
- };
393
- }
394
- return section;
395
- },
396
- );
397
- return {
398
- formConfiguration: {
399
- ...currentState.formConfiguration!,
400
- sections,
401
- },
402
- };
403
- },
404
- onError: (_error, _currentState) => {
405
- // Revert optimistic update on error - reload original state
406
- if (action.payload.order !== undefined) {
407
- return {
408
- formConfiguration: state.formConfiguration,
409
- };
410
- }
411
- return {};
412
- },
413
- });
414
- }
415
-
416
- @Action(DeleteField)
417
- deleteField(ctx: StateContext<FormBuilderStateModel>, action: DeleteField) {
418
- const state = ctx.getState();
419
- const apiPath = `${this.getApiPath(state)}/sections/${action.sectionId}/fields/${action.fieldId}`;
420
- const req$ =
421
- this.http.delete<Response<{ id: string; sectionId: string }>>(apiPath);
422
-
423
- // Optimistically mark field as deleting
424
- const sectionsWithDeleting = (state.formConfiguration?.sections ?? []).map(
425
- (section) => {
426
- if (section.id === action.sectionId) {
427
- return {
428
- ...section,
429
- fields: section.fields.map((f) =>
430
- f.id === action.fieldId ? { ...f, _deleting: true } : f,
431
- ),
432
- };
433
- }
434
- return section;
435
- },
436
- );
437
- ctx.patchState({
438
- formConfiguration: {
439
- ...state.formConfiguration!,
440
- sections: sectionsWithDeleting,
441
- },
442
- });
443
-
444
- return handleApiRequest({
445
- ctx,
446
- key: FormBuilderActionKey.DeleteField,
447
- request$: req$,
448
- onSuccess: (res, currentState) => {
449
- const { id: deletedId, sectionId } = res.data;
450
- const sections = (currentState.formConfiguration?.sections ?? []).map(
451
- (section) => {
452
- if (section.id === sectionId) {
453
- return {
454
- ...section,
455
- fields: section.fields.filter((f) => f.id !== deletedId),
456
- };
457
- }
458
- return section;
459
- },
460
- );
461
- return {
462
- formConfiguration: {
463
- ...currentState.formConfiguration!,
464
- sections,
465
- },
466
- };
467
- },
468
- onError: (_error, currentState) => {
469
- // Remove _deleting flag on error
470
- const sections = (currentState.formConfiguration?.sections ?? []).map(
471
- (section) => {
472
- if (section.id === action.sectionId) {
473
- return {
474
- ...section,
475
- fields: section.fields.map((f) =>
476
- f.id === action.fieldId ? { ...f, _deleting: false } : f,
477
- ),
478
- };
479
- }
480
- return section;
481
- },
482
- );
483
- return {
484
- formConfiguration: {
485
- ...currentState.formConfiguration!,
486
- sections,
487
- },
488
- };
489
- },
490
- });
491
- }
492
-
493
- @Action(MoveField)
494
- moveField(ctx: StateContext<FormBuilderStateModel>, action: MoveField) {
495
- const state = ctx.getState();
496
- const apiPath = `${this.getApiPath(state)}/sections/${action.sectionId}/fields/${action.fieldId}/move`;
497
- const req$ = this.http.put<Response<FormField>>(apiPath, action.payload);
498
-
499
- // Apply optimistic update - move field immediately
500
- const sourceSectionId = action.sectionId;
501
- const targetSectionId = action.payload.targetSectionId;
502
- const targetOrder = action.payload.order ?? 0;
503
-
504
- let movedFieldData: FormField | null = null;
505
- const optimisticSections = (state.formConfiguration?.sections ?? []).map(
506
- (section) => {
507
- if (section.id === sourceSectionId) {
508
- const fieldToMove = section.fields.find(
509
- (f) => f.id === action.fieldId,
510
- );
511
- if (fieldToMove) {
512
- movedFieldData = { ...fieldToMove, order: targetOrder };
513
- }
514
- return {
515
- ...section,
516
- fields: section.fields.filter((f) => f.id !== action.fieldId),
517
- };
518
- }
519
- return section;
520
- },
521
- );
522
-
523
- // Add to target section
524
- const sectionsWithMoved = optimisticSections.map((section) => {
525
- if (section.id === targetSectionId && movedFieldData) {
526
- const fields = [...section.fields];
527
- fields.splice(targetOrder, 0, movedFieldData);
528
- return { ...section, fields };
529
- }
530
- return section;
531
- });
532
-
533
- ctx.patchState({
534
- formConfiguration: {
535
- ...state.formConfiguration!,
536
- sections: sectionsWithMoved,
537
- },
538
- });
539
-
540
- return handleApiRequest({
541
- ctx,
542
- key: FormBuilderActionKey.MoveField,
543
- request$: req$,
544
- onSuccess: (res, currentState) => {
545
- const movedField = res.data;
546
- const sections = (currentState.formConfiguration?.sections ?? []).map(
547
- (section) => {
548
- // Update the moved field with server response
549
- if (section.id === targetSectionId) {
550
- return {
551
- ...section,
552
- fields: section.fields
553
- .map((f) => (f.id === movedField.id ? movedField : f))
554
- .sort((a, b) => a.order - b.order),
555
- };
556
- }
557
- return section;
558
- },
559
- );
560
- return {
561
- formConfiguration: {
562
- ...currentState.formConfiguration!,
563
- sections,
564
- },
565
- };
566
- },
567
- onError: (_error, _currentState) => {
568
- // Revert optimistic update on error
569
- return {
570
- formConfiguration: state.formConfiguration,
571
- };
572
- },
573
- });
574
- }
575
- }
@@ -1,5 +0,0 @@
1
- export * from './api.model';
2
- export * from './form-builder.actions';
3
- export * from './form-builder.facade';
4
- export * from './form-builder.model';
5
- export * from './form-builder.state';
package/tsconfig.json DELETED
@@ -1,31 +0,0 @@
1
- {
2
- "extends": "../../../tsconfig.json",
3
- "compilerOptions": {
4
- "paths": {
5
- "@masterteam/components": [
6
- "../../../dist/masterteam/components"
7
- ],
8
- "@masterteam/components/*": [
9
- "../../../dist/masterteam/components/*"
10
- ],
11
- "@masterteam/forms": [
12
- "../../../dist/masterteam/forms"
13
- ],
14
- "@masterteam/forms/*": [
15
- "../../../dist/masterteam/forms/*"
16
- ],
17
- "@masterteam/icons": [
18
- "../../../dist/masterteam/icons"
19
- ],
20
- "@masterteam/properties": [
21
- "../../../dist/masterteam/properties"
22
- ]
23
- }
24
- },
25
- "angularCompilerOptions": {
26
- "compilationMode": "partial"
27
- },
28
- "exclude": [
29
- "node_modules"
30
- ]
31
- }