muffin-admin 6.1.3 → 7.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/types.d.ts CHANGED
@@ -2,7 +2,7 @@ export type TID = string | number;
2
2
  export type TProps = {
3
3
  [key: string]: any;
4
4
  };
5
- export type TAdminType = "action-form" | "admin" | "appbar" | "auth-provider" | "auth-storage-get" | "auth-storage-set" | "create" | "create-toolbar" | "create-inputs" | "dashboard" | "dashboard-actions" | "dashboard-content" | "data-provider" | "edit" | "edit-actions" | "edit-toolbar" | "edit-inputs" | "layout" | "list" | "list-actions" | "list-toolbar" | "list-grid" | "list-grid-buttons" | "list-filters" | "list-fields" | "locale" | "loginpage" | "resource" | "show" | "show-actions" | "show-toolbar" | "show-fields" | "show-links";
5
+ export type TAdminType = "action-form" | "admin" | "appbar" | "auth-provider" | "auth-storage-get" | "auth-storage-set" | "create" | "create-toolbar" | "create-inputs" | "dashboard" | "dashboard-actions" | "dashboard-content" | "data-provider" | "edit" | "edit-actions" | "edit-toolbar" | "edit-form-toolbar" | "edit-inputs" | "layout" | "list" | "list-actions" | "list-toolbar" | "list-grid" | "list-grid-buttons" | "list-filters" | "list-fields" | "locale" | "loginpage" | "resource" | "show" | "show-actions" | "show-toolbar" | "show-fields" | "show-links";
6
6
  export type TAdminPath = [type: TAdminType, ...ids: string[]];
7
7
  export type AdminAction = {
8
8
  view?: ("show" | "edit" | "list" | "bulk")[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muffin-admin",
3
- "version": "6.1.3",
3
+ "version": "7.0.0",
4
4
  "description": "NPM package for https://github.com/klen/muffin-admin",
5
5
  "files": [
6
6
  "src",
@@ -23,10 +23,11 @@
23
23
  },
24
24
  "homepage": "https://github.com/klen/muffin-admin#readme",
25
25
  "dependencies": {
26
+ "@mui/x-date-pickers": "^7.22.3",
26
27
  "js-cookie": "^3.0.5",
27
28
  "ra-language-russian": "^4.14.2",
28
29
  "react": "^18.3.1",
29
- "react-admin": "^5.0.3",
30
+ "react-admin": "^5.4.0",
30
31
  "react-dom": "^18.3.1",
31
32
  "validator": "^13.12.0"
32
33
  },
@@ -7,15 +7,20 @@ export function MuffinResource({ name, ...props }: ResourceProps) {
7
7
  const opts = adminOpts?.resources.find((r) => r.name == name)
8
8
  if (!opts) return null
9
9
 
10
+ const Create = findBuilder(["create", name])
11
+ const Edit = findBuilder(["edit", name])
12
+ const List = findBuilder(["list", name])
13
+ const Show = findBuilder(["show", name])
14
+
10
15
  return (
11
16
  <Resource
12
17
  key={name}
13
18
  name={name}
14
19
  icon={findIcon(opts.icon)}
15
- create={findBuilder(["create", name])}
16
- edit={findBuilder(["edit", name])}
17
- list={findBuilder(["list", name])}
18
- show={findBuilder(["show", name])}
20
+ create={Create}
21
+ edit={Edit}
22
+ list={List}
23
+ show={Show}
19
24
  {...props}
20
25
  />
21
26
  )
@@ -13,43 +13,56 @@ import {
13
13
  import { buildRA } from "./buildRA"
14
14
  import { ActionButton } from "./buttons"
15
15
  import { useMuffinAdminOpts, useMuffinResourceOpts } from "./hooks"
16
- import { buildAdmin, findBuilder, setupAdmin } from "./utils"
16
+ import { findBuilder, setupAdmin } from "./utils"
17
17
 
18
18
  export function MuffinEdit({ children }: PropsWithChildren) {
19
19
  const {
20
20
  adminProps: { mutationMode = "optimistic" },
21
21
  } = useMuffinAdminOpts()
22
+
22
23
  const { edit, name } = useMuffinResourceOpts()
23
24
  if (!edit) return null
24
25
 
25
- const { inputs, remove } = edit
26
- const ActionsToolbar = findBuilder(["edit-toolbar", name])
26
+ const Actions = findBuilder(["edit-toolbar", name])
27
+ const Inputs = findBuilder(["edit-inputs", name])
28
+ const FormToolbar = findBuilder(["edit-form-toolbar", name])
27
29
 
28
30
  return (
29
- <Edit actions={<ActionsToolbar />} mutationMode={mutationMode}>
31
+ <Edit actions={<Actions />} mutationMode={mutationMode}>
30
32
  {children}
31
- <SimpleForm
32
- toolbar={
33
- <Toolbar>
34
- <SaveButton />
35
- {remove && (
36
- <DeleteButton
37
- sx={{
38
- marginLeft: "auto",
39
- }}
40
- />
41
- )}
42
- </Toolbar>
43
- }
44
- >
45
- {buildAdmin(["edit-inputs", name], inputs)}
33
+ <SimpleForm toolbar={<FormToolbar />}>
34
+ <Inputs />
46
35
  </SimpleForm>
47
36
  </Edit>
48
37
  )
49
38
  }
50
39
 
51
40
  setupAdmin(["edit"], MuffinEdit)
52
- setupAdmin(["edit-inputs"], buildRA)
41
+
42
+ export function MuffinEditFormToolbar() {
43
+ const { edit } = useMuffinResourceOpts()
44
+ if (!edit) return null
45
+
46
+ const { remove } = edit
47
+ return (
48
+ <Toolbar>
49
+ <SaveButton />
50
+ {remove && <DeleteButton sx={{ marginLeft: "auto" }} />}
51
+ </Toolbar>
52
+ )
53
+ }
54
+
55
+ setupAdmin(["edit-form-toolbar"], MuffinEditFormToolbar)
56
+
57
+ export function MuffinEditInputs() {
58
+ const { edit } = useMuffinResourceOpts()
59
+ if (!edit) return null
60
+
61
+ const { inputs } = edit
62
+
63
+ return <>{buildRA(inputs)}</>
64
+ }
65
+ setupAdmin(["edit-inputs"], MuffinEditInputs)
53
66
 
54
67
  export function MuffinEditToolbar({ children }: PropsWithChildren) {
55
68
  const resource = useResourceContext()
@@ -15,25 +15,27 @@ import {
15
15
  SelectColumnsButton,
16
16
  TopToolbar,
17
17
  } from "react-admin"
18
- import { buildRA } from "./buildRA"
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
23
  import { buildAdmin, findBuilder, setupAdmin } from "./utils"
23
24
 
24
25
  export function MuffinList({ children }: PropsWithChildren) {
25
26
  const { name, list } = useMuffinResourceOpts()
26
- const { limit, limitMax, limitTotal, filters, sort } = list
27
+ const { limit, limitMax, limitTotal, sort, filters } = list
27
28
 
28
29
  const DataGrid = findBuilder(["list-grid", name])
29
30
  const Toolbar = findBuilder(["list-toolbar", name])
31
+ const raFilters = buildAdmin(["list-filters", name], filters)
30
32
 
31
33
  return limitTotal ? (
32
34
  <List
33
35
  sort={sort}
34
36
  perPage={limit}
37
+ filters={raFilters}
35
38
  actions={<Toolbar />}
36
- filters={buildAdmin(["list-filters", name], filters)}
37
39
  pagination={
38
40
  <Pagination rowsPerPageOptions={sortBy(uniq([10, 25, 50, 100, limit, limitMax]))} />
39
41
  }
@@ -42,12 +44,7 @@ export function MuffinList({ children }: PropsWithChildren) {
42
44
  <DataGrid />
43
45
  </List>
44
46
  ) : (
45
- <InfiniteList
46
- sort={sort}
47
- perPage={limit}
48
- actions={<Toolbar />}
49
- filters={buildAdmin(["list-filters", name], filters)}
50
- >
47
+ <InfiniteList sort={sort} perPage={limit} actions={<Toolbar />} filters={raFilters}>
51
48
  {children}
52
49
  <DataGrid />
53
50
  </InfiniteList>
@@ -56,7 +53,17 @@ export function MuffinList({ children }: PropsWithChildren) {
56
53
 
57
54
  setupAdmin(["list"], MuffinList)
58
55
  setupAdmin(["list-fields"], buildRA)
59
- setupAdmin(["list-filters"], buildRA)
56
+
57
+ function muffinListFilters(filters: AdminInput[]) {
58
+ return filters.map((props) => {
59
+ const [rtype, opts] = props
60
+ return buildRAComponent(rtype, {
61
+ ...opts,
62
+ variant: "outlined",
63
+ })
64
+ })
65
+ }
66
+ setupAdmin(["list-filters"], muffinListFilters)
60
67
 
61
68
  function MuffinListDatagrid() {
62
69
  const { name, list } = useMuffinResourceOpts()
@@ -125,7 +132,3 @@ function MuffinListActions() {
125
132
  }
126
133
 
127
134
  setupAdmin(["list-actions"], MuffinListActions)
128
-
129
- function MuffinPagination() {
130
- return null
131
- }
@@ -8,17 +8,17 @@ import { buildAdmin, findBuilder, setupAdmin } from "./utils"
8
8
 
9
9
  export function MuffinShow({ children }: PropsWithChildren) {
10
10
  const { show, name } = useMuffinResourceOpts()
11
- const ActionsToolbar = findBuilder(["show-toolbar", name])
11
+ const ShowToolbar = findBuilder(["show-toolbar", name])
12
12
 
13
13
  return (
14
- <Show actions={<ActionsToolbar />}>
14
+ <Show actions={<ShowToolbar />}>
15
15
  {children}
16
16
  <SimpleShowLayout>{buildAdmin(["show-fields", name], show)}</SimpleShowLayout>
17
17
  </Show>
18
18
  )
19
19
  }
20
20
 
21
- setupAdmin(["show"], (props) => <MuffinShow {...props} />)
21
+ setupAdmin(["show"], MuffinShow)
22
22
  setupAdmin(["show-fields"], ({ fields }) => buildRA(fields))
23
23
 
24
24
  export function MuffinShowToolbar({ children }: PropsWithChildren) {
package/src/buildRA.tsx CHANGED
@@ -25,17 +25,18 @@ import {
25
25
  ReferenceField,
26
26
  ReferenceInput,
27
27
  ReferenceManyField,
28
+ required,
28
29
  RichTextField,
30
+ SelectArrayInput,
29
31
  SelectField,
30
32
  SelectInput,
31
33
  TextField,
32
34
  TextInput,
33
35
  UrlField,
34
- required,
35
36
  } from "react-admin"
36
37
 
37
38
  import { AvatarField, EditableBooleanField, FKField, JsonField } from "./fields"
38
- import { FKInput, JsonInput } from "./inputs"
39
+ import { FilterInput, FKInput, JsonInput } from "./inputs"
39
40
 
40
41
  const UI: Record<string, JSX.ElementType> = {
41
42
  // Fields
@@ -60,25 +61,27 @@ const UI: Record<string, JSX.ElementType> = {
60
61
  JsonField,
61
62
 
62
63
  // Inputs
64
+ ArrayInput,
65
+ AutocompleteArrayInput,
66
+ AutocompleteInput,
63
67
  BooleanInput,
64
- NullableBooleanInput,
68
+ CheckboxGroupInput,
65
69
  DateInput,
70
+ FilterInput,
66
71
  DateTimeInput,
67
- ImageInput,
72
+ FKInput,
68
73
  FileInput,
74
+ ImageInput,
75
+ JsonInput,
76
+ NullableBooleanInput,
69
77
  NumberInput,
70
78
  PasswordInput,
71
- TextInput,
72
- AutocompleteInput,
73
79
  RadioButtonGroupInput,
74
- SelectInput,
75
- ArrayInput,
76
- AutocompleteArrayInput,
77
- CheckboxGroupInput,
78
80
  ReferenceArrayInput,
79
81
  ReferenceInput,
80
- JsonInput,
81
- FKInput,
82
+ SelectInput,
83
+ TextInput,
84
+ SelectArrayInput,
82
85
  }
83
86
 
84
87
  export function buildRAComponent(name: string, props: any) {
@@ -13,7 +13,14 @@ export function MuffinDataprovider(apiUrl: string) {
13
13
  getList: async (resource: string, params: any) => {
14
14
  const { filter, pagination, sort } = params
15
15
  const query: APIParams["query"] = {}
16
- if (filter) query.where = JSON.stringify(filter)
16
+ if (filter) {
17
+ query.where = JSON.stringify(
18
+ Object.entries(filter).reduce((acc, [key, value]) => {
19
+ acc[key] = Array.isArray(value) ? { $in: value } : value
20
+ return acc
21
+ }, {})
22
+ )
23
+ }
17
24
  if (pagination) {
18
25
  const { page, perPage: limit } = pagination
19
26
  query.limit = limit
@@ -85,7 +92,7 @@ export function MuffinDataprovider(apiUrl: string) {
85
92
  return await methods.getList(resource, { filter, ...opts })
86
93
  },
87
94
 
88
- runAction: async (resource: string, action: string, props: TActionProps) => {
95
+ runAction: async (_: string, action: string, props: TActionProps) => {
89
96
  const { payload, ids, record } = props
90
97
  action = action.replace(/^\/+/, "")
91
98
  if (record) {
package/src/i18n/ru.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import { TranslationMessages } from "ra-core"
2
2
  import raRussianMessages from "ra-language-russian"
3
3
 
4
- const muffinRussianMessages: TranslationMessages = {
4
+ const muffinRussianMessages = {
5
5
  ...raRussianMessages,
6
6
  muffin: {
7
7
  instructions: "Инструкции",
8
8
  how_to_use_admin: "Как использовать админку?",
9
9
  },
10
- }
10
+ } as unknown as TranslationMessages
11
11
 
12
12
  export default muffinRussianMessages
@@ -0,0 +1,67 @@
1
+ import Button from "@mui/material/Button"
2
+ import FormGroup from "@mui/material/FormGroup"
3
+ import Menu from "@mui/material/Menu"
4
+ import MenuItem from "@mui/material/MenuItem"
5
+ import TextField from "@mui/material/TextField"
6
+ import { useState } from "react"
7
+ import { useInput } from "react-admin"
8
+
9
+ const OPERATORS = {
10
+ $eq: "=",
11
+ $ne: "≠",
12
+ $gt: ">",
13
+ $ge: "≥",
14
+ $lt: "<",
15
+ $le: "≤",
16
+ }
17
+
18
+ export function FilterInput({ type = "text", ...props }: any) {
19
+ const { field } = useInput({
20
+ format: (value: any) => (value ? value[Object.keys(value)[0]] : ""),
21
+ ...props,
22
+ })
23
+ const [op, setOp] = useState((field.value && Object.keys(field.value)[0]) || "$eq")
24
+
25
+ const onChange = function(e: any) {
26
+ field.onChange({ [op]: e.target.value })
27
+ }
28
+
29
+ const [menuEl, setMenuEl] = useState(null)
30
+ const open = Boolean(menuEl)
31
+ return (
32
+ <>
33
+ <FormGroup row sx={{ flexWrap: "nowrap" }}>
34
+ <Button
35
+ size="small"
36
+ disableElevation
37
+ sx={{ minWidth: 30 }}
38
+ onClick={(e) => setMenuEl(e.currentTarget)}
39
+ >
40
+ <span style={{ fontSize: "1.5em" }}>{OPERATORS[op]}</span>
41
+ </Button>
42
+ <TextField
43
+ {...field}
44
+ onChange={onChange}
45
+ value={field.value}
46
+ variant="outlined"
47
+ type={type}
48
+ />
49
+ </FormGroup>
50
+ <Menu open={open} onClose={() => setMenuEl(null)} anchorEl={menuEl}>
51
+ {Object.entries(OPERATORS).map(([key, value]) => (
52
+ <MenuItem
53
+ key={key}
54
+ selected={op === value}
55
+ onClick={() => {
56
+ setOp(key)
57
+ setMenuEl(null)
58
+ field.onChange({ [key]: field.value })
59
+ }}
60
+ >
61
+ {value}
62
+ </MenuItem>
63
+ ))}
64
+ </Menu>
65
+ </>
66
+ )
67
+ }
@@ -1,2 +1,3 @@
1
+ export * from "./FilterInput"
1
2
  export * from "./FKInput"
2
3
  export * from "./JsonInput"
package/src/types.ts CHANGED
@@ -21,6 +21,7 @@ export type TAdminType =
21
21
  | "edit"
22
22
  | "edit-actions"
23
23
  | "edit-toolbar"
24
+ | "edit-form-toolbar"
24
25
  | "edit-inputs"
25
26
  | "layout"
26
27
  | "list"
@@ -54,7 +55,7 @@ export type AdminAction = {
54
55
  }
55
56
 
56
57
  export type AdminField = [string, { source: string }]
57
- export type AdminInput = [string, { required: boolean; source: string;[key: string]: any }]
58
+ export type AdminInput = [string, { required: boolean; source: string; [key: string]: any }]
58
59
 
59
60
  export type AdminShowLink = {
60
61
  label: string
@@ -85,11 +86,11 @@ export type AdminResourceProps = {
85
86
  actions: AdminAction[]
86
87
  create: AdminInput[] | false
87
88
  edit:
88
- | {
89
- inputs: AdminInput[]
90
- remove?: boolean
91
- }
92
- | false
89
+ | {
90
+ inputs: AdminInput[]
91
+ remove?: boolean
92
+ }
93
+ | false
93
94
  list: {
94
95
  limit: number
95
96
  limitMax: number