@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,208 +0,0 @@
1
- import {
2
- Component,
3
- computed,
4
- inject,
5
- input,
6
- signal,
7
- effect,
8
- } from '@angular/core';
9
- import { CommonModule } from '@angular/common';
10
- import { FormsModule } from '@angular/forms';
11
- import { TabsModule } from 'primeng/tabs';
12
- import { Button } from '@masterteam/components/button';
13
- import { Card } from '@masterteam/components/card';
14
- import { ModalService } from '@masterteam/components/modal';
15
- import { ConfirmationService } from '@masterteam/components/confirmation';
16
- import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
17
- import { FBSection } from './fb-section/fb-section';
18
- import {
19
- EnrichedFormSection,
20
- EnrichedFormField,
21
- PropertyItem,
22
- } from './form-builder.model';
23
- import type { FieldWidth } from '../store/form-builder/form-builder.model';
24
- import { CdkDragDrop, DragDropModule } from '@angular/cdk/drag-drop';
25
- import { FBSectionForm } from './fb-section-form/fb-section-form';
26
- import { FBPreviewForm } from './fb-preview-form/fb-preview-form';
27
- import { FormBuilderFacade } from '../store/form-builder/form-builder.facade';
28
-
29
- @Component({
30
- selector: 'mt-form-builder',
31
- standalone: true,
32
- imports: [
33
- CommonModule,
34
- FormsModule,
35
- TabsModule,
36
- Button,
37
- Card,
38
- TranslocoDirective,
39
- FBSection,
40
- DragDropModule,
41
- ],
42
- templateUrl: './form-builder.html',
43
- styleUrl: './form-builder.scss',
44
- })
45
- export class FormBuilder {
46
- private readonly modalService = inject(ModalService);
47
- private readonly confirmationService = inject(ConfirmationService);
48
- private readonly translocoService = inject(TranslocoService);
49
- protected readonly facade = inject(FormBuilderFacade);
50
-
51
- private dialogRef: any;
52
-
53
- // Inputs - only properties needed from parent
54
- readonly properties = input<PropertyItem[]>([]);
55
-
56
- // Local UI state
57
- readonly activeTab = signal<string>('system');
58
-
59
- // State from facade
60
- readonly sections = this.facade.sections;
61
- readonly isLoading = this.facade.isLoadingFormConfiguration;
62
- readonly error = this.facade.formConfigurationError;
63
-
64
- // Properties map for enrichment
65
- private readonly propertiesMap = computed(() => {
66
- const map = new Map<number, PropertyItem>();
67
- for (const prop of this.properties()) {
68
- map.set(prop.id, prop);
69
- }
70
- return map;
71
- });
72
-
73
- // Enrich sections with property data for UI display
74
- readonly enrichedSections = computed<EnrichedFormSection[]>(() => {
75
- const sections = this.sections();
76
- const propsMap = this.propertiesMap();
77
- const lang = document.documentElement.lang as 'en' | 'ar';
78
-
79
- return sections.map((section) => ({
80
- ...section,
81
- fields: section.fields.map((field): EnrichedFormField => {
82
- const prop = propsMap.get(field.propertyId);
83
- const propName = prop?.name;
84
- const name =
85
- typeof propName === 'string'
86
- ? propName
87
- : (propName?.[lang] ??
88
- propName?.['en'] ??
89
- `Property ${field.propertyId}`);
90
-
91
- return {
92
- ...field,
93
- name,
94
- type: prop?.viewType || 'text',
95
- data: prop,
96
- };
97
- }),
98
- }));
99
- });
100
-
101
- // Available properties for toolbox tabs
102
- readonly availableTabs = computed(() => {
103
- const tabs: Array<{
104
- id: string;
105
- title: string;
106
- properties: PropertyItem[];
107
- }> = [];
108
- const usedPropertyIds = new Set(
109
- this.sections().flatMap((s) => s.fields.map((f) => f.propertyId)),
110
- );
111
-
112
- const availableProps = this.properties().filter(
113
- (p) => !usedPropertyIds.has(p.id),
114
- );
115
-
116
- const systemProps = availableProps.filter((p) => p.isSystem);
117
- const customProps = availableProps.filter((p) => !p.isSystem);
118
-
119
- if (systemProps.length > 0) {
120
- tabs.push({ id: 'system', title: 'System', properties: systemProps });
121
- }
122
- if (customProps.length > 0) {
123
- tabs.push({ id: 'custom', title: 'Custom', properties: customProps });
124
- }
125
-
126
- return tabs;
127
- });
128
-
129
- constructor() {
130
- // Update active tab when tabs change
131
- effect(() => {
132
- const tabs = this.availableTabs();
133
- const currentTab = this.activeTab();
134
- if (tabs.length > 0 && !tabs.some((t) => t.id === currentTab)) {
135
- this.activeTab.set(tabs[0].id);
136
- }
137
- });
138
- }
139
-
140
- drop(event: CdkDragDrop<EnrichedFormField[] | PropertyItem[]>): void {
141
- const targetSectionId = event.container.id;
142
-
143
- if (event.previousContainer === event.container) {
144
- // Reordering within the same section
145
- const field = event.item.data as EnrichedFormField;
146
-
147
- // Update the field order via API
148
- this.facade.updateField(targetSectionId, field.id, {
149
- order: event.currentIndex,
150
- });
151
- } else if (event.previousContainer.id.startsWith('toolbox-')) {
152
- // Adding from toolbox
153
- const propertyItem = event.item.data as PropertyItem;
154
- this.facade.addField(targetSectionId, {
155
- propertyId: propertyItem.id,
156
- width: '100' as FieldWidth,
157
- order: event.currentIndex,
158
- hiddenInCreation: false,
159
- });
160
- } else {
161
- // Moving between sections
162
- const sourceSectionId = event.previousContainer.id;
163
- const field = event.item.data as EnrichedFormField;
164
- this.facade.moveField(sourceSectionId, field.id, {
165
- targetSectionId,
166
- order: event.currentIndex,
167
- });
168
- }
169
- }
170
-
171
- addSection(): void {
172
- this.dialogRef = this.modalService.openModal(FBSectionForm, 'drawer', {
173
- header: this.translocoService.translate('formBuilder.add-section'),
174
- height: '20vw',
175
- styleClass: '!w-100 !absolute ',
176
- position: 'end',
177
- appendTo: '#page-content',
178
- modal: true,
179
- inputValues: {
180
- sectionsCount: this.sections().length,
181
- },
182
- });
183
- }
184
-
185
- openPreview(): void {
186
- this.dialogRef = this.modalService.openModal(FBPreviewForm, 'drawer', {
187
- header: this.translocoService.translate('formBuilder.preview'),
188
- styleClass: '!w-[80vw] ',
189
- position: 'end',
190
- modal: true,
191
- inputValues: {
192
- sections: this.enrichedSections(),
193
- },
194
- });
195
- }
196
-
197
- resetFormConfiguration(): void {
198
- this.confirmationService.confirm({
199
- type: 'dialog',
200
- acceptButtonStyleClass: 'p-button-danger',
201
- accept: () => {
202
- this.facade.resetFormConfiguration();
203
- },
204
- });
205
- }
206
-
207
- noReturnPredicate = (): boolean => false;
208
- }
package/src/public-api.ts DELETED
@@ -1,6 +0,0 @@
1
- /*
2
- * Public API Surface of form-builder
3
- */
4
-
5
- export * from './lib/form-builder';
6
- export * from './store/form-builder';
@@ -1,13 +0,0 @@
1
- /**
2
- * Standard API Response wrapper
3
- */
4
- export interface Response<T> {
5
- endpoint: string;
6
- status: number;
7
- code: number;
8
- locale: string;
9
- message?: string | null;
10
- errors?: any | null;
11
- data: T;
12
- cacheSession?: string;
13
- }
@@ -1,98 +0,0 @@
1
- import type {
2
- AddFieldPayload,
3
- AddSectionPayload,
4
- MoveFieldPayload,
5
- UpdateFieldPayload,
6
- UpdateSectionPayload,
7
- } from './form-builder.model';
8
-
9
- // ============================================================================
10
- // Module Configuration Actions
11
- // ============================================================================
12
-
13
- export class SetModuleInfo {
14
- static readonly type = '[FormBuilder] Set Module Info';
15
- constructor(
16
- public moduleType: string,
17
- public moduleId: string | number,
18
- public parentModuleType?: string,
19
- public parentModuleId?: string | number,
20
- public parentPath?: string,
21
- ) {}
22
- }
23
-
24
- export class ResetFormBuilderState {
25
- static readonly type = '[FormBuilder] Reset State';
26
- }
27
-
28
- // ============================================================================
29
- // Form Configuration Actions
30
- // ============================================================================
31
-
32
- export class GetFormConfiguration {
33
- static readonly type = '[FormBuilder] Get Form Configuration';
34
- }
35
-
36
- export class ResetFormConfiguration {
37
- static readonly type = '[FormBuilder] Reset Form Configuration';
38
- }
39
-
40
- // ============================================================================
41
- // Section Actions
42
- // ============================================================================
43
-
44
- export class AddSection {
45
- static readonly type = '[FormBuilder] Add Section';
46
- constructor(public payload: AddSectionPayload) {}
47
- }
48
-
49
- export class UpdateSection {
50
- static readonly type = '[FormBuilder] Update Section';
51
- constructor(
52
- public sectionId: string,
53
- public payload: UpdateSectionPayload,
54
- ) {}
55
- }
56
-
57
- export class DeleteSection {
58
- static readonly type = '[FormBuilder] Delete Section';
59
- constructor(public sectionId: string) {}
60
- }
61
-
62
- // ============================================================================
63
- // Field Actions
64
- // ============================================================================
65
-
66
- export class AddField {
67
- static readonly type = '[FormBuilder] Add Field';
68
- constructor(
69
- public sectionId: string,
70
- public payload: AddFieldPayload,
71
- ) {}
72
- }
73
-
74
- export class UpdateField {
75
- static readonly type = '[FormBuilder] Update Field';
76
- constructor(
77
- public sectionId: string,
78
- public fieldId: string,
79
- public payload: UpdateFieldPayload,
80
- ) {}
81
- }
82
-
83
- export class DeleteField {
84
- static readonly type = '[FormBuilder] Delete Field';
85
- constructor(
86
- public sectionId: string,
87
- public fieldId: string,
88
- ) {}
89
- }
90
-
91
- export class MoveField {
92
- static readonly type = '[FormBuilder] Move Field';
93
- constructor(
94
- public sectionId: string,
95
- public fieldId: string,
96
- public payload: MoveFieldPayload,
97
- ) {}
98
- }
@@ -1,194 +0,0 @@
1
- import { computed, Injectable, inject } from '@angular/core';
2
- import { Store, select } from '@ngxs/store';
3
-
4
- import {
5
- AddFieldPayload,
6
- AddSectionPayload,
7
- FormBuilderActionKey,
8
- MoveFieldPayload,
9
- UpdateFieldPayload,
10
- UpdateSectionPayload,
11
- } from './form-builder.model';
12
- import {
13
- AddField,
14
- AddSection,
15
- DeleteField,
16
- DeleteSection,
17
- GetFormConfiguration,
18
- MoveField,
19
- ResetFormBuilderState,
20
- ResetFormConfiguration,
21
- SetModuleInfo,
22
- UpdateField,
23
- UpdateSection,
24
- } from './form-builder.actions';
25
- import { FormBuilderState } from './form-builder.state';
26
-
27
- @Injectable({ providedIn: 'root' })
28
- export class FormBuilderFacade {
29
- private readonly store = inject(Store);
30
-
31
- // ============================================================================
32
- // State Selectors
33
- // ============================================================================
34
-
35
- private readonly stateSignal = select(FormBuilderState.getState);
36
-
37
- readonly formConfiguration = select(FormBuilderState.getFormConfiguration);
38
- readonly sections = select(FormBuilderState.getSections);
39
- readonly moduleType = select(FormBuilderState.getModuleType);
40
- readonly moduleId = select(FormBuilderState.getModuleId);
41
-
42
- // ============================================================================
43
- // Loading Signals
44
- // ============================================================================
45
-
46
- readonly isLoadingFormConfiguration = computed(() =>
47
- this.stateSignal().loadingActive.includes(
48
- FormBuilderActionKey.GetFormConfiguration,
49
- ),
50
- );
51
-
52
- readonly isResettingFormConfiguration = computed(() =>
53
- this.stateSignal().loadingActive.includes(
54
- FormBuilderActionKey.ResetFormConfiguration,
55
- ),
56
- );
57
-
58
- readonly isAddingSection = computed(() =>
59
- this.stateSignal().loadingActive.includes(FormBuilderActionKey.AddSection),
60
- );
61
-
62
- readonly isUpdatingSection = computed(() =>
63
- this.stateSignal().loadingActive.includes(
64
- FormBuilderActionKey.UpdateSection,
65
- ),
66
- );
67
-
68
- readonly isDeletingSection = computed(() =>
69
- this.stateSignal().loadingActive.includes(
70
- FormBuilderActionKey.DeleteSection,
71
- ),
72
- );
73
-
74
- readonly isAddingField = computed(() =>
75
- this.stateSignal().loadingActive.includes(FormBuilderActionKey.AddField),
76
- );
77
-
78
- readonly isUpdatingField = computed(() =>
79
- this.stateSignal().loadingActive.includes(FormBuilderActionKey.UpdateField),
80
- );
81
-
82
- readonly isDeletingField = computed(() =>
83
- this.stateSignal().loadingActive.includes(FormBuilderActionKey.DeleteField),
84
- );
85
-
86
- readonly isMovingField = computed(() =>
87
- this.stateSignal().loadingActive.includes(FormBuilderActionKey.MoveField),
88
- );
89
-
90
- // ============================================================================
91
- // Error Signals
92
- // ============================================================================
93
-
94
- readonly formConfigurationError = computed(
95
- () =>
96
- this.stateSignal().errors[FormBuilderActionKey.GetFormConfiguration] ??
97
- null,
98
- );
99
-
100
- readonly sectionError = computed(() => {
101
- const errors = this.stateSignal().errors;
102
- return (
103
- errors[FormBuilderActionKey.AddSection] ??
104
- errors[FormBuilderActionKey.UpdateSection] ??
105
- errors[FormBuilderActionKey.DeleteSection] ??
106
- null
107
- );
108
- });
109
-
110
- readonly fieldError = computed(() => {
111
- const errors = this.stateSignal().errors;
112
- return (
113
- errors[FormBuilderActionKey.AddField] ??
114
- errors[FormBuilderActionKey.UpdateField] ??
115
- errors[FormBuilderActionKey.DeleteField] ??
116
- errors[FormBuilderActionKey.MoveField] ??
117
- null
118
- );
119
- });
120
-
121
- // ============================================================================
122
- // Module Configuration Dispatchers
123
- // ============================================================================
124
-
125
- setModuleInfo(
126
- moduleType: string,
127
- moduleId: string | number,
128
- parentModuleType?: string,
129
- parentModuleId?: string | number,
130
- parentPath?: string,
131
- ) {
132
- return this.store.dispatch(
133
- new SetModuleInfo(
134
- moduleType,
135
- moduleId,
136
- parentModuleType,
137
- parentModuleId,
138
- parentPath,
139
- ),
140
- );
141
- }
142
-
143
- resetState() {
144
- return this.store.dispatch(new ResetFormBuilderState());
145
- }
146
-
147
- // ============================================================================
148
- // Form Configuration Dispatchers
149
- // ============================================================================
150
-
151
- getFormConfiguration() {
152
- return this.store.dispatch(new GetFormConfiguration());
153
- }
154
-
155
- resetFormConfiguration() {
156
- return this.store.dispatch(new ResetFormConfiguration());
157
- }
158
-
159
- // ============================================================================
160
- // Section Dispatchers
161
- // ============================================================================
162
-
163
- addSection(payload: AddSectionPayload) {
164
- return this.store.dispatch(new AddSection(payload));
165
- }
166
-
167
- updateSection(sectionId: string, payload: UpdateSectionPayload) {
168
- return this.store.dispatch(new UpdateSection(sectionId, payload));
169
- }
170
-
171
- deleteSection(sectionId: string) {
172
- return this.store.dispatch(new DeleteSection(sectionId));
173
- }
174
-
175
- // ============================================================================
176
- // Field Dispatchers
177
- // ============================================================================
178
-
179
- addField(sectionId: string, payload: AddFieldPayload) {
180
- return this.store.dispatch(new AddField(sectionId, payload));
181
- }
182
-
183
- updateField(sectionId: string, fieldId: string, payload: UpdateFieldPayload) {
184
- return this.store.dispatch(new UpdateField(sectionId, fieldId, payload));
185
- }
186
-
187
- deleteField(sectionId: string, fieldId: string) {
188
- return this.store.dispatch(new DeleteField(sectionId, fieldId));
189
- }
190
-
191
- moveField(sectionId: string, fieldId: string, payload: MoveFieldPayload) {
192
- return this.store.dispatch(new MoveField(sectionId, fieldId, payload));
193
- }
194
- }
@@ -1,112 +0,0 @@
1
- import type { LoadingStateShape } from '@masterteam/components';
2
-
3
- // ============================================================================
4
- // Action Keys Enum
5
- // ============================================================================
6
- export enum FormBuilderActionKey {
7
- // Form Configuration
8
- GetFormConfiguration = 'getFormConfiguration',
9
- ResetFormConfiguration = 'resetFormConfiguration',
10
-
11
- // Section CRUD
12
- AddSection = 'addSection',
13
- UpdateSection = 'updateSection',
14
- DeleteSection = 'deleteSection',
15
-
16
- // Field CRUD
17
- AddField = 'addField',
18
- UpdateField = 'updateField',
19
- DeleteField = 'deleteField',
20
- MoveField = 'moveField',
21
- }
22
-
23
- // ============================================================================
24
- // API Models
25
- // ============================================================================
26
-
27
- export type FieldWidth = '25' | '50' | '100';
28
-
29
- export interface FormField {
30
- id: string;
31
- sectionId: string;
32
- propertyId: number;
33
- width: FieldWidth;
34
- order: number;
35
- hiddenInCreation?: boolean;
36
- hiddenInEditForm?: boolean;
37
- isRequired?: boolean;
38
- showConditionalDisplayFormula?: boolean;
39
- conditionalDisplayFormula?: string;
40
- _pending?: boolean; // Optimistic: field is being added
41
- _deleting?: boolean; // Optimistic: field is being deleted
42
- }
43
-
44
- export interface FormSection {
45
- id: string;
46
- name: {
47
- en: string;
48
- ar: string;
49
- };
50
- order: number;
51
- fields: FormField[];
52
- }
53
-
54
- export interface FormConfiguration {
55
- isActive: boolean;
56
- sections: FormSection[];
57
- }
58
-
59
- // ============================================================================
60
- // Request Payloads
61
- // ============================================================================
62
-
63
- export interface AddSectionPayload {
64
- name: { en: string; ar: string };
65
- order?: number;
66
- }
67
-
68
- export interface UpdateSectionPayload {
69
- name?: { en: string; ar: string };
70
- order?: number;
71
- }
72
-
73
- export interface AddFieldPayload {
74
- propertyId: number;
75
- width: FieldWidth;
76
- order?: number;
77
- hiddenInCreation?: boolean;
78
- hiddenInEditForm?: boolean;
79
- isRequired?: boolean;
80
- showConditionalDisplayFormula?: boolean;
81
- conditionalDisplayFormula?: string;
82
- }
83
-
84
- export interface UpdateFieldPayload {
85
- width?: FieldWidth;
86
- order?: number;
87
- hiddenInCreation?: boolean;
88
- hiddenInEditForm?: boolean;
89
- isRequired?: boolean;
90
- showConditionalDisplayFormula?: boolean;
91
- conditionalDisplayFormula?: string | null;
92
- }
93
-
94
- export interface MoveFieldPayload {
95
- targetSectionId: string;
96
- order?: number;
97
- }
98
-
99
- // ============================================================================
100
- // State Model
101
- // ============================================================================
102
- export interface FormBuilderStateModel extends LoadingStateShape<FormBuilderActionKey> {
103
- // Module configuration
104
- moduleType: string | null;
105
- moduleId: string | number | null;
106
- parentModuleType: string | null;
107
- parentModuleId: string | number | null;
108
- parentPath: string;
109
-
110
- // Form data
111
- formConfiguration: FormConfiguration | null;
112
- }