muffin-admin 1.36.4 → 1.36.6
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/dist/esm/admin.js +71 -0
- package/dist/esm/authprovider.js +191 -0
- package/dist/esm/buttons/ActionButton.js +81 -0
- package/dist/esm/buttons/LinkButton.js +23 -0
- package/dist/esm/dashboard.js +60 -0
- package/dist/esm/dataprovider.js +296 -0
- package/dist/esm/fields/AvatarField.js +29 -0
- package/dist/esm/fields/FKField.js +27 -0
- package/dist/esm/fields/JsonField.js +28 -0
- package/dist/esm/hooks/useAction.js +8 -0
- package/dist/esm/index.js +40 -0
- package/dist/esm/inputs/FKInput.js +33 -0
- package/dist/esm/inputs/JsonInput.js +63 -0
- package/dist/esm/resources.js +38 -0
- package/dist/esm/ui.js +71 -0
- package/dist/esm/utils.js +38 -0
- package/dist/esm/views/create.js +16 -0
- package/dist/esm/views/edit.js +37 -0
- package/dist/esm/views/list.js +64 -0
- package/dist/esm/views/show.js +26 -0
- package/dist/esm/web.js +2 -0
- package/package.json +7 -11
- package/src/admin.jsx +78 -0
- package/src/authprovider.js +85 -0
- package/src/buttons/ActionButton.jsx +99 -0
- package/src/buttons/LinkButton.jsx +20 -0
- package/src/dashboard.jsx +74 -0
- package/src/dataprovider.js +94 -0
- package/src/fields/AvatarField.jsx +33 -0
- package/src/fields/FKField.jsx +27 -0
- package/src/fields/JsonField.jsx +26 -0
- package/src/hooks/useAction.js +9 -0
- package/src/index.js +14 -0
- package/src/inputs/FKInput.jsx +41 -0
- package/src/inputs/JsonInput.jsx +62 -0
- package/src/resources.jsx +36 -0
- package/src/ui.jsx +107 -0
- package/src/utils.js +38 -0
- package/src/views/create.jsx +30 -0
- package/src/views/edit.jsx +41 -0
- package/src/views/list.jsx +86 -0
- package/src/views/show.jsx +42 -0
- package/src/web.js +3 -0
- package/.eslintrc.json +0 -44
- package/Makefile +0 -18
- package/jsconfig.json +0 -9
- package/webpack.config.js +0 -45
- /package/dist/{admin.js → cjs/admin.js} +0 -0
- /package/dist/{authprovider.js → cjs/authprovider.js} +0 -0
- /package/dist/{buttons → cjs/buttons}/ActionButton.js +0 -0
- /package/dist/{buttons → cjs/buttons}/LinkButton.js +0 -0
- /package/dist/{dashboard.js → cjs/dashboard.js} +0 -0
- /package/dist/{dataprovider.js → cjs/dataprovider.js} +0 -0
- /package/dist/{fields → cjs/fields}/AvatarField.js +0 -0
- /package/dist/{fields → cjs/fields}/FKField.js +0 -0
- /package/dist/{fields → cjs/fields}/JsonField.js +0 -0
- /package/dist/{hooks → cjs/hooks}/useAction.js +0 -0
- /package/dist/{index.js → cjs/index.js} +0 -0
- /package/dist/{inputs → cjs/inputs}/FKInput.js +0 -0
- /package/dist/{inputs → cjs/inputs}/JsonInput.js +0 -0
- /package/dist/{resources.js → cjs/resources.js} +0 -0
- /package/dist/{ui.js → cjs/ui.js} +0 -0
- /package/dist/{utils.js → cjs/utils.js} +0 -0
- /package/dist/{views → cjs/views}/create.js +0 -0
- /package/dist/{views → cjs/views}/edit.js +0 -0
- /package/dist/{views → cjs/views}/list.js +0 -0
- /package/dist/{views → cjs/views}/show.js +0 -0
- /package/dist/{web.js → cjs/web.js} +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Create, SimpleForm, TopToolbar, ListButton } from "react-admin";
|
|
4
|
+
import initRAItems from "../ui";
|
|
5
|
+
import { checkParams, processAdmin, setupAdmin } from "../utils";
|
|
6
|
+
|
|
7
|
+
// Initialize a create component
|
|
8
|
+
setupAdmin("create", checkParams(function (inputs, res) {
|
|
9
|
+
return function MACreate(props) {
|
|
10
|
+
var Actions = /*#__PURE__*/React.createElement(TopToolbar, null, /*#__PURE__*/React.createElement(ListButton, null));
|
|
11
|
+
return /*#__PURE__*/React.createElement(Create, _extends({
|
|
12
|
+
actions: Actions
|
|
13
|
+
}, props), /*#__PURE__*/React.createElement(SimpleForm, null, processAdmin("create-inputs", inputs, res)));
|
|
14
|
+
};
|
|
15
|
+
}));
|
|
16
|
+
setupAdmin("create-inputs", initRAItems);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
3
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
4
|
+
var _excluded = ["basePath", "resource"];
|
|
5
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
6
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { Edit, ListButton, ShowButton, SimpleForm, TopToolbar } from "react-admin";
|
|
9
|
+
import initRAItems from "../ui";
|
|
10
|
+
import { checkParams, processAdmin, setupAdmin } from "../utils";
|
|
11
|
+
import { ActionButton } from "../buttons/ActionButton";
|
|
12
|
+
|
|
13
|
+
// Initialize an edit component
|
|
14
|
+
setupAdmin("edit", checkParams(function (_ref, res) {
|
|
15
|
+
var actions = _ref.actions,
|
|
16
|
+
inputs = _ref.inputs;
|
|
17
|
+
return function MAEdit(props) {
|
|
18
|
+
var Actions = function Actions(_ref2) {
|
|
19
|
+
var basePath = _ref2.basePath,
|
|
20
|
+
resource = _ref2.resource,
|
|
21
|
+
baseProps = _objectWithoutProperties(_ref2, _excluded);
|
|
22
|
+
return /*#__PURE__*/React.createElement(TopToolbar, null, actions.map(function (props, idx) {
|
|
23
|
+
var aProps = _objectSpread(_objectSpread({
|
|
24
|
+
resource: resource,
|
|
25
|
+
basePath: basePath
|
|
26
|
+
}, baseProps), props);
|
|
27
|
+
return /*#__PURE__*/React.createElement(ActionButton, _extends({
|
|
28
|
+
key: idx
|
|
29
|
+
}, aProps));
|
|
30
|
+
}), /*#__PURE__*/React.createElement(ListButton, null), /*#__PURE__*/React.createElement(ShowButton, null));
|
|
31
|
+
};
|
|
32
|
+
return /*#__PURE__*/React.createElement(Edit, _extends({
|
|
33
|
+
actions: /*#__PURE__*/React.createElement(Actions, null)
|
|
34
|
+
}, props), /*#__PURE__*/React.createElement(SimpleForm, null, processAdmin("edit-inputs", inputs, res)));
|
|
35
|
+
};
|
|
36
|
+
}));
|
|
37
|
+
setupAdmin("edit-inputs", initRAItems);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
3
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
4
|
+
var _excluded = ["children", "filters", "edit", "pagination", "show", "limit", "limitMax", "actions"];
|
|
5
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
6
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { BulkDeleteButton, Datagrid, EditButton, Filter, List, Pagination } from "react-admin";
|
|
9
|
+
import uniq from "lodash/uniq";
|
|
10
|
+
import sortBy from "lodash/sortBy";
|
|
11
|
+
import initRAItems from "../ui";
|
|
12
|
+
import { checkParams, processAdmin, setupAdmin } from "../utils";
|
|
13
|
+
import { BulkActionButton } from "../buttons/ActionButton";
|
|
14
|
+
var defaultPagination = /*#__PURE__*/React.createElement(Pagination, {
|
|
15
|
+
rowsPerPageOptions: [10, 25, 50, 100]
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Initiliaze a list component
|
|
19
|
+
setupAdmin("list", checkParams(function (props, res) {
|
|
20
|
+
var children = props.children,
|
|
21
|
+
filters = props.filters,
|
|
22
|
+
edit = props.edit,
|
|
23
|
+
pagination = props.pagination,
|
|
24
|
+
show = props.show,
|
|
25
|
+
limit = props.limit,
|
|
26
|
+
limitMax = props.limitMax,
|
|
27
|
+
actions = props.actions,
|
|
28
|
+
listProps = _objectWithoutProperties(props, _excluded);
|
|
29
|
+
children = processAdmin("list-fields", children, res);
|
|
30
|
+
if (edit) children.push( /*#__PURE__*/React.createElement(EditButton, {
|
|
31
|
+
key: "edit-button"
|
|
32
|
+
}));
|
|
33
|
+
pagination = pagination || /*#__PURE__*/React.createElement(Pagination, {
|
|
34
|
+
rowsPerPageOptions: sortBy(uniq([10, 25, 50, 100, limit, limitMax]))
|
|
35
|
+
});
|
|
36
|
+
return function MAList(props) {
|
|
37
|
+
var Filters = function Filters(props) {
|
|
38
|
+
return /*#__PURE__*/React.createElement(Filter, props, processAdmin("list-filters", filters, res));
|
|
39
|
+
};
|
|
40
|
+
var Actions = processAdmin("list-bulkActions", actions);
|
|
41
|
+
props = _objectSpread(_objectSpread({}, props), listProps);
|
|
42
|
+
return /*#__PURE__*/React.createElement(List, _extends({
|
|
43
|
+
filters: /*#__PURE__*/React.createElement(Filters, null),
|
|
44
|
+
bulkActionButtons: /*#__PURE__*/React.createElement(Actions, null),
|
|
45
|
+
perPage: limit,
|
|
46
|
+
pagination: pagination || defaultPagination
|
|
47
|
+
}, props), /*#__PURE__*/React.createElement(Datagrid, {
|
|
48
|
+
rowClick: show ? "show" : null
|
|
49
|
+
}, children));
|
|
50
|
+
};
|
|
51
|
+
}));
|
|
52
|
+
setupAdmin("list-bulkActions", function (actions) {
|
|
53
|
+
return function MABulkActions(props) {
|
|
54
|
+
var buttons = actions.map(function (action, idx) {
|
|
55
|
+
var aProps = _objectSpread(_objectSpread({}, action), props);
|
|
56
|
+
return /*#__PURE__*/React.createElement(BulkActionButton, _extends({
|
|
57
|
+
key: idx
|
|
58
|
+
}, aProps));
|
|
59
|
+
});
|
|
60
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, buttons, /*#__PURE__*/React.createElement(BulkDeleteButton, props));
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
setupAdmin("list-filters", initRAItems);
|
|
64
|
+
setupAdmin("list-fields", initRAItems);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { ListButton, EditButton, Show, SimpleShowLayout, TopToolbar } from "react-admin";
|
|
4
|
+
import initRAItems from "../ui";
|
|
5
|
+
import { checkParams, processAdmin, setupAdmin } from "../utils";
|
|
6
|
+
import { ActionButton } from "../buttons/ActionButton";
|
|
7
|
+
|
|
8
|
+
// Initiliaze a show component
|
|
9
|
+
setupAdmin("show", checkParams(function (_ref, resource) {
|
|
10
|
+
var actions = _ref.actions,
|
|
11
|
+
fields = _ref.fields;
|
|
12
|
+
return function MAShow(props) {
|
|
13
|
+
var Actions = function Actions() {
|
|
14
|
+
return /*#__PURE__*/React.createElement(TopToolbar, null, actions.map(function (props, idx) {
|
|
15
|
+
return /*#__PURE__*/React.createElement(ActionButton, _extends({
|
|
16
|
+
key: idx,
|
|
17
|
+
resource: resource
|
|
18
|
+
}, props));
|
|
19
|
+
}), /*#__PURE__*/React.createElement(ListButton, null), /*#__PURE__*/React.createElement(EditButton, null));
|
|
20
|
+
};
|
|
21
|
+
return /*#__PURE__*/React.createElement(Show, _extends({
|
|
22
|
+
actions: /*#__PURE__*/React.createElement(Actions, null)
|
|
23
|
+
}, props), /*#__PURE__*/React.createElement(SimpleShowLayout, null, processAdmin("show-fields", fields, resource)));
|
|
24
|
+
};
|
|
25
|
+
}));
|
|
26
|
+
setupAdmin("show-fields", initRAItems);
|
package/dist/esm/web.js
ADDED
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muffin-admin",
|
|
3
|
-
"version": "1.36.
|
|
3
|
+
"version": "1.36.6",
|
|
4
4
|
"description": "NPM package for https://github.com/klen/muffin-admin",
|
|
5
|
-
"
|
|
5
|
+
"files": [
|
|
6
|
+
"src",
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"main": "dist/cjs/index.js",
|
|
10
|
+
"module": "dist/esm/index.js",
|
|
6
11
|
"scripts": {
|
|
7
12
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
13
|
},
|
|
@@ -35,14 +40,5 @@
|
|
|
35
40
|
"webpack": "^5.24.4",
|
|
36
41
|
"webpack-cli": "^5.0.0",
|
|
37
42
|
"webpack-dev-server": "^4.3.1"
|
|
38
|
-
},
|
|
39
|
-
"babel": {
|
|
40
|
-
"presets": [
|
|
41
|
-
"@babel/preset-env",
|
|
42
|
-
"@babel/preset-react"
|
|
43
|
-
],
|
|
44
|
-
"plugins": [
|
|
45
|
-
"@babel/plugin-transform-runtime"
|
|
46
|
-
]
|
|
47
43
|
}
|
|
48
44
|
}
|
package/src/admin.jsx
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Admin,
|
|
5
|
+
AppBar,
|
|
6
|
+
Layout,
|
|
7
|
+
Login,
|
|
8
|
+
ToggleThemeButton,
|
|
9
|
+
defaultTheme,
|
|
10
|
+
} from "react-admin"
|
|
11
|
+
import {
|
|
12
|
+
IconButton,
|
|
13
|
+
SvgIcon,
|
|
14
|
+
Tooltip,
|
|
15
|
+
Typography,
|
|
16
|
+
createTheme,
|
|
17
|
+
} from "@mui/material"
|
|
18
|
+
import * as icons from "@mui/icons-material"
|
|
19
|
+
|
|
20
|
+
import authProvider from "./authprovider"
|
|
21
|
+
import dataProvider from "./dataprovider"
|
|
22
|
+
import { processAdmin, setupAdmin } from "./utils"
|
|
23
|
+
|
|
24
|
+
import "./dashboard"
|
|
25
|
+
import "./resources"
|
|
26
|
+
|
|
27
|
+
const darkTheme = createTheme({
|
|
28
|
+
palette: { mode: "dark" },
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// Initialize the admin
|
|
32
|
+
setupAdmin("admin", (props) => {
|
|
33
|
+
const { apiUrl, auth, adminProps, appBarLinks, dashboard, resources } = props
|
|
34
|
+
|
|
35
|
+
let appBar = (props) => (
|
|
36
|
+
<AppBar {...props}>
|
|
37
|
+
<Typography
|
|
38
|
+
variant="h6"
|
|
39
|
+
color="inherit"
|
|
40
|
+
id="react-admin-title"
|
|
41
|
+
style={{
|
|
42
|
+
flex: 1,
|
|
43
|
+
textOverflow: "ellipsis",
|
|
44
|
+
whiteSpace: "nowrap",
|
|
45
|
+
overflow: "hidden",
|
|
46
|
+
}}
|
|
47
|
+
/>
|
|
48
|
+
<ToggleThemeButton lightTheme={defaultTheme} darkTheme={darkTheme} />
|
|
49
|
+
{appBarLinks.map((info) => (
|
|
50
|
+
<Tooltip key={info.url} title={info.title}>
|
|
51
|
+
<IconButton color="inherit" href={info.url}>
|
|
52
|
+
<SvgIcon component={icons[info.icon]} />
|
|
53
|
+
</IconButton>
|
|
54
|
+
</Tooltip>
|
|
55
|
+
))}
|
|
56
|
+
</AppBar>
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<Admin
|
|
61
|
+
authProvider={processAdmin("auth", auth)}
|
|
62
|
+
dataProvider={processAdmin("data", apiUrl)}
|
|
63
|
+
dashboard={processAdmin("dashboard", dashboard)}
|
|
64
|
+
loginPage={processAdmin("login", auth)}
|
|
65
|
+
layout={(props) => <Layout appBar={appBar} {...props} />}
|
|
66
|
+
{...adminProps}
|
|
67
|
+
>
|
|
68
|
+
{processAdmin("resources", resources)}
|
|
69
|
+
</Admin>
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// Initialize authentication and data providers
|
|
74
|
+
setupAdmin("auth", authProvider)
|
|
75
|
+
setupAdmin("data", dataProvider)
|
|
76
|
+
|
|
77
|
+
// Initialize login page
|
|
78
|
+
setupAdmin("login", () => Login)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import Cookies from "js-cookie"
|
|
2
|
+
|
|
3
|
+
import { makeRequest, processAdmin, requestHeaders, setupAdmin } from "./utils"
|
|
4
|
+
|
|
5
|
+
setupAdmin(
|
|
6
|
+
"auth-get",
|
|
7
|
+
({ storage }) =>
|
|
8
|
+
(name) =>
|
|
9
|
+
storage == "localstorage" ? localStorage.getItem(name) : Cookies.get(name)
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
setupAdmin("auth-set", ({ storage }) => (name, value) => {
|
|
13
|
+
if (storage == "localstorage") {
|
|
14
|
+
localStorage.setItem(name, value)
|
|
15
|
+
requestHeaders["Authorization"] = value
|
|
16
|
+
} else Cookies.set(name, value)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export default (props) => {
|
|
20
|
+
const { identityURL, authorizeURL, logoutURL, required, storage_name } = props
|
|
21
|
+
|
|
22
|
+
const authGet = processAdmin("auth-get", props)
|
|
23
|
+
const authSet = processAdmin("auth-set", props)
|
|
24
|
+
|
|
25
|
+
// Initialize request headers
|
|
26
|
+
authSet(storage_name, authGet(storage_name))
|
|
27
|
+
|
|
28
|
+
let getIdentity = async () => {
|
|
29
|
+
if (identityURL) {
|
|
30
|
+
let { json } = await makeRequest(identityURL)
|
|
31
|
+
return json
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (required)
|
|
36
|
+
return {
|
|
37
|
+
login: async (data) => {
|
|
38
|
+
if (!authorizeURL) throw { message: "Authorization is not supported" }
|
|
39
|
+
|
|
40
|
+
let { json } = await makeRequest(authorizeURL, { data, method: "POST" })
|
|
41
|
+
authSet(storage_name, json)
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
checkError: async (error) => {
|
|
45
|
+
const { status } = error
|
|
46
|
+
|
|
47
|
+
if (status == 401 || status == 403) {
|
|
48
|
+
// Clean storage
|
|
49
|
+
authSet(storage_name, "")
|
|
50
|
+
|
|
51
|
+
throw {
|
|
52
|
+
message: "Invalid authorization",
|
|
53
|
+
redirectTo: logoutURL,
|
|
54
|
+
logoutUser: !logoutURL,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
checkAuth: async () => {
|
|
60
|
+
const auth = authGet(storage_name)
|
|
61
|
+
if (!auth) throw { message: "Authorization required" }
|
|
62
|
+
|
|
63
|
+
if (!identityURL) return auth
|
|
64
|
+
|
|
65
|
+
let user = await getIdentity()
|
|
66
|
+
if (!user) throw { message: "Authorization required" }
|
|
67
|
+
|
|
68
|
+
return user
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
logout: async () => {
|
|
72
|
+
// Clean storage
|
|
73
|
+
authSet(storage_name, "")
|
|
74
|
+
|
|
75
|
+
if (logoutURL) window.location = logoutURL
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
getIdentity,
|
|
79
|
+
|
|
80
|
+
getPermissions: () => {
|
|
81
|
+
const role = authGet(storage_name + "_role")
|
|
82
|
+
return role ? Promise.resolve(role) : Promise.reject()
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
useRefresh,
|
|
6
|
+
useUnselectAll,
|
|
7
|
+
useNotify,
|
|
8
|
+
useRecordContext,
|
|
9
|
+
} from "react-admin"
|
|
10
|
+
import * as icons from "@mui/icons-material"
|
|
11
|
+
|
|
12
|
+
import useAction from "../hooks/useAction"
|
|
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
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { Button } from "react-admin"
|
|
3
|
+
import { Link } from "react-router-dom"
|
|
4
|
+
import * as icons from "@mui/icons-material"
|
|
5
|
+
|
|
6
|
+
const LinkButton = ({ label, icon, title, resource, filters }) => {
|
|
7
|
+
const Icon = icons[icon] || icon
|
|
8
|
+
let linkParams = {
|
|
9
|
+
pathname: `/${resource}`,
|
|
10
|
+
search: filters ? `filter=${JSON.stringify(filters)}` : undefined,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<Button label={label} title={title} component={Link} to={linkParams}>
|
|
15
|
+
{Icon && <Icon />}
|
|
16
|
+
</Button>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default LinkButton
|
|
@@ -0,0 +1,74 @@
|
|
|
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
|
+
|
|
14
|
+
import { setupAdmin } from "./utils"
|
|
15
|
+
|
|
16
|
+
const AdminTableView = ({ src }) => (
|
|
17
|
+
<Table>
|
|
18
|
+
<TableBody>
|
|
19
|
+
{src.map((row, idx) => (
|
|
20
|
+
<TableRow key={idx} hover>
|
|
21
|
+
{row.map((cell, idx) => (
|
|
22
|
+
<TableCell key={idx}>{cell}</TableCell>
|
|
23
|
+
))}
|
|
24
|
+
</TableRow>
|
|
25
|
+
))}
|
|
26
|
+
</TableBody>
|
|
27
|
+
</Table>
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
const AdminCard = ({ src }) => {
|
|
31
|
+
if (Array.isArray(src)) {
|
|
32
|
+
return (
|
|
33
|
+
<Grid container item spacing={2}>
|
|
34
|
+
{src.map((card, idx) => (
|
|
35
|
+
<AdminCard key={idx} src={card} />
|
|
36
|
+
))}
|
|
37
|
+
</Grid>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let { title, value } = src
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Grid item xs>
|
|
45
|
+
<Card>
|
|
46
|
+
<CardContent>
|
|
47
|
+
<Typography
|
|
48
|
+
variant="h5"
|
|
49
|
+
component="h2"
|
|
50
|
+
m={2}
|
|
51
|
+
style={{ textAlign: "center" }}
|
|
52
|
+
>
|
|
53
|
+
{title}
|
|
54
|
+
</Typography>
|
|
55
|
+
{(Array.isArray(value) && <AdminTableView src={value} />) || (
|
|
56
|
+
<pre>{JSON.stringify(value, null, 2)}</pre>
|
|
57
|
+
)}
|
|
58
|
+
</CardContent>
|
|
59
|
+
</Card>
|
|
60
|
+
</Grid>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Process dashboard
|
|
65
|
+
setupAdmin("dashboard", (props) => {
|
|
66
|
+
if (!props) return
|
|
67
|
+
|
|
68
|
+
const Dashboard = () => (
|
|
69
|
+
<Grid container spacing={1}>
|
|
70
|
+
<AdminCard src={props} />
|
|
71
|
+
</Grid>
|
|
72
|
+
)
|
|
73
|
+
return Dashboard
|
|
74
|
+
})
|
|
@@ -0,0 +1,94 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import get from "lodash/get"
|
|
3
|
+
import { useRecordContext } from "ra-core"
|
|
4
|
+
import Avatar from "@mui/material/Avatar"
|
|
5
|
+
|
|
6
|
+
const AvatarField = ({ source, alt, style, nameProp, ...props }) => {
|
|
7
|
+
const record = useRecordContext(props)
|
|
8
|
+
|
|
9
|
+
let value = get(record, source),
|
|
10
|
+
name = record[nameProp]
|
|
11
|
+
|
|
12
|
+
const letters = name
|
|
13
|
+
? name
|
|
14
|
+
.trim(" ")
|
|
15
|
+
.split(/\s+/)
|
|
16
|
+
.slice(0, 2)
|
|
17
|
+
.map((n) => n[0].toUpperCase())
|
|
18
|
+
.join("")
|
|
19
|
+
: ""
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Avatar src={value} alt={alt} style={style}>
|
|
23
|
+
{letters}
|
|
24
|
+
</Avatar>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
AvatarField.displayName = "AvatarField"
|
|
29
|
+
AvatarField.defaultProps = {
|
|
30
|
+
addLabel: true,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default AvatarField
|