@canmingir/link 1.2.64 → 1.2.65

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": "@canmingir/link",
3
- "version": "1.2.64",
3
+ "version": "1.2.65",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,7 +5,7 @@ import { useLocalStorage } from "../../../hooks/use-local-storage";
5
5
 
6
6
  import { useCallback, useMemo, useState } from "react";
7
7
 
8
- const STORAGE_KEY = "settings";
8
+ const STORAGE_KEY = "link.settings";
9
9
 
10
10
  export function SettingsProvider({ children, defaultSettings }) {
11
11
  const { state, update, reset } = useLocalStorage(
package/src/http/index.js CHANGED
@@ -14,7 +14,7 @@ const instance = axios.create({
14
14
 
15
15
  instance.interceptors.request.use((request) => {
16
16
  const { base } = config();
17
- const accessToken = storage.get("link", "accessToken");
17
+ const accessToken = storage.get("link", "accesstoken");
18
18
 
19
19
  if (!accessToken) {
20
20
  window.location.href = base === "/" ? "/login" : `${base}/login`;
@@ -40,11 +40,11 @@ function processQueue(error, token = null) {
40
40
 
41
41
  async function doRefresh() {
42
42
  const { appId } = config();
43
- const projectId = storage.get("projectId");
44
- const identityProvider = storage.get("link", "identityProvider");
43
+ const projectId = storage.get("link", "projectid");
44
+ const identityProvider = storage.get("link", "identityprovider");
45
45
 
46
46
  const { data } = await oauth.post("/oauth", {
47
- refreshToken: storage.get("link", "refreshToken"),
47
+ refreshToken: storage.get("link", "refreshtoken"),
48
48
  appId,
49
49
  projectId,
50
50
  identityProvider,
@@ -54,9 +54,9 @@ async function doRefresh() {
54
54
  }),
55
55
  });
56
56
 
57
- storage.set("link", "accessToken", data.accessToken);
57
+ storage.set("link", "accesstoken", data.accessToken);
58
58
  if (data.refreshToken) {
59
- storage.set("link", "refreshToken", data.refreshToken);
59
+ storage.set("link", "refreshtoken", data.refreshToken);
60
60
  }
61
61
  return data.accessToken;
62
62
  }
@@ -81,7 +81,7 @@ instance.interceptors.response.use(
81
81
  "Bearer ",
82
82
  ""
83
83
  );
84
- const storedToken = storage.get("link", "accessToken");
84
+ const storedToken = storage.get("link", "accesstoken");
85
85
 
86
86
  if (storedToken && storedToken !== usedToken) {
87
87
  originalRequest.headers["Authorization"] = `Bearer ${storedToken}`;
@@ -107,8 +107,8 @@ instance.interceptors.response.use(
107
107
  } catch (refreshError) {
108
108
  processQueue(refreshError, null);
109
109
  const { base } = config();
110
- storage.remove("link", "accessToken");
111
- storage.remove("link", "refreshToken");
110
+ storage.remove("link", "accesstoken");
111
+ storage.remove("link", "refreshtoken");
112
112
  window.location.href = `${window.location.origin}${base}/login`;
113
113
  return Promise.reject(refreshError);
114
114
  } finally {
package/src/http/user.js CHANGED
@@ -13,7 +13,7 @@ const instance = axios.create({
13
13
  });
14
14
 
15
15
  instance.interceptors.request.use(async (request) => {
16
- const refreshToken = await storage.get("link", "refreshToken");
16
+ const refreshToken = await storage.get("link", "refreshtoken");
17
17
  if (refreshToken) {
18
18
  request.headers["Authorization"] = `Bearer ${refreshToken}`;
19
19
  }
@@ -22,7 +22,7 @@ instance.interceptors.request.use(async (request) => {
22
22
 
23
23
  instance.getUserDetails = async () => {
24
24
  try {
25
- const refreshToken = storage.get("link", "refreshToken");
25
+ const refreshToken = storage.get("link", "refreshtoken");
26
26
 
27
27
  if (!refreshToken) {
28
28
  return null;
@@ -47,8 +47,8 @@ instance.getUserDetails = async () => {
47
47
 
48
48
  instance.getPermittedUsers = async () => {
49
49
  const { appId } = config();
50
- const projectId = storage.get("projectId");
51
- const identityProvider = storage.get("link", "identityProvider");
50
+ const projectId = storage.get("link", "projectid");
51
+ const identityProvider = storage.get("link", "identityprovider");
52
52
 
53
53
  const response = await http.get("/permissions");
54
54
 
@@ -110,7 +110,7 @@ instance.getPermittedUsers = async () => {
110
110
  }
111
111
 
112
112
  if (identityProvider?.toUpperCase() === "COGNITO") {
113
- const accessToken = await storage.get("link", "accessToken");
113
+ const accessToken = await storage.get("link", "accesstoken");
114
114
  let currentUserId = null;
115
115
 
116
116
  try {
@@ -45,7 +45,7 @@ function ProjectBar() {
45
45
  }, [projectCreated]);
46
46
 
47
47
  const id = window.matchMedia("projectId").matches;
48
- const [selectedProjectId] = useStorage("projectId", id);
48
+ const [selectedProjectId] = useStorage("link", "projectid", id);
49
49
 
50
50
  const [selectedProject, setSelectedProject] = useState();
51
51
 
@@ -91,7 +91,7 @@ function ProjectBar() {
91
91
  const handleSelect = (project) => {
92
92
  const { id: projectId } = project;
93
93
 
94
- const identityProviderRaw = storage.get("link", "identityProvider");
94
+ const identityProviderRaw = storage.get("link", "identityprovider");
95
95
  const identityProvider = identityProviderRaw?.toUpperCase();
96
96
 
97
97
  const payload = {
@@ -104,7 +104,7 @@ function ProjectBar() {
104
104
  payload.username = "admin";
105
105
  payload.password = "admin";
106
106
  } else {
107
- payload.refreshToken = storage.get("link", "refreshToken");
107
+ payload.refreshToken = storage.get("link", "refreshtoken");
108
108
  }
109
109
 
110
110
  const request = oauth.post("/oauth", payload);
@@ -112,9 +112,9 @@ function ProjectBar() {
112
112
  request
113
113
  .then(({ data }) => {
114
114
  const { refreshToken, accessToken } = data;
115
- storage.set("link", "accessToken", accessToken);
116
- storage.set("link", "refreshToken", refreshToken);
117
- storage.set("projectId", projectId);
115
+ storage.set("link", "accesstoken", accessToken);
116
+ storage.set("link", "refreshtoken", refreshToken);
117
+ storage.set("link", "projectid", projectId);
118
118
  })
119
119
  .finally(() => {
120
120
  setSelectedProject(project);
@@ -1,5 +1,3 @@
1
- import React from "react";
2
-
3
1
  import { Close, Fullscreen, FullscreenExit } from "@mui/icons-material";
4
2
  import {
5
3
  Dialog,
@@ -8,9 +6,9 @@ import {
8
6
  DialogTitle,
9
7
  IconButton,
10
8
  } from "@mui/material";
11
- import { storage, useStorage } from "@nucleoidjs/webstorage";
9
+ import React, { useState } from "react";
12
10
 
13
- function NucDialog({
11
+ function APITreeDialog({
14
12
  title,
15
13
  minWidth = 600,
16
14
  children,
@@ -20,7 +18,7 @@ function NucDialog({
20
18
  maximizedDimensions = { width: "65rem", height: "50rem" },
21
19
  minimizedDimensions = { width: "55rem", height: "40rem" },
22
20
  }) {
23
- const [maximized] = useStorage("platform", title, "maximized", false);
21
+ const [maximized, setMaximized] = useState(false);
24
22
  const currentDimensions = maximized
25
23
  ? maximizedDimensions
26
24
  : minimizedDimensions;
@@ -63,7 +61,7 @@ function NucDialog({
63
61
  {maximized ? (
64
62
  <IconButton
65
63
  aria-label="collapse"
66
- onClick={() => storage.set("platform", title, "maximized", false)}
64
+ onClick={() => setMaximized(false)}
67
65
  sx={{
68
66
  position: "absolute",
69
67
  right: 48,
@@ -76,7 +74,7 @@ function NucDialog({
76
74
  ) : (
77
75
  <IconButton
78
76
  aria-label="expand"
79
- onClick={() => storage.set("platform", title, "maximized", true)}
77
+ onClick={() => setMaximized(true)}
80
78
  sx={{
81
79
  position: "absolute",
82
80
  right: 48,
@@ -105,4 +103,4 @@ function NucDialog({
105
103
  );
106
104
  }
107
105
 
108
- export default NucDialog;
106
+ export default APITreeDialog;
@@ -0,0 +1 @@
1
+ export { default } from "./APITreeDialog";
package/src/lib/index.js CHANGED
@@ -42,7 +42,7 @@ export { default as APIPath } from "./APIPath";
42
42
  export { default as APITypes } from "./APITypes";
43
43
  export { default as BlankTreeMessage } from "./BlankTreeMessage";
44
44
  export { default as DialogTooltip } from "./DialogTootip";
45
- export { default as NucDialog } from "./NucDialog";
45
+ export { default as APITreeDialog } from "./APITreeDialog";
46
46
  export { default as ParamTable } from "./ParamTable";
47
47
  export { default as ResourceMenu } from "./ResourceMenu";
48
48
  export { default as Schema } from "./Schema";
@@ -72,10 +72,10 @@ function Callback() {
72
72
  const defaultProjectId = "05708cf7-b9bf-4209-95fe-68d9138d2032";
73
73
 
74
74
  if (projectBar) {
75
- projectId = storage.get("projectId");
75
+ projectId = storage.get("link", "projectid");
76
76
  } else {
77
77
  projectId = defaultProjectId;
78
- storage.set("projectId", projectId);
78
+ storage.set("link", "projectid", projectId);
79
79
  }
80
80
 
81
81
  oauth
@@ -91,10 +91,10 @@ function Callback() {
91
91
  const accessToken = data.accessToken;
92
92
  const refreshToken = data.refreshToken;
93
93
 
94
- storage.set("link", "accessToken", accessToken);
95
- storage.set("link", "refreshToken", refreshToken);
94
+ storage.set("link", "accesstoken", accessToken);
95
+ storage.set("link", "refreshtoken", refreshToken);
96
96
  // TODO - update provider info
97
- storage.set("link", "identityProvider", identityProvider);
97
+ storage.set("link", "identityprovider", identityProvider);
98
98
 
99
99
  publish("LOGIN", { data: data });
100
100
 
@@ -16,8 +16,8 @@ function LoginPage() {
16
16
 
17
17
  function token() {
18
18
  if (
19
- storage.get("link", "refreshToken") &&
20
- storage.get("link", "accessToken")
19
+ storage.get("link", "refreshtoken") &&
20
+ storage.get("link", "accesstoken")
21
21
  ) {
22
22
  return true;
23
23
  } else {
@@ -2,10 +2,10 @@ import APIDialogAction from "../lib/APIDialogAction/APIDialogAction";
2
2
  import APIParams from "../lib/APIParams/APIParams";
3
3
  import APIPath from "../lib/APIPath/APIPath";
4
4
  import APITree from "../lib/APITree/APITree";
5
+ import APITreeDialog from "../lib/APITreeDialog/APITreeDialog";
5
6
  import APITypes from "../lib/APITypes/APITypes";
6
7
  import CssBaseline from "@mui/material/CssBaseline";
7
8
  import NewAPIBody from "../lib/NewApiBody/NewAPIBody";
8
- import NucDialog from "../lib/NucDialog/NucDialog";
9
9
 
10
10
  import { Box, Divider } from "@mui/material";
11
11
  import React, { useEffect, useRef, useState } from "react";
@@ -80,7 +80,8 @@ export default {
80
80
  description: {
81
81
  component:
82
82
  "APITree displays API endpoints in a tree structure grouped by path segments. " +
83
- "It supports context menus for editing/deleting methods and a resource menu for adding new endpoints.",
83
+ "It is a controlled component: selection and expansion are driven by the " +
84
+ "selected/expanded props and reported via onSelect/onExpand.",
84
85
  },
85
86
  },
86
87
  layout: "centered",
@@ -92,6 +93,8 @@ const ALL_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"];
92
93
  const APIWorkspace = () => {
93
94
  const [view, setView] = useState("PARAMS");
94
95
  const [dialogKey, setDialogKey] = useState(0);
96
+ const [expanded, setExpanded] = useState(["/", "/items"]);
97
+ const [selectedItem, setSelectedItem] = useState(null);
95
98
 
96
99
  const methodRef = useRef("GET");
97
100
  const pathRef = useRef("/");
@@ -337,10 +340,17 @@ const APIWorkspace = () => {
337
340
  return (
338
341
  <Box sx={{ display: "flex", height: "100%", gap: 2 }}>
339
342
  <Box sx={{ width: 280, flexShrink: 0, height: "100%" }}>
340
- <APITree />
343
+ <APITree
344
+ api={api}
345
+ theme={theme}
346
+ expanded={expanded}
347
+ selected={selectedItem}
348
+ onExpand={(_event, itemIds) => setExpanded(itemIds)}
349
+ onSelect={(_event, itemId) => setSelectedItem(itemId)}
350
+ />
341
351
  </Box>
342
352
 
343
- <NucDialog
353
+ <APITreeDialog
344
354
  title={dialogTitle}
345
355
  open={dialog.open}
346
356
  handleClose={handleCloseDialog}
@@ -398,7 +408,7 @@ const APIWorkspace = () => {
398
408
  <APITypes tstypes={[]} nuctypes={types} typesRef={typesRef} />
399
409
  )}
400
410
  </Box>
401
- </NucDialog>
411
+ </APITreeDialog>
402
412
  </Box>
403
413
  );
404
414
  };
@@ -420,9 +430,9 @@ export const APITreeWorkspace = {
420
430
  docs: {
421
431
  description: {
422
432
  story:
423
- "Full workspace: right-click a path node or click the + FAB to open the ResourceMenu, " +
424
- "then choose Resource, Method, or Delete. Adding a resource or method opens the API dialog " +
425
- "where you can configure the path, method, params, and body before saving.",
433
+ "Full workspace: click a method node to select it, then use the API dialog " +
434
+ "events (API_DIALOG_OPEN) to add or edit endpoints, configuring the path, " +
435
+ "method, params, and body before saving.",
426
436
  },
427
437
  },
428
438
  },
@@ -69,9 +69,9 @@ export default function CognitoLogin() {
69
69
 
70
70
  const data = await res.json();
71
71
 
72
- storage.set("link", "accessToken", data.accessToken);
73
- storage.set("link", "refreshToken", data.refreshToken);
74
- storage.set("link", "identityProvider", "COGNITO");
72
+ storage.set("link", "accesstoken", data.accessToken);
73
+ storage.set("link", "refreshtoken", data.refreshToken);
74
+ storage.set("link", "identityprovider", "COGNITO");
75
75
 
76
76
  navigate("/");
77
77
  } catch (e) {
@@ -43,9 +43,9 @@ export default function DemoLogin() {
43
43
 
44
44
  const data = await res.json();
45
45
 
46
- storage.set("link", "accessToken", data.accessToken);
47
- storage.set("link", "refreshToken", data.refreshToken);
48
- storage.set("link", "identityProvider", "DEMO");
46
+ storage.set("link", "accesstoken", data.accessToken);
47
+ storage.set("link", "refreshtoken", data.refreshToken);
48
+ storage.set("link", "identityprovider", "DEMO");
49
49
 
50
50
  navigate("/");
51
51
  }
@@ -18,8 +18,8 @@ export default function Auth0LoginView() {
18
18
 
19
19
  function token() {
20
20
  if (
21
- storage.get("link", "refreshToken") &&
22
- storage.get("link", "accessToken")
21
+ storage.get("link", "refreshtoken") &&
22
+ storage.get("link", "accesstoken")
23
23
  ) {
24
24
  return true;
25
25
  } else {
@@ -1,3 +1,11 @@
1
+ import Iconify from "../components/Iconify";
2
+ import config from "../config/config";
3
+ import { useEvent } from "@nucleoidai/react-event";
4
+ import useSettings from "../hooks/useSettings";
5
+ import { useSettingsContext } from "../components/settings/context";
6
+ import { useStorage } from "@nucleoidjs/webstorage";
7
+ import { useUser } from "../hooks/use-user";
8
+
1
9
  import {
2
10
  Avatar,
3
11
  Box,
@@ -23,13 +31,6 @@ import {
23
31
  import { Button, Dialog, DialogActions, DialogContent } from "@mui/material";
24
32
  import React, { useEffect, useState } from "react";
25
33
 
26
- import Iconify from "../components/Iconify";
27
- import config from "../config/config";
28
- import { useEvent } from "@nucleoidai/react-event";
29
- import useSettings from "../hooks/useSettings";
30
- import { useSettingsContext } from "../components/settings/context";
31
- import { useUser } from "../hooks/use-user";
32
-
33
34
  let pkg = {
34
35
  name: "",
35
36
  version: "",
@@ -296,7 +297,7 @@ const Permission = () => {
296
297
  };
297
298
 
298
299
  const Settings = () => {
299
- const projectId = localStorage.getItem("projectId");
300
+ const [projectId] = useStorage("link", "projectid");
300
301
  const { settings, updateSettings } = useSettings(projectId);
301
302
  const { beta, onUpdate } = useSettingsContext();
302
303
 
@@ -1,19 +0,0 @@
1
- import React from "react";
2
-
3
- const ContextStore = React.createContext();
4
- ContextStore.displayName = "ContextStore";
5
-
6
- const useContext = () => React.useContext(ContextStore);
7
-
8
- const ContextProvider = ({ children, state, reducer }) => {
9
- const [contextState, contextDispatch] = React.useReducer(reducer, state);
10
-
11
- return (
12
- <ContextStore.Provider value={[contextState, contextDispatch]}>
13
- {children}
14
- </ContextStore.Provider>
15
- );
16
- };
17
-
18
- export { useContext };
19
- export default ContextProvider;
@@ -1,98 +0,0 @@
1
- class Context {
2
- constructor(data = {}) {
3
- this.login = data.login !== undefined ? data.login : false;
4
- this.itemId = data.itemId || null;
5
-
6
- this.specification = {
7
- api: [],
8
- functions: [],
9
- types: [],
10
- declarations: [],
11
- ...(data.specification || {}),
12
- };
13
-
14
- this.pages = {
15
- api: {
16
- selected: { path: "/", method: "get" },
17
- dialog: {
18
- type: null,
19
- action: null,
20
- open: false,
21
- view: null,
22
- map: {},
23
- params: {},
24
- },
25
- resourceMenu: {
26
- open: false,
27
- anchor: null,
28
- path: null,
29
- },
30
- ...(data.pages?.api || {}),
31
- },
32
- functions: {
33
- selected: null,
34
- dialog: {
35
- type: null,
36
- open: false,
37
- },
38
- AIDialog: {
39
- open: false,
40
- },
41
- ...(data.pages?.functions || {}),
42
- },
43
- logic: {
44
- selected: null,
45
- AIDialog: {
46
- open: false,
47
- },
48
- ...(data.pages?.logic || {}),
49
- },
50
- query: {
51
- results: null,
52
- ...(data.pages?.query || {}),
53
- },
54
- ...(data.pages || {}),
55
- };
56
- }
57
-
58
- static copy(context) {
59
- const copied = new Context();
60
-
61
- copied.login = context.login;
62
- copied.itemId = context.itemId;
63
-
64
- copied.specification = {
65
- api: context.specification?.api ? [...context.specification.api] : [],
66
- functions: context.specification?.functions
67
- ? [...context.specification.functions]
68
- : [],
69
- types: context.specification?.types
70
- ? [...context.specification.types]
71
- : [],
72
- declarations: context.specification?.declarations
73
- ? [...context.specification.declarations]
74
- : [],
75
- };
76
-
77
- copied.pages = JSON.parse(JSON.stringify(context.pages || {}));
78
-
79
- return copied;
80
- }
81
-
82
- get(path) {
83
- const keys = path.split(".");
84
- let result = this;
85
-
86
- for (const key of keys) {
87
- if (result && typeof result === "object" && key in result) {
88
- result = result[key];
89
- } else {
90
- return undefined;
91
- }
92
- }
93
-
94
- return result;
95
- }
96
- }
97
-
98
- export default Context;
@@ -1,632 +0,0 @@
1
- import ActionTemplate from "../templates/ActionTemplate.js";
2
- import Context from "./Context.js";
3
- import { jwtDecode } from "jwt-decode";
4
- import { publish } from "@nucleoidai/react-event";
5
- import { storage } from "@nucleoidjs/webstorage";
6
- import { v4 as uuid } from "uuid";
7
-
8
- let login = true;
9
- const itemId = storage.get("itemId");
10
- try {
11
- const token = storage.get("link", "accessToken");
12
- const decodedToken = jwtDecode(token);
13
-
14
- if (decodedToken.exp * 1000 < Date.now()) {
15
- login = false;
16
- }
17
- } catch (err) {
18
- login = false;
19
- }
20
-
21
- export const initialState = new Context({
22
- login,
23
- itemId,
24
- specification: {
25
- api: [],
26
- functions: [],
27
- types: [],
28
- declarations: [],
29
- },
30
- pages: {
31
- api: {
32
- selected: { path: "/", method: "get" },
33
- dialog: {
34
- type: null,
35
- action: null,
36
- open: false,
37
- view: null,
38
- map: {},
39
- params: {},
40
- },
41
- resourceMenu: {
42
- open: false,
43
- anchor: null,
44
- path: null,
45
- },
46
- },
47
- functions: {
48
- selected: null,
49
- dialog: {
50
- type: null,
51
- open: false,
52
- },
53
- AIDialog: {
54
- open: false,
55
- },
56
- },
57
- logic: {
58
- selected: null,
59
- AIDialog: {
60
- open: false,
61
- },
62
- },
63
- query: {
64
- results: null,
65
- },
66
- },
67
- });
68
-
69
- export const reducer = (context, action) => {
70
- context = Context.copy(context);
71
-
72
- const { specification, pages } = context;
73
-
74
- switch (action.type) {
75
- case "LOGIN": {
76
- context.login = true;
77
- break;
78
- }
79
- case "LOGOUT": {
80
- storage.clear();
81
- context.login = false;
82
- break;
83
- }
84
-
85
- case "ITEM_SELECT": {
86
- context.itemId = action.payload;
87
- storage.set("itemId", context.itemId);
88
- break;
89
- }
90
-
91
- case "ITEM_DELETE": {
92
- storage.remove("itemId");
93
- context.itemId = null;
94
- break;
95
- }
96
-
97
- case "SET_PROJECT": {
98
- specification.api = action.payload.project.specification.api;
99
- specification.functions = action.payload.project.specification.functions;
100
- specification.types = action.payload.project.specification.types;
101
- pages.api.selected.path = "/";
102
- break;
103
- }
104
-
105
- case "OPEN_API_DIALOG": {
106
- const { type, action: dialogAction } = action.payload;
107
- pages.api.dialog.type = type;
108
- pages.api.dialog.action = dialogAction;
109
- pages.api.dialog.open = true;
110
- break;
111
- }
112
-
113
- case "SAVE_API_DIALOG": {
114
- const {
115
- path,
116
- method,
117
- request,
118
- response,
119
- params,
120
- types,
121
- summary,
122
- description,
123
- } = action.payload;
124
-
125
- const newApi = {
126
- path,
127
- method,
128
- request,
129
- response,
130
- params,
131
- summary,
132
- description,
133
- action: ActionTemplate[method.toUpperCase()],
134
- };
135
- pages.api.dialog.open = false;
136
- specification.api.push(newApi);
137
- pages.api.selected = { path, method };
138
- publish("SELECTED_API_CHANGED", {
139
- path: path,
140
- method: method,
141
- });
142
-
143
- specification.types = types;
144
- break;
145
- }
146
-
147
- case "CLOSE_API_DIALOG": {
148
- pages.api.dialog.open = false;
149
- break;
150
- }
151
-
152
- case "SET_API_DIALOG_VIEW":
153
- pages.api.dialog.view = action.payload.view;
154
- break;
155
-
156
- case "SET_SELECTED_API": {
157
- if (action.payload.method === null) {
158
- const endpoint = specification.api.find(
159
- (endpoint) => endpoint.path === action.payload.path
160
- );
161
-
162
- if (endpoint && endpoint.method) {
163
- action.payload.method = endpoint.method;
164
- }
165
- }
166
- pages.api.selected = {
167
- path: action.payload.path,
168
- method: action.payload.method,
169
- };
170
-
171
- publish("SELECTED_API_CHANGED", {
172
- path: action.payload.path,
173
- method: action.payload.method,
174
- });
175
-
176
- break;
177
- }
178
-
179
- case "SET_SELECTED_FUNCTION": {
180
- pages.functions.selected = action.payload.function;
181
- break;
182
- }
183
-
184
- case "SET_SELECTED_LOGIC": {
185
- pages.logic.selected = action.payload.logic;
186
- break;
187
- }
188
-
189
- // ===== Resource Menu Actions =====
190
- case "OPEN_RESOURCE_MENU":
191
- pages.api.resourceMenu.open = true;
192
- pages.api.resourceMenu.anchor = action.payload.anchor;
193
- pages.api.resourceMenu.path = action.payload.path;
194
- break;
195
-
196
- case "DELETE_RESOURCE": {
197
- const { path } = action.payload;
198
-
199
- specification.api = specification.api.filter(
200
- (api) => !api.path.startsWith(path)
201
- );
202
-
203
- if (pages.api.selected.path.startsWith(path)) {
204
- if (specification.api.length > 0) {
205
- pages.api.selected.path = specification.api[0].path;
206
- pages.api.selected.method = specification.api[0].method;
207
- } else {
208
- pages.api.selected.path = "/";
209
- pages.api.selected.method = "get";
210
- }
211
-
212
- publish("SELECTED_API_CHANGED", {
213
- path: pages.api.selected.path,
214
- method: pages.api.selected.method,
215
- });
216
- }
217
-
218
- break;
219
- }
220
-
221
- case "DELETE_METHOD": {
222
- const { path, method } = pages.api.selected;
223
-
224
- const routeIndex = specification.api.findIndex(
225
- (route) =>
226
- route.path === path &&
227
- route.method.toLowerCase() === method.toLowerCase()
228
- );
229
-
230
- if (routeIndex !== -1) {
231
- specification.api.splice(routeIndex, 1);
232
- }
233
-
234
- const samePathRoutes = specification.api.filter(
235
- (route) => route.path === path
236
- );
237
- if (samePathRoutes.length > 0) {
238
- context.pages.api.selected.method = samePathRoutes[0].method;
239
- } else {
240
- context.pages.api.selected.method = null;
241
- }
242
-
243
- break;
244
- }
245
-
246
- case "CLOSE_RESOURCE_MENU":
247
- pages.api.resourceMenu.open = false;
248
- pages.api.resourceMenu = {};
249
- break;
250
-
251
- // ===== Function Dialog Actions =====
252
- case "OPEN_FUNCTION_DIALOG": {
253
- pages.functions.dialog.type = action.payload.type;
254
- pages.functions.dialog.open = true;
255
- break;
256
- }
257
-
258
- case "OPEN_AI_DIALOG": {
259
- const page = action.payload.page;
260
- pages[page].AIDialog.open = true;
261
- break;
262
- }
263
-
264
- case "CLOSE_AI_DIALOG": {
265
- const page = action.payload.page;
266
- pages[page].AIDialog.open = false;
267
- break;
268
- }
269
-
270
- case "SAVE_LOGIC_DIALOG": {
271
- const { description, summary, definition } = action.payload;
272
- const declarations = specification.declarations;
273
-
274
- declarations.push({
275
- description,
276
- summary,
277
- definition,
278
- });
279
-
280
- publish("LOGIC_ADDED", {
281
- declaration: declarations[declarations.length - 1],
282
- });
283
-
284
- break;
285
- }
286
-
287
- case "SAVE_FUNCTION_DIALOG": {
288
- const { path, type, definition, params, ext } = action.payload;
289
- const functions = specification.functions;
290
-
291
- functions.push({
292
- path,
293
- type,
294
- ext,
295
- definition,
296
- params,
297
- });
298
-
299
- publish("CONTEXT_CHANGED", {
300
- files: [{ key: `${path}.${ext}`, value: definition }],
301
- });
302
-
303
- break;
304
- }
305
-
306
- case "DELETE_FUNCTION": {
307
- const { path } = action.payload;
308
-
309
- if (specification.functions.length > 1) {
310
- const index = specification.functions.findIndex(
311
- (data) => data.path === path
312
- );
313
-
314
- const deletedFunction = specification.functions[index];
315
- specification.functions.splice(index, 1);
316
-
317
- const remainingFunctions = specification.functions.filter(
318
- (data) => data.path !== path
319
- );
320
-
321
- const files = remainingFunctions.map((func) => ({
322
- key: `${func.path}.ts`,
323
- value: func,
324
- }));
325
-
326
- publish("CONTEXT_CHANGED", {
327
- files: [{ key: `${deletedFunction.path}.ts` }, ...files],
328
- });
329
- context.pages.functions.selected = specification.functions[0].path;
330
- }
331
-
332
- break;
333
- }
334
-
335
- case "CLOSE_FUNCTION_DIALOG": {
336
- pages.functions.dialog.open = false;
337
- break;
338
- }
339
-
340
- // ===== Type Management Actions =====
341
- case "UPDATE_TYPE": {
342
- const { id, name, type } = action.payload;
343
- const map = pages.api.dialog.map;
344
-
345
- if (name !== undefined) map[id].name = name;
346
- if (type !== undefined) {
347
- if (map[id].type === "array") map[id].items.type = type;
348
- else {
349
- map[id].type = type;
350
-
351
- if (type === "array") map[id].items = { type: "integer" };
352
- else if (type === "object") map[id].properties = {};
353
- }
354
- }
355
-
356
- break;
357
- }
358
-
359
- case "ADD_SCHEMA_PROPERTY": {
360
- const { id } = action.payload;
361
- const map = pages.api.dialog.map;
362
- const key = uuid();
363
- map[key] = map[id].properties[key] = {
364
- id: key,
365
- type: "integer",
366
- };
367
- break;
368
- }
369
-
370
- case "REMOVE_SCHEMA_PROPERTY": {
371
- const { id } = action.payload;
372
- const map = pages.api.dialog.map;
373
- delete map[id];
374
- break;
375
- }
376
-
377
- case "ADD_PARAM": {
378
- const map = pages.api.dialog.map;
379
- const id = uuid();
380
- pages.api.dialog.params[id] = map[id] = {
381
- id,
382
- type: "string",
383
- required: true,
384
- };
385
- break;
386
- }
387
-
388
- case "REMOVE_PARAM": {
389
- const { id } = action.payload;
390
- const map = pages.api.dialog.map;
391
- delete pages.api.dialog.params[id];
392
- delete map[id];
393
- break;
394
- }
395
-
396
- case "QUERY_RESULTS": {
397
- const query = context.get("pages.query");
398
- query.results = action.payload.results;
399
- break;
400
- }
401
-
402
- // ===== CRUD Actions =====
403
- case "CREATE_SAMPLE_CRUD": {
404
- const { api, functions } = specification;
405
- const className = action.payload.resource;
406
- const resource = action.payload.resource.toLowerCase() + "s";
407
-
408
- const template = {
409
- request: { type: "object", properties: {} },
410
- response: { type: "object", properties: {} },
411
- };
412
-
413
- api[`/${resource}`] = {
414
- get: {
415
- ...template,
416
- summary: `Get ${className} list`,
417
- description: `Get ${className} list`,
418
- action: `function action(req) {\n return ${className};\n}\n`,
419
- },
420
- post: {
421
- ...template,
422
- summary: `Create ${className}`,
423
- description: `Create ${className}`,
424
- action: `function action(req) {\n return new ${className}();\n}\n`,
425
- },
426
- };
427
- api[`/${resource}/{${resource}}`] = {
428
- get: {
429
- ...template,
430
- summary: `Get ${className}`,
431
- description: `Get ${className}`,
432
- action: `function action(req) {\n const ${resource} = req.params.${resource};\n return ${className}[${resource}];\n}\n`,
433
- },
434
- put: {
435
- ...template,
436
- summary: `Update ${className}`,
437
- description: `Update ${className}`,
438
- action: `function action(req) {\n const ${resource} = req.params.${resource};\n return ${className}[${resource}];\n}\n`,
439
- },
440
- del: {
441
- ...template,
442
- summary: `Delete ${className}`,
443
- description: `Delete ${className}`,
444
- action: `function action(req) {\n const ${resource} = req.params.${resource};\n delete ${className}[${resource}];\n}\n`,
445
- },
446
- };
447
-
448
- const sample = {
449
- definition: `class ${className} {\n constructor() {\n }\n}\n`,
450
- ext: "ts",
451
- params: [],
452
- path: `/${className}`,
453
- type: "CLASS",
454
- };
455
-
456
- functions.push(sample);
457
-
458
- publish("CONTEXT_CHANGED", {
459
- files: [
460
- { key: `${sample.path}.${sample.ext}`, value: sample.definition },
461
- ],
462
- });
463
-
464
- break;
465
- }
466
-
467
- case "UPDATE_API_SCHEMAS": {
468
- const { path, method, requestSchema, responseSchema } = action.payload;
469
- const apiIndex = specification.api.findIndex(
470
- (api) => api.path === path && api.method === method
471
- );
472
-
473
- if (apiIndex !== -1) {
474
- specification.api[apiIndex].request = {
475
- ...specification.api[apiIndex].request,
476
- schema: requestSchema,
477
- };
478
- specification.api[apiIndex].response = {
479
- ...specification.api[apiIndex].response,
480
- schema: responseSchema,
481
- };
482
- }
483
- break;
484
- }
485
-
486
- case "UPDATE_API_SUMMARY": {
487
- const { path, method, newSummary } = action.payload;
488
- const apiEndpoint = specification.api.find(
489
- (api) => api.path === path && api.method === method
490
- );
491
- if (apiEndpoint) {
492
- apiEndpoint.summary = newSummary;
493
- }
494
- break;
495
- }
496
-
497
- case "UPDATE_API_DESCRIPTION": {
498
- const { path, method, newDescription } = action.payload;
499
- const apiEndpoint = specification.api.find(
500
- (api) => api.path === path && api.method === method
501
- );
502
- if (apiEndpoint) {
503
- apiEndpoint.description = newDescription;
504
- }
505
- break;
506
- }
507
-
508
- case "DELETE_API": {
509
- const selectedIndex = specification.api.findIndex(
510
- (api) =>
511
- api.path === pages.api.selected.path &&
512
- api.method === pages.api.selected.method
513
- );
514
-
515
- if (selectedIndex > -1) {
516
- specification.api.splice(selectedIndex, 1);
517
- if (specification.api.length > 0) {
518
- pages.api.selected.path = specification.api[0].path;
519
- pages.api.selected.method = specification.api[0].method;
520
- } else {
521
- pages.api.selected.path = "/";
522
- pages.api.selected.method = "get";
523
- }
524
- }
525
- pages.api.dialog.open = false;
526
-
527
- break;
528
- }
529
-
530
- case "UPDATE_API_TYPES": {
531
- const { updatedTypes } = action.payload;
532
- const typeIndex = specification.types.findIndex(
533
- (type) => type.name === updatedTypes.name
534
- );
535
- const updatedType = {
536
- ...specification.types[typeIndex],
537
- schema: {
538
- ...updatedTypes,
539
- },
540
- };
541
- if (typeIndex !== -1) {
542
- specification.types[typeIndex] = updatedType;
543
- } else {
544
- specification.types.push(updatedTypes);
545
- }
546
- break;
547
- }
548
-
549
- case "ADD_TYPE": {
550
- const { typeName } = action.payload;
551
- const newType = {
552
- name: typeName,
553
- schema: {
554
- name: typeName,
555
- type: "object",
556
- properties: [
557
- { type: "string", name: "id" },
558
- { type: "string", name: "name" },
559
- ],
560
- },
561
- };
562
- specification.types.push(newType);
563
- break;
564
- }
565
-
566
- case "DELETE_TYPE": {
567
- const { typeName } = action.payload;
568
- const typeIndex = specification.types.findIndex(
569
- (type) => type.name === typeName
570
- );
571
-
572
- if (typeIndex !== -1) {
573
- specification.types.splice(typeIndex, 1);
574
- }
575
- break;
576
- }
577
-
578
- case "UPDATE_TYPE_NAME": {
579
- const { oldTypeName, newTypeName } = action.payload;
580
- const typeIndex = specification.types.findIndex(
581
- (type) => type.name === oldTypeName
582
- );
583
-
584
- if (typeIndex !== -1) {
585
- specification.types[typeIndex].name = newTypeName;
586
- specification.types[typeIndex].schema.name = newTypeName;
587
- }
588
- break;
589
- }
590
-
591
- case "SAVE_API_PARAMS": {
592
- console.log("SAVE_API_PARAMS", action.payload);
593
- const { path, method, params } = action.payload;
594
- const apiIndex = specification.api.findIndex(
595
- (api) => api.path === path && api.method === method
596
- );
597
-
598
- if (apiIndex !== -1) {
599
- specification.api[apiIndex].params = params;
600
- }
601
- break;
602
- }
603
-
604
- case "UPDATE_API_PATH_METHOD": {
605
- const { path, method } = action.payload;
606
-
607
- const apiIndex = specification.api.findIndex(
608
- (api) =>
609
- api.path === pages.api.selected.path &&
610
- api.method === pages.api.selected.method
611
- );
612
-
613
- if (apiIndex !== -1) {
614
- specification.api[apiIndex].path = path;
615
- specification.api[apiIndex].method = method;
616
- }
617
-
618
- pages.api.selected = {
619
- ...pages.api.selected,
620
- path: path,
621
- method: method,
622
- };
623
-
624
- break;
625
- }
626
-
627
- default:
628
- }
629
-
630
- console.debug("reducer", action.type, context);
631
- return context;
632
- };
@@ -1,113 +0,0 @@
1
- import { Box } from "@mui/material";
2
- import MiniTopBar from "../components/MiniTopBar";
3
- import Sidebar from "../components/Sidebar";
4
- import TopNavBar from "../components/TopNavBar";
5
- import config from "../config/config";
6
- import { useContext } from "../ContextProvider/ContextProvider";
7
- import user from "../http/user";
8
-
9
- import { Outlet, useLocation } from "react-router-dom";
10
- import React, { useEffect, useState } from "react";
11
-
12
- function AppLayout() {
13
- const [anchorElUser, setAnchorElUser] = useState(null);
14
- const [isCollapsed, setIsCollapsed] = useState(false);
15
- const [isSubmenuOpen, setSubmenuOpen] = useState(false);
16
- const [selectedItem, setSelectedItem] = useState();
17
- const [state, dispatch] = useContext();
18
- const [miniTopMenu, setMiniTopMenu] = useState(false);
19
- const [userData, setUserData] = useState(null);
20
- const location = useLocation();
21
- const globalConfig = useConfig();
22
-
23
- const toggleCollapse = () => {
24
- setIsCollapsed(!isCollapsed);
25
- if (isSubmenuOpen) {
26
- setSubmenuOpen(false);
27
- }
28
- };
29
-
30
- const handleOpenUserMenu = (event) => {
31
- setAnchorElUser(event.currentTarget);
32
- };
33
-
34
- const handleCloseUserMenu = () => {
35
- setAnchorElUser(null);
36
- };
37
-
38
- const handleItemSelect = (item) => {
39
- dispatch({ type: "ITEM_SELECT", payload: item.id });
40
- };
41
-
42
- useEffect(() => {
43
- const foundItem = globalConfig.itemsData.find(
44
- (item) => item.id === state.itemId
45
- );
46
-
47
- if (foundItem) {
48
- setSelectedItem(foundItem);
49
- }
50
- selectedItem && setSubmenuOpen(false);
51
-
52
- // eslint-disable-next-line react-hooks/exhaustive-deps
53
- }, []);
54
-
55
- useEffect(() => {
56
- user.get("https://api.github.com/user").then((response) => {
57
- setUserData(response.data);
58
- });
59
- // eslint-disable-next-line react-hooks/exhaustive-deps
60
- }, [state.login]);
61
-
62
- useEffect(() => {
63
- const targetPage = globalConfig.topMenu.find(
64
- (target) => target.url === location.pathname
65
- );
66
-
67
- targetPage?.hideTopBar
68
- ? (setMiniTopMenu(true), setIsCollapsed(true))
69
- : (setMiniTopMenu(false), setIsCollapsed(false));
70
- // eslint-disable-next-line react-hooks/exhaustive-deps
71
- }, [location]);
72
-
73
- return (
74
- <Box>
75
- {state.login &&
76
- (miniTopMenu ? (
77
- <MiniTopBar />
78
- ) : (
79
- <TopNavBar
80
- sx={{ flexShrink: 0 }}
81
- anchorElUser={anchorElUser}
82
- handleOpenUserMenu={handleOpenUserMenu}
83
- handleCloseUserMenu={handleCloseUserMenu}
84
- routes={globalConfig.topMenu}
85
- itemsData={globalConfig.itemsData}
86
- onItemSelect={handleItemSelect}
87
- sideBarToggle={toggleCollapse}
88
- selectedItem={selectedItem}
89
- setSelectedItem={setSelectedItem}
90
- itemUrl="/"
91
- itemName="ITEEEEEM"
92
- userData={userData}
93
- />
94
- ))}
95
-
96
- <Box sx={{ display: "flex", flexGrow: 1 }}>
97
- {state.itemId ? (
98
- <Sidebar
99
- sx={{ flexShrink: 0 }}
100
- routes={globalConfig.sideMenu}
101
- isCollapsed={isCollapsed}
102
- currentPage={location.pathname}
103
- />
104
- ) : null}
105
- <Box sx={{ flexGrow: 1, overflow: "auto" }}>
106
- <Outlet />
107
- </Box>
108
- </Box>
109
- </Box>
110
- );
111
- }
112
-
113
- export default AppLayout;
@@ -1 +0,0 @@
1
- export { default } from "./NucDialog";
@@ -1,24 +0,0 @@
1
- const ActionTemplate = {
2
- GET: `function action(req) {
3
- // Implement your GET logic here
4
- return {};
5
- }`,
6
- POST: `function action(req) {
7
- // Implement your POST logic here
8
- return {};
9
- }`,
10
- PUT: `function action(req) {
11
- // Implement your PUT logic here
12
- return {};
13
- }`,
14
- DELETE: `function action(req) {
15
- // Implement your DELETE logic here
16
- return {};
17
- }`,
18
- PATCH: `function action(req) {
19
- // Implement your PATCH logic here
20
- return {};
21
- }`,
22
- };
23
-
24
- export default ActionTemplate;