muffin-admin 1.28.1 → 1.30.0

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/dataprovider.js CHANGED
@@ -1,81 +1,94 @@
1
- import { makeRequest } from './utils';
2
-
1
+ import { makeRequest } from './utils'
3
2
 
4
3
  export default (apiUrl) => {
5
-
6
4
  const methods = {
7
-
8
- getList: async (resource, {filter, pagination, sort}) => {
9
- const query = {};
10
- if (filter) query.where = JSON.stringify(filter);
5
+ getList: async (resource, { filter, pagination, sort }) => {
6
+ const query = {}
7
+ if (filter) query.where = JSON.stringify(filter)
11
8
  if (pagination) {
12
- const {page, perPage: limit} = pagination;
13
- query.limit = limit;
14
- query.offset = limit * (page - 1);
9
+ const { page, perPage: limit } = pagination
10
+ query.limit = limit
11
+ query.offset = limit * (page - 1)
15
12
  }
16
13
  if (sort) {
17
- const {field, order} = sort;
18
- query.sort = order == 'ASC' ? field : `-${field}`;
19
- };
14
+ const { field, order } = sort
15
+ query.sort = order == 'ASC' ? field : `-${field}`
16
+ }
20
17
 
21
- const {headers, json} = await makeRequest(`${apiUrl}/${resource}`, {query});
18
+ const { headers, json } = await makeRequest(`${apiUrl}/${resource}`, {
19
+ query,
20
+ })
22
21
  return {
23
22
  data: json,
24
23
  total: parseInt(headers.get('x-total'), 10),
25
- };
26
-
24
+ }
27
25
  },
28
26
 
29
- getOne: async (resource, {id}) => {
30
- const {json} = await makeRequest(`${apiUrl}/${resource}/${id}`);
31
- return {data: json};
27
+ getOne: async (resource, { id }) => {
28
+ const { json } = await makeRequest(`${apiUrl}/${resource}/${id}`)
29
+ return { data: json }
32
30
  },
33
31
 
34
- create: async (resource, {data}) => {
35
- const {json} = await makeRequest(`${apiUrl}/${resource}`, {data, method: 'POST'});
36
- return {data: json};
32
+ create: async (resource, { data }) => {
33
+ const { json } = await makeRequest(`${apiUrl}/${resource}`, {
34
+ data,
35
+ method: 'POST',
36
+ })
37
+ return { data: json }
37
38
  },
38
39
 
39
- update: async (resource, {id, data}) => {
40
- const {json} = await makeRequest(`${apiUrl}/${resource}/${id}`, {data, method: 'PUT'});
41
- return {data: json};
40
+ update: async (resource, { id, data }) => {
41
+ const { json } = await makeRequest(`${apiUrl}/${resource}/${id}`, {
42
+ data,
43
+ method: 'PUT',
44
+ })
45
+ return { data: json }
42
46
  },
43
47
 
44
- updateMany: async (resource, {ids, data}) => {
45
- await Promise.all(ids.map(id => methods.update(resource, {id, data})));
46
- return {data: ids};
48
+ updateMany: async (resource, { ids, data }) => {
49
+ await Promise.all(ids.map((id) => methods.update(resource, { id, data })))
50
+ return { data: ids }
47
51
  },
48
52
 
49
- delete: async (resource, {id}) => {
50
- const {json} = await makeRequest(`${apiUrl}/${resource}/${id}`, {method: 'DELETE'});
51
- return {data: {id}};
53
+ delete: async (resource, { id }) => {
54
+ await makeRequest(`${apiUrl}/${resource}/${id}`, {
55
+ method: 'DELETE',
56
+ })
57
+ return { data: { id } }
52
58
  },
53
59
 
54
- deleteMany: async (resource, {ids}) => {
55
- const {json} = await makeRequest(`${apiUrl}/${resource}`, {data: ids, method: 'DELETE'});
56
- return {data: ids};
60
+ deleteMany: async (resource, { ids }) => {
61
+ await makeRequest(`${apiUrl}/${resource}`, {
62
+ data: ids,
63
+ method: 'DELETE',
64
+ })
65
+ return { data: ids }
57
66
  },
58
67
 
59
- getMany: (resource, {ids}) => {
60
- return methods.getList(resource, {filter: {id: {"$in": ids}}});
68
+ getMany: (resource, { ids }) => {
69
+ return methods.getList(resource, { filter: { id: { $in: ids } } })
61
70
  },
62
71
 
63
- getManyReference: async (resource, {target, id, filter, ...opts}) => {
64
- filter = filter || {};
65
- filter[target] = id;
66
- return await methods.getList(resource, {filter, ...opts});
72
+ getManyReference: async (resource, { target, id, filter, ...opts }) => {
73
+ filter = filter || {}
74
+ filter[target] = id
75
+ return await methods.getList(resource, { filter, ...opts })
67
76
  },
68
77
 
69
- runAction: async (resource, props) => {
70
- let {action, payload, ids, record} = props;
78
+ runAction: async (resource, action, props) => {
79
+ const { payload, ids, record } = props
71
80
  action = action.replace(/^\/+/, '')
72
- if (record) { action = action.replace(/\{([^}]+)\}/, (_, field) => record[field]) }
73
- const {json} = await makeRequest(`${apiUrl}/${action}`, {query: {ids}, method: 'POST', data: payload});
74
- return {data: json};
81
+ if (record) {
82
+ action = action.replace(/\{([^}]+)\}/, (_, field) => record[field])
83
+ }
84
+ const { json } = await makeRequest(`${apiUrl}/${action}`, {
85
+ query: { ids },
86
+ method: 'POST',
87
+ data: payload,
88
+ })
89
+ return { data: json }
75
90
  },
91
+ }
76
92
 
77
- };
78
-
79
- return methods;
80
-
93
+ return methods
81
94
  }
@@ -1,25 +1,34 @@
1
- import React from 'react';
2
-
3
- import get from 'lodash/get';
4
- import { useRecordContext } from 'ra-core';
5
- import Avatar from '@material-ui/core/Avatar';
6
-
7
-
8
- const AvatarField = ({ source, record, alt, style, nameProp, ...props }) => {
9
-
10
- let sourceValue = get(record, source),
11
- letters = null,
12
- name = record[nameProp];
13
-
14
- if (name) letters = name.trim(' ').split(/\s+/).slice(0, 2).map( n => n[0].toUpperCase() ).join('');
15
-
16
- return <Avatar src={ record[source] } alt={ alt } style={ style }>{ letters }</Avatar>
17
-
1
+ import React from 'react'
2
+
3
+ import get from 'lodash/get'
4
+ import { useRecordContext } from 'ra-core'
5
+ import Avatar from '@mui/material/Avatar'
6
+
7
+ const AvatarField = ({ source, alt, style, nameProp, ...props }) => {
8
+ const record = useRecordContext(props)
9
+
10
+ let value = get(record, source),
11
+ name = record[nameProp]
12
+
13
+ const letters = name
14
+ ? name
15
+ .trim(' ')
16
+ .split(/\s+/)
17
+ .slice(0, 2)
18
+ .map((n) => n[0].toUpperCase())
19
+ .join('')
20
+ : ''
21
+
22
+ return (
23
+ <Avatar src={value} alt={alt} style={style}>
24
+ {letters}
25
+ </Avatar>
26
+ )
18
27
  }
19
28
 
20
- AvatarField.displayName = 'AvatarField';
29
+ AvatarField.displayName = 'AvatarField'
21
30
  AvatarField.defaultProps = {
22
31
  addLabel: true,
23
32
  }
24
33
 
25
- export default AvatarField;
34
+ export default AvatarField
@@ -1,24 +1,28 @@
1
- import React from "react";
1
+ import React from 'react'
2
2
 
3
3
  import { ReferenceField, TextField } from 'react-admin'
4
4
 
5
- const FKField = ({refSource, refID, link, ...props}) => {
5
+ const FKField = ({ refSource, refID, link, reference, source, ...props }) => {
6
6
  return (
7
- <ReferenceField link={ link || 'show' } { ...props }>
7
+ <ReferenceField
8
+ link={link || 'show'}
9
+ reference={reference}
10
+ source={source}
11
+ {...props}
12
+ >
8
13
  <>
9
- <TextField source={ refSource } />
10
- { " (#" }
11
- <TextField source={ refID || 'id' } />
12
- { ")" }
14
+ <TextField source={refSource} />
15
+ {' (#'}
16
+ <TextField source={refID || 'id'} />
17
+ {')'}
13
18
  </>
14
19
  </ReferenceField>
15
20
  )
16
21
  }
17
22
 
18
- FKField.displayName = 'FKField';
23
+ FKField.displayName = 'FKField'
19
24
  FKField.defaultProps = {
20
- addLabel: true,
21
- };
22
-
23
- export default FKField;
25
+ addLabel: true,
26
+ }
24
27
 
28
+ export default FKField
@@ -0,0 +1,25 @@
1
+ import get from 'lodash/get'
2
+ import React, { useState } from 'react'
3
+ import { Button, FunctionField, useRecordContext } from 'react-admin'
4
+
5
+ export default function JsonField({ label, source }) {
6
+ const [expand, setExpand] = useState(false)
7
+ const record = useRecordContext()
8
+ const src = JSON.stringify(get(record, source), null, 2)
9
+ const retval = (
10
+ <div>
11
+ <p></p>
12
+ {label}
13
+ <Button
14
+ variant='contained'
15
+ color='primary'
16
+ size='small'
17
+ label={expand ? 'colapse' : 'expand'}
18
+ onClick={() => setExpand(!expand)}
19
+ />
20
+ <p>{expand && <pre>{src}</pre>}</p>
21
+ </div>
22
+ )
23
+
24
+ return <FunctionField render={() => retval} />
25
+ }
@@ -0,0 +1,9 @@
1
+ import { useDataProvider } from 'react-admin'
2
+ import { useMutation } from 'react-query'
3
+
4
+ export default function useAction(resource, action) {
5
+ const dataProvider = useDataProvider()
6
+ return useMutation((params) =>
7
+ dataProvider.runAction(resource, action, params)
8
+ )
9
+ }
package/index.js CHANGED
@@ -1,11 +1,14 @@
1
- import ReactDOM from 'react-dom';
1
+ import { createRoot } from 'react-dom/client'
2
2
 
3
3
  import { setupAdmin, processAdmin } from './utils.js'
4
4
 
5
- import './admin.jsx';
5
+ import './admin.jsx'
6
6
 
7
-
8
- let initAdmin = globalThis.initAdmin = (props) => ReactDOM.render(processAdmin('admin', props), document.getElementById('root'))
7
+ const initAdmin = (globalThis.initAdmin = (props) => {
8
+ const container = document.getElementById('root')
9
+ const root = createRoot(container)
10
+ root.render(processAdmin('admin', props))
11
+ })
9
12
 
10
13
  export { setupAdmin, processAdmin }
11
14
 
@@ -1,30 +1,42 @@
1
- import React from "react";
1
+ import React from 'react'
2
2
 
3
3
  import { ReferenceInput, AutocompleteInput } from 'react-admin'
4
4
 
5
-
6
- const FKInput = ({refProp, refSource, ...props}) => {
7
-
8
- let filterToQuery = search => {
9
- let filters = {};
10
- filters[refProp] = search;
11
- return filters;
5
+ const FKInput = ({
6
+ refProp,
7
+ refSource,
8
+ fullWidth,
9
+ reference,
10
+ source,
11
+ ...props
12
+ }) => {
13
+ let filterToQuery = (search) => {
14
+ let filters = {}
15
+ filters[refProp] = search
16
+ return filters
12
17
  }
13
18
 
14
- refSource = refSource || 'id';
19
+ refSource = refSource || 'id'
15
20
 
16
- let renderText = record => {
17
- let pk = record[refSource];
18
- return pk && `${record[refProp]} (#${pk})` || "";
21
+ let renderText = (record) => {
22
+ let pk = record[refSource]
23
+ return (pk && `${record[refProp]} (#${pk})`) || ''
19
24
  }
20
25
 
21
26
  return (
22
- <ReferenceInput { ...props } filterToQuery={ filterToQuery }>
23
- <AutocompleteInput source={ refSource } optionText={ renderText } />
27
+ <ReferenceInput
28
+ reference={reference}
29
+ source={source}
30
+ filterToQuery={filterToQuery}
31
+ {...props}
32
+ >
33
+ <AutocompleteInput
34
+ source={refSource}
35
+ optionText={renderText}
36
+ fullWidth={fullWidth}
37
+ />
24
38
  </ReferenceInput>
25
39
  )
26
-
27
-
28
40
  }
29
41
 
30
- export default FKInput;
42
+ export default FKInput
@@ -0,0 +1,62 @@
1
+ import React from 'react'
2
+ import { TextInput } from 'react-admin'
3
+ import isJSON from 'validator/lib/isJSON'
4
+
5
+ const DEFAULT_ERRORTEXT = 'Invalid JSON'
6
+
7
+ const parseFunction = (json) => {
8
+ let retval = json
9
+ try {
10
+ retval = JSON.parse(json)
11
+ } finally {
12
+ return retval
13
+ }
14
+ }
15
+
16
+ /**
17
+ *
18
+ * `JsonInput` validates if the entered value is JSON or not. If entered value is not a invalid JSON, `JsonInput` will throw an error.
19
+ * Default error message is: `Invalid JSON` and can be overridden using `errortext` prop.
20
+ *
21
+ * @example
22
+ * <JsonInput source='config' label='JSON Config' errortext='Enter a valid JSON'/>
23
+ *
24
+ * or use translate function:
25
+ *
26
+ * @example
27
+ * <JsonInput source='config' label={translate('resources.resource_name.fields.config')} errortext={translate('myroot.validate.json')}/>
28
+ *
29
+ * By default, `JsonInput` parses and returns the entered string as object. Instead, to send string directly, please pass `parse` prop as `false`
30
+ *
31
+ * @example
32
+ * <JsonInput source='config' label='JSON Config' parse={false}/>
33
+ */
34
+ export default function JsonInput(props) {
35
+ const {
36
+ validate = [],
37
+ errortext = DEFAULT_ERRORTEXT,
38
+ multiline = true,
39
+ parse = true,
40
+ ...rest
41
+ } = props
42
+ // const errorobj = { message: errortext }
43
+
44
+ const validateJSON = (value) => {
45
+ if (!value || typeof value === 'object') return undefined
46
+ return isJSON(value) ? undefined : errortext
47
+ }
48
+ validate.push(validateJSON)
49
+ rest.validate = validate
50
+
51
+ const formatJSON = (json) => {
52
+ let retval = json
53
+ if (retval && typeof retval === 'object')
54
+ retval = JSON.stringify(retval, null, 2)
55
+ return retval
56
+ }
57
+ rest.format = formatJSON
58
+ //
59
+ if (parse) rest.parse = parseFunction
60
+ if (multiline) rest.multiline = true
61
+ return <TextInput {...rest} />
62
+ }
package/jsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "compilerOptions": {
3
+ "noImplicitAny": false,
4
+ "target": "ESNext",
5
+ "moduleResolution": "node",
6
+ "checkJs": true,
7
+ "jsx": "react-jsx"
8
+ }
9
+ }
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "muffin-admin",
3
- "version": "1.28.1",
3
+ "version": "1.30.0",
4
4
  "description": "NPM package for https://github.com/klen/muffin-admin",
5
5
  "main": "index.js",
6
+ "module": "index.js",
6
7
  "scripts": {
7
8
  "test": "echo \"Error: no test specified\" && exit 1"
8
9
  },
@@ -17,20 +18,22 @@
17
18
  },
18
19
  "homepage": "https://github.com/klen/muffin-admin#readme",
19
20
  "dependencies": {
20
- "@material-ui/core": "^4.12.3",
21
- "@material-ui/icons": "^4.11.2",
21
+ "@mui/icons-material": "^5.6.2",
22
+ "@mui/material": "^5.6.3",
22
23
  "js-cookie": "^3.0.1",
23
- "react": "17.0.2",
24
- "react-admin": "3.19.0",
25
- "react-admin-json-view": "^1.2.0",
26
- "react-dom": "17.0.2"
24
+ "react": "^18.1.0",
25
+ "react-admin": "^4.0.1",
26
+ "react-dom": "^18.1.0",
27
+ "validator": "^13.7.0"
27
28
  },
28
29
  "devDependencies": {
29
- "@babel/core": "^7.13.10",
30
- "@babel/plugin-transform-runtime": "^7.13.10",
31
- "@babel/preset-env": "^7.13.10",
32
- "@babel/preset-react": "^7.12.13",
33
- "babel-loader": "^8.2.2",
30
+ "@babel/core": "^7.17.8",
31
+ "@babel/eslint-parser": "^7.17.0",
32
+ "@babel/plugin-transform-runtime": "^7.17.0",
33
+ "@babel/preset-env": "^7.16.11",
34
+ "@babel/preset-react": "^7.16.7",
35
+ "babel-loader": "^8.2.4",
36
+ "eslint-plugin-react": "^7.29.4",
34
37
  "webpack": "^5.24.4",
35
38
  "webpack-cli": "^4.9.1",
36
39
  "webpack-dev-server": "^4.3.1"
package/resources.jsx CHANGED
@@ -1,27 +1,37 @@
1
- import React from "react";
1
+ import React from 'react'
2
2
 
3
- import { Resource } from "react-admin";
4
- import * as icons from "@material-ui/icons"
3
+ import { Resource } from 'react-admin'
4
+ import * as icons from '@mui/icons-material'
5
5
 
6
- import { checkParams, processAdmin, initRAItems, setupAdmin } from './utils'
6
+ import { checkParams, processAdmin, setupAdmin } from './utils'
7
7
 
8
8
  import './views/list.jsx'
9
9
  import './views/show.jsx'
10
10
  import './views/edit.jsx'
11
11
  import './views/create.jsx'
12
12
 
13
-
14
13
  // Initialize Resources Components
15
- setupAdmin('resources', resources => resources.map(res => processAdmin('resource', res, res.name)));
14
+ setupAdmin('resources', (resources) =>
15
+ resources.map((res) => processAdmin('resource', res, res.name))
16
+ )
16
17
 
17
18
  // Initialize a resource's component
18
- setupAdmin('resource', checkParams((props, res) => {
19
- let { create, edit, icon, list, name, show, ...resProps } = props;
19
+ setupAdmin(
20
+ 'resource',
21
+ checkParams((props, res) => {
22
+ let { create, edit, icon, list, name, show, ...resProps } = props
20
23
 
21
- return <Resource key={ name } name={ name } icon={ icons[icon] }
22
- create={ processAdmin('create', create, res) }
23
- edit={ processAdmin('edit', edit, res) }
24
- list={ processAdmin('list', list, res) }
25
- show={ processAdmin('show', show, res) }
26
- { ...resProps } />;
27
- }));
24
+ return (
25
+ <Resource
26
+ key={name}
27
+ name={name}
28
+ icon={icons[icon]}
29
+ create={processAdmin('create', create, res)}
30
+ edit={processAdmin('edit', edit, res)}
31
+ list={processAdmin('list', list, res)}
32
+ show={processAdmin('show', show, res)}
33
+ {...resProps}
34
+ />
35
+ )
36
+ })
37
+ )
package/ui.jsx CHANGED
@@ -1,51 +1,107 @@
1
- import React from "react";
1
+ import React from 'react'
2
2
 
3
- import { required } from 'react-admin';
4
- import { BooleanField, ChipField, DateField, EmailField, ImageField, FileField, NumberField, RichTextField, TextField, UrlField, SelectField, ArrayField, ReferenceField, ReferenceManyField, ReferenceArrayField } from "react-admin";
5
- import { BooleanInput, NullableBooleanInput, DateInput, DateTimeInput, ImageInput, FileInput, NumberInput, PasswordInput, TextInput, AutocompleteInput, RadioButtonGroupInput, SelectInput, ArrayInput, AutocompleteArrayInput, CheckboxGroupInput, ReferenceArrayInput, ReferenceInput } from "react-admin";
6
- import { JsonField, JsonInput } from "react-admin-json-view";
3
+ import { required } from 'react-admin'
4
+ import {
5
+ BooleanField,
6
+ ChipField,
7
+ DateField,
8
+ EmailField,
9
+ ImageField,
10
+ FileField,
11
+ NumberField,
12
+ RichTextField,
13
+ TextField,
14
+ UrlField,
15
+ SelectField,
16
+ ArrayField,
17
+ ReferenceField,
18
+ ReferenceManyField,
19
+ ReferenceArrayField,
20
+ } from 'react-admin'
21
+ import {
22
+ BooleanInput,
23
+ NullableBooleanInput,
24
+ DateInput,
25
+ DateTimeInput,
26
+ ImageInput,
27
+ FileInput,
28
+ NumberInput,
29
+ PasswordInput,
30
+ TextInput,
31
+ AutocompleteInput,
32
+ RadioButtonGroupInput,
33
+ SelectInput,
34
+ ArrayInput,
35
+ AutocompleteArrayInput,
36
+ CheckboxGroupInput,
37
+ ReferenceArrayInput,
38
+ ReferenceInput,
39
+ } from 'react-admin'
7
40
 
8
- import AvatarField from "./fields/AvatarField.jsx"
9
- import FKField from "./fields/FKField.jsx"
10
- import FKInput from "./inputs/FKInput.jsx"
11
-
12
-
13
- // Fix JsonField
14
- JsonField.displayName = 'JsonField';
15
- JsonField.defaultProps = {
16
- addLabel: true,
17
- };
41
+ import AvatarField from './fields/AvatarField.jsx'
42
+ import FKField from './fields/FKField.jsx'
43
+ import FKInput from './inputs/FKInput.jsx'
44
+ import JsonField from './fields/JsonField.jsx'
45
+ import JsonInput from './inputs/JsonInput.jsx'
18
46
 
19
47
  const ui = {
20
-
21
48
  // Fields
22
- BooleanField, ChipField, DateField, EmailField, ImageField, FileField,
23
- NumberField, RichTextField, TextField, UrlField, SelectField, ArrayField,
24
- ReferenceField, ReferenceManyField, ReferenceArrayField, AvatarField,
25
- JsonField, FKField,
49
+ BooleanField,
50
+ ChipField,
51
+ DateField,
52
+ EmailField,
53
+ ImageField,
54
+ FileField,
55
+ NumberField,
56
+ RichTextField,
57
+ TextField,
58
+ UrlField,
59
+ SelectField,
60
+ ArrayField,
61
+ ReferenceField,
62
+ ReferenceManyField,
63
+ ReferenceArrayField,
64
+ AvatarField,
65
+ JsonField,
66
+ FKField,
26
67
 
27
68
  // Inputs
28
- BooleanInput, NullableBooleanInput, DateInput, DateTimeInput, ImageInput,
29
- FileInput, NumberInput, PasswordInput, TextInput, AutocompleteInput,
30
- RadioButtonGroupInput, SelectInput, ArrayInput, AutocompleteArrayInput,
31
- CheckboxGroupInput, ReferenceArrayInput, ReferenceInput, JsonInput, FKInput,
32
-
69
+ BooleanInput,
70
+ NullableBooleanInput,
71
+ DateInput,
72
+ DateTimeInput,
73
+ ImageInput,
74
+ FileInput,
75
+ NumberInput,
76
+ PasswordInput,
77
+ TextInput,
78
+ AutocompleteInput,
79
+ RadioButtonGroupInput,
80
+ SelectInput,
81
+ ArrayInput,
82
+ AutocompleteArrayInput,
83
+ CheckboxGroupInput,
84
+ ReferenceArrayInput,
85
+ ReferenceInput,
86
+ JsonInput,
87
+ FKInput,
33
88
  }
34
89
 
35
- const initRAItems = itemsProps => itemsProps.map((item) => {
90
+ const initRAItems = (itemsProps) =>
91
+ itemsProps.map((item) => {
36
92
  const Item = ui[item[0]],
37
- props = {...item[1]};
93
+ props = { ...item[1] }
38
94
 
39
95
  if (props.required) {
40
- props.validate = required();
41
- delete props.required
96
+ props.validate = required()
97
+ delete props.required
42
98
  }
43
99
 
44
100
  props.fullWidth = props.fullWidth ?? true
45
101
 
46
- if (props.children) props.children = initRAItems(props.children)[0];
47
-
48
- return <Item key={ props.source } { ...props } />
49
- })
102
+ if (props.children) props.children = initRAItems(props.children)[0]
103
+
104
+ return <Item key={props.source} {...props} />
105
+ })
50
106
 
51
107
  export default initRAItems