@verisoft/store 20.1.1 → 20.1.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.
@@ -1,283 +0,0 @@
1
- import { Router } from '@angular/router';
2
- import { Actions, createEffect, ofType } from '@ngrx/effects';
3
- import { BaseHttpService } from '@verisoft/core';
4
- import {
5
- catchError,
6
- EMPTY,
7
- map,
8
- Observable,
9
- of,
10
- switchMap,
11
- tap,
12
- withLatestFrom,
13
- } from 'rxjs';
14
- import {
15
- createDeleteDetailAction,
16
- createDeleteDetailFailureAction,
17
- createDeleteDetailSuccessAction,
18
- createInitDetailAction,
19
- createInitNewDetailAction,
20
- createLoadDetailFailureAction,
21
- createLoadDetailSuccessAction,
22
- createSaveDetailAction,
23
- createSaveDetailFailureAction,
24
- createSaveDetailSuccessAction,
25
- } from './detail.actions';
26
-
27
- export interface CreateInitFormConfig<T> {
28
- service?: BaseHttpService<T>;
29
- get?: (obj: any) => Observable<T>;
30
- snackbar?: any;
31
- errorMessage?: string;
32
- notFoundMessage?: string;
33
- successMessge?: string;
34
- }
35
-
36
- export interface DetailEffectConfig<T> {
37
- detail$: Observable<T | undefined>;
38
- snackbar?: any;
39
- router?: Router;
40
- redirectPath?: string;
41
- useItemId?: boolean;
42
- navigationMap?: Map<keyof T, string>;
43
- }
44
-
45
- export function createInitDetailEffect<T>(
46
- detailsRepository: string,
47
- actions$: Actions,
48
- config: CreateInitFormConfig<T>
49
- ) {
50
- return createEffect(() => {
51
- return actions$.pipe(
52
- ofType(createInitDetailAction(detailsRepository)),
53
- switchMap(({ obj }) => {
54
- const get$: Observable<any> = (config?.service?.get(obj as any) ??
55
- config?.get?.(obj)) as Observable<any>;
56
- if (!get$) return EMPTY;
57
- return get$?.pipe(
58
- map((item) => {
59
- return createLoadDetailSuccessAction(detailsRepository)({ item });
60
- }),
61
- catchError((error) => {
62
- if (error.status === 404) {
63
- config?.snackbar?.showError(config?.notFoundMessage ?? error);
64
- } else {
65
- config?.snackbar?.showError(config?.errorMessage ?? error);
66
- }
67
-
68
- console.error('Error', error);
69
- return of(
70
- createLoadDetailFailureAction(detailsRepository)({ error })
71
- );
72
- })
73
- );
74
- })
75
- );
76
- });
77
- }
78
-
79
- export function createInitNewDetailEffect(
80
- detailsRepository: string,
81
- actions$: Actions
82
- ) {
83
- return createEffect(() =>
84
- actions$.pipe(
85
- ofType(createInitNewDetailAction(detailsRepository)),
86
- map(() => {
87
- return createLoadDetailSuccessAction(detailsRepository)({ item: null });
88
- })
89
- )
90
- );
91
- }
92
-
93
- export function createSaveDetailEffect<
94
- T extends { id: number | string },
95
- SERVICE extends BaseHttpService<T>
96
- >(
97
- detailsRepository: string,
98
- actions$: Actions,
99
- service: SERVICE,
100
- config: DetailEffectConfig<T>
101
- ) {
102
- return createEffect(() =>
103
- actions$.pipe(
104
- ofType(createSaveDetailAction(detailsRepository)),
105
- withLatestFrom(config.detail$),
106
- switchMap(([, savedItem]) => {
107
- const isNew = !savedItem?.id;
108
- const saveAction = isNew
109
- ? service.post(savedItem as T)
110
- : service.put(savedItem.id, savedItem);
111
- return saveAction.pipe(
112
- map((item) => {
113
- if (isNew && config?.router && config?.redirectPath) {
114
- const suffix = config?.useItemId ? '/' + item.id : '';
115
- config.router.navigateByUrl(config.redirectPath + suffix);
116
- }
117
-
118
- if (isNew && config?.router && config?.navigationMap) {
119
- const url = constructUrlFromNavigationMap(item, config.navigationMap)
120
- config.router.navigateByUrl(url);
121
- }
122
-
123
- return createSaveDetailSuccessAction(detailsRepository)({
124
- item: item,
125
- });
126
- }),
127
- catchError((error) => {
128
- console.error('Error', error);
129
- return of(
130
- createSaveDetailFailureAction(detailsRepository)({ error })
131
- );
132
- })
133
- );
134
- })
135
- )
136
- );
137
- }
138
-
139
- export function createSaveDetailSuccessEffect(
140
- detailsRepository: string,
141
- actions$: Actions,
142
- config?: CreateInitFormConfig<unknown>
143
- ) {
144
- return createEffect(
145
- () =>
146
- actions$.pipe(
147
- ofType(createSaveDetailSuccessAction(detailsRepository)),
148
- map((data: any) => {
149
- config?.snackbar?.showSuccess(
150
- config?.successMessge ?? `Item successfully saved.`
151
- );
152
- return createInitDetailAction(detailsRepository)({ obj: data.item.id });
153
- })
154
- )
155
- );
156
- }
157
-
158
- export function createSaveDetailFailureEffect(
159
- detailsRepository: string,
160
- actions$: Actions,
161
- config?: CreateInitFormConfig<unknown>
162
- ) {
163
- return createEffect(
164
- () =>
165
- actions$.pipe(
166
- ofType(createSaveDetailFailureAction(detailsRepository)),
167
- tap(({ error }) => {
168
- config?.snackbar?.showError(
169
- config.errorMessage ?? `Error saving item. ${error.message}`
170
- );
171
- })
172
- ),
173
- {
174
- dispatch: false,
175
- }
176
- );
177
- }
178
-
179
- export function createDeleteDetailEffect<
180
- T extends { id: number | string },
181
- SERVICE extends BaseHttpService<T>
182
- >(
183
- detailsRepository: string,
184
- actions$: Actions,
185
- service: SERVICE,
186
- config: DetailEffectConfig<T>,
187
- ) {
188
- return createEffect(() =>
189
- actions$.pipe(
190
- ofType(createDeleteDetailAction(detailsRepository)),
191
- withLatestFrom(config.detail$),
192
- switchMap(([, savedItem]) => {
193
- const id = savedItem?.id;
194
- if (!id) {
195
- return of(
196
- createSaveDetailFailureAction(detailsRepository)({
197
- error: 'Item has no id',
198
- })
199
- );
200
- }
201
-
202
- return service.delete(id).pipe(
203
- map(() => {
204
- if (config?.router && config?.redirectPath) {
205
- config.router.navigateByUrl(`${config.redirectPath}`);
206
- }
207
-
208
- if (config?.router && config?.navigationMap) {
209
- const url = constructUrlFromNavigationMap(savedItem, config.navigationMap)
210
- config.router.navigateByUrl(url);
211
- }
212
-
213
- return createDeleteDetailSuccessAction(detailsRepository)();
214
- }),
215
- catchError((error) => {
216
- console.error('Error', error);
217
- config.snackbar?.showError('Failed to delete item.');
218
- return of(
219
- createDeleteDetailFailureAction(detailsRepository)({ error })
220
- );
221
- })
222
- );
223
- })
224
- )
225
- );
226
- }
227
-
228
- export function createDeleteDetailSuccessEffect(
229
- detailsRepository: string,
230
- actions$: Actions,
231
- config?: CreateInitFormConfig<unknown>
232
- ) {
233
- return createEffect(
234
- () =>
235
- actions$.pipe(
236
- ofType(createDeleteDetailSuccessAction(detailsRepository)),
237
- tap(() => {
238
- config?.snackbar?.showSuccess(
239
- config.successMessge ?? `Item successfully deleted.`
240
- );
241
- })
242
- ),
243
- {
244
- dispatch: false,
245
- }
246
- );
247
- }
248
-
249
- export function createDeleteDetailFailureEffect(
250
- detailsRepository: string,
251
- actions$: Actions,
252
- config?: CreateInitFormConfig<unknown>
253
- ) {
254
- return createEffect(
255
- () =>
256
- actions$.pipe(
257
- ofType(createDeleteDetailFailureAction(detailsRepository)),
258
- tap(({ error }) => {
259
- config?.snackbar?.showError(
260
- config.errorMessage ?? `Error deleting item. ${error.message}`
261
- );
262
- })
263
- ),
264
- {
265
- dispatch: false,
266
- }
267
- );
268
- }
269
-
270
- function constructUrlFromNavigationMap<T extends object>(
271
- item: T,
272
- navigationMap: Map<keyof T, string>)
273
- : string {
274
- let url = '';
275
- navigationMap.forEach((urlPartValue, suffixPropertyKey) => {
276
- const suffix = item[suffixPropertyKey];
277
- if (suffix) {
278
- url += `${urlPartValue}/${suffix}`;
279
- }
280
- });
281
-
282
- return url;
283
- }
@@ -1,42 +0,0 @@
1
- export interface DetailState<T> {
2
- loaded: boolean;
3
- item?: T;
4
- formState?: FormState;
5
- error?: string | null;
6
- saveItemState: SaveItemState;
7
- backendValidationErrors: BackendValidationError[];
8
- }
9
-
10
- export interface BackendValidationError {
11
- code: string;
12
- message: string;
13
- parameters: string;
14
- }
15
-
16
- export interface ChosenRowsState<T> {
17
- rows: Array<T>;
18
- }
19
-
20
- export interface SaveItemState {
21
- saveInProgress: boolean;
22
- error?: string | null;
23
- }
24
-
25
- export const INITIAL_SAVE_ITEM_STATE: SaveItemState = {
26
- saveInProgress: false,
27
- };
28
-
29
- export const INITIAL_CHOSEN_ROW_STATE: ChosenRowsState<any> = {
30
- rows: []
31
- }
32
-
33
- export const INITIAL_DETAIL_STATE: DetailState<any> = {
34
- loaded: false,
35
- saveItemState: INITIAL_SAVE_ITEM_STATE,
36
- backendValidationErrors: [],
37
- };
38
-
39
- export interface FormState {
40
- dirty: boolean;
41
- valid: boolean;
42
- }
@@ -1,147 +0,0 @@
1
- import { ActionCreator, createReducer, on, ReducerTypes } from '@ngrx/store';
2
- import {
3
- createInitDetailAction,
4
- createInitNewDetailAction,
5
- createLoadDetailFailureAction,
6
- createLoadDetailSuccessAction,
7
- createResetStateAction,
8
- createSaveDetailAction,
9
- createSaveDetailFailureAction,
10
- createSaveDetailSuccessAction,
11
- createUpdateDetailAction,
12
- createUpdateDetailSetErrorsAction,
13
- createUpdateFormStateAction,
14
- } from './detail.actions';
15
- import {
16
- DetailState,
17
- INITIAL_DETAIL_STATE,
18
- INITIAL_SAVE_ITEM_STATE,
19
- } from './detail.models';
20
-
21
- export function createDetailReducers<
22
- T = any,
23
- TState extends DetailState<T> = DetailState<T>
24
- >(
25
- detailRepository: string,
26
- initialState: TState = INITIAL_DETAIL_STATE as unknown as TState,
27
- ...ons: ReducerTypes<TState, readonly ActionCreator[]>[]
28
- ) {
29
- return createReducer(
30
- initialState,
31
- on(createInitDetailAction(detailRepository), (state) => {
32
- return {
33
- ...state,
34
- loaded: false,
35
- error: null,
36
- };
37
- }),
38
-
39
- on(createInitNewDetailAction(detailRepository), (state) => {
40
- return {
41
- ...state,
42
- loaded: false,
43
- error: null,
44
- };
45
- }),
46
-
47
- on(createLoadDetailSuccessAction(detailRepository), (state, { item }) => {
48
- return {
49
- ...state,
50
- loaded: true,
51
- item,
52
- };
53
- }),
54
-
55
- on(createLoadDetailFailureAction(detailRepository), (state, { error }) => {
56
- return {
57
- ...state,
58
- loaded: true,
59
- error,
60
- };
61
- }),
62
-
63
- on(createUpdateDetailAction(detailRepository), (state, { item }) => {
64
- return {
65
- ...state,
66
- item,
67
- formState: {
68
- dirty: true,
69
- valid: state.formState?.valid ?? true,
70
- },
71
- };
72
- }),
73
-
74
- on(createUpdateDetailSetErrorsAction(detailRepository), (state, { error }) => {
75
- return {
76
- ...state,
77
- backendValidationErrors: error
78
- }
79
- }),
80
-
81
- on(
82
- createUpdateFormStateAction(detailRepository),
83
- (state, { formState }) => {
84
- return {
85
- ...state,
86
- formState,
87
- };
88
- }
89
- ),
90
-
91
- on(createSaveDetailSuccessAction(detailRepository), (state) => {
92
- return {
93
- ...state,
94
- formState: {
95
- dirty: false,
96
- valid: state.formState?.valid ?? true,
97
- },
98
- };
99
- }),
100
-
101
- on(createResetStateAction(detailRepository), () => {
102
- return {
103
- ...(initialState as any),
104
- };
105
- }),
106
-
107
- on(createSaveDetailFailureAction(detailRepository), (state, { error }) => {
108
- return {
109
- ...state,
110
- backendValidationErrors: error.error
111
- }
112
- }),
113
- ...ons
114
- );
115
- }
116
-
117
- export function createSaveDetailReducers(
118
- detailRepository: string,
119
- initialState = INITIAL_SAVE_ITEM_STATE
120
- ) {
121
- return createReducer(
122
- initialState,
123
-
124
- on(createSaveDetailAction(detailRepository), (state) => {
125
- return {
126
- ...state,
127
- saveInProgress: true,
128
- };
129
- }),
130
-
131
- on(createSaveDetailSuccessAction(detailRepository), (state, { item }) => {
132
- return {
133
- ...state,
134
- item,
135
- saveInProgress: false,
136
- };
137
- }),
138
-
139
- on(createSaveDetailFailureAction(detailRepository), (state, { error }) => {
140
- return {
141
- ...state,
142
- error,
143
- saveInProgress: false,
144
- };
145
- })
146
- );
147
- }
@@ -1,93 +0,0 @@
1
- import { createAction, props } from '@ngrx/store';
2
- import { Page, Sort } from '@verisoft/core';
3
-
4
- enum TablePageAction {
5
- GET_PAGE = 'Get page',
6
- CREATE_STATIC_FILTER = 'Create static filter',
7
- DATA_LOAD_SUCCESS = 'Data load success',
8
- DATA_LOAD_ERROR = 'Data load error',
9
- REFRESH_PAGE = 'Refresh page',
10
- FILTER_PAGE = 'Filter page',
11
- CHANGE_PAGE_SIZE = 'Change page size',
12
- SORT_PAGE = 'Sort page',
13
- DESTROY = 'Destroy',
14
- SELECT_ITEMS = 'Select items',
15
- REMOVE_RANGE = 'Remove range',
16
- }
17
-
18
- export function createGetPageTableAction<T = any>(tableRepository: string) {
19
- return createAction(
20
- `${tableRepository} ${TablePageAction.GET_PAGE}`,
21
- props<{
22
- page: number;
23
- size: number;
24
- id?: string | null;
25
- filter?: Partial<T>;
26
- sort?: Sort[];
27
- }>()
28
- );
29
- }
30
-
31
- export function createStaticFilterTableAction<T = any>(
32
- tableRepository: string
33
- ) {
34
- return createAction(
35
- `${tableRepository} ${TablePageAction.CREATE_STATIC_FILTER}`,
36
- props<{
37
- filter?: Partial<T>;
38
- }>()
39
- );
40
- }
41
-
42
- export function createDataLoadSuccessTableAction<T>(tableRepository: string) {
43
- return createAction(
44
- `${tableRepository} ${TablePageAction.DATA_LOAD_SUCCESS}`,
45
- props<{ gPage: Page<T> }>()
46
- );
47
- }
48
-
49
- export function createDataLoadErrorTableAction(tableRepository: string) {
50
- return createAction(
51
- `${tableRepository} ${TablePageAction.DATA_LOAD_ERROR}`,
52
- props<{ error: any }>()
53
- );
54
- }
55
-
56
- export function createChangePageSizeTableAction(tableRepository: string) {
57
- return createAction(
58
- `${tableRepository} ${TablePageAction.CHANGE_PAGE_SIZE}`,
59
- props<{ size: number }>()
60
- );
61
- }
62
-
63
- // TODO: use action in delete item effect
64
- export function createRefreshPageTableAction(tableRepository: string) {
65
- return createAction(`${tableRepository} ${TablePageAction.REFRESH_PAGE}`);
66
- }
67
-
68
- export function createFilterPageTableAction<T>(tableRepository: string) {
69
- return createAction(
70
- `${tableRepository} ${TablePageAction.REFRESH_PAGE}`,
71
- props<{ filter: Partial<T> }>()
72
- );
73
- }
74
-
75
- export function createResetTableFilterAction(tableRepository: string) {
76
- return createAction(`${tableRepository} ${TablePageAction.REFRESH_PAGE}`);
77
- }
78
-
79
- export function createDestroyTableAction(tableRepository: string) {
80
- return createAction(`${tableRepository} ${TablePageAction.DESTROY}`);
81
- }
82
-
83
- export function createSelectItemsTableAction<T>(tableRepository: string) {
84
- return createAction(
85
- `${tableRepository} ${TablePageAction.SELECT_ITEMS}`,
86
- props<{ selectedItems: T[] }>()
87
- );
88
- }
89
-
90
- export function createRemoveRangeTableAction(tableRepository: string) {
91
- return createAction(`${tableRepository} ${TablePageAction.REMOVE_RANGE}`
92
- );
93
- }
@@ -1,99 +0,0 @@
1
- import { Actions, createEffect, ofType } from '@ngrx/effects';
2
- import { createFeatureSelector, createSelector, Store } from '@ngrx/store';
3
- import { BaseHttpService, DEFAULT_SEARCH_LIMIT, Page, RequestParams } from '@verisoft/core';
4
- import { catchError, map, Observable, of, switchMap, withLatestFrom } from 'rxjs';
5
- import {
6
- createGetPageTableAction,
7
- createDataLoadSuccessTableAction,
8
- createDataLoadErrorTableAction,
9
- createRemoveRangeTableAction,
10
- } from './actions';
11
- import { TableState } from './models';
12
-
13
- export interface CreateGetPageActionConfig<T> {
14
- service?: BaseHttpService<T>;
15
- fetchList?: (requestParams: RequestParams<any>) => Observable<Page<T>>;
16
- snackbar?: any;
17
- ngrxFeatureKey?: string;
18
- requireFilters?: boolean;
19
- }
20
-
21
- export function createGetPageTableEffect<T>(
22
- tableRepository: string,
23
- actions$: Actions,
24
- config: CreateGetPageActionConfig<T>
25
- ) {
26
- return createEffect(() => {
27
- return actions$.pipe(
28
- ofType(createGetPageTableAction(tableRepository)),
29
- switchMap(({ page, filter, sort, size }) => {
30
- const requestParams: RequestParams<any> = {
31
- offset: page * size,
32
- limit: size,
33
- sort,
34
- filter,
35
- };
36
-
37
- const params = config.service?.createParams(requestParams) ?? requestParams;
38
-
39
- const fetchList$: Observable<Page<T>> = (config.service?.fetchList(
40
- params
41
- ) ?? config.fetchList?.(params)) as Observable<Page<T>>;
42
- if (!fetchList$) {
43
- throw new Error('Service or fetchList$ must by defined.');
44
- }
45
-
46
- return fetchList$.pipe(
47
- map((gPage) => {
48
- const p = { ...gPage, number: page };
49
- return createDataLoadSuccessTableAction(tableRepository)({
50
- gPage: p,
51
- });
52
- }),
53
- catchError((error) => {
54
- config.snackbar?.showError(error.message);
55
- return of(
56
- createDataLoadErrorTableAction(tableRepository)({ error })
57
- );
58
- })
59
- );
60
- })
61
- );
62
- });
63
- }
64
-
65
- export function createRemoveRangeTableEffect<T>(
66
- tableRepository: string,
67
- ngrxFeatureKey: string,
68
- actions$: Actions,
69
- store$: Store<any>,
70
- config: CreateGetPageActionConfig<T>
71
- ) {
72
- return createEffect(() => {
73
- if(!config?.service) {
74
- throw new Error('Service must be defined!');
75
- }
76
-
77
- const selectedItems = createSelector(
78
- createFeatureSelector<any>(ngrxFeatureKey),
79
- (state: any) => {
80
- return (state?.[tableRepository] as TableState<any>)?.selectedItems as any;
81
- }
82
- );
83
-
84
- return actions$.pipe(
85
- ofType(createRemoveRangeTableAction(tableRepository)),
86
- withLatestFrom(selectedItems),
87
- switchMap(([, { selectedItems }]) => {
88
- return config.service!.removeRange(selectedItems).pipe(
89
- map(() => {
90
- return createGetPageTableAction(tableRepository)({ page: 0, size: DEFAULT_SEARCH_LIMIT })
91
- }),
92
- catchError(error => {
93
- return of(createDataLoadErrorTableAction(tableRepository)({ error }))
94
- })
95
- );
96
- })
97
- );
98
- });
99
- }
@@ -1,20 +0,0 @@
1
- import { DEFAULT_SEARCH_LIMIT, DEFAULT_SEARCH_PARAMS, Page, RequestParams } from "@verisoft/core";
2
-
3
- export interface TableState<T> {
4
- dataLoading: boolean;
5
- requestParams: RequestParams<T>;
6
- gPage?: Page<T>;
7
- error?: string | null;
8
- selectedItems?: T[];
9
- }
10
-
11
- export const INITIAL_TABLE_STATE: TableState<any> = {
12
- gPage: {
13
- data: [],
14
- size: DEFAULT_SEARCH_LIMIT,
15
- total: 0.
16
- } as unknown as Page<any>,
17
- dataLoading: false,
18
- requestParams: DEFAULT_SEARCH_PARAMS,
19
- selectedItems: []
20
- };