diff --git a/src/components/molecules/PairDetail.tsx b/src/components/molecules/PairDetail.tsx new file mode 100644 index 00000000..9d37bb6d --- /dev/null +++ b/src/components/molecules/PairDetail.tsx @@ -0,0 +1,310 @@ +import React, { useLayoutEffect, useRef, useState } from 'react'; +import { WhereWhatPair } from "@wbr-project/wbr-interpret"; +import { Box, Button, IconButton, MenuItem, Stack, TextField, Tooltip, Typography } from "@mui/material"; +import { Close, KeyboardArrowDown, KeyboardArrowUp } from "@mui/icons-material"; +import TreeView from '@mui/lab/TreeView'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ChevronRightIcon from '@mui/icons-material/ChevronRight'; +import TreeItem from '@mui/lab/TreeItem'; +import { AddButton } from "../atoms/buttons/AddButton"; +import { WarningText } from "../atoms/texts"; +import NotificationImportantIcon from '@mui/icons-material/NotificationImportant'; +import { RemoveButton } from "../atoms/buttons/RemoveButton"; +import { AddWhereCondModal } from "./AddWhereCondModal"; +import { UpdatePair } from "../../api/workflow"; +import { useSocketStore } from "../../context/socket"; +import { AddWhatCondModal } from "./AddWhatCondModal"; + +interface PairDetailProps { + pair: WhereWhatPair | null; + index: number; +} + +export const PairDetail = ({ pair, index }: PairDetailProps) => { + const [pairIsSelected, setPairIsSelected] = useState(false); + const [collapseWhere, setCollapseWhere] = useState(true); + const [collapseWhat, setCollapseWhat] = useState(true); + const [rerender, setRerender] = useState(false); + const [expanded, setExpanded] = React.useState( + pair ? Object.keys(pair.where).map((key, index) => `${key}-${index}`) : [] + ); + const [addWhereCondOpen, setAddWhereCondOpen] = useState(false); + const [addWhatCondOpen, setAddWhatCondOpen] = useState(false); + + const { socket } = useSocketStore(); + + const handleCollapseWhere = () => { + setCollapseWhere(!collapseWhere); + } + + const handleCollapseWhat = () => { + setCollapseWhat(!collapseWhat); + } + + const handleToggle = (event: React.SyntheticEvent, nodeIds: string[]) => { + setExpanded(nodeIds); + }; + + useLayoutEffect(() => { + if (pair) { + setPairIsSelected(true); + } + }, [pair]) + + const handleChangeValue = (value: any, where: boolean, keys: (string|number)[]) => { + // a moving reference to internal objects within pair.where or pair.what + let schema: any = where ? pair?.where : pair?.what; + const length = keys.length; + for(let i = 0; i < length-1; i++) { + const elem = keys[i]; + if( !schema[elem] ) schema[elem] = {} + schema = schema[elem]; + } + + schema[keys[length-1]] = value; + if (pair && socket) { + socket.emit('updatePair', {index: index-1, pair: pair}); + } + setRerender(!rerender); + } + + + const DisplayValueContent = (value: any, keys: (string|number)[], where: boolean = true) => { + switch (typeof(value)) { + case 'string': + return { + try { + const obj = JSON.parse(e.target.value); + handleChangeValue(obj, where, keys); + } catch (error) { + const num = Number(e.target.value); + if (!isNaN(num)) { + handleChangeValue(num, where, keys); + } + handleChangeValue(e.target.value, where, keys) + } + }} + defaultValue={value} + key={`text-field-${keys.join('-')}-${where}`} + /> + case 'number': + return handleChangeValue(Number(e.target.value), where, keys)} + defaultValue={value} + key={`text-field-${keys.join('-')}-${where}`} + /> + case 'object': + if (value) { + if (Array.isArray(value)) { + return ( + + { + value.map((element, index) => { + return DisplayValueContent(element, [...keys, index], where); + }) + } + { + let prevValue:any = where ? pair?.where : pair?.what; + for (const key of keys) { + prevValue = prevValue[key]; + } + handleChangeValue([...prevValue, ''], where, keys); + setRerender(!rerender); + }} hoverEffect={false}/> + { + let prevValue:any = where ? pair?.where : pair?.what; + for (const key of keys) { + prevValue = prevValue[key]; + } + prevValue.splice(-1); + handleChangeValue(prevValue, where, keys); + setRerender(!rerender); + }}/> + + ) + } else { + return ( + } + defaultExpandIcon={} + sx={{ flexGrow: 1, overflowY: 'auto' }} + key={`tree-view-nested-${keys.join('-')}-${where}`} + > + { + Object.keys(value).map((key2, index) => + { + return ( + + { DisplayValueContent(value[key2], [...keys, key2], where) } + + ) + }) + } + + ) + } + } + break; + default: + return null; + } + } + + return ( + + { pair && + + setAddWhatCondOpen(false)} + pair={pair} index={index}/> + setAddWhereCondOpen(false)} + pair={pair} index={index}/> + + } + { + pairIsSelected + ? ( +
+ Pair number: {index} + { + if (pair && socket) { + socket.emit('updatePair', {index: index-1, pair: pair}); + pair.id = e.target.value; + } + }} + value={pair ? pair.id ? pair.id : '' : ''} + /> + + + Where + +
+ { + setAddWhereCondOpen(true); + }} style={{color:'rgba(0, 0, 0, 0.54)', background:'transparent'}}/> +
+
+
+ {(collapseWhere && pair && pair.where) + ? + + { Object.keys(pair.where).map((key, index) => { + return ( + } + defaultExpandIcon={} + sx={{ flexGrow: 1, overflowY: 'auto' }} + onNodeToggle={handleToggle} + key={`tree-view-${key}-${index}`} + > + + { + // @ts-ignore + DisplayValueContent(pair.where[key], [key]) + } + + + ); + })} + + : null + } + + + What + + +
+ { + setAddWhatCondOpen(true); + }} style={{color:'rgba(0, 0, 0, 0.54)', background:'transparent'}}/> +
+
+
+ {(collapseWhat && pair && pair.what) + ?( + + { Object.keys(pair.what).map((key, index) => { + return ( + } + defaultExpandIcon={} + sx={{ flexGrow: 1, overflowY: 'auto' }} + key={`tree-view-2-${key}-${index}`} + > + + { + // @ts-ignore + DisplayValueContent(pair.what[key], [key], false) + } + +
+ { + //@ts-ignore + pair.what.splice(key, 1); + setRerender(!rerender); + }}/> +
+
+
+
+ ); + })} +
+ ) + : null + } +
+ ) + : + + No pair from the left side panel was selected. + + } +
+ ); +} + +interface CollapseButtonProps { + handleClick: () => void; + isCollapsed?: boolean; +} + +const CollapseButton = ({handleClick, isCollapsed } : CollapseButtonProps) => { + return ( + + { isCollapsed ? : } + + ); +} + +const CloseButton = ({handleClick } : CollapseButtonProps) => { + return ( + + + + ); +}