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.
- package/Makefile +7 -0
- package/dist/admin.js +82 -0
- package/dist/authprovider.js +250 -0
- package/dist/buttons/ActionButton.js +110 -0
- package/dist/buttons/LinkButton.js +44 -0
- package/dist/dashboard.js +70 -0
- package/dist/dataprovider.js +379 -0
- package/dist/fields/AvatarField.js +46 -0
- package/dist/fields/FKField.js +43 -0
- package/dist/fields/JsonField.js +51 -0
- package/dist/hooks/useAction.js +17 -0
- package/dist/index.js +20 -0
- package/dist/inputs/FKInput.js +53 -0
- package/dist/inputs/JsonInput.js +82 -0
- package/dist/resources.js +57 -0
- package/dist/ui.js +95 -0
- package/dist/utils.js +67 -0
- package/dist/views/create.js +25 -0
- package/dist/views/edit.js +53 -0
- package/dist/views/list.js +83 -0
- package/dist/views/show.js +36 -0
- package/dist/web.js +33 -0
- package/package.json +12 -3
- package/webpack.config.js +7 -6
- package/.babelrc +0 -5
- package/admin.jsx +0 -60
- package/authprovider.js +0 -84
- package/buttons/ActionButton.jsx +0 -99
- package/buttons/LinkButton.jsx +0 -21
- package/dashboard.jsx +0 -74
- package/dataprovider.js +0 -94
- package/fields/AvatarField.jsx +0 -34
- package/fields/FKField.jsx +0 -28
- package/fields/JsonField.jsx +0 -25
- package/hooks/useAction.js +0 -9
- package/index.js +0 -15
- package/inputs/FKInput.jsx +0 -42
- package/inputs/JsonInput.jsx +0 -62
- package/resources.jsx +0 -37
- package/ui.jsx +0 -107
- package/utils.js +0 -39
- package/views/create.jsx +0 -30
- package/views/edit.jsx +0 -42
- package/views/list.jsx +0 -86
- 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
|
-
}
|
package/buttons/ActionButton.jsx
DELETED
|
@@ -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
|
package/buttons/LinkButton.jsx
DELETED
|
@@ -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
|
-
}
|
package/fields/AvatarField.jsx
DELETED
|
@@ -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
|
package/fields/FKField.jsx
DELETED
|
@@ -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
|
package/fields/JsonField.jsx
DELETED
|
@@ -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
|
-
}
|
package/hooks/useAction.js
DELETED
|
@@ -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
|
package/inputs/FKInput.jsx
DELETED
|
@@ -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
|
package/inputs/JsonInput.jsx
DELETED
|
@@ -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
|
-
)
|