@applica-software-guru/react-admin 1.5.234 → 1.5.236

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
@@ -115,5 +115,5 @@
115
115
  "type": "module",
116
116
  "types": "dist/index.d.ts",
117
117
  "typings": "dist/index.d.ts",
118
- "version": "1.5.234"
118
+ "version": "1.5.236"
119
119
  }
@@ -1,25 +1,9 @@
1
- import { useAppConfig } from '@/components/AppStateProvider';
2
1
  import { Toolbar } from '@/components/ra-forms';
3
- import ContentAdd from '@mui/icons-material/Add';
4
- import { Dialog, Fab, useMediaQuery } from '@mui/material';
5
- import { SxProps, Theme, styled } from '@mui/material/styles';
6
- import clsx from 'clsx';
2
+ import { Breakpoint, Dialog } from '@mui/material';
3
+ import { CreateBase, UseCreateMutateParams, useResourceContext } from 'ra-core';
4
+ import { Button, CreateButton, CreateButtonProps, CreateProps, SaveButton } from 'ra-ui-materialui';
7
5
  import React, { Children, PropsWithChildren, useCallback, useState } from 'react';
8
- import {
9
- Button,
10
- CreateButtonClasses,
11
- CreateContextProvider,
12
- CreateControllerProps,
13
- CreateControllerResult,
14
- RedirectionSideEffect,
15
- SaveButton,
16
- UseCreateMutateParams,
17
- useCreateController,
18
- useNotify,
19
- useRedirect,
20
- useResourceContext,
21
- useTranslate
22
- } from 'react-admin';
6
+ import { FieldValues, useFormContext } from 'react-hook-form';
23
7
  import { useQueryClient } from 'react-query';
24
8
 
25
9
  function updateColl(old: any, data: any) {
@@ -43,80 +27,74 @@ function setListQueryData(res: any, data: any) {
43
27
  return res && res.data ? { ...res, data: updateColl(res.data, data), total: res.total + 1 } : res;
44
28
  }
45
29
 
46
- function CreateInDialogButton({
47
- fullWidth = true,
48
- maxWidth = 'md',
49
- label = 'ra.action.create',
50
- record,
51
- redirect,
52
- scrollToTop = true,
53
- className,
54
- sx,
55
- style,
56
- fab = true,
57
- onSubmit,
58
- onSuccess,
59
- onError,
60
- ...controllerProps
61
- }: CreateInDialogButtonProps): JSX.Element {
62
- const [open, setOpen] = useState(false);
63
- const translate = useTranslate();
64
- const resource = useResourceContext();
65
- const { openDialog, closeDialog } = useAppConfig();
66
- const handleOpen = useCallback(() => openDialog(resource, () => setOpen(true)), [openDialog, resource]);
67
- const handleClose = useCallback(() => closeDialog(resource, () => setOpen(false)), [closeDialog, resource]);
68
- const isMobile = useMediaQuery((theme: Theme) => theme.breakpoints.down('md'));
69
- const isSmall = fab && isMobile;
70
- return (
71
- <>
72
- {isSmall ? (
73
- <StyledFab
74
- // @ts-expect-error WARN: Why state prop not exists on styled fab?
75
- state={scrollStates[String(scrollToTop)]}
76
- color="primary"
77
- className={clsx(CreateButtonClasses.floating, className)}
78
- aria-label={label ? translate(label) : null}
79
- onClick={handleOpen}
80
- >
81
- <ContentAdd />
82
- </StyledFab>
83
- ) : (
84
- <Button sx={sx} label={label} onClick={handleOpen} style={style}>
85
- <ContentAdd />
86
- </Button>
87
- )}
88
- <Dialog open={open} scroll="body" onClose={handleClose} fullWidth={fullWidth} maxWidth={maxWidth}>
89
- <CreateInDialogContent
90
- {...controllerProps}
91
- redirect={redirect}
92
- record={record}
93
- onClose={handleClose}
94
- onSubmit={onSubmit}
95
- onSuccess={onSuccess}
96
- onError={onError}
97
- />
98
- </Dialog>
99
- </>
30
+ type ResponsiveButtonProps = CreateButtonProps & {
31
+ label?: string;
32
+ onClick?: () => void;
33
+ };
34
+ function ResponsiveButton(props: ResponsiveButtonProps): JSX.Element {
35
+ const handleClick = useCallback(
36
+ (event: any) => {
37
+ (event as Event).preventDefault();
38
+ (event as Event).stopPropagation();
39
+ props?.onClick?.();
40
+ },
41
+ [props]
100
42
  );
43
+ return <CreateButton {...props} onClick={handleClick} />;
101
44
  }
45
+ type CloseDialogFunction = () => void;
46
+ type SubmitFunction = (values: FieldValues, closeDialog: CloseDialogFunction) => void;
47
+ type SubmitButtonProps = {
48
+ onSubmit: SubmitFunction;
49
+ closeDialog: CloseDialogFunction;
50
+ };
51
+ function SubmitButton(props: SubmitButtonProps) {
52
+ const { onSubmit, closeDialog } = props;
53
+ const { handleSubmit } = useFormContext();
54
+ const handleClick = useCallback(
55
+ (event: any) => {
56
+ (event as Event).preventDefault();
57
+ (event as Event).stopPropagation();
58
+ handleSubmit((fieldValues) => onSubmit(fieldValues, closeDialog))();
59
+ },
60
+ [handleSubmit, onSubmit, closeDialog]
61
+ );
62
+ return <Button onClick={handleClick} label="ra.action.save" color="primary" variant="contained" size="medium" />;
63
+ }
64
+ type CreateInDialogButtonProps = PropsWithChildren &
65
+ CreateProps &
66
+ ResponsiveButtonProps & {
67
+ maxWidth: Breakpoint;
68
+ fullWidth?: boolean;
69
+ fullScreen?: boolean;
102
70
 
103
- function CreateInDialogContent({
104
- onClose,
105
- record,
106
- children,
107
- redirect: _redirect = false,
108
- onSubmit,
109
- onSuccess,
110
- onError,
111
- ...props
112
- }: CreateInDialogContentProps) {
113
- const Child = Children.only(children);
71
+ /**
72
+ * Custom handler for the submit event, in this case you are responsible for closing the dialog.
73
+ * @example
74
+ *
75
+ * const handleSubmit = (values, closeDialog) => {
76
+ * console.log(values); // { title: 'Hello World' }
77
+ * closeDialog();
78
+ * }
79
+ * <CreateInDialogButton onSubmit={handleSubmit}>
80
+ * <SimpleForm>
81
+ * <TextInput source="title" />
82
+ * </SimpleForm>
83
+ * </CreateInDialogButton>
84
+ *
85
+ */
86
+ onSubmit?: SubmitFunction;
87
+ };
88
+ function CreateInDialogButton(props: CreateInDialogButtonProps) {
89
+ const { children, maxWidth = 'md', fullWidth = true, fullScreen, onSubmit, mutationOptions, ...rest } = props;
114
90
  const queryClient = useQueryClient();
115
91
  const resource = useResourceContext();
116
- const redirect = useRedirect();
117
- const notify = useNotify();
92
+ const Child = Children.only(children);
93
+ const [open, setOpen] = useState(false);
94
+ const handleOpen = useCallback(() => setOpen(true), []);
95
+ const handleClose = useCallback(() => setOpen(false), []);
118
96
  const handleSuccess = useCallback(
119
- (data: any, variables: UseCreateMutateParams<any>, context: unknown) => {
97
+ (data: any, params: UseCreateMutateParams<any>, context: unknown) => {
120
98
  const now = Date.now();
121
99
  const updatedAt = now;
122
100
 
@@ -130,139 +108,44 @@ function CreateInDialogContent({
130
108
  queryClient.setQueriesData([resource, 'getManyReference'], (res: any) => setManyReferenceQueryData(res, data), {
131
109
  updatedAt
132
110
  });
133
-
134
- onClose();
135
- notify('ra.notification.created');
136
- if (_redirect !== undefined) {
137
- redirect(_redirect as RedirectionSideEffect, resource, data.id, data);
138
- }
139
- if (onSuccess) {
140
- onSuccess(data, onClose);
141
- }
142
- if (props?.mutationOptions?.onSuccess) {
143
- props.mutationOptions.onSuccess(data, variables, { ...(context as any), onClose });
144
- }
145
- },
146
- [onClose, onSuccess, queryClient, resource, notify, redirect, _redirect, props?.mutationOptions]
147
- );
148
- const { save, ...createProps } = useCreateController({
149
- ...props,
150
- mutationOptions: {
151
- ...props?.mutationOptions,
152
- onSuccess: handleSuccess,
153
- onError: onError
154
- }
155
- });
156
- const handleSave = useCallback(
157
- (record: any) => {
158
- if (onSubmit) {
159
- onSubmit(record, onClose);
160
- } else if (save) {
161
- save(record);
162
- }
111
+ handleClose();
112
+ props?.mutationOptions?.onSuccess?.(data, params, context);
163
113
  },
164
- [onSubmit, save, onClose]
114
+ [handleClose, props.mutationOptions, queryClient, resource]
165
115
  );
116
+
166
117
  return (
167
- <CreateContextProvider
168
- value={
169
- {
170
- record: record,
171
- save: handleSave,
172
- saving: createProps?.saving,
173
- redirect: _redirect
174
- } as CreateControllerResult
175
- }
176
- >
177
- {React.isValidElement(Child)
178
- ? React.cloneElement(Child, {
179
- ...Child.props,
180
- modal: true,
181
- toolbar: (
182
- <Toolbar>
183
- <Button
184
- variant="text"
185
- size="medium"
186
- label="ra.action.cancel"
187
- onClick={onClose}
188
- disabled={createProps?.saving}
189
- />
190
- <SaveButton type="button" disabled={createProps?.saving} />
191
- </Toolbar>
192
- )
193
- })
194
- : null}
195
- </CreateContextProvider>
118
+ <>
119
+ <ResponsiveButton {...rest} onClick={handleOpen} />
120
+ <CreateBase
121
+ {...props}
122
+ mutationOptions={{
123
+ ...mutationOptions,
124
+ onSuccess: handleSuccess
125
+ }}
126
+ >
127
+ <Dialog open={open} onClose={handleClose} maxWidth={maxWidth} fullWidth={fullWidth} fullScreen={fullScreen}>
128
+ {React.isValidElement(Child)
129
+ ? React.cloneElement(Child, {
130
+ ...Child.props,
131
+ modal: true,
132
+ toolbar: (
133
+ <Toolbar>
134
+ <Button variant="text" size="medium" label="ra.action.cancel" onClick={handleClose} />
135
+ {onSubmit ? (
136
+ <SubmitButton onSubmit={onSubmit!} closeDialog={handleClose} />
137
+ ) : (
138
+ <SaveButton type="button" />
139
+ )}
140
+ </Toolbar>
141
+ )
142
+ })
143
+ : null}
144
+ </Dialog>
145
+ </CreateBase>
146
+ </>
196
147
  );
197
148
  }
198
149
 
199
- const scrollStates = {
200
- true: { _scrollToTop: true },
201
- false: {}
202
- } as any;
203
-
204
- const StyledFab = styled(Fab, { overridesResolver: (_props, styles) => styles.root })(({ theme }) => ({
205
- [`&.${CreateButtonClasses.floating}`]: {
206
- color: theme.palette.getContrastText(theme.palette.primary.main),
207
- margin: 0,
208
- top: 'auto',
209
- right: 20,
210
- bottom: 60,
211
- left: 'auto',
212
- position: 'fixed',
213
- zIndex: 1000
214
- }
215
- }));
216
-
217
- type CreateInDialogButtonProps = CreateControllerProps &
218
- PropsWithChildren<{
219
- fullWidth?: boolean;
220
- maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | false;
221
- label?: string;
222
- record?: any;
223
- redirect?: RedirectionSideEffect;
224
- scrollToTop?: boolean;
225
- className?: string;
226
- sx?: SxProps;
227
- style?: React.CSSProperties;
228
- /**
229
- * If set to false, the button will always render as a regular button, regardless of the screen size.
230
- */
231
- fab: boolean;
232
- /**
233
- * @deprecated Use mutationOptions.onSubmit instead
234
- */
235
- onSubmit?: (record: any, close: () => void) => void;
236
- /**
237
- * @deprecated Use mutationOptions.onSuccess instead
238
- */
239
- onSuccess?: (data: any) => void;
240
- /**
241
- *
242
- * @deprecated Use mutationOptions.onError instead
243
- */
244
- onError?: (error: any) => void;
245
- }>;
246
-
247
- type CreateInDialogContentProps = CreateControllerProps &
248
- PropsWithChildren<{
249
- onClose: () => void;
250
- record?: any;
251
- redirect: RedirectionSideEffect | boolean | undefined;
252
- /**
253
- * You can use it to handle the form submission yourself.
254
- * Good luck with that.
255
- */
256
- onSubmit?: (record: any, close: () => void) => void;
257
- /**
258
- * @deprecated Use mutationOptions.onSuccess instead
259
- */
260
- onSuccess?: (data: any, close: () => void) => void;
261
- /**
262
- * @deprecated Use mutationOptions.onError instead
263
- */
264
- onError?: (error: any) => void;
265
- }>;
266
-
267
150
  export { CreateInDialogButton };
268
151
  export type { CreateInDialogButtonProps };
@@ -1,119 +1,84 @@
1
- import { useAppConfig } from '@/components/AppStateProvider';
2
1
  import { Toolbar } from '@/components/ra-forms';
3
- import { Edit } from '@mui/icons-material';
4
2
  import { Breakpoint, Dialog } from '@mui/material';
3
+ import { EditBase, UseUpdateMutateParams, useRecordContext } from 'ra-core';
4
+ import { Button, EditButton, EditProps, SaveButton } from 'ra-ui-materialui';
5
5
  import React, { Children, PropsWithChildren, useCallback, useState } from 'react';
6
- import {
7
- Button,
8
- EditContextProvider,
9
- EditControllerProps,
10
- SaveButton,
11
- useEditController,
12
- useGetOne,
13
- useNotify,
14
- useRecordContext,
15
- useResourceContext
16
- } from 'react-admin';
17
6
 
18
- type EditInDialogContentProps = EditControllerProps &
19
- PropsWithChildren<{
20
- onClose: () => void;
21
- }>;
22
- function EditInDialogContent({ onClose, children, mutationMode = 'pessimistic', ...props }: EditInDialogContentProps) {
23
- const Child = Children.only(children);
24
- const notify = useNotify();
25
- const record = useRecordContext();
26
- const resource = useResourceContext();
27
- const { id } = record;
28
- const { isLoading, data } = useGetOne(resource, { id });
29
- const { onSuccess } = props;
7
+ type ResponsiveButtonProps = {
8
+ label?: string;
9
+ onClick: () => void;
10
+ };
30
11
 
31
- const handleSuccess = useCallback(
32
- (...args: any) => {
33
- notify('ra.notification.updated', {
34
- type: 'info',
35
- messageArgs: {
36
- smart_count: 1
37
- }
38
- });
39
- onClose();
40
- if (onSuccess) {
41
- onSuccess(...args);
42
- }
12
+ function ResponsiveButton(props: ResponsiveButtonProps): JSX.Element {
13
+ const handleClick = useCallback(
14
+ (event: any) => {
15
+ (event as Event).preventDefault();
16
+ (event as Event).stopPropagation();
17
+ props?.onClick?.();
43
18
  },
44
- [onClose, notify, onSuccess]
45
- );
46
- const { save, saving } = useEditController({
47
- ...props,
48
- resource,
49
- id,
50
- mutationMode,
51
- mutationOptions: {
52
- ...props?.mutationOptions,
53
- onSuccess: handleSuccess
54
- }
55
- });
56
-
57
- return (
58
- <EditContextProvider
59
- // @ts-ignore
60
- value={{
61
- record: data,
62
- isLoading,
63
- save: save,
64
- saving: saving
65
- }}
66
- >
67
- {React.isValidElement(Child)
68
- ? React.cloneElement(Child, {
69
- ...Child.props,
70
- modal: true,
71
- toolbar: (
72
- <Toolbar>
73
- <Button variant="text" size="medium" label="ra.action.cancel" onClick={onClose} />
74
- <SaveButton type="button" />
75
- </Toolbar>
76
- )
77
- })
78
- : null}
79
- </EditContextProvider>
19
+ [props]
80
20
  );
21
+ return <EditButton {...props} onClick={handleClick} />;
81
22
  }
82
23
 
83
- function EditInDialogButton({
84
- fullWidth = true,
85
- maxWidth = 'md',
86
- label = 'ra.action.edit',
87
- style,
88
- children,
89
- ...props
90
- }: EditInDialogButtonProps): JSX.Element {
24
+ type EditInDialogButtonProps = PropsWithChildren &
25
+ EditProps &
26
+ ResponsiveButtonProps & {
27
+ maxWidth: Breakpoint;
28
+ fullWidth?: boolean;
29
+ fullScreen?: boolean;
30
+ };
31
+ function EditInDialogButton(props: EditInDialogButtonProps): JSX.Element {
32
+ const { children, maxWidth = 'md', fullWidth = true, fullScreen, mutationOptions, ...rest } = props;
91
33
  const [open, setOpen] = useState(false);
92
- const { openDialog, closeDialog } = useAppConfig();
93
- const resource = useResourceContext();
94
- const handleOpen = useCallback(() => openDialog(resource, () => setOpen(true)), [openDialog, resource]);
95
- const handleClose = useCallback(() => closeDialog(resource, () => setOpen(false)), [closeDialog, resource]);
34
+ const Child = Children.only(children);
35
+ const handleOpen = useCallback(() => setOpen(true), []);
36
+ const handleClose = useCallback(() => setOpen(false), []);
37
+ const handleSuccess = useCallback(
38
+ (data: any, params: UseUpdateMutateParams<any>, context: unknown) => {
39
+ handleClose();
40
+ props?.mutationOptions?.onSuccess?.(data, params, context);
41
+ },
42
+ [handleClose, props.mutationOptions]
43
+ );
44
+ const record = useRecordContext();
96
45
  return (
97
46
  <>
98
- <Button label={label} onClick={handleOpen} style={style}>
99
- <Edit />
100
- </Button>
101
- <Dialog open={open} scroll="body" onClose={handleClose} fullWidth={fullWidth} maxWidth={maxWidth}>
102
- <EditInDialogContent {...props} onClose={handleClose}>
103
- {children}
104
- </EditInDialogContent>
105
- </Dialog>
47
+ <ResponsiveButton {...rest} onClick={handleOpen} />
48
+ <EditBase
49
+ {...props}
50
+ id={record.id}
51
+ mutationMode="pessimistic"
52
+ mutationOptions={{
53
+ ...mutationOptions,
54
+ onSuccess: handleSuccess
55
+ }}
56
+ >
57
+ <Dialog
58
+ open={open}
59
+ onClose={handleClose}
60
+ fullWidth={fullWidth}
61
+ maxWidth={maxWidth}
62
+ fullScreen={fullScreen}
63
+ keepMounted={false}
64
+ >
65
+ {React.isValidElement(Child)
66
+ ? React.cloneElement(Child, {
67
+ ...Child.props,
68
+ modal: true,
69
+ toolbar: (
70
+ <Toolbar>
71
+ <Button variant="text" size="medium" label="ra.action.cancel" onClick={handleClose} />
72
+ <SaveButton type="button" />
73
+ </Toolbar>
74
+ )
75
+ })
76
+ : null}
77
+ </Dialog>
78
+ </EditBase>
106
79
  </>
107
80
  );
108
81
  }
109
82
 
110
- type EditInDialogButtonProps = EditControllerProps &
111
- PropsWithChildren<{
112
- fullWidth?: boolean;
113
- maxWidth?: false | Breakpoint | undefined;
114
- label?: string;
115
- style?: React.CSSProperties;
116
- }>;
117
-
118
83
  export { EditInDialogButton };
119
84
  export type { EditInDialogButtonProps };
@@ -47,38 +47,40 @@ function LocalizedTextInput(props: ILocalizedTextInputProps) {
47
47
 
48
48
  return (
49
49
  <LabeledInput {...props} helperText={error?.message}>
50
- <Box flex={1}>
51
- {_.map(locales, (l, index) => {
52
- return (
53
- <TextInput
54
- key={index}
55
- {...props}
56
- source={`${source}.${l.locale}`}
57
- sx={{ width: '100%', display: l.locale === locale ? 'inline-flex' : 'none' }}
58
- label={false}
59
- InputProps={{
60
- endAdornment: (
61
- <Chip
62
- label={<Typography variant="button">{locale}</Typography>}
63
- size="small"
64
- onClick={toggle}
65
- color={isMissingLocalizations ? 'warning' : undefined}
66
- />
67
- )
68
- }}
69
- />
70
- );
71
- })}
72
- <Menu
73
- anchorEl={anchorEl}
74
- open={open}
75
- anchorOrigin={ANCHOR_ORIGIN}
76
- transformOrigin={TRANSFORM_ORIGIN}
77
- onClose={handleClose}
78
- >
79
- {MenuItems}
80
- </Menu>
81
- </Box>
50
+ <>
51
+ <Box flex={1}>
52
+ {_.map(locales, (l, index) => {
53
+ return (
54
+ <TextInput
55
+ key={index}
56
+ {...props}
57
+ source={`${source}.${l.locale}`}
58
+ sx={{ width: '100%', display: l.locale === locale ? 'inline-flex' : 'none' }}
59
+ label={false}
60
+ InputProps={{
61
+ endAdornment: (
62
+ <Chip
63
+ label={<Typography variant="button">{locale}</Typography>}
64
+ size="small"
65
+ onClick={toggle}
66
+ color={isMissingLocalizations ? 'warning' : undefined}
67
+ />
68
+ )
69
+ }}
70
+ />
71
+ );
72
+ })}
73
+ <Menu
74
+ anchorEl={anchorEl}
75
+ open={open}
76
+ anchorOrigin={ANCHOR_ORIGIN}
77
+ transformOrigin={TRANSFORM_ORIGIN}
78
+ onClose={handleClose}
79
+ >
80
+ {MenuItems}
81
+ </Menu>
82
+ </Box>
83
+ </>
82
84
  </LabeledInput>
83
85
  );
84
86
  }
@@ -45,6 +45,7 @@ function App() {
45
45
  <Resource name="entities/user" {...entities.user} />
46
46
  <Resource name="entities/i18n-message" {...entities.i18nMessage} />
47
47
  <Resource name="entities/device" {...entities.device} />
48
+ <Resource name="entities/category" {...entities.category} />
48
49
  <CustomRoutes noLayout>
49
50
  <Route path="/register" element={<RegisterPage name={APP_NAME} copy="Test" version={build.version} />} />
50
51
  <Route path="/recover" element={<RecoverPage name={APP_NAME} copy="Test" version={build.version} />} />
@@ -0,0 +1,12 @@
1
+ import { BooleanInput, SimpleForm, TextInput } from '@/';
2
+
3
+ function CategoryForm(props) {
4
+ return (
5
+ <SimpleForm {...props}>
6
+ <TextInput source="description" fullWidth />
7
+ <BooleanInput source="active" />
8
+ </SimpleForm>
9
+ );
10
+ }
11
+
12
+ export { CategoryForm };
@@ -1,3 +1,4 @@
1
+ export * from './CategoryForm';
1
2
  export * from './DeviceForm';
2
3
  export * from './I18nMessageForm';
3
4
  export * from './TestModelForm';