muffin-admin 1.30.0 → 1.31.2

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 (45) hide show
  1. package/Makefile +7 -0
  2. package/dist/admin.js +82 -0
  3. package/dist/authprovider.js +250 -0
  4. package/dist/buttons/ActionButton.js +110 -0
  5. package/dist/buttons/LinkButton.js +44 -0
  6. package/dist/dashboard.js +70 -0
  7. package/dist/dataprovider.js +379 -0
  8. package/dist/fields/AvatarField.js +46 -0
  9. package/dist/fields/FKField.js +43 -0
  10. package/dist/fields/JsonField.js +51 -0
  11. package/dist/hooks/useAction.js +17 -0
  12. package/dist/index.js +20 -0
  13. package/dist/inputs/FKInput.js +53 -0
  14. package/dist/inputs/JsonInput.js +82 -0
  15. package/dist/resources.js +57 -0
  16. package/dist/ui.js +95 -0
  17. package/dist/utils.js +67 -0
  18. package/dist/views/create.js +25 -0
  19. package/dist/views/edit.js +53 -0
  20. package/dist/views/list.js +83 -0
  21. package/dist/views/show.js +36 -0
  22. package/dist/web.js +33 -0
  23. package/package.json +12 -3
  24. package/webpack.config.js +7 -6
  25. package/.babelrc +0 -5
  26. package/admin.jsx +0 -60
  27. package/authprovider.js +0 -84
  28. package/buttons/ActionButton.jsx +0 -99
  29. package/buttons/LinkButton.jsx +0 -21
  30. package/dashboard.jsx +0 -74
  31. package/dataprovider.js +0 -94
  32. package/fields/AvatarField.jsx +0 -34
  33. package/fields/FKField.jsx +0 -28
  34. package/fields/JsonField.jsx +0 -25
  35. package/hooks/useAction.js +0 -9
  36. package/index.js +0 -15
  37. package/inputs/FKInput.jsx +0 -42
  38. package/inputs/JsonInput.jsx +0 -62
  39. package/resources.jsx +0 -37
  40. package/ui.jsx +0 -107
  41. package/utils.js +0 -39
  42. package/views/create.jsx +0 -30
  43. package/views/edit.jsx +0 -42
  44. package/views/list.jsx +0 -86
  45. package/views/show.jsx +0 -43
package/authprovider.js DELETED
@@ -1,84 +0,0 @@
1
- import Cookies from 'js-cookie'
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
- })
17
-
18
- export default (props) => {
19
- const { identityURL, authorizeURL, logoutURL, required, storage_name } = props
20
-
21
- const authGet = processAdmin('auth-get', props)
22
- const authSet = processAdmin('auth-set', props)
23
-
24
- // Initialize request headers
25
- authSet(storage_name, authGet(storage_name))
26
-
27
- let getIdentity = async () => {
28
- if (identityURL) {
29
- let { json } = await makeRequest(identityURL)
30
- return json
31
- }
32
- }
33
-
34
- if (required)
35
- return {
36
- login: async (data) => {
37
- if (!authorizeURL) throw { message: 'Authorization is not supported' }
38
-
39
- let { json } = await makeRequest(authorizeURL, { data, method: 'POST' })
40
- authSet(storage_name, json)
41
- },
42
-
43
- checkError: async (error) => {
44
- const { status } = error
45
-
46
- if (status == 401 || status == 403) {
47
- // Clean storage
48
- authSet(storage_name, '')
49
-
50
- throw {
51
- message: 'Invalid authorization',
52
- redirectTo: logoutURL,
53
- logoutUser: !logoutURL,
54
- }
55
- }
56
- },
57
-
58
- checkAuth: async (data) => {
59
- const auth = authGet(storage_name)
60
- if (!auth) throw { message: 'Authorization required' }
61
-
62
- if (!identityURL) return auth
63
-
64
- let user = await getIdentity()
65
- if (!user) throw { message: 'Authorization required' }
66
-
67
- return user
68
- },
69
-
70
- logout: async () => {
71
- // Clean storage
72
- authSet(storage_name, '')
73
-
74
- if (logoutURL) window.location = logoutURL
75
- },
76
-
77
- getIdentity,
78
-
79
- getPermissions: (data) => {
80
- const role = authGet(storage_name + '_role')
81
- return role ? Promise.resolve(role) : Promise.reject()
82
- },
83
- }
84
- }
@@ -1,99 +0,0 @@
1
- import React, { useEffect } from 'react'
2
-
3
- import useAction from '../hooks/useAction'
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'
13
-
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]
26
-
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' })
43
- },
44
- }
45
- )
46
- }
47
-
48
- return (
49
- <Button
50
- label={label}
51
- title={title}
52
- onClick={onClick}
53
- disabled={mutation.isLoading}
54
- >
55
- {Icon && <Icon />}
56
- </Button>
57
- )
58
- }
59
-
60
- export const ActionButton = (props) => {
61
- const { icon, label, title, resource, action } = props
62
- const record = useRecordContext()
63
- const mutation = useAction(resource, action)
64
-
65
- const refresh = useRefresh(),
66
- notify = useNotify(),
67
- Icon = icons[icon]
68
-
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' })
77
- },
78
- onError: (error) => {
79
- notify(typeof error === 'string' ? error : error.message, {
80
- type: 'error',
81
- })
82
- },
83
- }
84
- )
85
- }
86
-
87
- return (
88
- <Button
89
- label={label}
90
- title={title}
91
- onClick={onClick}
92
- disabled={mutation.isLoading}
93
- >
94
- {Icon && <Icon />}
95
- </Button>
96
- )
97
- }
98
-
99
- export default ActionButton
@@ -1,21 +0,0 @@
1
- import React from 'react'
2
-
3
- import { Button } from 'react-admin'
4
- import { Link } from 'react-router-dom'
5
- import * as icons from '@mui/icons-material'
6
-
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
-
14
- return (
15
- <Button label={label} title={title} component={Link} to={linkParams}>
16
- {Icon && <Icon />}
17
- </Button>
18
- )
19
- }
20
-
21
- export default LinkButton
package/dashboard.jsx DELETED
@@ -1,74 +0,0 @@
1
- import React from 'react'
2
-
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";
14
-
15
- import { setupAdmin } from './utils'
16
-
17
- const AdminTableView = ({ src }) => (
18
- <Table>
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>
28
- </Table>
29
- )
30
-
31
- const AdminCard = ({ src }) => {
32
- if (Array.isArray(src)) {
33
- return (
34
- <Grid container item spacing={2}>
35
- {src.map((card, idx) => (
36
- <AdminCard key={idx} src={card} />
37
- ))}
38
- </Grid>
39
- )
40
- }
41
-
42
- let { title, value } = src
43
-
44
- return (
45
- <Grid item xs>
46
- <Card>
47
- <CardContent>
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
- )}
59
- </CardContent>
60
- </Card>
61
- </Grid>
62
- )
63
- }
64
-
65
- // Process dashboard
66
- setupAdmin('dashboard', (props) => {
67
- if (!props) return
68
-
69
- return () => (
70
- <Grid container spacing={1}>
71
- <AdminCard src={props} />
72
- </Grid>
73
- )
74
- })
package/dataprovider.js DELETED
@@ -1,94 +0,0 @@
1
- import { makeRequest } from './utils'
2
-
3
- export default (apiUrl) => {
4
- const methods = {
5
- getList: async (resource, { filter, pagination, sort }) => {
6
- const query = {}
7
- if (filter) query.where = JSON.stringify(filter)
8
- if (pagination) {
9
- const { page, perPage: limit } = pagination
10
- query.limit = limit
11
- query.offset = limit * (page - 1)
12
- }
13
- if (sort) {
14
- const { field, order } = sort
15
- query.sort = order == 'ASC' ? field : `-${field}`
16
- }
17
-
18
- const { headers, json } = await makeRequest(`${apiUrl}/${resource}`, {
19
- query,
20
- })
21
- return {
22
- data: json,
23
- total: parseInt(headers.get('x-total'), 10),
24
- }
25
- },
26
-
27
- getOne: async (resource, { id }) => {
28
- const { json } = await makeRequest(`${apiUrl}/${resource}/${id}`)
29
- return { data: json }
30
- },
31
-
32
- create: async (resource, { data }) => {
33
- const { json } = await makeRequest(`${apiUrl}/${resource}`, {
34
- data,
35
- method: 'POST',
36
- })
37
- return { data: json }
38
- },
39
-
40
- update: async (resource, { id, data }) => {
41
- const { json } = await makeRequest(`${apiUrl}/${resource}/${id}`, {
42
- data,
43
- method: 'PUT',
44
- })
45
- return { data: json }
46
- },
47
-
48
- updateMany: async (resource, { ids, data }) => {
49
- await Promise.all(ids.map((id) => methods.update(resource, { id, data })))
50
- return { data: ids }
51
- },
52
-
53
- delete: async (resource, { id }) => {
54
- await makeRequest(`${apiUrl}/${resource}/${id}`, {
55
- method: 'DELETE',
56
- })
57
- return { data: { id } }
58
- },
59
-
60
- deleteMany: async (resource, { ids }) => {
61
- await makeRequest(`${apiUrl}/${resource}`, {
62
- data: ids,
63
- method: 'DELETE',
64
- })
65
- return { data: ids }
66
- },
67
-
68
- getMany: (resource, { ids }) => {
69
- return methods.getList(resource, { filter: { id: { $in: ids } } })
70
- },
71
-
72
- getManyReference: async (resource, { target, id, filter, ...opts }) => {
73
- filter = filter || {}
74
- filter[target] = id
75
- return await methods.getList(resource, { filter, ...opts })
76
- },
77
-
78
- runAction: async (resource, action, props) => {
79
- const { payload, ids, record } = props
80
- action = action.replace(/^\/+/, '')
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 }
90
- },
91
- }
92
-
93
- return methods
94
- }
@@ -1,34 +0,0 @@
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
- )
27
- }
28
-
29
- AvatarField.displayName = 'AvatarField'
30
- AvatarField.defaultProps = {
31
- addLabel: true,
32
- }
33
-
34
- export default AvatarField
@@ -1,28 +0,0 @@
1
- import React from 'react'
2
-
3
- import { ReferenceField, TextField } from 'react-admin'
4
-
5
- const FKField = ({ refSource, refID, link, reference, source, ...props }) => {
6
- return (
7
- <ReferenceField
8
- link={link || 'show'}
9
- reference={reference}
10
- source={source}
11
- {...props}
12
- >
13
- <>
14
- <TextField source={refSource} />
15
- {' (#'}
16
- <TextField source={refID || 'id'} />
17
- {')'}
18
- </>
19
- </ReferenceField>
20
- )
21
- }
22
-
23
- FKField.displayName = 'FKField'
24
- FKField.defaultProps = {
25
- addLabel: true,
26
- }
27
-
28
- export default FKField
@@ -1,25 +0,0 @@
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
- }
@@ -1,9 +0,0 @@
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 DELETED
@@ -1,15 +0,0 @@
1
- import { createRoot } from 'react-dom/client'
2
-
3
- import { setupAdmin, processAdmin } from './utils.js'
4
-
5
- import './admin.jsx'
6
-
7
- const initAdmin = (globalThis.initAdmin = (props) => {
8
- const container = document.getElementById('root')
9
- const root = createRoot(container)
10
- root.render(processAdmin('admin', props))
11
- })
12
-
13
- export { setupAdmin, processAdmin }
14
-
15
- export default initAdmin
@@ -1,42 +0,0 @@
1
- import React from 'react'
2
-
3
- import { ReferenceInput, AutocompleteInput } from 'react-admin'
4
-
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
17
- }
18
-
19
- refSource = refSource || 'id'
20
-
21
- let renderText = (record) => {
22
- let pk = record[refSource]
23
- return (pk && `${record[refProp]} (#${pk})`) || ''
24
- }
25
-
26
- return (
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
- />
38
- </ReferenceInput>
39
- )
40
- }
41
-
42
- export default FKInput
@@ -1,62 +0,0 @@
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/resources.jsx DELETED
@@ -1,37 +0,0 @@
1
- import React from 'react'
2
-
3
- import { Resource } from 'react-admin'
4
- import * as icons from '@mui/icons-material'
5
-
6
- import { checkParams, processAdmin, setupAdmin } from './utils'
7
-
8
- import './views/list.jsx'
9
- import './views/show.jsx'
10
- import './views/edit.jsx'
11
- import './views/create.jsx'
12
-
13
- // Initialize Resources Components
14
- setupAdmin('resources', (resources) =>
15
- resources.map((res) => processAdmin('resource', res, res.name))
16
- )
17
-
18
- // Initialize a resource's component
19
- setupAdmin(
20
- 'resource',
21
- checkParams((props, res) => {
22
- let { create, edit, icon, list, name, show, ...resProps } = props
23
-
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
- )