Passed
Push — trunk ( 024f41...cfd10b )
by Christian
15:08 queued 13s
created

src/Administration/Resources/app/administration/src/app/mixin/listing.mixin.ts   B

Complexity

Total Complexity 52
Complexity/F 1.86

Size

Lines of Code 375
Function Count 28

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 289
dl 0
loc 375
rs 7.44
c 0
b 0
f 0
wmc 52
mnd 24
bc 24
fnc 28
bpm 0.8571
cpm 1.8571
noi 0

27 Functions

Rating   Name   Duplication   Size   Complexity  
A listing.mixin.ts ➔ term 0 4 2
A listing.mixin.ts ➔ getList 0 5 1
B listing.mixin.ts ➔ created 0 25 5
A listing.mixin.ts ➔ onSearch 0 13 2
A listing.mixin.ts ➔ addQueryScores 0 17 3
A listing.mixin.ts ➔ sortBy 0 3 1
A listing.mixin.ts ➔ selection 0 3 5
A listing.mixin.ts ➔ selectionCount 0 3 1
A listing.mixin.ts ➔ maxPage 0 2 1
A listing.mixin.ts ➔ searchRankingFields 0 7 2
A listing.mixin.ts ➔ filters 0 6 1
A listing.mixin.ts ➔ onPageChange 0 13 2
A listing.mixin.ts ➔ onRefresh 0 3 1
A listing.mixin.ts ➔ sortDirection 0 3 1
A listing.mixin.ts ➔ selectionArray 0 3 1
A listing.mixin.ts ➔ routeName 0 3 1
A listing.mixin.ts ➔ onSwitchFilter 0 5 1
B listing.mixin.ts ➔ onSortColumn 0 26 6
A listing.mixin.ts ➔ getMainListingParams 0 22 2
A listing.mixin.ts ➔ updateRoute 0 33 2
A listing.mixin.ts ➔ resetListing 0 12 1
A listing.mixin.ts ➔ onSort 0 17 2
A listing.mixin.ts ➔ isValidTerm 0 3 1
A listing.mixin.ts ➔ data 0 28 1
A listing.mixin.ts ➔ updateSelection 0 3 1
A listing.mixin.ts ➔ updateData 0 14 3
A listing.mixin.ts ➔ currentSortBy 0 3 2

How to fix   Complexity   

Complexity

Complex classes like src/Administration/Resources/app/administration/src/app/mixin/listing.mixin.ts often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/**
2
 * @package admin
3
 */
4
5
/* @private */
6
import type { Dictionary } from 'vue-router/types/router';
7
import type { RawLocation } from 'vue-router';
8
import type Criteria from '@shopware-ag/admin-extension-sdk/es/data/Criteria';
9
10
/* @private */
11
export {};
12
13
/* Mixin uses many untyped dependencies */
14
/* eslint-disable @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,max-len,@typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-explicit-any */
15
16
/**
17
 * @deprecated tag:v6.6.0 - Will be private
18
 */
19
Shopware.Mixin.register('listing', {
20
    inject: ['searchRankingService', 'feature'],
21
22
    data(): {
23
        page: number,
24
        limit: number,
25
        total: number,
26
        sortBy: string|null,
27
        sortDirection: string,
28
        naturalSorting: boolean,
29
        selection: Record<string, any>,
30
        term: string|undefined,
31
        disableRouteParams: boolean,
32
        searchConfigEntity: string|null,
33
        entitySearchable: boolean,
34
        freshSearchTerm: boolean,
35
        } {
36
        return {
37
            page: 1,
38
            limit: 25,
39
            total: 0,
40
            sortBy: null,
41
            sortDirection: 'ASC',
42
            naturalSorting: false,
43
            selection: [],
44
            term: undefined,
45
            disableRouteParams: false,
46
            searchConfigEntity: null,
47
            entitySearchable: true,
48
            freshSearchTerm: false,
49
        };
50
    },
51
52
    computed: {
53
        maxPage() {
54
            return Math.ceil(this.total / this.limit);
55
        },
56
57
        routeName() {
58
            return this.$route.name;
59
        },
60
61
        selectionArray() {
62
            return Object.values(this.selection);
63
        },
64
65
        selectionCount() {
66
            return this.selectionArray.length;
67
        },
68
69
        filters(): {
70
            active: boolean,
71
        }[] {
72
            // You can create your custom filters by defining the computed property "filters"
73
            return [];
74
        },
75
76
        searchRankingFields() {
77
            if (!this.searchConfigEntity) {
78
                return {};
79
            }
80
81
            return this.searchRankingService.getSearchFieldsByEntity(this.searchConfigEntity);
82
        },
83
84
        currentSortBy() {
85
            return this.freshSearchTerm ? null : this.sortBy;
86
        },
87
    },
88
89
    created() {
90
        if (this.disableRouteParams) {
91
            this.getList();
92
            return;
93
        }
94
95
        const actualQueryParameters: Dictionary<(string|null)[]|string|boolean> = this.$route.query;
96
97
        // When no route information are provided
98
        if (Shopware.Utils.types.isEmpty(actualQueryParameters)) {
99
            this.resetListing();
100
        } else {
101
            // When we get the parameters on the route, true and false will be a string so we should convert to boolean
102
            Object.keys(actualQueryParameters).forEach((key) => {
103
                if (actualQueryParameters[key] === 'true') {
104
                    actualQueryParameters[key] = true;
105
                } else if (actualQueryParameters[key] === 'false') {
106
                    actualQueryParameters[key] = false;
107
                }
108
            });
109
110
            // otherwise update local data and fetch from server
111
            this.updateData(actualQueryParameters);
112
            this.getList();
113
        }
114
    },
115
116
    watch: {
117
        // Watch for changes in query parameters and update listing
118
        '$route'(newRoute, oldRoute) {
119
            if (this.disableRouteParams) {
120
                return;
121
            }
122
123
            const query = this.$route.query;
124
125
            if (Shopware.Utils.types.isEmpty(query)) {
126
                this.resetListing();
127
            }
128
129
            // Update data information from the url
130
            this.updateData(query);
131
132
            if (newRoute.query[this.storeKey] !== oldRoute.query[this.storeKey] && this.filterCriteria.length) {
133
                // @ts-expect-error - filterCriteria is defined in base component
134
                this.filterCriteria = [];
135
                return;
136
            }
137
138
            // Fetch new list
139
            this.getList();
140
        },
141
142
        selection() {
143
            Shopware.State.commit('shopwareApps/setSelectedIds', Object.keys(this.selection));
144
        },
145
146
        term(newValue) {
147
            if (newValue && newValue.length) {
148
                this.freshSearchTerm = true;
149
            }
150
        },
151
152
        sortBy() {
153
            this.freshSearchTerm = false;
154
        },
155
156
        sortDirection() {
157
            this.freshSearchTerm = false;
158
        },
159
    },
160
161
    methods: {
162
        updateData(customData: {
163
            page?: number,
164
            limit?: number,
165
            term?: string,
166
            sortBy?: string,
167
            sortDirection?: string,
168
            naturalSorting?: boolean,
169
        }) {
170
            this.page = parseInt(customData.page as unknown as string, 10) || this.page;
171
            this.limit = parseInt(customData.limit as unknown as string, 10) || this.limit;
172
            this.term = customData.term ?? this.term;
173
            this.sortBy = customData.sortBy || this.sortBy;
174
            this.sortDirection = customData.sortDirection || this.sortDirection;
175
            this.naturalSorting = customData.naturalSorting || this.naturalSorting;
176
        },
177
178
        updateRoute(customQuery: {
179
            limit?: number,
180
            page?: number,
181
            term?: string,
182
            sortBy?: string,
183
            sortDirection?: string,
184
            naturalSorting?: boolean,
185
        }, queryExtension = {}) {
186
            // Get actual query parameter
187
            const query = customQuery || this.$route.query;
188
            const routeQuery = this.$route.query;
189
190
            // Create new route
191
            const route = {
192
                name: this.$route.name,
193
                params: this.$route.params,
194
                query: {
195
                    limit: query.limit || this.limit,
196
                    page: query.page || this.page,
197
                    term: query.term || this.term,
198
                    sortBy: query.sortBy || this.sortBy,
199
                    sortDirection: query.sortDirection || this.sortDirection,
200
                    naturalSorting: query.naturalSorting || this.naturalSorting,
201
                    ...queryExtension,
202
                },
203
            };
204
205
            // If query is empty then replace route, otherwise push
206
            if (Shopware.Utils.types.isEmpty(routeQuery)) {
207
                void this.$router.replace(route as unknown as RawLocation);
208
            } else {
209
                void this.$router.push(route as unknown as RawLocation);
210
            }
211
        },
212
213
        resetListing() {
214
            this.updateRoute({
215
                // @ts-expect-error
216
                name: this.$route.name,
217
                query: {
218
                    limit: this.limit,
219
                    page: this.page,
220
                    term: this.term,
221
                    sortBy: this.sortBy,
222
                    sortDirection: this.sortDirection,
223
                    naturalSorting: this.naturalSorting,
224
                },
225
            });
226
        },
227
228
        getMainListingParams() {
229
            if (this.disableRouteParams) {
230
                return {
231
                    limit: this.limit,
232
                    page: this.page,
233
                    term: this.term,
234
                    sortBy: this.sortBy,
235
                    sortDirection: this.sortDirection,
236
                    naturalSorting: this.naturalSorting,
237
                };
238
            }
239
            // Get actual query parameter
240
            const query = this.$route.query;
241
242
            return {
243
                limit: query.limit,
244
                page: query.page,
245
                term: query.term,
246
                sortBy: query.sortBy || this.sortBy,
247
                sortDirection: query.sortDirection || this.sortDirection,
248
                naturalSorting: query.naturalSorting || this.naturalSorting,
249
            };
250
        },
251
252
        updateSelection(selection: Record<string, any>) {
253
            this.selection = selection;
254
        },
255
256
        onPageChange(opts: {
257
            page: number,
258
            limit: number,
259
        }) {
260
            this.page = opts.page;
261
            this.limit = opts.limit;
262
            if (this.disableRouteParams) {
263
                this.getList();
264
                return;
265
            }
266
            this.updateRoute({
267
                page: this.page,
268
            });
269
        },
270
271
        onSearch(value: string|undefined) {
272
            this.term = value;
273
274
            if (this.disableRouteParams) {
275
                this.page = 1;
276
                this.getList();
277
                return;
278
            }
279
280
            this.updateRoute({
281
                term: this.term,
282
                page: 1,
283
            });
284
        },
285
286
        onSwitchFilter(filter: any, filterIndex: number) {
287
            this.filters[filterIndex].active = !this.filters[filterIndex].active;
288
289
            this.page = 1;
290
        },
291
292
        onSort({ sortBy, sortDirection }: {
293
            sortBy: string,
294
            sortDirection: string,
295
        }) {
296
            if (this.disableRouteParams) {
297
                this.updateData({
298
                    sortBy,
299
                    sortDirection,
300
                });
301
            } else {
302
                this.updateRoute({
303
                    sortBy,
304
                    sortDirection,
305
                });
306
            }
307
308
            this.getList();
309
        },
310
311
        onSortColumn(column: {
312
            dataIndex: string,
313
            naturalSorting: boolean,
314
        }) {
315
            if (this.disableRouteParams) {
316
                if (this.sortBy === column.dataIndex) {
317
                    this.sortDirection = (this.sortDirection === 'ASC' ? 'DESC' : 'ASC');
318
                } else {
319
                    this.sortDirection = 'ASC';
320
                    this.sortBy = column.dataIndex;
321
                }
322
                this.getList();
323
                return;
324
            }
325
326
            if (this.sortBy === column.dataIndex) {
327
                this.updateRoute({
328
                    sortDirection: (this.sortDirection === 'ASC' ? 'DESC' : 'ASC'),
329
                });
330
            } else {
331
                this.naturalSorting = column.naturalSorting;
332
                this.updateRoute({
333
                    sortBy: column.dataIndex,
334
                    sortDirection: 'ASC',
335
                    naturalSorting: column.naturalSorting,
336
                });
337
            }
338
        },
339
340
        onRefresh() {
341
            this.getList();
342
        },
343
344
        getList() {
345
            Shopware.Utils.debug.warn(
346
                'Listing Mixin',
347
                'When using the listing mixin you have to implement your custom "getList()" method.',
348
            );
349
        },
350
351
        isValidTerm(term: string) {
352
            return term && term.trim().length > 1;
353
        },
354
355
        async addQueryScores(term: string, originalCriteria: Criteria) {
356
            this.entitySearchable = true;
357
            if (!this.searchConfigEntity || !this.isValidTerm(term)) {
358
                return originalCriteria;
359
            }
360
            const searchRankingFields = await this.searchRankingService.getSearchFieldsByEntity(this.searchConfigEntity);
361
            // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
362
            if (!searchRankingFields || Object.keys(searchRankingFields).length < 1) {
363
                this.entitySearchable = false;
364
                return originalCriteria;
365
            }
366
367
            return this.searchRankingService.buildSearchQueriesForEntity(
368
                searchRankingFields,
369
                term,
370
                originalCriteria,
371
            );
372
        },
373
    },
374
});
375