diff --git a/src/app/components/ConfigRowItem/ConfigRowItem.component.scss b/src/app/components/ConfigRowItem/ConfigRowItem.component.scss new file mode 100755 index 0000000..e44dd7e --- /dev/null +++ b/src/app/components/ConfigRowItem/ConfigRowItem.component.scss @@ -0,0 +1,3 @@ +.ar-ConfigRowItem { + +} diff --git a/src/app/components/ConfigRowItem/ConfigRowItem.test.ts b/src/app/components/ConfigRowItem/ConfigRowItem.test.ts new file mode 100755 index 0000000..7a68779 --- /dev/null +++ b/src/app/components/ConfigRowItem/ConfigRowItem.test.ts @@ -0,0 +1,8 @@ +import React from "react" +import ConfigRowItem from "./ConfigRowItem" + +describe("ConfigRowItem", () => { + it("renders without error", () => { + + }) +}) diff --git a/src/app/components/ConfigRowItem/ConfigRowItem.tsx b/src/app/components/ConfigRowItem/ConfigRowItem.tsx new file mode 100755 index 0000000..166a3b5 --- /dev/null +++ b/src/app/components/ConfigRowItem/ConfigRowItem.tsx @@ -0,0 +1,97 @@ +import { ChangeEvent, useState } from "react" +import { ArButtonVariants, ConfigRowItemProps } from "@armco/types" +import { Button, LoadableIcon, TextInput } from ".." +import "./ConfigRowItem.component.scss" + +const ConfigRowItem = (props: ConfigRowItemProps): JSX.Element => { + const { config, disabled, isNew, onAdd, onUpdate, onDelete } = props + const [key, setKey] = useState(config?.key || "") + const [value, setValue] = useState(config?.value || "") + const [edited, setEdited] = useState() + + const configIsSubmittable = (isNew || edited) && key && value + return ( +
+
+ ) => + setKey(e.target.value) + } + placeholder="Config key" + /> +
+
+ ) => + setValue(e.target.value) + } + value={value} + /> +
+ {isNew ? ( +
+
+ ) : ( +
+ + { + config?._id && + configIsSubmittable && + onUpdate && + onUpdate(config?._id, key, value) + setKey("") + setValue("") + }} + hoverShadow + /> + + + (edited ? setEdited(false) : setEdited(true))} + /> + + + onAdd && onAdd(key, value)} + /> + + + onDelete && onDelete(config?._id)} + /> + +
+ )} +
+ ) +} + +export default ConfigRowItem diff --git a/src/app/components/ConfigRowItem/index.ts b/src/app/components/ConfigRowItem/index.ts new file mode 100755 index 0000000..65b15c7 --- /dev/null +++ b/src/app/components/ConfigRowItem/index.ts @@ -0,0 +1,3 @@ +import ConfigRowItem from "./ConfigRowItem" + +export default ConfigRowItem diff --git a/src/app/components/ConfigurationList/ConfigurationList.component.scss b/src/app/components/ConfigurationList/ConfigurationList.component.scss new file mode 100755 index 0000000..29d14c2 --- /dev/null +++ b/src/app/components/ConfigurationList/ConfigurationList.component.scss @@ -0,0 +1,3 @@ +.ar-ConfigurationList { + +} diff --git a/src/app/components/ConfigurationList/ConfigurationList.test.ts b/src/app/components/ConfigurationList/ConfigurationList.test.ts new file mode 100755 index 0000000..b93cfd8 --- /dev/null +++ b/src/app/components/ConfigurationList/ConfigurationList.test.ts @@ -0,0 +1,8 @@ +import React from "react" +import ConfigurationList from "./ConfigurationList" + +describe("ConfigurationList", () => { + it("renders without error", () => { + + }) +}) diff --git a/src/app/components/ConfigurationList/ConfigurationList.tsx b/src/app/components/ConfigurationList/ConfigurationList.tsx new file mode 100755 index 0000000..a66696c --- /dev/null +++ b/src/app/components/ConfigurationList/ConfigurationList.tsx @@ -0,0 +1,28 @@ +import { useNavigate } from "react-router-dom" +import { ConfigurationListProps, TreeListData } from "@armco/types" +import { Network, TreeList } from ".." +import "./ConfigurationList.component.scss" + +const ConfigurationList = (props: ConfigurationListProps): JSX.Element => { + const { list } = props + const navigate = useNavigate() + const handleComponentSelect = (treeNode: TreeListData) => { + const params = treeNode.data?.props + treeNode.data?.component && + navigate( + Network.stringifyUrl(`/components/${treeNode.data?.component}`, params), + ) + } + return ( +
+ +
+ ) +} + +export default ConfigurationList diff --git a/src/app/components/ConfigurationList/index.ts b/src/app/components/ConfigurationList/index.ts new file mode 100755 index 0000000..916bcb0 --- /dev/null +++ b/src/app/components/ConfigurationList/index.ts @@ -0,0 +1,3 @@ +import ConfigurationList from "./ConfigurationList" + +export default ConfigurationList diff --git a/src/app/components/ConfigurationLoginPrompt/ConfigurationLoginPrompt.component.scss b/src/app/components/ConfigurationLoginPrompt/ConfigurationLoginPrompt.component.scss new file mode 100755 index 0000000..9433e39 --- /dev/null +++ b/src/app/components/ConfigurationLoginPrompt/ConfigurationLoginPrompt.component.scss @@ -0,0 +1,10 @@ +.ar-ConfigurationLoginPrompt { + width: 50vw; + background-color: var(--ar-bg); + border-radius: 0.25rem; + + small { + color: var(--ar-color-secondary); + line-height: 1rem; + } +} diff --git a/src/app/components/ConfigurationLoginPrompt/ConfigurationLoginPrompt.test.ts b/src/app/components/ConfigurationLoginPrompt/ConfigurationLoginPrompt.test.ts new file mode 100755 index 0000000..8c7db1a --- /dev/null +++ b/src/app/components/ConfigurationLoginPrompt/ConfigurationLoginPrompt.test.ts @@ -0,0 +1,8 @@ +import React from "react" +import ConfigurationLoginPrompt from "./ConfigurationLoginPrompt" + +describe("ConfigurationLoginPrompt", () => { + it("renders without error", () => { + + }) +}) diff --git a/src/app/components/ConfigurationLoginPrompt/ConfigurationLoginPrompt.tsx b/src/app/components/ConfigurationLoginPrompt/ConfigurationLoginPrompt.tsx new file mode 100755 index 0000000..0ea25de --- /dev/null +++ b/src/app/components/ConfigurationLoginPrompt/ConfigurationLoginPrompt.tsx @@ -0,0 +1,48 @@ +import { ArButtonVariants, ConfigurationLoginPromptProps } from "@armco/types" +import { Button, LoadableIcon, setRightPanelContent, useAppDispatch } from ".." +import "./ConfigurationLoginPrompt.component.scss" + +const ConfigurationLoginPrompt = ( + props: ConfigurationLoginPromptProps, +): JSX.Element => { + const dispatch = useAppDispatch() + return ( +
+
+
+ + Please login to continue +
+ + This is the configurations feature where you can create and save + configurations as key-value pairs. + + + These key-value pairs can then be accessed using an endpoint inside + your application. + + + + In order to be able to save and secure these configurations, you + need to create an account, if one doesn't exist already and login + using the same. Please use the button below to continue. + + +
+
+ ) +} + +export default ConfigurationLoginPrompt diff --git a/src/app/components/ConfigurationLoginPrompt/index.ts b/src/app/components/ConfigurationLoginPrompt/index.ts new file mode 100755 index 0000000..284ee9e --- /dev/null +++ b/src/app/components/ConfigurationLoginPrompt/index.ts @@ -0,0 +1,3 @@ +import ConfigurationLoginPrompt from "./ConfigurationLoginPrompt" + +export default ConfigurationLoginPrompt diff --git a/src/app/components/ConfigurationNoConfigPrompt/ConfigurationNoConfigPrompt.component.scss b/src/app/components/ConfigurationNoConfigPrompt/ConfigurationNoConfigPrompt.component.scss new file mode 100755 index 0000000..8182efa --- /dev/null +++ b/src/app/components/ConfigurationNoConfigPrompt/ConfigurationNoConfigPrompt.component.scss @@ -0,0 +1,3 @@ +.ar-ConfigurationNoConfigPrompt { + +} diff --git a/src/app/components/ConfigurationNoConfigPrompt/ConfigurationNoConfigPrompt.test.ts b/src/app/components/ConfigurationNoConfigPrompt/ConfigurationNoConfigPrompt.test.ts new file mode 100755 index 0000000..00819a1 --- /dev/null +++ b/src/app/components/ConfigurationNoConfigPrompt/ConfigurationNoConfigPrompt.test.ts @@ -0,0 +1,8 @@ +import React from "react" +import ConfigurationNoConfigPrompt from "./ConfigurationNoConfigPrompt" + +describe("ConfigurationNoConfigPrompt", () => { + it("renders without error", () => { + + }) +}) diff --git a/src/app/components/ConfigurationNoConfigPrompt/ConfigurationNoConfigPrompt.tsx b/src/app/components/ConfigurationNoConfigPrompt/ConfigurationNoConfigPrompt.tsx new file mode 100755 index 0000000..ba8622b --- /dev/null +++ b/src/app/components/ConfigurationNoConfigPrompt/ConfigurationNoConfigPrompt.tsx @@ -0,0 +1,102 @@ +import { ReactNode } from "react" +import { + ArButtonVariants, + ArPopoverSlots, + ArPopoverTriggers, + ConfigurationNoConfigPromptProps, +} from "@armco/types" +import { + Button, + LoadableIcon, + NamespaceOrgForm, + Popover, + setModalState, + useAppDispatch, +} from ".." +import "./ConfigurationNoConfigPrompt.component.scss" + +const ConfigurationNoConfigPrompt = ( + props: ConfigurationNoConfigPromptProps, +): ReactNode => { + const dispatch = useAppDispatch() + + return ( +
+ +

+ You don't have any configurations created yet, you can start by + selecting one of the options below +

+
+ +
+
+ ) +} + +export default ConfigurationNoConfigPrompt diff --git a/src/app/components/ConfigurationNoConfigPrompt/index.ts b/src/app/components/ConfigurationNoConfigPrompt/index.ts new file mode 100755 index 0000000..0191b87 --- /dev/null +++ b/src/app/components/ConfigurationNoConfigPrompt/index.ts @@ -0,0 +1,3 @@ +import ConfigurationNoConfigPrompt from "./ConfigurationNoConfigPrompt" + +export default ConfigurationNoConfigPrompt diff --git a/src/app/components/ConfigurationViewer/ConfigurationViewer.component.scss b/src/app/components/ConfigurationViewer/ConfigurationViewer.component.scss new file mode 100755 index 0000000..b6632cf --- /dev/null +++ b/src/app/components/ConfigurationViewer/ConfigurationViewer.component.scss @@ -0,0 +1,13 @@ +.ar-ConfigurationViewer { + background-color: var(--ar-bg); + .ar-ConfigurationViewer__header { + background-color: var(--ar-bg-base); + border-bottom: 1px solid var(--ar-color-layout-border); + } + + input:disabled { + border: none; + background: transparent; + font-weight: bold; + } +} \ No newline at end of file diff --git a/src/app/components/ConfigurationViewer/ConfigurationViewer.test.ts b/src/app/components/ConfigurationViewer/ConfigurationViewer.test.ts new file mode 100755 index 0000000..a580626 --- /dev/null +++ b/src/app/components/ConfigurationViewer/ConfigurationViewer.test.ts @@ -0,0 +1,8 @@ +import React from "react" +import ConfigurationViewer from "./ConfigurationViewer" + +describe("ConfigurationViewer", () => { + it("renders without error", () => { + + }) +}) diff --git a/src/app/components/ConfigurationViewer/ConfigurationViewer.tsx b/src/app/components/ConfigurationViewer/ConfigurationViewer.tsx new file mode 100755 index 0000000..ecb172f --- /dev/null +++ b/src/app/components/ConfigurationViewer/ConfigurationViewer.tsx @@ -0,0 +1,210 @@ +import { v4 as uuid } from "uuid" +import { + ArAlertType, + ArButtonVariants, + ArPopoverPositions, + ArPopoverSlots, + ArSizes, + ConfigurationViewerProps, + ObjectType, +} from "@armco/types" +import { + API_CONFIG, + Breadcrumb, + Button, + ConfigRowItem, + ENDPOINTS, + InlineMenu, + LoadableIcon, + NamespaceInfoBox, + NamespaceOrgForm, + Network, + notify, + Popover, + setModalState, + useAppDispatch, +} from ".." +import "./ConfigurationViewer.component.scss" + +const ConfigurationViewer = (props: ConfigurationViewerProps): JSX.Element => { + const { namespace, fetchNamespaces } = props + const configurations = namespace?.configs + const dispatch = useAppDispatch() + + const addConfig = (key: string, value: string, _id?: string) => { + const payload: ObjectType = { + key, + value, + namespace: namespace._id, + version: "v1", + } + _id && (payload._id = _id) + Network.post( + API_CONFIG.CONFIG[process.env.NODE_ENV] + + ENDPOINTS.CONFIG.ROOT + + ENDPOINTS.CONFIG.SAVE + + namespace._id, + payload, + ) + .then((res) => { + if (res.status === 200) { + fetchNamespaces() + dispatch( + notify({ + show: true, + message: "Added a new config successfully", + type: ArAlertType.SUCCESS, + uid: uuid(), + }), + ) + } + }) + .catch((e) => { + dispatch( + notify({ + show: true, + message: "Failed to add new config", + type: ArAlertType.ERROR, + uid: uuid(), + }), + ) + }) + } + + const deleteConfig = (_id?: string) => { + Network.get( + API_CONFIG.CONFIG[process.env.NODE_ENV] + + ENDPOINTS.CONFIG.ROOT + + ENDPOINTS.CONFIG.DELETE + + "/" + + namespace._id + + "/" + + _id, + ) + .then((res) => { + if (res.status === 200) { + fetchNamespaces() + dispatch( + notify({ + show: true, + message: "Removed config successfully", + type: ArAlertType.SUCCESS, + uid: uuid(), + }), + ) + } + }) + .catch((e) => { + dispatch( + notify({ + show: true, + message: "Failed to delete config", + type: ArAlertType.ERROR, + uid: uuid(), + }), + ) + }) + } + return ( +
+
+ +
+
+
+ +
+ {configurations && configurations.length > 0 ? ( + configurations.map((configuration) => { + return ( + + addConfig(key, value, _id) + } + onDelete={deleteConfig} + config={configuration} + disabled + /> + ) + }) + ) : ( +
+
+ Nothing here, start by adding configurations above +
+
+ )} +
+
+
+
+
+ ) +} + +export default ConfigurationViewer diff --git a/src/app/components/ConfigurationViewer/index.ts b/src/app/components/ConfigurationViewer/index.ts new file mode 100755 index 0000000..087e3eb --- /dev/null +++ b/src/app/components/ConfigurationViewer/index.ts @@ -0,0 +1,3 @@ +import ConfigurationViewer from "./ConfigurationViewer" + +export default ConfigurationViewer diff --git a/src/app/components/NamespaceInfoBox/NamespaceInfoBox.component.scss b/src/app/components/NamespaceInfoBox/NamespaceInfoBox.component.scss new file mode 100755 index 0000000..1bbfb3b --- /dev/null +++ b/src/app/components/NamespaceInfoBox/NamespaceInfoBox.component.scss @@ -0,0 +1,9 @@ +.ar-NamespaceInfoBox { + .ar-NamespaceInfoBox__namespace-link { + border: var(--ar-border); + background-color: var(--ar-bg-hover); + border-radius: 3px; + font-size: 0.75rem; + cursor: pointer; + } +} diff --git a/src/app/components/NamespaceInfoBox/NamespaceInfoBox.test.ts b/src/app/components/NamespaceInfoBox/NamespaceInfoBox.test.ts new file mode 100755 index 0000000..76c62aa --- /dev/null +++ b/src/app/components/NamespaceInfoBox/NamespaceInfoBox.test.ts @@ -0,0 +1,8 @@ +import React from "react" +import NamespaceInfoBox from "./NamespaceInfoBox" + +describe("NamespaceInfoBox", () => { + it("renders without error", () => { + + }) +}) diff --git a/src/app/components/NamespaceInfoBox/NamespaceInfoBox.tsx b/src/app/components/NamespaceInfoBox/NamespaceInfoBox.tsx new file mode 100755 index 0000000..4ad75b5 --- /dev/null +++ b/src/app/components/NamespaceInfoBox/NamespaceInfoBox.tsx @@ -0,0 +1,128 @@ +import { useRef, useState } from "react" +import { v4 as uuid } from "uuid" +import hljs from "highlight.js/lib/core" +import javascript from "highlight.js/lib/languages/javascript" +import { NamespaceInfoBoxProps } from "@armco/types" +import { + Alert, + API_CONFIG, + ENDPOINTS, + Helper, + Link, + LoadableIcon, + Text, +} from ".." +import "highlight.js/styles/github.css" +import "./NamespaceInfoBox.component.scss" + +hljs.registerLanguage("javascript", javascript) + +const NamespaceInfoBox = (props: NamespaceInfoBoxProps): JSX.Element => { + const { namespace } = props + const codeRef = useRef(null) + const [displayed, show] = useState<{ displayed: boolean }>() + const [message, setMessage] = useState() + const helpText = + "Now that your namespace has been created you can integrate it with your project using the below endpoint" + const nsLink = + API_CONFIG.CONFIG[process.env.NODE_ENV] + + ENDPOINTS.NAMESPACE.ROOT + + ENDPOINTS.NAMESPACE.FETCH + + namespace._id + const nsAllLink = + API_CONFIG.CONFIG[process.env.NODE_ENV] + + ENDPOINTS.NAMESPACE.ROOT + + ENDPOINTS.NAMESPACE.FETCHALL + const codeStr = ` + // API to fetch configurations for the namespace ${namespace.name} + fetch("${nsLink}") + .then((res) => { + if (res.status === 200) { + console.log(res.body.namespace); + } + }) + ` + const code = hljs.highlight("javascript", codeStr).value + + return ( +
+
+
+ {namespace.name} +
+
+
+ + { + Helper.copyOrPrompt(nsLink, () => { + show({ displayed: true }) + setMessage("Link Copied!") + }) + }} + preemptNavigation + /> + + Or to fetch all of your namespaces + + { + Helper.copyOrPrompt(nsAllLink, () => { + show({ displayed: true }) + setMessage("Link Copied!") + }) + }} + preemptNavigation + /> +
+ { + const code = codeRef.current?.innerText + Helper.copyOrPrompt(code, () => { + show({ displayed: true }) + setMessage( + "Copied code for integrating your configurations", + ) + }) + }} + /> +
+                
+              
+
+
+
+ +
+
+ ) +} + +export default NamespaceInfoBox diff --git a/src/app/components/NamespaceInfoBox/index.ts b/src/app/components/NamespaceInfoBox/index.ts new file mode 100755 index 0000000..161886f --- /dev/null +++ b/src/app/components/NamespaceInfoBox/index.ts @@ -0,0 +1,3 @@ +import NamespaceInfoBox from "./NamespaceInfoBox" + +export default NamespaceInfoBox diff --git a/src/app/components/NamespaceOrgForm/NamespaceOrgForm.component.scss b/src/app/components/NamespaceOrgForm/NamespaceOrgForm.component.scss new file mode 100755 index 0000000..e98e7c4 --- /dev/null +++ b/src/app/components/NamespaceOrgForm/NamespaceOrgForm.component.scss @@ -0,0 +1,13 @@ +.ar-NamespaceOrgForm { + header { + min-height: var(--ar-height-header); + height: unset; + background-color: var(--ar-bg-base); + } + + footer { + min-height: var(--ar-height-footer); + height: unset; + background-color: var(--ar-bg-base); + } +} diff --git a/src/app/components/NamespaceOrgForm/NamespaceOrgForm.test.ts b/src/app/components/NamespaceOrgForm/NamespaceOrgForm.test.ts new file mode 100755 index 0000000..17b2cd8 --- /dev/null +++ b/src/app/components/NamespaceOrgForm/NamespaceOrgForm.test.ts @@ -0,0 +1,5 @@ +import NamespaceOrgForm from "./NamespaceOrgForm" + +describe("NamespaceOrgForm", () => { + it("renders without error", () => {}) +}) diff --git a/src/app/components/NamespaceOrgForm/NamespaceOrgForm.tsx b/src/app/components/NamespaceOrgForm/NamespaceOrgForm.tsx new file mode 100755 index 0000000..7bca274 --- /dev/null +++ b/src/app/components/NamespaceOrgForm/NamespaceOrgForm.tsx @@ -0,0 +1,119 @@ +import { ChangeEvent, useState } from "react" +import { v4 as uuid } from "uuid" +import { + ArAlertType, + ArButtonVariants, + ArSizes, + FunctionType, + NamespaceOrgFormProps, +} from "@armco/types" +import { + API_CONFIG, + Button, + ENDPOINTS, + Loader, + NamespaceInfoBox, + Network, + notify, + setModalState, + TextInput, + useAppDispatch, +} from ".." +import "./NamespaceOrgForm.component.scss" + +const NamespaceOrgForm = (props: NamespaceOrgFormProps): JSX.Element => { + const { context } = props + const [namespace, setNamespace] = useState() + const [org, setOrg] = useState() + const [loading, setLoading] = useState() + const dispatch = useAppDispatch() + + const nsURL = + API_CONFIG.CONFIG[process.env.NODE_ENV] + + ENDPOINTS.NAMESPACE.ROOT + + ENDPOINTS.NAMESPACE.CREATE + + const helpText = + context === "org" + ? "You may think of an organization as a way to organize your configuration spaces. A typical use case could be managing configurations for multiple projects, and maintaining configuration for each project in it's own organization. An organization may contain one or more organizations and/or namespaces" + : "You may enter a name that indicates the purpose of the configuration held under this space, for eg. an environment name - test, pre-prod, prod; or a space for form field configurations, eg. field_definitions, etc." + + return ( +
+ {loading && } +
+
+ {context === "org" ? "Create Organization" : "Create a Space"} +
+
+
+ ) => + (context === "org" ? setOrg : setNamespace)( + event.target.value, + )) as FunctionType + } + /> + {helpText} +
+
+
+
+
+
+ ) +} + +export default NamespaceOrgForm diff --git a/src/app/components/NamespaceOrgForm/index.ts b/src/app/components/NamespaceOrgForm/index.ts new file mode 100755 index 0000000..75607ab --- /dev/null +++ b/src/app/components/NamespaceOrgForm/index.ts @@ -0,0 +1,3 @@ +import NamespaceOrgForm from "./NamespaceOrgForm" + +export default NamespaceOrgForm diff --git a/src/app/pages/ConfigurationsPage/ConfigurationsPage.page.scss b/src/app/pages/ConfigurationsPage/ConfigurationsPage.page.scss deleted file mode 100755 index e2530d7..0000000 --- a/src/app/pages/ConfigurationsPage/ConfigurationsPage.page.scss +++ /dev/null @@ -1,14 +0,0 @@ -.ar-ConfigurationsPage { - .ar-ConfigurationNoConfigPrompt { - background-color: var(--ar-bg-base); - - .ar-ConfigurationNoConfigPrompt__message { - line-height: 1.5rem; - text-align: center; - } - } - - .ar-ConfigurationPage__tree-badge { - top: -10px; - } -} diff --git a/src/app/pages/ConfigurationsPage/ConfigurationsPage.slice.ts b/src/app/pages/ConfigurationsPage/ConfigurationsPage.slice.ts deleted file mode 100644 index d0ca8ec..0000000 --- a/src/app/pages/ConfigurationsPage/ConfigurationsPage.slice.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createSlice } from "@reduxjs/toolkit" - -export interface ConfigurationsPageState {} - -const initialState: ConfigurationsPageState = {} - -export const configurationsPageSlice = createSlice({ - name: "configurationsPage", - initialState, - reducers: { - increment: (state) => {}, - }, - extraReducers: (builder) => {}, -}) - -export const { increment } = configurationsPageSlice.actions - -export default configurationsPageSlice.reducer diff --git a/src/app/pages/ConfigurationsPage/ConfigurationsPage.test.ts b/src/app/pages/ConfigurationsPage/ConfigurationsPage.test.ts deleted file mode 100755 index 76ecb84..0000000 --- a/src/app/pages/ConfigurationsPage/ConfigurationsPage.test.ts +++ /dev/null @@ -1,3 +0,0 @@ -describe("ConfigurationsPage", () => { - it("renders without error", () => {}) -}) diff --git a/src/app/pages/ConfigurationsPage/ConfigurationsPage.tsx b/src/app/pages/ConfigurationsPage/ConfigurationsPage.tsx deleted file mode 100755 index a3efb8b..0000000 --- a/src/app/pages/ConfigurationsPage/ConfigurationsPage.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { useEffect, useState } from "react" -import { useLocation, useNavigate } from "react-router-dom" -import { v4 as uuid } from "uuid" -import { - ArAlertType, - ArBadgeType, - ModalProps, - Namespace, - ObjectType, - TreeListData, -} from "@armco/types" -import Main from "../../components/Main" -import Footer from "../../components/molecules/Footer" -import { - API_CONFIG, - ConfigurationList, - ConfigurationLoginPrompt, - ConfigurationNoConfigPrompt, - ConfigurationViewer, - ENDPOINTS, - ErrorBoundary, - getLoggedIn, - getModalState, - getRightPanelContent, - Modal, - Network, - notify, - useAppDispatch, - useAppSelector, -} from "../../components" -import "./ConfigurationsPage.page.scss" - -const ConfigurationsPage = (): JSX.Element => { - const navigate = useNavigate() - const location = useLocation() - const [namespaces, setNamespaces] = useState>() - const [sidebarJuice, setSidebarJuice] = useState>() - const [namespace, setSelectedNamespace] = useState() - const isLoggedIn = useAppSelector(getLoggedIn) - const rightPanelContent = useAppSelector< - | { name?: string; props?: ObjectType; component?: JSX.Element | null } - | undefined - >(getRightPanelContent) - const modalState = useAppSelector(getModalState) - const dispatch = useAppDispatch() - - useEffect(() => { - const orgTree: Array = [] - const onItemSelect = (tld: TreeListData) => { - const ns = tld.data - // setSelectedNamespace(orgTree[0].children[0].data) - navigate("/config/" + ns._id, { replace: true, state: ns }) - } - namespaces && - namespaces.forEach((namespace) => { - const org = namespace.org - if (org._id) { - let orgTreeItem = orgTree.find((item) => item.label === org.name) - if (!orgTreeItem) { - orgTreeItem = { label: org.name, data: org, children: [] } - orgTree.push(orgTreeItem) - } - orgTreeItem.children?.push({ - label: namespace.name, - data: namespace, - badges: [ - { - text: "NS", - type: ArBadgeType.COMPLETE, - }, - ], - onItemSelect, - }) - } - }) - setSidebarJuice(orgTree) - const ns = orgTree[0] && orgTree[0].children && orgTree[0].children[0].data - ns && navigate("/config/" + ns._id, { replace: true, state: ns }) - }, [namespaces]) - - useEffect(() => { - const urlParts = location.pathname.split("/").filter((part) => !!part) - if (urlParts.length > 1 && !!location.state) { - setSelectedNamespace(location.state) - } - }, [location]) - - useEffect(() => { - isLoggedIn && fetchNamespaces() - }, [isLoggedIn]) - - const fetchNamespaces = () => { - Network.get( - API_CONFIG.CONFIG[process.env.NODE_ENV] + - ENDPOINTS.NAMESPACE.ROOT + - ENDPOINTS.NAMESPACE.FETCHALL, - ) - .then((res) => { - if (res.status === 200) { - setNamespaces(res.body) - } - }) - .catch((e) => { - dispatch( - notify({ - show: true, - message: "Failed to fetch your spaces", - type: ArAlertType.ERROR, - uid: uuid(), - }), - ) - }) - } - - return ( -
-
} - mainContent={ - - {isLoggedIn ? ( - namespace ? ( - - ) : ( - - ) - ) : ( - - )} - - } - rightPanelContent={rightPanelContent} - /> - -
-
- ) -} - -export default ConfigurationsPage diff --git a/src/app/pages/ConfigurationsPage/index.ts b/src/app/pages/ConfigurationsPage/index.ts deleted file mode 100755 index bbf1b7e..0000000 --- a/src/app/pages/ConfigurationsPage/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import ConfigurationsPage from "./ConfigurationsPage" - -export default ConfigurationsPage diff --git a/src/features/counter/Counter.tsx b/src/features/counter/Counter.tsx deleted file mode 100644 index cb01aa3..0000000 --- a/src/features/counter/Counter.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { useState } from "react" - -import { useAppSelector, useAppDispatch } from "../../app/hooks" -import { - decrement, - increment, - incrementByAmount, - incrementAsync, - incrementIfOdd, - selectCount, -} from "./counterSlice" - -export function Counter() { - const count = useAppSelector(selectCount) - const dispatch = useAppDispatch() - const [incrementAmount, setIncrementAmount] = useState("2") - - const incrementValue = Number(incrementAmount) || 0 - - return ( -
-
- - {count} - -
-
- setIncrementAmount(e.target.value)} - /> - - - -
-
- ) -} diff --git a/src/features/counter/counterAPI.ts b/src/features/counter/counterAPI.ts deleted file mode 100644 index c5c2a7e..0000000 --- a/src/features/counter/counterAPI.ts +++ /dev/null @@ -1,6 +0,0 @@ -// A mock function to mimic making an async request for data -export function fetchCount(amount = 1) { - return new Promise<{ data: number }>((resolve) => - setTimeout(() => resolve({ data: amount }), 500), - ) -} diff --git a/src/features/counter/counterSlice.spec.ts b/src/features/counter/counterSlice.spec.ts deleted file mode 100644 index 4d465e4..0000000 --- a/src/features/counter/counterSlice.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import counterReducer, { - CounterState, - increment, - decrement, - incrementByAmount, -} from "./counterSlice" - -describe("counter reducer", () => { - const initialState: CounterState = { - value: 3, - status: "idle", - } - it("should handle initial state", () => { - expect(counterReducer(undefined, { type: "unknown" })).toEqual({ - value: 0, - status: "idle", - }) - }) - - it("should handle increment", () => { - const actual = counterReducer(initialState, increment()) - expect(actual.value).toEqual(4) - }) - - it("should handle decrement", () => { - const actual = counterReducer(initialState, decrement()) - expect(actual.value).toEqual(2) - }) - - it("should handle incrementByAmount", () => { - const actual = counterReducer(initialState, incrementByAmount(2)) - expect(actual.value).toEqual(5) - }) -}) diff --git a/src/features/counter/counterSlice.ts b/src/features/counter/counterSlice.ts deleted file mode 100644 index 3870246..0000000 --- a/src/features/counter/counterSlice.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit" -import { RootState, AppThunk } from "../../app/store" -import { fetchCount } from "./counterAPI" - -export interface CounterState { - value: number - status: "idle" | "loading" | "failed" -} - -const initialState: CounterState = { - value: 0, - status: "idle", -} - -// The function below is called a thunk and allows us to perform async logic. It -// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This -// will call the thunk with the `dispatch` function as the first argument. Async -// code can then be executed and other actions can be dispatched. Thunks are -// typically used to make async requests. -export const incrementAsync = createAsyncThunk( - "counter/fetchCount", - async (amount: number) => { - const response = await fetchCount(amount) - // The value we return becomes the `fulfilled` action payload - return response.data - }, -) - -export const counterSlice = createSlice({ - name: "counter", - initialState, - // The `reducers` field lets us define reducers and generate associated actions - reducers: { - increment: (state) => { - // Redux Toolkit allows us to write "mutating" logic in reducers. It - // doesn"t actually mutate the state because it uses the Immer library, - // which detects changes to a "draft state" and produces a brand new - // immutable state based off those changes - state.value += 1 - }, - decrement: (state) => { - state.value -= 1 - }, - // Use the PayloadAction type to declare the contents of `action.payload` - incrementByAmount: (state, action: PayloadAction) => { - state.value += action.payload - }, - }, - // The `extraReducers` field lets the slice handle actions defined elsewhere, - // including actions generated by createAsyncThunk or in other slices. - extraReducers: (builder) => { - builder - .addCase(incrementAsync.pending, (state) => { - state.status = "loading" - }) - .addCase(incrementAsync.fulfilled, (state, action) => { - state.status = "idle" - state.value += action.payload - }) - .addCase(incrementAsync.rejected, (state) => { - state.status = "failed" - }) - }, -}) - -export const { increment, decrement, incrementByAmount } = counterSlice.actions - -// The function below is called a selector and allows us to select a value from -// the state. Selectors can also be defined inline where they"re used instead of -// in the slice file. For example: `useSelector((state: RootState) => state.counter.value)` -export const selectCount = (state: RootState) => state.counter.value - -// We can also write thunks by hand, which may contain both sync and async logic. -// Here"s an example of conditionally dispatching actions based on current state. -export const incrementIfOdd = - (amount: number): AppThunk => - (dispatch, getState) => { - const currentValue = selectCount(getState()) - if (currentValue % 2 === 1) { - dispatch(incrementByAmount(amount)) - } - } - -export default counterSlice.reducer