import * as React from 'react'; import { useEffect, useState } from 'react'; import Container from '@mui/material/Container'; import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; // import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, DragOverlay } from "@dnd-kit/core"; import { arrayMove, SortableContext, sortableKeyboardCoordinates, rectSortingStrategy } from "@dnd-kit/sortable"; import type { DragStartEvent, DragEndEvent, UniqueIdentifier } from "@dnd-kit/core"; import { Module } from './types'; import { modules, steps, module_types } from './schema.json'; import { Stack, Button, } from '@mui/material'; import Grid from '@mui/material/Grid2'; import { parseDocument, Document } from 'yaml' import StepCard from './StepCard'; function FileDrop({ setYamlFile }: { setYamlFile: React.Dispatch> }) { const [showError, setShowError] = useState(false); const [label, setLabel] = useState("Drag and drop your orchestration.yaml file here, or click to select a file."); function openYAMLFile(event: any) { let file = event.target.files[0]; if (file.type !== 'application/x-yaml') { setShowError(true); setLabel("Invalid type, only YAML files are accepted.") return; } let reader = new FileReader(); reader.onload = function (e) { let contents = e.target ? e.target.result : ''; try { let document = parseDocument(contents as string); if (document.errors.length > 0) { // not a valid yaml file setShowError(true); setLabel("Invalid file. Make sure your Orchestration is a valid YAML file with a 'steps' section in it.") return; } else { setShowError(false); setLabel("File loaded successfully.") } setYamlFile(document); } catch (e) { console.error(e); } } reader.readAsText(file); } return ( <>
{label}
); } function ModuleTypes({ stepType, setEnabledModules, enabledModules, configValues }: { stepType: string, setEnabledModules: any, enabledModules: any, configValues: any }) { const [showError, setShowError] = useState(false); const [activeId, setActiveId] = useState(); const [items, setItems] = useState([]); useEffect(() => { setItems(enabledModules[stepType].map(([name, enabled]: [string, boolean]) => name)); } , [enabledModules]); const toggleModule = (event: any) => { // make sure that 'feeder' and 'formatter' types only have one value let name = event.target.id; let checked = event.target.checked; if (stepType === 'feeders' || stepType === 'formatters') { // check how many modules of this type are enabled const checkedModules = enabledModules[stepType].filter(([m, enabled]: [string, boolean]) => { return (m !== name && enabled) || (checked && m === name) }); if (checkedModules.length > 1) { setShowError(true); } else { setShowError(false); } } else { setShowError(false); } let newEnabledModules = Object.fromEntries(Object.keys(enabledModules).map((type: string) => { return [type, enabledModules[type].map(([m, enabled]: [string, boolean]) => { return (m === name) ? [m, checked] : [m, enabled]; })]; } )); setEnabledModules(newEnabledModules); } const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) ); const handleDragStart = (event: DragStartEvent) => { setActiveId(event.active.id); }; const handleDragEnd = (event: DragEndEvent) => { setActiveId(undefined); const { active, over } = event; if (active.id !== over?.id) { const oldIndex = items.indexOf(active.id as string); const newIndex = items.indexOf(over?.id as string); let newArray = arrayMove(items, oldIndex, newIndex); // set it also on steps let newEnabledModules = { ...enabledModules }; newEnabledModules[stepType] = enabledModules[stepType].sort((a, b) => { return newArray.indexOf(a[0]) - newArray.indexOf(b[0]); }) setEnabledModules(newEnabledModules); } }; return ( <> {stepType} Select the {stepType} you wish to enable. You can drag and move to reorder. Learn more about {stepType} here. {showError ? Only one {stepType.slice(0,-1)} can be enabled at a time. : null} {items.map((name: string) => { let m: Module = modules[name]; return ( ); })} {activeId ? (
) : null}
); } export default function App() { const [yamlFile, setYamlFile] = useState(new Document()); const [enabledModules, setEnabledModules] = useState<{}>(Object.fromEntries(Object.keys(steps).map(type => [type, steps[type].map((name: string) => [name, false])]))); const [configValues, setConfigValues] = useState<{ [key: string]: { [key: string ]: any } }>( Object.keys(modules).reduce((acc, module) => { acc[module] = {}; return acc; }, {}) ); const saveSettings = function (copy: boolean = false) { // edit the yamlFile // generate the steps config let stepsConfig = enabledModules; // create a yaml file from const finalYaml = { 'steps': Object.keys(steps).reduce((acc, stepType: string) => { acc[stepType] = stepsConfig[stepType].filter(([name, enabled]: [string, boolean]) => enabled).map(([name, enabled]: [string, boolean]) => name); return acc; }, {}) }; Object.keys(configValues).map((module: string) => { let module_values = configValues[module]; if (module_values) { finalYaml[module] = module_values; } }); let newFile = new Document(finalYaml); if (copy) { navigator.clipboard.writeText(String(newFile)).then(() => { alert("Settings copied to clipboard."); }); } else { // offer the file for download const blob = new Blob([String(newFile)], { type: 'application/x-yaml' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'orchestration.yaml'; a.click(); } } useEffect(() => { // load the configs, and set the default values if they exist let newConfigValues = {}; Object.keys(modules).map((module: string) => { let m = modules[module]; let configs = m.configs; if (!configs) { return; } newConfigValues[module] = {}; Object.keys(configs).map((config: string) => { let config_args = configs[config]; if (config_args.default !== undefined) { newConfigValues[module][config] = config_args.default; } }); }) setConfigValues(newConfigValues); }, []); useEffect(() => { if (!yamlFile || yamlFile.contents == null) { return; } let settings = yamlFile.toJS(); // make a deep copy of settings let stepSettings = settings['steps']; let newEnabledModules = Object.fromEntries(Object.keys(steps).map((type: string) => { return [type, steps[type].map((name: string) => { return [name, stepSettings[type].indexOf(name) !== -1]; }).sort((a, b) => { let aIndex = stepSettings[type].indexOf(a[0]); let bIndex = stepSettings[type].indexOf(b[0]); if (aIndex === -1 && bIndex === -1) { return a - b; } if (bIndex === -1) { return -1; } if (aIndex === -1) { return 1; } return aIndex - bIndex; })]; }).sort((a, b) => { return module_types.indexOf(a[0]) - module_types.indexOf(b[0]); })); setEnabledModules(newEnabledModules); }, [yamlFile]); return ( 1. Select your orchestration.yaml settings file. 2. Choose the Modules you wish to enable/disable {Object.keys(steps).map((stepType: string) => { return ( ); })} 3. Configure your Enabled Modules Next to each module you've enabled, you can click 'Configure' to set the module's settings. 4. Save your settings ); }