@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@addev-be/ui",
3
- "version": "0.6.23",
3
+ "version": "0.6.25",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "watch": "tsc -b --watch",
@@ -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
- <input type="checkbox" checked={row[key]} />
254
- <span>{row[key] ? ' Oui' : ' Non'}</span>
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
  },
@@ -0,0 +1,3 @@
1
+ export const basePermissions: Record<string, string> = {
2
+ 'users.manage': 'Gérer les utilisateurs',
3
+ };
@@ -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,
@@ -11,4 +11,3 @@ export * from './types/auth';
11
11
  export * from './types/base';
12
12
  export * from './types/users';
13
13
  export * from './types/userProfiles';
14
- export * from './types/userPermissions';
@@ -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.string,
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
- id: t.string,
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
- id: t.string,
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
- &nbsp;
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
- &nbsp;
272
- </div>
273
- ),
274
- },
275
- ...options,
276
- },
277
- });
@@ -1,3 +0,0 @@
1
- export const userPermissions: Record<string, string> = {
2
- 'users:manage': 'Gérer les utilisateurs',
3
- };