@dashadmin/dash-admin 1.3.45 → 1.3.47

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 (43) hide show
  1. package/dist/components/i18n/BridgedLocalesMenuButton.js +1 -2
  2. package/dist/components/menu/DarkToggleMode.js +1 -1
  3. package/dist/components/subscription/PlanPricesSettings.js +4 -2
  4. package/dist/components/tenancy/GlobalTenantWrapper.js +64 -0
  5. package/dist/components/tenancy/TenantSwitcher.js +389 -0
  6. package/dist/contexts/com/components/EchoNotificationsCenter.js +40 -0
  7. package/dist/default-theme/menu/AppMenuComponents/expanded/SidebarItem.js +1 -0
  8. package/dist/index.js +60 -40
  9. package/dist/pages/Profile.js +32 -38
  10. package/dist/providers/i18n/en.js +33 -0
  11. package/dist/providers/i18n/es.js +33 -0
  12. package/dist/resources/User/components/AvatarUploadField.js +61 -0
  13. package/dist/resources/User/components/PasswordFields.js +67 -0
  14. package/dist/resources/User/getProfileMenu.js +36 -0
  15. package/dist/resources/User/profileResource.js +33 -0
  16. package/dist/resources/User/userLayout.js +58 -0
  17. package/dist/resources/User/userResource.js +90 -0
  18. package/dist/resources/User/userSchema.js +206 -0
  19. package/dist/resources/User/usersFilters.js +22 -0
  20. package/dist/systemResources.js +30 -19
  21. package/package.json +1 -1
  22. package/src/components/i18n/BridgedLocalesMenuButton.tsx +1 -2
  23. package/src/components/menu/DarkToggleMode.tsx +1 -1
  24. package/src/components/subscription/PlanPricesSettings.tsx +2 -2
  25. package/src/components/tenancy/GlobalTenantWrapper.tsx +84 -0
  26. package/src/components/tenancy/TenantSwitcher.tsx +485 -0
  27. package/src/contexts/com/components/EchoNotificationsCenter.tsx +65 -0
  28. package/src/default-theme/menu/AppMaterialMenu.tsx +1 -1
  29. package/src/default-theme/menu/AppMenuComponents/expanded/SidebarItem.tsx +1 -0
  30. package/src/default-theme/menu/SidebarActions.tsx +1 -1
  31. package/src/index.tsx +15 -0
  32. package/src/pages/Profile.tsx +69 -107
  33. package/src/providers/i18n/en.ts +33 -0
  34. package/src/providers/i18n/es.ts +33 -0
  35. package/src/resources/User/components/AvatarUploadField.tsx +57 -0
  36. package/src/resources/User/components/PasswordFields.tsx +73 -0
  37. package/src/resources/User/getProfileMenu.ts +57 -0
  38. package/src/resources/User/profileResource.tsx +51 -0
  39. package/src/resources/User/userLayout.tsx +62 -0
  40. package/src/resources/User/userResource.tsx +111 -0
  41. package/src/resources/User/userSchema.tsx +213 -0
  42. package/src/resources/User/usersFilters.tsx +21 -0
  43. package/src/systemResources.tsx +31 -20
@@ -1,5 +1,4 @@
1
1
  import { FC, useEffect, useState } from 'react';
2
- //import { Button as LoadingButton } from "@mui/material";
3
2
 
4
3
  import { Button, Card, CardContent, CardHeader, Grid, Typography } from '@mui/material';
5
4
 
@@ -8,7 +7,7 @@ import {
8
7
  useRefresh,
9
8
  Loading,
10
9
  useNotify,
11
- useGetIdentity,
10
+ useTranslate,
12
11
  TextInput,
13
12
  } from 'react-admin';
14
13
 
@@ -16,54 +15,61 @@ import { useDispatch } from 'react-redux';
16
15
  import { DASH_REDUX_ACTIONS } from 'dash-admin-state';
17
16
 
18
17
  import React from 'react';
19
- import useAxios from '../hooks/axios';
20
- import SingleImageUploader from './SingleImageUploader';
18
+ import { useAxios } from 'dash-axios-hook';
19
+ import { SingleImageUploader } from 'dash-components';
21
20
  import { useAuthContext } from '../contexts/auth';
22
- import UserPreferences from "../components/user/UserPreferences";
21
+ import UserPreferences from '../components/user/UserPreferences';
23
22
 
24
23
  const Profile: FC = (_props) => {
25
24
  const [currTab, setCurrTab] = useState('auth/update/info');
26
25
  const [isLoading, setIsLoading] = useState(false);
27
- //const { identity, isLoading: identityLoading } = useGetIdentity();
28
-
29
- const { user, fetchAuth } = useAuthContext(); // Add fetchCompleteAuth here
26
+
27
+ const { user, fetchAuth } = useAuthContext();
30
28
  const [avatar, setAvatar] = useState({ rawFile: null, urlFile: '' });
31
29
  const notify = useNotify();
32
-
33
- const { axios } = useAxios();
30
+ const translate = useTranslate();
31
+
32
+ const axios = useAxios();
34
33
  const refresh = useRefresh();
35
34
 
35
+ const { showPreferences = false } = _props;
36
+
36
37
  const dispatch = useDispatch();
37
38
 
38
39
  useEffect(() => {
39
40
  dispatch(
40
41
  DASH_REDUX_ACTIONS.updatePage({
41
- title: 'Perfil',
42
+ title: translate('profile.title', { _: 'Perfil' }),
42
43
  //icon:
43
- subTitle: 'Configuración de perfil',
44
+ subTitle: translate('profile.subtitle', { _: 'Configuración de perfil' }),
44
45
  }),
45
46
  );
46
- }, []);
47
+ // translate is included so the page header re-translates on locale change
48
+ // (react-admin returns a new translate reference when the locale changes)
49
+ }, [translate]);
47
50
 
48
51
  const validateUserCreation = (values) => {
49
52
  const errors: any = {};
50
53
  if (currTab === 'auth/update/password') {
54
+ const required = translate('profile.validation.required', { _: 'El campo es requerido' });
55
+ const mismatch = translate('profile.validation.password_mismatch', { _: 'Las contraseñas no coinciden' });
56
+
51
57
  if (
52
58
  (values.password || values.password_confirmation) &&
53
59
  !values.current_password
54
60
  ) {
55
- errors.current_password = 'El campo es requerido';
61
+ errors.current_password = required;
56
62
  }
57
63
  if (
58
64
  values.current_password &&
59
65
  (!values.password || !values.password_confirmation)
60
66
  ) {
61
- errors.password = 'El campo es requerido';
62
- errors.password_confirmation = 'El campo es requerido';
67
+ errors.password = required;
68
+ errors.password_confirmation = required;
63
69
  }
64
70
  if (values.password_confirmation !== values.password) {
65
- errors.password = 'Las contraseñas no coinciden';
66
- errors.password_confirmation = 'Las contraseñas no coinciden';
71
+ errors.password = mismatch;
72
+ errors.password_confirmation = mismatch;
67
73
  }
68
74
  }
69
75
 
@@ -72,7 +78,7 @@ const Profile: FC = (_props) => {
72
78
 
73
79
  const handleSubmit = async (values) => {
74
80
  setIsLoading(true);
75
-
81
+
76
82
  const formData = new FormData();
77
83
 
78
84
  const passwords = {
@@ -96,31 +102,27 @@ const Profile: FC = (_props) => {
96
102
  } else {
97
103
  await axios.put(currTab, passwords);
98
104
  }
99
-
100
-
101
-
105
+
102
106
  // Refresh the auth context with updated user data
103
107
  try {
104
-
105
108
  await fetchAuth();
106
109
  console.log('Auth context refreshed after profile update');
107
110
  } catch (authError) {
108
- console.error('Faileds to refresh auth context:', authError);
111
+ console.error('Failed to refresh auth context:', authError);
109
112
  // Don't show error to user as the main operation succeeded
110
113
  }
111
114
 
112
- notify('Usuario actualizado correctamente', { type:'success' });
115
+ notify(translate('profile.notify.success', { _: 'Usuario actualizado correctamente' }), { type: 'success' });
113
116
  refresh();
114
117
  } catch (error: any) {
115
118
 
116
-
117
119
  // Handle backend error messages gracefully
118
120
  let mainMessage = error?.response?.data?.message || error?.body?.message || '';
119
121
  let fieldErrors = error?.response?.data?.errors || error?.body?.errors || error;
120
122
  if (mainMessage) {
121
- notify(`Error al actualizar el usuario, ${mainMessage}`, { type: 'error' });
123
+ notify(translate('profile.notify.error_with_message', { message: mainMessage, _: `Error al actualizar el usuario, ${mainMessage}` }), { type: 'error' });
122
124
  } else {
123
- notify('Error al actualizar el usuario', { type: 'error' });
125
+ notify(translate('profile.notify.error', { _: 'Error al actualizar el usuario' }), { type: 'error' });
124
126
  }
125
127
  if (fieldErrors && typeof fieldErrors === 'object') {
126
128
  const errors: any = {};
@@ -133,6 +135,8 @@ const Profile: FC = (_props) => {
133
135
  }
134
136
  } finally {
135
137
  setIsLoading(false);
138
+ // TechDebt: the avatar update doesn't reflect immediately after the update, this is a quick fix to reload the page and show the new avatar, ideally we should update the auth context with the new avatar url and avoid reloading the page
139
+ window.location.reload();
136
140
  }
137
141
  };
138
142
 
@@ -146,17 +150,6 @@ const Profile: FC = (_props) => {
146
150
 
147
151
  return (
148
152
  <div className='dash-profile'>
149
-
150
- {/*Page Header
151
- <Row>
152
- <Col xs={24} md={12}>
153
- <div className="dash-mb-2">
154
- <h2 className="dash-page-title">Perfil</h2>
155
- </div>
156
- </Col>
157
- </Row>
158
- */}
159
-
160
153
 
161
154
  {user ? (
162
155
  <Form
@@ -165,16 +158,16 @@ const Profile: FC = (_props) => {
165
158
  defaultValues={{ name: user?.name, email: user?.email, lastname: user?.lastname }}
166
159
  className='dash-form'
167
160
  >
168
-
161
+
169
162
  <div className='dash-card-content dash-module dash-card-profile'>
170
-
171
- <Grid
172
- container
163
+
164
+ <Grid
165
+ container
173
166
  spacing={{ xs: 2, md: 3 }}
174
167
  sx={{ minHeight: '400px' }}
175
168
  >
176
169
  {/* Profile Image Section */}
177
- <Grid
170
+ <Grid
178
171
  size={{ xs: 12, md: 4 }}
179
172
  display="flex"
180
173
  justifyContent="center"
@@ -190,51 +183,18 @@ const Profile: FC = (_props) => {
190
183
  width: '100%',
191
184
  }}
192
185
  >
193
- {/*<div className='dash-profile-img'>
194
- {identity.image_url || avatar.urlFile ? (
195
- <img src={avatar.urlFile || identity.image_url} alt='' />
196
- ) : (
197
- <div className='dash-profile-icon'>
198
- <Icon.User size={'90px'} color={'#fff'} />
199
- </div>
200
- )}
201
- <IconButton
202
- aria-label='upload picture'
203
- component='label'
204
- className='dash-profile-edit'
205
- >
206
- <input
207
- hidden
208
- accept='image/*'
209
- type='file'
210
- onChange={(e) =>
211
- setAvatar({
212
- rawFile: e.target.files[0],
213
- urlFile: URL.createObjectURL(e.target.files[0]),
214
- })
215
- }
216
- />
217
- <Icon.PenTool
218
- size={'22px'}
219
- color={'#000'}
220
- style={{ opacity: '0.6' }}
221
- />
222
- </IconButton>
223
- </div>*/}
224
-
225
- {"<!-- F -->"}
226
- <SingleImageUploader
227
- currentUrl={user?.image_path}
186
+ <SingleImageUploader
187
+ currentUrl={user?.image_path}
228
188
  onChange={(file: File) => {
229
189
  setAvatar({
230
190
  rawFile: file,
231
191
  urlFile: URL.createObjectURL(file),
232
192
  });
233
- }}
234
- classNamePrefix='dash-profile'
193
+ }}
194
+ classNamePrefix='dash-profile'
235
195
  />
236
- <Typography
237
- className='dash-profile-name'
196
+ <Typography
197
+ className='dash-profile-name'
238
198
  variant='h2'
239
199
  sx={{ mt: 2, textAlign: 'center' }}
240
200
  >
@@ -257,16 +217,16 @@ const Profile: FC = (_props) => {
257
217
 
258
218
  {/* User Info Section */}
259
219
  <Grid size={{ xs: 12, md: 4 }}>
260
- <CardHeader
261
- sx={{ padding: '16px 25px 0' }}
262
- title='Info'
220
+ <CardHeader
221
+ sx={{ padding: '16px 25px 0' }}
222
+ title={translate('profile.info.title', { _: 'Info' })}
263
223
  />
264
224
  <CardContent sx={{ padding: '25px' }}>
265
225
  <Grid container spacing={2}>
266
226
  <Grid size={12}>
267
227
  <TextInput
268
228
  source='name'
269
- label='Nombre'
229
+ label={translate('profile.info.name', { _: 'Nombre' })}
270
230
  variant='outlined'
271
231
  fullWidth
272
232
  />
@@ -274,7 +234,7 @@ const Profile: FC = (_props) => {
274
234
  <Grid size={12}>
275
235
  <TextInput
276
236
  source='lastname'
277
- label='Apellido'
237
+ label={translate('profile.info.lastname', { _: 'Apellido' })}
278
238
  variant='outlined'
279
239
  fullWidth
280
240
  />
@@ -282,7 +242,7 @@ const Profile: FC = (_props) => {
282
242
  <Grid size={12}>
283
243
  <TextInput
284
244
  source='email'
285
- label='Email'
245
+ label={translate('profile.info.email', { _: 'Email' })}
286
246
  variant='outlined'
287
247
  fullWidth
288
248
  />
@@ -303,7 +263,9 @@ const Profile: FC = (_props) => {
303
263
  }
304
264
  }}
305
265
  >
306
- {isLoading && currTab === 'auth/update/info' ? 'Guardando...' : 'Guardar'}
266
+ {isLoading && currTab === 'auth/update/info'
267
+ ? translate('profile.info.saving', { _: 'Guardando...' })
268
+ : translate('profile.info.save', { _: 'Guardar' })}
307
269
  </Button>
308
270
  </Grid>
309
271
  </Grid>
@@ -314,7 +276,7 @@ const Profile: FC = (_props) => {
314
276
  <Grid size={{ xs: 12, md: 4 }}>
315
277
  <CardHeader
316
278
  sx={{ padding: '16px 25px 0' }}
317
- title='Contraseñas'
279
+ title={translate('profile.password.title', { _: 'Contraseñas' })}
318
280
  />
319
281
  <CardContent sx={{ padding: '25px' }}>
320
282
  <Grid container spacing={2}>
@@ -322,7 +284,7 @@ const Profile: FC = (_props) => {
322
284
  <TextInput
323
285
  type='password'
324
286
  source='current_password'
325
- label='Contraseña actual'
287
+ label={translate('profile.password.current', { _: 'Contraseña actual' })}
326
288
  variant='outlined'
327
289
  fullWidth
328
290
  />
@@ -331,14 +293,14 @@ const Profile: FC = (_props) => {
331
293
  <TextInput
332
294
  type='password'
333
295
  source='password'
334
- label='Nueva contraseña'
296
+ label={translate('profile.password.new', { _: 'Nueva contraseña' })}
335
297
  variant='outlined'
336
298
  fullWidth
337
299
  />
338
300
  </Grid>
339
301
  <Grid size={12}>
340
302
  <TextInput
341
- label='Repita nueva contraseña'
303
+ label={translate('profile.password.confirm', { _: 'Repita nueva contraseña' })}
342
304
  type='password'
343
305
  source='password_confirmation'
344
306
  variant='outlined'
@@ -361,7 +323,9 @@ const Profile: FC = (_props) => {
361
323
  }
362
324
  }}
363
325
  >
364
- {isLoading && currTab === 'auth/update/password' ? 'Guardando Contraseñas...' : 'Guardar Contraseñas'}
326
+ {isLoading && currTab === 'auth/update/password'
327
+ ? translate('profile.password.saving', { _: 'Guardando Contraseñas...' })
328
+ : translate('profile.password.save', { _: 'Guardar Contraseñas' })}
365
329
  </Button>
366
330
  </Grid>
367
331
  </Grid>
@@ -369,21 +333,19 @@ const Profile: FC = (_props) => {
369
333
  </Grid>
370
334
 
371
335
  {/* User Preferences Section */}
372
- <Grid size={{ xs: 12
373
- }}>
374
- <CardHeader
375
- sx={{ padding: '16px 25px 0' }}
376
- title='Preferencias'
377
- />
378
- <CardContent sx={{ padding: '25px' }}>
379
- <UserPreferences />
380
- </CardContent>
381
- </Grid>
336
+ {showPreferences && (
337
+ <Grid size={{ xs: 12 }}>
338
+ <CardContent sx={{ padding: '25px' }}>
339
+ <UserPreferences />
340
+ </CardContent>
341
+ </Grid>
342
+ )}
343
+
382
344
  </Grid>
383
345
  </div>
384
-
346
+
385
347
  </Form>
386
-
348
+
387
349
  ) : (
388
350
  <Loading />
389
351
  )}
@@ -605,6 +605,39 @@ const dashEnglish = {
605
605
  'Uses AI to create a complete color scheme based on extracted colors and your description',
606
606
  },
607
607
  },
608
+ // Profile page
609
+ profile: {
610
+ title: 'Profile',
611
+ subtitle: 'Profile settings',
612
+ info: {
613
+ title: 'Info',
614
+ name: 'Name',
615
+ lastname: 'Last name',
616
+ email: 'Email',
617
+ save: 'Save',
618
+ saving: 'Saving...',
619
+ },
620
+ password: {
621
+ title: 'Passwords',
622
+ current: 'Current password',
623
+ new: 'New password',
624
+ confirm: 'Confirm new password',
625
+ save: 'Save Passwords',
626
+ saving: 'Saving Passwords...',
627
+ },
628
+ preferences: {
629
+ title: 'Preferences',
630
+ },
631
+ validation: {
632
+ required: 'This field is required',
633
+ password_mismatch: 'Passwords do not match',
634
+ },
635
+ notify: {
636
+ success: 'User updated successfully',
637
+ error: 'Error updating user',
638
+ error_with_message: 'Error updating user, %{message}',
639
+ },
640
+ },
608
641
  };
609
642
 
610
643
  export default {
@@ -611,6 +611,39 @@ const dashSpanish = {
611
611
  'Usa IA para crear un esquema de color completo basado en los colores extraídos y tu descripción',
612
612
  },
613
613
  },
614
+ // Profile page
615
+ profile: {
616
+ title: 'Perfil',
617
+ subtitle: 'Configuración de perfil',
618
+ info: {
619
+ title: 'Info',
620
+ name: 'Nombre',
621
+ lastname: 'Apellido',
622
+ email: 'Email',
623
+ save: 'Guardar',
624
+ saving: 'Guardando...',
625
+ },
626
+ password: {
627
+ title: 'Contraseñas',
628
+ current: 'Contraseña actual',
629
+ new: 'Nueva contraseña',
630
+ confirm: 'Repita nueva contraseña',
631
+ save: 'Guardar Contraseñas',
632
+ saving: 'Guardando Contraseñas...',
633
+ },
634
+ preferences: {
635
+ title: 'Preferencias',
636
+ },
637
+ validation: {
638
+ required: 'El campo es requerido',
639
+ password_mismatch: 'Las contraseñas no coinciden',
640
+ },
641
+ notify: {
642
+ success: 'Usuario actualizado correctamente',
643
+ error: 'Error al actualizar el usuario',
644
+ error_with_message: 'Error al actualizar el usuario, %{message}',
645
+ },
646
+ },
614
647
  };
615
648
 
616
649
  export default {
@@ -0,0 +1,57 @@
1
+ import * as React from 'react';
2
+ import { IDashAutoAdminCustomFieldComponent } from 'dash-auto-admin';
3
+ import { useRecordContext } from 'react-admin';
4
+ import { SingleImageUploader } from 'dash-components';
5
+ import { useFormContext } from 'react-hook-form';
6
+
7
+ export type AvatarComponent = IDashAutoAdminCustomFieldComponent;
8
+
9
+ export const AvatarHandler: React.FC<AvatarComponent> = (props) => {
10
+ const { method, attribute } = props;
11
+ const record = useRecordContext();
12
+ const {
13
+ setValue,
14
+ formState: { errors },
15
+ } = useFormContext();
16
+
17
+ const onChange = (file: File) => {
18
+ setValue(attribute.attribute, file, { shouldDirty: true });
19
+ };
20
+
21
+ return (
22
+ <>
23
+ <SingleImageUploader
24
+ classNamePrefix='dash-profile'
25
+ {...method === "edit" && { currentUrl: `${record.image_path}`} }
26
+ onChange={onChange}
27
+ />
28
+ {errors[attribute.attribute] && (
29
+ <span style={{ color: 'red' }}>
30
+ {errors[attribute.attribute]?.message?.toString() || 'Error'}
31
+ </span>
32
+ )}
33
+ </>
34
+ );
35
+ };
36
+
37
+ /**
38
+ * Generic user-avatar upload field for the shared user resource schema.
39
+ *
40
+ * Moved from kt-ecommerce (components/User/Avatar.tsx) - purely generic
41
+ * (dash-auto-admin/dash-components/react-hook-form only, no kitchntabs- or
42
+ * vanexa-specific logic), so it belongs alongside userSchema.tsx here rather
43
+ * than duplicated per domain. Renamed from "Avatar" to avoid colliding with
44
+ * dash-admin's unrelated components/user/AvatarComponent.tsx (the topbar
45
+ * user-menu avatar widget - a different component for a different purpose).
46
+ */
47
+ const AvatarUploadField = ({ method, attribute, resourceConfig }: IDashAutoAdminCustomFieldComponent) => {
48
+ switch (method) {
49
+ case 'edit':
50
+ return <AvatarHandler attribute={attribute} method={method} resourceConfig={resourceConfig} />;
51
+ case 'create':
52
+ return <AvatarHandler attribute={attribute} method={method} resourceConfig={resourceConfig} />;
53
+ case 'view':
54
+ return <></>;
55
+ }
56
+ };
57
+ export default AvatarUploadField;
@@ -0,0 +1,73 @@
1
+ import React from 'react';
2
+ import { IDashAutoAdminCustomFieldComponent } from 'dash-auto-admin';
3
+ import {
4
+ PasswordInput,
5
+ } from 'react-admin';
6
+ import { Box } from '@mui/material';
7
+
8
+ /**
9
+ * Generic password + confirm-password field pair for the shared user
10
+ * resource schema. Moved from kt-ecommerce (components/User/Password.tsx) -
11
+ * no kitchntabs- or vanexa-specific logic. Renamed from "Password" to avoid
12
+ * ambiguity with other password-related components in this package.
13
+ */
14
+ export type PasswordComponent = IDashAutoAdminCustomFieldComponent;
15
+ export const PasswordShow: React.FC<PasswordComponent> = (_props) => {
16
+ return <></>;
17
+ };
18
+ export const PasswordEdit: React.FC<PasswordComponent> = (_props) => {
19
+
20
+ return (
21
+ <>
22
+ <Box>
23
+ <PasswordInput
24
+ sx={{ width: '100%' }}
25
+ autoComplete='off'
26
+ source='Contraseña'
27
+ name='password'
28
+ />
29
+ </Box>
30
+ <Box>
31
+ <PasswordInput
32
+ sx={{ width: '100%' }}
33
+ autoComplete='off'
34
+ source='Confirmar Contraseña'
35
+ name='password_confirmation'
36
+ />
37
+ </Box>
38
+ </>
39
+ );
40
+ };
41
+ export const PasswordCreate: React.FC<PasswordComponent> = (_props) => {
42
+
43
+
44
+ return (
45
+ <>
46
+ <Box>
47
+ <PasswordInput
48
+ sx={{ width: '100%' }}
49
+ source='Contraseña'
50
+ name='password'
51
+ />
52
+ </Box>
53
+ <Box>
54
+ <PasswordInput
55
+ sx={{ width: '100%' }}
56
+ source='Confirmar Contraseña'
57
+ name='password_confirmation'
58
+ />
59
+ </Box>
60
+ </>
61
+ );
62
+ };
63
+ const PasswordFields = ({ method, attribute, resourceConfig }: IDashAutoAdminCustomFieldComponent) => {
64
+ switch (method) {
65
+ case 'edit':
66
+ return <PasswordEdit attribute={attribute} method={method} resourceConfig={resourceConfig} />;
67
+ case 'create':
68
+ return <PasswordCreate attribute={attribute} method={method} resourceConfig={resourceConfig} />;
69
+ case 'view':
70
+ return <PasswordShow attribute={attribute} method={method} resourceConfig={resourceConfig} />;
71
+ }
72
+ };
73
+ export default PasswordFields;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Profile menu generator for user navigation.
3
+ *
4
+ * Moved here from kt-utils (getProfileMenu.ts) per the profile/user resource
5
+ * refactor - it is consumed by resources/User/profileResource.tsx, which now
6
+ * also lives in this package, and doesn't depend on anything kitchntabs- or
7
+ * vanexa-specific beyond the 'Cliente' role check below (kept verbatim - both
8
+ * domains currently use it identically; a domain that needs a different
9
+ * profile menu can still override profileResource.menu after importing it).
10
+ */
11
+
12
+ import { dashStorage } from 'dash-utils';
13
+ import type IApplicationLayoutMenuItem from 'dash-auto-admin/src/interfaces/IDashApplicationLayoutMenuItem';
14
+
15
+ /**
16
+ * Generates the profile menu based on user roles
17
+ */
18
+ const getProfileMenu = (resourceConfig?: any): IApplicationLayoutMenuItem[] => {
19
+ const userStr = dashStorage.getItem('user');
20
+ const rolesStr = dashStorage.getItem('roles');
21
+
22
+ const user = userStr ? JSON.parse(userStr) : null;
23
+ const roles: any[] = rolesStr ? JSON.parse(rolesStr) : [];
24
+
25
+ const profileMenu: IApplicationLayoutMenuItem[] = [
26
+ {
27
+ title: 'Perfil',
28
+ redirect: '/profile',
29
+ },
30
+ ];
31
+
32
+ const isCliente = roles
33
+ ? roles.map((role) => role.name).includes('Cliente')
34
+ : false;
35
+
36
+ if (isCliente) {
37
+ profileMenu.push(
38
+ {
39
+ title: 'Comunas',
40
+ redirect: '/profile/commune',
41
+ },
42
+ {
43
+ title: 'Centros Logísticos',
44
+ redirect: '/profile/logistic-center',
45
+ },
46
+ {
47
+ title: 'Notificaciones',
48
+ redirect: '/profile/notification',
49
+ },
50
+ );
51
+ }
52
+
53
+ return profileMenu;
54
+ };
55
+
56
+ export default getProfileMenu;
57
+ export { getProfileMenu };
@@ -0,0 +1,51 @@
1
+ import { IDashAutoAdminResourceConfig } from 'dash-auto-admin';
2
+ import ResourceTemplate from '../../templates/ResourceTemplate';
3
+ import Dashboard from '@mui/icons-material/Dashboard';
4
+ import {DASHAdminSystemConstants} from 'dash-constants';
5
+
6
+ import getProfileMenu from './getProfileMenu';
7
+
8
+ /**
9
+ * Shared "/profile" resource shell.
10
+ *
11
+ * Moved here from kt-ecommerce (resources/user/profileResource.tsx) - the
12
+ * config itself is generic. `listComponent` is intentionally left unset: the
13
+ * kt-ecommerce original hardcoded kt-pages' CustomProfile page, which would
14
+ * have made this package depend on a kitchntabs-flavored consumer package
15
+ * (backwards - dash-admin is core, kt-pages/kt-ecommerce are domain layers
16
+ * built on top of it). Each app supplies its own profile page component by
17
+ * overriding `listComponent` after importing this base config, e.g.:
18
+ *
19
+ * import profileResource from 'dash-admin/src/resources/User/profileResource';
20
+ * import CustomProfile from 'kt-pages/src/pages/Account/CustomProfile';
21
+ *
22
+ * export default {
23
+ * ...profileResource,
24
+ * listComponent: (_resourceConfig) => <CustomProfile />,
25
+ * };
26
+ */
27
+ const profileResource: IDashAutoAdminResourceConfig = {
28
+ filterWithSubmit: true,
29
+ roles: ["*"
30
+ /*
31
+ DASHAdminSystemConstants.system.DASH_SYSTEM_ROLE,
32
+ DASHAppConstants.system.TENANT_ROLE,
33
+ DASHAppConstants.system.USER_ROLE,
34
+ DASHAppConstants.system.CREATOR_ROLE,
35
+ DASHAppConstants.system.CLOSING_ROLE,
36
+ DASHAppConstants.system.GUNNER_ROLE,
37
+ DASHAppConstants.system.DRIVER_ROLE
38
+ */
39
+ ],
40
+ component: ResourceTemplate,
41
+ hidden: true,
42
+ model: 'profile',
43
+ group: 'Perfil',
44
+ label: 'Perfil',
45
+ icon: <Dashboard />,
46
+ schema: null,
47
+ menu: getProfileMenu,
48
+ listProps : { perPage: DASHAdminSystemConstants.system.DEFAULT_PER_PAGE || 50}
49
+ };
50
+
51
+ export default profileResource;