@masterteam/form-builder 0.0.2 → 0.0.4

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 (33) hide show
  1. package/assets/form-builder.css +2 -4
  2. package/fesm2022/masterteam-form-builder.mjs +1908 -0
  3. package/fesm2022/masterteam-form-builder.mjs.map +1 -0
  4. package/package.json +16 -16
  5. package/types/masterteam-form-builder.d.ts +299 -0
  6. package/.angular/cache/21.0.2/ng-packagr/db70d8f07b5a2d2d1c3124ca92e8d56d14fb894dce4d4867ba7c0db29ba913a3 +0 -1
  7. package/.angular/cache/21.0.2/ng-packagr/tsbuildinfo/masterteam-form-builder.tsbuildinfo +0 -1
  8. package/BACKEND_API_SPEC.md +0 -338
  9. package/angular.json +0 -26
  10. package/ng-package.json +0 -13
  11. package/src/lib/fb-field-conditions/condition-constants.ts +0 -262
  12. package/src/lib/fb-field-conditions/fb-field-conditions.html +0 -35
  13. package/src/lib/fb-field-conditions/fb-field-conditions.ts +0 -123
  14. package/src/lib/fb-field-form/fb-field-form.html +0 -59
  15. package/src/lib/fb-field-form/fb-field-form.ts +0 -250
  16. package/src/lib/fb-preview-form/fb-preview-form.html +0 -31
  17. package/src/lib/fb-preview-form/fb-preview-form.ts +0 -147
  18. package/src/lib/fb-section/fb-section.html +0 -130
  19. package/src/lib/fb-section/fb-section.ts +0 -211
  20. package/src/lib/fb-section-form/fb-section-form.html +0 -38
  21. package/src/lib/fb-section-form/fb-section-form.ts +0 -128
  22. package/src/lib/form-builder.html +0 -166
  23. package/src/lib/form-builder.model.ts +0 -27
  24. package/src/lib/form-builder.scss +0 -20
  25. package/src/lib/form-builder.ts +0 -208
  26. package/src/public-api.ts +0 -6
  27. package/src/store/form-builder/api.model.ts +0 -13
  28. package/src/store/form-builder/form-builder.actions.ts +0 -113
  29. package/src/store/form-builder/form-builder.facade.ts +0 -207
  30. package/src/store/form-builder/form-builder.model.ts +0 -147
  31. package/src/store/form-builder/form-builder.state.ts +0 -668
  32. package/src/store/form-builder/index.ts +0 -5
  33. package/tsconfig.json +0 -31
@@ -1,130 +0,0 @@
1
- <ng-container *transloco="let t; prefix: 'formBuilder'">
2
- <div class="flex flex-col">
3
- <div
4
- class="flex justify-between items-center bg-primary text-primary-contrast p-2 rounded-xl"
5
- >
6
- <mt-button
7
- size="small"
8
- icon="arrow.chevron-down"
9
- class="transition-[rotate]"
10
- [class.rotate-180]="expanded()"
11
- (onClick)="toggleExpanded()"
12
- ></mt-button>
13
- <span class="font-bold">
14
- {{ sectionName() }}
15
- </span>
16
- <div class="flex gap-2">
17
- <mt-button
18
- size="small"
19
- icon="general.edit-02"
20
- [tooltip]="t('edit-section')"
21
- (onClick)="editSection($event)"
22
- ></mt-button>
23
- </div>
24
- </div>
25
- @if (expanded()) {
26
- <div
27
- cdkDropList
28
- [id]="section().id"
29
- cdkDropListOrientation="mixed"
30
- [cdkDropListData]="fields()"
31
- (cdkDropListDropped)="onDrop($event)"
32
- class="grid grid-cols-12 gap-4 relative py-4"
33
- [class.min-h-27]="fields().length === 0"
34
- >
35
- @for (field of fields(); track field.id) {
36
- <div
37
- cdkDrag
38
- [cdkDragData]="field"
39
- [cdkDragDisabled]="field._pending || field._deleting"
40
- [class]="
41
- getFieldColSpan(field) + ' cursor-grab active:cursor-grabbing'
42
- "
43
- >
44
- <div
45
- *cdkDragPlaceholder
46
- [class]="
47
- 'h-full min-h-27 bg-black/10 rounded-2xl ' +
48
- getFieldColSpan(field)
49
- "
50
- ></div>
51
- @if (field._pending) {
52
- <!-- Skeleton for pending field -->
53
- <mt-card class="h-full animate-pulse">
54
- <div class="flex gap-2">
55
- <div class="flex-1 space-y-3">
56
- <div class="h-4 bg-surface-200 rounded w-1/3"></div>
57
- <div class="h-10 bg-surface-200 rounded"></div>
58
- </div>
59
- <div class="flex flex-col gap-1">
60
- <div class="h-8 w-8 bg-surface-200 rounded"></div>
61
- <div class="h-8 w-8 bg-surface-200 rounded"></div>
62
- </div>
63
- </div>
64
- </mt-card>
65
- } @else {
66
- <mt-card
67
- class="h-full relative"
68
- [class.opacity-50]="field._deleting"
69
- >
70
- @if (field._deleting) {
71
- <div
72
- class="absolute inset-0 flex items-center justify-center bg-white/50 rounded-xl z-10"
73
- >
74
- <div
75
- class="animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full"
76
- ></div>
77
- </div>
78
- }
79
- <div class="flex gap-2">
80
- <div class="flex-1">
81
- <form [formGroup]="getFormGroup(field)">
82
- <mt-dynamic-field
83
- [fieldName]="field.name"
84
- [fieldConfig]="{
85
- label: field.name,
86
- readonly: true,
87
- type: getFieldType(field),
88
- }"
89
- />
90
- </form>
91
- </div>
92
- <div class="flex flex-col gap-1">
93
- <mt-button
94
- size="small"
95
- icon="general.settings-01"
96
- [tooltip]="t('edit-field')"
97
- outlined
98
- [disabled]="field._deleting"
99
- (onClick)="editField(field)"
100
- ></mt-button>
101
- <mt-button
102
- size="small"
103
- icon="general.trash-01"
104
- severity="danger"
105
- outlined
106
- [tooltip]="t('remove-field')"
107
- [disabled]="field._deleting"
108
- (onClick)="removeField($event, field)"
109
- ></mt-button>
110
- </div>
111
- </div>
112
- </mt-card>
113
- }
114
- </div>
115
- } @empty {
116
- <mt-card class="absolute inset-0 top-4 h-full" paddingless>
117
- <div class="size-full p-4">
118
- <div
119
- class="flex justify-center items-center gap-4 h-full border-2 border-primary rounded-xl bg-primary-200 text-primary"
120
- >
121
- <mt-icon icon="editor.move" class="text-3xl" />
122
- <span>{{ t("drag-from-the-side") }}</span>
123
- </div>
124
- </div>
125
- </mt-card>
126
- }
127
- </div>
128
- }
129
- </div>
130
- </ng-container>
@@ -1,211 +0,0 @@
1
- import {
2
- Component,
3
- computed,
4
- inject,
5
- input,
6
- output,
7
- signal,
8
- effect,
9
- } from '@angular/core';
10
- import type {
11
- EnrichedFormField,
12
- EnrichedFormSection,
13
- } from '../../store/form-builder/form-builder.model';
14
- import { ConfirmationService } from '@masterteam/components/confirmation';
15
- import { ModalService } from '@masterteam/components/modal';
16
- import { TranslocoService } from '@jsverse/transloco';
17
- import { Button } from '@masterteam/components/button';
18
- import { Card } from '@masterteam/components/card';
19
- import {
20
- CdkDrag,
21
- CdkDragDrop,
22
- CdkDragPlaceholder,
23
- CdkDropList,
24
- } from '@angular/cdk/drag-drop';
25
- import { TranslocoDirective } from '@jsverse/transloco';
26
- import { DynamicField } from '@masterteam/forms/dynamic-field';
27
- import { ReactiveFormsModule, FormGroup, FormControl } from '@angular/forms';
28
- import { Icon } from '@masterteam/icons';
29
- import { FormBuilderFacade } from '../../store/form-builder';
30
- import { FBFieldForm } from '../fb-field-form/fb-field-form';
31
- import { FBSectionForm } from '../fb-section-form/fb-section-form';
32
-
33
- @Component({
34
- selector: 'mt-fb-section',
35
- standalone: true,
36
- imports: [
37
- Button,
38
- Card,
39
- Icon,
40
- CdkDrag,
41
- CdkDropList,
42
- CdkDragPlaceholder,
43
- TranslocoDirective,
44
- DynamicField,
45
- ReactiveFormsModule,
46
- ],
47
- templateUrl: './fb-section.html',
48
- })
49
- export class FBSection {
50
- private readonly confirmationService = inject(ConfirmationService);
51
- private readonly modalService = inject(ModalService);
52
- private readonly translocoService = inject(TranslocoService);
53
- private readonly facade = inject(FormBuilderFacade);
54
-
55
- // Inputs
56
- readonly section = input.required<EnrichedFormSection>();
57
- readonly sectionsCount = input<number>(0);
58
- /** All sections - used to get available fields for condition formulas */
59
- readonly allSections = input<EnrichedFormSection[]>([]);
60
-
61
- // Outputs - only keep drag/drop since it needs coordination with parent drop list group
62
- readonly onFieldDrop = output<CdkDragDrop<EnrichedFormField[]>>();
63
-
64
- // Computed
65
- readonly sectionName = computed(() => {
66
- const lang = document.documentElement.lang as 'en' | 'ar';
67
- const section = this.section();
68
- return section.name[lang] ?? section.name['en'];
69
- });
70
-
71
- readonly fields = computed(() => this.section().fields);
72
-
73
- // UI State
74
- readonly expanded = signal(true);
75
-
76
- // Form groups cache for dynamic fields
77
- private readonly formGroupsCache = new Map<string, FormGroup>();
78
-
79
- constructor() {
80
- // Cleanup stale form groups when fields change
81
- effect(() => {
82
- const currentIds = new Set(this.fields().map((f) => f.id));
83
- for (const id of this.formGroupsCache.keys()) {
84
- if (!currentIds.has(id)) {
85
- this.formGroupsCache.delete(id);
86
- }
87
- }
88
- });
89
- }
90
-
91
- getFormGroup(field: EnrichedFormField): FormGroup {
92
- let fg = this.formGroupsCache.get(field.id);
93
-
94
- if (fg) {
95
- // Check if field name changed
96
- if (!fg.contains(field.name)) {
97
- const oldKey = Object.keys(fg.controls)[0];
98
- const value = oldKey ? fg.get(oldKey)?.value : field.data?.data;
99
-
100
- fg = new FormGroup({
101
- [field.name]: new FormControl(value),
102
- });
103
- this.formGroupsCache.set(field.id, fg);
104
- }
105
- } else {
106
- fg = new FormGroup({
107
- [field.name]: new FormControl(field.data?.data),
108
- });
109
- this.formGroupsCache.set(field.id, fg);
110
- }
111
-
112
- return fg;
113
- }
114
-
115
- toggleExpanded(): void {
116
- this.expanded.update((v) => !v);
117
- }
118
-
119
- onDrop(event: CdkDragDrop<EnrichedFormField[]>): void {
120
- this.onFieldDrop.emit(event);
121
- }
122
-
123
- editSection(event: Event): void {
124
- event.stopPropagation();
125
- const section = this.section();
126
-
127
- this.modalService.openModal(FBSectionForm, 'drawer', {
128
- header: this.translocoService.translate('formBuilder.edit-section'),
129
- height: '20vw',
130
- styleClass: '!w-100 !absolute ',
131
- position: 'end',
132
- appendTo: '#page-content',
133
- modal: true,
134
- dismissible: true,
135
-
136
- inputValues: {
137
- sectionId: section.id,
138
- initialData: section.name,
139
- sectionsCount: this.sectionsCount(),
140
- },
141
- });
142
- }
143
-
144
- removeField(event: Event, field: EnrichedFormField): void {
145
- this.confirmationService.confirmDelete({
146
- event,
147
- type: 'popup',
148
- accept: () => {
149
- this.facade.deleteField(this.section().id, field.id);
150
- },
151
- });
152
- }
153
-
154
- editField(field: EnrichedFormField): void {
155
- this.modalService.openModal(FBFieldForm, 'drawer', {
156
- header: this.translocoService.translate('formBuilder.field-settings'),
157
- height: '20vw',
158
- styleClass: '!w-100 !absolute !shadow-none',
159
- position: 'end',
160
- modal: true,
161
- dismissible: true,
162
-
163
- appendTo: '#page-content',
164
- inputValues: {
165
- initialData: field,
166
- sectionId: this.section().id,
167
- allSections: this.allSections(),
168
- },
169
- });
170
- }
171
-
172
- getFieldColSpan(field: EnrichedFormField): string {
173
- switch (field.width) {
174
- case '100':
175
- return 'col-span-12';
176
- case '50':
177
- return 'col-span-6';
178
- case '25':
179
- return 'col-span-3';
180
- default:
181
- return 'col-span-12';
182
- }
183
- }
184
-
185
- getFieldType(field: EnrichedFormField): string {
186
- const typeMap: Record<string, string> = {
187
- User: 'select',
188
- Text: 'text',
189
- LongText: 'editor-field',
190
- Percentage: 'slider',
191
- Date: 'date',
192
- Currency: 'text',
193
- Number: 'number',
194
- Lookup: 'select',
195
- LookupMultiSelect: 'select',
196
- Checkbox: 'toggle',
197
- InternalModule: 'select',
198
- DynamicList: 'select',
199
- API: 'select',
200
- Time: 'date',
201
- Status: 'select',
202
- Attachment: 'attachment',
203
- EditableListView: 'actionableTable',
204
- LookupLog: 'actionableTable',
205
- LookupMatrix: 'select',
206
- Location: 'select',
207
- };
208
-
209
- return typeMap[field.type] ?? 'text';
210
- }
211
- }
@@ -1,38 +0,0 @@
1
- <ng-container *transloco="let t; prefix: 'formBuilder'">
2
- <div [class]="[modalService.contentClass, 'p-4', 'overflow-y-hidden!']">
3
- <div class="mt-4 h-full overflow-y-auto pb-10">
4
- <mt-dynamic-form [formConfig]="formConfig" [formControl]="formControl">
5
- </mt-dynamic-form>
6
- </div>
7
- </div>
8
-
9
- <div [class]="modalService.footerClass">
10
- @if (sectionId()) {
11
- <mt-button
12
- [tooltip]="t('delete')"
13
- severity="danger"
14
- outlined
15
- icon="general.trash-01"
16
- [loading]="deleting()"
17
- [disabled]="submitting()"
18
- (onClick)="onDelete($event)"
19
- class="me-auto"
20
- ></mt-button>
21
- }
22
- <mt-button
23
- [label]="t('cancel')"
24
- severity="secondary"
25
- [disabled]="submitting() || deleting()"
26
- (onClick)="onCancel()"
27
- >
28
- </mt-button>
29
- <mt-button
30
- [disabled]="!formControl.valid || deleting()"
31
- [label]="t('save')"
32
- severity="primary"
33
- [loading]="submitting()"
34
- (onClick)="onSave()"
35
- >
36
- </mt-button>
37
- </div>
38
- </ng-container>
@@ -1,128 +0,0 @@
1
- import { Component, effect, inject, input, signal } from '@angular/core';
2
- import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
3
- import { ReactiveFormsModule, FormControl } from '@angular/forms';
4
- import { DynamicForm } from '@masterteam/forms/dynamic-form';
5
- import { DynamicFormConfig } from '@masterteam/components';
6
- import { Button } from '@masterteam/components/button';
7
- import { ModalService } from '@masterteam/components/modal';
8
- import { ModalRef } from '@masterteam/components/dialog';
9
- import { ConfirmationService } from '@masterteam/components/confirmation';
10
- import { FormBuilderFacade } from '../../store/form-builder';
11
-
12
- @Component({
13
- selector: 'mt-fb-section-form',
14
- standalone: true,
15
- imports: [TranslocoDirective, ReactiveFormsModule, DynamicForm, Button],
16
- templateUrl: './fb-section-form.html',
17
- })
18
- export class FBSectionForm {
19
- private readonly transloco = inject(TranslocoService);
20
- protected readonly modalService = inject(ModalService);
21
- private readonly ref = inject(ModalRef);
22
- private readonly confirmationService = inject(ConfirmationService);
23
- private readonly facade = inject(FormBuilderFacade);
24
-
25
- // Inputs
26
- readonly sectionId = input<string | null>(null);
27
- readonly initialData = input<{ ar: string; en: string } | null>(null);
28
- readonly sectionsCount = input<number>(0);
29
-
30
- // UI State
31
- readonly submitting = signal(false);
32
- readonly deleting = signal(false);
33
-
34
- // Form
35
- readonly formControl = new FormControl();
36
- readonly formConfig: DynamicFormConfig = {
37
- sections: [
38
- {
39
- key: 'section-form',
40
- type: 'none',
41
- columns: 12,
42
- order: 1,
43
- fields: [
44
- {
45
- key: 'name-ar',
46
- label: this.transloco.translate('formBuilder.name-ar'),
47
- type: 'text',
48
- placeholder: this.transloco.translate('formBuilder.name-ar'),
49
- colSpan: 12,
50
- order: 1,
51
- },
52
- {
53
- key: 'name-en',
54
- label: this.transloco.translate('formBuilder.name-en'),
55
- type: 'text',
56
- placeholder: this.transloco.translate('formBuilder.name-en'),
57
- colSpan: 12,
58
- order: 2,
59
- },
60
- ],
61
- },
62
- ],
63
- };
64
-
65
- constructor() {
66
- effect(() => {
67
- const data = this.initialData();
68
- if (data) {
69
- this.formControl.patchValue({
70
- 'name-ar': data.ar,
71
- 'name-en': data.en,
72
- });
73
- }
74
- });
75
- }
76
-
77
- onSave(): void {
78
- if (this.formControl.invalid) return;
79
-
80
- const formValue = this.formControl.value;
81
- const payload = {
82
- name: {
83
- ar: formValue['name-ar'],
84
- en: formValue['name-en'],
85
- },
86
- };
87
-
88
- this.submitting.set(true);
89
- const sectionId = this.sectionId();
90
-
91
- if (sectionId) {
92
- // Update existing section
93
- this.facade.updateSection(sectionId, payload).subscribe({
94
- next: () => this.ref.close(true),
95
- error: () => this.submitting.set(false),
96
- });
97
- } else {
98
- // Create new section
99
- this.facade
100
- .addSection({ ...payload, order: this.sectionsCount() })
101
- .subscribe({
102
- next: () => this.ref.close(true),
103
- error: () => this.submitting.set(false),
104
- });
105
- }
106
- }
107
-
108
- onCancel(): void {
109
- this.ref.close(false);
110
- }
111
-
112
- onDelete(event: Event): void {
113
- const sectionId = this.sectionId();
114
- if (!sectionId) return;
115
-
116
- this.confirmationService.confirmDelete({
117
- event,
118
- type: 'popup',
119
- accept: () => {
120
- this.deleting.set(true);
121
- this.facade.deleteSection(sectionId).subscribe({
122
- next: () => this.ref.close(true),
123
- error: () => this.deleting.set(false),
124
- });
125
- },
126
- });
127
- }
128
- }
@@ -1,166 +0,0 @@
1
- <ng-container *transloco="let t; prefix: 'formBuilder'">
2
- <div class="flex gap-4 h-full w-full overflow-hidden" cdkDropListGroup>
3
- <!-- Properties Sidebar -->
4
- <mt-card
5
- class="z-1 w-1/5 min-w-xs shrink-0 h-full flex flex-col overflow-hidden"
6
- >
7
- <ng-template #headless>
8
- <!-- Header -->
9
- <div class="flex items-center justify-between px-4 pt-5">
10
- <h3 class="text-xl font-semibold">{{ t("form-elements") }}</h3>
11
- </div>
12
-
13
- @if (properties().length === 0) {
14
- @if (isLoading()) {
15
- <!-- Properties Loading Skeleton -->
16
- <div class="p-4 space-y-3">
17
- <p-skeleton height="2rem" styleClass="mb-4" />
18
- @for (i of [1, 2, 3, 4, 5, 6]; track i) {
19
- <p-skeleton height="3rem" styleClass="rounded-lg" />
20
- }
21
- </div>
22
- } @else {
23
- <!-- No Properties State -->
24
- <div class="flex-1 flex items-center justify-center p-4">
25
- <div class="text-center text-muted-color">
26
- <p class="text-sm">{{ t("no-properties") }}</p>
27
- </div>
28
- </div>
29
- }
30
- } @else {
31
- <!-- Tabs using PrimeNG -->
32
- <p-tabs
33
- [(value)]="activeTab"
34
- styleClass="structure-tabs"
35
- class="flex flex-1 flex-col min-h-0"
36
- >
37
- <p-tablist class="shrink-0">
38
- @for (tab of availableTabs(); track tab.id) {
39
- <p-tab [value]="tab.id">{{ tab.title | titlecase }}</p-tab>
40
- }
41
- </p-tablist>
42
- <p-tabpanels class="!bg-transparent !p-0 flex-1 overflow-hidden">
43
- @for (tab of availableTabs(); track tab.id) {
44
- <p-tabpanel [value]="tab.id" class="h-full">
45
- <!-- Node List -->
46
- <div
47
- class="space-y-1 p-4 [&_.cdk-drag-placeholder]:hidden h-full overflow-y-auto"
48
- [id]="'toolbox-' + tab.id"
49
- cdkDropList
50
- cdkDropListSortingDisabled
51
- [cdkDropListData]="tab.properties"
52
- [cdkDropListEnterPredicate]="noReturnPredicate"
53
- >
54
- @for (node of tab.properties; track $index) {
55
- <div
56
- cdkDrag
57
- [cdkDragData]="node"
58
- class="group cursor-move select-none relative flex items-center gap-3 py-3 px-4 rounded-lg border border-dashed border-surface-300 hover:border-solid hover:bg-emphasis dark:border-surface-500 transition-colors"
59
- >
60
- <div
61
- *cdkDragPlaceholder
62
- class="col-span-12 min-h-27 w-full rounded-2xl bg-black/10 z-1"
63
- ></div>
64
- <span class="flex-1 text-base font-medium">{{
65
- node.name
66
- }}</span>
67
- </div>
68
- }
69
-
70
- @if (tab.properties.length === 0) {
71
- <div class="py-8 text-center text-muted-color">
72
- <p class="text-sm">
73
- All {{ tab.title }} items are in use
74
- </p>
75
- </div>
76
- }
77
- </div>
78
- </p-tabpanel>
79
- }
80
- </p-tabpanels>
81
- </p-tabs>
82
- }
83
- </ng-template>
84
- </mt-card>
85
-
86
- <!-- Main Canvas Area -->
87
- <div class="flex flex-col gap-4 flex-1 w-full h-full overflow-y-auto">
88
- <mt-card>
89
- <ng-template #headless>
90
- <div class="p-4 flex items-center gap-2">
91
- <mt-button
92
- icon="layout.layout-top"
93
- [label]="t('add-section')"
94
- (onClick)="addSection()"
95
- [disabled]="isLoading()"
96
- ></mt-button>
97
- <mt-button
98
- icon="general.eye"
99
- [label]="t('preview')"
100
- (onClick)="openPreview()"
101
- [disabled]="isLoading()"
102
- ></mt-button>
103
- <mt-button
104
- icon="finance.credit-card-plus"
105
- [label]="t('reset')"
106
- (onClick)="resetFormConfiguration()"
107
- [disabled]="isLoading()"
108
- ></mt-button>
109
- </div>
110
- </ng-template>
111
- </mt-card>
112
-
113
- @if (isLoading()) {
114
- <!-- Form Loading Skeleton -->
115
- @for (i of [1, 2]; track i) {
116
- <mt-card>
117
- <ng-template #headless>
118
- <div class="p-4 space-y-4">
119
- <!-- Section header skeleton -->
120
- <div class="flex items-center justify-between">
121
- <p-skeleton width="10rem" height="1.5rem" />
122
- <div class="flex gap-2">
123
- <p-skeleton width="2rem" height="2rem" shape="circle" />
124
- <p-skeleton width="2rem" height="2rem" shape="circle" />
125
- </div>
126
- </div>
127
- <!-- Fields skeleton -->
128
- <div class="grid grid-cols-12 gap-4">
129
- <div class="col-span-6">
130
- <p-skeleton height="4rem" styleClass="rounded-lg" />
131
- </div>
132
- <div class="col-span-6">
133
- <p-skeleton height="4rem" styleClass="rounded-lg" />
134
- </div>
135
- <div class="col-span-12">
136
- <p-skeleton height="4rem" styleClass="rounded-lg" />
137
- </div>
138
- </div>
139
- </div>
140
- </ng-template>
141
- </mt-card>
142
- }
143
- } @else {
144
- @for (section of enrichedSections(); track section.id) {
145
- <mt-fb-section
146
- [section]="section"
147
- [sectionsCount]="enrichedSections().length"
148
- [allSections]="enrichedSections()"
149
- (onFieldDrop)="drop($event)"
150
- >
151
- </mt-fb-section>
152
- } @empty {
153
- <mt-card>
154
- <div class="h-27 p-4">
155
- <div
156
- class="flex justify-center items-center gap-4 h-full border-2 border-primary rounded-xl bg-primary-200 text-primary"
157
- >
158
- <span>{{ t("no-section") }}</span>
159
- </div>
160
- </div>
161
- </mt-card>
162
- }
163
- }
164
- </div>
165
- </div>
166
- </ng-container>