@smartsoft001/crud-shell-angular 1.2.90 → 2.69.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.
Files changed (214) hide show
  1. package/.storybook/addons.js +1 -0
  2. package/.storybook/config.js +5 -0
  3. package/.storybook/tsconfig.json +8 -0
  4. package/.storybook/webpack.config.js +7 -0
  5. package/jest.config.ts +38 -0
  6. package/ng-package.json +7 -0
  7. package/package.json +12 -23
  8. package/project.json +49 -0
  9. package/src/index.ts +13 -0
  10. package/src/lib/+state/crud.facade.ts +82 -0
  11. package/src/lib/+state/crud.feature.methods.ts +258 -0
  12. package/src/lib/+state/crud.feature.store.ts +77 -0
  13. package/src/lib/components/components.module.ts +58 -0
  14. package/src/lib/components/export/export.component.ts +69 -0
  15. package/src/lib/components/filter/base/base.component.ts +175 -0
  16. package/src/lib/components/filter/check/check.component.scss +25 -0
  17. package/src/lib/components/filter/check/check.component.ts +72 -0
  18. package/src/lib/components/filter/date/date.component.html +30 -0
  19. package/src/lib/components/filter/date/date.component.ts +70 -0
  20. package/src/lib/components/filter/date-time/date-time.component.html +62 -0
  21. package/src/lib/components/filter/date-time/date-time.component.ts +56 -0
  22. package/src/lib/components/filter/date-with-edit/date-with-edit.component.html +109 -0
  23. package/src/lib/components/filter/date-with-edit/date-with-edit.component.ts +70 -0
  24. package/src/lib/components/filter/filter.component.html +52 -0
  25. package/src/lib/components/filter/filter.component.ts +34 -0
  26. package/src/lib/components/filter/flag/flag.component.ts +55 -0
  27. package/src/lib/components/filter/index.ts +9 -0
  28. package/src/lib/components/filter/int/int.component.html +74 -0
  29. package/src/lib/components/filter/int/int.component.ts +57 -0
  30. package/src/lib/components/filter/radio/radio.component.ts +74 -0
  31. package/src/lib/components/filter/text/text.component.ts +46 -0
  32. package/src/lib/components/filters/filters.component.scss +150 -0
  33. package/src/lib/components/filters/filters.component.ts +157 -0
  34. package/src/lib/components/filters-config/filters-config.component.ts +69 -0
  35. package/src/lib/components/group/group.component.ts +93 -0
  36. package/src/lib/components/index.ts +7 -0
  37. package/src/lib/components/multiselect/multiselect.component.html +41 -0
  38. package/src/lib/components/multiselect/multiselect.component.scss +125 -0
  39. package/src/lib/components/multiselect/multiselect.component.ts +104 -0
  40. package/src/lib/crud-full.module.ts +51 -0
  41. package/src/lib/crud.config.ts +79 -0
  42. package/src/lib/crud.module.ts +72 -0
  43. package/src/lib/factories/list-pagination/list-pagination.factory.ts +77 -0
  44. package/src/lib/models/index.ts +1 -0
  45. package/src/lib/models/interfaces.ts +34 -0
  46. package/src/lib/pages/index.ts +6 -0
  47. package/src/lib/pages/item/base/base.component.ts +54 -0
  48. package/src/lib/pages/item/item.component.ts +513 -0
  49. package/src/lib/pages/item/standard/standard.component.scss +0 -0
  50. package/src/lib/pages/item/standard/standard.component.ts +50 -0
  51. package/src/lib/pages/list/base/base.component.ts +29 -0
  52. package/src/lib/pages/list/list.component.stories.ts +77 -0
  53. package/src/lib/pages/list/list.component.ts +438 -0
  54. package/src/lib/pages/list/standard/standard.component.ts +42 -0
  55. package/src/lib/pipes/form-options/form-options.pipe.ts +61 -0
  56. package/src/lib/pipes/index.ts +2 -0
  57. package/src/lib/pipes/pipes.module.ts +9 -0
  58. package/src/lib/providers/model-possibilities/model-possibilities.provider.ts +19 -0
  59. package/src/lib/services/crud/crud.service.spec.ts +303 -0
  60. package/src/lib/services/crud/crud.service.ts +178 -0
  61. package/src/lib/services/list-group/list-group.service.spec.ts +87 -0
  62. package/src/lib/services/list-group/list-group.service.ts +65 -0
  63. package/src/lib/services/page/page.service.spec.ts +143 -0
  64. package/src/lib/services/page/page.service.ts +44 -0
  65. package/src/lib/services/search/search.service.spec.ts +134 -0
  66. package/src/lib/services/search/search.service.ts +42 -0
  67. package/test-setup.ts +0 -0
  68. package/tsconfig.json +13 -0
  69. package/tsconfig.lib.json +31 -0
  70. package/tsconfig.lib.prod.json +11 -0
  71. package/tsconfig.spec.json +10 -0
  72. package/tslint.json +10 -0
  73. package/esm2022/index.mjs +0 -18
  74. package/esm2022/lib/+state/crud.actions.mjs +0 -224
  75. package/esm2022/lib/+state/crud.effects.mjs +0 -182
  76. package/esm2022/lib/+state/crud.facade.mjs +0 -91
  77. package/esm2022/lib/+state/crud.reducer.mjs +0 -214
  78. package/esm2022/lib/+state/crud.selectors.mjs +0 -11
  79. package/esm2022/lib/components/components.module.mjs +0 -110
  80. package/esm2022/lib/components/export/export.component.mjs +0 -47
  81. package/esm2022/lib/components/filter/base/base.component.mjs +0 -152
  82. package/esm2022/lib/components/filter/check/check.component.mjs +0 -36
  83. package/esm2022/lib/components/filter/date/date.component.mjs +0 -47
  84. package/esm2022/lib/components/filter/date-time/date-time.component.mjs +0 -43
  85. package/esm2022/lib/components/filter/date-with-edit/date-with-edit.component.mjs +0 -49
  86. package/esm2022/lib/components/filter/filter.component.mjs +0 -30
  87. package/esm2022/lib/components/filter/flag/flag.component.mjs +0 -16
  88. package/esm2022/lib/components/filter/index.mjs +0 -10
  89. package/esm2022/lib/components/filter/int/int.component.mjs +0 -38
  90. package/esm2022/lib/components/filter/radio/radio.component.mjs +0 -16
  91. package/esm2022/lib/components/filter/text/text.component.mjs +0 -16
  92. package/esm2022/lib/components/filters/filters.component.mjs +0 -111
  93. package/esm2022/lib/components/filters-config/filters-config.component.mjs +0 -37
  94. package/esm2022/lib/components/group/group.component.mjs +0 -54
  95. package/esm2022/lib/components/index.mjs +0 -8
  96. package/esm2022/lib/components/multiselect/multiselect.component.mjs +0 -71
  97. package/esm2022/lib/crud-full.module.mjs +0 -103
  98. package/esm2022/lib/crud.config.mjs +0 -17
  99. package/esm2022/lib/crud.module.mjs +0 -106
  100. package/esm2022/lib/factories/list-pagination/list-pagination.factory.mjs +0 -69
  101. package/esm2022/lib/models/index.mjs +0 -2
  102. package/esm2022/lib/models/interfaces.mjs +0 -2
  103. package/esm2022/lib/pages/index.mjs +0 -7
  104. package/esm2022/lib/pages/item/base/base.component.mjs +0 -44
  105. package/esm2022/lib/pages/item/item.component.mjs +0 -395
  106. package/esm2022/lib/pages/item/standard/standard.component.mjs +0 -15
  107. package/esm2022/lib/pages/list/base/base.component.mjs +0 -23
  108. package/esm2022/lib/pages/list/list.component.mjs +0 -355
  109. package/esm2022/lib/pages/list/standard/standard.component.mjs +0 -34
  110. package/esm2022/lib/pipes/form-options/form-options.pipe.mjs +0 -55
  111. package/esm2022/lib/pipes/index.mjs +0 -3
  112. package/esm2022/lib/pipes/pipes.module.mjs +0 -20
  113. package/esm2022/lib/providers/model-possibilities/model-possibilities.provider.mjs +0 -11
  114. package/esm2022/lib/services/crud/crud.service.mjs +0 -138
  115. package/esm2022/lib/services/list-group/list-group.service.mjs +0 -60
  116. package/esm2022/lib/services/page/page.service.mjs +0 -41
  117. package/esm2022/lib/services/search/search.service.mjs +0 -41
  118. package/esm2022/lib/services/socket/socket.service.mjs +0 -17
  119. package/esm2022/smartsoft001-crud-shell-angular.mjs +0 -5
  120. package/fesm2022/smartsoft001-crud-shell-angular.mjs +0 -2810
  121. package/fesm2022/smartsoft001-crud-shell-angular.mjs.map +0 -1
  122. package/index.d.ts +0 -18
  123. package/index.d.ts.map +0 -1
  124. package/lib/+state/crud.actions.d.ts +0 -152
  125. package/lib/+state/crud.actions.d.ts.map +0 -1
  126. package/lib/+state/crud.effects.d.ts +0 -18
  127. package/lib/+state/crud.effects.d.ts.map +0 -1
  128. package/lib/+state/crud.facade.d.ts +0 -44
  129. package/lib/+state/crud.facade.d.ts.map +0 -1
  130. package/lib/+state/crud.reducer.d.ts +0 -43
  131. package/lib/+state/crud.reducer.d.ts.map +0 -1
  132. package/lib/+state/crud.selectors.d.ts +0 -10
  133. package/lib/+state/crud.selectors.d.ts.map +0 -1
  134. package/lib/components/components.module.d.ts +0 -27
  135. package/lib/components/components.module.d.ts.map +0 -1
  136. package/lib/components/export/export.component.d.ts +0 -21
  137. package/lib/components/export/export.component.d.ts.map +0 -1
  138. package/lib/components/filter/base/base.component.d.ts +0 -39
  139. package/lib/components/filter/base/base.component.d.ts.map +0 -1
  140. package/lib/components/filter/check/check.component.d.ts +0 -19
  141. package/lib/components/filter/check/check.component.d.ts.map +0 -1
  142. package/lib/components/filter/date/date.component.d.ts +0 -16
  143. package/lib/components/filter/date/date.component.d.ts.map +0 -1
  144. package/lib/components/filter/date-time/date-time.component.d.ts +0 -15
  145. package/lib/components/filter/date-time/date-time.component.d.ts.map +0 -1
  146. package/lib/components/filter/date-with-edit/date-with-edit.component.d.ts +0 -17
  147. package/lib/components/filter/date-with-edit/date-with-edit.component.d.ts.map +0 -1
  148. package/lib/components/filter/filter.component.d.ts +0 -14
  149. package/lib/components/filter/filter.component.d.ts.map +0 -1
  150. package/lib/components/filter/flag/flag.component.d.ts +0 -8
  151. package/lib/components/filter/flag/flag.component.d.ts.map +0 -1
  152. package/lib/components/filter/index.d.ts +0 -10
  153. package/lib/components/filter/index.d.ts.map +0 -1
  154. package/lib/components/filter/int/int.component.d.ts +0 -15
  155. package/lib/components/filter/int/int.component.d.ts.map +0 -1
  156. package/lib/components/filter/radio/radio.component.d.ts +0 -8
  157. package/lib/components/filter/radio/radio.component.d.ts.map +0 -1
  158. package/lib/components/filter/text/text.component.d.ts +0 -8
  159. package/lib/components/filter/text/text.component.d.ts.map +0 -1
  160. package/lib/components/filters/filters.component.d.ts +0 -83
  161. package/lib/components/filters/filters.component.d.ts.map +0 -1
  162. package/lib/components/filters-config/filters-config.component.d.ts +0 -19
  163. package/lib/components/filters-config/filters-config.component.d.ts.map +0 -1
  164. package/lib/components/group/group.component.d.ts +0 -23
  165. package/lib/components/group/group.component.d.ts.map +0 -1
  166. package/lib/components/index.d.ts +0 -8
  167. package/lib/components/index.d.ts.map +0 -1
  168. package/lib/components/multiselect/multiselect.component.d.ts +0 -28
  169. package/lib/components/multiselect/multiselect.component.d.ts.map +0 -1
  170. package/lib/crud-full.module.d.ts +0 -28
  171. package/lib/crud-full.module.d.ts.map +0 -1
  172. package/lib/crud.config.d.ts +0 -65
  173. package/lib/crud.config.d.ts.map +0 -1
  174. package/lib/crud.module.d.ts +0 -34
  175. package/lib/crud.module.d.ts.map +0 -1
  176. package/lib/factories/list-pagination/list-pagination.factory.d.ts +0 -20
  177. package/lib/factories/list-pagination/list-pagination.factory.d.ts.map +0 -1
  178. package/lib/models/index.d.ts +0 -2
  179. package/lib/models/index.d.ts.map +0 -1
  180. package/lib/models/interfaces.d.ts +0 -30
  181. package/lib/models/interfaces.d.ts.map +0 -1
  182. package/lib/pages/index.d.ts +0 -7
  183. package/lib/pages/index.d.ts.map +0 -1
  184. package/lib/pages/item/base/base.component.d.ts +0 -25
  185. package/lib/pages/item/base/base.component.d.ts.map +0 -1
  186. package/lib/pages/item/item.component.d.ts +0 -70
  187. package/lib/pages/item/item.component.d.ts.map +0 -1
  188. package/lib/pages/item/standard/standard.component.d.ts +0 -8
  189. package/lib/pages/item/standard/standard.component.d.ts.map +0 -1
  190. package/lib/pages/list/base/base.component.d.ts +0 -15
  191. package/lib/pages/list/base/base.component.d.ts.map +0 -1
  192. package/lib/pages/list/list.component.d.ts +0 -54
  193. package/lib/pages/list/list.component.d.ts.map +0 -1
  194. package/lib/pages/list/standard/standard.component.d.ts +0 -14
  195. package/lib/pages/list/standard/standard.component.d.ts.map +0 -1
  196. package/lib/pipes/form-options/form-options.pipe.d.ts +0 -15
  197. package/lib/pipes/form-options/form-options.pipe.d.ts.map +0 -1
  198. package/lib/pipes/index.d.ts +0 -3
  199. package/lib/pipes/index.d.ts.map +0 -1
  200. package/lib/pipes/pipes.module.d.ts +0 -8
  201. package/lib/pipes/pipes.module.d.ts.map +0 -1
  202. package/lib/providers/model-possibilities/model-possibilities.provider.d.ts +0 -18
  203. package/lib/providers/model-possibilities/model-possibilities.provider.d.ts.map +0 -1
  204. package/lib/services/crud/crud.service.d.ts +0 -38
  205. package/lib/services/crud/crud.service.d.ts.map +0 -1
  206. package/lib/services/list-group/list-group.service.d.ts +0 -15
  207. package/lib/services/list-group/list-group.service.d.ts.map +0 -1
  208. package/lib/services/page/page.service.d.ts +0 -12
  209. package/lib/services/page/page.service.d.ts.map +0 -1
  210. package/lib/services/search/search.service.d.ts +0 -16
  211. package/lib/services/search/search.service.d.ts.map +0 -1
  212. package/lib/services/socket/socket.service.d.ts +0 -10
  213. package/lib/services/socket/socket.service.d.ts.map +0 -1
  214. package/smartsoft001-crud-shell-angular.d.ts.map +0 -1
@@ -0,0 +1,513 @@
1
+ import { Location, NgTemplateOutlet } from '@angular/common';
2
+ import {
3
+ ChangeDetectorRef,
4
+ Component,
5
+ ComponentFactory,
6
+ effect,
7
+ ElementRef,
8
+ inject,
9
+ input,
10
+ OnDestroy,
11
+ OnInit,
12
+ Signal,
13
+ signal,
14
+ TemplateRef,
15
+ viewChild,
16
+ viewChildren,
17
+ ViewContainerRef,
18
+ WritableSignal,
19
+ } from '@angular/core';
20
+ import {
21
+ AbstractControl,
22
+ UntypedFormArray,
23
+ UntypedFormControl,
24
+ UntypedFormGroup,
25
+ } from '@angular/forms';
26
+ import { ActivatedRoute, Router } from '@angular/router';
27
+ import { TranslateService } from '@ngx-translate/core';
28
+ import { BehaviorSubject, Subscription } from 'rxjs';
29
+
30
+ import {
31
+ CreateDynamicComponent,
32
+ DetailsService,
33
+ DynamicComponentLoader,
34
+ DynamicContentDirective,
35
+ ICellPipe,
36
+ IDetailsOptions,
37
+ IIconButtonOptions,
38
+ IPageOptions,
39
+ PageComponent,
40
+ StyleService,
41
+ ToastService,
42
+ } from '@smartsoft001/angular';
43
+ import { IEntity } from '@smartsoft001/domain-core';
44
+ import { getModelOptions } from '@smartsoft001/models';
45
+ import { SpecificationService } from '@smartsoft001/utils';
46
+
47
+ import { CrudFacade } from '../../+state/crud.facade';
48
+ import { CrudFullConfig } from '../../crud.config';
49
+ import { ICrudFilter } from '../../models';
50
+ import { CrudItemPageBaseComponent } from './base/base.component';
51
+ import { ItemStandardComponent } from './standard/standard.component';
52
+ import { CrudService } from '../../services/crud/crud.service';
53
+ import { PageService } from '../../services/page/page.service';
54
+
55
+ @Component({
56
+ selector: 'smart-crud-item-page',
57
+ imports: [PageComponent, ItemStandardComponent, NgTemplateOutlet],
58
+ template: `
59
+ <smart-page [options]="pageOptions()">
60
+ <div #topTpl class="text-xl py-2.5 separator"></div>
61
+ @if (template() === 'default') {
62
+ <smart-crud-item-standard-page
63
+ [detailsOptions]="detailsOptions()"
64
+ [mode]="mode"
65
+ [uniqueProvider]="uniqueProvider()"
66
+ (onPartialChange)="onPartialChange($event)"
67
+ (onChange)="onChange($event)"
68
+ (onValidChange)="onValidChange($event)"
69
+ >
70
+ <ng-container [ngTemplateOutlet]="contentTpl"></ng-container>
71
+ </smart-crud-item-standard-page>
72
+ }
73
+ <ng-template #contentTpl>
74
+ <ng-content></ng-content>
75
+ </ng-template>
76
+ <div class="dynamic-content"></div>
77
+ <div #bottomTpl class="text-xl py-2.5 separator"></div>
78
+ </smart-page>
79
+ `,
80
+ })
81
+ export class ItemComponent<T extends IEntity<string>>
82
+ extends CreateDynamicComponent<CrudItemPageBaseComponent<any>>(
83
+ 'crud-item-page',
84
+ )
85
+ implements OnInit, OnDestroy
86
+ {
87
+ private router = inject(Router);
88
+ private activeRoute = inject(ActivatedRoute);
89
+ private facade = inject(CrudFacade<T>);
90
+ private service = inject(CrudService<T>);
91
+ private dynamicComponentLoader = inject(DynamicComponentLoader<T>);
92
+ private translateService = inject(TranslateService);
93
+ public config = inject(CrudFullConfig<T>);
94
+ private location = inject(Location);
95
+ private cd = inject(ChangeDetectorRef);
96
+ private styleService = inject(StyleService);
97
+ private elementRef = inject(ElementRef);
98
+ private toastService = inject(ToastService);
99
+ private pageService = inject(PageService<T>);
100
+ private detailsService = inject(DetailsService);
101
+
102
+ private _mode: WritableSignal<string>;
103
+
104
+ pageOptions: WritableSignal<IPageOptions> = signal({
105
+ title: '',
106
+ showBackButton: true,
107
+ hideMenuButton: true,
108
+ });
109
+ detailsOptions: WritableSignal<IDetailsOptions<T>>;
110
+ id: string;
111
+ formValue: T;
112
+ formValid = false;
113
+ item: T;
114
+ formPartialValue: Partial<T>;
115
+ uniqueProvider: WritableSignal<
116
+ (values: Record<keyof T, any>) => Promise<boolean>
117
+ >;
118
+
119
+ set mode(val: string) {
120
+ this._mode = signal(val);
121
+ this.refreshDynamicInstance();
122
+ }
123
+ get mode(): string {
124
+ return this._mode();
125
+ }
126
+
127
+ selected: Signal<T>;
128
+
129
+ standardComponents = viewChildren(ItemStandardComponent);
130
+ // @ViewChild(IonContent, { static: true }) content: IonContent; //TODO: rewrite when rewriting ionic
131
+
132
+ contentTpl = viewChild<TemplateRef<any>>('contentTpl');
133
+
134
+ topTpl = viewChild<ViewContainerRef>('topTpl');
135
+
136
+ bottomTpl = viewChild<ViewContainerRef>('bottomTpl');
137
+
138
+ dynamicContents = viewChildren<DynamicContentDirective>(
139
+ DynamicContentDirective,
140
+ );
141
+
142
+ subscription = new Subscription();
143
+
144
+ constructor() {
145
+ super();
146
+
147
+ this.selected = this.facade.selected;
148
+ }
149
+
150
+ refreshProperties() {
151
+ this.baseComponentRef.setInput('detailsOptions', this.detailsOptions());
152
+ this.baseComponentRef.setInput('mode', this.mode);
153
+ this.baseComponentRef.setInput('uniqueProvider', this.uniqueProvider());
154
+ this.subscription.add(
155
+ this.baseInstance.onPartialChange.subscribe((val) =>
156
+ this.onPartialChange(val),
157
+ ),
158
+ );
159
+ this.subscription.add(
160
+ this.baseInstance.onChange.subscribe((val) => this.onChange(val)),
161
+ );
162
+ this.subscription.add(
163
+ this.baseInstance.onValidChange.subscribe((val) =>
164
+ this.onValidChange(val),
165
+ ),
166
+ );
167
+ }
168
+
169
+ async ngOnInit() {
170
+ this.detailsService.init();
171
+ this.styleService.init(this.elementRef);
172
+
173
+ this.pageService.checkPermissions();
174
+
175
+ if (this.router.routerState.snapshot.url.split('?')[0].endsWith('/add')) {
176
+ this.mode = 'create';
177
+ await this.generateComponents('add');
178
+ } else {
179
+ this.mode = this.config.details ? 'details' : 'update';
180
+
181
+ if (this.mode === 'update') {
182
+ await this.generateComponents('edit');
183
+ }
184
+
185
+ this.activeRoute.params
186
+ .pipe(this.takeUntilDestroy)
187
+ .subscribe(({ id }) => {
188
+ this.facade.select(id);
189
+ this.id = id;
190
+ });
191
+
192
+ this.activeRoute.queryParams
193
+ .pipe(this.takeUntilDestroy)
194
+ .subscribe(({ edit }) => {
195
+ if (edit) {
196
+ this.mode = 'update';
197
+ this.generateComponents('edit');
198
+ }
199
+ });
200
+ }
201
+
202
+ this.uniqueProvider.set(async (values) => {
203
+ const filter: ICrudFilter = {
204
+ query: [],
205
+ };
206
+
207
+ Object.keys(values).forEach((key) => {
208
+ filter.query.push({
209
+ key: key,
210
+ value: values[key],
211
+ type: '=',
212
+ });
213
+ });
214
+
215
+ if (this.id) {
216
+ filter.query.push({
217
+ key: 'id',
218
+ value: this.id,
219
+ type: '!=',
220
+ });
221
+ }
222
+ const { totalCount } = await this.service.getList(filter);
223
+
224
+ return !totalCount;
225
+ });
226
+
227
+ if (this.config.details) {
228
+ const compiledComponents: {
229
+ component: any;
230
+ factory: ComponentFactory<any>;
231
+ }[] = await this.dynamicComponentLoader.getComponentsWithFactories({
232
+ components: [
233
+ ...(this.config.details &&
234
+ this.config.details['components'] &&
235
+ this.config.details['components'].top
236
+ ? [this.config.details['components'].top]
237
+ : []),
238
+ ...(this.config.details &&
239
+ this.config.details['components'] &&
240
+ this.config.details['components'].bottom
241
+ ? [this.config.details['components'].bottom]
242
+ : []),
243
+ ],
244
+ });
245
+
246
+ this.detailsOptions.set({
247
+ type: this.config.type,
248
+ item: this.facade.selected,
249
+ cellPipe: this.config.details
250
+ ? (this.config.details as { cellPipe?: ICellPipe<T> }).cellPipe
251
+ : null,
252
+ componentFactories: {
253
+ top:
254
+ this.config.details &&
255
+ this.config.details['components'] &&
256
+ this.config.details['components'].top
257
+ ? compiledComponents.find(
258
+ (cc) =>
259
+ cc.component === this.config.details['components'].top,
260
+ ).factory
261
+ : null,
262
+ bottom:
263
+ this.config.details &&
264
+ this.config.details['components'] &&
265
+ this.config.details['components'].bottom
266
+ ? compiledComponents.find(
267
+ (cc) =>
268
+ cc.component === this.config.details['components'].bottom,
269
+ ).factory
270
+ : null,
271
+ },
272
+ });
273
+ }
274
+
275
+ this.initPageOptions();
276
+
277
+ effect(() => {
278
+ this.item = this.facade.selected();
279
+ this.initPageOptions();
280
+ this.cd.detectChanges();
281
+ });
282
+
283
+ this.refreshDynamicInstance();
284
+ }
285
+
286
+ ngOnDestroy() {
287
+ super.ngOnDestroy();
288
+
289
+ this.subscription.unsubscribe();
290
+ }
291
+
292
+ private async generateComponents(mode: 'add' | 'edit') {
293
+ const compiledComponents =
294
+ await this.dynamicComponentLoader.getComponentsWithFactories({
295
+ components: [
296
+ ...(this.config[mode] &&
297
+ this.config[mode]['components'] &&
298
+ this.config[mode]['components'].top
299
+ ? [this.config[mode]['components'].top]
300
+ : []),
301
+ ...(this.config[mode] &&
302
+ this.config[mode]['components'] &&
303
+ this.config[mode]['components'].bottom
304
+ ? [this.config[mode]['components'].bottom]
305
+ : []),
306
+ ],
307
+ });
308
+
309
+ const topComponentFactory = compiledComponents.find(
310
+ (cc) => cc.component === this.config[mode]['components']?.top,
311
+ )?.factory;
312
+
313
+ const bottomComponentFactory = compiledComponents.find(
314
+ (cc) => cc.component === this.config[mode]['components']?.bottom,
315
+ )?.factory;
316
+
317
+ if (!this.topTpl().get(0) && topComponentFactory) {
318
+ this.topTpl().createComponent(topComponentFactory);
319
+ }
320
+
321
+ if (!this.bottomTpl().get(0) && bottomComponentFactory) {
322
+ this.bottomTpl().createComponent(bottomComponentFactory);
323
+ }
324
+ }
325
+
326
+ onPartialChange(val: Partial<T>) {
327
+ this.formPartialValue = val;
328
+ }
329
+
330
+ onChange(val: T) {
331
+ this.formValue = val;
332
+ }
333
+
334
+ onValidChange(val: boolean) {
335
+ this.formValid = val;
336
+ }
337
+
338
+ private initPageOptions(): void {
339
+ this.pageOptions.set({
340
+ ...this.pageOptions(),
341
+ title: this.getTitle(),
342
+ endButtons: this.getButtons(),
343
+ });
344
+ }
345
+
346
+ private getButtons(): Array<IIconButtonOptions> {
347
+ const options = getModelOptions(this.config.type);
348
+
349
+ switch (this.mode) {
350
+ case 'create':
351
+ return [
352
+ {
353
+ icon: 'add',
354
+ text: 'add',
355
+ handler: () => {
356
+ if (this.checkFirstInvalid()) {
357
+ return;
358
+ }
359
+
360
+ this.facade.create(this.formValue);
361
+ this.location.back();
362
+ },
363
+ },
364
+ ];
365
+ case 'update':
366
+ return [
367
+ ...(this.config.details
368
+ ? [
369
+ {
370
+ icon: 'close',
371
+ text: 'cancel',
372
+ handler: () => {
373
+ this.mode = 'details';
374
+ this.initPageOptions();
375
+ this.topTpl().clear();
376
+ this.bottomTpl().clear();
377
+ },
378
+ disabled$: new BehaviorSubject(false),
379
+ },
380
+ ]
381
+ : []),
382
+ {
383
+ icon: 'save',
384
+ text: 'save',
385
+ handler: () => {
386
+ if (this.checkFirstInvalid()) {
387
+ return;
388
+ }
389
+
390
+ this.formPartialValue.id = this.id;
391
+ this.facade.updatePartial(this.formPartialValue as any);
392
+
393
+ if (this.config.details) {
394
+ this.mode = 'details';
395
+ this.initPageOptions();
396
+ } else {
397
+ this.location.back();
398
+ }
399
+ },
400
+ },
401
+ ];
402
+ case 'details':
403
+ return this.config.edit &&
404
+ (!options?.update?.enabled ||
405
+ SpecificationService.valid(this.item, options?.update?.enabled))
406
+ ? [
407
+ {
408
+ icon: 'create',
409
+ text: 'edit',
410
+ handler: () => {
411
+ this.mode = 'update';
412
+ this.generateComponents('edit');
413
+ this.initPageOptions();
414
+ },
415
+ disabled$: new BehaviorSubject(false),
416
+ },
417
+ ]
418
+ : [];
419
+ }
420
+ }
421
+
422
+ private getTitle(): string {
423
+ const options = getModelOptions(this.config.type);
424
+
425
+ const prefix =
426
+ options.titleKey && this.item
427
+ ? this.removeParagraph(this.item[options.titleKey]) + ' - '
428
+ : '';
429
+
430
+ switch (this.mode) {
431
+ case 'create':
432
+ return 'add';
433
+ case 'update':
434
+ return prefix + this.translateService.instant('change');
435
+ case 'details':
436
+ return prefix + this.translateService.instant('details');
437
+ }
438
+ }
439
+
440
+ private removeParagraph(val: string): string {
441
+ if (!val || val.indexOf('<p>') !== 0) return val;
442
+
443
+ const div = document.createElement('div');
444
+ div.innerHTML = val;
445
+
446
+ const item = div.querySelectorAll('p').item(0);
447
+
448
+ return item.innerHTML;
449
+ }
450
+
451
+ private checkFirstInvalid(): boolean {
452
+ this.cd.detectChanges();
453
+
454
+ const form =
455
+ this.template() === 'default'
456
+ ? this.standardComponents()?.[0]?.formComponents?.[0].form
457
+ : this.baseInstance.formComponents()[0].form;
458
+
459
+ this.cd.detectChanges();
460
+
461
+ if (form.valid) return false;
462
+
463
+ const invalidFields = [];
464
+
465
+ this.getInvalidFields(form, invalidFields, '');
466
+
467
+ this.toastService.info({
468
+ title: this.translateService.instant('INPUT.ERRORS.requires'),
469
+ message: invalidFields.slice(0, 3).join('<br/>'),
470
+ });
471
+
472
+ return true;
473
+ }
474
+
475
+ private getInvalidFields(
476
+ control: AbstractControl,
477
+ invalidFields: any[],
478
+ baseField: string,
479
+ key = null,
480
+ ) {
481
+ if (control.valid) return;
482
+
483
+ const field = key
484
+ ? baseField + ' > ' + this.translateService.instant('MODEL.' + key)
485
+ : baseField;
486
+
487
+ if (control.errors?.customMessage) {
488
+ invalidFields.push(field + ` (${control.errors.customMessage})`);
489
+ return;
490
+ }
491
+
492
+ if (control instanceof UntypedFormControl) {
493
+ invalidFields.push(field);
494
+ }
495
+
496
+ if (control instanceof UntypedFormGroup) {
497
+ Object.keys(control.controls).forEach((groupKey) => {
498
+ this.getInvalidFields(
499
+ control.controls[groupKey],
500
+ invalidFields,
501
+ field,
502
+ groupKey,
503
+ );
504
+ });
505
+ }
506
+
507
+ if (control instanceof UntypedFormArray) {
508
+ control.controls.forEach((c, index) => {
509
+ this.getInvalidFields(c, invalidFields, field + `(${index + 1})`);
510
+ });
511
+ }
512
+ }
513
+ }
@@ -0,0 +1,50 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import { Component } from '@angular/core';
3
+
4
+ import { DetailsComponent, FormComponent } from '@smartsoft001/angular';
5
+ import { IEntity } from '@smartsoft001/domain-core';
6
+
7
+ import { FormOptionsPipe } from '../../../pipes';
8
+ import { CrudItemPageBaseComponent } from '../base/base.component';
9
+
10
+ @Component({
11
+ selector: 'smart-crud-item-standard-page',
12
+ template: `
13
+ @if (mode() === 'details') {
14
+ @if (detailsOptions()) {
15
+ <smart-details [options]="detailsOptions()"></smart-details>
16
+ }
17
+ } @else {
18
+ <ng-container [ngTemplateOutlet]="editTpl"></ng-container>
19
+ }
20
+
21
+ <ng-template #editTpl>
22
+ <smart-form
23
+ [options]="
24
+ selected()
25
+ | smartFormOptions
26
+ : mode()
27
+ : config?.type
28
+ : uniqueProvider()
29
+ : config.inputComponents
30
+ "
31
+ (valuePartialChange)="onPartialChange.emit($event)"
32
+ (valueChange)="onChange.emit($event)"
33
+ (validChange)="onValidChange.emit($event)"
34
+ ></smart-form>
35
+ </ng-template>
36
+
37
+ <br /><br />
38
+ `,
39
+ styleUrls: ['./standard.component.scss'],
40
+ imports: [
41
+ DetailsComponent,
42
+ FormComponent,
43
+ FormOptionsPipe,
44
+ NgTemplateOutlet,
45
+ FormComponent,
46
+ ],
47
+ })
48
+ export class ItemStandardComponent<
49
+ T extends IEntity<string>,
50
+ > extends CrudItemPageBaseComponent<T> {}
@@ -0,0 +1,29 @@
1
+ import {
2
+ Directive,
3
+ inject,
4
+ input,
5
+ viewChild,
6
+ ViewContainerRef,
7
+ } from '@angular/core';
8
+
9
+ import {
10
+ BaseComponent,
11
+ DynamicComponentType,
12
+ IListOptions,
13
+ } from '@smartsoft001/angular';
14
+ import { IEntity } from '@smartsoft001/domain-core';
15
+
16
+ import { CrudFullConfig } from '../../../crud.config';
17
+
18
+ @Directive()
19
+ export abstract class CrudListPageBaseComponent<
20
+ T extends IEntity<string>,
21
+ > extends BaseComponent {
22
+ public config = inject(CrudFullConfig<T>);
23
+
24
+ static smartType: DynamicComponentType = 'crud-list-page';
25
+
26
+ contentTpl = viewChild<ViewContainerRef>('contentTpl');
27
+
28
+ listOptions = input<IListOptions<T>>();
29
+ }
@@ -0,0 +1,77 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import { Component } from '@angular/core';
3
+ import { RouterModule } from '@angular/router';
4
+ import { TranslateModule } from '@ngx-translate/core';
5
+ import { moduleMetadata } from '@storybook/angular';
6
+ import type { Meta, StoryObj } from '@storybook/angular';
7
+
8
+ import { SharedModule } from '@smartsoft001/angular';
9
+ import { Field, Model } from '@smartsoft001/models';
10
+
11
+ import { CrudModule } from '../../crud.module';
12
+
13
+ @Component({
14
+ template: ` <p>Test details</p> `,
15
+ })
16
+ export class TestDetailsComponent {}
17
+
18
+ @Model({})
19
+ export class Note {
20
+ @Field({ list: true })
21
+ title: string;
22
+
23
+ @Field({ list: true })
24
+ body: string;
25
+ }
26
+
27
+ const meta: Meta = {
28
+ title: 'Smart-Crud/List Page',
29
+ component: 'smart-crud-list-page',
30
+ decorators: [
31
+ moduleMetadata({
32
+ declarations: [TestDetailsComponent],
33
+ imports: [
34
+ CommonModule,
35
+ SharedModule,
36
+ TranslateModule.forRoot(),
37
+ RouterModule.forRoot([], { useHash: true }),
38
+ CrudModule.forFeature({
39
+ routing: true,
40
+ config: {
41
+ type: Note,
42
+ title: 'Note',
43
+ entity: 'notes',
44
+ export: true,
45
+ pagination: { limit: 25 },
46
+ apiUrl: 'http://207.180.210.142:1201/api/notes',
47
+ },
48
+ }),
49
+ ],
50
+ }),
51
+ ],
52
+ };
53
+
54
+ export default meta;
55
+ type Story = StoryObj<typeof meta>;
56
+
57
+ export const Export: Story = {
58
+ name: 'Z eksportem',
59
+ render: () => ({
60
+ template: `
61
+ <div style="height: 400px">
62
+ <smart-crud-list-page></smart-crud-list-page>
63
+ </div>
64
+ `,
65
+ }),
66
+ };
67
+
68
+ export const CustomDetails: Story = {
69
+ name: 'Niestandardowe szczegóły',
70
+ render: () => ({
71
+ template: `
72
+ <div style="height: 400px">
73
+ <smart-crud-list-page></smart-crud-list-page>
74
+ </div>
75
+ `,
76
+ }),
77
+ };