Merge branch 'dev' into merge_repo
This commit is contained in:
commit
13495cedc1
19 changed files with 862 additions and 690 deletions
|
|
@ -1,149 +1,144 @@
|
||||||
import "reactflow/dist/style.css";
|
import "reactflow/dist/style.css";
|
||||||
import { useState, useRef, useEffect, useContext } from "react";
|
import { useState, useEffect, useContext } from "react";
|
||||||
import { ReactFlowProvider } from "reactflow";
|
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import ErrorAlert from "./alerts/error";
|
import ErrorAlert from "./alerts/error";
|
||||||
import NoticeAlert from "./alerts/notice";
|
import NoticeAlert from "./alerts/notice";
|
||||||
import SuccessAlert from "./alerts/success";
|
import SuccessAlert from "./alerts/success";
|
||||||
import ExtraSidebar from "./components/ExtraSidebarComponent";
|
import ExtraSidebar from "./components/ExtraSidebarComponent";
|
||||||
import { alertContext } from "./contexts/alertContext";
|
import { alertContext } from "./contexts/alertContext";
|
||||||
import { locationContext } from "./contexts/locationContext";
|
import { locationContext } from "./contexts/locationContext";
|
||||||
import FlowPage from "./pages/FlowPage";
|
|
||||||
import Sidebar from "./components/SidebarComponent";
|
import Sidebar from "./components/SidebarComponent";
|
||||||
import Header from "./components/HeaderComponent";
|
import Header from "./components/HeaderComponent";
|
||||||
import { TabsProvider } from "./contexts/tabsContext";
|
|
||||||
import { TabsManager } from "./pages/FlowPage/flowManager";
|
import { TabsManager } from "./pages/FlowPage/flowManager";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
var _ = require("lodash");
|
var _ = require("lodash");
|
||||||
|
|
||||||
|
|
||||||
let { setAtual, setShowSideBar, setIsStackedOpen} =
|
let { setCurrent, setShowSideBar, setIsStackedOpen } =
|
||||||
useContext(locationContext);
|
useContext(locationContext);
|
||||||
let location = useLocation();
|
let location = useLocation();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAtual(location.pathname.replace(/\/$/g, "").split("/"));
|
setCurrent(location.pathname.replace(/\/$/g, "").split("/"));
|
||||||
setShowSideBar(true);
|
setShowSideBar(true);
|
||||||
setIsStackedOpen(true);
|
setIsStackedOpen(true);
|
||||||
}, [location.pathname, setAtual, setIsStackedOpen, setShowSideBar]);
|
}, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
errorData,
|
errorData,
|
||||||
errorOpen,
|
errorOpen,
|
||||||
setErrorOpen,
|
setErrorOpen,
|
||||||
noticeData,
|
noticeData,
|
||||||
noticeOpen,
|
noticeOpen,
|
||||||
setNoticeOpen,
|
setNoticeOpen,
|
||||||
successData,
|
successData,
|
||||||
successOpen,
|
successOpen,
|
||||||
setSuccessOpen,
|
setSuccessOpen,
|
||||||
} = useContext(alertContext);
|
} = useContext(alertContext);
|
||||||
|
|
||||||
const [alertsList, setAlertsList] = useState([]);
|
const [alertsList, setAlertsList] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (errorOpen && errorData) {
|
if (errorOpen && errorData) {
|
||||||
setErrorOpen(false);
|
setErrorOpen(false);
|
||||||
setAlertsList((old) => {
|
setAlertsList((old) => {
|
||||||
let newAlertsList = [
|
let newAlertsList = [
|
||||||
...old,
|
...old,
|
||||||
{ type: "error", data: _.cloneDeep(errorData), id: _.uniqueId() },
|
{ type: "error", data: _.cloneDeep(errorData), id: _.uniqueId() },
|
||||||
];
|
];
|
||||||
return newAlertsList;
|
return newAlertsList;
|
||||||
});
|
});
|
||||||
} else if (noticeOpen && noticeData) {
|
} else if (noticeOpen && noticeData) {
|
||||||
setNoticeOpen(false);
|
setNoticeOpen(false);
|
||||||
setAlertsList((old) => {
|
setAlertsList((old) => {
|
||||||
let newAlertsList = [
|
let newAlertsList = [
|
||||||
...old,
|
...old,
|
||||||
{ type: "notice", data: _.cloneDeep(noticeData), id: _.uniqueId() },
|
{ type: "notice", data: _.cloneDeep(noticeData), id: _.uniqueId() },
|
||||||
];
|
];
|
||||||
return newAlertsList;
|
return newAlertsList;
|
||||||
});
|
});
|
||||||
} else if (successOpen && successData) {
|
} else if (successOpen && successData) {
|
||||||
setSuccessOpen(false);
|
setSuccessOpen(false);
|
||||||
setAlertsList((old) => {
|
setAlertsList((old) => {
|
||||||
let newAlertsList = [
|
let newAlertsList = [
|
||||||
...old,
|
...old,
|
||||||
{ type: "success", data: _.cloneDeep(successData), id: _.uniqueId() },
|
{ type: "success", data: _.cloneDeep(successData), id: _.uniqueId() },
|
||||||
];
|
];
|
||||||
return newAlertsList;
|
return newAlertsList;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [errorData, errorOpen, noticeData, noticeOpen, successData, successOpen]);
|
}, [errorData, errorOpen, noticeData, noticeOpen, successData, successOpen]);
|
||||||
|
|
||||||
const removeAlert = (id: number) => {
|
const removeAlert = (id: number) => {
|
||||||
setAlertsList((prevAlertsList) =>
|
setAlertsList((prevAlertsList) =>
|
||||||
prevAlertsList.filter((alert) => alert.id !== id)
|
prevAlertsList.filter((alert) => alert.id !== id)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
name: "Whitney Francis",
|
name: "Whitney Francis",
|
||||||
email: "whitney.francis@example.com",
|
email: "whitney.francis@example.com",
|
||||||
imageUrl:
|
imageUrl:
|
||||||
"https://images.unsplash.com/photo-1517365830460-955ce3ccd263?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80",
|
"https://images.unsplash.com/photo-1517365830460-955ce3ccd263?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80",
|
||||||
};
|
};
|
||||||
|
|
||||||
const userNavigation = [
|
const userNavigation = [
|
||||||
{ name: "Your Projects", href: "/" },
|
{ name: "Your Projects", href: "/" },
|
||||||
// {
|
// {
|
||||||
// name: "Account settings",
|
// name: "Account settings",
|
||||||
// href: "http://localhost:4455/.ory/kratos/public/self-service/settings/browser",
|
// href: "http://localhost:4455/.ory/kratos/public/self-service/settings/browser",
|
||||||
// },
|
// },
|
||||||
{ name: "Sign out", href: "/" },
|
{ name: "Sign out", href: "/" },
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
//need parent component with width and height
|
//need parent component with width and height
|
||||||
<div className="h-full flex flex-col">
|
<div className="h-full flex flex-col">
|
||||||
|
<div className="flex grow-0 shrink basis-auto">
|
||||||
<div className="flex grow-0 shrink basis-auto">
|
<Header userNavigation={userNavigation} user={user}></Header>
|
||||||
<Header userNavigation={userNavigation} user={user}></Header>
|
</div>
|
||||||
</div>
|
<div className="flex grow shrink basis-auto min-h-0 flex-1 overflow-hidden">
|
||||||
<div className="flex grow shrink basis-auto min-h-0 flex-1 overflow-hidden">
|
<Sidebar />
|
||||||
<Sidebar />
|
<ExtraSidebar />
|
||||||
<ExtraSidebar />
|
|
||||||
|
|
||||||
{/* Main area */}
|
{/* Main area */}
|
||||||
<main className="min-w-0 flex-1 border-t border-gray-200 flex">
|
<main className="min-w-0 flex-1 border-t border-gray-200 flex">
|
||||||
{/* Primary column */}
|
{/* Primary column */}
|
||||||
<div className="w-full h-full">
|
<div className="w-full h-full">
|
||||||
<TabsManager></TabsManager>
|
<TabsManager></TabsManager>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex z-50 flex-col-reverse fixed bottom-5 left-5">
|
<div className="flex z-50 flex-col-reverse fixed bottom-5 left-5">
|
||||||
{alertsList.map((alert) => (
|
{alertsList.map((alert) => (
|
||||||
<div key={alert.id}>
|
<div key={alert.id}>
|
||||||
{alert.type === "error" ? (
|
{alert.type === "error" ? (
|
||||||
<ErrorAlert
|
<ErrorAlert
|
||||||
key={alert.id}
|
key={alert.id}
|
||||||
title={alert.data.title}
|
title={alert.data.title}
|
||||||
list={alert.data.list}
|
list={alert.data.list}
|
||||||
id={alert.id}
|
id={alert.id}
|
||||||
removeAlert={removeAlert}
|
removeAlert={removeAlert}
|
||||||
/>
|
/>
|
||||||
) : alert.type === "notice" ? (
|
) : alert.type === "notice" ? (
|
||||||
<NoticeAlert
|
<NoticeAlert
|
||||||
key={alert.id}
|
key={alert.id}
|
||||||
title={alert.data.title}
|
title={alert.data.title}
|
||||||
link={alert.data.link}
|
link={alert.data.link}
|
||||||
id={alert.id}
|
id={alert.id}
|
||||||
removeAlert={removeAlert}
|
removeAlert={removeAlert}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<SuccessAlert
|
<SuccessAlert
|
||||||
key={alert.id}
|
key={alert.id}
|
||||||
title={alert.data.title}
|
title={alert.data.title}
|
||||||
id={alert.id}
|
id={alert.id}
|
||||||
removeAlert={removeAlert}
|
removeAlert={removeAlert}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
const defaultErrorMessages ={
|
|
||||||
deleteRLAS:"Could not remove label from record, please try again",
|
|
||||||
addRLAS:"Could not label this record, please try again",
|
|
||||||
deleteRecords:"Could not delete record, please try again later"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export class CustomError extends Error{
|
|
||||||
constructor(message:string){
|
|
||||||
super(message)
|
|
||||||
Object.setPrototypeOf(this,CustomError.prototype)
|
|
||||||
}
|
|
||||||
|
|
||||||
getErrorMessage(): string{
|
|
||||||
return defaultErrorMessages[this.message]?? "unknow error, please try again"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
import { Disclosure } from "@headlessui/react";
|
import { Disclosure } from "@headlessui/react";
|
||||||
import { ChevronLeftIcon } from "@heroicons/react/24/outline";
|
import { ArrowUpTrayIcon, ChevronLeftIcon } from "@heroicons/react/24/outline";
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { classNames } from "../../utils";
|
import { classNames } from "../../utils";
|
||||||
import { locationContext } from "../../contexts/locationContext";
|
import { locationContext } from "../../contexts/locationContext";
|
||||||
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
|
|
||||||
export default function ExtraSidebar() {
|
export default function ExtraSidebar() {
|
||||||
|
const {uploadFlow} = useContext(TabsContext)
|
||||||
const {
|
const {
|
||||||
atual,
|
current,
|
||||||
isStackedOpen,
|
isStackedOpen,
|
||||||
setIsStackedOpen,
|
setIsStackedOpen,
|
||||||
extraNavigation,
|
extraNavigation,
|
||||||
|
|
@ -20,7 +22,7 @@ export default function ExtraSidebar() {
|
||||||
isStackedOpen ? "w-60" : "w-0 "
|
isStackedOpen ? "w-60" : "w-0 "
|
||||||
} flex-shrink-0 flex overflow-hidden flex-col border-r transition-all duration-500`}
|
} flex-shrink-0 flex overflow-hidden flex-col border-r transition-all duration-500`}
|
||||||
>
|
>
|
||||||
<div className="w-60 overflow-y-auto scrollbar-hide h-full">
|
<div className="w-60 overflow-y-auto scrollbar-hide h-full flex flex-col items-start">
|
||||||
<div className="flex pt-4 px-4 justify-between align-middle w-full">
|
<div className="flex pt-4 px-4 justify-between align-middle w-full">
|
||||||
<span className="text-gray-900 text-lg ml-2 font-semibold">
|
<span className="text-gray-900 text-lg ml-2 font-semibold">
|
||||||
{extraNavigation.title}
|
{extraNavigation.title}
|
||||||
|
|
@ -32,7 +34,7 @@ export default function ExtraSidebar() {
|
||||||
<ChevronLeftIcon className="h-6 w-6"></ChevronLeftIcon>
|
<ChevronLeftIcon className="h-6 w-6"></ChevronLeftIcon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-grow flex-col">
|
<div className="flex flex-grow flex-col w-full">
|
||||||
{extraNavigation.options ? (
|
{extraNavigation.options ? (
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<nav className="flex-1 space-y-1">
|
<nav className="flex-1 space-y-1">
|
||||||
|
|
@ -42,7 +44,7 @@ export default function ExtraSidebar() {
|
||||||
<Link
|
<Link
|
||||||
to={item.href}
|
to={item.href}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
item.href.split("/")[2] === atual[4]
|
item.href.split("/")[2] === current[4]
|
||||||
? "bg-gray-100 text-gray-900"
|
? "bg-gray-100 text-gray-900"
|
||||||
: "bg-white text-gray-600 hover:bg-gray-50 hover:text-gray-900",
|
: "bg-white text-gray-600 hover:bg-gray-50 hover:text-gray-900",
|
||||||
"group w-full flex items-center pl-2 py-2 text-sm font-medium rounded-md"
|
"group w-full flex items-center pl-2 py-2 text-sm font-medium rounded-md"
|
||||||
|
|
@ -50,7 +52,7 @@ export default function ExtraSidebar() {
|
||||||
>
|
>
|
||||||
<item.icon
|
<item.icon
|
||||||
className={classNames(
|
className={classNames(
|
||||||
item.href.split("/")[2] === atual[4]
|
item.href.split("/")[2] === current[4]
|
||||||
? "text-gray-500"
|
? "text-gray-500"
|
||||||
: "text-gray-400 group-hover:text-gray-500",
|
: "text-gray-400 group-hover:text-gray-500",
|
||||||
"mr-3 flex-shrink-0 h-6 w-6"
|
"mr-3 flex-shrink-0 h-6 w-6"
|
||||||
|
|
@ -69,7 +71,7 @@ export default function ExtraSidebar() {
|
||||||
<>
|
<>
|
||||||
<Disclosure.Button
|
<Disclosure.Button
|
||||||
className={classNames(
|
className={classNames(
|
||||||
item.href.split("/")[2] === atual[4]
|
item.href.split("/")[2] === current[4]
|
||||||
? "bg-gray-100 text-gray-900"
|
? "bg-gray-100 text-gray-900"
|
||||||
: "bg-white text-gray-600 hover:bg-gray-50 hover:text-gray-900",
|
: "bg-white text-gray-600 hover:bg-gray-50 hover:text-gray-900",
|
||||||
"group w-full flex items-center pl-2 pr-1 py-2 text-left text-sm font-medium rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
"group w-full flex items-center pl-2 pr-1 py-2 text-left text-sm font-medium rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
||||||
|
|
@ -102,7 +104,7 @@ export default function ExtraSidebar() {
|
||||||
key={subItem.name}
|
key={subItem.name}
|
||||||
to={subItem.href}
|
to={subItem.href}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
subItem.href.split("/")[3] === atual[5]
|
subItem.href.split("/")[3] === current[5]
|
||||||
? "bg-gray-100 text-gray-900"
|
? "bg-gray-100 text-gray-900"
|
||||||
: "bg-white text-gray-600 hover:bg-gray-50 hover:text-gray-900",
|
: "bg-white text-gray-600 hover:bg-gray-50 hover:text-gray-900",
|
||||||
"group flex w-full items-center rounded-md py-2 pl-11 pr-2 text-sm font-medium"
|
"group flex w-full items-center rounded-md py-2 pl-11 pr-2 text-sm font-medium"
|
||||||
|
|
@ -122,7 +124,11 @@ export default function ExtraSidebar() {
|
||||||
) : (
|
) : (
|
||||||
extraComponent
|
extraComponent
|
||||||
)}
|
)}
|
||||||
|
{/* need to convert to multi stackbar logic */}
|
||||||
</div>
|
</div>
|
||||||
|
{/* <div className="w-full flex content-center justify-center mt-auto mb-8">
|
||||||
|
<button onClick={()=>uploadFlow()} className="flex content-center justify-center py-3 px-6 border rounded-lg border-blue-500 text-blue-500 hover:text-white hover:bg-blue-500"><span>import flow</span><ArrowUpTrayIcon className=" ml-2 w-5 h-5"/> </button>
|
||||||
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ export default function Header({user, userNavigation}){
|
||||||
<div className="min-w-0 flex-1 ml-5">
|
<div className="min-w-0 flex-1 ml-5">
|
||||||
<Breadcrumb />
|
<Breadcrumb />
|
||||||
</div>
|
</div>
|
||||||
{/* <div className="ml-10 flex shrink-0 items-center space-x-10 pr-4">
|
<div className="ml-10 flex shrink-0 items-center space-x-10 pr-4">
|
||||||
<div className="flex items-center space-x-8">
|
<div className="flex items-center space-x-8">
|
||||||
<span className="inline-flex">
|
<span className="inline-flex">
|
||||||
<button type="button" {...triggerProps} className="-mx-1 rounded-full bg-white p-1 text-gray-400 hover:text-gray-500 relative" onClick={()=>{setNotificationCenter(false);setIsOpen(true)}}>
|
<button type="button" {...triggerProps} className="-mx-1 rounded-full bg-white p-1 text-gray-400 hover:text-gray-500 relative" onClick={()=>{setNotificationCenter(false);setIsOpen(true)}}>
|
||||||
|
|
@ -56,7 +56,7 @@ export default function Header({user, userNavigation}){
|
||||||
</button>{renderLayer(<div {...layerProps}><AlertDropdown closeFunction={()=>setIsOpen(false)} open={isOpen}></AlertDropdown></div>)}
|
</button>{renderLayer(<div {...layerProps}><AlertDropdown closeFunction={()=>setIsOpen(false)} open={isOpen}></AlertDropdown></div>)}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<Menu as="div" className="relative inline-block text-left">
|
{/* <Menu as="div" className="relative inline-block text-left">
|
||||||
<Menu.Button className="flex rounded-full bg-white text-sm focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2">
|
<Menu.Button className="flex rounded-full bg-white text-sm focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2">
|
||||||
<span className="sr-only">Open user menu</span>
|
<span className="sr-only">Open user menu</span>
|
||||||
<img className="h-8 w-8 rounded-full" src={user.imageUrl} alt="" />
|
<img className="h-8 w-8 rounded-full" src={user.imageUrl} alt="" />
|
||||||
|
|
@ -100,9 +100,9 @@ export default function Header({user, userNavigation}){
|
||||||
</div>
|
</div>
|
||||||
</Menu.Items>
|
</Menu.Items>
|
||||||
</Transition>
|
</Transition>
|
||||||
</Menu>
|
</Menu> */}
|
||||||
</div>
|
</div>
|
||||||
</div> */}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ import { useContext } from "react"
|
||||||
import { locationContext } from "../../../contexts/locationContext";
|
import { locationContext } from "../../../contexts/locationContext";
|
||||||
|
|
||||||
export default function SidebarButton({item}){
|
export default function SidebarButton({item}){
|
||||||
let {atual}= useContext(locationContext);
|
let {current}= useContext(locationContext);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Link
|
<Link
|
||||||
key={item.name}
|
key={item.name}
|
||||||
to={item.href}
|
to={item.href}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
item.href.split("/")[1]=== atual[3]? 'bg-gray-900 text-white' : 'text-gray-400 hover:bg-gray-700',
|
item.href.split("/")[1]=== current[3]? 'bg-gray-900 text-white' : 'text-gray-400 hover:bg-gray-700',
|
||||||
'flex-shrink-0 inline-flex items-center justify-center h-14 w-14 rounded-lg'
|
'flex-shrink-0 inline-flex items-center justify-center h-14 w-14 rounded-lg'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ function getPages(atual){
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Breadcrumb(){
|
export default function Breadcrumb(){
|
||||||
let {atual} = useContext(locationContext);
|
let {current} = useContext(locationContext);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<nav className="flex ml-2" aria-label="Breadcrumb">
|
<nav className="flex ml-2" aria-label="Breadcrumb">
|
||||||
|
|
@ -35,7 +35,7 @@ export default function Breadcrumb(){
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{getPages(atual).map((page) => (
|
{getPages(current).map((page) => (
|
||||||
<li key={page.href}>
|
<li key={page.href}>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<ChevronRightIcon className="h-5 w-5 flex-shrink-0 text-gray-400" aria-hidden="true" />
|
<ChevronRightIcon className="h-5 w-5 flex-shrink-0 text-gray-400" aria-hidden="true" />
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,16 @@ import { useContext, useEffect, useRef, useState } from "react";
|
||||||
import { sendAll } from "../../controllers/NodesServices";
|
import { sendAll } from "../../controllers/NodesServices";
|
||||||
import { alertContext } from "../../contexts/alertContext";
|
import { alertContext } from "../../contexts/alertContext";
|
||||||
import { nodeColors } from "../../utils";
|
import { nodeColors } from "../../utils";
|
||||||
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
|
|
||||||
const _ = require("lodash");
|
const _ = require("lodash");
|
||||||
|
|
||||||
export default function Chat({ reactFlowInstance }) {
|
export default function Chat({flow, reactFlowInstance }) {
|
||||||
|
const {updateFlow} = useContext(TabsContext)
|
||||||
|
const [saveChat,setSaveChat] = useState(false)
|
||||||
const [open, setOpen] = useState(true);
|
const [open, setOpen] = useState(true);
|
||||||
const [chatValue, setChatValue] = useState("");
|
const [chatValue, setChatValue] = useState("");
|
||||||
const [chatHistory, setChatHistory] = useState([]);
|
const [chatHistory, setChatHistory] = useState(flow.chat);
|
||||||
const {setErrorData} = useContext(alertContext);
|
const {setErrorData} = useContext(alertContext);
|
||||||
const addChatHistory = (message, isSend) => {
|
const addChatHistory = (message, isSend) => {
|
||||||
setChatHistory((old) => {
|
setChatHistory((old) => {
|
||||||
|
|
@ -23,9 +26,18 @@ export default function Chat({ reactFlowInstance }) {
|
||||||
newChat.push({ message, isSend });
|
newChat.push({ message, isSend });
|
||||||
return newChat;
|
return newChat;
|
||||||
});
|
});
|
||||||
|
setSaveChat(chat=>!chat)
|
||||||
};
|
};
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
ref.current.scrollIntoView({behavior: 'smooth'});
|
console.log("flow")
|
||||||
|
updateFlow({..._.cloneDeep(flow),chat:chatHistory})
|
||||||
|
},[saveChat])
|
||||||
|
useEffect(()=>{
|
||||||
|
setChatHistory(flow.chat)
|
||||||
|
},[flow])
|
||||||
|
useEffect(()=>{
|
||||||
|
if(ref.current)
|
||||||
|
ref.current.scrollIntoView({behavior: 'smooth'});
|
||||||
}, [chatHistory])
|
}, [chatHistory])
|
||||||
function validateNodes(){
|
function validateNodes(){
|
||||||
if(reactFlowInstance.getNodes().some((n) => (n.data.node && Object.keys(n.data.node.template).some((t: any) => ((n.data.node.template[t].required && n.data.node.template[t].value === "") && (n.data.node.template[t].required && !reactFlowInstance.getEdges().some((e) => (e.sourceHandle.split('|')[1] === t && e.sourceHandle.split('|')[2] === n.id)))))))){
|
if(reactFlowInstance.getNodes().some((n) => (n.data.node && Object.keys(n.data.node.template).some((t: any) => ((n.data.node.template[t].required && n.data.node.template[t].value === "") && (n.data.node.template[t].required && !reactFlowInstance.getEdges().some((e) => (e.sourceHandle.split('|')[1] === t && e.sourceHandle.split('|')[2] === n.id)))))))){
|
||||||
|
|
@ -40,6 +52,29 @@ export default function Chat({ reactFlowInstance }) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
|
|
||||||
|
function sendMessage(){
|
||||||
|
console.log(reactFlowInstance.toObject())
|
||||||
|
if(chatValue !== ""){
|
||||||
|
if(validateNodes()){
|
||||||
|
if(validateChatNodes()){
|
||||||
|
let message = chatValue;
|
||||||
|
setChatValue("");
|
||||||
|
addChatHistory(message, true);
|
||||||
|
console.log({...reactFlowInstance.toObject(),message,chatHistory})
|
||||||
|
sendAll({...reactFlowInstance.toObject(),message,chatHistory}).then((r) => {addChatHistory(r.data.result, false);});
|
||||||
|
} else {
|
||||||
|
setErrorData({title: 'Error sending message', list:['Chat nodes are missing.']})
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
setErrorData({title: 'Error sending message', list:['There are required fields not filled yet.']})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setErrorData({title: 'Error sending message', list:['The message cannot be empty.']})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Transition
|
<Transition
|
||||||
|
|
@ -71,14 +106,14 @@ export default function Chat({ reactFlowInstance }) {
|
||||||
{chatHistory.map((c, i) => (
|
{chatHistory.map((c, i) => (
|
||||||
<div key={i}>
|
<div key={i}>
|
||||||
{c.isSend ? (
|
{c.isSend ? (
|
||||||
<div className="w-full text-start">
|
<div className="w-full text-end">
|
||||||
<div className="text-start inline-block bg-gray-200 rounded-xl p-3 overflow-hidden w-fit max-w-[280px] px-5 text-sm font-normal rounded-tl-none">
|
<div className="text-start inline-block bg-gray-200 rounded-xl p-3 overflow-hidden w-fit max-w-[280px] px-5 text-sm font-normal rounded-tr-none">
|
||||||
{c.message}
|
{c.message}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-full text-end">
|
<div className="w-full text-start">
|
||||||
<div style={{backgroundColor: nodeColors['chat']}} className="text-start inline-block rounded-xl p-3 overflow-hidden w-fit max-w-[280px] px-5 text-sm text-white font-normal rounded-tr-none">
|
<div style={{backgroundColor: nodeColors['chat']}} className="text-start inline-block rounded-xl p-3 overflow-hidden w-fit max-w-[280px] px-5 text-sm text-white font-normal rounded-tl-none">
|
||||||
{c.message}
|
{c.message}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -90,6 +125,11 @@ export default function Chat({ reactFlowInstance }) {
|
||||||
<div className="w-full bg-white border-t flex items-center justify-between p-3">
|
<div className="w-full bg-white border-t flex items-center justify-between p-3">
|
||||||
<div className="relative w-full mt-1 rounded-md shadow-sm">
|
<div className="relative w-full mt-1 rounded-md shadow-sm">
|
||||||
<input
|
<input
|
||||||
|
onKeyDown={(event)=>{
|
||||||
|
if(event.key==='Enter'){
|
||||||
|
sendMessage()
|
||||||
|
}
|
||||||
|
}}
|
||||||
type="text"
|
type="text"
|
||||||
value={chatValue}
|
value={chatValue}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
|
@ -100,28 +140,7 @@ export default function Chat({ reactFlowInstance }) {
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
|
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => sendMessage()}
|
||||||
console.log(reactFlowInstance.toObject())
|
|
||||||
if(chatValue !== ""){
|
|
||||||
if(validateNodes()){
|
|
||||||
if(validateChatNodes()){
|
|
||||||
let message = chatValue;
|
|
||||||
setChatValue("");
|
|
||||||
addChatHistory(message, true);
|
|
||||||
sendAll({...reactFlowInstance.toObject(),message}).then((r) => {addChatHistory(r.data.result, false);});
|
|
||||||
} else {
|
|
||||||
setErrorData({title: 'Error sending message', list:['Chat nodes are missing.']})
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
setErrorData({title: 'Error sending message', list:['There are required fields not filled yet.']})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setErrorData({title: 'Error sending message', list:['The message cannot be empty.']})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<PaperAirplaneIcon
|
<PaperAirplaneIcon
|
||||||
className="h-5 w-5 text-gray-400 hover:text-gray-600"
|
className="h-5 w-5 text-gray-400 hover:text-gray-600"
|
||||||
|
|
|
||||||
|
|
@ -3,101 +3,146 @@ import { alertDropdownItem } from "../alerts/alertDropDown";
|
||||||
|
|
||||||
var _ = require("lodash");
|
var _ = require("lodash");
|
||||||
|
|
||||||
type alertContextType=
|
//types for alertContextType
|
||||||
{
|
type alertContextType = {
|
||||||
errorData: {title: string, list?: Array<string>};
|
errorData: { title: string; list?: Array<string> };
|
||||||
setErrorData:(newState:{title: string, list?: Array<string>})=>void;
|
setErrorData: (newState: { title: string; list?: Array<string> }) => void;
|
||||||
errorOpen: boolean;
|
errorOpen: boolean;
|
||||||
setErrorOpen:(newState:boolean)=>void;
|
setErrorOpen: (newState: boolean) => void;
|
||||||
noticeData: {title: string, link?: string};
|
noticeData: { title: string; link?: string };
|
||||||
setNoticeData:(newState:{title: string, link?: string})=>void;
|
setNoticeData: (newState: { title: string; link?: string }) => void;
|
||||||
noticeOpen: boolean;
|
noticeOpen: boolean;
|
||||||
setNoticeOpen:(newState:boolean)=>void;
|
setNoticeOpen: (newState: boolean) => void;
|
||||||
successData: {title: string};
|
successData: { title: string };
|
||||||
setSuccessData:(newState:{title: string})=>void;
|
setSuccessData: (newState: { title: string }) => void;
|
||||||
successOpen: boolean;
|
successOpen: boolean;
|
||||||
setSuccessOpen:(newState:boolean)=>void;
|
setSuccessOpen: (newState: boolean) => void;
|
||||||
notificationCenter:boolean
|
notificationCenter: boolean;
|
||||||
setNotificationCenter:(newState:boolean)=>void;
|
setNotificationCenter: (newState: boolean) => void;
|
||||||
notificationList:Array<alertDropdownItem>;
|
notificationList: Array<alertDropdownItem>;
|
||||||
pushNotificationList:(Object)=>void
|
pushNotificationList: (Object) => void;
|
||||||
clearNotificationList:()=>void,
|
clearNotificationList: () => void;
|
||||||
removeFromNotificationList:(index:number)=>void
|
removeFromNotificationList: (index: number) => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
const initialValue= {
|
//initial values to alertContextType
|
||||||
errorData: {title:"", list:[]},
|
const initialValue = {
|
||||||
setErrorData:()=>{},
|
errorData: { title: "", list: [] },
|
||||||
errorOpen: false,
|
setErrorData: () => {},
|
||||||
setErrorOpen:()=>{},
|
errorOpen: false,
|
||||||
noticeData: {title:"", link:""},
|
setErrorOpen: () => {},
|
||||||
setNoticeData:()=>{},
|
noticeData: { title: "", link: "" },
|
||||||
noticeOpen: false,
|
setNoticeData: () => {},
|
||||||
setNoticeOpen:()=>{},
|
noticeOpen: false,
|
||||||
successData: {title:""},
|
setNoticeOpen: () => {},
|
||||||
setSuccessData:()=>{},
|
successData: { title: "" },
|
||||||
successOpen: false,
|
setSuccessData: () => {},
|
||||||
setSuccessOpen:()=>{},
|
successOpen: false,
|
||||||
notificationCenter:false,
|
setSuccessOpen: () => {},
|
||||||
setNotificationCenter:()=>{},
|
notificationCenter: false,
|
||||||
notificationList:[],
|
setNotificationCenter: () => {},
|
||||||
pushNotificationList:()=>{},
|
notificationList: [],
|
||||||
clearNotificationList:()=>{},
|
pushNotificationList: () => {},
|
||||||
removeFromNotificationList:()=>{}
|
clearNotificationList: () => {},
|
||||||
}
|
removeFromNotificationList: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
export const alertContext = createContext<alertContextType>(initialValue);
|
export const alertContext = createContext<alertContextType>(initialValue);
|
||||||
|
|
||||||
export function AlertProvider({children}){
|
export function AlertProvider({ children }) {
|
||||||
const [errorData, setErrorDataState] = useState<{title:string,list?:Array<string>}>({title:"",list:[]});
|
const [errorData, setErrorDataState] = useState<{
|
||||||
const [errorOpen, setErrorOpen] = useState(false);
|
title: string;
|
||||||
const [noticeData, setNoticeDataState] = useState<{title:string,link?:string}>({title:"",link:""});
|
list?: Array<string>;
|
||||||
const [noticeOpen, setNoticeOpen] = useState(false);
|
}>({ title: "", list: [] });
|
||||||
const [successData, setSuccessDataState] = useState<{title:string}>({title:""});
|
const [errorOpen, setErrorOpen] = useState(false);
|
||||||
const [successOpen, setSuccessOpen] = useState(false);
|
const [noticeData, setNoticeDataState] = useState<{
|
||||||
const [notificationCenter,setNotificationCenter]=useState(false);
|
title: string;
|
||||||
const [notificationList,setNotificationList] = useState([])
|
link?: string;
|
||||||
const pushNotificationList = (notification:alertDropdownItem)=>{
|
}>({ title: "", link: "" });
|
||||||
setNotificationList((old) => {
|
const [noticeOpen, setNoticeOpen] = useState(false);
|
||||||
let newNotificationList = _.cloneDeep(old);
|
const [successData, setSuccessDataState] = useState<{ title: string }>({
|
||||||
newNotificationList.unshift(notification);
|
title: "",
|
||||||
return newNotificationList;
|
});
|
||||||
})
|
const [successOpen, setSuccessOpen] = useState(false);
|
||||||
}
|
const [notificationCenter, setNotificationCenter] = useState(false);
|
||||||
function setErrorData (newState:{title: string, list?: Array<string>}){
|
const [notificationList, setNotificationList] = useState([]);
|
||||||
setErrorDataState(newState)
|
const pushNotificationList = (notification: alertDropdownItem) => {
|
||||||
setErrorOpen(true)
|
setNotificationList((old) => {
|
||||||
if(newState.title){
|
let newNotificationList = _.cloneDeep(old);
|
||||||
setNotificationCenter(true)
|
newNotificationList.unshift(notification);
|
||||||
pushNotificationList({type:"error",title:newState.title,list:newState.list, id:_.uniqueId()})
|
return newNotificationList;
|
||||||
}
|
});
|
||||||
}
|
};
|
||||||
function setNoticeData(newState:{title: string, link?: string}){
|
function setErrorData(newState: { title: string; list?: Array<string> }) {
|
||||||
setNoticeDataState(newState)
|
setErrorDataState(newState);
|
||||||
setNoticeOpen(true)
|
setErrorOpen(true);
|
||||||
if(newState.title){
|
if (newState.title) {
|
||||||
setNotificationCenter(true)
|
setNotificationCenter(true);
|
||||||
pushNotificationList({type:"notice",title:newState.title,link:newState.link, id:_.uniqueId()})
|
pushNotificationList({
|
||||||
}
|
type: "error",
|
||||||
}
|
title: newState.title,
|
||||||
function setSuccessData(newState:{title: string}){
|
list: newState.list,
|
||||||
setSuccessDataState(newState)
|
id: _.uniqueId(),
|
||||||
setSuccessOpen(true)
|
});
|
||||||
if(newState.title){
|
}
|
||||||
setNotificationCenter(true)
|
}
|
||||||
pushNotificationList({type:"success",title:newState.title, id:_.uniqueId()})
|
function setNoticeData(newState: { title: string; link?: string }) {
|
||||||
}
|
setNoticeDataState(newState);
|
||||||
}
|
setNoticeOpen(true);
|
||||||
function clearNotificationList(){
|
if (newState.title) {
|
||||||
setNotificationList([])
|
setNotificationCenter(true);
|
||||||
}
|
pushNotificationList({
|
||||||
function removeFromNotificationList(index:number){
|
type: "notice",
|
||||||
setNotificationList((prevAlertsList) => prevAlertsList.filter((alert) => alert.id !== index));
|
title: newState.title,
|
||||||
}
|
link: newState.link,
|
||||||
return (
|
id: _.uniqueId(),
|
||||||
<alertContext.Provider value={{ removeFromNotificationList,clearNotificationList,notificationList,pushNotificationList,setNotificationCenter,notificationCenter,errorData, setErrorData,
|
});
|
||||||
errorOpen,setErrorOpen, noticeData, setNoticeData, noticeOpen, setNoticeOpen, successData, setSuccessData, successOpen, setSuccessOpen}}>
|
}
|
||||||
{children}
|
}
|
||||||
</alertContext.Provider>
|
function setSuccessData(newState: { title: string }) {
|
||||||
)
|
setSuccessDataState(newState);
|
||||||
|
setSuccessOpen(true);
|
||||||
|
if (newState.title) {
|
||||||
|
setNotificationCenter(true);
|
||||||
|
pushNotificationList({
|
||||||
|
type: "success",
|
||||||
|
title: newState.title,
|
||||||
|
id: _.uniqueId(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function clearNotificationList() {
|
||||||
|
setNotificationList([]);
|
||||||
|
}
|
||||||
|
function removeFromNotificationList(index: number) {
|
||||||
|
setNotificationList((prevAlertsList) =>
|
||||||
|
prevAlertsList.filter((alert) => alert.id !== index)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<alertContext.Provider
|
||||||
|
value={{
|
||||||
|
removeFromNotificationList,
|
||||||
|
clearNotificationList,
|
||||||
|
notificationList,
|
||||||
|
pushNotificationList,
|
||||||
|
setNotificationCenter,
|
||||||
|
notificationCenter,
|
||||||
|
errorData,
|
||||||
|
setErrorData,
|
||||||
|
errorOpen,
|
||||||
|
setErrorOpen,
|
||||||
|
noticeData,
|
||||||
|
setNoticeData,
|
||||||
|
noticeOpen,
|
||||||
|
setNoticeOpen,
|
||||||
|
successData,
|
||||||
|
setSuccessData,
|
||||||
|
successOpen,
|
||||||
|
setSuccessOpen,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</alertContext.Provider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { TabsProvider } from "./tabsContext";
|
||||||
import { TypesProvider } from "./typesContext";
|
import { TypesProvider } from "./typesContext";
|
||||||
|
|
||||||
export default function ContextWrapper({ children }) {
|
export default function ContextWrapper({ children }) {
|
||||||
|
//element to wrap all context
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<LocationProvider>
|
<LocationProvider>
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,79 @@
|
||||||
import { createContext, useState } from "react";
|
import { createContext, useState } from "react";
|
||||||
|
|
||||||
type locationContextType=
|
//types for location context
|
||||||
{
|
type locationContextType = {
|
||||||
atual:Array<string>;
|
current: Array<string>;
|
||||||
setAtual:(newState:Array<string>)=>void;
|
setCurrent: (newState: Array<string>) => void;
|
||||||
isStackedOpen: boolean;
|
isStackedOpen: boolean;
|
||||||
setIsStackedOpen:(newState:boolean)=>void;
|
setIsStackedOpen: (newState: boolean) => void;
|
||||||
showSideBar:boolean;
|
showSideBar: boolean;
|
||||||
setShowSideBar:(newState:boolean)=>void;
|
setShowSideBar: (newState: boolean) => void;
|
||||||
extraNavigation:{title:string, options?:Array<{name:string, href:string, icon: any, children?:Array<any>}>};
|
extraNavigation: {
|
||||||
setExtraNavigation:(newState:{title:string, options?:Array<{name:string, href:string, icon: any, children?:Array<any>}>}) => void;
|
title: string;
|
||||||
extraComponent:any;
|
options?: Array<{
|
||||||
setExtraComponent:(newState:any) => void;
|
name: string;
|
||||||
}
|
href: string;
|
||||||
|
icon: any;
|
||||||
const initialValue= {
|
children?: Array<any>;
|
||||||
atual : window.location.pathname.replace(/\/$/g, '').split("/"),
|
}>;
|
||||||
isStackedOpen:((window.innerWidth > 1024 && window.location.pathname.split("/")[1]) ? true : false),
|
};
|
||||||
setAtual: ()=>{},
|
setExtraNavigation: (newState: {
|
||||||
setIsStackedOpen:()=>{},
|
title: string;
|
||||||
showSideBar: window.location.pathname.split("/")[1]?true:false,
|
options?: Array<{
|
||||||
setShowSideBar:()=>{},
|
name: string;
|
||||||
extraNavigation: {title:""},
|
href: string;
|
||||||
setExtraNavigation:()=>{},
|
icon: any;
|
||||||
extraComponent: <></>,
|
children?: Array<any>;
|
||||||
setExtraComponent:()=>{},
|
}>;
|
||||||
}
|
}) => void;
|
||||||
|
extraComponent: any;
|
||||||
|
setExtraComponent: (newState: any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
//initial value for location context
|
||||||
|
const initialValue = {
|
||||||
|
//actual
|
||||||
|
current: window.location.pathname.replace(/\/$/g, "").split("/"),
|
||||||
|
isStackedOpen:
|
||||||
|
window.innerWidth > 1024 && window.location.pathname.split("/")[1]
|
||||||
|
? true
|
||||||
|
: false,
|
||||||
|
setCurrent: () => {},
|
||||||
|
setIsStackedOpen: () => {},
|
||||||
|
showSideBar: window.location.pathname.split("/")[1] ? true : false,
|
||||||
|
setShowSideBar: () => {},
|
||||||
|
extraNavigation: { title: "" },
|
||||||
|
setExtraNavigation: () => {},
|
||||||
|
extraComponent: <></>,
|
||||||
|
setExtraComponent: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
export const locationContext = createContext<locationContextType>(initialValue);
|
export const locationContext = createContext<locationContextType>(initialValue);
|
||||||
|
|
||||||
export function LocationProvider({children}){
|
export function LocationProvider({ children }) {
|
||||||
const [atual,setAtual] = useState(initialValue.atual)
|
const [current, setCurrent] = useState(initialValue.current);
|
||||||
const [isStackedOpen,setIsStackedOpen] = useState(initialValue.isStackedOpen)
|
const [isStackedOpen, setIsStackedOpen] = useState(
|
||||||
const [showSideBar,setShowSideBar] = useState(initialValue.showSideBar)
|
initialValue.isStackedOpen
|
||||||
const [extraNavigation, setExtraNavigation] = useState({title:""})
|
);
|
||||||
const [extraComponent, setExtraComponent] = useState(<></>)
|
const [showSideBar, setShowSideBar] = useState(initialValue.showSideBar);
|
||||||
return (
|
const [extraNavigation, setExtraNavigation] = useState({ title: "" });
|
||||||
<locationContext.Provider value={{isStackedOpen,setIsStackedOpen,atual,setAtual, showSideBar, setShowSideBar,extraNavigation, setExtraNavigation, extraComponent, setExtraComponent}}>
|
const [extraComponent, setExtraComponent] = useState(<></>);
|
||||||
{children}
|
return (
|
||||||
</locationContext.Provider>
|
<locationContext.Provider
|
||||||
)
|
value={{
|
||||||
|
isStackedOpen,
|
||||||
|
setIsStackedOpen,
|
||||||
|
current,
|
||||||
|
setCurrent,
|
||||||
|
showSideBar,
|
||||||
|
setShowSideBar,
|
||||||
|
extraNavigation,
|
||||||
|
setExtraNavigation,
|
||||||
|
extraComponent,
|
||||||
|
setExtraComponent,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</locationContext.Provider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,33 @@
|
||||||
import { createContext } from "react";
|
import { createContext } from "react";
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
//context to set JSX element on the DOM
|
||||||
export const PopUpContext = createContext({
|
export const PopUpContext = createContext({
|
||||||
openPopUp: (popUpElement: JSX.Element) => {},
|
openPopUp: (popUpElement: JSX.Element) => {},
|
||||||
closePopUp: () => {}
|
closePopUp: () => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
interface PopUpProviderProps {
|
interface PopUpProviderProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PopUpProvider = ({ children }: PopUpProviderProps) => {
|
const PopUpProvider = ({ children }: PopUpProviderProps) => {
|
||||||
const [popUpElement, setPopUpElement] = useState<JSX.Element | null>(null);
|
const [popUpElement, setPopUpElement] = useState<JSX.Element | null>(null);
|
||||||
|
|
||||||
const openPopUp = (element: JSX.Element) => {
|
const openPopUp = (element: JSX.Element) => {
|
||||||
setPopUpElement(element);
|
setPopUpElement(element);
|
||||||
};
|
};
|
||||||
|
|
||||||
const closePopUp = () => {
|
const closePopUp = () => {
|
||||||
setPopUpElement(null);
|
setPopUpElement(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PopUpContext.Provider value={{ openPopUp, closePopUp }}>
|
<PopUpContext.Provider value={{ openPopUp, closePopUp }}>
|
||||||
{children}
|
{children}
|
||||||
{popUpElement}
|
{popUpElement}
|
||||||
</PopUpContext.Provider>
|
</PopUpContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PopUpProvider;
|
export default PopUpProvider;
|
||||||
|
|
|
||||||
|
|
@ -1,102 +1,158 @@
|
||||||
import { createContext, useEffect, useState, useRef } from "react";
|
import { createContext, useEffect, useState, useRef } from "react";
|
||||||
|
import { example } from "../data_assets/example";
|
||||||
|
|
||||||
type flow={name:string,id:string,data:any}
|
type flow = {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
data: any;
|
||||||
|
chat: Array<{ message: string; isSend: boolean }>;
|
||||||
|
};
|
||||||
|
|
||||||
type TabsContextType={
|
type TabsContextType = {
|
||||||
tabIndex:number;
|
tabIndex: number;
|
||||||
setTabIndex:(index:number)=>void;
|
setTabIndex: (index: number) => void;
|
||||||
flows:Array<flow>
|
flows: Array<flow>;
|
||||||
removeFlow:(id:string)=>void;
|
removeFlow: (id: string) => void;
|
||||||
addFlow:(flowData?:any)=>void;
|
addFlow: (flowData?: any) => void;
|
||||||
updateFlow:(newFlow:flow)=>void;
|
updateFlow: (newFlow: flow) => void;
|
||||||
incrementNodeId:()=>number,
|
incrementNodeId: () => number;
|
||||||
}
|
downloadFlow: () => void;
|
||||||
|
uploadFlow: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
const TabsContextInitialValue = {
|
const TabsContextInitialValue = {
|
||||||
tabIndex : 0,
|
tabIndex: 0,
|
||||||
setTabIndex:(index:number)=>{},
|
setTabIndex: (index: number) => {},
|
||||||
flows:[],
|
flows: [],
|
||||||
removeFlow:(id:string)=>{},
|
removeFlow: (id: string) => {},
|
||||||
addFlow:(flowData?:any)=>{},
|
addFlow: (flowData?: any) => {},
|
||||||
updateFlow:(newFlow:flow)=>{},
|
updateFlow: (newFlow: flow) => {},
|
||||||
incrementNodeId:()=>0,
|
incrementNodeId: () => 0,
|
||||||
|
downloadFlow: () => {},
|
||||||
|
uploadFlow: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
}
|
export const TabsContext = createContext<TabsContextType>(
|
||||||
|
TabsContextInitialValue
|
||||||
export const TabsContext = createContext<TabsContextType>(TabsContextInitialValue)
|
);
|
||||||
|
|
||||||
let _ = require('lodash');
|
let _ = require("lodash");
|
||||||
|
|
||||||
export function TabsProvider({children}){
|
export function TabsProvider({ children }) {
|
||||||
const [tabIndex,setTabIndex] = useState(0)
|
const [tabIndex, setTabIndex] = useState(0);
|
||||||
const [flows,setFlows] = useState<Array<flow>>([])
|
const [flows, setFlows] = useState<Array<flow>>([]);
|
||||||
const [id, setId] = useState(0);
|
const [id, setId] = useState(0);
|
||||||
|
|
||||||
const newNodeId = useRef(0);
|
const newNodeId = useRef(0);
|
||||||
function incrementNodeId(){
|
function incrementNodeId() {
|
||||||
newNodeId.current = newNodeId.current + 1;
|
newNodeId.current = newNodeId.current + 1;
|
||||||
return newNodeId.current;
|
return newNodeId.current;
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(flows.length !== 0)
|
if (flows.length !== 0)
|
||||||
window.localStorage.setItem('tabsData', JSON.stringify({tabIndex, flows, id, nodeId: newNodeId.current}));
|
window.localStorage.setItem(
|
||||||
}, [flows, id, tabIndex, newNodeId]);
|
"tabsData",
|
||||||
|
JSON.stringify({ tabIndex, flows, id, nodeId: newNodeId.current })
|
||||||
useEffect(() => {
|
);
|
||||||
let cookie = window.localStorage.getItem('tabsData');
|
}, [flows, id, tabIndex, newNodeId]);
|
||||||
if(cookie){
|
|
||||||
let cookieObject = JSON.parse(cookie);
|
useEffect(() => {
|
||||||
setTabIndex(cookieObject.tabIndex);
|
let cookie = window.localStorage.getItem("tabsData");
|
||||||
setFlows(cookieObject.flows);
|
if (cookie) {
|
||||||
setId(cookieObject.id);
|
let cookieObject = JSON.parse(cookie);
|
||||||
newNodeId.current= cookieObject.nodeId;
|
setTabIndex(cookieObject.tabIndex);
|
||||||
}
|
setFlows(cookieObject.flows);
|
||||||
}, [])
|
setId(cookieObject.id);
|
||||||
|
newNodeId.current = cookieObject.nodeId;
|
||||||
function removeFlow(id:string){
|
}
|
||||||
setFlows(prevState=>{
|
}, []);
|
||||||
const newFlows = [...prevState];
|
|
||||||
const index = newFlows.findIndex(flow=>flow.id===id);
|
function downloadFlow() {
|
||||||
if(index >= 0){
|
const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
|
||||||
if(index===tabIndex){
|
JSON.stringify(flows[tabIndex])
|
||||||
setTabIndex(flows.length-2);
|
)}`;
|
||||||
newFlows.splice(index,1);
|
const link = document.createElement("a");
|
||||||
} else {
|
link.href = jsonString;
|
||||||
let flowId = flows[tabIndex].id;
|
link.download = `${flows[tabIndex].name}.json`;
|
||||||
newFlows.splice(index,1);
|
link.click();
|
||||||
setTabIndex(newFlows.findIndex(flow=>flow.id === flowId));
|
}
|
||||||
}
|
|
||||||
|
function uploadFlow() {
|
||||||
}
|
const input = document.createElement("input");
|
||||||
return newFlows;
|
input.type = "file";
|
||||||
});
|
input.onchange = (e: Event) => {
|
||||||
}
|
if ((e.target as HTMLInputElement).files[0].type === "application/json") {
|
||||||
function addFlow(flowData?:flow) {
|
const file = (e.target as HTMLInputElement).files[0];
|
||||||
const data = flowData?flowData:null
|
file.text().then((text) => {
|
||||||
let newFlow: flow = {name: "flow"+id, id: id.toString(), data}
|
console.log(JSON.parse(text),"json from upload")
|
||||||
setId((old) => old+1);
|
addFlow(JSON.parse(text));
|
||||||
setFlows(prevState => {
|
});
|
||||||
const newFlows = [...prevState, newFlow];
|
}
|
||||||
return newFlows;
|
};
|
||||||
});
|
input.click();
|
||||||
setTabIndex(flows.length);
|
}
|
||||||
}
|
|
||||||
function updateFlow(newFlow:flow){
|
function removeFlow(id: string) {
|
||||||
setFlows(prevState=>{
|
setFlows((prevState) => {
|
||||||
const newFlows = [...prevState];
|
const newFlows = [...prevState];
|
||||||
const index = newFlows.findIndex(flow=>flow.id===newFlow.id)
|
const index = newFlows.findIndex((flow) => flow.id === id);
|
||||||
if(index!==-1){
|
if (index >= 0) {
|
||||||
newFlows[index].data = newFlow.data
|
if (index === tabIndex) {
|
||||||
newFlows[index].name = newFlow.name
|
setTabIndex(flows.length - 2);
|
||||||
}
|
newFlows.splice(index, 1);
|
||||||
return newFlows;
|
} else {
|
||||||
});
|
let flowId = flows[tabIndex].id;
|
||||||
}
|
newFlows.splice(index, 1);
|
||||||
|
setTabIndex(newFlows.findIndex((flow) => flow.id === flowId));
|
||||||
return(
|
}
|
||||||
<TabsContext.Provider value={{tabIndex,setTabIndex,flows,incrementNodeId, removeFlow,addFlow,updateFlow}}>
|
}
|
||||||
{children}
|
return newFlows;
|
||||||
</TabsContext.Provider>
|
});
|
||||||
)
|
}
|
||||||
|
function addFlow(flow?: flow) {
|
||||||
|
const data = flow?.data ? flow.data : null;
|
||||||
|
let newFlow: flow = {
|
||||||
|
name: flow ? flow.name : "flow" + id,
|
||||||
|
id: id.toString(),
|
||||||
|
data,
|
||||||
|
chat: flow ? flow.chat : [],
|
||||||
|
};
|
||||||
|
setId((old) => old + 1);
|
||||||
|
setFlows((prevState) => {
|
||||||
|
const newFlows = [...prevState, newFlow];
|
||||||
|
return newFlows;
|
||||||
|
});
|
||||||
|
setTabIndex(flows.length);
|
||||||
|
}
|
||||||
|
function updateFlow(newFlow: flow) {
|
||||||
|
console.log(newFlow);
|
||||||
|
setFlows((prevState) => {
|
||||||
|
const newFlows = [...prevState];
|
||||||
|
const index = newFlows.findIndex((flow) => flow.id === newFlow.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
newFlows[index].data = newFlow.data;
|
||||||
|
newFlows[index].name = newFlow.name;
|
||||||
|
newFlows[index].chat = newFlow.chat;
|
||||||
|
}
|
||||||
|
return newFlows;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TabsContext.Provider
|
||||||
|
value={{
|
||||||
|
tabIndex,
|
||||||
|
setTabIndex,
|
||||||
|
flows,
|
||||||
|
incrementNodeId,
|
||||||
|
removeFlow,
|
||||||
|
addFlow,
|
||||||
|
updateFlow,
|
||||||
|
downloadFlow,
|
||||||
|
uploadFlow,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</TabsContext.Provider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,44 @@
|
||||||
import { ReactEventHandler, createContext, useState } from "react";
|
import { createContext, useState } from "react";
|
||||||
import { ReactFlowInstance } from "reactflow";
|
import { ReactFlowInstance } from "reactflow";
|
||||||
|
|
||||||
type typesContextType=
|
//context to share types adn functions from nodes to flow
|
||||||
{
|
type typesContextType = {
|
||||||
reactFlowInstance: ReactFlowInstance;
|
reactFlowInstance: ReactFlowInstance;
|
||||||
setReactFlowInstance: any;
|
setReactFlowInstance: any;
|
||||||
deleteNode:(idx:number)=>void;
|
deleteNode: (idx: number) => void;
|
||||||
types: {};
|
types: {};
|
||||||
setTypes:(newState:{})=>void;
|
setTypes: (newState: {}) => void;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
const initialValue = {
|
||||||
|
reactFlowInstance: null,
|
||||||
const initialValue= {
|
setReactFlowInstance: () => {},
|
||||||
reactFlowInstance: null,
|
deleteNode: () => {},
|
||||||
setReactFlowInstance: ()=>{},
|
types: {},
|
||||||
deleteNode: ()=>{},
|
setTypes: () => {},
|
||||||
types: {},
|
};
|
||||||
setTypes:()=>{},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const typesContext = createContext<typesContextType>(initialValue);
|
export const typesContext = createContext<typesContextType>(initialValue);
|
||||||
|
|
||||||
export function TypesProvider({children}){
|
export function TypesProvider({ children }) {
|
||||||
const [types, setTypes] = useState({});
|
const [types, setTypes] = useState({});
|
||||||
const [reactFlowInstance, setReactFlowInstance] = useState(null);
|
const [reactFlowInstance, setReactFlowInstance] = useState(null);
|
||||||
function deleteNode(idx){
|
function deleteNode(idx) {
|
||||||
reactFlowInstance.setNodes(
|
reactFlowInstance.setNodes(
|
||||||
reactFlowInstance.getNodes().filter((n) => n.id !== idx)
|
reactFlowInstance.getNodes().filter((n) => n.id !== idx)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<typesContext.Provider value={{ types, setTypes, reactFlowInstance, setReactFlowInstance, deleteNode}}>
|
<typesContext.Provider
|
||||||
{children}
|
value={{
|
||||||
</typesContext.Provider>
|
types,
|
||||||
)
|
setTypes,
|
||||||
|
reactFlowInstance,
|
||||||
|
setReactFlowInstance,
|
||||||
|
deleteNode,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</typesContext.Provider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ import {
|
||||||
|
|
||||||
export const sidebarNavigation = [
|
export const sidebarNavigation = [
|
||||||
{ name: 'Home', href: '/', icon: HomeIcon, current: true },
|
{ name: 'Home', href: '/', icon: HomeIcon, current: true },
|
||||||
{ name: 'Table', href: '/table/', icon: TableCellsIcon, current: false },
|
// { name: 'Table', href: '/table/', icon: TableCellsIcon, current: false },
|
||||||
//{ name: 'Train', href: '#', icon: BoltIcon, current: false },
|
// //{ name: 'Train', href: '#', icon: BoltIcon, current: false },
|
||||||
//{ name: 'Model Details', href: '#', icon: LightBulbIcon, current: false },
|
// //{ name: 'Model Details', href: '#', icon: LightBulbIcon, current: false },
|
||||||
//{ name: 'Deploy', href: '#', icon: RocketLaunchIcon, current: false },
|
// //{ name: 'Deploy', href: '#', icon: RocketLaunchIcon, current: false },
|
||||||
{ name: 'Settings', href: '/settings/', icon: Cog6ToothIcon, current: false },
|
// { name: 'Settings', href: '/settings/', icon: Cog6ToothIcon, current: false },
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ export default function ExtraSidebar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-4">
|
<div className="mt-4 w-full">
|
||||||
{Object.keys(data).map((d, i) => (
|
{Object.keys(data).map((d, i) => (
|
||||||
<DisclosureComponent
|
<DisclosureComponent
|
||||||
key={i}
|
key={i}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { ArrowDownTrayIcon } from "@heroicons/react/24/outline";
|
||||||
import { PlusIcon, XMarkIcon } from "@heroicons/react/24/solid";
|
import { PlusIcon, XMarkIcon } from "@heroicons/react/24/solid";
|
||||||
import { useContext, useRef, useState } from "react";
|
import { useContext, useRef, useState } from "react";
|
||||||
import { TabsContext } from "../../../../contexts/tabsContext";
|
import { TabsContext } from "../../../../contexts/tabsContext";
|
||||||
|
|
@ -5,76 +6,85 @@ import { TabsContext } from "../../../../contexts/tabsContext";
|
||||||
var _ = require("lodash");
|
var _ = require("lodash");
|
||||||
|
|
||||||
export default function TabComponent({ selected, flow, onClick }) {
|
export default function TabComponent({ selected, flow, onClick }) {
|
||||||
const { removeFlow, updateFlow,flows } = useContext(TabsContext);
|
const { removeFlow, updateFlow, flows, downloadFlow } =
|
||||||
const [isRename, setIsRename] = useState(false);
|
useContext(TabsContext);
|
||||||
const [value, setValue] = useState("");
|
const [isRename, setIsRename] = useState(false);
|
||||||
return (
|
const [value, setValue] = useState("");
|
||||||
<>
|
return (
|
||||||
{flow ? (
|
<>
|
||||||
!selected ? (
|
{flow ? (
|
||||||
<div
|
!selected ? (
|
||||||
className="flex justify-between select-none truncate w-44 items-center px-4 my-1.5 border-x border-t border-t-transparent border-gray-300 -ml-px"
|
<div
|
||||||
onClick={onClick}
|
className="flex justify-between select-none truncate w-44 items-center px-4 my-1.5 border-x border-t border-t-transparent border-gray-300 -ml-px"
|
||||||
>
|
onClick={onClick}
|
||||||
{flow.name}
|
>
|
||||||
<button
|
{flow.name}
|
||||||
onClick={(e) => {
|
<button
|
||||||
e.stopPropagation();
|
onClick={(e) => {
|
||||||
removeFlow(flow.id);
|
e.stopPropagation();
|
||||||
}}
|
removeFlow(flow.id);
|
||||||
>
|
}}
|
||||||
<XMarkIcon className="h-4 hover:bg-white rounded-full" />
|
>
|
||||||
</button>
|
<XMarkIcon className="h-4 hover:bg-white rounded-full" />
|
||||||
</div>
|
</button>
|
||||||
) : (
|
</div>
|
||||||
<div className="bg-white flex select-none justify-between w-44 items-center border border-b-0 border-gray-300 px-4 py-1.5 rounded-t-xl -ml-px">
|
) : (
|
||||||
{isRename ? (
|
<div className="bg-white flex select-none justify-between w-44 items-center border border-b-0 border-gray-300 px-4 py-1.5 rounded-t-xl -ml-px">
|
||||||
<input
|
{isRename ? (
|
||||||
autoFocus
|
<input
|
||||||
className="bg-transparent focus:border-none active:outline hover:outline focus:outline outline-gray-300 rounded-md w-28"
|
autoFocus
|
||||||
onBlur={() => {
|
className="bg-transparent focus:border-none active:outline hover:outline focus:outline outline-gray-300 rounded-md w-28"
|
||||||
setIsRename(false);
|
onBlur={() => {
|
||||||
if (value !== "") {
|
setIsRename(false);
|
||||||
let newFlow = _.cloneDeep(flow);
|
if (value !== "") {
|
||||||
newFlow.name = value;
|
let newFlow = _.cloneDeep(flow);
|
||||||
updateFlow(newFlow);
|
newFlow.name = value;
|
||||||
}
|
updateFlow(newFlow);
|
||||||
}}
|
}
|
||||||
value={value}
|
}}
|
||||||
onChange={(e) => {
|
value={value}
|
||||||
setValue(e.target.value);
|
onChange={(e) => {
|
||||||
}}
|
setValue(e.target.value);
|
||||||
/>
|
}}
|
||||||
) : (
|
/>
|
||||||
<span
|
) : (
|
||||||
className="text-left truncate"
|
<div className="flex items-center gap-2">
|
||||||
onDoubleClick={() => {
|
<span
|
||||||
setIsRename(true);
|
className="text-left truncate"
|
||||||
setValue(flow.name);
|
onDoubleClick={() => {
|
||||||
}}
|
setIsRename(true);
|
||||||
>
|
setValue(flow.name);
|
||||||
{flow.name}
|
}}
|
||||||
</span>
|
>
|
||||||
)}
|
{flow.name}
|
||||||
<button
|
</span>
|
||||||
onClick={() => {
|
{/* <ArrowDownTrayIcon
|
||||||
removeFlow(flow.id);
|
onClick={() => downloadFlow()}
|
||||||
}}
|
className="w-4 h-4 hover:text-blue-500 cursor-pointer"
|
||||||
>
|
/> */}
|
||||||
{flows.length>1&& <XMarkIcon className="h-4 hover:bg-gray-100 rounded-full" />}
|
</div>
|
||||||
</button>
|
)}
|
||||||
</div>
|
<button
|
||||||
)
|
onClick={() => {
|
||||||
) : (
|
removeFlow(flow.id);
|
||||||
<div className="h-full py-1.5 flex justify-center items-center">
|
}}
|
||||||
<button
|
>
|
||||||
className="px-3 flex items-center h-full pb-0.5 pt-0.5 border-gray-300 -ml-px border-t border-t-transparent"
|
{flows.length > 1 && (
|
||||||
onClick={onClick}
|
<XMarkIcon className="h-4 hover:bg-gray-100 rounded-full" />
|
||||||
>
|
)}
|
||||||
<PlusIcon className="h-5 rounded-full hover:bg-white" />
|
</button>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
)
|
||||||
)}
|
) : (
|
||||||
</>
|
<div className="h-full py-1.5 flex justify-center items-center">
|
||||||
);
|
<button
|
||||||
|
className="px-3 flex items-center h-full pb-0.5 pt-0.5 border-gray-300 -ml-px border-t border-t-transparent"
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-5 rounded-full hover:bg-white" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
||||||
import ReactFlow, {
|
import ReactFlow, {
|
||||||
Background,
|
Background,
|
||||||
Controls,
|
Controls,
|
||||||
addEdge,
|
addEdge,
|
||||||
useEdgesState,
|
useEdgesState,
|
||||||
useNodesState,
|
useNodesState,
|
||||||
ReactFlowProvider,
|
ReactFlowProvider,
|
||||||
useReactFlow,
|
useReactFlow,
|
||||||
|
ControlButton,
|
||||||
} from "reactflow";
|
} from "reactflow";
|
||||||
import { locationContext } from "../../contexts/locationContext";
|
import { locationContext } from "../../contexts/locationContext";
|
||||||
import ExtraSidebar from "./components/extraSidebarComponent";
|
import ExtraSidebar from "./components/extraSidebarComponent";
|
||||||
|
|
@ -20,164 +21,181 @@ import BooleanNode from "../../CustomNodes/BooleanNode";
|
||||||
import { alertContext } from "../../contexts/alertContext";
|
import { alertContext } from "../../contexts/alertContext";
|
||||||
import { TabsContext } from "../../contexts/tabsContext";
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
import { typesContext } from "../../contexts/typesContext";
|
import { typesContext } from "../../contexts/typesContext";
|
||||||
|
import {
|
||||||
|
ArrowDownTrayIcon,
|
||||||
|
ArrowUpTrayIcon,
|
||||||
|
} from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
genericNode: GenericNode,
|
genericNode: GenericNode,
|
||||||
inputNode: InputNode,
|
inputNode: InputNode,
|
||||||
chatInputNode: ChatInputNode,
|
chatInputNode: ChatInputNode,
|
||||||
chatOutputNode: ChatOutputNode,
|
chatOutputNode: ChatOutputNode,
|
||||||
booleanNode: BooleanNode,
|
booleanNode: BooleanNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
var _ = require("lodash");
|
var _ = require("lodash");
|
||||||
|
|
||||||
export default function FlowPage({ flow }) {
|
export default function FlowPage({ flow }) {
|
||||||
let { updateFlow, incrementNodeId } = useContext(TabsContext);
|
let { updateFlow, incrementNodeId, downloadFlow, uploadFlow } =
|
||||||
const { types, reactFlowInstance, setReactFlowInstance } =
|
useContext(TabsContext);
|
||||||
useContext(typesContext);
|
const { types, reactFlowInstance, setReactFlowInstance } =
|
||||||
const reactFlowWrapper = useRef(null);
|
useContext(typesContext);
|
||||||
|
const reactFlowWrapper = useRef(null);
|
||||||
|
|
||||||
function getId(){
|
function getId() {
|
||||||
return `dndnode_}`+incrementNodeId();
|
return `dndnode_` + incrementNodeId();
|
||||||
};
|
}
|
||||||
|
|
||||||
const { setExtraComponent, setExtraNavigation } = useContext(locationContext);
|
const { setExtraComponent, setExtraNavigation } = useContext(locationContext);
|
||||||
const { setErrorData } = useContext(alertContext);
|
const { setErrorData } = useContext(alertContext);
|
||||||
|
|
||||||
const [nodes, setNodes, onNodesChange] = useNodesState(
|
const [nodes, setNodes, onNodesChange] = useNodesState(
|
||||||
flow.data?.nodes ?? []
|
flow.data?.nodes ?? []
|
||||||
);
|
);
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState(
|
const [edges, setEdges, onEdgesChange] = useEdgesState(
|
||||||
flow.data?.edges ?? []
|
flow.data?.edges ?? []
|
||||||
);
|
);
|
||||||
const {setViewport} = useReactFlow()
|
const { setViewport } = useReactFlow();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (reactFlowInstance && flow) {
|
if (reactFlowInstance && flow) {
|
||||||
flow.data = reactFlowInstance.toObject();
|
flow.data = reactFlowInstance.toObject();
|
||||||
updateFlow(flow);
|
updateFlow(flow);
|
||||||
}
|
}
|
||||||
}, [nodes, edges]);
|
}, [nodes, edges]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setNodes(flow?.data?.nodes ?? []);
|
setNodes(flow?.data?.nodes ?? []);
|
||||||
setEdges(flow?.data?.edges ?? []);
|
setEdges(flow?.data?.edges ?? []);
|
||||||
if (reactFlowInstance) {
|
if (reactFlowInstance) {
|
||||||
setViewport(
|
setViewport(flow?.data?.viewport ?? { x: 1, y: 0, zoom: 1 });
|
||||||
flow?.data?.viewport ?? { x: 1, y: 0, zoom: 1 }
|
}
|
||||||
);
|
}, [flow, reactFlowInstance, setEdges, setNodes]);
|
||||||
}
|
|
||||||
}, [flow, reactFlowInstance, setEdges, setNodes]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setExtraComponent(<ExtraSidebar />);
|
setExtraComponent(<ExtraSidebar />);
|
||||||
setExtraNavigation({ title: "Nodes" });
|
setExtraNavigation({ title: "Nodes" });
|
||||||
}, [setExtraComponent, setExtraNavigation]);
|
}, [setExtraComponent, setExtraNavigation]);
|
||||||
|
|
||||||
const onEdgesChangeMod = useCallback(
|
const onEdgesChangeMod = useCallback(
|
||||||
(s) => {
|
(s) => {
|
||||||
onEdgesChange(s);
|
onEdgesChange(s);
|
||||||
setNodes((x) => {
|
setNodes((x) => {
|
||||||
let newX = _.cloneDeep(x);
|
let newX = _.cloneDeep(x);
|
||||||
return newX;
|
return newX;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[onEdgesChange, setNodes]
|
[onEdgesChange, setNodes]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onConnect = useCallback(
|
const onConnect = useCallback(
|
||||||
(params) => {
|
(params) => {
|
||||||
setEdges((eds) =>
|
setEdges((eds) =>
|
||||||
addEdge({ ...params, className: "animate-pulse" }, eds)
|
addEdge({ ...params, className: "animate-pulse" }, eds)
|
||||||
);
|
);
|
||||||
setNodes((x) => {
|
setNodes((x) => {
|
||||||
let newX = _.cloneDeep(x);
|
let newX = _.cloneDeep(x);
|
||||||
return newX;
|
return newX;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setEdges, setNodes]
|
[setEdges, setNodes]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onDragOver = useCallback((event) => {
|
const onDragOver = useCallback((event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.dataTransfer.dropEffect = "move";
|
event.dataTransfer.dropEffect = "move";
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onDrop = useCallback(
|
const onDrop = useCallback(
|
||||||
(event) => {
|
(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const reactflowBounds = reactFlowWrapper.current.getBoundingClientRect();
|
const reactflowBounds = reactFlowWrapper.current.getBoundingClientRect();
|
||||||
let data = JSON.parse(event.dataTransfer.getData("json"));
|
let data = JSON.parse(event.dataTransfer.getData("json"));
|
||||||
if (
|
if (
|
||||||
data.type !== "chatInput" ||
|
data.type !== "chatInput" ||
|
||||||
(data.type === "chatInput" &&
|
(data.type === "chatInput" &&
|
||||||
!reactFlowInstance.getNodes().some((n) => n.type === "chatInputNode"))
|
!reactFlowInstance.getNodes().some((n) => n.type === "chatInputNode"))
|
||||||
) {
|
) {
|
||||||
const position = reactFlowInstance.project({
|
const position = reactFlowInstance.project({
|
||||||
x: event.clientX - reactflowBounds.left,
|
x: event.clientX - reactflowBounds.left,
|
||||||
y: event.clientY - reactflowBounds.top,
|
y: event.clientY - reactflowBounds.top,
|
||||||
});
|
});
|
||||||
let newId = getId();
|
let newId = getId();
|
||||||
|
|
||||||
const newNode = {
|
const newNode = {
|
||||||
id: newId,
|
id: newId,
|
||||||
type:
|
type:
|
||||||
data.type === "str"
|
data.type === "str"
|
||||||
? "inputNode"
|
? "inputNode"
|
||||||
: data.type === "chatInput"
|
: data.type === "chatInput"
|
||||||
? "chatInputNode"
|
? "chatInputNode"
|
||||||
: data.type === "chatOutput"
|
: data.type === "chatOutput"
|
||||||
? "chatOutputNode"
|
? "chatOutputNode"
|
||||||
: data.type === "bool"
|
: data.type === "bool"
|
||||||
? "booleanNode"
|
? "booleanNode"
|
||||||
: "genericNode",
|
: "genericNode",
|
||||||
position,
|
position,
|
||||||
data: {
|
data: {
|
||||||
...data,
|
...data,
|
||||||
id: newId,
|
id: newId,
|
||||||
value: null,
|
value: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
setNodes((nds) => nds.concat(newNode));
|
setNodes((nds) => nds.concat(newNode));
|
||||||
} else {
|
} else {
|
||||||
setErrorData({
|
setErrorData({
|
||||||
title: "Error creating node",
|
title: "Error creating node",
|
||||||
list: ["There can't be more than one chat input."],
|
list: ["There can't be more than one chat input."],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[reactFlowInstance, setErrorData, setNodes]
|
[reactFlowInstance, setErrorData, setNodes]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full" ref={reactFlowWrapper}>
|
<div className="w-full h-full" ref={reactFlowWrapper}>
|
||||||
{Object.keys(types).length > 0 ? (
|
{Object.keys(types).length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
onMove={() =>
|
onMove={() =>
|
||||||
updateFlow({ ...flow, data: reactFlowInstance.toObject() })
|
updateFlow({ ...flow, data: reactFlowInstance.toObject() })
|
||||||
}
|
}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
onNodesChange={onNodesChange}
|
onNodesChange={onNodesChange}
|
||||||
onEdgesChange={onEdgesChangeMod}
|
onEdgesChange={onEdgesChangeMod}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
onLoad={setReactFlowInstance}
|
onLoad={setReactFlowInstance}
|
||||||
onInit={setReactFlowInstance}
|
onInit={setReactFlowInstance}
|
||||||
nodeTypes={nodeTypes}
|
nodeTypes={nodeTypes}
|
||||||
connectionLineComponent={connection}
|
connectionLineComponent={connection}
|
||||||
onDragOver={onDragOver}
|
onDragOver={onDragOver}
|
||||||
onDrop={onDrop}
|
onDrop={onDrop}
|
||||||
>
|
>
|
||||||
<Background />
|
<Background />
|
||||||
<Controls></Controls>
|
<Controls>
|
||||||
</ReactFlow>
|
<ControlButton
|
||||||
<Chat reactFlowInstance={reactFlowInstance} />
|
className="text-black hover:text-blue-500"
|
||||||
</>
|
onClick={() => uploadFlow()}
|
||||||
) : (
|
>
|
||||||
<></>
|
<ArrowUpTrayIcon />
|
||||||
)}
|
</ControlButton>
|
||||||
</div>
|
|
||||||
);
|
<ControlButton
|
||||||
|
className="text-black hover:text-blue-500"
|
||||||
|
onClick={() => downloadFlow()}
|
||||||
|
>
|
||||||
|
<ArrowDownTrayIcon />
|
||||||
|
</ControlButton>
|
||||||
|
</Controls>
|
||||||
|
</ReactFlow>
|
||||||
|
<Chat flow={flow} reactFlowInstance={reactFlowInstance} />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
space_flow/src/react-app-env.d.ts
vendored
1
space_flow/src/react-app-env.d.ts
vendored
|
|
@ -1 +0,0 @@
|
||||||
/// <reference types="react-scripts" />
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
import '@testing-library/jest-dom';
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue