@applica-software-guru/react-admin 1.0.83 → 1.1.85

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": "@applica-software-guru/react-admin",
3
- "version": "1.0.83",
3
+ "version": "1.1.85",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -71,7 +71,7 @@
71
71
  "simplebar-react": "3.2.4"
72
72
  },
73
73
  "devDependencies": {
74
- "@applica-software-guru/crud-client": "1.0.*",
74
+ "@applica-software-guru/crud-client": "1.1.*",
75
75
  "@applica-software-guru/iam-client": "1.0.*",
76
76
  "@testing-library/jest-dom": "^5.16.5",
77
77
  "@testing-library/react": "^14.0.0",
@@ -18,7 +18,7 @@ const StyledRoot = styled('div', {
18
18
  [theme.breakpoints.up('sm')]: {
19
19
  marginTop: `-${theme.spacing(2)}`,
20
20
  marginBottom: theme.spacing(margin ? 0 : 1),
21
- marginLeft: theme.spacing(margin ? 1 : 2.5),
21
+ marginLeft: theme.spacing(margin ? 1 : 2),
22
22
  marginRight: theme.spacing(margin ? 1 : 2.5),
23
23
  width: margin ? 'auto' : `calc(100% - ${theme.spacing(5)})`
24
24
  },
@@ -28,6 +28,7 @@ const StyledRoot = styled('div', {
28
28
  },
29
29
  '& .RaDatagrid-root': {
30
30
  overflowX: 'auto',
31
+ paddingTop: 48,
31
32
  [theme.breakpoints.down('sm')]: {
32
33
  width: 'calc(100vw - 34px)'
33
34
  }
@@ -43,12 +44,17 @@ const StyledRoot = styled('div', {
43
44
  minHeight: 0
44
45
  },
45
46
  '& .RaBulkActionsToolbar-toolbar': {
46
- minHeight: 64,
47
+ minHeight: 48,
48
+ bottom: -56,
47
49
  '& h6': {
48
50
  marginTop: theme.spacing(0.5)
49
51
  },
50
52
  '& .RaBulkActionsToolbar-topToolbar': {
51
53
  marginTop: theme.spacing(1)
54
+ },
55
+ [theme.breakpoints.down('sm')]: {
56
+ bottom: -48,
57
+ borderRadius: 0
52
58
  }
53
59
  }
54
60
  }));
@@ -1,4 +1,5 @@
1
- import { FileInput as RaFileInput, useRecordContext } from 'react-admin';
1
+ /* eslint-disable no-console */
2
+ import { FileInputProps, FileInput as RaFileInput, useInput, useRecordContext } from 'react-admin';
2
3
 
3
4
  import { AttachmentField } from '../ra-fields';
4
5
  import LabeledInput from './LabeledInput';
@@ -6,7 +7,7 @@ import PropTypes from 'prop-types';
6
7
  import React from 'react';
7
8
  import { styled } from '@mui/material/styles';
8
9
 
9
- const removeDeleteIfNecessary = (disabled) => {
10
+ const removeDeleteIfNecessary = (disabled: boolean) => {
10
11
  if (disabled) {
11
12
  return {
12
13
  '& .RaFileInput-removeButton > .RaFileInputPreview-removeButton': {
@@ -25,7 +26,7 @@ const removeDeleteIfNecessary = (disabled) => {
25
26
  const StyledFileInput = styled(RaFileInput, {
26
27
  name: 'RaApplicaFileInput',
27
28
  slot: 'root'
28
- })(({ disabled, theme }) => ({
29
+ })(({ disabled, theme }: { disabled: boolean; theme: any }) => ({
29
30
  '& .previews': {},
30
31
  '& .previews>div': {
31
32
  marginTop: theme.spacing(1),
@@ -47,12 +48,31 @@ const StyledFileInput = styled(RaFileInput, {
47
48
  ...removeDeleteIfNecessary(disabled)
48
49
  }));
49
50
 
50
- const AttachmentInput = ({ children, disabled, ...props }) => {
51
+ export type AttachmentInputProps = FileInputProps & {
52
+ children?: React.ReactNode;
53
+ disabled?: boolean;
54
+ source?: string;
55
+ type?: 'file' | 'image' | 'attachment';
56
+ };
57
+
58
+ /**
59
+ * Consente di definire un campo di tipo input all'interno del quale è possibile caricare uno o più file.
60
+ *
61
+ * @example
62
+ * <AttachmentInput source="attachments" title="name" accept="image/*" multiple>
63
+ *
64
+ * @param {AttachmentInputProps}
65
+ * @returns
66
+ */
67
+ const AttachmentInput = ({ children, disabled, type, ...props }: AttachmentInputProps): JSX.Element => {
51
68
  const record = useRecordContext(props);
52
69
  const property = props?.source;
53
70
  const entityId = record?.id;
71
+ useInput({ source: `__${type}__${props?.source}`, defaultValue: props?.source });
54
72
  return (
73
+ /** @ts-ignore */
55
74
  <LabeledInput {...props}>
75
+ {/** @ts-ignore */}
56
76
  <StyledFileInput disabled={disabled}>{React.cloneElement(children, { ...children.props, entityId, property })}</StyledFileInput>
57
77
  </LabeledInput>
58
78
  );
@@ -66,9 +86,11 @@ AttachmentInput.propTypes = {
66
86
  };
67
87
 
68
88
  AttachmentInput.defaultProps = {
89
+ /** @ts-ignore */
69
90
  children: <AttachmentField title="name" source="path" />,
70
91
  multiple: true,
71
- disabled: false
92
+ disabled: false,
93
+ type: 'attachment'
72
94
  };
73
95
 
74
96
  export default AttachmentInput;
@@ -1,4 +1,4 @@
1
- import { ImageInput as RaImageInput, useRecordContext } from 'react-admin';
1
+ import { ImageInput as RaImageInput, useInput, useRecordContext } from 'react-admin';
2
2
 
3
3
  import { CoverField } from '../ra-fields';
4
4
  import LabeledInput from './LabeledInput';
@@ -11,6 +11,7 @@ const ImageInput = ({ children, title, ...props }) => {
11
11
  const file = useWatch({ name: props.source });
12
12
  const record = useRecordContext(props);
13
13
  const { source } = props;
14
+ useInput({ source: `__image__${props?.source}`, defaultValue: props?.source });
14
15
  return (
15
16
  <LabeledInput {...props}>
16
17
  <RaImageInput accept="image/*">
@@ -39,13 +39,13 @@ const ApplicaStyledList = styled(RaList, {
39
39
  padding: theme.spacing(2),
40
40
  paddingTop: 0,
41
41
  '& .RaFilterForm-filterFormInput': {
42
+ marginTop: theme.spacing(1),
42
43
  '& .ra-input': {
43
44
  alignSelf: 'center'
44
45
  },
45
46
  [theme.breakpoints.down('sm')]: {
46
47
  // Quando lo schermo è piccolo, il filtro viene visualizzato in una colonna.
47
48
  // Diamo un margine maggiore in basso per evitare che siano troppo vicini.
48
- marginTop: theme.spacing(1),
49
49
  marginBottom: theme.spacing(1)
50
50
  }
51
51
  },
@@ -29,7 +29,6 @@ const RegisterPage = ({ name, version }: BaseAuthProps) => {
29
29
  redirect('/login');
30
30
  })
31
31
  .catch((error: any) => {
32
- console.log('error:', error);
33
32
  notify(error, { type: 'error' });
34
33
  })
35
34
  .finally(() => setLoading(false));
@@ -2,7 +2,7 @@
2
2
 
3
3
  import * as entities from './entities';
4
4
 
5
- import { API_URL, APP_NAME, FILE_FIELDS } from './config';
5
+ import { API_URL, APP_NAME } from './config';
6
6
  import { ActivatePage, ApplicaAdmin, HttpError, RecoverPage, RegisterPage, Resource } from '@applica-software-guru/react-admin';
7
7
  import { createAttachmentsParser, createDataProvider } from '@applica-software-guru/crud-client';
8
8
 
@@ -20,7 +20,7 @@ const dataProvider = createDataProvider({
20
20
  authProvider,
21
21
  getHeaders: () => authProvider.getHeaders(),
22
22
  getToken: () => authProvider.getToken(),
23
- attachmentsParser: createAttachmentsParser(FILE_FIELDS),
23
+ attachmentsParser: createAttachmentsParser(),
24
24
  HttpErrorClass: HttpError
25
25
  });
26
26
  const App = () => {
@@ -44,7 +44,6 @@ const App = () => {
44
44
  <CustomRoutes>
45
45
  <Route path="/custom-page" element={<CustomPage />} />
46
46
  </CustomRoutes>
47
-
48
47
  <Resource name="entities/notification" {...entities.notification} />
49
48
  <Resource name="entities/user" {...entities.user} />
50
49
  <Resource name="entities/i18n-message" {...entities.i18nMessage} />
@@ -0,0 +1,48 @@
1
+ import {
2
+ ArrayInput,
3
+ AttachmentInput,
4
+ LongForm,
5
+ MainCard,
6
+ SaveButton,
7
+ SimpleFormIterator,
8
+ TextInput,
9
+ Toolbar
10
+ } from '@applica-software-guru/react-admin';
11
+
12
+ import { Grid } from '@mui/material';
13
+
14
+ const OrderForm = () => (
15
+ <LongForm>
16
+ <LongForm.Tab label="Test">
17
+ <MainCard title="Foo">
18
+ <Grid container>
19
+ <Grid item xs={12}>
20
+ <TextInput source="name" />
21
+ <AttachmentInput source="attachment" title="name" multiple={false} />
22
+ <AttachmentInput source="attachments" title="name" multiple={true} />
23
+ </Grid>
24
+ <Grid item xs={12}>
25
+ <ArrayInput source="items">
26
+ <SimpleFormIterator>
27
+ <TextInput source="name" />
28
+ <AttachmentInput source="attachment" multiple={false} title="attachment.name" />
29
+ <ArrayInput source="subItems">
30
+ <SimpleFormIterator>
31
+ <TextInput source="name" />
32
+ <AttachmentInput source="attachment" multiple={false} title="attachment.name" />
33
+ <AttachmentInput source="subAttachments" multiple title="attachment.name" />
34
+ </SimpleFormIterator>
35
+ </ArrayInput>
36
+ </SimpleFormIterator>
37
+ </ArrayInput>
38
+ </Grid>
39
+ </Grid>
40
+ <Toolbar>
41
+ <SaveButton />
42
+ </Toolbar>
43
+ </MainCard>
44
+ </LongForm.Tab>
45
+ </LongForm>
46
+ );
47
+
48
+ export default OrderForm;
@@ -1,4 +1,4 @@
1
- import DeviceForm from './DeviceForm';
2
- import I18nMessageForm from './I18nMessageForm';
3
- import UserForm from './UserForm';
4
- export { DeviceForm, I18nMessageForm, UserForm };
1
+ export { default as DeviceForm } from './DeviceForm';
2
+ export { default as I18nMessageForm } from './I18nMessageForm';
3
+ export { default as UserForm } from './UserForm';
4
+ export { default as OrderForm } from './OrderForm';
@@ -0,0 +1,12 @@
1
+ import { Datagrid, DateField, EditButton, List, TextField } from '@applica-software-guru/react-admin';
2
+ const OrderList = () => (
3
+ <List perPage={25}>
4
+ <Datagrid>
5
+ <TextField source="name" />
6
+ <DateField source="updated" showTime />
7
+ <EditButton />
8
+ </Datagrid>
9
+ </List>
10
+ );
11
+
12
+ export default OrderList;
@@ -1,4 +1,4 @@
1
- import DeviceList from './DeviceList';
2
- import I18nMessageList from './I18nMessageList';
3
- import UserList from './UserList';
4
- export { DeviceList, I18nMessageList, UserList };
1
+ export { default as DeviceList } from './DeviceList';
2
+ export { default as I18nMessageList } from './I18nMessageList';
3
+ export { default as UserList } from './UserList';
4
+ export { default as OrderList } from './OrderList';
@@ -9,11 +9,6 @@ export const APP_URL = appUrl;
9
9
  export const API_URL = `${APP_URL}api`;
10
10
  export const ENVIRONMENT = environment;
11
11
 
12
- export const FILE_FIELDS = {
13
- images: ['image'],
14
- files: ['file'],
15
- attachments: ['attachment']
16
- };
17
12
  export const CONFIGURED_ROLES = [
18
13
  { id: 'ROLE_ADMIN', name: 'Admin' },
19
14
  { id: 'ROLE_USER', name: 'User' }
@@ -2,3 +2,4 @@ export { default as user } from './user';
2
2
  export { default as notification } from './notification';
3
3
  export { default as i18nMessage } from './i18n-message';
4
4
  export { default as device } from './device';
5
+ // export { default as order } from './order'; # Just for local tests.
@@ -0,0 +1,24 @@
1
+ import { Create, Edit } from '@applica-software-guru/react-admin';
2
+ import { OrderForm, OrderList } from '../components';
3
+
4
+ const OrderCreate = () => (
5
+ <Create>
6
+ <OrderForm />
7
+ </Create>
8
+ );
9
+ const EditForm = () => (
10
+ <Edit mutationMode="pessimistic" redirect={false}>
11
+ <OrderForm />
12
+ </Edit>
13
+ );
14
+
15
+ const config = {
16
+ list: OrderList,
17
+ edit: EditForm,
18
+ create: OrderCreate,
19
+ options: {
20
+ group: 'dashboard'
21
+ }
22
+ };
23
+
24
+ export default config;
@@ -8,11 +8,18 @@ const config = [
8
8
  icon: DashboardOutlined,
9
9
  children: [
10
10
  {
11
- id: '/entities/notification',
11
+ id: 'entities/notification',
12
12
  title: 'ra.menu.item.notification',
13
13
  type: 'item',
14
14
  url: '/entities/notification',
15
15
  icon: NotificationOutlined
16
+ },
17
+ {
18
+ id: 'entities/order',
19
+ title: 'ra.menu.item.order',
20
+ type: 'item',
21
+ url: '/entities/order',
22
+ icon: TableOutlined
16
23
  }
17
24
  ]
18
25
  },
@@ -1,9 +1,3 @@
1
- const theme = () => ({
2
- components: {
3
- MuiPaper: {
4
- styleOverrides: {}
5
- }
6
- }
7
- });
1
+ const theme = () => ({});
8
2
 
9
3
  export default theme;
@@ -0,0 +1,94 @@
1
+ import { CreateParams, DataProvider, HttpError, UpdateManyParams, UpdateParams } from 'ra-core';
2
+
3
+ export type AttachmentParserConfig = {
4
+ images: string[];
5
+ files: string[];
6
+ attachments?: string[];
7
+ };
8
+
9
+ export type AttachmentParserResult = {
10
+ data: any;
11
+ parts: any[];
12
+ };
13
+ export type ErrorMapperResult = {
14
+ errors?: any[];
15
+ };
16
+
17
+ export type CreateHeaderOptions = {
18
+ headers: Headers | any;
19
+ body?: any;
20
+ user?: {
21
+ token: string;
22
+ };
23
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE' | string;
24
+ };
25
+
26
+ export type UpdateManyParamsExtended = UpdateManyParams & {
27
+ rows: any[];
28
+ };
29
+
30
+ export type ApplicaDataProviderConfig = {
31
+ apiUrl: string;
32
+ getHeaders: () => Promise<Headers | any>;
33
+ getToken: () => Promise<string>;
34
+ HttpErrorClass: any | HttpError | Error;
35
+ attachmentsParser: (data: any) => Promise<AttachmentParserResult>;
36
+ prepareData?: (data: any, resource?: string, params?: CreateParams | UpdateParams | any) => any;
37
+ /**
38
+ * Consente di specificare se l'applicazione è in modalità mobile.
39
+ * In questo caso il dataProvider può adottare comportamenti differenti, in particolar modo
40
+ * per le chiamate di creazione e modifica dei records in cui l'utilizzo di FormData
41
+ * è soppiantato dall'utilizzo di chiamate REST pure e l'upload dei file viene gestito
42
+ * separatamente.
43
+ */
44
+ mobile?: boolean;
45
+ };
46
+
47
+ export type ApplicaDataProviderInterface = DataProvider & {
48
+ /**
49
+ * Consente di ottenere l'url del servizio REST.
50
+ */
51
+ getApiUrl(): string;
52
+ /**
53
+ * Consente di recuperare un singolo file dal server utilizzando eventuali
54
+ * meccanismi di autenticazione associati al dataProvider (es. JWT passato mediante getHeaders).
55
+ *
56
+ * @param {String} resource URL del file da recuperare
57
+ * @returns {Promise} Restituisce una promise che contiene il file recuperato
58
+ *
59
+ * @example Simulazione del download di un file.
60
+ * const attachment = await dataProvider.getFile(`/attachments/post/1/picture/1}`);
61
+ * const link = document.createElement('a');
62
+ * link.href = attachment;
63
+ * link.download = get(record, props?.title || props?.source);
64
+ * link.click();
65
+ *
66
+ */
67
+ getFile(resource: string): Promise<string>;
68
+ /**
69
+ * Consente di eseguire una chiamata GET generica al servizio REST.
70
+ * La risposta del servizio deve contenere almeno 'responseCode' cosi come previsto da standard applica.
71
+ * Response code deve essere uguale ad 'ok' se la chiamata ha avuto successo.
72
+ *
73
+ * @example
74
+ * // Esempio di chiamata: users?name=Roberto
75
+ * const data = await dataProvider.get("users", { name: "Roberto" })
76
+ *
77
+ *
78
+ * @param resource Risorsa da richiedere
79
+ * @param params Parametri della query string da inserire
80
+ */
81
+ get(resource: string, params: any): Promise<any>;
82
+ /**
83
+ * Consente di eseguire una chiamata POST generica al servizio REST.
84
+ * La risposta del servizio deve contenere almeno 'responseCode' cosi come previsto da standard applica.
85
+ * Response code deve essere uguale ad 'ok' se la chiamata ha avuto successo.
86
+ *
87
+ * @example
88
+ * const data = await dataProvider.post("users", { name: "Roberto" })
89
+ *
90
+ * @param resource Risorsa da richiedere
91
+ * @param params Parametri della query string da inserire
92
+ */
93
+ post(resource: string, params: any): Promise<any>;
94
+ };