muffin-admin 11.8.0 → 13.0.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/dist/MuffinAdmin.d.ts +5 -5
- package/dist/MuffinAdmin.js +1 -1
- package/dist/MuffinDashboard.d.ts +1 -1
- package/dist/MuffinDashboard.js +3 -3
- package/dist/MuffinMenu.d.ts +2 -2
- package/dist/MuffinMenu.js +2 -2
- package/dist/MuffinRecordList.d.ts +2 -2
- package/dist/MuffinRecordList.js +1 -1
- package/dist/MuffinResource.d.ts +2 -2
- package/dist/MuffinResourceCreate.d.ts +3 -3
- package/dist/MuffinResourceEdit.d.ts +6 -6
- package/dist/MuffinResourceEdit.js +36 -4
- package/dist/MuffinResourceList.d.ts +2 -2
- package/dist/MuffinResourceList.js +10 -5
- package/dist/MuffinResourceShow.d.ts +5 -5
- package/dist/MuffinResourceShow.js +43 -6
- package/dist/actions/LinkAction.d.ts +2 -2
- package/dist/authprovider.js +7 -9
- package/dist/buildRA.d.ts +4 -3
- package/dist/buildRA.js +1 -1
- package/dist/buttons/ActionButton.d.ts +5 -5
- package/dist/buttons/ActionButton.js +5 -5
- package/dist/buttons/CopyButton.d.ts +1 -1
- package/dist/buttons/LinkButton.d.ts +2 -2
- package/dist/buttons/LinkButton.js +8 -2
- package/dist/common/AdminModal.d.ts +6 -4
- package/dist/common/AdminModal.js +2 -2
- package/dist/common/ConfirmationProvider.d.ts +2 -2
- package/dist/common/ConfirmationProvider.js +1 -1
- package/dist/common/HelpLink.d.ts +1 -1
- package/dist/common/PayloadButtons.d.ts +1 -1
- package/dist/common/PayloadButtons.js +1 -1
- package/dist/context.d.ts +1 -1
- package/dist/dataprovider.d.ts +7 -7
- package/dist/fields/AvatarField.d.ts +3 -3
- package/dist/fields/AvatarField.js +2 -2
- package/dist/fields/CopyField.d.ts +1 -1
- package/dist/fields/EditableBooleanField.d.ts +1 -1
- package/dist/fields/FKField.d.ts +4 -4
- package/dist/fields/FKField.js +12 -2
- package/dist/fields/JsonField.d.ts +1 -1
- package/dist/filters/DateRangeFilter.d.ts +1 -1
- package/dist/filters/Filter.d.ts +2 -2
- package/dist/filters/Filter.js +2 -2
- package/dist/filters/SearchFilter.d.ts +2 -2
- package/dist/filters/SearchFilter.js +6 -4
- package/dist/hooks/useAction.d.ts +2 -2
- package/dist/hooks/useAction.js +2 -2
- package/dist/i18n/en.d.ts +1 -1
- package/dist/i18n/provider.d.ts +3 -3
- package/dist/i18n/ru.d.ts +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +12 -12
- package/dist/inputs/DateRangeInput.d.ts +2 -2
- package/dist/inputs/FKInput.d.ts +2 -2
- package/dist/inputs/ImgInput.d.ts +2 -2
- package/dist/inputs/JsonInput.d.ts +1 -1
- package/dist/inputs/JsonInput.js +1 -1
- package/dist/inputs/TimestampInput.d.ts +2 -2
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/dist/web.js +0 -1
- package/package.json +15 -29
- package/src/MuffinAdmin.tsx +6 -7
- package/src/MuffinDashboard.tsx +6 -6
- package/src/MuffinMenu.tsx +3 -5
- package/src/MuffinRecordList.tsx +2 -2
- package/src/MuffinResource.tsx +1 -1
- package/src/MuffinResourceCreate.tsx +1 -1
- package/src/MuffinResourceEdit.tsx +73 -12
- package/src/MuffinResourceList.tsx +17 -6
- package/src/MuffinResourceShow.tsx +93 -15
- package/src/actions/LinkAction.tsx +1 -1
- package/src/authprovider.ts +88 -101
- package/src/buildRA.tsx +4 -3
- package/src/buttons/ActionButton.tsx +9 -10
- package/src/buttons/LinkButton.tsx +11 -3
- package/src/common/AdminModal.tsx +8 -5
- package/src/common/ConfirmationProvider.tsx +1 -1
- package/src/common/PayloadButtons.tsx +1 -1
- package/src/context.ts +1 -1
- package/src/dataprovider.ts +2 -2
- package/src/fields/AvatarField.tsx +8 -10
- package/src/fields/FKField.tsx +14 -2
- package/src/filters/Filter.tsx +2 -2
- package/src/filters/SearchFilter.tsx +14 -7
- package/src/globals.d.ts +1 -0
- package/src/hooks/useAction.ts +4 -4
- package/src/i18n/en.ts +1 -1
- package/src/i18n/ru.ts +1 -1
- package/src/index.tsx +12 -13
- package/src/inputs/DateRangeInput.tsx +1 -1
- package/src/inputs/JsonInput.tsx +1 -1
- package/src/utils.tsx +2 -1
- package/src/version.ts +1 -0
- package/src/web.ts +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Box, Typography } from "@mui/material"
|
|
2
|
+
import type { PropsWithChildren } from "react"
|
|
2
3
|
import {
|
|
3
4
|
DeleteButton,
|
|
4
5
|
Edit,
|
|
@@ -8,10 +9,14 @@ import {
|
|
|
8
9
|
SimpleForm,
|
|
9
10
|
Toolbar,
|
|
10
11
|
TopToolbar,
|
|
12
|
+
useRecordContext,
|
|
11
13
|
useResourceContext,
|
|
14
|
+
useTranslate,
|
|
12
15
|
} from "react-admin"
|
|
16
|
+
import { useLocation, useNavigate } from "react-router-dom"
|
|
13
17
|
import { buildRA } from "./buildRA"
|
|
14
18
|
import { ActionButton } from "./buttons"
|
|
19
|
+
import { AdminModal } from "./common"
|
|
15
20
|
import { useMuffinAdminOpts, useMuffinResourceOpts } from "./hooks"
|
|
16
21
|
import { findBuilder, setupAdmin } from "./utils"
|
|
17
22
|
|
|
@@ -21,18 +26,64 @@ export function MuffinEdit({ children }: PropsWithChildren) {
|
|
|
21
26
|
} = useMuffinAdminOpts()
|
|
22
27
|
|
|
23
28
|
const { edit, name, key } = useMuffinResourceOpts()
|
|
29
|
+
const location = useLocation()
|
|
30
|
+
const navigate = useNavigate()
|
|
31
|
+
const returnTo = new URLSearchParams(location.search).get("returnTo")
|
|
24
32
|
if (!edit) return null
|
|
25
33
|
|
|
34
|
+
const handleClose = () => {
|
|
35
|
+
if (returnTo) {
|
|
36
|
+
navigate(returnTo)
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (window.history.length > 1) {
|
|
41
|
+
navigate(-1)
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
navigate(`/${name}`)
|
|
46
|
+
}
|
|
47
|
+
|
|
26
48
|
const Actions = findBuilder(["edit-toolbar", name])
|
|
27
49
|
const Inputs = findBuilder(["edit-inputs", name])
|
|
28
50
|
const FormToolbar = findBuilder(["edit-form-toolbar", name])
|
|
29
51
|
|
|
30
52
|
return (
|
|
31
|
-
<Edit
|
|
32
|
-
{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
53
|
+
<Edit
|
|
54
|
+
actions={false}
|
|
55
|
+
title={false}
|
|
56
|
+
component="div"
|
|
57
|
+
mutationMode={mutationMode}
|
|
58
|
+
queryOptions={{ meta: { key } }}
|
|
59
|
+
sx={{
|
|
60
|
+
"& .RaEdit-main": {
|
|
61
|
+
padding: 0,
|
|
62
|
+
},
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
<AdminModal
|
|
66
|
+
open={true}
|
|
67
|
+
onClose={handleClose}
|
|
68
|
+
title={<Actions />}
|
|
69
|
+
maxWidth="xl"
|
|
70
|
+
contentSx={{
|
|
71
|
+
paddingTop: "8px !important",
|
|
72
|
+
paddingBottom: "12px !important",
|
|
73
|
+
paddingLeft: "16px !important",
|
|
74
|
+
paddingRight: "16px !important",
|
|
75
|
+
}}
|
|
76
|
+
sx={{
|
|
77
|
+
"& .MuiDialog-paper": {
|
|
78
|
+
width: "min(1400px, 96vw)",
|
|
79
|
+
},
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
{children}
|
|
83
|
+
<SimpleForm toolbar={<FormToolbar />}>
|
|
84
|
+
<Inputs />
|
|
85
|
+
</SimpleForm>
|
|
86
|
+
</AdminModal>
|
|
36
87
|
</Edit>
|
|
37
88
|
)
|
|
38
89
|
}
|
|
@@ -66,14 +117,24 @@ setupAdmin(["edit-inputs"], MuffinEditInputs)
|
|
|
66
117
|
|
|
67
118
|
export function MuffinEditToolbar({ children }: PropsWithChildren) {
|
|
68
119
|
const resource = useResourceContext()
|
|
120
|
+
const record = useRecordContext()
|
|
121
|
+
const translate = useTranslate()
|
|
69
122
|
const Actions = findBuilder(["edit-actions", resource])
|
|
70
123
|
return (
|
|
71
|
-
<
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
124
|
+
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
|
125
|
+
<TopToolbar sx={{ pl: 0, pr: 0, width: "100%", justifyContent: "space-between" }}>
|
|
126
|
+
<Typography variant="h6" sx={{ mr: 2 }}>
|
|
127
|
+
{translate(`resources.${resource}.name`, { smart_count: 1, _: resource })}
|
|
128
|
+
{record?.id ? ` #${record.id}` : ""}
|
|
129
|
+
</Typography>
|
|
130
|
+
<Box sx={{ display: "flex", alignItems: "center" }}>
|
|
131
|
+
<Actions />
|
|
132
|
+
{children}
|
|
133
|
+
<ShowButton />
|
|
134
|
+
<ListButton />
|
|
135
|
+
</Box>
|
|
136
|
+
</TopToolbar>
|
|
137
|
+
</Box>
|
|
77
138
|
)
|
|
78
139
|
}
|
|
79
140
|
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import sortBy from "lodash/sortBy"
|
|
2
2
|
import uniq from "lodash/uniq"
|
|
3
|
-
import { PropsWithChildren } from "react"
|
|
3
|
+
import type { PropsWithChildren } from "react"
|
|
4
4
|
import {
|
|
5
5
|
BulkDeleteButton,
|
|
6
6
|
BulkExportButton,
|
|
7
7
|
CreateButton,
|
|
8
8
|
DatagridConfigurable,
|
|
9
9
|
EditButton,
|
|
10
|
-
ExportButton,
|
|
11
10
|
FilterButton,
|
|
12
11
|
InfiniteList,
|
|
13
12
|
List,
|
|
14
13
|
Pagination,
|
|
15
14
|
SelectColumnsButton,
|
|
16
15
|
TopToolbar,
|
|
16
|
+
useTranslate,
|
|
17
17
|
} from "react-admin"
|
|
18
18
|
import { buildRA, buildRAComponent } from "./buildRA"
|
|
19
19
|
import { BulkActionButton, ListActionButton } from "./buttons"
|
|
20
20
|
import { HelpLink } from "./common/HelpLink"
|
|
21
21
|
import { useMuffinResourceOpts } from "./hooks"
|
|
22
|
-
import { AdminInput } from "./types"
|
|
22
|
+
import type { AdminInput } from "./types"
|
|
23
23
|
import { buildAdmin, findBuilder, setupAdmin } from "./utils"
|
|
24
24
|
|
|
25
25
|
export function MuffinList({ children }: PropsWithChildren) {
|
|
@@ -76,6 +76,7 @@ function MuffinListDatagrid() {
|
|
|
76
76
|
const { name, list } = useMuffinResourceOpts()
|
|
77
77
|
const { fields, edit, show } = list
|
|
78
78
|
const BulkActions = findBuilder(["list-actions", name])
|
|
79
|
+
|
|
79
80
|
return (
|
|
80
81
|
<DatagridConfigurable
|
|
81
82
|
rowClick={show ? "show" : edit ? "edit" : false}
|
|
@@ -91,9 +92,21 @@ setupAdmin(["list-grid"], MuffinListDatagrid)
|
|
|
91
92
|
|
|
92
93
|
function MuffinListGridButtons() {
|
|
93
94
|
const { list } = useMuffinResourceOpts()
|
|
95
|
+
const translate = useTranslate()
|
|
94
96
|
const { edit } = list
|
|
95
97
|
if (!edit) return null
|
|
96
|
-
return
|
|
98
|
+
return (
|
|
99
|
+
<EditButton
|
|
100
|
+
label=""
|
|
101
|
+
title={translate("ra.action.edit", { _: "Edit" })}
|
|
102
|
+
sx={{
|
|
103
|
+
minWidth: "auto",
|
|
104
|
+
width: "100%",
|
|
105
|
+
justifyContent: "flex-end",
|
|
106
|
+
"& .MuiButton-startIcon": { margin: 0 },
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
)
|
|
97
110
|
}
|
|
98
111
|
|
|
99
112
|
setupAdmin(["list-grid-buttons"], MuffinListGridButtons)
|
|
@@ -105,7 +118,6 @@ function MuffinListToolbar() {
|
|
|
105
118
|
list: { create },
|
|
106
119
|
} = useMuffinResourceOpts()
|
|
107
120
|
const actions = baseActions.filter((a) => a.view?.includes("list"))
|
|
108
|
-
const hasExport = actions.some((a) => a.id === "export")
|
|
109
121
|
return (
|
|
110
122
|
<TopToolbar>
|
|
111
123
|
{help && <HelpLink href={help} />}
|
|
@@ -115,7 +127,6 @@ function MuffinListToolbar() {
|
|
|
115
127
|
{actions.length
|
|
116
128
|
? actions.map((props) => <ListActionButton key={props.id} {...props} />)
|
|
117
129
|
: null}
|
|
118
|
-
{!hasExport && <ExportButton />}
|
|
119
130
|
</TopToolbar>
|
|
120
131
|
)
|
|
121
132
|
}
|
|
@@ -1,19 +1,86 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Box, Typography } from "@mui/material"
|
|
2
|
+
import type { PropsWithChildren } from "react"
|
|
3
|
+
import {
|
|
4
|
+
EditButton,
|
|
5
|
+
ListButton,
|
|
6
|
+
Show,
|
|
7
|
+
SimpleShowLayout,
|
|
8
|
+
TopToolbar,
|
|
9
|
+
useRecordContext,
|
|
10
|
+
useTranslate,
|
|
11
|
+
} from "react-admin"
|
|
12
|
+
import { useLocation, useNavigate } from "react-router-dom"
|
|
3
13
|
import { LinkAction } from "./actions"
|
|
4
14
|
import { buildRA } from "./buildRA"
|
|
5
15
|
import { ActionButton } from "./buttons"
|
|
16
|
+
import { AdminModal } from "./common"
|
|
6
17
|
import { useMuffinResourceOpts } from "./hooks"
|
|
7
18
|
import { buildAdmin, findBuilder, setupAdmin } from "./utils"
|
|
8
19
|
|
|
9
20
|
export function MuffinShow({ children }: PropsWithChildren) {
|
|
10
21
|
const { show, name, key } = useMuffinResourceOpts()
|
|
22
|
+
const location = useLocation()
|
|
23
|
+
const navigate = useNavigate()
|
|
11
24
|
const ShowToolbar = findBuilder(["show-toolbar", name])
|
|
25
|
+
const returnTo = new URLSearchParams(location.search).get("returnTo")
|
|
26
|
+
|
|
27
|
+
const handleClose = () => {
|
|
28
|
+
if (returnTo) {
|
|
29
|
+
navigate(returnTo)
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (window.history.length > 1) {
|
|
34
|
+
navigate(-1)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
navigate(`/${name}`)
|
|
39
|
+
}
|
|
12
40
|
|
|
13
41
|
return (
|
|
14
|
-
<Show
|
|
15
|
-
|
|
16
|
-
{
|
|
42
|
+
<Show
|
|
43
|
+
actions={false}
|
|
44
|
+
title={false}
|
|
45
|
+
component="div"
|
|
46
|
+
queryOptions={{ meta: { key } }}
|
|
47
|
+
sx={{
|
|
48
|
+
"& .RaShow-main": {
|
|
49
|
+
padding: 0,
|
|
50
|
+
},
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
<AdminModal
|
|
54
|
+
open={true}
|
|
55
|
+
onClose={handleClose}
|
|
56
|
+
title={<ShowToolbar />}
|
|
57
|
+
maxWidth="xl"
|
|
58
|
+
hideBackdrop
|
|
59
|
+
disableScrollLock
|
|
60
|
+
contentSx={{
|
|
61
|
+
paddingTop: "8px !important",
|
|
62
|
+
paddingBottom: "12px !important",
|
|
63
|
+
paddingLeft: "16px !important",
|
|
64
|
+
paddingRight: "16px !important",
|
|
65
|
+
}}
|
|
66
|
+
sx={{
|
|
67
|
+
"& .MuiDialog-paper": {
|
|
68
|
+
width: "min(1400px, 96vw)",
|
|
69
|
+
},
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
<SimpleShowLayout
|
|
73
|
+
sx={{
|
|
74
|
+
padding: 0,
|
|
75
|
+
"& .RaSimpleShowLayout-stack": {
|
|
76
|
+
rowGap: 1,
|
|
77
|
+
},
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
{buildAdmin(["show-fields", name], show)}
|
|
81
|
+
</SimpleShowLayout>
|
|
82
|
+
{children}
|
|
83
|
+
</AdminModal>
|
|
17
84
|
</Show>
|
|
18
85
|
)
|
|
19
86
|
}
|
|
@@ -21,21 +88,32 @@ export function MuffinShow({ children }: PropsWithChildren) {
|
|
|
21
88
|
setupAdmin(["show"], MuffinShow)
|
|
22
89
|
setupAdmin(["show-fields"], ({ fields }) => buildRA(fields))
|
|
23
90
|
|
|
24
|
-
export function MuffinShowToolbar(
|
|
91
|
+
export function MuffinShowToolbar() {
|
|
25
92
|
const { show, name } = useMuffinResourceOpts()
|
|
93
|
+
const record = useRecordContext()
|
|
94
|
+
const translate = useTranslate()
|
|
26
95
|
const { edit } = show
|
|
27
96
|
|
|
28
97
|
const Links = findBuilder(["show-links", name])
|
|
29
98
|
const Actions = findBuilder(["show-actions", name])
|
|
30
99
|
|
|
31
100
|
return (
|
|
32
|
-
<
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
101
|
+
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
|
102
|
+
<TopToolbar sx={{ pl: 0, pr: 0, width: "100%", justifyContent: "space-between" }}>
|
|
103
|
+
<Typography variant="h6" sx={{ mr: 2 }}>
|
|
104
|
+
{translate(`resources.${name}.name`, { smart_count: 1, _: name })}
|
|
105
|
+
{record?.id ? ` #${record.id}` : ""}
|
|
106
|
+
</Typography>
|
|
107
|
+
<Box sx={{ display: "flex", alignItems: "center" }}>
|
|
108
|
+
<Actions />
|
|
109
|
+
{edit && <EditButton />}
|
|
110
|
+
<ListButton />
|
|
111
|
+
</Box>
|
|
112
|
+
</TopToolbar>
|
|
113
|
+
<Box sx={{ mt: 0.5 }}>
|
|
114
|
+
<Links />
|
|
115
|
+
</Box>
|
|
116
|
+
</Box>
|
|
39
117
|
)
|
|
40
118
|
}
|
|
41
119
|
|
|
@@ -60,12 +138,12 @@ export function MuffinShowLinks({ children }: PropsWithChildren) {
|
|
|
60
138
|
const { show } = useMuffinResourceOpts()
|
|
61
139
|
const { links } = show
|
|
62
140
|
return (
|
|
63
|
-
<
|
|
141
|
+
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 1 }}>
|
|
64
142
|
{links.map(([key, props]) => (
|
|
65
143
|
<LinkAction key={key} resource={key} {...props} />
|
|
66
144
|
))}
|
|
67
145
|
{children}
|
|
68
|
-
</
|
|
146
|
+
</Box>
|
|
69
147
|
)
|
|
70
148
|
}
|
|
71
149
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useRecordContext, useResourceContext } from "react-admin"
|
|
2
2
|
import LinkButton from "../buttons/LinkButton"
|
|
3
3
|
import { useMuffinAdminOpts } from "../hooks"
|
|
4
|
-
import { AdminShowLink } from "../types"
|
|
4
|
+
import type { AdminShowLink } from "../types"
|
|
5
5
|
|
|
6
6
|
export function LinkAction({
|
|
7
7
|
field,
|
package/src/authprovider.ts
CHANGED
|
@@ -1,107 +1,94 @@
|
|
|
1
|
-
import Cookies from "js-cookie"
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import Cookies from "js-cookie"
|
|
2
|
+
import type { AdminOpts } from "./types"
|
|
3
|
+
import { findBuilder, makeRequest, requestHeaders, setupAdmin } from "./utils"
|
|
4
4
|
|
|
5
5
|
setupAdmin(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (storage == "localstorage") {
|
|
19
|
-
localStorage.setItem(name, value);
|
|
20
|
-
requestHeaders["Authorization"] = value;
|
|
21
|
-
} else Cookies.set(name, value);
|
|
22
|
-
},
|
|
23
|
-
);
|
|
6
|
+
["auth-storage-get"],
|
|
7
|
+
({ storage, storageName }: AdminOpts["auth"]) =>
|
|
8
|
+
() =>
|
|
9
|
+
storage == "localstorage" ? localStorage.getItem(storageName) : Cookies.get(storageName)
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
setupAdmin(["auth-storage-set"], ({ storage }) => (name, value, _requestOnly) => {
|
|
13
|
+
if (storage == "localstorage") {
|
|
14
|
+
localStorage.setItem(name, value)
|
|
15
|
+
requestHeaders.Authorization = value
|
|
16
|
+
} else Cookies.set(name, value)
|
|
17
|
+
})
|
|
24
18
|
|
|
25
19
|
export function MuffinAuthProvider(props: AdminOpts["auth"]) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
getIdentity,
|
|
99
|
-
|
|
100
|
-
getPermissions: async () => {
|
|
101
|
-
const role = authGet(storageName + "_role");
|
|
102
|
-
return role || "";
|
|
103
|
-
},
|
|
104
|
-
};
|
|
20
|
+
const { identityURL, authorizeURL, logoutURL, required, storageName, storage } = props
|
|
21
|
+
|
|
22
|
+
const authGet = findBuilder(["auth-storage-get"])(props)
|
|
23
|
+
const authSet = findBuilder(["auth-storage-set"])(props)
|
|
24
|
+
|
|
25
|
+
// Initialize request headers
|
|
26
|
+
if (storage == "localstorage") {
|
|
27
|
+
const token = authGet(storageName)
|
|
28
|
+
if (token) requestHeaders.Authorization = token
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const getIdentity = async () => {
|
|
32
|
+
if (identityURL) {
|
|
33
|
+
const { json } = await makeRequest(identityURL)
|
|
34
|
+
return json
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (required)
|
|
39
|
+
return {
|
|
40
|
+
login: async (data) => {
|
|
41
|
+
if (!authorizeURL) throw { message: "Authorization is not supported" }
|
|
42
|
+
|
|
43
|
+
const { json } = await makeRequest(authorizeURL, {
|
|
44
|
+
data,
|
|
45
|
+
method: "POST",
|
|
46
|
+
})
|
|
47
|
+
authSet(storageName, json)
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
checkError: async (error) => {
|
|
51
|
+
const { status } = error || {}
|
|
52
|
+
|
|
53
|
+
if (status == 401 || status == 403) {
|
|
54
|
+
// Clean storage
|
|
55
|
+
authSet(storageName, "")
|
|
56
|
+
|
|
57
|
+
throw {
|
|
58
|
+
message: "Invalid authorization",
|
|
59
|
+
redirectTo: logoutURL,
|
|
60
|
+
logoutUser: !logoutURL,
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
checkAuth: async () => {
|
|
66
|
+
const auth = authGet(storageName)
|
|
67
|
+
if (!auth) throw { message: "Authorization required" }
|
|
68
|
+
|
|
69
|
+
if (!identityURL) return auth
|
|
70
|
+
|
|
71
|
+
const user = await getIdentity()
|
|
72
|
+
if (!user) throw { message: "Authorization required" }
|
|
73
|
+
|
|
74
|
+
return user
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
getAuthorization: () => authGet(storageName),
|
|
78
|
+
|
|
79
|
+
logout: async () => {
|
|
80
|
+
// Clean storage
|
|
81
|
+
authSet(storageName, "")
|
|
82
|
+
if (logoutURL) window.location.href = logoutURL
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
getIdentity,
|
|
86
|
+
|
|
87
|
+
getPermissions: async () => {
|
|
88
|
+
const role = authGet(`${storageName}_role`)
|
|
89
|
+
return role || ""
|
|
90
|
+
},
|
|
91
|
+
}
|
|
105
92
|
}
|
|
106
93
|
|
|
107
|
-
setupAdmin(["auth-provider"], MuffinAuthProvider)
|
|
94
|
+
setupAdmin(["auth-provider"], MuffinAuthProvider)
|
package/src/buildRA.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ElementType } from "react"
|
|
1
2
|
import {
|
|
2
3
|
ArrayField,
|
|
3
4
|
ArrayInput,
|
|
@@ -25,8 +26,8 @@ import {
|
|
|
25
26
|
ReferenceField,
|
|
26
27
|
ReferenceInput,
|
|
27
28
|
ReferenceManyField,
|
|
28
|
-
required,
|
|
29
29
|
RichTextField,
|
|
30
|
+
required,
|
|
30
31
|
SearchInput,
|
|
31
32
|
SelectArrayInput,
|
|
32
33
|
SelectField,
|
|
@@ -40,7 +41,7 @@ import { AvatarField, CopyField, EditableBooleanField, FKField, JsonField } from
|
|
|
40
41
|
import { DateRangeFilter, Filter, SearchFilter } from "./filters"
|
|
41
42
|
import { FKInput, ImgInput, JsonInput, TimestampInput } from "./inputs"
|
|
42
43
|
|
|
43
|
-
const UI: Record<string,
|
|
44
|
+
const UI: Record<string, ElementType> = {
|
|
44
45
|
// Fields
|
|
45
46
|
BooleanField,
|
|
46
47
|
ChipField,
|
|
@@ -112,6 +113,6 @@ export function buildRA(items: [string, Record<string, any>][]) {
|
|
|
112
113
|
return items.map((item) => buildRAComponent(item[0], item[1]))
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
export function registerUI(name: string, component:
|
|
116
|
+
export function registerUI(name: string, component: ElementType) {
|
|
116
117
|
UI[name] = component
|
|
117
118
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Link, Stack } from "@mui/material"
|
|
2
|
+
import { useState } from "react"
|
|
1
3
|
import {
|
|
2
4
|
Button,
|
|
3
5
|
FormGroupsProvider,
|
|
@@ -8,15 +10,12 @@ import {
|
|
|
8
10
|
useTranslate,
|
|
9
11
|
} from "react-admin"
|
|
10
12
|
import { FormProvider } from "react-hook-form"
|
|
11
|
-
|
|
12
|
-
import { Link, Stack } from "@mui/material"
|
|
13
|
-
import { useState } from "react"
|
|
14
13
|
import { buildRA } from "../buildRA"
|
|
15
14
|
import { AdminModal, PayloadButtons, useConfirmation } from "../common"
|
|
16
15
|
import { HelpLink } from "../common/HelpLink"
|
|
17
16
|
import { useMuffinAdminOpts } from "../hooks"
|
|
18
17
|
import { useAction } from "../hooks/useAction"
|
|
19
|
-
import { AdminAction, AdminPayloadProps } from "../types"
|
|
18
|
+
import type { AdminAction, AdminPayloadProps } from "../types"
|
|
20
19
|
import { buildIcon, findBuilder, prepareFilters, requestHeaders } from "../utils"
|
|
21
20
|
|
|
22
21
|
export type ActionPayloadProps = {
|
|
@@ -160,11 +159,11 @@ export function CommonPayload({
|
|
|
160
159
|
title={
|
|
161
160
|
help
|
|
162
161
|
? ((
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
162
|
+
<Stack sx={{ alignItems: "flex-start" }}>
|
|
163
|
+
<span>{translate(title, { _: title })}</span>
|
|
164
|
+
<HelpLink href={help} style={{ alignSelf: "flex-end" }} />
|
|
165
|
+
</Stack>
|
|
166
|
+
) as any)
|
|
168
167
|
: translate(title, { _: title })
|
|
169
168
|
}
|
|
170
169
|
actions={
|
|
@@ -198,7 +197,7 @@ function FileButton({
|
|
|
198
197
|
const { apiUrl } = useMuffinAdminOpts()
|
|
199
198
|
let url = `${apiUrl}${path}?f`
|
|
200
199
|
if (record) url = url.replace("{id}", record.id as string)
|
|
201
|
-
const authorization = requestHeaders
|
|
200
|
+
const authorization = requestHeaders.Authorization
|
|
202
201
|
if (authorization) url += `&t=${authorization}`
|
|
203
202
|
if (Object.keys(filterValues).length) url += `&where=${prepareFilters(filterValues)}`
|
|
204
203
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Button } from "react-admin"
|
|
2
|
-
import { Link } from "react-router-dom"
|
|
3
|
-
import { AdminShowLink } from "../types"
|
|
2
|
+
import { Link, useLocation } from "react-router-dom"
|
|
3
|
+
import type { AdminShowLink } from "../types"
|
|
4
4
|
import { buildIcon } from "../utils"
|
|
5
5
|
|
|
6
6
|
const LinkButton = ({
|
|
@@ -11,9 +11,17 @@ const LinkButton = ({
|
|
|
11
11
|
id,
|
|
12
12
|
title,
|
|
13
13
|
}: AdminShowLink & { resource: string; filters?: any; id?: string }) => {
|
|
14
|
+
const location = useLocation()
|
|
15
|
+
const searchParams = new URLSearchParams()
|
|
16
|
+
searchParams.set("returnTo", `${location.pathname}${location.search}`)
|
|
17
|
+
|
|
18
|
+
if (filters) {
|
|
19
|
+
searchParams.set("filter", JSON.stringify(filters))
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
const linkParams = {
|
|
15
23
|
pathname: `/${resource}`,
|
|
16
|
-
search:
|
|
24
|
+
search: `?${searchParams.toString()}`,
|
|
17
25
|
}
|
|
18
26
|
|
|
19
27
|
if (id) linkParams.pathname += `/${id}/show`
|