@canmingir/link 1.2.23 → 1.2.25

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 (60) hide show
  1. package/package.json +3 -1
  2. package/src/Platform.jsx +10 -14
  3. package/src/context/Context.js +98 -0
  4. package/src/context/reducer.js +590 -10
  5. package/src/layouts/auth/modern.jsx +2 -2
  6. package/src/lib/APIDialogAction/APIDialogAction.jsx +109 -0
  7. package/src/lib/APIDialogAction/index.js +1 -0
  8. package/src/lib/APIDialogAction/styles.js +6 -0
  9. package/src/lib/APIParams/APIParams.jsx +57 -0
  10. package/src/lib/APIParams/index.js +1 -0
  11. package/src/lib/APIPath/APIPath.jsx +82 -0
  12. package/src/lib/APIPath/index.js +1 -0
  13. package/src/lib/APIPath/styles.js +19 -0
  14. package/src/lib/APITree/APITree.jsx +409 -0
  15. package/src/lib/APITree/Arrow.jsx +21 -0
  16. package/src/lib/APITree/DeleteMethodDialog.jsx +41 -0
  17. package/src/lib/APITree/index.js +1 -0
  18. package/src/lib/APITree/styles.js +19 -0
  19. package/src/lib/APITypes/APITypes.jsx +141 -0
  20. package/src/lib/APITypes/TypeEditor.jsx +46 -0
  21. package/src/lib/APITypes/TypeList.jsx +180 -0
  22. package/src/lib/APITypes/index.js +1 -0
  23. package/src/lib/BlankTreeMessage/BlankTreeMessage.jsx +39 -0
  24. package/src/lib/BlankTreeMessage/index.js +1 -0
  25. package/src/lib/DialogTootip/DialogTooltip.jsx +67 -0
  26. package/src/lib/DialogTootip/index.js +1 -0
  27. package/src/lib/DialogTootip/styles.js +9 -0
  28. package/src/lib/NewApiBody/NewAPIBody.jsx +97 -0
  29. package/src/lib/NewApiBody/ParamView.jsx +38 -0
  30. package/src/lib/NucDialog/NucDialog.jsx +108 -0
  31. package/src/lib/NucDialog/index.js +1 -0
  32. package/src/lib/ParamTable/ParamTable.jsx +133 -0
  33. package/src/lib/ParamTable/TypeMenu.jsx +102 -0
  34. package/src/lib/ParamTable/defaults.js +47 -0
  35. package/src/lib/ParamTable/index.js +1 -0
  36. package/src/lib/ParamTable/styles.js +12 -0
  37. package/src/lib/ResourceMenu/AlertMassage.jsx +28 -0
  38. package/src/lib/ResourceMenu/DeleteResourceDialog.jsx +60 -0
  39. package/src/lib/ResourceMenu/ResourceMenu.jsx +156 -0
  40. package/src/lib/ResourceMenu/index.js +1 -0
  41. package/src/lib/ResourceMenu/styles.js +5 -0
  42. package/src/lib/Schema/Schema.jsx +204 -0
  43. package/src/lib/Schema/index.js +1 -0
  44. package/src/lib/SchemaEditor/SchemaEditor.jsx +258 -0
  45. package/src/lib/SchemaEditor/SchemaEditor.test.js +193 -0
  46. package/src/lib/SchemaEditor/SchemaPropertyEditor.jsx +135 -0
  47. package/src/lib/SchemaEditor/SchemaUtils.js +152 -0
  48. package/src/lib/SchemaEditor/index.js +1 -0
  49. package/src/lib/ToggleableMenu/ToggleableMenu.jsx +35 -0
  50. package/src/lib/ToggleableMenu/index.js +1 -0
  51. package/src/lib/index.js +14 -0
  52. package/src/pages/Callback.jsx +2 -4
  53. package/src/pages/LoginPage.jsx +3 -12
  54. package/src/stories/APITree.stories.jsx +429 -0
  55. package/src/stories/FlowChart.stories.jsx +1 -1
  56. package/src/templates/ActionTemplate.js +24 -0
  57. package/src/widgets/Login/CognitoLogin.jsx +2 -2
  58. package/src/widgets/Login/DemoLogin.jsx +1 -1
  59. package/src/widgets/LoginForm/LoginForm.jsx +8 -3
  60. package/src/widgets/SettingsDialog.jsx +33 -11
@@ -0,0 +1,38 @@
1
+ import { v4 as uuid } from "uuid";
2
+
3
+ import {
4
+ Table,
5
+ TableBody,
6
+ TableCell,
7
+ TableHead,
8
+ TableRow,
9
+ } from "@mui/material";
10
+
11
+ function ParamView({ params }) {
12
+ params = params || [];
13
+
14
+ return (
15
+ <Table size={"small"} data-cy="param-view">
16
+ <colgroup>
17
+ <col style={{ width: "50%" }} />
18
+ <col style={{ width: "50%" }} />
19
+ </colgroup>
20
+ <TableHead>
21
+ <TableRow>
22
+ <TableCell>Parameter</TableCell>
23
+ <TableCell>Data Type</TableCell>
24
+ </TableRow>
25
+ </TableHead>
26
+ <TableBody>
27
+ {params.map((param) => (
28
+ <TableRow key={uuid()}>
29
+ <TableCell>{param.name}</TableCell>
30
+ <TableCell>{param.type}</TableCell>
31
+ </TableRow>
32
+ ))}
33
+ </TableBody>
34
+ </Table>
35
+ );
36
+ }
37
+
38
+ export default ParamView;
@@ -0,0 +1,108 @@
1
+ import { Close } from "@mui/icons-material";
2
+ import Fullscreen from "@mui/icons-material/Fullscreen";
3
+ import FullscreenExit from "@mui/icons-material/FullscreenExit";
4
+ import React from "react";
5
+
6
+ import {
7
+ Dialog,
8
+ DialogActions,
9
+ DialogContent,
10
+ DialogTitle,
11
+ IconButton,
12
+ } from "@mui/material";
13
+ import { storage, useStorage } from "@nucleoidjs/webstorage";
14
+
15
+ export default function NucDialog({
16
+ title,
17
+ minWidth = 600,
18
+ children,
19
+ action,
20
+ open,
21
+ handleClose,
22
+ maximizedDimensions = { width: "65rem", height: "50rem" },
23
+ minimizedDimensions = { width: "55rem", height: "40rem" },
24
+ }) {
25
+ const [maximized] = useStorage("platform", title, "maximized", false);
26
+ const currentDimensions = maximized
27
+ ? maximizedDimensions
28
+ : minimizedDimensions;
29
+
30
+ return (
31
+ <Dialog
32
+ open={open}
33
+ maxWidth={false}
34
+ onClose={handleClose}
35
+ sx={{
36
+ "& .MuiDialog-paper": {
37
+ width: currentDimensions.width,
38
+ height: currentDimensions.height,
39
+ transition: "width 0.3s ease-in-out, height 0.3s ease-in-out",
40
+ },
41
+ }}
42
+ >
43
+ <DialogTitle
44
+ sx={{
45
+ m: 0,
46
+ p: 2,
47
+ backgroundColor: (theme) => theme.palette.background.default,
48
+ }}
49
+ >
50
+ {title}
51
+ </DialogTitle>
52
+ <IconButton
53
+ aria-label="close"
54
+ onClick={handleClose}
55
+ sx={{
56
+ position: "absolute",
57
+ right: 8,
58
+ top: 8,
59
+ color: (theme) => theme.palette.grey[500],
60
+ }}
61
+ data-cy="close-dialog-button"
62
+ >
63
+ <Close />
64
+ </IconButton>
65
+ {maximized ? (
66
+ <IconButton
67
+ aria-label="collapse"
68
+ onClick={() => storage.set("platform", title, "maximized", false)}
69
+ sx={{
70
+ position: "absolute",
71
+ right: 48,
72
+ top: 8,
73
+ color: (theme) => theme.palette.grey[500],
74
+ }}
75
+ >
76
+ <FullscreenExit />
77
+ </IconButton>
78
+ ) : (
79
+ <IconButton
80
+ aria-label="expand"
81
+ onClick={() => storage.set("platform", title, "maximized", true)}
82
+ sx={{
83
+ position: "absolute",
84
+ right: 48,
85
+ top: 8,
86
+ color: (theme) => theme.palette.grey[500],
87
+ }}
88
+ >
89
+ <Fullscreen />
90
+ </IconButton>
91
+ )}
92
+
93
+ <DialogContent
94
+ sx={{
95
+ minWidth,
96
+ backgroundColor: (theme) => theme.palette.background.default,
97
+ }}
98
+ >
99
+ {children}
100
+ </DialogContent>
101
+ <DialogActions
102
+ sx={{ backgroundColor: (theme) => theme.palette.background.default }}
103
+ >
104
+ {action}
105
+ </DialogActions>
106
+ </Dialog>
107
+ );
108
+ }
@@ -0,0 +1 @@
1
+ export { default } from "./NucDialog";
@@ -0,0 +1,133 @@
1
+ import { DataGrid } from "@mui/x-data-grid";
2
+ import HighlightOffIcon from "@mui/icons-material/HighlightOff";
3
+ import React from "react";
4
+ import TypeMenu from "./TypeMenu";
5
+ import styles from "./styles";
6
+
7
+ import { Checkbox, IconButton, InputBase } from "@mui/material";
8
+
9
+ const ParamTable = ({ types, params, setParams }) => {
10
+ const columns = [
11
+ {
12
+ field: "name",
13
+ headerName: "Name",
14
+ renderCell: (param) => {
15
+ const { id } = param.row;
16
+ return (
17
+ <InputBase
18
+ disabled={param.row.in === "path"}
19
+ value={param.value || ""}
20
+ onChange={(event) => updateParam(id, "name", event.target.value)}
21
+ fullWidth
22
+ placeholder="Enter name"
23
+ data-cy={`param-name-field-${id}`}
24
+ />
25
+ );
26
+ },
27
+ flex: 1,
28
+ },
29
+ {
30
+ field: "type",
31
+ headerName: "Type",
32
+ renderCell: (param) => {
33
+ const { id } = param.row;
34
+ return (
35
+ <TypeMenu
36
+ disabled={param.row.in === "path"}
37
+ primitive
38
+ types={types}
39
+ id={id}
40
+ type={param.value}
41
+ onChange={(type) => updateParam(id, "type", type)}
42
+ />
43
+ );
44
+ },
45
+ flex: 1,
46
+ },
47
+ {
48
+ field: "in",
49
+ headerName: "Param",
50
+ flex: 1,
51
+ valueGetter: (param) => param.value || "query",
52
+ },
53
+ {
54
+ field: "required",
55
+ headerName: "Required",
56
+ renderCell: (param) => {
57
+ const { id } = param.row;
58
+ return (
59
+ <Checkbox
60
+ disabled={param.row.in === "path"}
61
+ checked={param.value}
62
+ onChange={(event) =>
63
+ updateParam(id, "required", event.target.checked)
64
+ }
65
+ data-cy={`param-required-checkbox-${id}`}
66
+ />
67
+ );
68
+ },
69
+ flex: 1,
70
+ },
71
+ {
72
+ field: "description",
73
+ headerName: "Description",
74
+ renderCell: (param) => {
75
+ const { id } = param.row;
76
+ return (
77
+ <InputBase
78
+ disabled={param.row.in === "path"}
79
+ value={param.value || ""}
80
+ onChange={(event) =>
81
+ updateParam(id, "description", event.target.value)
82
+ }
83
+ fullWidth
84
+ placeholder="Enter description"
85
+ data-cy={`param-description-field-${id}`}
86
+ />
87
+ );
88
+ },
89
+ flex: 2,
90
+ },
91
+ {
92
+ field: "action",
93
+ headerName: " ",
94
+ sortable: false,
95
+ renderCell: (param) => {
96
+ const { id } = param.row;
97
+ return (
98
+ <IconButton
99
+ disabled={param.row.in === "path"}
100
+ onClick={() => removeParam(id)}
101
+ size="large"
102
+ >
103
+ <HighlightOffIcon />
104
+ </IconButton>
105
+ );
106
+ },
107
+ },
108
+ ];
109
+
110
+ const removeParam = (id) => {
111
+ setParams((prevParams) => prevParams.filter((param) => param.id !== id));
112
+ };
113
+
114
+ const updateParam = (id, field, value) => {
115
+ setParams((prevParams) =>
116
+ prevParams.map((param) =>
117
+ param.id === id ? { ...param, [field]: value } : param
118
+ )
119
+ );
120
+ };
121
+
122
+ return (
123
+ <DataGrid
124
+ sx={styles.dataGrid}
125
+ columns={columns}
126
+ rows={params}
127
+ hideFooter
128
+ disableSelectionOnClick
129
+ />
130
+ );
131
+ };
132
+
133
+ export default ParamTable;
@@ -0,0 +1,102 @@
1
+ import Defaults from "./defaults";
2
+ import { v4 as uuid } from "uuid";
3
+
4
+ import { Divider, MenuItem, Select } from "@mui/material";
5
+ import { forwardRef, useState } from "react";
6
+
7
+ const TypeMenu = forwardRef(
8
+ (
9
+ {
10
+ id,
11
+ type,
12
+ types,
13
+ map,
14
+ edit,
15
+ setKey,
16
+ primitive,
17
+ objAndArr,
18
+ globalTypes,
19
+ disabled,
20
+ },
21
+ ref
22
+ ) => {
23
+ const [selectedType, setSelectedType] = useState(type);
24
+
25
+ function updateType(id, value) {
26
+ if (ref) {
27
+ ref[id].type = value;
28
+ } else {
29
+ switch (value) {
30
+ case "array":
31
+ if (map.type === "object") delete map["properties"];
32
+ map.type = "array";
33
+ map["items"] = Defaults.compiledObject(uuid(), uuid());
34
+
35
+ break;
36
+ case "object":
37
+ if (map.type === "array") delete map["items"];
38
+ map.type = "object";
39
+ map["properties"] = Defaults.compiledProperty(uuid());
40
+
41
+ break;
42
+ default:
43
+ if (map["properties"]) delete map["properties"];
44
+ if (map["items"]) delete map["items"];
45
+ map.type = value;
46
+
47
+ break;
48
+ }
49
+ }
50
+ setKey && setKey(uuid());
51
+ setSelectedType(value);
52
+ }
53
+
54
+ return (
55
+ <>
56
+ {edit && (
57
+ <Select
58
+ disabled={disabled}
59
+ value={selectedType}
60
+ onChange={(event) => {
61
+ updateType(id, event.target.value);
62
+ }}
63
+ >
64
+ {primitive && [
65
+ <MenuItem key={uuid()} value={"integer"}>
66
+ integer
67
+ </MenuItem>,
68
+ <MenuItem key={uuid()} value={"string"}>
69
+ string
70
+ </MenuItem>,
71
+ <MenuItem key={uuid()} value={"boolean"}>
72
+ boolean
73
+ </MenuItem>,
74
+ <Divider key={uuid()} id={uuid()} />,
75
+ ]}
76
+
77
+ {objAndArr && [
78
+ <MenuItem key={uuid()} id={uuid()} value={"object"}>
79
+ object
80
+ </MenuItem>,
81
+ <MenuItem key={uuid()} id={uuid()} value={"array"}>
82
+ array
83
+ </MenuItem>,
84
+ ]}
85
+ {globalTypes && <Divider />}
86
+ {globalTypes &&
87
+ types.map((item) => {
88
+ return (
89
+ <MenuItem key={uuid()} id={uuid()} value={item.name}>
90
+ {item.name}
91
+ </MenuItem>
92
+ );
93
+ })}
94
+ </Select>
95
+ )}
96
+ {!edit && <>{type}</>}
97
+ </>
98
+ );
99
+ }
100
+ );
101
+
102
+ export default TypeMenu;
@@ -0,0 +1,47 @@
1
+ const Defaults = {
2
+ compiledObject: (objID, objPropID) => {
3
+ return {
4
+ [objID]: {
5
+ type: "object",
6
+ id: objID,
7
+ properties: {
8
+ [objPropID]: {
9
+ id: objPropID,
10
+ name: "id",
11
+ type: "string",
12
+ },
13
+ },
14
+ },
15
+ };
16
+ },
17
+ compiledProperty: (id) => {
18
+ return {
19
+ [id]: {
20
+ id: id,
21
+ name: "id",
22
+ type: "string",
23
+ },
24
+ };
25
+ },
26
+ object: {
27
+ type: "object",
28
+ properties: {
29
+ id: {
30
+ type: "string",
31
+ },
32
+ },
33
+ },
34
+ array: {
35
+ type: "array",
36
+ items: {
37
+ id: {
38
+ type: "string",
39
+ },
40
+ },
41
+ },
42
+ action: `function action(req) {\n\treturn req.body.name;\n}\n`,
43
+ summary: "Summary",
44
+ description: "Description",
45
+ };
46
+
47
+ export default Defaults;
@@ -0,0 +1 @@
1
+ export { default } from "./ParamTable";
@@ -0,0 +1,12 @@
1
+ const styles = {
2
+ dataGrid: {
3
+ border: "none",
4
+ width: "100%",
5
+ "& .MuiDataGrid-cell": {
6
+ cursor: "pointer",
7
+ borderBottom: "none",
8
+ },
9
+ },
10
+ };
11
+
12
+ export default styles;
@@ -0,0 +1,28 @@
1
+ import { Alert } from "@mui/material";
2
+ import React from "react";
3
+ import Snackbar from "@mui/material/Snackbar";
4
+
5
+ export default function AlertMassage({ message }) {
6
+ const [open, setOpen] = React.useState(true);
7
+
8
+ function handleClose(event, reason) {
9
+ if (reason === "clickaway") return;
10
+ setOpen(false);
11
+ }
12
+
13
+ return (
14
+ <Snackbar
15
+ anchorOrigin={{
16
+ vertical: "bottom",
17
+ horizontal: "center",
18
+ }}
19
+ open={open}
20
+ autoHideDuration={2000}
21
+ onClose={handleClose}
22
+ >
23
+ <Alert onClose={handleClose} severity="info">
24
+ {message}
25
+ </Alert>
26
+ </Snackbar>
27
+ );
28
+ }
@@ -0,0 +1,60 @@
1
+ import Button from "@mui/material/Button";
2
+ import Dialog from "@mui/material/Dialog";
3
+ import DialogActions from "@mui/material/DialogActions";
4
+ import DialogContent from "@mui/material/DialogContent";
5
+ import DialogTitle from "@mui/material/DialogTitle";
6
+ import { Typography } from "@mui/material";
7
+ import { forwardRef } from "react";
8
+
9
+ const DeleteResourceDialog = forwardRef(({ setOpen, deleteResource }, ref) => {
10
+ const resource = ref.current;
11
+
12
+ const handleClose = () => {
13
+ setOpen(false);
14
+ };
15
+
16
+ const handleDelete = () => {
17
+ deleteResource();
18
+ };
19
+
20
+ return (
21
+ <Dialog
22
+ open={true}
23
+ onClose={handleClose}
24
+ aria-labelledby="responsive-dialog-title"
25
+ >
26
+ <DialogTitle id="responsive-dialog-title">Delete resource</DialogTitle>
27
+ <DialogContent>
28
+ <Typography>
29
+ {`The selected resource "${resource?.deleteAdress}" will be deleted.`}
30
+ </Typography>
31
+ {resource?.deleteList.length > 0 && (
32
+ <>
33
+ <Typography sx={{ mt: 2 }}>
34
+ The following child resources will also be deleted:
35
+ </Typography>
36
+ {resource?.deleteList.map((item, index) => (
37
+ <Typography key={index} sx={{ ml: 2 }}>
38
+ - {item}
39
+ </Typography>
40
+ ))}
41
+ </>
42
+ )}
43
+ </DialogContent>
44
+ <DialogActions>
45
+ <Button autoFocus onClick={handleClose}>
46
+ Cancel
47
+ </Button>
48
+ <Button
49
+ onClick={handleDelete}
50
+ autoFocus
51
+ data-cy="delete-resource-confirm-button"
52
+ >
53
+ Delete
54
+ </Button>
55
+ </DialogActions>
56
+ </Dialog>
57
+ );
58
+ });
59
+
60
+ export default DeleteResourceDialog;
@@ -0,0 +1,156 @@
1
+ import AlertMassage from "./AlertMassage";
2
+ import DeleteIcon from "@mui/icons-material/Delete";
3
+ import DeleteResourceDialog from "./DeleteResourceDialog";
4
+ import Fade from "@mui/material/Fade";
5
+ import HttpIcon from "@mui/icons-material/Http";
6
+ import React from "react";
7
+ import SourceIcon from "@mui/icons-material/Source";
8
+ import styles from "./styles";
9
+ import { useRef } from "react";
10
+
11
+ import { Divider, Menu, MenuItem, Typography } from "@mui/material";
12
+ import { publish, useEvent } from "@nucleoidai/react-event";
13
+
14
+ const ResourceMenu = (props) => {
15
+ const { anchor, openMenu, handleClose, hash, map } = props;
16
+
17
+ const [methodDisabled, setMethodDisabled] = React.useState();
18
+ const [alertMessage, setAlertMessage] = React.useState("");
19
+ const [open, setOpen] = React.useState(false);
20
+ const resourceRef = useRef();
21
+
22
+ const [api] = useEvent("API_DATA_CHANGED", []);
23
+ const [selectedAPI] = useEvent("SELECTED_API_CHANGED", {
24
+ path: "/",
25
+ method: "GET",
26
+ });
27
+
28
+ React.useEffect(() => {
29
+ const checkMethodAddable = () => {
30
+ const countMethodsForPath = (path) => {
31
+ return api.filter((endpoint) => endpoint.path === path).length;
32
+ };
33
+
34
+ if (hash) {
35
+ const path = map ? map[hash].path : null;
36
+ return countMethodsForPath(path) > 3;
37
+ }
38
+
39
+ if (selectedAPI) {
40
+ const apiSelectedPath = selectedAPI.path;
41
+ return countMethodsForPath(apiSelectedPath) > 4;
42
+ }
43
+ return false;
44
+ };
45
+
46
+ setMethodDisabled(checkMethodAddable());
47
+ }, [api, hash, map, selectedAPI]);
48
+
49
+ const addMethod = () => {
50
+ selectPath();
51
+ publish("API_DIALOG_OPEN", { type: "method", action: "add" });
52
+ handleClose();
53
+ };
54
+
55
+ const addResource = () => {
56
+ selectPath();
57
+ publish("API_DIALOG_OPEN", { type: "resource", action: "add" });
58
+ handleClose();
59
+ };
60
+
61
+ const deleteResource = () => {
62
+ const path = selectedAPI?.path;
63
+ if (!path) return;
64
+
65
+ publish("API_RESOURCE_DELETE", { path });
66
+ handleClose();
67
+ setOpen(false);
68
+ };
69
+
70
+ const handleResourceDeleteDialog = () => {
71
+ selectPath();
72
+
73
+ if (selectedAPI?.path === "/") {
74
+ setAlertMessage("Root path cannot be deleted");
75
+ handleClose();
76
+ } else {
77
+ const selectedPath = selectedAPI?.path;
78
+ const deleteList = api
79
+ .filter(
80
+ (item) =>
81
+ item.path.startsWith(selectedPath) && item.path !== selectedPath
82
+ )
83
+ .map((item) => item.path)
84
+ .filter((path, index, self) => self.indexOf(path) === index);
85
+
86
+ resourceRef.current = {
87
+ deleteAdress: selectedPath,
88
+ deleteList: deleteList,
89
+ };
90
+ handleClose();
91
+ setOpen(true);
92
+ }
93
+ };
94
+
95
+ const selectPath = () => {
96
+ const item = map ? map[hash] : null;
97
+
98
+ if (item) {
99
+ publish("SELECTED_API_CHANGED", { path: item.path, method: null });
100
+ } else if (selectedAPI) {
101
+ publish("SELECTED_API_CHANGED", {
102
+ path: selectedAPI.path,
103
+ method: null,
104
+ });
105
+ }
106
+ };
107
+
108
+ return (
109
+ <>
110
+ {open && (
111
+ <DeleteResourceDialog
112
+ open={open}
113
+ setOpen={setOpen}
114
+ deleteResource={deleteResource}
115
+ ref={resourceRef}
116
+ />
117
+ )}
118
+ {alertMessage && <AlertMassage message={alertMessage} />}
119
+ <Menu
120
+ open={openMenu}
121
+ onClose={handleClose}
122
+ onContextMenu={(event) => event.preventDefault()}
123
+ anchorReference="anchorPosition"
124
+ anchorPosition={{
125
+ top: anchor?.clientY || 0,
126
+ left: anchor?.clientX || 0,
127
+ }}
128
+ TransitionComponent={Fade}
129
+ >
130
+ <MenuItem onClick={addResource} data-cy="add-resource">
131
+ <SourceIcon />
132
+ <Typography sx={styles.menuItemText}>Resource</Typography>
133
+ </MenuItem>
134
+ <MenuItem
135
+ onClick={addMethod}
136
+ disabled={methodDisabled}
137
+ data-cy="add-method"
138
+ >
139
+ <HttpIcon />
140
+ <Typography sx={styles.menuItemText}>Method</Typography>
141
+ </MenuItem>
142
+ <Divider />
143
+ <MenuItem
144
+ onClick={handleResourceDeleteDialog}
145
+ disabled={selectedAPI?.path === "/"}
146
+ data-cy="delete-resource"
147
+ >
148
+ <DeleteIcon />
149
+ <Typography sx={styles.menuItemText}>Delete</Typography>
150
+ </MenuItem>
151
+ </Menu>
152
+ </>
153
+ );
154
+ };
155
+
156
+ export default ResourceMenu;
@@ -0,0 +1 @@
1
+ export { default } from "./ResourceMenu";
@@ -0,0 +1,5 @@
1
+ const styles = {
2
+ menuItemText: { pl: 3 / 2 },
3
+ };
4
+
5
+ export default styles;