muffin-admin 1.29.0 → 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/.babelrc ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "presets": [
3
+ "@babel/preset-react"
4
+ ]
5
+ }
package/.eslintrc.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "root": true,
3
+ "extends": [
4
+ "eslint:recommended",
5
+ "plugin:react/recommended",
6
+ "prettier"
7
+ ],
8
+ "parser": "@babel/eslint-parser",
9
+ "settings": {
10
+ "react": {
11
+ "version": "detect"
12
+ }
13
+ },
14
+ "parserOptions": {
15
+ "ecmaFeatures": {
16
+ "jsx": true,
17
+ "experimentalObjectRestSpread": true
18
+ },
19
+ "ecmaVersion": 2020,
20
+ "sourceType": "module",
21
+ "babelOptions": {
22
+ "presets": [
23
+ "@babel/preset-react"
24
+ ],
25
+ "rootMode": "upward"
26
+ }
27
+ },
28
+ "plugins": [
29
+ "prettier",
30
+ "react"
31
+ ],
32
+ "env": {
33
+ "browser": true,
34
+ "es6": true,
35
+ "jest": true,
36
+ "node": true
37
+ },
38
+ "rules": {
39
+ "no-console": "warn",
40
+ "no-debugger": "warn",
41
+ "prettier/prettier": "warn",
42
+ "react/prop-types": "off"
43
+ }
44
+ }
package/admin.jsx CHANGED
@@ -1,56 +1,60 @@
1
- import React from "react"
1
+ import React from 'react'
2
2
 
3
- import { Admin, AppBar, Layout, Login } from "react-admin"
4
- import { IconButton, SvgIcon, Tooltip, Typography } from "@material-ui/core"
5
- import * as icons from "@material-ui/icons"
3
+ import { Admin, AppBar, Layout, Login } from 'react-admin'
4
+ import { IconButton, SvgIcon, Tooltip, Typography } from '@mui/material'
5
+ import * as icons from '@mui/icons-material'
6
6
 
7
- import authProvider from "./authprovider"
8
- import dataProvider from "./dataprovider"
7
+ import authProvider from './authprovider'
8
+ import dataProvider from './dataprovider'
9
9
  import { processAdmin, setupAdmin } from './utils'
10
10
 
11
- import './dashboard.jsx';
12
- import './resources.jsx';
13
-
11
+ import './dashboard.jsx'
12
+ import './resources.jsx'
14
13
 
15
14
  // Initialize the admin
16
- setupAdmin('admin', props => {
17
- const { apiUrl, auth, adminProps, appBarLinks, dashboard, resources } = props;
18
-
19
- let appBar = props => (
20
- <AppBar {...props}>
21
- <Typography
22
- variant="h6"
23
- color="inherit"
24
- id="react-admin-title"
25
- style={{
26
- flex: 1,
27
- textOverflow: 'ellipsis',
28
- whiteSpace: 'nowrap',
29
- overflow: 'hidden',
30
- }}
31
- />
32
- { appBarLinks.map( info => (
33
- <Tooltip key={ info.url } title={ info.title }>
34
- <IconButton color="inherit" href={ info.url }>
35
- <SvgIcon component={ icons[info.icon] }/>
36
- </IconButton>
37
- </Tooltip>
38
- ))}
39
- </AppBar>
40
- )
41
-
42
- return <Admin authProvider={ processAdmin('auth', auth) }
43
- dataProvider={ processAdmin('data', apiUrl) }
44
- dashboard={ processAdmin('dashboard', dashboard) }
45
- loginPage={ processAdmin('login', auth) }
46
- layout={ props => <Layout appBar={appBar} {...props} /> }
47
- children={ processAdmin('resources', resources) }
48
- { ...adminProps } />
49
- });
15
+ setupAdmin('admin', (props) => {
16
+ const { apiUrl, auth, adminProps, appBarLinks, dashboard, resources } = props
17
+
18
+ let appBar = (props) => (
19
+ <AppBar {...props}>
20
+ <Typography
21
+ variant='h6'
22
+ color='inherit'
23
+ id='react-admin-title'
24
+ style={{
25
+ flex: 1,
26
+ textOverflow: 'ellipsis',
27
+ whiteSpace: 'nowrap',
28
+ overflow: 'hidden',
29
+ }}
30
+ />
31
+ {appBarLinks.map((info) => (
32
+ <Tooltip key={info.url} title={info.title}>
33
+ <IconButton color='inherit' href={info.url}>
34
+ <SvgIcon component={icons[info.icon]} />
35
+ </IconButton>
36
+ </Tooltip>
37
+ ))}
38
+ </AppBar>
39
+ )
40
+
41
+ return (
42
+ <Admin
43
+ authProvider={processAdmin('auth', auth)}
44
+ dataProvider={processAdmin('data', apiUrl)}
45
+ dashboard={processAdmin('dashboard', dashboard)}
46
+ loginPage={processAdmin('login', auth)}
47
+ layout={(props) => <Layout appBar={appBar} {...props} />}
48
+ {...adminProps}
49
+ >
50
+ {processAdmin('resources', resources)}
51
+ </Admin>
52
+ )
53
+ })
50
54
 
51
55
  // Initialize authentication and data providers
52
- setupAdmin('auth', authProvider);
53
- setupAdmin('data', dataProvider);
56
+ setupAdmin('auth', authProvider)
57
+ setupAdmin('data', dataProvider)
54
58
 
55
59
  // Initialize login page
56
- setupAdmin('login', (params, res) => (props) => <Login {...props} />);
60
+ setupAdmin('login', () => Login)
package/authprovider.js CHANGED
@@ -1,79 +1,84 @@
1
1
  import Cookies from 'js-cookie'
2
- import { makeRequest, requestHeaders } from './utils';
3
-
2
+ import { makeRequest, processAdmin, requestHeaders, setupAdmin } from './utils'
3
+
4
+ setupAdmin(
5
+ 'auth-get',
6
+ ({ storage }) =>
7
+ (name) =>
8
+ storage == 'localstorage' ? localStorage.getItem(name) : Cookies.get(name)
9
+ )
10
+
11
+ setupAdmin('auth-set', ({ storage }) => (name, value) => {
12
+ if (storage == 'localstorage') {
13
+ localStorage.setItem(name, value)
14
+ requestHeaders['Authorization'] = value
15
+ } else Cookies.set(name, value)
16
+ })
4
17
 
5
18
  export default (props) => {
19
+ const { identityURL, authorizeURL, logoutURL, required, storage_name } = props
6
20
 
7
- const { identityURL, authorizeURL, logoutURL, required, storage, storage_name } = props;
8
-
9
- const authorize = (name, value) => {
10
- if (value === undefined) {
11
- return storage == 'localstorage' ? localStorage.getItem(name) : Cookies.get(name);
12
- }
13
-
14
- if (storage == 'localstorage') requestHeaders['Authorization'] = value;
15
- storage == 'localstorage' ? localStorage.setItem(name, value) : Cookies.set(name, value);
16
- return value;
17
- }
21
+ const authGet = processAdmin('auth-get', props)
22
+ const authSet = processAdmin('auth-set', props)
18
23
 
19
24
  // Initialize request headers
20
- authorize(storage_name, authorize(storage_name));
25
+ authSet(storage_name, authGet(storage_name))
21
26
 
22
27
  let getIdentity = async () => {
23
- if (identityURL) {
24
- let {json} = await makeRequest(identityURL);
25
- return json;
26
- }
28
+ if (identityURL) {
29
+ let { json } = await makeRequest(identityURL)
30
+ return json
31
+ }
27
32
  }
28
33
 
29
- if (required) return {
30
-
31
- getIdentity,
32
-
33
- checkAuth: async (data) => {
34
- const auth = authorize(storage_name);
35
- if (!auth) throw {message: 'Authorization required'}
36
-
37
- if (!identityURL) return auth;
38
-
39
- let user = await getIdentity();
40
- if (!user) throw {message: 'Authorization required'};
34
+ if (required)
35
+ return {
36
+ login: async (data) => {
37
+ if (!authorizeURL) throw { message: 'Authorization is not supported' }
41
38
 
42
- return user;
39
+ let { json } = await makeRequest(authorizeURL, { data, method: 'POST' })
40
+ authSet(storage_name, json)
43
41
  },
44
42
 
45
43
  checkError: async (error) => {
46
- const {message, status, body} = error;
44
+ const { status } = error
47
45
 
48
46
  if (status == 401 || status == 403) {
49
47
  // Clean storage
50
- authorize(storage_name, '');
48
+ authSet(storage_name, '')
51
49
 
52
- throw {message: 'Invalid authorization', redirectTo: logoutURL, logoutUser: !logoutURL};
50
+ throw {
51
+ message: 'Invalid authorization',
52
+ redirectTo: logoutURL,
53
+ logoutUser: !logoutURL,
54
+ }
53
55
  }
54
-
55
56
  },
56
57
 
57
- login: async (data) => {
58
- if (!authorizeURL) throw {message: 'Authorization is not supported'}
58
+ checkAuth: async (data) => {
59
+ const auth = authGet(storage_name)
60
+ if (!auth) throw { message: 'Authorization required' }
61
+
62
+ if (!identityURL) return auth
59
63
 
60
- let {json} = await makeRequest(authorizeURL, {data, method: 'POST'})
61
- authorize(storage_name, json);
64
+ let user = await getIdentity()
65
+ if (!user) throw { message: 'Authorization required' }
66
+
67
+ return user
62
68
  },
63
69
 
64
70
  logout: async () => {
71
+ // Clean storage
72
+ authSet(storage_name, '')
65
73
 
66
- // Clean storage
67
- authorize(storage_name, '');
68
-
69
- if (logoutURL) window.location = logoutURL;
74
+ if (logoutURL) window.location = logoutURL
70
75
  },
71
76
 
77
+ getIdentity,
78
+
72
79
  getPermissions: (data) => {
73
- const role = authorize(storage_name + '_role');
74
- return role ? Promise.resolve(role) : Promise.reject();
80
+ const role = authGet(storage_name + '_role')
81
+ return role ? Promise.resolve(role) : Promise.reject()
75
82
  },
76
-
77
- }
78
-
83
+ }
79
84
  }
@@ -1,68 +1,99 @@
1
- import React from 'react';
1
+ import React, { useEffect } from 'react'
2
2
 
3
- import { Button, CreateButton, useRefresh, useUnselectAll, useMutation, useNotify } from 'react-admin';
4
- import * as icons from "@material-ui/icons"
3
+ import useAction from '../hooks/useAction'
5
4
 
5
+ import {
6
+ Button,
7
+ useRefresh,
8
+ useUnselectAll,
9
+ useNotify,
10
+ useRecordContext,
11
+ } from 'react-admin'
12
+ import * as icons from '@mui/icons-material'
6
13
 
7
- export const BulkActionButton = ({label, icon, title, action, resource, selectedIds, ...props}) => {
8
- const refresh = useRefresh(), unselectAll = useUnselectAll(),
9
- notify = useNotify(), [mutate, { loading }] = useMutation(),
10
- Icon = icons[icon];
14
+ export const BulkActionButton = ({
15
+ label,
16
+ icon,
17
+ title,
18
+ action,
19
+ resource,
20
+ selectedIds,
21
+ }) => {
22
+ const refresh = useRefresh(),
23
+ unselectAll = useUnselectAll(resource),
24
+ notify = useNotify(),
25
+ Icon = icons[icon]
11
26
 
12
- let onClick = event => {
13
- mutate({
14
- resource,
15
- type: 'runAction',
16
- payload: {action, ids: selectedIds},
17
- }, {
18
- onSuccess: res => {
19
- if (res && res.data && res.data.message) notify(res.data.message, 'success');
20
- refresh();
21
- unselectAll(resource);
27
+ const mutation = useAction(resource, action)
28
+
29
+ let onClick = () => {
30
+ mutation.mutate(
31
+ {
32
+ action,
33
+ ids: selectedIds,
34
+ },
35
+ {
36
+ onSuccess: ({ data }) => {
37
+ if (data && data.message) notify(data.message, { type: 'success' })
38
+ refresh()
39
+ unselectAll()
40
+ },
41
+ onError: (err) => {
42
+ notify(typeof err === 'string' ? err : err.message, { type: 'error' })
22
43
  },
23
- onFailure: err => {
24
- notify(typeof err === 'string' ? err : err.message, 'error')
25
- }
26
- })
44
+ }
45
+ )
27
46
  }
28
47
 
29
48
  return (
30
- <Button label={ label } title={ title } onClick={ onClick } disabled={ loading }>
31
- { Icon && <Icon /> }
49
+ <Button
50
+ label={label}
51
+ title={title}
52
+ onClick={onClick}
53
+ disabled={mutation.isLoading}
54
+ >
55
+ {Icon && <Icon />}
32
56
  </Button>
33
57
  )
34
58
  }
35
59
 
36
- export const ActionButton = ({icon, label, title, record, resource, action}) => {
37
- const refresh = useRefresh(), notify = useNotify(),
38
- [mutate, { loading }] = useMutation(), Icon = icons[icon];
39
-
40
- let onClick = event => {
41
- mutate({
42
- resource,
43
- type: 'runAction',
44
- payload: {action, record},
45
- }, {
46
- onSuccess: res => {
47
- let data = res && res.data;
60
+ export const ActionButton = (props) => {
61
+ const { icon, label, title, resource, action } = props
62
+ const record = useRecordContext()
63
+ const mutation = useAction(resource, action)
48
64
 
49
- if (data && data.redirectTo) window.location = data.redirectTo;
50
- if (data && data.message) notify(data.message, 'success');
65
+ const refresh = useRefresh(),
66
+ notify = useNotify(),
67
+ Icon = icons[icon]
51
68
 
52
- refresh();
69
+ let onClick = () => {
70
+ mutation.mutate(
71
+ { record },
72
+ {
73
+ onSuccess: ({ data }) => {
74
+ refresh()
75
+ if (data && data.redirectTo) window.location = data.redirectTo
76
+ if (data && data.message) notify(data.message, { type: 'success' })
53
77
  },
54
- onFailure: err => {
55
- notify(typeof err === 'string' ? err : err.message, 'error')
56
- }
57
- })
78
+ onError: (error) => {
79
+ notify(typeof error === 'string' ? error : error.message, {
80
+ type: 'error',
81
+ })
82
+ },
83
+ }
84
+ )
58
85
  }
59
86
 
60
87
  return (
61
- <Button label={ label } title={ title } onClick={ onClick } disabled={ loading }>
62
- { Icon && <Icon /> }
88
+ <Button
89
+ label={label}
90
+ title={title}
91
+ onClick={onClick}
92
+ disabled={mutation.isLoading}
93
+ >
94
+ {Icon && <Icon />}
63
95
  </Button>
64
96
  )
65
-
66
97
  }
67
98
 
68
99
  export default ActionButton
@@ -1,19 +1,19 @@
1
- import React from 'react';
1
+ import React from 'react'
2
2
 
3
- import { Button } from 'react-admin';
4
- import { Link } from 'react-router-dom';
5
- import * as icons from "@material-ui/icons"
3
+ import { Button } from 'react-admin'
4
+ import { Link } from 'react-router-dom'
5
+ import * as icons from '@mui/icons-material'
6
6
 
7
- // import LinkButton from '../buttons/LinkButton'
8
- // <LinkButton label="Messages" icon="Message" resource="message" filters={{ user: data.id }} />
9
- //
10
- const LinkButton = ({label, icon, title, resource, filters, ...props}) => {
11
- let Icon = icons[icon] || icon, linkParams = {pathname: `/${resource}`};
12
- if (filters) linkParams.search = `filter=${JSON.stringify(filters)}`;
7
+ const LinkButton = ({ label, icon, title, resource, filters }) => {
8
+ const Icon = icons[icon] || icon
9
+ let linkParams = {
10
+ pathname: `/${resource}`,
11
+ search: filters ? `filter=${JSON.stringify(filters)}` : undefined,
12
+ }
13
13
 
14
14
  return (
15
- <Button label={ label } title={ title } component={ Link } to={ linkParams }>
16
- { Icon && <Icon /> }
15
+ <Button label={label} title={title} component={Link} to={linkParams}>
16
+ {Icon && <Icon />}
17
17
  </Button>
18
18
  )
19
19
  }
package/dashboard.jsx CHANGED
@@ -1,45 +1,61 @@
1
- import React from "react";
1
+ import React from 'react'
2
2
 
3
- import { Grid, Card, CardContent, Typography, Table, TableBody, TableRow, TableCell } from "@material-ui/core";
4
- import ReactJSON from "react-json-view";
3
+ import {
4
+ Grid,
5
+ Card,
6
+ CardContent,
7
+ Typography,
8
+ Table,
9
+ TableBody,
10
+ TableRow,
11
+ TableCell,
12
+ } from '@mui/material'
13
+ // import ReactJSON from "react-json-view";
5
14
 
6
- import { setupAdmin } from './utils';
15
+ import { setupAdmin } from './utils'
7
16
 
8
-
9
- const AdminTableView = ({src}) => (
17
+ const AdminTableView = ({ src }) => (
10
18
  <Table>
11
- <TableBody>{
12
- src.map((row, idx) =>
13
- <TableRow key={ idx } hover>{
14
- row.map((cell, idx) =>
15
- <TableCell key={ idx }>{ cell }</TableCell>
16
- )
17
- }</TableRow>
18
- )
19
- }</TableBody>
19
+ <TableBody>
20
+ {src.map((row, idx) => (
21
+ <TableRow key={idx} hover>
22
+ {row.map((cell, idx) => (
23
+ <TableCell key={idx}>{cell}</TableCell>
24
+ ))}
25
+ </TableRow>
26
+ ))}
27
+ </TableBody>
20
28
  </Table>
21
-
22
29
  )
23
30
 
24
31
  const AdminCard = ({ src }) => {
25
32
  if (Array.isArray(src)) {
26
33
  return (
27
- <Grid container item spacing={ 2 }>
28
- { src.map((card, idx) => <AdminCard key={ idx } src={ card } />) }
34
+ <Grid container item spacing={2}>
35
+ {src.map((card, idx) => (
36
+ <AdminCard key={idx} src={card} />
37
+ ))}
29
38
  </Grid>
30
39
  )
31
40
  }
32
41
 
33
- let {title, value} = src;
42
+ let { title, value } = src
34
43
 
35
44
  return (
36
45
  <Grid item xs>
37
46
  <Card>
38
47
  <CardContent>
39
- <Typography variant="h5" component="h2" m={ 2 } style={{ textAlign: 'center' }}>{title}</Typography>
40
- {
41
- Array.isArray(value) && <AdminTableView src={ value } /> || <ReactJSON src={ value } />
42
- }
48
+ <Typography
49
+ variant='h5'
50
+ component='h2'
51
+ m={2}
52
+ style={{ textAlign: 'center' }}
53
+ >
54
+ {title}
55
+ </Typography>
56
+ {(Array.isArray(value) && <AdminTableView src={value} />) || (
57
+ <pre>{JSON.stringify(value, null, 2)}</pre>
58
+ )}
43
59
  </CardContent>
44
60
  </Card>
45
61
  </Grid>
@@ -47,11 +63,12 @@ const AdminCard = ({ src }) => {
47
63
  }
48
64
 
49
65
  // Process dashboard
50
- setupAdmin('dashboard', props => {
51
- if (!props) return;
66
+ setupAdmin('dashboard', (props) => {
67
+ if (!props) return
52
68
 
53
- return () =>
54
- <Grid container spacing={ 1 }>
55
- <AdminCard src={ props } />
56
- </Grid>
69
+ return () => (
70
+ <Grid container spacing={1}>
71
+ <AdminCard src={props} />
72
+ </Grid>
73
+ )
57
74
  })