@addev-be/ui 0.6.23 → 0.6.25
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.
- package/package.json +1 -1
- package/src/components/data/AdvancedRequestDataGrid/helpers/columns.tsx +1 -1
- package/src/components/data/DataGrid/helpers/columns.tsx +1 -1
- package/src/components/data/SqlRequestDataGrid/helpers/columns.tsx +10 -7
- package/src/providers/AuthenticationProvider/helpers.ts +3 -0
- package/src/providers/AuthenticationProvider/index.tsx +24 -0
- package/src/services/index.ts +0 -1
- package/src/services/types/userProfiles.ts +3 -2
- package/src/services/types/users.ts +9 -1
- package/src/components/data/SqlRequestGrid/helpers/columns.tsx +0 -277
- package/src/services/types/userPermissions.ts +0 -3
package/package.json
CHANGED
|
@@ -193,7 +193,7 @@ export const advancedCheckboxColumn = <R extends Record<string, any>>(
|
|
|
193
193
|
name: title,
|
|
194
194
|
render: (row) => (
|
|
195
195
|
<>
|
|
196
|
-
<input type="checkbox" checked={row[key]} />
|
|
196
|
+
<input type="checkbox" checked={row[key]} readOnly />
|
|
197
197
|
<span>{row[key] ? ' Oui' : ' Non'}</span>
|
|
198
198
|
</>
|
|
199
199
|
),
|
|
@@ -282,7 +282,7 @@ export const checkboxColumn = <R extends Record<string, any>>(
|
|
|
282
282
|
name: title,
|
|
283
283
|
render: (row) => (
|
|
284
284
|
<>
|
|
285
|
-
<input type="checkbox" checked={row[key]} />
|
|
285
|
+
<input type="checkbox" checked={row[key]} readOnly />
|
|
286
286
|
<span>{row[key] ? ' Oui' : ' Non'}</span>
|
|
287
287
|
</>
|
|
288
288
|
),
|
|
@@ -248,15 +248,18 @@ export const sqlCheckboxColumn = <R extends Record<string, any>>(
|
|
|
248
248
|
): SqlRequestDataGridColumns<R> => ({
|
|
249
249
|
[key]: {
|
|
250
250
|
name: title,
|
|
251
|
-
render: (row) =>
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
251
|
+
render: (row) => {
|
|
252
|
+
const value = !!+(row[key] ?? 0);
|
|
253
|
+
return (
|
|
254
|
+
<>
|
|
255
|
+
<input type="checkbox" checked={value} readOnly />
|
|
256
|
+
<span>{value ? ' Oui' : ' Non'}</span>
|
|
257
|
+
</>
|
|
258
|
+
);
|
|
259
|
+
},
|
|
257
260
|
getter: (row) => row[key] ?? '',
|
|
258
261
|
sortGetter: (row) => row[key] ?? '',
|
|
259
|
-
filter: { ...numberFilter(key), getter: (value) => value[key] ?? 0 },
|
|
262
|
+
filter: { ...numberFilter(key), getter: (value) => +(value[key] ?? 0) },
|
|
260
263
|
footer: (rows) => `${rows[0][key]} éléments`,
|
|
261
264
|
...options,
|
|
262
265
|
},
|
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
useSendRecoveryKeyRequestHandler,
|
|
19
19
|
} from '../../services';
|
|
20
20
|
|
|
21
|
+
import { basePermissions } from './helpers';
|
|
22
|
+
|
|
21
23
|
export type AuthenticationContextProps = {
|
|
22
24
|
token?: string;
|
|
23
25
|
user?: UserDTO;
|
|
@@ -28,6 +30,8 @@ export type AuthenticationContextProps = {
|
|
|
28
30
|
sendRecoveryKey: (email: string) => Promise<number>;
|
|
29
31
|
checkRecoveryKey: (key: string) => Promise<number>;
|
|
30
32
|
resetPassword: (key: string, password: string) => Promise<number>;
|
|
33
|
+
permissions: Record<string, string>;
|
|
34
|
+
hasPermission: (permission: string) => boolean;
|
|
31
35
|
};
|
|
32
36
|
|
|
33
37
|
export const AuthenticationContext = createContext<AuthenticationContextProps>({
|
|
@@ -38,18 +42,26 @@ export const AuthenticationContext = createContext<AuthenticationContextProps>({
|
|
|
38
42
|
sendRecoveryKey: async () => -1,
|
|
39
43
|
checkRecoveryKey: async () => -1,
|
|
40
44
|
resetPassword: async () => -1,
|
|
45
|
+
permissions: {},
|
|
46
|
+
hasPermission: () => false,
|
|
41
47
|
});
|
|
42
48
|
|
|
43
49
|
export type AuthenticationProviderProps = PropsWithChildren<{
|
|
50
|
+
permissions?: Record<string, string>;
|
|
44
51
|
loginComponent?: FC;
|
|
45
52
|
requestHandlerName?: string;
|
|
46
53
|
}>;
|
|
47
54
|
|
|
48
55
|
export const AuthenticationProvider: FC<AuthenticationProviderProps> = ({
|
|
56
|
+
permissions,
|
|
49
57
|
loginComponent: LoginComponent,
|
|
50
58
|
children,
|
|
51
59
|
requestHandlerName,
|
|
52
60
|
}) => {
|
|
61
|
+
const allPermissions = useMemo(
|
|
62
|
+
() => ({ ...basePermissions, ...permissions }),
|
|
63
|
+
[permissions]
|
|
64
|
+
);
|
|
53
65
|
const [, setWebSocketStatus] = useState<boolean | undefined>(false);
|
|
54
66
|
const [token, setToken] = useState<string | undefined>(
|
|
55
67
|
sessionStorage.getItem('authToken') || undefined
|
|
@@ -148,6 +160,14 @@ export const AuthenticationProvider: FC<AuthenticationProviderProps> = ({
|
|
|
148
160
|
[sendCheckRecoveryKeyRequest]
|
|
149
161
|
);
|
|
150
162
|
|
|
163
|
+
const hasPermission = useCallback(
|
|
164
|
+
(permission: string) =>
|
|
165
|
+
permission in allPermissions &&
|
|
166
|
+
!!allPermissions[permission] &&
|
|
167
|
+
(user?.isAdmin || (user?.permissions?.includes(permission) ?? false)),
|
|
168
|
+
[allPermissions, user?.isAdmin, user?.permissions]
|
|
169
|
+
);
|
|
170
|
+
|
|
151
171
|
const contextValue = useMemo(
|
|
152
172
|
() => ({
|
|
153
173
|
token,
|
|
@@ -159,10 +179,14 @@ export const AuthenticationProvider: FC<AuthenticationProviderProps> = ({
|
|
|
159
179
|
sendRecoveryKey,
|
|
160
180
|
checkRecoveryKey,
|
|
161
181
|
resetPassword,
|
|
182
|
+
permissions: allPermissions,
|
|
183
|
+
hasPermission,
|
|
162
184
|
}),
|
|
163
185
|
[
|
|
186
|
+
allPermissions,
|
|
164
187
|
authenticate,
|
|
165
188
|
checkRecoveryKey,
|
|
189
|
+
hasPermission,
|
|
166
190
|
login,
|
|
167
191
|
logout,
|
|
168
192
|
resetPassword,
|
package/src/services/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ export const userProfileDtoCodec = t.type(
|
|
|
7
7
|
...baseModelDtoCodec.props,
|
|
8
8
|
name: t.string,
|
|
9
9
|
permissions: t.array(t.string),
|
|
10
|
-
isAdmin: t.
|
|
10
|
+
isAdmin: t.boolean,
|
|
11
11
|
},
|
|
12
12
|
'UserProfileDTO'
|
|
13
13
|
);
|
|
@@ -25,6 +25,7 @@ export const getUserProfileRequestDtoCodec = t.type(
|
|
|
25
25
|
|
|
26
26
|
export const getUserProfileResponseDtoCodec = t.type(
|
|
27
27
|
{
|
|
28
|
+
status: t.number,
|
|
28
29
|
data: userProfileDtoCodec,
|
|
29
30
|
},
|
|
30
31
|
'GetUserProfileResponseDTO'
|
|
@@ -62,7 +63,7 @@ export type GetAllUserProfilesResponseDTO = t.TypeOf<
|
|
|
62
63
|
|
|
63
64
|
export const saveUserProfileRequestDtoCodec = t.type(
|
|
64
65
|
{
|
|
65
|
-
|
|
66
|
+
data: userProfileDtoCodec,
|
|
66
67
|
},
|
|
67
68
|
'SaveUserProfileRequestDTO'
|
|
68
69
|
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as t from 'io-ts';
|
|
2
2
|
|
|
3
3
|
import { baseModelDtoCodec } from './base';
|
|
4
|
+
import { userProfileDtoCodec } from './userProfiles';
|
|
4
5
|
|
|
5
6
|
export const userDtoCodec = t.intersection(
|
|
6
7
|
[
|
|
@@ -9,6 +10,7 @@ export const userDtoCodec = t.intersection(
|
|
|
9
10
|
username: t.string,
|
|
10
11
|
permissions: t.array(t.string),
|
|
11
12
|
isAdmin: t.boolean,
|
|
13
|
+
profile: t.union([userProfileDtoCodec, t.null]),
|
|
12
14
|
}),
|
|
13
15
|
t.partial({
|
|
14
16
|
name: t.string,
|
|
@@ -60,7 +62,13 @@ export type GetAllUsersResponseDTO = t.TypeOf<
|
|
|
60
62
|
|
|
61
63
|
export const saveUserRequestDtoCodec = t.type(
|
|
62
64
|
{
|
|
63
|
-
|
|
65
|
+
data: t.intersection([
|
|
66
|
+
userDtoCodec,
|
|
67
|
+
t.partial({
|
|
68
|
+
password: t.string,
|
|
69
|
+
}),
|
|
70
|
+
]),
|
|
71
|
+
password: t.string,
|
|
64
72
|
},
|
|
65
73
|
'SaveUserRequestDTO'
|
|
66
74
|
);
|
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
|
|
3
|
-
import { SqlRequestDataGridColumn, SqlRequestDataGridColumns } from '../types';
|
|
4
|
-
import {
|
|
5
|
-
buildExcelFormat,
|
|
6
|
-
numberFilter,
|
|
7
|
-
textFilter,
|
|
8
|
-
} from '../../DataGrid/helpers';
|
|
9
|
-
import {
|
|
10
|
-
formatMoney,
|
|
11
|
-
formatNumber,
|
|
12
|
-
formatNumberInvariant,
|
|
13
|
-
formatPercentage,
|
|
14
|
-
} from '../../../../helpers/numbers';
|
|
15
|
-
|
|
16
|
-
import { formatDate } from '../../../../helpers/dates';
|
|
17
|
-
|
|
18
|
-
export const sqlTextColumn = <R extends Record<string, any>>(
|
|
19
|
-
key: string,
|
|
20
|
-
title: string,
|
|
21
|
-
options?: Partial<SqlRequestDataGridColumn<R>>
|
|
22
|
-
): SqlRequestDataGridColumns<R> => ({
|
|
23
|
-
[key]: {
|
|
24
|
-
name: title,
|
|
25
|
-
render: (row) => row[key] ?? '',
|
|
26
|
-
getter: (row) => row[key] ?? '',
|
|
27
|
-
sortGetter: (row) => row[key] ?? '',
|
|
28
|
-
filter: { ...textFilter(key), getter: (value) => value[key] ?? '' },
|
|
29
|
-
...options,
|
|
30
|
-
footer: (rows) => `${rows[0][key]} éléments`,
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Creates a column with a composed value from multiple fields,
|
|
36
|
-
* and filtered by a text filter on the first field
|
|
37
|
-
*/
|
|
38
|
-
export const sqlComposedColumn = <R extends Record<string, any>>(
|
|
39
|
-
key: string,
|
|
40
|
-
title: string,
|
|
41
|
-
fields: string[],
|
|
42
|
-
options?: Partial<SqlRequestDataGridColumn<R>>
|
|
43
|
-
): SqlRequestDataGridColumns<R> => ({
|
|
44
|
-
[key]: {
|
|
45
|
-
field: {
|
|
46
|
-
fieldAlias: key,
|
|
47
|
-
operator: 'jsonObject',
|
|
48
|
-
operands: fields.flatMap((field) => [
|
|
49
|
-
{ constantValue: field },
|
|
50
|
-
{ fieldName: field },
|
|
51
|
-
]),
|
|
52
|
-
},
|
|
53
|
-
name: title,
|
|
54
|
-
render: (row) => row[key] ?? '',
|
|
55
|
-
getter: (row) => row[key] ?? '',
|
|
56
|
-
sortGetter: (row) => row[key] ?? '',
|
|
57
|
-
filter: {
|
|
58
|
-
...textFilter(fields[0]),
|
|
59
|
-
getter: (value) => value[fields[0]] ?? 0,
|
|
60
|
-
},
|
|
61
|
-
filterField: fields[0],
|
|
62
|
-
sortField: fields[0],
|
|
63
|
-
footer: (rows) => `${rows[0][key]} éléments`,
|
|
64
|
-
...options,
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
export const sqlMailColumn = <R extends Record<string, any>>(
|
|
69
|
-
key: string,
|
|
70
|
-
title: string,
|
|
71
|
-
options?: Partial<SqlRequestDataGridColumn<R>>
|
|
72
|
-
): SqlRequestDataGridColumns<R> => ({
|
|
73
|
-
[key]: {
|
|
74
|
-
name: title,
|
|
75
|
-
render: (row) => <a href={`mailto:${row[key]}`}>{row[key] ?? ''}</a>,
|
|
76
|
-
getter: (row) => row[key] ?? '',
|
|
77
|
-
sortGetter: (row) => row[key] ?? '',
|
|
78
|
-
filter: { ...textFilter(key), getter: (value) => value[key] ?? '' },
|
|
79
|
-
footer: (rows) => `${rows[0][key]} éléments`,
|
|
80
|
-
...options,
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
export const sqlPhoneColumn = <R extends Record<string, any>>(
|
|
85
|
-
key: string,
|
|
86
|
-
title: string,
|
|
87
|
-
options?: Partial<SqlRequestDataGridColumn<R>>
|
|
88
|
-
): SqlRequestDataGridColumns<R> => ({
|
|
89
|
-
[key]: {
|
|
90
|
-
name: title,
|
|
91
|
-
render: (row) => <a href={`tel:${row[key]}`}>{row[key] ?? ''}</a>,
|
|
92
|
-
getter: (row) => row[key] ?? '',
|
|
93
|
-
sortGetter: (row) => row[key] ?? '',
|
|
94
|
-
filter: { ...textFilter(key), getter: (value) => value[key] ?? '' },
|
|
95
|
-
footer: (rows) => `${rows[0][key]} éléments`,
|
|
96
|
-
...options,
|
|
97
|
-
},
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
export const sqlDateColumn = <R extends Record<string, any>>(
|
|
101
|
-
key: string,
|
|
102
|
-
title: string,
|
|
103
|
-
options?: Partial<SqlRequestDataGridColumn<R>>
|
|
104
|
-
): SqlRequestDataGridColumns<R> => ({
|
|
105
|
-
[key]: {
|
|
106
|
-
name: title,
|
|
107
|
-
type: 'date',
|
|
108
|
-
render: (row) => formatDate(row[key]),
|
|
109
|
-
getter: (row) => row[key] ?? '',
|
|
110
|
-
sortGetter: (row) => row[key] ?? '',
|
|
111
|
-
excelFormatter: () => 'dd/mm/yyyy',
|
|
112
|
-
excelValue: (value) => formatDate(value, 'YYYY-MM-DD'),
|
|
113
|
-
filter: {
|
|
114
|
-
...textFilter(key),
|
|
115
|
-
getter: (value) => value[key] ?? '',
|
|
116
|
-
formatter: (value) => formatDate(value),
|
|
117
|
-
renderer: (value) => formatDate(value),
|
|
118
|
-
},
|
|
119
|
-
footer: (rows) => `${rows[0][key]} éléments`,
|
|
120
|
-
...options,
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
export const sqlMonthColumn = <R extends Record<string, any>>(
|
|
125
|
-
key: string,
|
|
126
|
-
title: string,
|
|
127
|
-
options?: Partial<SqlRequestDataGridColumn<R>>
|
|
128
|
-
): SqlRequestDataGridColumns<R> => ({
|
|
129
|
-
[key]: {
|
|
130
|
-
name: title,
|
|
131
|
-
render: (row) => (row[key] ? `${row[key]} mois ` : ''),
|
|
132
|
-
getter: (row) => row[key] ?? '',
|
|
133
|
-
sortGetter: (row) => row[key] ?? '',
|
|
134
|
-
filter: { ...textFilter(key), getter: (value) => value[key] ?? '' },
|
|
135
|
-
footer: (rows) => `${rows[0][key]} éléments`,
|
|
136
|
-
...options,
|
|
137
|
-
},
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
export const sqlNumberColumn = <R extends Record<string, any>>(
|
|
141
|
-
key: string,
|
|
142
|
-
title: string,
|
|
143
|
-
decimals = 2,
|
|
144
|
-
options?: Partial<SqlRequestDataGridColumn<R>>
|
|
145
|
-
): SqlRequestDataGridColumns<R> => ({
|
|
146
|
-
[key]: {
|
|
147
|
-
name: title,
|
|
148
|
-
render: (row) => formatNumber(row[key], decimals) ?? '',
|
|
149
|
-
excelFormatter: () => buildExcelFormat(decimals),
|
|
150
|
-
excelValue: (value) => formatNumberInvariant(value, decimals),
|
|
151
|
-
getter: (row) => row[key] ?? '',
|
|
152
|
-
sortGetter: (row) => row[key] ?? '',
|
|
153
|
-
filter: {
|
|
154
|
-
...numberFilter(key),
|
|
155
|
-
getter: (value) => value[key] ?? 0,
|
|
156
|
-
renderer: (value) => formatNumber(value, decimals) ?? '',
|
|
157
|
-
},
|
|
158
|
-
footer: {
|
|
159
|
-
sum: null,
|
|
160
|
-
avg: null,
|
|
161
|
-
count: null,
|
|
162
|
-
max: null,
|
|
163
|
-
min: null,
|
|
164
|
-
},
|
|
165
|
-
...options,
|
|
166
|
-
},
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
export const sqlMoneyColumn = <R extends Record<string, any>>(
|
|
170
|
-
key: string,
|
|
171
|
-
title: string,
|
|
172
|
-
decimals = 2,
|
|
173
|
-
options?: Partial<SqlRequestDataGridColumn<R>>
|
|
174
|
-
): SqlRequestDataGridColumns<R> => ({
|
|
175
|
-
[key]: {
|
|
176
|
-
name: title,
|
|
177
|
-
type: 'number',
|
|
178
|
-
render: (row) => formatMoney(row[key], decimals) ?? '',
|
|
179
|
-
excelFormatter: () => buildExcelFormat(decimals, ' €'),
|
|
180
|
-
excelValue: (value) => formatNumberInvariant(value, decimals),
|
|
181
|
-
getter: (row) => row[key] ?? '',
|
|
182
|
-
sortGetter: (row) => row[key] ?? '',
|
|
183
|
-
filter: {
|
|
184
|
-
...numberFilter(key),
|
|
185
|
-
getter: (value) => value[key] ?? 0,
|
|
186
|
-
renderer: (value) => formatMoney(value, decimals) ?? '',
|
|
187
|
-
},
|
|
188
|
-
footer: {
|
|
189
|
-
sum: null,
|
|
190
|
-
avg: null,
|
|
191
|
-
count: null,
|
|
192
|
-
max: null,
|
|
193
|
-
min: null,
|
|
194
|
-
},
|
|
195
|
-
...options,
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
export const sqlPercentageColumn = <R extends Record<string, any>>(
|
|
200
|
-
key: string,
|
|
201
|
-
title: string,
|
|
202
|
-
decimals = 2,
|
|
203
|
-
options?: Partial<SqlRequestDataGridColumn<R>>
|
|
204
|
-
): SqlRequestDataGridColumns<R> => ({
|
|
205
|
-
[key]: {
|
|
206
|
-
name: title,
|
|
207
|
-
render: (row) => formatPercentage(row[key]) ?? '',
|
|
208
|
-
excelFormatter: () => buildExcelFormat(decimals, '%'),
|
|
209
|
-
excelValue: (value) => formatNumberInvariant(value, decimals),
|
|
210
|
-
getter: (row) => row[key] ?? '',
|
|
211
|
-
sortGetter: (row) => row[key] ?? '',
|
|
212
|
-
filter: {
|
|
213
|
-
...numberFilter(key),
|
|
214
|
-
getter: (value) => value[key] ?? 0,
|
|
215
|
-
renderer: (value) => formatPercentage(value, decimals) ?? '',
|
|
216
|
-
},
|
|
217
|
-
...options,
|
|
218
|
-
},
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
export const sqlCheckboxColumn = <R extends Record<string, any>>(
|
|
222
|
-
key: string,
|
|
223
|
-
title: string,
|
|
224
|
-
options?: Partial<SqlRequestDataGridColumn<R>>
|
|
225
|
-
): SqlRequestDataGridColumns<R> => ({
|
|
226
|
-
[key]: {
|
|
227
|
-
name: title,
|
|
228
|
-
render: (row) => (
|
|
229
|
-
<>
|
|
230
|
-
<input type="checkbox" checked={row[key]} />
|
|
231
|
-
<span>{row[key] ? ' Oui' : ' Non'}</span>
|
|
232
|
-
</>
|
|
233
|
-
),
|
|
234
|
-
getter: (row) => row[key] ?? '',
|
|
235
|
-
sortGetter: (row) => row[key] ?? '',
|
|
236
|
-
filter: { ...numberFilter(key), getter: (value) => value[key] ?? 0 },
|
|
237
|
-
footer: (rows) => `${rows[0][key]} éléments`,
|
|
238
|
-
...options,
|
|
239
|
-
},
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
export const sqlColorColumn = <R extends Record<string, any>>(
|
|
243
|
-
key: string,
|
|
244
|
-
title: string,
|
|
245
|
-
options?: Partial<SqlRequestDataGridColumn<R>>
|
|
246
|
-
): SqlRequestDataGridColumns<R> => ({
|
|
247
|
-
[key]: {
|
|
248
|
-
name: title,
|
|
249
|
-
render: (row) => (
|
|
250
|
-
<div
|
|
251
|
-
style={{ position: 'absolute', inset: 0, backgroundColor: row[key] }}
|
|
252
|
-
>
|
|
253
|
-
|
|
254
|
-
</div>
|
|
255
|
-
),
|
|
256
|
-
excelValue: () => '',
|
|
257
|
-
excelBackgroundColor: (value) => value,
|
|
258
|
-
getter: (row) => row[key] ?? '',
|
|
259
|
-
sortGetter: (row) => row[key] ?? '',
|
|
260
|
-
filter: {
|
|
261
|
-
...textFilter(key),
|
|
262
|
-
getter: (value) => value[key] ?? '',
|
|
263
|
-
renderer: (value) => (
|
|
264
|
-
<div
|
|
265
|
-
style={{
|
|
266
|
-
backgroundColor: value,
|
|
267
|
-
width: 'var(--space-16)',
|
|
268
|
-
height: '1em',
|
|
269
|
-
}}
|
|
270
|
-
>
|
|
271
|
-
|
|
272
|
-
</div>
|
|
273
|
-
),
|
|
274
|
-
},
|
|
275
|
-
...options,
|
|
276
|
-
},
|
|
277
|
-
});
|