Backing up

This commit is contained in:
2025-09-16 00:31:45 +05:30
parent d2ca38572b
commit ad60f2b05c
66 changed files with 491 additions and 20113 deletions

31
build-tools/build.sh Executable file
View File

@@ -0,0 +1,31 @@
#!/bin/bash
# Get the directory of the current script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Default values
DEV_FLAG=""
# Parse arguments
for arg in "$@"
do
case $arg in
--dev)
DEV_FLAG="--dev"
shift # Remove --dev from processing
;;
esac
done
echo "[BUILD:SH] Dev flag is: $DEV_FLAG"
echo "[BUILD:SH] Removing build if exists"
rm -rf build
echo "[BUILD:SH] Checking TS Types"
npx tsc
echo "[BUILD:SH] Initiating build..."
# Conditionally use vite-dev.config.ts if --dev flag is present
if [ "$DEV_FLAG" == "--dev" ]; then
vite build --config vite-dev.config.ts
else
vite build
fi

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" ar-theme="th-light-1">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
@@ -8,7 +8,7 @@
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<div id="root" style="height:100vh;"></div>
<script type="module" src="/src/Test.tsx"></script>
</body>
</html>

19341
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,22 +3,20 @@
"private": true,
"version": "0.0.0",
"type": "module",
"main": "./build/cjs/Confitron.js",
"module": "./build/es/Confitron.js",
"types": "./build/types/Confitron.d.ts",
"scripts": {
"dev": "vite",
"start": "vite",
"build": "tsc && vite build",
"generate": "plop",
"atom": "plop atom",
"molecule": "plop molecule",
"component": "plop component",
"page": "plop page",
"preview": "vite preview",
"test": "vitest",
"format": "prettier --write .",
"lint": "eslint .",
"type-check": "tsc"
"dev": "NODE_ENV=development vite --config vite-run.config.ts",
"start": "serve -s build",
"build": "./build-tools/build.sh",
"build:sm": "./build-tools/build.sh --dev"
},
"dependencies": {
"peerDependencies": {
"@armco/types": "^0.0.18",
"@armco/configs": "^0.0.11",
"@armco/utils": "^0.0.29",
"@armco/components": "^0.0.60",
"@reduxjs/toolkit": "^1.8.1",
"react": "^18.2.0",
"react-app-polyfill": "^3.0.0",
@@ -27,28 +25,6 @@
"react-redux": "^8.0.1",
"react-router-dom": "^6.13.0"
},
"devDependencies": {
"@armco/types": "^0.0.6",
"@testing-library/dom": "^9.2.0",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.2.5",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"@types/testing-library__jest-dom": "^5.14.5",
"@vitejs/plugin-react": "^4.0.0",
"eslint": "^8.0.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-prettier": "^4.2.1",
"jsdom": "^21.1.0",
"plop": "^3.1.2",
"prettier": "^2.7.1",
"prettier-config-nick": "^1.0.2",
"sass": "^1.63.4",
"typescript": "^5.0.2",
"vite": "^4.0.0",
"vitest": "^0.30.1"
},
"eslintConfig": {
"extends": [
"react-app",
@@ -63,10 +39,9 @@
}
},
"prettier": "prettier-config-nick",
"main": "index.tsx",
"repository": {
"type": "git",
"url": "git+https://github.com/ReStruct-Corporate-Advantage/.git"
"url": "git+https://github.com/ReStruct-Corporate-Advantage/confitronclient.git"
},
"keywords": [
"components",
@@ -76,7 +51,7 @@
],
"license": "ISC",
"bugs": {
"url": "https://github.com/ReStruct-Corporate-Advantage/react-vite-rtk-template/issues"
"url": "https://github.com/ReStruct-Corporate-Advantage/confitronclient/issues"
},
"homepage": "https://github.com/ReStruct-Corporate-Advantage/react-vite-rtk-template#readme"
"homepage": "https://github.com/ReStruct-Corporate-Advantage/confitronclient#readme"
}

View File

@@ -1,3 +0,0 @@
.c-{{pascalCase name}} {
}

View File

@@ -1,8 +0,0 @@
import React from "react"
import {{pascalCase name}} from "./{{pascalCase name}}"
describe("{{pascalCase name}}", () => {
it("renders without error", () => {
})
})

View File

@@ -1,10 +0,0 @@
import React from "react"
import "./{{pascalCase name}}.component.scss"
interface {{pascalCase name}}Props {}
const {{pascalCase name}} = (props: {{pascalCase name}}Props): JSX.Element => {
return <div className="c-{{pascalCase name}}">In Component {{pascalCase name}}</div>
}
export default {{pascalCase name}}

View File

@@ -1,3 +0,0 @@
import {{pascalCase name}} from "./{{pascalCase name}}.jsx"
export default {{pascalCase name}}

View File

@@ -1,3 +0,0 @@
.c-{{pascalCase name}} {
}

View File

@@ -1,18 +0,0 @@
import { createSlice } from "@reduxjs/toolkit"
export interface {{pascalCase name}}State {}
const initialState: {{pascalCase name}}State = {}
export const {{snakeCase name}}Slice = createSlice({
name: "{{snakeCase name}}",
initialState,
reducers: {
increment: (state) => {},
},
extraReducers: (builder) => {},
})
export const { increment } = {{snakeCase name}}Slice.actions
export default {{snakeCase name}}Slice.reducer

View File

@@ -1,8 +0,0 @@
import React from "react"
import {{pascalCase name}} from "./{{pascalCase name}}"
describe("{{pascalCase name}}", () => {
it("renders without error", () => {
})
})

View File

@@ -1,10 +0,0 @@
import React from "react"
import "./{{pascalCase name}}.module.scss"
interface {{pascalCase name}}Props {}
const {{pascalCase name}} = (props: {{pascalCase name}}Props): JSX.Element => {
return <div className="c-{{pascalCase name}}">In Page {{pascalCase name}}</div>
}
export default {{pascalCase name}}

View File

@@ -1,3 +0,0 @@
import {{pascalCase name}} from "./{{pascalCase name}}.jsx"
export default {{pascalCase name}}

View File

@@ -1,5 +0,0 @@
/* PLOP_INJECT_IMPORT */
export {
/* PLOP_INJECT_EXPORT */
}

View File

@@ -1,207 +0,0 @@
module.exports = (plop) => {
plop.setGenerator("component", {
description: "Create a component",
prompts: [
{
type: "input",
name: "name",
message: "What is your component name?",
},
],
actions: [
{
type: "add",
path: "src/app/components/{{pascalCase name}}/{{pascalCase name}}.tsx",
templateFile: "plop-templates/Component/Component.tsx.hbs",
},
{
type: "add",
path: "src/app/components/{{pascalCase name}}/{{pascalCase name}}.test.ts",
templateFile: "plop-templates/Component/Component.test.ts.hbs",
},
{
type: "add",
path: "src/app/components/{{pascalCase name}}/{{pascalCase name}}.component.scss",
templateFile: "plop-templates/Component/Component.component.scss.hbs",
},
{
type: "add",
path: "src/app/components/{{pascalCase name}}/index.ts",
templateFile: "plop-templates/Component/index.ts.hbs",
},
{
type: "add",
path: "src/app/components/index.ts",
templateFile: "plop-templates/injectable-index.ts.hbs",
skipIfExists: true,
},
{
type: "append",
path: "src/app/components/index.ts",
pattern: `/* PLOP_INJECT_IMPORT */`,
template: `import {{pascalCase name}} from "./{{pascalCase name}}"`,
},
{
type: "append",
path: "src/app/components/index.ts",
pattern: `/* PLOP_INJECT_EXPORT */`,
template: `\t{{pascalCase name}},`,
},
],
})
plop.setGenerator("atom", {
description: "Create a component",
prompts: [
{
type: "input",
name: "name",
message: "What is your component name?",
},
],
actions: [
{
type: "add",
path: "src/app/components/atoms/{{pascalCase name}}/{{pascalCase name}}.tsx",
templateFile: "plop-templates/Component/Component.tsx.hbs",
},
{
type: "add",
path: "src/app/components/atoms/{{pascalCase name}}/{{pascalCase name}}.test.ts",
templateFile: "plop-templates/Component/Component.test.ts.hbs",
},
{
type: "add",
path: "src/app/components/atoms/{{pascalCase name}}/{{pascalCase name}}.component.scss",
templateFile: "plop-templates/Component/Component.component.scss.hbs",
},
{
type: "add",
path: "src/app/components/atoms/{{pascalCase name}}/index.ts",
templateFile: "plop-templates/Component/index.ts.hbs",
},
{
type: "add",
path: "src/app/components/index.ts",
templateFile: "plop-templates/injectable-index.ts.hbs",
skipIfExists: true,
},
{
type: "append",
path: "src/app/components/index.ts",
pattern: `/* PLOP_INJECT_IMPORT */`,
template: `import {{pascalCase name}} from "./atoms/{{pascalCase name}}"`,
},
{
type: "append",
path: "src/app/components/index.ts",
pattern: `/* PLOP_INJECT_EXPORT */`,
template: `\t{{pascalCase name}},`,
},
],
})
plop.setGenerator("molecule", {
description: "Create a rich component",
prompts: [
{
type: "input",
name: "name",
message: "What is your component name?",
},
],
actions: [
{
type: "add",
path: "src/app/components/molecules/{{pascalCase name}}/{{pascalCase name}}.tsx",
templateFile: "plop-templates/Component/Component.tsx.hbs",
},
{
type: "add",
path: "src/app/components/molecules/{{pascalCase name}}/{{pascalCase name}}.test.ts",
templateFile: "plop-templates/Component/Component.test.ts.hbs",
},
{
type: "add",
path: "src/app/components/molecules/{{pascalCase name}}/{{pascalCase name}}.component.scss",
templateFile: "plop-templates/Component/Component.component.scss.hbs",
},
{
type: "add",
path: "src/app/components/molecules/{{pascalCase name}}/index.ts",
templateFile: "plop-templates/Component/index.ts.hbs",
},
{
type: "add",
path: "src/app/components/index.ts",
templateFile: "plop-templates/injectable-index.ts.hbs",
skipIfExists: true,
},
{
type: "append",
path: "src/app/components/index.ts",
pattern: `/* PLOP_INJECT_IMPORT */`,
template: `import {{pascalCase name}} from "./molecules/{{pascalCase name}}"`,
},
{
type: "append",
path: "src/app/components/index.ts",
pattern: `/* PLOP_INJECT_EXPORT */`,
template: `\t{{pascalCase name}},`,
},
],
})
plop.setGenerator("page", {
description: "Create a page",
prompts: [
{
type: "input",
name: "name",
message: "What is your page name?",
},
],
actions: [
{
type: "add",
path: "src/app/pages/{{pascalCase name}}/{{pascalCase name}}.tsx",
templateFile: "plop-templates/Page/Page.tsx.hbs",
},
{
type: "add",
path: "src/app/pages/{{pascalCase name}}/{{pascalCase name}}.test.ts",
templateFile: "plop-templates/Page/Page.test.ts.hbs",
},
{
type: "add",
path: "src/app/pages/{{pascalCase name}}/{{pascalCase name}}.module.scss",
templateFile: "plop-templates/Page/Page.module.scss.hbs",
},
{
type: "add",
path: "src/app/pages/{{pascalCase name}}/index.ts",
templateFile: "plop-templates/Page/index.ts.hbs",
},
{
type: "add",
path: "src/app/pages/{{pascalCase name}}/{{pascalCase name}}.slice.ts",
templateFile: "plop-templates/Page/Page.slice.ts.hbs",
},
{
type: "add",
path: "src/app/pages/index.ts",
templateFile: "plop-templates/injectable-index.ts.hbs",
skipIfExists: true,
},
{
type: "append",
path: "src/app/pages/index.ts",
pattern: `/* PLOP_INJECT_IMPORT */`,
template: `import {{pascalCase name}} from "./{{pascalCase name}}"`,
},
{
type: "append",
path: "src/app/pages/index.ts",
pattern: `/* PLOP_INJECT_EXPORT */`,
template: `\t{{pascalCase name}},`,
},
],
})
}

View File

@@ -1,7 +1,6 @@
import { ChangeEvent, useState } from "react"
import { ArButtonVariants, ConfigRowItemProps } from "@armco/types"
import { Button, LoadableIcon, TextInput } from ".."
import "./ConfigRowItem.component.scss"
import { Button, Icon, TextInput } from "@armco/components"
const ConfigRowItem = (props: ConfigRowItemProps): JSX.Element => {
const { config, disabled, isNew, onAdd, onUpdate, onDelete } = props
@@ -52,40 +51,65 @@ const ConfigRowItem = (props: ConfigRowItemProps): JSX.Element => {
configIsSubmittable ? " cursor-pointer" : " pe-none"
}`}
>
<LoadableIcon
<Icon
icon="fa/FaCheck"
color={configIsSubmittable ? "green" : "rgba(0, 128, 0, 0.3)"}
onClick={() => {
config?._id &&
configIsSubmittable &&
onUpdate &&
onUpdate(config?._id, key, value)
setKey("")
setValue("")
attributes={{
colors: {
fillColor: configIsSubmittable
? "green"
: "rgba(0, 128, 0, 0.3)",
},
}}
events={{
onClick: () => {
config?._id &&
configIsSubmittable &&
onUpdate &&
onUpdate(config?._id, key, value)
setKey("")
setValue("")
},
}}
hoverShadow
/>
</span>
<span className={`me-3${edited ? " pe-none" : " cursor-pointer"}`}>
<LoadableIcon
<Icon
icon={edited ? "rx/RxCross2" : "md/MdModeEditOutline"}
color={isNew || edited ? "rgba(165, 42, 42, 0.3)" : "brown"}
onClick={() => (edited ? setEdited(false) : setEdited(true))}
attributes={{
colors: {
fillColor:
isNew || edited ? "rgba(165, 42, 42, 0.3)" : "brown",
},
}}
events={{
onClick: () => (edited ? setEdited(false) : setEdited(true)),
}}
/>
</span>
<span className={`me-3 ${edited ? "pe-none" : "cursor-pointer"}`}>
<LoadableIcon
<Icon
icon="md/MdAdd"
width="1.3rem"
color={!configIsSubmittable ? "green" : "rgba(0, 128, 0, 0.3)"}
onClick={() => onAdd && onAdd(key, value)}
attributes={{
colors: {
fillColor: !configIsSubmittable
? "green"
: "rgba(0, 128, 0, 0.3)",
},
width: "1.3rem",
}}
events={{ onClick: () => onAdd && onAdd(key, value) }}
/>
</span>
<span className={edited ? "pe-none" : "cursor-pointer"}>
<LoadableIcon
<Icon
icon="ri/RiDeleteBin6Line"
color={isNew || edited ? "rgba(128, 0, 0, 0.3)" : "red"}
onClick={() => onDelete && onDelete(config?._id)}
attributes={{
colors: {
fillColor: isNew || edited ? "rgba(128, 0, 0, 0.3)" : "red",
},
}}
events={{ onClick: () => onDelete && onDelete(config?._id) }}
/>
</span>
</div>

View File

@@ -1,7 +1,7 @@
import { useNavigate } from "react-router-dom"
import { ConfigurationListProps, TreeListData } from "@armco/types"
import { Network, TreeList } from ".."
import "./ConfigurationList.component.scss"
import { stringifyUrl } from "@armco/utils/network"
import { TreeList } from "@armco/components"
const ConfigurationList = (props: ConfigurationListProps): JSX.Element => {
const { list } = props
@@ -9,9 +9,7 @@ const ConfigurationList = (props: ConfigurationListProps): JSX.Element => {
const handleComponentSelect = (treeNode: TreeListData) => {
const params = treeNode.data?.props
treeNode.data?.component &&
navigate(
Network.stringifyUrl(`/components/${treeNode.data?.component}`, params),
)
navigate(stringifyUrl(`/components/${treeNode.data?.component}`, params))
}
return (
<div className="ar-ConfigurationList w-100 p-2">

View File

@@ -1,19 +1,22 @@
import { ArButtonVariants, ConfigurationLoginPromptProps } from "@armco/types"
import { Button, LoadableIcon, setRightPanelContent, useAppDispatch } from ".."
import { usePanelContent } from "@armco/utils/hooks"
import { Button, Icon } from "@armco/components"
import "./ConfigurationLoginPrompt.component.scss"
const ConfigurationLoginPrompt = (
props: ConfigurationLoginPromptProps,
): JSX.Element => {
const dispatch = useAppDispatch()
const { setPanelContent: setRightPanelContent } = usePanelContent(false)
return (
<div className="ar-ConfigurationLoginPrompt p-3 border">
<div className="flex-h-center flex-column">
<h6 className="flex-v-center" style={{ color: "#ffbf00" }}>
<LoadableIcon
classes="me-2"
<Icon
icon="fa/FaExclamationTriangle"
color="#ffbf00"
attributes={{
classes: "me-2",
colors: { fillColor: "#ffbf00" },
}}
/>
Please login to continue
</h6>
@@ -38,7 +41,7 @@ const ConfigurationLoginPrompt = (
variant={ArButtonVariants.SUCCESS}
postIcon="io5/IoArrowForwardCircle"
onClick={() => {
dispatch(setRightPanelContent({ name: "LoginProvider" }))
setRightPanelContent({ componentName: "LoginProvider" })
}}
/>
</div>

View File

@@ -3,30 +3,22 @@ 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()
import { useModalState } from "@armco/utils/hooks"
import { Button, Icon, Popover } from "@armco/components"
import NamespaceOrgForm from "./NamespaceOrgForm"
const ConfigurationNoConfigPrompt = (): ReactNode => {
const { setModalState } = useModalState()
return (
<div className="ar-ConfigurationNoConfigPrompt flex-center flex-column border px-3 py-5 w-50">
<LoadableIcon
classes="mb-4"
<Icon
attributes={{
colors: { fillColor: "lightgrey" },
classes: "mb-4",
size: "7rem",
}}
icon="gr/GrConfigure"
size="7rem"
color="lightgrey"
/>
<p className="ar-ConfigurationNoConfigPrompt__message mx-5">
You don't have any configurations created yet, you can start by
@@ -40,14 +32,12 @@ const ConfigurationNoConfigPrompt = (
variant={ArButtonVariants.SUCCESS}
slot={ArPopoverSlots.ANCHOR}
onClick={() =>
dispatch(
setModalState({
show: true,
isSticky: true,
content: <NamespaceOrgForm context="namespace" />,
closeHandler: () => dispatch(setModalState({ show: false })),
}),
)
setModalState({
show: true,
isSticky: true,
content: <NamespaceOrgForm context="namespace" />,
closeHandler: () => setModalState({ show: false }),
})
}
/>
<div
@@ -69,14 +59,12 @@ const ConfigurationNoConfigPrompt = (
content="+ Organization"
slot={ArPopoverSlots.ANCHOR}
onClick={() =>
dispatch(
setModalState({
show: true,
isSticky: true,
content: <NamespaceOrgForm context="org" />,
closeHandler: () => dispatch(setModalState({ show: false })),
}),
)
setModalState({
show: true,
isSticky: true,
content: <NamespaceOrgForm context="org" />,
closeHandler: () => setModalState({ show: false }),
})
}
/>
<div

View File

@@ -8,28 +8,26 @@ import {
ConfigurationViewerProps,
ObjectType,
} from "@armco/types"
import { API_CONFIG, ENDPOINTS } from "@armco/configs/endpoints"
import { get, post } from "@armco/utils/network"
import { useModalState, useNotification } from "@armco/utils/hooks"
import {
API_CONFIG,
Breadcrumb,
Button,
ConfigRowItem,
ENDPOINTS,
InlineMenu,
LoadableIcon,
NamespaceInfoBox,
NamespaceOrgForm,
Network,
notify,
Icon,
Popover,
setModalState,
useAppDispatch,
} from ".."
} from "@armco/components"
import ConfigRowItem from "./ConfigRowItem"
import NamespaceInfoBox from "./NamespaceInfoBox"
import NamespaceOrgForm from "./NamespaceOrgForm"
import "./ConfigurationViewer.component.scss"
const ConfigurationViewer = (props: ConfigurationViewerProps): JSX.Element => {
const { namespace, fetchNamespaces } = props
const configurations = namespace?.configs
const dispatch = useAppDispatch()
const { notify } = useNotification()
const { setModalState } = useModalState()
const addConfig = (key: string, value: string, _id?: string) => {
const payload: ObjectType = {
@@ -39,7 +37,7 @@ const ConfigurationViewer = (props: ConfigurationViewerProps): JSX.Element => {
version: "v1",
}
_id && (payload._id = _id)
Network.post(
post(
API_CONFIG.CONFIG[process.env.NODE_ENV] +
ENDPOINTS.CONFIG.ROOT +
ENDPOINTS.CONFIG.SAVE +
@@ -49,30 +47,26 @@ const ConfigurationViewer = (props: ConfigurationViewerProps): JSX.Element => {
.then((res) => {
if (res.status === 200) {
fetchNamespaces()
dispatch(
notify({
show: true,
message: "Added a new config successfully",
type: ArAlertType.SUCCESS,
uid: uuid(),
}),
)
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(),
}),
)
notify({
show: true,
message: "Failed to add new config",
type: ArAlertType.ERROR,
uid: uuid(),
})
})
}
const deleteConfig = (_id?: string) => {
Network.get(
get(
API_CONFIG.CONFIG[process.env.NODE_ENV] +
ENDPOINTS.CONFIG.ROOT +
ENDPOINTS.CONFIG.DELETE +
@@ -84,25 +78,21 @@ const ConfigurationViewer = (props: ConfigurationViewerProps): JSX.Element => {
.then((res) => {
if (res.status === 200) {
fetchNamespaces()
dispatch(
notify({
show: true,
message: "Removed config successfully",
type: ArAlertType.SUCCESS,
uid: uuid(),
}),
)
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(),
}),
)
notify({
show: true,
message: "Failed to delete config",
type: ArAlertType.ERROR,
uid: uuid(),
})
})
}
return (
@@ -140,33 +130,31 @@ const ConfigurationViewer = (props: ConfigurationViewerProps): JSX.Element => {
Org: {},
Namespace: {
onClick: () =>
dispatch(
setModalState({
show: true,
isSticky: true,
content: <NamespaceOrgForm context="namespace" />,
closeHandler: () =>
dispatch(setModalState({ show: false })),
}),
),
setModalState({
show: true,
isSticky: true,
content: <NamespaceOrgForm context="namespace" />,
closeHandler: () => setModalState({ show: false }),
}),
},
}}
/>
</Popover>
<LoadableIcon
classes="cursor-pointer"
<Icon
attributes={{
classes: "cursor-pointer",
colors: { fillColor: "#0d6efd" },
}}
icon="io/IoMdInformationCircle"
color="#0d6efd"
onClick={() =>
dispatch(
events={{
onClick: () =>
setModalState({
show: true,
isSticky: true,
content: <NamespaceInfoBox namespace={namespace} />,
closeHandler: () => dispatch(setModalState({ show: false })),
closeHandler: () => setModalState({ show: false }),
}),
)
}
}}
/>
</div>
<div className="ar-ConfigurationViewer__content flex-1 d-flex px-3 py-2 w-100">

14
src/Confitron.page.scss Executable file
View File

@@ -0,0 +1,14 @@
.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;
}
}

125
src/Confitron.tsx Executable file
View File

@@ -0,0 +1,125 @@
import { useEffect, useState } from "react"
import { useLocation, useNavigate } from "react-router-dom"
import { v4 as uuid } from "uuid"
import { ArAlertType, ArBadgeType, Namespace, TreeListData } from "@armco/types"
import { API_CONFIG, ENDPOINTS } from "@armco/configs/endpoints"
import { get } from "@armco/utils/network"
import {
useLoggedIn,
useModalState,
useNotification,
usePanelContent,
} from "@armco/utils/hooks"
import { ErrorBoundary, Modal, Main } from "@armco/components"
import ConfigurationList from "./ConfigurationList"
import ConfigurationLoginPrompt from "./ConfigurationLoginPrompt"
import ConfigurationNoConfigPrompt from "./ConfigurationNoConfigPrompt"
import ConfigurationViewer from "./ConfigurationViewer"
import "./Confitron.page.scss"
const Confitron = (): JSX.Element => {
const navigate = useNavigate()
const location = useLocation()
const [namespaces, setNamespaces] = useState<Array<Namespace>>()
const [sidebarJuice, setSidebarJuice] = useState<Array<TreeListData>>()
const [namespace, setSelectedNamespace] = useState<Namespace>()
const { isLoggedIn } = useLoggedIn()
const { notify } = useNotification()
const { modalState } = useModalState()
const { panelContent: rightPanelContent } = usePanelContent(false)
useEffect(() => {
const orgTree: Array<TreeListData> = []
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 = () => {
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) => {
notify({
show: true,
message: "Failed to fetch your spaces",
type: ArAlertType.ERROR,
uid: uuid(),
})
})
}
return (
<div className="ar-ConfigurationsPage">
<Main
contentClasses="p-2"
drawerContent={isLoggedIn && <ConfigurationList list={sidebarJuice} />}
mainContent={
<ErrorBoundary>
{isLoggedIn ? (
namespace ? (
<ConfigurationViewer
namespace={namespace}
fetchNamespaces={fetchNamespaces}
/>
) : (
<ConfigurationNoConfigPrompt />
)
) : (
<ConfigurationLoginPrompt />
)}
</ErrorBoundary>
}
rightPanelContent={rightPanelContent}
/>
<Modal {...modalState} />
</div>
)
}
export default Confitron

View File

@@ -3,15 +3,9 @@ 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 { API_CONFIG, ENDPOINTS } from "@armco/configs/endpoints"
import { copyOrPrompt } from "@armco/utils/helper"
import { Alert, Link, Icon, Text } from "@armco/components"
import "highlight.js/styles/github.css"
import "./NamespaceInfoBox.component.scss"
@@ -67,7 +61,7 @@ const NamespaceInfoBox = (props: NamespaceInfoBoxProps): JSX.Element => {
label={nsLink}
classes="ar-NamespaceInfoBox__namespace-link px-3 py-2 mb-3"
onClick={() => {
Helper.copyOrPrompt(nsLink, () => {
copyOrPrompt(nsLink, () => {
show({ displayed: true })
setMessage("Link Copied!")
})
@@ -82,7 +76,7 @@ const NamespaceInfoBox = (props: NamespaceInfoBoxProps): JSX.Element => {
label={nsAllLink}
classes="ar-NamespaceInfoBox__namespace-link px-3 py-2 mb-3"
onClick={() => {
Helper.copyOrPrompt(nsAllLink, () => {
copyOrPrompt(nsAllLink, () => {
show({ displayed: true })
setMessage("Link Copied!")
})
@@ -90,19 +84,25 @@ const NamespaceInfoBox = (props: NamespaceInfoBoxProps): JSX.Element => {
preemptNavigation
/>
<div className="ar-NamespaceInfoBox__sample-code border p-2 w-100 overflow-auto position-relative">
<LoadableIcon
classes="ar-NamespaceInfoBox__copy-button position-absolute"
<Icon
icon="md/MdContentCopy"
color="grey"
hoverColor="black"
onClick={() => {
const code = codeRef.current?.innerText
Helper.copyOrPrompt(code, () => {
show({ displayed: true })
setMessage(
"Copied code for integrating your configurations",
)
})
attributes={{
classes: "ar-NamespaceInfoBox__copy-button position-absolute",
colors: {
fillColor: "grey",
hoverFillColor: "black",
},
}}
events={{
onClick: () => {
const code = codeRef.current?.innerText
copyOrPrompt(code, () => {
show({ displayed: true })
setMessage(
"Copied code for integrating your configurations",
)
})
},
}}
/>
<pre>

View File

@@ -7,18 +7,11 @@ import {
FunctionType,
NamespaceOrgFormProps,
} from "@armco/types"
import {
API_CONFIG,
Button,
ENDPOINTS,
Loader,
NamespaceInfoBox,
Network,
notify,
setModalState,
TextInput,
useAppDispatch,
} from ".."
import { API_CONFIG, ENDPOINTS } from "@armco/configs/endpoints"
import { useNotification, useModalState } from "@armco/utils/hooks"
import { post } from "@armco/utils/network"
import { Button, Loader, TextInput } from "@armco/components"
import NamespaceInfoBox from "./NamespaceInfoBox"
import "./NamespaceOrgForm.component.scss"
const NamespaceOrgForm = (props: NamespaceOrgFormProps): JSX.Element => {
@@ -26,7 +19,8 @@ const NamespaceOrgForm = (props: NamespaceOrgFormProps): JSX.Element => {
const [namespace, setNamespace] = useState<string>()
const [org, setOrg] = useState<string>()
const [loading, setLoading] = useState<boolean>()
const dispatch = useAppDispatch()
const { notify } = useNotification()
const { setModalState } = useModalState()
const nsURL =
API_CONFIG.CONFIG[process.env.NODE_ENV] +
@@ -69,38 +63,32 @@ const NamespaceOrgForm = (props: NamespaceOrgFormProps): JSX.Element => {
content="Submit"
onClick={() => {
setLoading(true)
Network.post(nsURL, { org, namespace })
post(nsURL, { org, namespace })
.then((res) => {
console.log(res)
setLoading(false)
if (res.status === 200) {
dispatch(
notify({
show: true,
message: "Namespace created successfully!",
type: ArAlertType.SUCCESS,
uid: uuid(),
}),
)
dispatch(
setModalState({
content: namespace ? (
<NamespaceInfoBox namespace={res.body.namespace} />
) : null,
}),
)
notify({
show: true,
message: "Namespace created successfully!",
type: ArAlertType.SUCCESS,
uid: uuid(),
})
setModalState({
content: namespace ? (
<NamespaceInfoBox namespace={res.body.namespace} />
) : null,
})
}
})
.catch((err) => {
setLoading(false)
dispatch(
notify({
show: true,
message: "Failed to create namespace/org",
type: ArAlertType.ERROR,
uid: uuid(),
}),
)
notify({
show: true,
message: "Failed to create namespace/org",
type: ArAlertType.ERROR,
uid: uuid(),
})
})
}}
/>
@@ -108,7 +96,7 @@ const NamespaceOrgForm = (props: NamespaceOrgFormProps): JSX.Element => {
variant={ArButtonVariants.SECONDARY}
size={ArSizes.SMALL}
content="Cancel"
onClick={() => dispatch(setModalState({ show: false }))}
onClick={() => setModalState({ show: false })}
/>
</footer>
</div>

17
src/Test.tsx Normal file
View File

@@ -0,0 +1,17 @@
import React from "react"
import ReactDOM from "react-dom/client"
import { ArProvider } from "@armco/utils/providers"
import Confitron from "./Confitron"
import { BrowserRouter } from "react-router-dom"
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement)
root.render(
<React.StrictMode>
<ArProvider>
<BrowserRouter>
<Confitron />
</BrowserRouter>
</ArProvider>
</React.StrictMode>,
)

View File

@@ -1,12 +0,0 @@
import { useRoutes } from "react-router-dom"
import * as pages from "./pages"
import Helper from "./utils/helper"
import ROUTES from "./routes"
Helper.populateComponentsInRoutes(ROUTES, pages)
interface RouterProps {}
const Router = (props: RouterProps): JSX.Element | null => useRoutes(ROUTES)
export default Router

View File

@@ -1,3 +0,0 @@
.ar-ConfigRowItem {
}

View File

@@ -1,8 +0,0 @@
import React from "react"
import ConfigRowItem from "./ConfigRowItem"
describe("ConfigRowItem", () => {
it("renders without error", () => {
})
})

View File

@@ -1,3 +0,0 @@
import ConfigRowItem from "./ConfigRowItem"
export default ConfigRowItem

View File

@@ -1,3 +0,0 @@
.ar-ConfigurationList {
}

View File

@@ -1,8 +0,0 @@
import React from "react"
import ConfigurationList from "./ConfigurationList"
describe("ConfigurationList", () => {
it("renders without error", () => {
})
})

View File

@@ -1,3 +0,0 @@
import ConfigurationList from "./ConfigurationList"
export default ConfigurationList

View File

@@ -1,8 +0,0 @@
import React from "react"
import ConfigurationLoginPrompt from "./ConfigurationLoginPrompt"
describe("ConfigurationLoginPrompt", () => {
it("renders without error", () => {
})
})

View File

@@ -1,3 +0,0 @@
import ConfigurationLoginPrompt from "./ConfigurationLoginPrompt"
export default ConfigurationLoginPrompt

View File

@@ -1,3 +0,0 @@
.ar-ConfigurationNoConfigPrompt {
}

View File

@@ -1,8 +0,0 @@
import React from "react"
import ConfigurationNoConfigPrompt from "./ConfigurationNoConfigPrompt"
describe("ConfigurationNoConfigPrompt", () => {
it("renders without error", () => {
})
})

View File

@@ -1,3 +0,0 @@
import ConfigurationNoConfigPrompt from "./ConfigurationNoConfigPrompt"
export default ConfigurationNoConfigPrompt

View File

@@ -1,8 +0,0 @@
import React from "react"
import ConfigurationViewer from "./ConfigurationViewer"
describe("ConfigurationViewer", () => {
it("renders without error", () => {
})
})

View File

@@ -1,3 +0,0 @@
import ConfigurationViewer from "./ConfigurationViewer"
export default ConfigurationViewer

View File

@@ -1,8 +0,0 @@
import React from "react"
import NamespaceInfoBox from "./NamespaceInfoBox"
describe("NamespaceInfoBox", () => {
it("renders without error", () => {
})
})

View File

@@ -1,3 +0,0 @@
import NamespaceInfoBox from "./NamespaceInfoBox"
export default NamespaceInfoBox

View File

@@ -1,5 +0,0 @@
import NamespaceOrgForm from "./NamespaceOrgForm"
describe("NamespaceOrgForm", () => {
it("renders without error", () => {})
})

View File

@@ -1,3 +0,0 @@
import NamespaceOrgForm from "./NamespaceOrgForm"
export default NamespaceOrgForm

View File

@@ -1,6 +0,0 @@
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"
import type { RootState, AppDispatch } from "./store"
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

View File

@@ -1,3 +0,0 @@
.c-Home {
}

View File

@@ -1,18 +0,0 @@
import { createSlice } from "@reduxjs/toolkit"
export interface HomeState {}
const initialState: HomeState = {}
export const homeSlice = createSlice({
name: "home",
initialState,
reducers: {
increment: (state) => {},
},
extraReducers: (builder) => {},
})
export const { increment } = homeSlice.actions
export default homeSlice.reducer

View File

@@ -1,8 +0,0 @@
import React from "react"
import Home from "./Home"
describe("Home", () => {
it("renders without error", () => {
})
})

View File

@@ -1,14 +0,0 @@
import React from "react"
import "./Home.module.scss"
interface HomeProps {}
const Home = props => {
return (
<div className="c-Home">
In Page Home
</div>
)
}
export default Home

View File

@@ -1,3 +0,0 @@
import Home from "./Home.jsx"
export default Home

View File

@@ -1,7 +0,0 @@
/* PLOP_INJECT_IMPORT */
import Home from "./Home"
export {
/* PLOP_INJECT_EXPORT */
Home,
}

View File

@@ -1,9 +0,0 @@
const ROUTES = [
{
path: "/",
class: "landing",
element: "Home",
},
]
export default ROUTES

View File

@@ -1,18 +0,0 @@
html, body, #root {
height: 100%;
width: 100%;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View File

@@ -1,17 +0,0 @@
import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit"
import counterReducer from "../features/counter/counterSlice"
export const store = configureStore({
reducer: {
counter: counterReducer,
},
})
export type AppDispatch = typeof store.dispatch
export type RootState = ReturnType<typeof store.getState>
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>

View File

@@ -1,6 +0,0 @@
interface RouteConfig {
path: String
class?: String
element: String | JSX.Element | null
children?: Array<RouteConfig>
}

View File

@@ -1,15 +0,0 @@
class Helper {
static populateComponentsInRoutes(routes: RouteConfig[], components: any) {
routes &&
routes.forEach((route) => {
const Component: JSX.ElementType =
components[route.element as keyof object]
route.element = <Component />
if (route.children) {
Helper.populateComponentsInRoutes(route.children, components)
}
})
}
}
export default Helper

View File

@@ -1,19 +0,0 @@
import React from "react"
import ReactDOM from "react-dom/client"
import { BrowserRouter } from "react-router-dom"
import { Provider } from "react-redux"
import { store } from "./app/store"
import Router from "./app/Router"
import "./app/static/styles/global.scss"
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement)
root.render(
<React.StrictMode>
<BrowserRouter>
<Provider store={store}>
<Router />
</Provider>
</BrowserRouter>
</React.StrictMode>,
)

View File

@@ -4,7 +4,7 @@
declare namespace NodeJS {
interface ProcessEnv {
readonly NODE_ENV: "development" | "production" | "test"
readonly NODE_ENV: "development" | "production"
readonly PUBLIC_URL: string
}
}
@@ -40,16 +40,16 @@ declare module "*.png" {
}
declare module "*.webp" {
const src: string
export default src
const src: string
export default src
}
declare module "*.svg" {
import * as React from "react"
export const ReactComponent: React.FunctionComponent<React.SVGProps<
SVGSVGElement
> & { title?: string }>
export const ReactComponent: React.FunctionComponent<
React.SVGProps<SVGSVGElement> & { title?: string }
>
const src: string
export default src

View File

@@ -1,5 +0,0 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import "@testing-library/jest-dom"

42
vite-dev.config.ts Normal file
View File

@@ -0,0 +1,42 @@
import { resolve } from "path"
import { glob } from "glob"
import { defineConfig } from "vitest/config"
import react from "@vitejs/plugin-react"
import dts from "vite-plugin-dts"
import { libInjectCss } from "vite-plugin-lib-inject-css"
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), libInjectCss(), dts({ outDir: "build/types" })],
build: {
outDir: "build",
lib: {
entry: glob.sync(resolve(__dirname, "src/**/!(*.d|Test).{ts,tsx}")),
},
sourcemap: true,
rollupOptions: {
treeshake: true,
external: [
new RegExp("react*"),
new RegExp("highcharts*"),
new RegExp("@armco/*"),
"d3",
"uuid",
],
output: [
{
format: "es",
dir: "build/es",
entryFileNames: "[name].js",
chunkFileNames: "[name]-chunk.js",
},
{
format: "cjs",
dir: "build/cjs",
entryFileNames: "[name].js",
chunkFileNames: "[name]-chunk.js",
},
],
},
},
})

26
vite-run.config.ts Normal file
View File

@@ -0,0 +1,26 @@
import { defineConfig } from "vitest/config"
import react from "@vitejs/plugin-react"
import { config } from "dotenv"
config()
// https://vitejs.dev/config/
export default defineConfig({
define: {
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
},
plugins: [react()],
server: {
open: true,
},
build: {
outDir: "build",
sourcemap: true,
},
test: {
globals: true,
environment: "jsdom",
setupFiles: "src/setupTests",
mockReset: true,
},
})

View File

@@ -1,20 +1,41 @@
import { resolve } from "path"
import { glob } from "glob"
import { defineConfig } from "vitest/config"
import react from "@vitejs/plugin-react"
import dts from "vite-plugin-dts"
import { libInjectCss } from "vite-plugin-lib-inject-css"
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
server: {
open: true,
},
plugins: [react(), libInjectCss(), dts({ outDir: "build/types" })],
build: {
outDir: "build",
sourcemap: true,
},
test: {
globals: true,
environment: "jsdom",
setupFiles: "src/setupTests",
mockReset: true,
lib: {
entry: glob.sync(resolve(__dirname, "src/**/!(*.d|Test).{ts,tsx}")),
},
rollupOptions: {
treeshake: true,
external: [
new RegExp("react*"),
new RegExp("highcharts*"),
new RegExp("@armco/*"),
"d3",
"uuid",
],
output: [
{
format: "es",
dir: "build/es",
entryFileNames: "[name].js",
chunkFileNames: "[name]-chunk.js",
},
{
format: "cjs",
dir: "build/cjs",
entryFileNames: "[name].js",
chunkFileNames: "[name]-chunk.js",
},
],
},
},
})