@applica-software-guru/react-admin 1.0.45 → 1.0.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 (41) hide show
  1. package/dist/AdminContext.d.ts +5 -10
  2. package/dist/AdminContext.d.ts.map +1 -1
  3. package/dist/ApplicaAdmin.d.ts +51 -17
  4. package/dist/ApplicaAdmin.d.ts.map +1 -1
  5. package/dist/components/@extended/Breadcrumbs.d.ts +1 -0
  6. package/dist/components/@extended/Breadcrumbs.d.ts.map +1 -1
  7. package/dist/components/Layout/Drawer/DrawerHeader/DrawerHeaderStyled.d.ts +1 -1
  8. package/dist/components/Notification.d.ts +2 -2
  9. package/dist/components/ra-forms/TabbedForm.d.ts +2 -2
  10. package/dist/components/ra-lists/Datagrid.d.ts +6 -6
  11. package/dist/contexts/MenuConfigContext.d.ts +34 -12
  12. package/dist/contexts/MenuConfigContext.d.ts.map +1 -1
  13. package/dist/contexts/MenuPropTypes.d.ts +1 -0
  14. package/dist/contexts/MenuPropTypes.d.ts.map +1 -1
  15. package/dist/dev/index.d.ts +1 -2
  16. package/dist/dev/index.d.ts.map +1 -1
  17. package/dist/dev/useCliErrorCatcher.d.ts +57 -29
  18. package/dist/dev/useCliErrorCatcher.d.ts.map +1 -1
  19. package/dist/i18n/useI18nCatcher.d.ts +26 -13
  20. package/dist/i18n/useI18nCatcher.d.ts.map +1 -1
  21. package/dist/react-admin.cjs.js +44 -44
  22. package/dist/react-admin.cjs.js.map +1 -1
  23. package/dist/react-admin.es.js +3891 -3885
  24. package/dist/react-admin.es.js.map +1 -1
  25. package/dist/react-admin.umd.js +44 -44
  26. package/dist/react-admin.umd.js.map +1 -1
  27. package/dist/types.d.ts +54 -0
  28. package/dist/types.d.ts.map +1 -0
  29. package/package.json +1 -1
  30. package/src/AdminContext.tsx +19 -0
  31. package/src/{ApplicaAdmin.jsx → ApplicaAdmin.tsx} +115 -50
  32. package/src/components/Layout/Header/HeaderContent/Profile.jsx +1 -1
  33. package/src/contexts/MenuConfigContext.tsx +117 -0
  34. package/src/contexts/MenuPropTypes.jsx +2 -1
  35. package/src/dev/useCliErrorCatcher.ts +142 -0
  36. package/src/i18n/{useI18nCatcher.jsx → useI18nCatcher.ts} +49 -11
  37. package/src/types.ts +55 -0
  38. package/src/AdminContext.jsx +0 -24
  39. package/src/contexts/MenuConfigContext.jsx +0 -93
  40. package/src/dev/useCliErrorCatcher.jsx +0 -86
  41. /package/src/dev/{index.jsx → index.ts} +0 -0
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+ /**
3
+ * Definisce le proprietà di un elemento del menu.
4
+ */
5
+ export type MenuItemProps = {
6
+ id: string;
7
+ /**
8
+ * Titolo da attribure all'elemento del menu.
9
+ * Il titolo deve essere una stringa localizzata: es. ra.menu.item.foo
10
+ */
11
+ title: string;
12
+ /**
13
+ * Icona da attribuire all'elemento del menu.
14
+ */
15
+ icon: React.ReactNode;
16
+ /**
17
+ * Tipo di elemento del menu.
18
+ * - item: elemento di menu normale
19
+ * - group: gruppo di elementi di menu
20
+ * - collapse: gruppo di elementi di menu con collapse
21
+ */
22
+ type: 'item' | 'group' | 'collapse';
23
+ /**
24
+ * URL da attribuire all'elemento del menu.
25
+ * Può puntare ad una risorsa o ad una pagina personalizzata purché la stessa sia stata registrata nell'appliazione.
26
+ *
27
+ * @example
28
+ * /entities/user
29
+ * /pages/foo
30
+ */
31
+ url: string;
32
+ /**
33
+ * Indica se l'elemento del menu è una risorsa.
34
+ */
35
+ resource: boolean;
36
+ /**
37
+ * Elenco dei ruoli che possono accedere all'elemento del menu.
38
+ * Se l'utente collegato non contiene almeno uno dei ruoli specificati l'elemento del menu non viene visualizzato.
39
+ *
40
+ * @example
41
+ * ["ROLE_ADMIN", "ROLE_USER"]
42
+ */
43
+ roles: string[];
44
+ /**
45
+ * Elenco dei figli dell'elemento del menu.
46
+ * Valido solo per i tipi "group" e "collapse".
47
+ */
48
+ children?: MenuItemProps[];
49
+ };
50
+ /**
51
+ * Indica come deve essere configurato il menu dell'applicazione.
52
+ */
53
+ export type MenuProps = MenuItemProps[];
54
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB;;;;;OAKG;IACH,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;IACpC;;;;;;;OAOG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;CAC5B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,aAAa,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applica-software-guru/react-admin",
3
- "version": "1.0.45",
3
+ "version": "1.0.47",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,19 @@
1
+ import { CoreAdminContext, CoreAdminContextProps } from 'react-admin';
2
+
3
+ import { ScrollTop } from './components';
4
+ import { ThemeCustomization } from './themes';
5
+
6
+ const AdminContext = ({ children, theme, ...props }: CoreAdminContextProps) => (
7
+ <CoreAdminContext {...props}>
8
+ <ThemeCustomization themeOverrides={theme}>
9
+ <ScrollTop>
10
+ {/** @ts-ignore */}
11
+ {children}
12
+ </ScrollTop>
13
+ </ThemeCustomization>
14
+ </CoreAdminContext>
15
+ );
16
+
17
+ AdminContext.displayName = 'ApplicaAdminContext';
18
+
19
+ export default AdminContext;
@@ -4,13 +4,16 @@ import { Layout, MainIcon, Notification, SmallIcon } from './components';
4
4
  import { createI18nProvider, useI18nCatcher, useI18nLanguages } from './i18n';
5
5
 
6
6
  import Admin from './Admin';
7
+ import { AdminProps } from 'react-admin';
8
+ import { I18nCatcherBodyBuilderResultProps } from './i18n/useI18nCatcher';
9
+ import { MenuProps } from 'src/types';
7
10
  import PropTypes from 'prop-types';
8
11
  import { QueryClient } from 'react-query';
9
12
  import { useMemo } from 'react';
10
13
 
11
- const languageResponseMapper = (response) =>
14
+ const languageResponseMapper = (response: any) =>
12
15
  response.reduce(
13
- (messages, message) => ({
16
+ (messages: any, message: any) => ({
14
17
  ...messages,
15
18
  [message.lang]: {
16
19
  ...(messages[message.lang] || {}),
@@ -20,8 +23,8 @@ const languageResponseMapper = (response) =>
20
23
  {}
21
24
  );
22
25
 
23
- const i18nBodyRequestMapper = (locale, message) => ({
24
- lang: locale,
26
+ const i18nBodyRequestMapper = (lang: string, message: string): I18nCatcherBodyBuilderResultProps => ({
27
+ lang,
25
28
  code: message,
26
29
  text: message
27
30
  });
@@ -34,22 +37,125 @@ const queryClient = new QueryClient({
34
37
  }
35
38
  });
36
39
 
40
+ export type ApplicaAdminProps = AdminProps & {
41
+ theme: any;
42
+ apiUrl: string;
43
+ defaultLocale: string;
44
+ development: boolean;
45
+ logoMain: any;
46
+ logoIcon: any;
47
+ menu: MenuProps;
48
+ name: string;
49
+ version: string;
50
+ dataProvider: any;
51
+ authProvider: any;
52
+ notification: string;
53
+ disableNotification: boolean;
54
+ };
55
+
56
+ /**
57
+ * Definisce un'applicazione super figa basata su React Admi, Mantis Theme ed il nostro stile.
58
+ * Ogni applicazione che crei dovrebbe partire con un 'C'era una volta un ApplicaAdmin'.
59
+ *
60
+ * @example
61
+ * // Esempio di utilizzo
62
+ * import { ApplicaAdmin } from '@applica-software-guru/react-admin';
63
+ * import { createAuthProvider } from '@applica-software-guru/iam-client';
64
+ * import { createDataProvider } from '@applica-software-guru/crud-client';
65
+ * import * as entities from './entities';
66
+ *
67
+ * const apiUrl = "https://bimbobruno.applica.guru/api";
68
+ * const authProvider = createAuthProvider({ apiUrl });
69
+ * const getToken = async () => await authProvider.getToken();
70
+ * const getHeaders = async () => await authProvider.getHeaders();
71
+ *
72
+ * const dataProvider = createDataProvider({ apiUrl, getToken, getHeaders });
73
+ *
74
+ * const App = () => (
75
+ * <ApplicaAdmin
76
+ * apiUrl="https://api.applica.software-guru.it"
77
+ * dataProvider={dataProvider}
78
+ * authProvider={authProvider}
79
+ * menu={menu}
80
+ * name="Applica Admin"
81
+ * version="1.0.0">
82
+ * <Resource name="entities/user" {...entities.user} />
83
+ * </ApplicaAdmin>
84
+ *
85
+ * @param {ApplicaAdminProps}
86
+ * @returns {React.ReactElement}
87
+ */
37
88
  const ApplicaAdmin = ({
89
+ /**
90
+ * Eventuali configurazioni aggiuntive da passare al tema.
91
+ * @remarks Questo tema è basato su Mantis Theme (https://mantisdashboard.io/)
92
+ *
93
+ * @see https://marmelab.com/react-admin/Theming.html
94
+ * @see https://material-ui.com/customization/theming/
95
+ */
38
96
  theme,
97
+ /**
98
+ * URL dell'API da utilizzare nell'applicazione.
99
+ * Sebbene venga già definito un dataProvider ed authProvider di default, è possibile che l'app
100
+ * utilizzi l'apiUrl specificato per ulteriori operazioni (es. getstione delle notifiche).
101
+ */
39
102
  apiUrl,
103
+ /**
104
+ * La lingua di default da utilizzare nell'applicazione.
105
+ */
40
106
  defaultLocale,
107
+ /**
108
+ * Se impostato a true, l'applicazione lavora in modalità sviluppo.
109
+ * La modalità sviluppo è utile per effettuare test e debug ed esegue attività che non sono
110
+ * consigliate in produzione (es. cattura e inoltro dei messaggi non localizzati).
111
+ */
41
112
  development,
113
+ /**
114
+ * Il logo da visualizzare nella barra laterale quando il menu è espanso lateralmente.
115
+ */
42
116
  logoMain,
117
+ /**
118
+ * Eventuale logo da visualizzare nella barra laterale quando il menu è ridotto lateralmente.
119
+ */
43
120
  logoIcon,
121
+ /**
122
+ * Configurazione del menu da visualizzare nell'applicazione.
123
+ */
44
124
  menu,
125
+ /**
126
+ * Nome dell'applicazione da mostrare nell'header.
127
+ */
45
128
  name,
129
+ /**
130
+ * Versione dell'applicazione da mostrare nel footer.
131
+ */
46
132
  version,
133
+ /**
134
+ * Il provider di autenticazione da utilizzare nell'applicazione.
135
+ */
47
136
  dataProvider,
137
+ /**
138
+ * Il provider di dati da utilizzare nell'applicazione.
139
+ */
48
140
  authProvider,
141
+ /**
142
+ * Indica il nome della risorsa REST da utilizzare per la gestione delle notifiche.
143
+ * @example
144
+ * // In questo caso, le notifiche verranno gestite tramite la risorsa "entities/notification"
145
+ * <ApplicaAdmin notification="entities/notification" />
146
+ */
49
147
  notification,
148
+ /**
149
+ * Indica se le notifiche devono essere disabilitate.
150
+ * Se le notifiche sono abilitate comparirà automaticamente un'icona in alto a destra nell'header.
151
+ *
152
+ * @example
153
+ * // In questo caso, le notifiche verranno disabilitate
154
+ * <ApplicaAdmin disableNotification />
155
+ */
50
156
  disableNotification,
51
157
  ...props
52
- }) => {
158
+ }: ApplicaAdminProps) => {
53
159
  const languages = useI18nLanguages({
54
160
  apiUrl,
55
161
  endpoint: '/i18n/messages',
@@ -66,9 +172,9 @@ const ApplicaAdmin = ({
66
172
  useCliErrorCatcher({
67
173
  apiUrl,
68
174
  endpoint: '/cli/error',
69
- catcherFn: (error) => {
70
- var errorMessage = error?.toString();
71
- var knownErrors = [
175
+ catcherFn: (error: any): CatchResult => {
176
+ const errorMessage = error?.toString();
177
+ const knownErrors = [
72
178
  // @see https://github.com/marmelab/react-admin/pull/8884
73
179
  'Invalid prop `file` of type `string` supplied to `FileInputPreview`, expected `object`.',
74
180
  'Failed prop type: Invalid prop `checked` of type `string` supplied to `ForwardRef(Switch)`, expected `boolean`.',
@@ -87,7 +193,7 @@ const ApplicaAdmin = ({
87
193
  });
88
194
 
89
195
  const layout = useMemo(
90
- () => (props) => {
196
+ () => (props: any) => {
91
197
  const _logoMain = name ? <MainIcon title={name} /> : logoMain;
92
198
  const _logoIcon = name ? <SmallIcon title={name} /> : logoIcon;
93
199
  return (
@@ -146,59 +252,18 @@ ApplicaAdmin.defaultProps = {
146
252
 
147
253
  ApplicaAdmin.propTypes = {
148
254
  ...Admin.propTypes,
149
- /**
150
- * Eventuali configurazioni aggiuntive da passare al tema.
151
- */
152
255
  theme: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
153
- /**
154
- * URL dell'API da utilizzare nell'applicazione.
155
- */
156
256
  apiUrl: PropTypes.string.isRequired,
157
-
158
- /**
159
- * La lingua di default da utilizzare nell'applicazione.
160
- */
161
257
  defaultLocale: PropTypes.string,
162
- /**
163
- * Se impostato a true, l'applicazione lavora in modalità sviluppo.
164
- * In questa modalità, per esempio, l'applicazione è in grando di catturare ed inoltrare al server eventuali messaggi non localizzati.
165
- */
166
258
  development: PropTypes.bool,
167
- /**
168
- * Il logo da visualizzare nella barra laterale quando il menu è espanso lateralmente.
169
- */
170
259
  logoMain: PropTypes.node,
171
- /**
172
- * Eventuale logo da visualizzare nella barra laterale quando il menu è ridotto lateralmente.
173
- */
174
260
  logoIcon: PropTypes.node,
175
- /**
176
- * Configurazione del menu da visualizzare nell'applicazione.
177
- */
178
261
  menu: PropTypes.arrayOf(MenuPropTypes),
179
- /**
180
- * Nome dell'applicazione.
181
- */
182
262
  name: PropTypes.string,
183
- /**
184
- * Versione dell'applicazione.
185
- */
186
263
  version: PropTypes.string,
187
- /**
188
- * Il provider di autenticazione da utilizzare nell'applicazione.
189
- */
190
264
  authProvider: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
191
- /**
192
- * Il provider di dati da utilizzare nell'applicazione.
193
- */
194
265
  dataProvider: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
195
- /**
196
- * Indica il nome della risorsa REST da utilizzare per la gestione delle notifiche.
197
- */
198
266
  notification: PropTypes.string,
199
- /**
200
- * Indica se le notifiche devono essere disabilitate.
201
- */
202
267
  disableNotification: PropTypes.bool
203
268
  };
204
269
 
@@ -129,7 +129,7 @@ const Profile = () => {
129
129
  <Stack>
130
130
  <Typography variant="h6">{identity?.name}</Typography>
131
131
  <Typography variant="body2" color="textSecondary">
132
- {identity?.roles?.map((r) => r.name).join(',')}
132
+ {identity?.roles?.map((r) => r.name.replace('ROLE_', '')).join(',')}
133
133
  </Typography>
134
134
  </Stack>
135
135
  </Stack>
@@ -0,0 +1,117 @@
1
+ import MenuPropTypes from './MenuPropTypes';
2
+ import { MenuProps } from '../types';
3
+ import PropTypes from 'prop-types';
4
+ import { createContext } from 'react';
5
+ import { useLocalStorage } from '../hooks';
6
+
7
+ export type MenuConfigContextProps = {
8
+ openItem: string[];
9
+ openComponent: string;
10
+ selectedID: string | null;
11
+ drawerOpen: boolean;
12
+ componentDrawerOpen: boolean;
13
+ menuDashboard: any;
14
+ error: any;
15
+ groups: MenuProps;
16
+ activeItem: (openItem: string[]) => void;
17
+ activeComponent: (openComponent: string) => void;
18
+ openDrawer: (drawerOpen: boolean) => void;
19
+ openComponentDrawer: (componentDrawerOpen: boolean) => void;
20
+ activeID: (selectedID: string | null) => void;
21
+ getMenuSuccess: (menuDashboard: any) => void;
22
+ hasError: (error: any) => void;
23
+ };
24
+
25
+ const initialState = {
26
+ openItem: ['dashboard'],
27
+ openComponent: 'buttons',
28
+ selectedID: null,
29
+ drawerOpen: false,
30
+ componentDrawerOpen: true,
31
+ menuDashboard: {},
32
+ error: null
33
+ };
34
+
35
+ const MenuConfigContext = createContext(initialState);
36
+
37
+ export type MenuConfigProviderProps = {
38
+ menu: MenuProps;
39
+ children: React.ReactNode;
40
+ };
41
+
42
+ const MenuConfigProvider = ({ menu: groups, children }: MenuConfigProviderProps) => {
43
+ const [menu, setMenu] = useLocalStorage('menu-config', { ...initialState });
44
+
45
+ const activeItem = (openItem: string) => {
46
+ setMenu((menu: MenuConfigContextProps) => ({
47
+ ...menu,
48
+ openItem
49
+ }));
50
+ };
51
+
52
+ const activeID = (selectedID: string) => {
53
+ setMenu((menu: MenuConfigContextProps) => ({
54
+ ...menu,
55
+ selectedID
56
+ }));
57
+ };
58
+
59
+ const activeComponent = (openComponent: string) => {
60
+ setMenu((menu: MenuConfigContextProps) => ({
61
+ ...menu,
62
+ openComponent
63
+ }));
64
+ };
65
+
66
+ const openDrawer = (drawerOpen: boolean) => {
67
+ setMenu((menu: MenuConfigContextProps) => ({
68
+ ...menu,
69
+ drawerOpen
70
+ }));
71
+ };
72
+
73
+ const openComponentDrawer = (componentDrawerOpen: boolean) => {
74
+ setMenu((menu: MenuConfigContextProps) => ({
75
+ ...menu,
76
+ componentDrawerOpen
77
+ }));
78
+ };
79
+
80
+ const getMenuSuccess = (menuDashboard: any) => {
81
+ setMenu((menu: MenuConfigContextProps) => ({
82
+ ...menu,
83
+ menuDashboard
84
+ }));
85
+ };
86
+
87
+ const hasError = (error: any) => {
88
+ setMenu((menu: MenuConfigContextProps) => ({
89
+ ...menu,
90
+ error
91
+ }));
92
+ };
93
+ return (
94
+ <MenuConfigContext.Provider
95
+ value={{
96
+ ...menu,
97
+ groups,
98
+ activeItem,
99
+ activeComponent,
100
+ openDrawer,
101
+ openComponentDrawer,
102
+ activeID,
103
+ getMenuSuccess,
104
+ hasError
105
+ }}
106
+ >
107
+ {children}
108
+ </MenuConfigContext.Provider>
109
+ );
110
+ };
111
+
112
+ MenuConfigProvider.propTypes = {
113
+ children: PropTypes.node,
114
+ menu: PropTypes.arrayOf(MenuPropTypes)
115
+ };
116
+
117
+ export { MenuConfigContext, MenuConfigProvider };
@@ -13,7 +13,8 @@ const MenuPropTypes = PropTypes.shape({
13
13
  icon: PropTypes.elementType,
14
14
  url: PropTypes.string,
15
15
  parent: PropTypes.shape(lazy(() => MenuPropTypes)),
16
- children: PropTypes.arrayOf(lazy(() => MenuPropTypes))
16
+ children: PropTypes.arrayOf(lazy(() => MenuPropTypes)),
17
+ resource: PropTypes.bool
17
18
  });
18
19
 
19
20
  export default MenuPropTypes;
@@ -0,0 +1,142 @@
1
+ /* eslint-disable prefer-rest-params */
2
+ /* eslint-disable no-console */
3
+ import * as React from 'react';
4
+
5
+ export type CatchResultProps = {
6
+ catch: boolean;
7
+ display: boolean;
8
+ log: boolean;
9
+ error?: string;
10
+ };
11
+
12
+ class CatchResult {
13
+ catch: boolean;
14
+ display: boolean;
15
+ log: boolean;
16
+ error?: string;
17
+
18
+ constructor({ catch: catchErr, display: displayErr, log: logErr, error }: CatchResultProps) {
19
+ this.catch = catchErr;
20
+ this.display = displayErr;
21
+ this.log = logErr;
22
+ this.error = error;
23
+ }
24
+
25
+ isCatched() {
26
+ return this.catch;
27
+ }
28
+
29
+ logError() {
30
+ return this.log;
31
+ }
32
+
33
+ displayError() {
34
+ return this.display;
35
+ }
36
+ }
37
+
38
+ export type CliErrorCatcherBodyBuilderProps = (error: string | any) => any;
39
+
40
+ type CliErrorCatcherPutProps = {
41
+ apiUrl: string;
42
+ endpoint?: string;
43
+ message: string;
44
+ bodyBuilder: CliErrorCatcherBodyBuilderProps;
45
+ };
46
+
47
+ const putError = ({ apiUrl, endpoint, message, bodyBuilder }: CliErrorCatcherPutProps) =>
48
+ fetch(`${apiUrl}${endpoint}`, {
49
+ method: 'PUT',
50
+ headers: new Headers({
51
+ Accept: 'application/json',
52
+ 'Content-Type': 'application/json'
53
+ }),
54
+ body: JSON.stringify(bodyBuilder(message))
55
+ });
56
+
57
+ export type UseCliErrorCatcherProps = {
58
+ /**
59
+ * Definisce l'URL del server API.
60
+ */
61
+ apiUrl: string;
62
+ /**
63
+ * Definisce se il catcher è abilitato.
64
+ */
65
+ enabled?: boolean;
66
+ /**
67
+ * Definisce l'endpoint del server API su cui inviare l'errore.
68
+ * Questo parametro viene aggiunto all'URL del server API (apiUrl) attraverso una semplice concatenazione.
69
+ *
70
+ * @example
71
+ * // Serve più per ricordarti come funziona quando lo utilizzerai attivamente dopo tanto tempo (ti conosco bene Roberto, dimenticherai tutto!).
72
+ * let apiUrl = 'http://localhost:3000';
73
+ * let endpoint = '/ui-errors/put';
74
+ * let url = apiUrl + endpoint;
75
+ * // url = 'http://localhost:3000/ui-errors/put'
76
+ */
77
+ endpoint?: string;
78
+ /**
79
+ * Definisce se l'applicazione chiamante è in caricamento.
80
+ * In tal caso il catcher non viene attivato.
81
+ */
82
+ loading?: boolean;
83
+ /**
84
+ * Definisce la funzione che determina se l'errore deve essere catturato.
85
+ *
86
+ * @param error
87
+ * @returns {CatchResult} Restituisce un oggetto di tipo CatchResult.
88
+ */
89
+ catcherFn?: (error: string | any) => CatchResult;
90
+ /**
91
+ * Definisce la funzione che costruisce il body da inviare al server API.
92
+ */
93
+ bodyBuilder?: CliErrorCatcherBodyBuilderProps;
94
+ };
95
+
96
+ const useCliErrorCatcher = ({
97
+ enabled = true,
98
+ apiUrl,
99
+ endpoint = '/ui-errors/put',
100
+ loading,
101
+ catcherFn = (error) => new CatchResult({ catch: error != undefined, display: false, log: false }),
102
+ bodyBuilder = (error) => ({ message: error })
103
+ }: UseCliErrorCatcherProps) => {
104
+ React.useMemo(() => {
105
+ if (loading) {
106
+ return;
107
+ }
108
+ if (!enabled) {
109
+ return;
110
+ }
111
+
112
+ const consoleError = console.error;
113
+
114
+ console.error = function (error, ...args) {
115
+ if (!error || typeof error !== 'string') {
116
+ consoleError.apply(console, arguments as any);
117
+ return;
118
+ }
119
+ const message = error.replace(/%s/g, () => args.shift());
120
+ const catchResult = catcherFn(message);
121
+ if (!catchResult.isCatched()) {
122
+ consoleError.apply(console, arguments as any);
123
+ return;
124
+ }
125
+
126
+ if (catchResult.logError()) {
127
+ putError({ apiUrl, endpoint, message, bodyBuilder });
128
+ }
129
+
130
+ if (catchResult.displayError()) {
131
+ consoleError.apply(console, arguments as any);
132
+ } else {
133
+ // eslint-disable-next-line prefer-spread
134
+ console.debug.apply(console, arguments as any);
135
+ }
136
+ };
137
+ }, [apiUrl, loading, bodyBuilder, endpoint, catcherFn, enabled]);
138
+ return true;
139
+ };
140
+ export { CatchResult };
141
+
142
+ export default useCliErrorCatcher;
@@ -3,9 +3,33 @@ import * as React from 'react';
3
3
 
4
4
  import { useLocaleState } from 'react-admin';
5
5
 
6
- const queued = [];
6
+ const queued: string[] = [];
7
7
 
8
- const putMessage = ({ apiUrl, endpoint, locale, message, bodyBuilder }) =>
8
+ export type I18nCatcherBodyBuilderResultProps =
9
+ | {
10
+ lang: string;
11
+ code: string;
12
+ text: string;
13
+ }
14
+ | {
15
+ message: {
16
+ lang: string;
17
+ code: string;
18
+ text: string;
19
+ };
20
+ };
21
+
22
+ export type I18nCatcherBodyBuilderProps = (lang: string, message: string) => I18nCatcherBodyBuilderResultProps;
23
+
24
+ type PutMessageProps = {
25
+ apiUrl: string;
26
+ endpoint?: string;
27
+ locale: string;
28
+ message: string;
29
+ bodyBuilder: I18nCatcherBodyBuilderProps;
30
+ };
31
+
32
+ const putMessage = ({ apiUrl, endpoint, locale, message, bodyBuilder }: PutMessageProps): any =>
9
33
  message != null &&
10
34
  message !== 'undefined' &&
11
35
  message.indexOf('[') === -1 &&
@@ -18,22 +42,34 @@ const putMessage = ({ apiUrl, endpoint, locale, message, bodyBuilder }) =>
18
42
  Accept: 'application/json',
19
43
  'Content-Type': 'application/json'
20
44
  }),
21
- body: JSON.stringify(bodyBuilder(locale, message))
45
+ body: JSON.stringify(bodyBuilder(locale, message) || {})
22
46
  });
23
47
 
48
+ export type UseI18nCatcherProps = {
49
+ apiUrl: string;
50
+ enabled?: boolean;
51
+ endpoint?: string;
52
+ loading?: boolean;
53
+ bodyBuilder: I18nCatcherBodyBuilderProps;
54
+ };
55
+
56
+ /**
57
+ * Hook che consente di catturare ed inoltrare al server API le stringhe non tradotte.
58
+ *
59
+ * @param {UseI18nCatcherProps}
60
+ * @returns {boolean}
61
+ */
24
62
  const useI18nCatcher = ({
25
63
  apiUrl,
26
64
  enabled = true,
27
65
  endpoint = '/languages/put-message',
28
66
  loading,
29
- bodyBuilder = (locale, message) => ({
30
- code: locale,
31
- message: {
32
- code: message,
33
- text: message
34
- }
67
+ bodyBuilder = (lang, message) => ({
68
+ lang,
69
+ code: message,
70
+ text: message
35
71
  })
36
- }) => {
72
+ }: UseI18nCatcherProps) => {
37
73
  const [locale] = useLocaleState();
38
74
  React.useMemo(() => {
39
75
  if (loading) {
@@ -62,7 +98,9 @@ const useI18nCatcher = ({
62
98
  return;
63
99
  }
64
100
 
65
- consoleError.apply(console, arguments);
101
+ // eslint-disable-next-line prefer-rest-params
102
+ // eslint-disable-next-line prefer-rest-params
103
+ consoleError.apply(console, arguments as any);
66
104
  };
67
105
  }, [apiUrl, locale, loading, bodyBuilder, endpoint, enabled]);
68
106
  return true;