muffin-admin 1.44.4 → 1.45.1

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.
Files changed (142) hide show
  1. package/dist/MuffinAdmin.d.ts +2 -0
  2. package/dist/MuffinAdmin.js +25 -0
  3. package/dist/MuffinDashboard.d.ts +2 -0
  4. package/dist/MuffinDashboard.js +16 -0
  5. package/dist/MuffinResource.d.ts +2 -0
  6. package/dist/MuffinResource.js +19 -0
  7. package/dist/MuffinResourceCreate.d.ts +4 -0
  8. package/dist/MuffinResourceCreate.js +11 -0
  9. package/dist/MuffinResourceEdit.d.ts +2 -0
  10. package/dist/MuffinResourceEdit.js +26 -0
  11. package/dist/MuffinResourceList.d.ts +2 -0
  12. package/dist/MuffinResourceList.js +16 -0
  13. package/dist/MuffinResourceShow.d.ts +2 -0
  14. package/dist/MuffinResourceShow.js +13 -0
  15. package/dist/actions/LinkAction.d.ts +4 -0
  16. package/dist/actions/LinkAction.js +10 -0
  17. package/dist/actions/index.d.ts +1 -0
  18. package/dist/actions/index.js +1 -0
  19. package/dist/authprovider.d.ts +9 -0
  20. package/dist/authprovider.js +80 -0
  21. package/dist/buildRA.d.ts +2 -0
  22. package/dist/buildRA.js +63 -0
  23. package/dist/buttons/ActionButton.d.ts +7 -0
  24. package/dist/buttons/ActionButton.js +51 -0
  25. package/dist/buttons/LinkButton.d.ts +6 -0
  26. package/dist/buttons/LinkButton.js +12 -0
  27. package/dist/buttons/index.d.ts +2 -0
  28. package/dist/buttons/index.js +2 -0
  29. package/dist/dataprovider.d.ts +59 -0
  30. package/dist/dataprovider.js +106 -0
  31. package/dist/fields/AvatarField.d.ts +13 -0
  32. package/dist/fields/AvatarField.js +33 -0
  33. package/dist/fields/EditableBooleanField.d.ts +3 -0
  34. package/dist/fields/EditableBooleanField.js +5 -0
  35. package/dist/fields/FKField.d.ts +14 -0
  36. package/dist/fields/FKField.js +21 -0
  37. package/dist/fields/JsonField.d.ts +4 -0
  38. package/dist/fields/JsonField.js +11 -0
  39. package/dist/fields/index.d.ts +4 -0
  40. package/dist/fields/index.js +4 -0
  41. package/dist/hooks/useAction.d.ts +5 -0
  42. package/dist/hooks/useAction.js +6 -0
  43. package/dist/index.d.ts +14 -0
  44. package/dist/index.js +35 -0
  45. package/dist/inputs/FKInput.d.ts +8 -0
  46. package/dist/inputs/FKInput.js +27 -0
  47. package/dist/inputs/JsonInput.d.ts +19 -0
  48. package/dist/inputs/JsonInput.js +69 -0
  49. package/dist/inputs/index.d.ts +2 -0
  50. package/dist/inputs/index.js +2 -0
  51. package/dist/tsconfig.tsbuildinfo +1 -0
  52. package/dist/types.d.ts +76 -0
  53. package/dist/types.js +1 -0
  54. package/dist/utils.d.ts +28 -0
  55. package/dist/utils.js +57 -0
  56. package/dist/web.d.ts +1 -0
  57. package/dist/web.js +3 -0
  58. package/package.json +31 -14
  59. package/src/MuffinAdmin.tsx +73 -0
  60. package/src/MuffinDashboard.tsx +52 -0
  61. package/src/MuffinResource.tsx +27 -0
  62. package/src/MuffinResourceCreate.tsx +24 -0
  63. package/src/MuffinResourceEdit.tsx +42 -0
  64. package/src/MuffinResourceList.tsx +46 -0
  65. package/src/MuffinResourceShow.tsx +39 -0
  66. package/src/actions/LinkAction.tsx +10 -0
  67. package/src/actions/index.ts +1 -0
  68. package/src/{authprovider.js → authprovider.ts} +27 -22
  69. package/src/{ui.jsx → buildRA.tsx} +30 -35
  70. package/src/buttons/{ActionButton.jsx → ActionButton.tsx} +18 -30
  71. package/src/buttons/{LinkButton.jsx → LinkButton.tsx} +11 -6
  72. package/src/buttons/index.ts +2 -0
  73. package/src/{dataprovider.js → dataprovider.ts} +8 -6
  74. package/src/fields/{AvatarField.jsx → AvatarField.tsx} +3 -7
  75. package/src/fields/EditableBooleanField.tsx +5 -0
  76. package/src/fields/{FKField.jsx → FKField.tsx} +2 -10
  77. package/src/fields/{JsonField.jsx → JsonField.tsx} +2 -3
  78. package/src/fields/index.ts +4 -0
  79. package/src/hooks/{useAction.js → useAction.ts} +2 -2
  80. package/src/index.tsx +25 -0
  81. package/src/inputs/FKInput.tsx +27 -0
  82. package/src/inputs/{JsonInput.jsx → JsonInput.tsx} +3 -4
  83. package/src/inputs/index.ts +2 -0
  84. package/src/types.ts +73 -0
  85. package/src/utils.tsx +69 -0
  86. package/src/web.ts +4 -0
  87. package/dist/cjs/admin.js +0 -80
  88. package/dist/cjs/authprovider.js +0 -214
  89. package/dist/cjs/buttons/ActionButton.js +0 -96
  90. package/dist/cjs/buttons/LinkButton.js +0 -34
  91. package/dist/cjs/dashboard.js +0 -63
  92. package/dist/cjs/dataprovider.js +0 -304
  93. package/dist/cjs/fields/AvatarField.js +0 -37
  94. package/dist/cjs/fields/EditableBooleanField.js +0 -25
  95. package/dist/cjs/fields/FKField.js +0 -35
  96. package/dist/cjs/fields/JsonField.js +0 -38
  97. package/dist/cjs/hooks/useAction.js +0 -14
  98. package/dist/cjs/index.js +0 -76
  99. package/dist/cjs/inputs/FKInput.js +0 -41
  100. package/dist/cjs/inputs/JsonInput.js +0 -70
  101. package/dist/cjs/resources.js +0 -52
  102. package/dist/cjs/ui.js +0 -76
  103. package/dist/cjs/utils.js +0 -49
  104. package/dist/cjs/views/create.js +0 -18
  105. package/dist/cjs/views/edit.js +0 -31
  106. package/dist/cjs/views/list.js +0 -67
  107. package/dist/cjs/views/show.js +0 -37
  108. package/dist/cjs/web.js +0 -4
  109. package/dist/esm/admin.js +0 -74
  110. package/dist/esm/authprovider.js +0 -206
  111. package/dist/esm/buttons/ActionButton.js +0 -83
  112. package/dist/esm/buttons/LinkButton.js +0 -23
  113. package/dist/esm/dashboard.js +0 -60
  114. package/dist/esm/dataprovider.js +0 -296
  115. package/dist/esm/fields/AvatarField.js +0 -29
  116. package/dist/esm/fields/EditableBooleanField.js +0 -17
  117. package/dist/esm/fields/FKField.js +0 -27
  118. package/dist/esm/fields/JsonField.js +0 -28
  119. package/dist/esm/hooks/useAction.js +0 -8
  120. package/dist/esm/index.js +0 -42
  121. package/dist/esm/inputs/FKInput.js +0 -33
  122. package/dist/esm/inputs/JsonInput.js +0 -63
  123. package/dist/esm/resources.js +0 -47
  124. package/dist/esm/ui.js +0 -70
  125. package/dist/esm/utils.js +0 -38
  126. package/dist/esm/views/create.js +0 -16
  127. package/dist/esm/views/edit.js +0 -29
  128. package/dist/esm/views/list.js +0 -64
  129. package/dist/esm/views/show.js +0 -35
  130. package/dist/esm/web.js +0 -2
  131. package/src/admin.jsx +0 -78
  132. package/src/dashboard.jsx +0 -74
  133. package/src/fields/EditableBooleanField.jsx +0 -11
  134. package/src/index.js +0 -16
  135. package/src/inputs/FKInput.jsx +0 -42
  136. package/src/resources.jsx +0 -39
  137. package/src/utils.js +0 -38
  138. package/src/views/create.jsx +0 -30
  139. package/src/views/edit.jsx +0 -48
  140. package/src/views/list.jsx +0 -86
  141. package/src/views/show.jsx +0 -51
  142. package/src/web.js +0 -3
@@ -0,0 +1,76 @@
1
+ export type TProps = {
2
+ [key: string]: any;
3
+ };
4
+ export type AdminAction = {
5
+ view: string;
6
+ icon: string;
7
+ action: string;
8
+ title: string;
9
+ label: string;
10
+ };
11
+ export type AdminField = [string, {
12
+ source: string;
13
+ }];
14
+ export type AdminInput = [string, {
15
+ required: boolean;
16
+ source: string;
17
+ }];
18
+ export type AdminShowLink = {
19
+ label: string;
20
+ title?: string;
21
+ icon?: string;
22
+ };
23
+ export type AdminShowProps = {
24
+ actions: AdminAction[];
25
+ fields: AdminField[];
26
+ links: Record<string, AdminShowLink>;
27
+ };
28
+ export type AdminResourceProps = {
29
+ name: string;
30
+ icon?: string;
31
+ label: string;
32
+ create: AdminInput[];
33
+ edit: {
34
+ actions: AdminAction[];
35
+ inputs: AdminInput[];
36
+ };
37
+ list: {
38
+ limit: number;
39
+ limitMax: number;
40
+ actions: AdminAction[];
41
+ fields: AdminField[];
42
+ filters: AdminInput[];
43
+ show: boolean;
44
+ edit?: boolean;
45
+ };
46
+ show: AdminShowProps;
47
+ };
48
+ export type AdminDashboardBlock = {
49
+ title: string;
50
+ value: any;
51
+ };
52
+ export type AdminOpts = {
53
+ adminProps: {
54
+ title: string;
55
+ disableTelemetry?: boolean;
56
+ mutationMode?: "optimistic" | "pessimistic" | "undoable";
57
+ };
58
+ apiUrl: string;
59
+ appBarLinks: [{
60
+ ulr: string;
61
+ title: string;
62
+ icon?: string;
63
+ }];
64
+ auth: {
65
+ identityURL: string;
66
+ authorizeURL: string;
67
+ loginURL?: string;
68
+ logoutURL?: string;
69
+ required: boolean;
70
+ storage: "localstorage" | "cookie";
71
+ storageName: string;
72
+ };
73
+ dashboard: AdminDashboardBlock[] | AdminDashboardBlock;
74
+ resources: AdminResourceProps[];
75
+ version: string;
76
+ };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ /// <reference types="react" />
2
+ import { AdminOpts } from "./types";
3
+ export declare function setupAdmin<T>(adminType: string[], fc: (props: any) => T): void;
4
+ export declare function buildAdmin(adminType: string[], props?: {}): any;
5
+ export declare function findBuilder(adminType: string[]): (props: any) => any;
6
+ export declare const AdminPropsContext: import("react").Context<AdminOpts>;
7
+ export type APIParams = {
8
+ method?: string;
9
+ query?: {
10
+ where?: string;
11
+ limit?: number;
12
+ offset?: number;
13
+ sort?: string;
14
+ ids?: string[];
15
+ };
16
+ data?: any;
17
+ headers?: any;
18
+ body?: string;
19
+ };
20
+ export declare const requestHeaders: {};
21
+ export declare function makeRequest(url: string, params?: APIParams): Promise<{
22
+ status: number;
23
+ headers: Headers;
24
+ body: string;
25
+ json: any;
26
+ }>;
27
+ export declare function findIcon(icon?: string): any;
28
+ export declare function buildIcon(icon?: string): import("react/jsx-runtime").JSX.Element;
package/dist/utils.js ADDED
@@ -0,0 +1,57 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { jsx as _jsx } from "react/jsx-runtime";
13
+ import * as icons from "@mui/icons-material";
14
+ import { stringify } from "query-string";
15
+ import { fetchUtils } from "ra-core";
16
+ import { createContext } from "react";
17
+ const builders = new Map();
18
+ export function setupAdmin(adminType, fc) {
19
+ const key = adminType.join("/");
20
+ builders.set(key, fc);
21
+ }
22
+ export function buildAdmin(adminType, props = {}) {
23
+ if (process.env.NODE_ENV == "development")
24
+ console.log(adminType, props);
25
+ const builder = findBuilder(adminType);
26
+ if (builder)
27
+ return builder(props);
28
+ console.warn(`No admin renderer found for ${adminType.join("/")}`);
29
+ return null;
30
+ }
31
+ export function findBuilder(adminType) {
32
+ const key = [...adminType];
33
+ while (key.length) {
34
+ const render = builders.get(key.join("/"));
35
+ if (render)
36
+ return render;
37
+ key.pop();
38
+ }
39
+ }
40
+ export const AdminPropsContext = createContext(null);
41
+ export const requestHeaders = {};
42
+ export function makeRequest(url, params = {}) {
43
+ const _a = params || {}, { data, query } = _a, opts = __rest(_a, ["data", "query"]);
44
+ if (data)
45
+ opts.body = JSON.stringify(data);
46
+ if (query)
47
+ url = `${url}?${stringify(query)}`;
48
+ return fetchUtils.fetchJson(url, Object.assign(Object.assign({}, opts), { headers: new Headers(Object.assign(Object.assign({}, requestHeaders), opts.headers)) }));
49
+ }
50
+ export function findIcon(icon) {
51
+ return icon ? icons[icon] : undefined;
52
+ }
53
+ export function buildIcon(icon) {
54
+ const Icon = findIcon(icon);
55
+ if (Icon)
56
+ return _jsx(Icon, {});
57
+ }
package/dist/web.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/web.js ADDED
@@ -0,0 +1,3 @@
1
+ import { initAdmin } from ".";
2
+ // @ts-ignore
3
+ globalThis.initAdmin = initAdmin;
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "muffin-admin",
3
- "version": "1.44.4",
3
+ "version": "1.45.1",
4
4
  "description": "NPM package for https://github.com/klen/muffin-admin",
5
5
  "files": [
6
6
  "src",
7
7
  "dist"
8
8
  ],
9
- "main": "dist/cjs/index.js",
10
- "module": "dist/esm/index.js",
9
+ "main": "dist/index.js",
10
+ "types": "dist/index.d.js",
11
11
  "scripts": {
12
+ "build": "tsc",
12
13
  "test": "echo \"Error: no test specified\" && exit 1"
13
14
  },
14
15
  "repository": {
@@ -22,26 +23,42 @@
22
23
  },
23
24
  "homepage": "https://github.com/klen/muffin-admin#readme",
24
25
  "dependencies": {
25
- "@mui/icons-material": "^5.14.1",
26
- "@mui/material": "^5.14.2",
27
26
  "js-cookie": "^3.0.5",
28
- "ra-ui-materialui": "^4.12.2",
29
27
  "react": "^18.2.0",
30
28
  "react-admin": "^4.12.2",
31
29
  "react-dom": "^18.2.0",
32
30
  "validator": "^13.9.0"
33
31
  },
34
32
  "devDependencies": {
35
- "@babel/cli": "^7.22.9",
36
- "@babel/core": "^7.22.9",
37
- "@babel/eslint-parser": "^7.22.9",
38
- "@babel/plugin-transform-runtime": "^7.22.9",
39
- "@babel/preset-env": "^7.22.9",
40
- "@babel/preset-react": "^7.22.5",
41
- "babel-loader": "^9.1.3",
42
- "eslint-plugin-react": "^7.33.0",
33
+ "@types/node": "20.3.2",
34
+ "@types/react": "18.2.14",
35
+ "@types/react-dom": "18.2.6",
36
+ "@types/react-window": "^1.8.5",
37
+ "@typescript-eslint/eslint-plugin": "^6.3.0",
38
+ "@typescript-eslint/parser": "^6.3.0",
39
+ "dotenv": "^16.3.1",
40
+ "eslint": "8.46.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.1",
46
+ "prettier-plugin-organize-imports": "^3.2.3",
47
+ "ts-loader": "^9.4.4",
48
+ "typescript": "^5.1.6",
43
49
  "webpack": "^5.88.2",
44
50
  "webpack-cli": "^5.1.4",
45
51
  "webpack-dev-server": "^4.15.1"
52
+ },
53
+ "prettier": {
54
+ "arrowParens": "always",
55
+ "plugins": [
56
+ "prettier-plugin-organize-imports"
57
+ ],
58
+ "printWidth": 100,
59
+ "semi": false,
60
+ "tabWidth": 2,
61
+ "trailingComma": "es5",
62
+ "useTabs": false
46
63
  }
47
64
  }
@@ -0,0 +1,73 @@
1
+ import IconButton from "@mui/material/IconButton"
2
+ import SvgIcon from "@mui/material/SvgIcon"
3
+ import Tooltip from "@mui/material/Tooltip"
4
+ import Typography from "@mui/material/Typography"
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"
15
+ import { AdminOpts } from "./types"
16
+ import { AdminPropsContext, buildAdmin, findBuilder, findIcon, setupAdmin } from "./utils"
17
+
18
+ const darkTheme = createTheme({
19
+ palette: { mode: "dark" },
20
+ })
21
+
22
+ export function MuffinAdmin(props: AdminOpts) {
23
+ const { resources, auth } = props
24
+ const children = resources.map((resource) =>
25
+ buildAdmin(["resource", resource.name], {
26
+ ...resource,
27
+ key: resource.name,
28
+ })
29
+ )
30
+ const appBar = (props) => (
31
+ <AppBar {...props}>
32
+ <Typography
33
+ variant="h6"
34
+ color="inherit"
35
+ id="react-admin-title"
36
+ style={{
37
+ flex: 1,
38
+ textOverflow: "ellipsis",
39
+ whiteSpace: "nowrap",
40
+ overflow: "hidden",
41
+ }}
42
+ />
43
+ <ToggleThemeButton lightTheme={defaultTheme} darkTheme={darkTheme} />
44
+ {props.appBarLinks.map((info) => (
45
+ <Tooltip key={info.url} title={info.title}>
46
+ <IconButton color="inherit" href={info.url}>
47
+ <SvgIcon component={findIcon(info.icon)} />
48
+ </IconButton>
49
+ </Tooltip>
50
+ ))}
51
+ </AppBar>
52
+ )
53
+
54
+ return (
55
+ <AdminPropsContext.Provider value={props}>
56
+ <Admin
57
+ requireAuth={auth.required}
58
+ dashboard={buildAdmin(["dashboard"])}
59
+ loginPage={findBuilder(["loginpage"])}
60
+ authProvider={buildAdmin(["authprovider"], auth)}
61
+ layout={(props) => <Layout {...props} appBar={appBar} />}
62
+ dataProvider={buildAdmin(["dataprovider"], props) as unknown as DataProvider}
63
+ >
64
+ {children}
65
+ </Admin>
66
+ </AdminPropsContext.Provider>
67
+ )
68
+ }
69
+
70
+ setupAdmin(["admin"], MuffinAdmin)
71
+
72
+ // Initialize login page
73
+ setupAdmin(["loginpage"], Login)
@@ -0,0 +1,52 @@
1
+ import Card from "@mui/material/Card"
2
+ import CardContent from "@mui/material/CardContent"
3
+ import Grid from "@mui/material/Grid"
4
+ import Table from "@mui/material/Table"
5
+ import TableBody from "@mui/material/TableBody"
6
+ import TableCell from "@mui/material/TableCell"
7
+ import TableRow from "@mui/material/TableRow"
8
+ import Typography from "@mui/material/Typography"
9
+ import { AdminDashboardBlock, AdminOpts } from "./types"
10
+
11
+ export function MuffinDashboard(props: AdminOpts["dashboard"]) {
12
+ return (
13
+ <Grid container spacing={1}>
14
+ {Array.isArray(props) ? (
15
+ props.map((card, idx) => <DashboardCard key={idx} {...card} />)
16
+ ) : (
17
+ <DashboardCard {...props} />
18
+ )}
19
+ </Grid>
20
+ )
21
+ }
22
+
23
+ function DashboardCard(title, value: AdminDashboardBlock) {
24
+ return (
25
+ <Grid item xs>
26
+ <Card>
27
+ <CardContent>
28
+ <Typography variant="h5" component="h2" m={2} style={{ textAlign: "center" }}>
29
+ {title}
30
+ </Typography>
31
+ {(Array.isArray(value) && <AdminTableView src={value} />) || (
32
+ <pre>{JSON.stringify(value, null, 2)}</pre>
33
+ )}
34
+ </CardContent>
35
+ </Card>
36
+ </Grid>
37
+ )
38
+ }
39
+
40
+ const AdminTableView = ({ src }) => (
41
+ <Table>
42
+ <TableBody>
43
+ {src.map((row, idx) => (
44
+ <TableRow key={idx} hover>
45
+ {row.map((cell, idx) => (
46
+ <TableCell key={idx}>{cell}</TableCell>
47
+ ))}
48
+ </TableRow>
49
+ ))}
50
+ </TableBody>
51
+ </Table>
52
+ )
@@ -0,0 +1,27 @@
1
+ import { Resource } from "react-admin"
2
+ import { AdminResourceProps } from "./types"
3
+ import { buildAdmin, findIcon, setupAdmin } from "./utils"
4
+
5
+ export function MuffinResource({
6
+ name,
7
+ create: createProps,
8
+ edit: editProps,
9
+ list: listProps,
10
+ show: showProps,
11
+ icon,
12
+ ...props
13
+ }: AdminResourceProps) {
14
+ return (
15
+ <Resource
16
+ name={name}
17
+ icon={findIcon(icon)}
18
+ create={buildAdmin(["create", name], createProps)}
19
+ edit={buildAdmin(["edit", name], editProps)}
20
+ list={buildAdmin(["list", name], listProps)}
21
+ show={buildAdmin(["show", name], showProps)}
22
+ {...props}
23
+ />
24
+ )
25
+ }
26
+
27
+ setupAdmin(["resource"], MuffinResource)
@@ -0,0 +1,24 @@
1
+ import { Create, ListButton, SimpleForm, TopToolbar, useResourceContext } from "react-admin"
2
+ import { buildRA } from "./buildRA"
3
+ import { AdminResourceProps } from "./types"
4
+ import { buildAdmin, setupAdmin } from "./utils"
5
+
6
+ export function MuffinResourceCreate({ inputs }: { inputs: AdminResourceProps["create"] }) {
7
+ const resourceName = useResourceContext()
8
+
9
+ return (
10
+ <Create actions={buildAdmin(["create-actions", resourceName])}>
11
+ <SimpleForm>{buildAdmin(["create-inputs", resourceName], inputs)}</SimpleForm>
12
+ </Create>
13
+ )
14
+ }
15
+
16
+ setupAdmin(["create"], (inputs) => <MuffinResourceCreate inputs={inputs} />)
17
+
18
+ setupAdmin(["create-actions"], () => (
19
+ <TopToolbar>
20
+ <ListButton />
21
+ </TopToolbar>
22
+ ))
23
+
24
+ setupAdmin(["create-inputs"], buildRA)
@@ -0,0 +1,42 @@
1
+ import { useContext } from "react"
2
+ import {
3
+ Edit,
4
+ ListButton,
5
+ ShowButton,
6
+ SimpleForm,
7
+ TopToolbar,
8
+ useResourceContext,
9
+ } from "react-admin"
10
+ import { buildRA } from "./buildRA"
11
+ import { ActionButton } from "./buttons"
12
+ import { AdminAction, AdminOpts, AdminResourceProps } from "./types"
13
+ import { AdminPropsContext, buildAdmin, setupAdmin } from "./utils"
14
+
15
+ export function MuffinResourceEdit({ actions, inputs, ...props }: AdminResourceProps["edit"]) {
16
+ const resourceName = useResourceContext()
17
+ const {
18
+ adminProps: { mutationMode },
19
+ } = useContext(AdminPropsContext) as AdminOpts
20
+
21
+ return (
22
+ <Edit
23
+ actions={buildAdmin(["edit-actions", resourceName], actions)}
24
+ mutationMode={mutationMode || "optimistic"}
25
+ {...props}
26
+ >
27
+ <SimpleForm>{buildAdmin(["edit-inputs", resourceName], inputs)}</SimpleForm>
28
+ </Edit>
29
+ )
30
+ }
31
+
32
+ setupAdmin(["edit"], (props) => <MuffinResourceEdit {...props} />)
33
+ setupAdmin(["edit-actions"], (actions: AdminAction[]) => (
34
+ <TopToolbar>
35
+ {actions.map((props, idx) => (
36
+ <ActionButton key={idx} {...props} />
37
+ ))}
38
+ <ListButton />
39
+ <ShowButton />
40
+ </TopToolbar>
41
+ ))
42
+ setupAdmin(["edit-inputs"], buildRA)
@@ -0,0 +1,46 @@
1
+ import sortBy from "lodash/sortBy"
2
+ import uniq from "lodash/uniq"
3
+ import {
4
+ BulkDeleteButton,
5
+ Datagrid,
6
+ EditButton,
7
+ List,
8
+ Pagination,
9
+ useResourceContext,
10
+ } from "react-admin"
11
+ import { buildRA } from "./buildRA"
12
+ import { BulkActionButton } from "./buttons"
13
+ import { AdminAction, AdminResourceProps } from "./types"
14
+ import { buildAdmin, setupAdmin } from "./utils"
15
+
16
+ export function MuffinResourceList(props: AdminResourceProps["list"]) {
17
+ const resourceName = useResourceContext()
18
+ const { fields, edit, show, limit, limitMax, filters, actions } = props
19
+ return (
20
+ <List
21
+ bulkActionButtons={buildAdmin(["list-actions", resourceName], actions)}
22
+ filters={buildAdmin(["list-filters", resourceName], filters) || undefined}
23
+ perPage={limit}
24
+ pagination={
25
+ <Pagination rowsPerPageOptions={sortBy(uniq([10, 25, 50, 100, limit, limitMax]))} />
26
+ }
27
+ >
28
+ <Datagrid rowClick={show ? "show" : edit ? "edit" : false}>
29
+ {buildAdmin(["list-fields", resourceName], fields)}
30
+ {edit && <EditButton />}
31
+ </Datagrid>
32
+ </List>
33
+ )
34
+ }
35
+
36
+ setupAdmin(["list"], (props) => <MuffinResourceList {...props} />)
37
+ setupAdmin(["list-fields"], buildRA)
38
+ setupAdmin(["list-filters"], buildRA)
39
+ setupAdmin(["list-actions"], (actions: AdminAction[]) => (
40
+ <>
41
+ {actions.map((props, idx) => (
42
+ <BulkActionButton key={idx} {...props} />
43
+ ))}
44
+ <BulkDeleteButton />
45
+ </>
46
+ ))
@@ -0,0 +1,39 @@
1
+ import {
2
+ EditButton,
3
+ ListButton,
4
+ Show,
5
+ SimpleShowLayout,
6
+ TopToolbar,
7
+ useResourceContext,
8
+ } from "react-admin"
9
+ import { LinkAction } from "./actions"
10
+ import { buildRA } from "./buildRA"
11
+ import { ActionButton } from "./buttons"
12
+ import { AdminShowProps } from "./types"
13
+ import { buildAdmin, setupAdmin } from "./utils"
14
+
15
+ export function MuffinResourceShow(props: AdminShowProps) {
16
+ const resourceName = useResourceContext()
17
+ return (
18
+ <Show actions={buildAdmin(["show-actions", resourceName], props)}>
19
+ <SimpleShowLayout>{buildAdmin(["show-fields", resourceName], props)}</SimpleShowLayout>
20
+ </Show>
21
+ )
22
+ }
23
+
24
+ setupAdmin(["show"], (props) => <MuffinResourceShow {...props} />)
25
+ setupAdmin(["show-fields"], ({ fields }) => buildRA(fields))
26
+ setupAdmin(["show-actions"], ({ actions, links }) => (
27
+ <TopToolbar>
28
+ <div style={{ marginRight: "auto" }}>
29
+ {Object.keys(links).map((key) => (
30
+ <LinkAction key={key} resource={key} {...links[key]} />
31
+ ))}
32
+ </div>
33
+ {actions.map((props, idx) => (
34
+ <ActionButton key={idx} {...props} />
35
+ ))}
36
+ <ListButton />
37
+ <EditButton />
38
+ </TopToolbar>
39
+ ))
@@ -0,0 +1,10 @@
1
+ import { useRecordContext, useResourceContext } from "react-admin"
2
+ import LinkButton from "../buttons/LinkButton"
3
+ import { AdminShowLink } from "../types"
4
+
5
+ export function LinkAction(props: AdminShowLink & { resource: string }) {
6
+ const record = useRecordContext()
7
+ const currentResource = useResourceContext()
8
+ if (!record) return null
9
+ return <LinkButton {...props} filters={{ [currentResource]: record.id }} />
10
+ }
@@ -0,0 +1 @@
1
+ export * from "./LinkAction"
@@ -1,33 +1,33 @@
1
1
  import Cookies from "js-cookie"
2
-
3
- import { makeRequest, processAdmin, requestHeaders, setupAdmin } from "./utils"
2
+ import { buildAdmin, makeRequest, requestHeaders, setupAdmin } from "."
3
+ import { AdminOpts } from "./types"
4
4
 
5
5
  setupAdmin(
6
- "auth-get",
7
- ({ storage }) =>
8
- (name) =>
9
- storage == "localstorage" ? localStorage.getItem(name) : Cookies.get(name)
6
+ ["auth-storage-get"],
7
+ ({ storage, storageName }: AdminOpts["auth"]) =>
8
+ () =>
9
+ storage == "localstorage" ? localStorage.getItem(storageName) : Cookies.get(storageName)
10
10
  )
11
11
 
12
- setupAdmin("auth-set", ({ storage }) => (name, value) => {
12
+ setupAdmin(["auth-storage-set"], ({ storage }) => (name, value) => {
13
13
  if (storage == "localstorage") {
14
14
  localStorage.setItem(name, value)
15
15
  requestHeaders["Authorization"] = value
16
16
  } else Cookies.set(name, value)
17
17
  })
18
18
 
19
- export default (props) => {
20
- const { identityURL, authorizeURL, logoutURL, required, storage_name } = props
19
+ export function MuffinAuthProvider(props: AdminOpts["auth"]) {
20
+ const { identityURL, authorizeURL, logoutURL, required, storageName } = props
21
21
 
22
- const authGet = processAdmin("auth-get", props)
23
- const authSet = processAdmin("auth-set", props)
22
+ const authGet = buildAdmin(["auth-storage-get"], props)
23
+ const authSet = buildAdmin(["auth-storage-set"], props)
24
24
 
25
25
  // Initialize request headers
26
- authSet(storage_name, authGet(storage_name))
26
+ authSet(storageName, authGet(storageName))
27
27
 
28
- let getIdentity = async () => {
28
+ const getIdentity = async () => {
29
29
  if (identityURL) {
30
- let { json } = await makeRequest(identityURL)
30
+ const { json } = await makeRequest(identityURL)
31
31
  return json
32
32
  }
33
33
  }
@@ -37,8 +37,11 @@ export default (props) => {
37
37
  login: async (data) => {
38
38
  if (!authorizeURL) throw { message: "Authorization is not supported" }
39
39
 
40
- let { json } = await makeRequest(authorizeURL, { data, method: "POST" })
41
- authSet(storage_name, json)
40
+ const { json } = await makeRequest(authorizeURL, {
41
+ data,
42
+ method: "POST",
43
+ })
44
+ authSet(storageName, json)
42
45
  },
43
46
 
44
47
  checkError: async (error) => {
@@ -46,7 +49,7 @@ export default (props) => {
46
49
 
47
50
  if (status == 401 || status == 403) {
48
51
  // Clean storage
49
- authSet(storage_name, "")
52
+ authSet(storageName, "")
50
53
 
51
54
  throw {
52
55
  message: "Invalid authorization",
@@ -57,12 +60,12 @@ export default (props) => {
57
60
  },
58
61
 
59
62
  checkAuth: async () => {
60
- const auth = authGet(storage_name)
63
+ const auth = authGet(storageName)
61
64
  if (!auth) throw { message: "Authorization required" }
62
65
 
63
66
  if (!identityURL) return auth
64
67
 
65
- let user = await getIdentity()
68
+ const user = await getIdentity()
66
69
  if (!user) throw { message: "Authorization required" }
67
70
 
68
71
  return user
@@ -70,16 +73,18 @@ export default (props) => {
70
73
 
71
74
  logout: async () => {
72
75
  // Clean storage
73
- authSet(storage_name, "")
76
+ authSet(storageName, "")
74
77
 
75
- if (logoutURL) window.location = logoutURL
78
+ if (logoutURL) window.location.href = logoutURL
76
79
  },
77
80
 
78
81
  getIdentity,
79
82
 
80
83
  getPermissions: async () => {
81
- const role = authGet(storage_name + "_role")
84
+ const role = authGet(storageName + "_role")
82
85
  return role || ""
83
86
  },
84
87
  }
85
88
  }
89
+
90
+ setupAdmin(["authprovider"], MuffinAuthProvider)