diff --git a/package.json b/package.json
index 63097c7..d6d25fc 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "insomnia-script-hub",
"private": true,
- "version": "0.0.240",
+ "version": "0.0.763",
"main": "dist/index.js",
"insomnia": {
"name": "script-hub",
@@ -31,11 +31,14 @@
}
},
"dependencies": {
+ "@emotion/react": "^11.14.0",
+ "@emotion/styled": "^11.14.0",
+ "@mui/material": "^7.1.1",
"prop-types": "^15.5.7",
- "react-json-tree": "^0.19.0",
- "react-json-view-lite": "^1.4.0",
"react": "^18.3.1",
- "react-dom": "^18.3.1"
+ "react-dom": "^18.3.1",
+ "react-json-tree": "^0.19.0",
+ "react-json-view-lite": "^1.4.0"
},
"devDependencies": {
"@seald-io/nedb": "^4.0.4",
@@ -50,10 +53,10 @@
"eslint": "^8.44.0",
"eslint-plugin-react": "^7.32.2",
"npm-watch": "^0.11.0",
+ "react-resizable-panels": "^2.0.20",
"request": "^2.88.0",
"rimraf": "^5.0.1",
"sass": "^1.63.6",
- "semver": "^7.3.8",
- "react-resizable-panels": "^2.0.20"
+ "semver": "^7.3.8"
}
}
\ No newline at end of file
diff --git a/src/app/fragments/checkbox/checkbox.tsx b/src/app/fragments/checkbox/checkbox.tsx
new file mode 100644
index 0000000..9888633
--- /dev/null
+++ b/src/app/fragments/checkbox/checkbox.tsx
@@ -0,0 +1,26 @@
+import React, { useState } from 'react';
+
+const CheckboxComponent = (props: { title: string; updateData: any; }) => {
+ const [isChecked, setIsChecked] = useState(false);
+
+ const handleCheckboxChange = (event: { target: { name: string; checked: boolean | ((prevState: boolean) => boolean); }; }) => {
+ setIsChecked(event.target.checked);
+ props.updateData({name: event.target.name, value: event.target.checked});
+ };
+
+ return (
+
+
+
+ );
+}
+
+export default CheckboxComponent;
\ No newline at end of file
diff --git a/src/app/fragments/input/input.tsx b/src/app/fragments/input/input.tsx
new file mode 100644
index 0000000..7a54501
--- /dev/null
+++ b/src/app/fragments/input/input.tsx
@@ -0,0 +1,39 @@
+import React from "react";
+import { InputClass } from "../../model/input-class";
+
+const InputImpl = (props: { id: React.Key, title: string, name: string, value: string }) => {
+ return (
+
+
+
+
+ )
+}
+
+export const Input = InputImpl;
+
+const InputUpdateImpl = (props: { id: React.Key, title: string, name: string, updateInput: (arg: any) => React.MouseEventHandler }) => {
+ return (
+ <>
+
+
+ >
+ )
+}
+
+export const InputUpdate = (props: { inputClass: InputClass, updateInput: any }) => {
+ return (
+ <>
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/src/app/model/argument-model.ts b/src/app/model/argument-model.ts
new file mode 100644
index 0000000..9e2d34f
--- /dev/null
+++ b/src/app/model/argument-model.ts
@@ -0,0 +1,4 @@
+interface ArgumentModel {
+ title: string;
+ value: string;
+}
\ No newline at end of file
diff --git a/src/app/model/card-data-model.ts b/src/app/model/card-data-model.ts
new file mode 100644
index 0000000..8a7289a
--- /dev/null
+++ b/src/app/model/card-data-model.ts
@@ -0,0 +1,7 @@
+interface CardData {
+ id: number;
+ title: string;
+ selectedOption: string;
+ options: Data[];
+ filePath: string;
+}
\ No newline at end of file
diff --git a/src/app/model/data-list.ts b/src/app/model/data-list.ts
index 50da713..65c8ff2 100644
--- a/src/app/model/data-list.ts
+++ b/src/app/model/data-list.ts
@@ -1,8 +1,11 @@
interface DataList {
types: Data[];
+ type: string;
}
interface Data {
type: string;
name: string;
+ cmdList?: any[];
+ types?: string[];
}
\ No newline at end of file
diff --git a/src/app/model/envs-data.ts b/src/app/model/envs-data.ts
new file mode 100644
index 0000000..ed73b92
--- /dev/null
+++ b/src/app/model/envs-data.ts
@@ -0,0 +1,20 @@
+interface EnvsData {
+ envs: Env[];
+ additionalCmd: AdditionalCmd[];
+}
+
+interface Env {
+ type: string;
+ envVars: EnvVar[];
+}
+
+interface EnvVar {
+ key: string;
+ value: string;
+}
+
+interface AdditionalCmd {
+ type: string;
+ value: string;
+ cmd: string;
+}
\ No newline at end of file
diff --git a/src/app/model/input-class.ts b/src/app/model/input-class.ts
new file mode 100644
index 0000000..edb4849
--- /dev/null
+++ b/src/app/model/input-class.ts
@@ -0,0 +1,6 @@
+export class InputClass {
+ id!: number;
+ title!: string;
+ name!: string;
+ value!: string;
+}
\ No newline at end of file
diff --git a/src/app/model/input-model.ts b/src/app/model/input-model.ts
new file mode 100644
index 0000000..73f9587
--- /dev/null
+++ b/src/app/model/input-model.ts
@@ -0,0 +1,10 @@
+interface InputList {
+ inputList: Input[];
+}
+
+interface Input {
+ id: React.Key;
+ title: string;
+ name: string;
+ value: string;
+}
\ No newline at end of file
diff --git a/src/app/model/settings-model.ts b/src/app/model/settings-model.ts
new file mode 100644
index 0000000..7450dc2
--- /dev/null
+++ b/src/app/model/settings-model.ts
@@ -0,0 +1,3 @@
+interface SettingsModel {
+ scriptHubPath: string;
+}
\ No newline at end of file
diff --git a/src/app/scripthub/additional-cmd-component.tsx b/src/app/scripthub/additional-cmd-component.tsx
new file mode 100644
index 0000000..dae2250
--- /dev/null
+++ b/src/app/scripthub/additional-cmd-component.tsx
@@ -0,0 +1,151 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { firstElement, getSettings, isEmpty, isListEmpty, readFileEnvs } from '../utils/Utils';
+import CheckboxComponent from '../fragments/checkbox/checkbox';
+
+const emptyCardData: CardData = { id: -1, title: '', selectedOption: '', options: [], filePath: '' };
+const emptyListCardData: CardData[] = [emptyCardData];
+const settingsFilename: string = 'settings.json';
+const scriptHubPath = getSettings(settingsFilename).scriptHubPath;
+
+const AdditionalCmdComponent = (props: { operation: CardData; selectedProject: string; updateData: any; }) => {
+ const [cards, setCards] = useState(emptyListCardData);
+ const isFirstRender = useRef(true);
+
+ useEffect(() => {
+ setCards(prev => prev.filter(operation => operation.id !== -1));
+ }, [])
+
+ useEffect(() => {
+ props.updateData([]);
+ }, [props.operation])
+
+ useEffect(() => {
+ if (isFirstRender.current) {
+ isFirstRender.current = false;
+ return;
+ }
+
+ if (cards.length >= 5) {
+ return;
+ }
+
+ let newElem: CardData;
+ if (isListEmpty(props.operation.options) || validateCards()) {
+ return;
+ }
+
+ const operationOptionElem = firstElement(props.operation.options);
+ console.log("0--------------->");
+ console.log(operationOptionElem.cmdList);
+ if (!isListEmpty(operationOptionElem.cmdList)) {
+ newElem = buildCardOfEnvType(operationOptionElem.cmdList);
+ } else {
+ newElem = emptyCardData;
+ }
+
+ console.log("1--------------->");
+ console.log(newElem);
+ newElem.options.push(...buildBooleanlist(operationOptionElem.cmdList));
+ /*if (!isListEmpty(operationOptionElem.cmdList) && operationOptionElem.cmdList.filter((cmd: { name: any; type: any; }) => cmd.name === "boolean")) {
+ const id = cards.length;
+ const title = "booleanList";
+ const selectedOption = "booleanList";
+ const options: Data[] = [];
+ const filePath = "";
+ newElem = { id: id, title: title, selectedOption: selectedOption, options: options, filePath: filePath };
+ }*/
+
+ if (newElem.id === -1 && newElem.options.length > 0) {
+ const id = cards.length;
+ const title = "booleanList";
+ const selectedOption = "booleanList";
+ const filePath = "";
+ newElem = { id: id, title: title, selectedOption: selectedOption, options: newElem.options, filePath: filePath };
+ }
+
+ if (newElem.id === -1) {
+ return;
+ }
+
+ console.log("2--------------->");
+ console.log(newElem);
+
+ setCards(prev => [
+ ...prev,
+ newElem
+ ]);
+ }, [cards, props.selectedProject]);
+
+ const validateCards = (): boolean => {
+ return cards.filter(card => card.title === "additionalCmd").length === 1 ||
+ cards.filter(card => card.title === "booleanList").length === 1;
+ }
+
+ const buildCardOfEnvType = (cmdList: any): CardData => {
+ const cmd: { name: any; type: any; } = firstElement(cmdList.filter((cmd: { name: any; type: any; }) => cmd.name === "additionalCmd"));
+
+ if (cmd === null) {
+ return emptyCardData;
+ }
+
+ const id = cards.length;
+ const title = cmd.name;
+ const selectedOption = cmd.type;
+ const options: Data[] = listOfAllAdditionalCmd();
+ const filePath = "";
+
+ if (isListEmpty(options)) {
+ return emptyCardData;
+ }
+
+ return { id: id, title: title, selectedOption: selectedOption, options: options, filePath: filePath };
+ };
+
+ const handleSelectChange = (data: any) => {
+ props.updateData({ id: 0, title: data.name, selectedOption: data.value, options: [], filePath: "" });
+ };
+
+ const listOfAllAdditionalCmd = (): Data[] => {
+ if (isEmpty(props.selectedProject)) {
+ return [];
+ }
+
+ const settings = readFileEnvs(`${scriptHubPath}/projects/${props.selectedProject}`);
+ const envList: Data[] = [];
+
+ settings.additionalCmd?.filter(arg => arg.type === "arg").forEach(env => {
+ envList.push({
+ type: env.value,
+ name: env.value
+ });
+ });
+ return envList;
+ }
+
+ const buildBooleanlist = (cmdList: any) => {
+ const cmdBoolean = cmdList.filter((cmd: { name: any; type: any; }) => cmd.type === "boolean");
+ if (isListEmpty(cmdBoolean)) {
+ return [];
+ }
+
+ const envList: Data[] = [];
+ cmdBoolean.forEach((env: { type: any; name: any; }) => {
+ envList.push({
+ type: env.type,
+ name: env.name
+ });
+ });
+
+ return envList;
+ }
+
+ return (
+
+ {!isListEmpty(cards) && firstElement(cards) !== null && !isListEmpty(firstElement(cards).options) && firstElement(cards).options.map((cmd: { name: string; }) => (
+
+ ))}
+
+ );
+};
+
+export default AdditionalCmdComponent;
\ No newline at end of file
diff --git a/src/app/scripthub/arg-list.tsx b/src/app/scripthub/arg-list.tsx
deleted file mode 100644
index 38a8021..0000000
--- a/src/app/scripthub/arg-list.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import React, { useEffect, useState } from "react";
-import { createRoot } from "react-dom/client";
-import { Dropdown } from "../utils/dropdown/dropdown";
-import { firstElement, getElementByType, readFileDataList } from "../utils/Utils";
-const os = require('os');
-const homeDir = os.homedir();
-
-const settingsFilename: string = 'settings.json';
-const scriptHubBasePathName: string = `${homeDir}/Documents/Confi/ScriptsHub/`;
-let scriptTypePathName = "";
-let scriptTypeData: DataList;
-let operationsData: DataList;
-
-
-const ArgList = (props: { argList: any[] }) => {
- const [scriptType, setScriptType] = useState("");
- const [operation, setOperation] = useState("");
-
- useEffect(() => {
- scriptTypeData = readFileDataList(scriptHubBasePathName, settingsFilename);
- const scriptTypeInit = firstElement(scriptTypeData.types).name;
- setScriptType(scriptTypeInit);
- setOperationData(scriptTypeInit);
- }, [])
-
-
- const handleOnClick = async () => {
- console.log(scriptType);
-
- /* const options: ExecSyncOptionsWithStringEncoding = {
- shell: "C:\\Program Files\\Git\\bin\\bash.exe",
- encoding: "utf8"
- };*/
-
- //const executeCmd = "scriptHub.sh scriptType=runners type=update projects=sf-display-service env=local";
- //const path = "cd ~ && cd Documents/Confi/ScriptsHub"
-
- //execSync(`cd ~ && cd ${scriptHubPathName} && ./scriptHub.sh scriptType=runners type=update projects=sf-display-service env=local`, options);
-
- /*exec(`${path} && ${executeCmd}`, options, (err: any, stdout: any, stderr: any) => {
- if (err) {
- console.error(err);
- } else {
- console.log(`The stdout Buffer from shell: ${stdout.toString()}`);
- console.log(`The stderr Buffer from shell: ${stderr.toString()}`);
- }
- });*/
- };
-
-
- const updateScriptType = (event: { target: any; }) => {
- setScriptType(event.target.value);
- setOperationData(event.target.value);
- }
-
- const updateOperation = (event: { target: any; }) => {
- setOperation(event.target.value);
-
- const operationDataType = getElementByType(operationsData.types, event.target.value);
- const operationPath = `${scriptTypePathName}${operationDataType.basePath}`;
- const settingsData = readFileDataList(operationPath, settingsFilename);
- console.log(getElementByType(settingsData.types, event.target.value));
- }
-
- const setOperationData = (scriptTypeInit: string) => {
- const scriptTypeDataType = getElementByType(scriptTypeData.types, scriptTypeInit);
- const scriptTypePath = `${scriptHubBasePathName}scripts${scriptTypeDataType.basePath}`;
- operationsData = readFileDataList(scriptTypePath, settingsFilename);
- scriptTypePathName = scriptTypePath;
-
- const operationDataType = getElementByType(operationsData.types, firstElement(operationsData.types).type);
- const operationPath = `${scriptTypePath}${operationDataType.basePath}`;
- const settingsData = readFileDataList(operationPath, settingsFilename);
- console.log(getElementByType(settingsData.types, firstElement(operationsData.types).type));
- }
-
- return (
- <>
- { props.argList.map(element => {
-
- })
- }
- >
- );
-}
-
-const RenderPanelArgs = (elemCustom: any) => {
- const elementRoot = createRoot(elemCustom);
- elementRoot.render(())
-};
-
-export default RenderPanelArgs;
\ No newline at end of file
diff --git a/src/app/scripthub/card-component.tsx b/src/app/scripthub/card-component.tsx
new file mode 100644
index 0000000..dc050e3
--- /dev/null
+++ b/src/app/scripthub/card-component.tsx
@@ -0,0 +1,225 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { checkForLastSlashInString, firstElement, getElementByType, getFolders, getLastElement, getSettings, isEmpty, isListEmpty, readFileDataList, readFileEnvs, removeAllElems } from '../utils/Utils';
+
+const emptyCardData: CardData = { id: -1, title: '', selectedOption: '', options: [], filePath: '' };
+const emptyListCardData: CardData[] = [emptyCardData];
+const settingsFilename: string = 'settings.json';
+const scriptHubPath = getSettings(settingsFilename).scriptHubPath;
+
+const CardComponent = () => {
+ const [cards, setCards] = useState(emptyListCardData);
+ const [cmdListCount, setCmdListCount] = useState(0);
+ const isFirstRender = useRef(true);
+
+ useEffect(() => {
+ const settings = readFileDataList(scriptHubPath, settingsFilename);
+ const options = settings.types;
+ const scriptTypeInit = firstElement(options);
+ setCards([{ id: 0, title: settings.type, selectedOption: scriptTypeInit.type, options: options, filePath: scriptHubPath }]);
+ }, [])
+
+ useEffect(() => {
+ if (isFirstRender.current) {
+ isFirstRender.current = false;
+ return;
+ }
+
+ const lastElem = getLastElement(cards, cmdListCount);
+ let newElem: CardData;
+ if (isListEmpty(lastElem.options)) {
+ return;
+ }
+
+ const firstOptionElem = firstElement(lastElem.options);
+ if (!isListEmpty(firstOptionElem.cmdList)) {
+ newElem = buildCardOfEnvType(firstOptionElem.cmdList, lastElem.selectedOption);
+ } else {
+ newElem = buildCardWithBasePath(lastElem, firstOptionElem);
+ }
+
+ if (newElem.id === -1) {
+ return;
+ }
+
+ setCards(prev => [
+ ...prev,
+ newElem
+ ]);
+ }, [cards]);
+
+ const buildCardOfEnvType = (cmdList: any, lastElemSelectedOption: string): CardData => {
+ if (cmdListCount + 1 > cmdList.length) {
+ return emptyCardData;
+ }
+
+ setCmdListCount(cmdListCount + 1);
+ const cmd: { name: any; type: any; } = cmdList[cmdListCount];
+ const id = cards.length;
+ const title = cmd.name;
+ const selectedOption = cmd.type;
+ const options: Data[] = getOptionsByType(cmd.type);
+ const filePath = "";
+
+ return { id: id, title: title, selectedOption: selectedOption, options: options, filePath: filePath };
+ };
+
+ const buildCardWithBasePath = (lastElem: CardData, firstOptionElem: any): CardData => {
+ if (lastElem.selectedOption) {
+ firstOptionElem = getElementByType(lastElem.options, lastElem.selectedOption);
+ }
+
+ const id = cards.length;
+ const basePath = firstOptionElem.basePath.replaceAll(".", "");
+ const basePathFiltered = checkForLastSlashInString(basePath.split(''));
+ const filePath = `${lastElem.filePath}${basePathFiltered}`;
+ const settings = readFileDataList(filePath, settingsFilename);
+ let options = settings.types;
+ const nextElem = firstElement(options);
+
+ if (isEmpty(nextElem)) {
+ return emptyCardData;
+ }
+
+ const title = settings.type;
+ let selectedOption = nextElem.type;
+
+ if(title==="operation") {
+ selectedOption = lastElem.selectedOption;
+
+ const rawOptions = firstElement(options.filter(option => option.types?.includes(selectedOption)));
+ const types = rawOptions.types;
+ const cmdList = rawOptions.cmdList;
+ options = [];
+ types?.forEach((type: string) => {
+ options.push({
+ type: type,
+ name: type,
+ cmdList: cmdList
+ });
+ });
+ }
+
+ return { id: id, title: title, selectedOption: selectedOption, options: options, filePath: filePath };
+ };
+
+ const handleSelectChange = (id: number, value: string) => {
+ cmdListCountRemoval(id);
+ setCards(removeAllElems(id, cards));
+ setCards(prev =>
+ prev.map(card => {
+ let currentOption = getElementByType(card.options, value);
+ return card.id === id ?
+ {
+ ...card,
+ selectedOption: getType(currentOption, value)
+ } : card;
+ }
+ )
+ );
+ };
+
+ const cmdListCountRemoval = (id: number) => {
+ const calc = cmdListCount - (cards.length - (id + 1));
+ if (calc < 0) {
+ setCmdListCount(0);
+ return;
+ }
+
+ setCmdListCount(calc);
+ }
+
+ const getType = (currentOption: any, value: string) => {
+ if (currentOption.type) {
+ return currentOption.type;
+ }
+
+ return currentOption.types.filter((type: string) => type === value);
+ }
+
+ const getOptionsByType = (type: string) => {
+ switch (type) {
+ case "projectList":
+ return listOfAllProjects();
+ case "envs":
+ return listOfAllEnvironments();
+ case "additionalCmd":
+ return listOfAllAdditionalCmd();
+ default:
+ return [{ type: "a", name: "b" }];
+ }
+
+ }
+
+ const getSelectedProject = () => {
+ return firstElement(cards.filter(cardData => cardData.title === "projects")).selectedOption;
+ }
+
+ const listOfAllProjects = (): Data[] => {
+ const folderList = getFolders(`${scriptHubPath}/projects`);
+ const folders: Data[] = [];
+ folderList?.forEach(element => {
+ folders.push({
+ type: element,
+ name: element
+ });
+ });
+ return folders;
+ }
+
+ const listOfAllEnvironments = (): Data[] => {
+ const selectedProject = getSelectedProject();
+ const settings = readFileEnvs(`${scriptHubPath}/projects/${selectedProject}`);
+ const envList: Data[] = [];
+ settings.envs?.forEach(env => {
+ envList.push({
+ type: env.type,
+ name: env.type
+ });
+ });
+ return envList;
+ }
+
+ const listOfAllAdditionalCmd = (): Data[] => {
+ const selectedProject = getSelectedProject();
+ const settings = readFileEnvs(`${scriptHubPath}/projects/${selectedProject}`);
+ const envList: Data[] = [];
+ settings.additionalCmd?.forEach(env => {
+ envList.push({
+ type: env.key,
+ name: env.key
+ });
+ });
+ return envList;
+ }
+
+ return (
+
+ {cards.filter(card => card.title !== "operation").map(card => (
+
+
{card.title}
+
+
+ ))}
+
+ );
+};
+
+export default CardComponent;
\ No newline at end of file
diff --git a/src/app/scripthub/operation-component.tsx b/src/app/scripthub/operation-component.tsx
new file mode 100644
index 0000000..eb6fe1b
--- /dev/null
+++ b/src/app/scripthub/operation-component.tsx
@@ -0,0 +1,164 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { firstElement, getElementByType, getFolders, getLastElement, getSettings, isListEmpty, readFileEnvs, removeAllElems } from '../utils/Utils';
+
+const emptyCardData: CardData = { id: -1, title: '', selectedOption: '', options: [], filePath: '' };
+const emptyListCardData: CardData[] = [emptyCardData];
+const settingsFilename: string = 'settings.json';
+const scriptHubPath = getSettings(settingsFilename).scriptHubPath;
+
+const OperationComponent = (props: { operation: CardData; operationData: CardData[]; updateData: any; }) => {
+ const isFirstRender = useRef(true);
+
+ useEffect(() => {
+ props.updateData((prev: any[]) => prev.filter(operation => operation.id !== -1));
+ }, [])
+
+ useEffect(() => {
+ props.updateData([]);
+ }, [props.operation])
+
+ useEffect(() => {
+ if (isFirstRender.current) {
+ isFirstRender.current = false;
+ return;
+ }
+
+ if (isListEmpty(props.operation.options)) {
+ return;
+ }
+
+ const operationOptionElem = firstElement(props.operation.options);
+ if (isListEmpty(operationOptionElem.cmdList)) {
+ return;
+ }
+
+ console.log("operationOptionElem");
+ console.log(operationOptionElem);
+
+ const newElem = buildCardOfEnvType(operationOptionElem.cmdList);
+ if (newElem.id === -1) {
+ return;
+ }
+
+ props.updateData((prev: any) => [
+ ...prev,
+ newElem
+ ]);
+ }, [props.operationData]);
+
+ const buildCardOfEnvType = (cmdList: any): CardData => {
+ const count = isListEmpty(getLastElement(props.operationData)) ? 0 : getLastElement(props.operationData).id + 1;
+ if (count > cmdList.length) {
+ return emptyCardData;
+ }
+
+ const cmd: { name: any; type: any; } = cmdList[count];
+ if (cmd === undefined || cmd.type === "additionalCmd" || cmd.type === "boolean") {
+ return emptyCardData;
+ }
+ const id = props.operationData.length;
+ const title = cmd.name;
+ const selectedOption = cmd.type;
+ const options: Data[] = getOptionsByType(cmd.type);
+ const filePath = "";
+
+ return { id: id, title: title, selectedOption: selectedOption, options: options, filePath: filePath };
+ };
+
+ const handleSelectChange = (id: number, value: string) => {
+ props.updateData(removeAllElems(id, props.operationData));
+ props.updateData((prev: any[]) =>
+ prev.map(card => {
+ let currentOption = getElementByType(card.options, value);
+ return card.id === id ?
+ {
+ ...card,
+ selectedOption: getType(currentOption, value)
+ } : card;
+ }
+ )
+ );
+ };
+
+ const getType = (currentOption: any, value: string) => {
+ if (currentOption.type) {
+ return currentOption.type;
+ }
+
+ return currentOption.types.filter((type: string) => type === value);
+ }
+
+ const getOptionsByType = (type: string) => {
+ switch (type) {
+ case "projectList":
+ return listOfAllProjects();
+ case "envs":
+ return listOfAllEnvironments();
+ case "boolean":
+ return listOfAllEnvironments();
+ default:
+ return [];
+ }
+
+ }
+
+ const getSelectedProject = () => {
+ return firstElement(props.operationData.filter(cardData => cardData.title === "projects")).selectedOption;
+ }
+
+ const listOfAllProjects = (): Data[] => {
+ const folderList = getFolders(`${scriptHubPath}/projects`);
+ const folders: Data[] = [];
+ folderList?.forEach(element => {
+ folders.push({
+ type: element,
+ name: element
+ });
+ });
+ return folders;
+ }
+
+ const listOfAllEnvironments = (): Data[] => {
+ const selectedProject = getSelectedProject();
+ const settings = readFileEnvs(`${scriptHubPath}/projects/${selectedProject}`);
+ const envList: Data[] = [];
+ settings.envs?.forEach(env => {
+ envList.push({
+ type: env.type,
+ name: env.type
+ });
+ });
+ return envList;
+ }
+
+ return (
+
+ {props.operationData.map(card => (
+
+
{card.title}
+
+
+ ))}
+
+ );
+};
+
+export default OperationComponent;
\ No newline at end of file
diff --git a/src/app/scripthub/script-component.tsx b/src/app/scripthub/script-component.tsx
new file mode 100644
index 0000000..f3141eb
--- /dev/null
+++ b/src/app/scripthub/script-component.tsx
@@ -0,0 +1,135 @@
+import React, { useEffect, useRef } from 'react';
+import { checkForLastSlashInString, firstElement, getElementByType, getLastElement, getSettings, isEmpty, isListEmpty, readFileDataList, removeAllElems } from '../utils/Utils';
+
+const emptyCardData: CardData = { id: -1, title: '', selectedOption: '', options: [], filePath: '' };
+const emptyListCardData: CardData[] = [emptyCardData];
+const settingsFilename: string = 'settings.json';
+const scriptHubPath = getSettings(settingsFilename).scriptHubPath;
+
+const ScriptComponent = (props: { scriptData: CardData[]; updateData: any; }) => {
+ const isFirstRender = useRef(true);
+
+ useEffect(() => {
+ const settings = readFileDataList(scriptHubPath, settingsFilename);
+ const options = settings.types;
+ const scriptTypeInit = firstElement(options);
+ const initData = [{ id: 0, title: settings.type, selectedOption: scriptTypeInit.type, options: options, filePath: scriptHubPath }];
+ props.updateData(initData);
+ }, [])
+
+ useEffect(() => {
+ if (isFirstRender.current) {
+ isFirstRender.current = false;
+ return;
+ }
+
+ const lastElem = getLastElement(props.scriptData);
+ if (lastElem.title === "operation") {
+ return;
+ }
+
+ const newElem = buildCardWithBasePath(lastElem);
+ if (newElem.id === -1) {
+ return;
+ }
+
+ props.updateData((prev: any) => [
+ ...prev,
+ newElem
+ ]);
+ }, [props.scriptData]);
+
+ const buildCardWithBasePath = (lastElem: CardData): CardData => {
+ if (!lastElem.selectedOption) {
+ return emptyCardData;
+ }
+
+ const firstOptionElem = getElementByType(lastElem.options, lastElem.selectedOption);
+ const id = props.scriptData.length;
+ const basePath = firstOptionElem.basePath.replaceAll(".", "");
+ const basePathFiltered = checkForLastSlashInString(basePath.split(''));
+ const filePath = `${lastElem.filePath}${basePathFiltered}`;
+ const settings = readFileDataList(filePath, settingsFilename);
+ let options = settings.types;
+ const nextElem = firstElement(options);
+
+ if (isEmpty(nextElem)) {
+ return emptyCardData;
+ }
+
+ const title = settings.type;
+ let selectedOption = nextElem.type;
+
+ if (title === "operation") {
+ selectedOption = lastElem.selectedOption;
+
+ const rawOptions = firstElement(options.filter(option => option.types?.includes(selectedOption)));
+ const types = rawOptions.types;
+ const cmdList = rawOptions.cmdList;
+ options = [];
+ types?.forEach((type: string) => {
+ options.push({
+ type: type,
+ name: type,
+ cmdList: cmdList
+ });
+ });
+ }
+
+ return { id: id, title: title, selectedOption: selectedOption, options: options, filePath: filePath };
+ };
+
+ const handleSelectChange = (id: number, value: string) => {
+ props.updateData(removeAllElems(id, props.scriptData));
+ props.updateData((prev: { options: any[]; id: number; }[]) =>
+ prev.map((card: { options: any[]; id: number; }) => {
+ let currentOption = getElementByType(card.options, value);
+ return card.id === id ?
+ {
+ ...card,
+ selectedOption: getType(currentOption, value)
+ } : card;
+ }
+ )
+ );
+ };
+
+ const getType = (currentOption: any, value: string) => {
+ if (currentOption.type) {
+ return currentOption.type;
+ }
+
+ return currentOption.types.filter((type: string) => type === value);
+ }
+
+ return (
+
+ {props.scriptData.filter(card => card.title !== "operation").map(card => (
+
+
{card.title}
+
+
+ ))}
+
+ );
+};
+
+export default ScriptComponent;
\ No newline at end of file
diff --git a/src/app/scripthub/script-hub.tsx b/src/app/scripthub/script-hub.tsx
index 5e11f97..6efaeeb 100644
--- a/src/app/scripthub/script-hub.tsx
+++ b/src/app/scripthub/script-hub.tsx
@@ -1,92 +1,131 @@
import React, { useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
-import { Dropdown } from "../utils/dropdown/dropdown";
-import { firstElement, getElementByType, readFileDataList } from "../utils/Utils";
-const os = require('os');
-const homeDir = os.homedir();
+import { firstElement, getLastElement, getSettings, isBlank, isEmpty, isListEmpty } from "../utils/Utils";
+import ScriptComponent from "./script-component";
+import OperationComponent from "./operation-component";
+import AdditionalCmdComponent from "./additional-cmd-component";
+import fs from 'fs';
+import path from "path";
+import { execSync } from "child_process";
+import { ExecSyncOptionsWithStringEncoding } from "child_process";
+const options: ExecSyncOptionsWithStringEncoding = {
+ encoding: "utf8",
+ cwd: "/"
+};
const settingsFilename: string = 'settings.json';
-const scriptHubBasePathName: string = `${homeDir}/Documents/Confi/ScriptsHub/`;
-let scriptTypePathName = "";
-let scriptTypeData: DataList;
-let operationsData: DataList;
-
+const scriptHubPath = getSettings(settingsFilename).scriptHubPath;
+const emptyCardData: CardData = { id: -1, title: '', selectedOption: '', options: [], filePath: '' };
+const emptyListCardData: CardData[] = [emptyCardData];
const ScriptHub = (props: {}) => {
- const [scriptType, setScriptType] = useState("");
- const [operation, setOperation] = useState("");
- const [cmdList, setCmdList] = useState([]);
+ const [scriptData, setScriptData] = useState(emptyListCardData);
+ const [operation, setOperation] = useState(emptyCardData);
+ const [operationData, setOperationData] = useState(emptyListCardData);
+ const [additionalCmd, setAdditionalCmd] = useState(emptyCardData);
+ const [selectedProject, setSelectedProject] = useState("");
- useEffect(() => {
- scriptTypeData = readFileDataList(scriptHubBasePathName, settingsFilename);
- const scriptTypeInit = firstElement(scriptTypeData.types).name;
- setScriptType(scriptTypeInit);
- setOperationData(scriptTypeInit);
- }, [])
+ const [argumentData, setArgumentData] = useState([]);
+ useEffect(() => {
+ console.log("Argument Data:");
+ console.log(argumentData);
+ }, [argumentData]);
- const handleOnClick = async () => {
- console.log(scriptType);
+ useEffect(() => {
+ console.log("Script Data:");
+ console.log(scriptData);
+
+ buildArgumentData(scriptData.filter(script => script.title !== "operation"));
+ buildOperation();
+ }, [scriptData]);
+
+ const buildOperation = () => {
+ const opetationResult = firstElement(scriptData.filter(script => script.title === "operation"));
+ if (opetationResult === null || opetationResult.id === -1) {
+ return;
+ }
+ setOperation(opetationResult);
+ }
- /* const options: ExecSyncOptionsWithStringEncoding = {
- shell: "C:\\Program Files\\Git\\bin\\bash.exe",
- encoding: "utf8"
- };*/
+ useEffect(() => {
+ console.log("Operation Data:");
+ console.log(operationData);
+
+ buildArgumentData(operationData.filter(card => card.id !== -1));
+ buildSelectedProject();
+ }, [operationData]);
+
+ const buildSelectedProject = () => {
+ const projects = firstElement(operationData.filter(data => data.title === "projects"));
+ if (projects !== null && !isEmpty(projects.selectedOption)) {
+ setSelectedProject(projects.selectedOption);
+ }
+ }
- //const executeCmd = "scriptHub.sh scriptType=runners type=update projects=sf-display-service env=local";
- //const path = "cd ~ && cd Documents/Confi/ScriptsHub"
+ useEffect(() => {
+ console.log("Additional Cmd Data:");
+ console.log(additionalCmd);
- //execSync(`cd ~ && cd ${scriptHubPathName} && ./scriptHub.sh scriptType=runners type=update projects=sf-display-service env=local`, options);
+ const cardDataList: CardData[] = [{ id: 0, title: additionalCmd.title, selectedOption: additionalCmd.selectedOption, options: [], filePath: '' }];
+ buildArgumentData(cardDataList);
+ }, [additionalCmd]);
- /*exec(`${path} && ${executeCmd}`, options, (err: any, stdout: any, stderr: any) => {
- if (err) {
- console.error(err);
- } else {
- console.log(`The stdout Buffer from shell: ${stdout.toString()}`);
- console.log(`The stderr Buffer from shell: ${stderr.toString()}`);
- }
- });*/
+ const handleOnClick = async () => {
+ const fileName = "scriptHub.sh";
+ const pathName = path.resolve(`${scriptHubPath}`).replaceAll("\\", "/").replaceAll("//", "/");
+ options.cwd = pathName;
+ options.shell = getTerminal();
+ console.log(options);
+ console.log(`sh ./${fileName} ${joinAllArguments()}`);
+ console.log(execSync(`sh ./${fileName} ${joinAllArguments()}`, options));
};
-
- const updateScriptType = (event: { target: any; }) => {
- setScriptType(event.target.value);
- setOperationData(event.target.value);
+ const joinAllArguments = () => {
+ let allArgsInfoText = "";
+ argumentData.forEach(data => {
+ allArgsInfoText += `${data.title}=${data.value} `;
+ });
+ return allArgsInfoText;
}
- const updateOperation = (event: { target: any; }) => {
- setOperation(event.target.value);
-
- const operationDataType = getElementByType(operationsData.types, event.target.value);
- const operationPath = `${scriptTypePathName}${operationDataType.basePath}`;
- const settingsData = readFileDataList(operationPath, settingsFilename);
- console.log(settingsData.types);
- console.log(getElementByType(settingsData.types, event.target.value));
+ const buildArgumentData = (cardData: CardData[]) => {
+ const newData: ArgumentModel[] = cardData.map(data => ({ title: data.title, value: data.selectedOption }));
+ const mergedUnique = Array.from(
+ new Map([...argumentData.filter(argument => !isBlank(argument.title)), ...newData].map(item => [item.title, item])).values()
+ );
+ setArgumentData(mergedUnique);
}
- const setOperationData = (scriptTypeInit: string) => {
- const scriptTypeDataType = getElementByType(scriptTypeData.types, scriptTypeInit);
- const scriptTypePath = `${scriptHubBasePathName}scripts${scriptTypeDataType.basePath}`;
- operationsData = readFileDataList(scriptTypePath, settingsFilename);
- scriptTypePathName = scriptTypePath;
-
- const operationDataType = getElementByType(operationsData.types, firstElement(operationsData.types).type);
- const operationPath = `${scriptTypePath}${operationDataType.basePath}`;
- const settingsData = readFileDataList(operationPath, settingsFilename);
- console.log(getElementByType(settingsData.types, firstElement(operationsData.types).type));
+ const getTerminal = (): string => {
+ if (process.platform === 'win32') {
+ return "C:\\Program Files\\Git\\bin\\bash.exe";
+ }
+
+ return process.env.SHELL || '/bin/bash';;
}
return (
<>
-
- {scriptTypeData !== undefined &&
-
+
+
+
+
+
+
+ {operation.id !== -1 &&
+
+
+
}
- {operationsData !== undefined &&
-
+ {!isBlank(selectedProject) &&
+
}
+
>
);
}
diff --git a/src/app/settings/settings-form.tsx b/src/app/settings/settings-form.tsx
new file mode 100644
index 0000000..179fc0e
--- /dev/null
+++ b/src/app/settings/settings-form.tsx
@@ -0,0 +1,70 @@
+import React, { useEffect, useState } from 'react';
+import {
+ Box,
+ Button,
+ TextField,
+ Typography,
+ Container,
+ Paper,
+} from '@mui/material';
+import { createTheme, ThemeProvider } from '@mui/material/styles';
+import { getSettings, saveSettings } from '../utils/Utils';
+
+const theme = createTheme({
+ palette: {
+ primary: {
+ main: '#1976d2',
+ },
+ },
+ typography: {
+ fontFamily: 'Roboto, Arial',
+ },
+});
+
+const SettingsFormImpl = (props: {}) => {
+ const [formData, setFormData] = useState({ scriptHubPath: '' });
+
+ useEffect(() => {
+ setFormData((prevData) => ({
+ ...prevData,
+ ...getSettings("settings.json")
+ }));
+ }, []);
+
+ const handleScriptHubPathChange = (event: { target: any; }) => {
+ setFormData((prevData) => ({
+ ...prevData,
+ scriptHubPath: event.target.value
+ }));
+ };
+
+ const handleSubmit = (e: { preventDefault: () => void; }) => {
+ e.preventDefault();
+ saveSettings(formData, "settings.json");
+ };
+
+ return (
+ <>
+
+
+
+ Settings
+
+
+
+
+
+
+
+ >
+ );
+}
+
+export const SettingsForm = SettingsFormImpl;
diff --git a/src/app/settings/settings.styles.scss b/src/app/settings/settings.styles.scss
new file mode 100644
index 0000000..7247356
--- /dev/null
+++ b/src/app/settings/settings.styles.scss
@@ -0,0 +1,59 @@
+.settings-card {
+ background: #a74646;
+ padding: 32px;
+ border-radius: 12px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.settings-form {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.form2 {
+ max-width: 400px;
+ margin: 2rem auto;
+ padding: 1.5rem;
+ border: 1px solid #ccc;
+ border-radius: 8px;
+ background: #f9f9f9;
+
+ &-title {
+ text-align: center;
+ margin-bottom: 1rem;
+ font-size: 1.5rem;
+ }
+
+ &-group {
+ margin-bottom: 1rem;
+ display: flex;
+ flex-direction: column;
+
+ label {
+ margin-bottom: 0.5rem;
+ font-weight: 600;
+ }
+
+ input {
+ padding: 0.5rem;
+ border: 1px solid #aaa;
+ border-radius: 4px;
+ font-size: 1rem;
+ }
+ }
+
+ &-button {
+ background-color: #007bff;
+ color: white;
+ padding: 0.6rem 1rem;
+ border: none;
+ border-radius: 4px;
+ font-size: 1rem;
+ cursor: pointer;
+
+ &:hover {
+ background-color: #0056b3;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/settings/settings.tsx b/src/app/settings/settings.tsx
index 41012da..8a06998 100644
--- a/src/app/settings/settings.tsx
+++ b/src/app/settings/settings.tsx
@@ -1,11 +1,12 @@
-import React from "react";
+import React from 'react';
import { createRoot } from "react-dom/client";
+import './settings.styles.scss';
+import { SettingsForm } from './settings-form';
const Settings = (props: {}) => {
- console.log("Settings");
return (
<>
-
+
>
);
}
diff --git a/src/app/utils/Utils.ts b/src/app/utils/Utils.ts
index b5737c1..75e685e 100644
--- a/src/app/utils/Utils.ts
+++ b/src/app/utils/Utils.ts
@@ -1,28 +1,86 @@
import fs from 'fs';
import path from "path";
+import os from 'os';
import { ExecSyncOptionsWithStringEncoding } from "child_process";
+
+const homeDir = os.homedir();
const options: ExecSyncOptionsWithStringEncoding = {
encoding: "utf8"
};
+const ENVS_FILE_NAME = "envs.json";
+
+export const log = (message: string) => console.log(message);
+export const warning = (message: string) => console.warn(message);
+export const error = (message: string) => console.error(message);
-const isListEmpty = (list: any[]) => !list || list.length <= 0;
+const openFile = (filePath: string) => fs.readFileSync(filePath.replaceAll("\/", path.sep), options);
+const getTypesOrType = (element: any, elementName: string) => {
+ if (element.type !== undefined) {
+ return element.type === elementName;
+ }
+
+ return element.types.includes(elementName);
+}
+
+export const isListEmpty = (list: any[]) => !list || list.length <= 0;
+export const getSettings = (fileName: string): SettingsModel => {
+ const pathName: string = path.resolve(__dirname, fileName);
+ if (!fs.existsSync(pathName)) {
+ let settings: SettingsModel = {
+ scriptHubPath: homeDir
+ };
+ saveSettings(settings!, pathName);
+ }
+
+ return JSON.parse(fs.readFileSync(pathName, options));
+};
+
+export const saveSettings = (formData: SettingsModel, fileName: string) => {
+ const pathName: string = path.resolve(__dirname, fileName);
+ fs.writeFileSync(pathName, JSON.stringify(formData), options);
+};
export const readFileDataList = (pathName: string, fileName: string): DataList => {
- return JSON.parse(openFile(`${pathName}/${fileName}`.replaceAll("\\", "/").replaceAll("//", "/")));
+ let settingsPath = `${pathName}/${fileName}`.replaceAll("\\", "/").replaceAll("//", "/");
+
+ if (!fs.existsSync(settingsPath)) {
+ return JSON.parse('{ "types": [] }');
+ }
+ return JSON.parse(openFile(settingsPath));
}
-const openFile = (filePath: string) => fs.readFileSync(filePath.replaceAll("\/", path.sep), options);
-export const isBlank = (stringValue: string) => (!stringValue || /^\s*$/.test(stringValue));
+export const readFileEnvs = (pathName: string): EnvsData => {
+ let settingsPath = `${pathName}/${ENVS_FILE_NAME}`.replaceAll("\\", "/").replaceAll("//", "/");
-export const firstElement = (list: any[]) => list.length > 0 ? list[0] : null;
-export const getFilteredList = (list: any[], filter: any) => list.filter(filter);
+ if (!fs.existsSync(settingsPath)) {
+ error(`The file envs.json does not exist. Returning empty json. Path: ${settingsPath}`);
+ return JSON.parse('{}');
+ }
+ return JSON.parse(openFile(settingsPath));
+}
-const getTypesOrType = (element: any, elementName: string) => {
- if(element.type !== undefined) {
- return element.type === elementName;
+export const checkForLastSlashInString = (chars: string[]) => {
+ const lastElem = getLastElement(chars);
+ if (lastElem === '/' || lastElem === '\\') {
+ chars.pop();
}
-
- return element.types.includes(elementName);
+ return chars.join('');
+}
+
+export const removeAllElems = (startIndex: number, list: CardData[]) => {
+ return list.filter(card => card.id <= startIndex);
}
-export const getElementByType = (list: any[], elementName: string) => isListEmpty(list) ? undefined : firstElement(list.filter(element => getTypesOrType(element, elementName)));
\ No newline at end of file
+export const isBlank = (stringValue: string) => (!stringValue || /^\s*$/.test(stringValue));
+export const isEmpty = (value: string | any[] | null) => value == null || value.length === 0;
+export const firstElement = (list: any[]) => list.length > 0 ? list[0] : null;
+export const getFilteredList = (list: any[], filter: any) => list.filter(filter);
+export const getElementByType = (list: any[], elementName: string) => isListEmpty(list) ? undefined : firstElement(list.filter(element => getTypesOrType(element, elementName)));
+export const getLastElement = (list: any[], offset: number = 0) => list[list.length - (1 + offset)];
+export const getFolders = (path: string) => {
+ if (!fs.existsSync(path)) {
+ return;
+ }
+
+ return fs.readdirSync(path).filter(file => fs.statSync(path + '/' + file).isDirectory());
+};
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 1363817..fc4f8e9 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,14 +1,14 @@
{
"compilerOptions": {
- "target": "ESNEXT" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
- "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
+ "target": "ESNEXT",
+ "module": "commonjs",
"declaration": true,
"outDir": "./dist",
"rootDir": "./",
- "strict": true /* Enable all strict type-checking options. */,
- "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
- "skipLibCheck": true /* Skip type checking of declaration files. */,
- "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
"downlevelIteration": true,
"lib": [
"ESNEXT",