muffin-admin 2.3.13 → 2.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muffin-admin",
3
- "version": "2.3.13",
3
+ "version": "2.5.0",
4
4
  "description": "NPM package for https://github.com/klen/muffin-admin",
5
5
  "files": [
6
6
  "src",
@@ -24,31 +24,32 @@
24
24
  "homepage": "https://github.com/klen/muffin-admin#readme",
25
25
  "dependencies": {
26
26
  "js-cookie": "^3.0.5",
27
- "react": "^18.2.0",
28
- "react-admin": "^4.12.2",
29
- "react-dom": "^18.2.0",
30
- "validator": "^13.9.0"
27
+ "react": "^18.3.1",
28
+ "react-admin": "^4.16.18",
29
+ "react-dom": "^18.3.1",
30
+ "validator": "^13.12.0"
31
31
  },
32
32
  "devDependencies": {
33
- "@types/node": "20.5.0",
34
- "@types/react": "18.2.20",
35
- "@types/react-dom": "18.2.7",
36
- "@types/react-window": "^1.8.5",
37
- "@typescript-eslint/eslint-plugin": "^6.4.0",
38
- "@typescript-eslint/parser": "^6.4.0",
39
- "dotenv": "^16.3.1",
40
- "eslint": "8.47.0",
41
- "eslint-config-prettier": "^9.0.0",
42
- "eslint-plugin-prettier": "^5.0.0",
43
- "html-webpack-plugin": "^5.5.3",
44
- "husky": "^8.0.1",
45
- "prettier": "^3.0.2",
46
- "prettier-plugin-organize-imports": "^3.2.3",
47
- "ts-loader": "^9.4.4",
48
- "typescript": "^5.1.6",
49
- "webpack": "^5.88.2",
33
+ "@types/node": "20.14.2",
34
+ "@types/react": "18.3.3",
35
+ "@types/react-dom": "18.3.0",
36
+ "@types/react-window": "^1.8.8",
37
+ "@typescript-eslint/eslint-plugin": "^7.12.0",
38
+ "@typescript-eslint/parser": "^7.12.0",
39
+ "dotenv": "^16.4.5",
40
+ "eslint": "^8",
41
+ "eslint-config-prettier": "^9.1.0",
42
+ "eslint-plugin-prettier": "^5.1.3",
43
+ "eslint-plugin-react": "^7.34.2",
44
+ "html-webpack-plugin": "^5.6.0",
45
+ "husky": "^9.0.11",
46
+ "prettier": "^3.2",
47
+ "prettier-plugin-organize-imports": "^3.2",
48
+ "ts-loader": "^9.5.1",
49
+ "typescript": "^5.4.5",
50
+ "webpack": "^5.91.0",
50
51
  "webpack-cli": "^5.1.4",
51
- "webpack-dev-server": "^4.15.1"
52
+ "webpack-dev-server": "^5.0.4"
52
53
  },
53
54
  "prettier": {
54
55
  "arrowParens": "always",
@@ -3,15 +3,7 @@ import SvgIcon from "@mui/material/SvgIcon"
3
3
  import Tooltip from "@mui/material/Tooltip"
4
4
  import Typography from "@mui/material/Typography"
5
5
  import createTheme from "@mui/material/styles/createTheme"
6
- import {
7
- Admin,
8
- AppBar,
9
- DataProvider,
10
- Layout,
11
- Login,
12
- ToggleThemeButton,
13
- defaultTheme,
14
- } from "react-admin"
6
+ import { Admin, AppBar, DataProvider, Layout, Login } from "react-admin"
15
7
  import { AdminOpts } from "./types"
16
8
  import { AdminPropsContext, buildAdmin, findBuilder, findIcon, setupAdmin } from "./utils"
17
9
 
@@ -24,7 +16,6 @@ export function MuffinAdmin(props: AdminOpts) {
24
16
  const children = resources.map((resource) =>
25
17
  buildAdmin(["resource", resource.name], {
26
18
  ...resource,
27
- key: resource.name,
28
19
  })
29
20
  )
30
21
 
@@ -43,7 +34,6 @@ export function MuffinAdmin(props: AdminOpts) {
43
34
  overflow: "hidden",
44
35
  }}
45
36
  />
46
- <ToggleThemeButton lightTheme={defaultTheme} darkTheme={darkTheme} />
47
37
  {appBarLinks.map((info) => (
48
38
  <Tooltip key={info.url} title={info.title}>
49
39
  <IconButton color="inherit" href={info.url}>
@@ -63,6 +53,7 @@ export function MuffinAdmin(props: AdminOpts) {
63
53
  layout={(props) => <Layout {...props} appBar={appBar} />}
64
54
  loginPage={findBuilder(["loginpage"])}
65
55
  requireAuth={auth.required}
56
+ darkTheme={{ palette: { mode: "dark" } }}
66
57
  >
67
58
  {children}
68
59
  </Admin>
@@ -13,6 +13,7 @@ export function MuffinResource({
13
13
  }: AdminResourceProps) {
14
14
  return (
15
15
  <Resource
16
+ key={name}
16
17
  name={name}
17
18
  icon={findIcon(icon)}
18
19
  create={createProps ? buildAdmin(["create", name], createProps) : undefined}
@@ -1,4 +1,5 @@
1
1
  import * as icons from "@mui/icons-material"
2
+ import { Box, Modal } from "@mui/material"
2
3
  import {
3
4
  Button,
4
5
  useListContext,
@@ -9,8 +10,9 @@ import {
9
10
  useUnselectAll,
10
11
  } from "react-admin"
11
12
 
13
+ import { useState } from "react"
12
14
  import { useAction } from "../hooks/useAction"
13
- import { buildIcon } from "../utils"
15
+ import { buildIcon, findBuilder, setupAdmin } from "../utils"
14
16
 
15
17
  export function BulkActionButton({ label, icon, title, action }) {
16
18
  const resource = useResourceContext()
@@ -19,10 +21,10 @@ export function BulkActionButton({ label, icon, title, action }) {
19
21
  unselectAll = useUnselectAll(resource),
20
22
  notify = useNotify()
21
23
 
22
- const mutation = useAction(resource, action)
24
+ const { mutate, isLoading } = useAction(action)
23
25
 
24
26
  const onClick = () => {
25
- mutation.mutate(
27
+ mutate(
26
28
  {
27
29
  action,
28
30
  ids: selectedIds,
@@ -41,42 +43,59 @@ export function BulkActionButton({ label, icon, title, action }) {
41
43
  }
42
44
 
43
45
  return (
44
- <Button label={label} title={title} onClick={onClick} disabled={mutation.isLoading}>
46
+ <Button label={label} title={title} onClick={onClick} disabled={isLoading}>
45
47
  {buildIcon(icon)}
46
48
  </Button>
47
49
  )
48
50
  }
49
51
 
50
52
  export function ActionButton(props) {
51
- const { icon, label, title, resource, action } = props
53
+ const { icon, label, title, action } = props
54
+
55
+ const [payload, setPayload] = useState(null)
56
+ const [payloadActive, setPayloadActive] = useState(false)
57
+
52
58
  const record = useRecordContext()
53
- const { mutate, isLoading } = useAction(resource, action)
59
+ const actionData = { record, payload }
60
+ const { mutate, isLoading } = useAction(action)
61
+
62
+ const PayloadBuilder = findBuilder(["action", "payload", action])
54
63
 
55
64
  const refresh = useRefresh(),
56
65
  notify = useNotify(),
57
66
  Icon = icons[icon]
58
67
 
59
- const onClick = () => {
60
- mutate(
61
- { record },
62
- {
63
- onSuccess: ({ data }) => {
64
- refresh()
65
- if (data && data.redirectTo) window.location = data.redirectTo
66
- if (data && data.message) notify(data.message, { type: "success" })
67
- },
68
- onError: (err) => {
69
- notify(typeof err === "string" ? err : err.message, {
70
- type: "error",
71
- })
72
- },
73
- }
74
- )
75
- }
68
+ const process = () =>
69
+ mutate(actionData, {
70
+ onSuccess: ({ data }) => {
71
+ if (data && data.message) notify(data.message, { type: "success" })
72
+ if (data && data.redirectTo) window.location = data.redirectTo
73
+ else refresh()
74
+ },
75
+ onError: (err) => {
76
+ notify(typeof err === "string" ? err : err.message, { type: "error" })
77
+ },
78
+ })
76
79
 
77
80
  return (
78
- <Button label={label} title={title} onClick={onClick} disabled={isLoading}>
79
- {Icon && <Icon />}
80
- </Button>
81
+ <>
82
+ <Button
83
+ label={label}
84
+ title={title}
85
+ onClick={PayloadBuilder ? () => setPayloadActive(true) : process}
86
+ disabled={isLoading}
87
+ >
88
+ {Icon && <Icon />}
89
+ </Button>
90
+ {PayloadBuilder && (
91
+ <PayloadBuilder
92
+ onChange={(payload) => {
93
+ setPayload(payload)
94
+ process()
95
+ }}
96
+ active={payloadActive}
97
+ />
98
+ )}
99
+ </>
81
100
  )
82
101
  }
@@ -75,7 +75,7 @@ export function MuffinDataprovider({ apiUrl }: AdminOpts) {
75
75
  return await methods.getList(resource, { filter, ...opts })
76
76
  },
77
77
 
78
- runAction: async (resource, action, props) => {
78
+ runAction: async (resource: string, action: string, props: TActionProps) => {
79
79
  const { payload, ids, record } = props
80
80
  action = action.replace(/^\/+/, "")
81
81
  if (record) {
@@ -92,4 +92,9 @@ export function MuffinDataprovider({ apiUrl }: AdminOpts) {
92
92
  return methods
93
93
  }
94
94
 
95
+ export type TActionProps = {
96
+ payload?: any
97
+ ids?: string[]
98
+ record: any
99
+ }
95
100
  setupAdmin(["dataprovider"], MuffinDataprovider)
@@ -24,6 +24,3 @@ export function AvatarField({ source, alt, style, nameProp, ...props }) {
24
24
  }
25
25
 
26
26
  AvatarField.displayName = "AvatarField"
27
- AvatarField.defaultProps = {
28
- addLabel: true,
29
- }
@@ -20,6 +20,3 @@ export function FKField({ reference, refSource, refKey, link, source, ...props }
20
20
  }
21
21
 
22
22
  FKField.displayName = "FKField"
23
- FKField.defaultProps = {
24
- addLabel: true,
25
- }
@@ -0,0 +1 @@
1
+ export * from './useAction'
@@ -1,9 +1,11 @@
1
- import { useDataProvider } from "react-admin"
1
+ import { useDataProvider, useResourceContext } from "react-admin"
2
2
  import { useMutation } from "react-query"
3
+ import { TActionProps } from "../dataprovider"
3
4
 
4
- export function useAction(resource, action) {
5
+ export function useAction(action) {
5
6
  const dataProvider = useDataProvider()
6
- return useMutation<{ data: any }, { message: string } | string, any>((params: any) =>
7
+ const resource = useResourceContext()
8
+ return useMutation<{ data: any }, { message: string } | string, any>((params: TActionProps) =>
7
9
  dataProvider.runAction(resource, action, params)
8
10
  )
9
11
  }
package/src/index.tsx CHANGED
@@ -14,6 +14,7 @@ export * from "./buildRA"
14
14
  export * from "./buttons"
15
15
  export * from "./dataprovider"
16
16
  export * from "./fields"
17
+ export * from "./hooks"
17
18
  export * from "./types"
18
19
  export * from "./utils"
19
20
 
@@ -16,7 +16,7 @@ export function FKInput({ refKey, refSource, fullWidth, reference, source, ...pr
16
16
  }
17
17
 
18
18
  return (
19
- <ReferenceInput reference={reference} source={source} {...props}>
19
+ <ReferenceInput reference={reference} source={source}>
20
20
  <AutocompleteInput source={refKey} optionText={renderText} fullWidth={fullWidth} {...props} />
21
21
  </ReferenceInput>
22
22
  )