Merge branch 'clean' into dev

This commit is contained in:
anovazzi1 2023-03-05 14:47:11 -03:00
commit 334597f8a5
15 changed files with 170 additions and 280 deletions

View file

@ -8,7 +8,6 @@ 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 Sidebar from "./components/SidebarComponent";
import Header from "./components/HeaderComponent"; import Header from "./components/HeaderComponent";
import TabsManagerComponent from "./pages/FlowPage/components/tabsManagerComponent"; import TabsManagerComponent from "./pages/FlowPage/components/tabsManagerComponent";
@ -36,38 +35,45 @@ export default function App() {
setSuccessOpen, setSuccessOpen,
} = useContext(alertContext); } = useContext(alertContext);
const [alertsList, setAlertsList] = useState<Array<{type:string,data:{title:string,list?:Array<string>,link?:string},id:string}>>([]); // Initialize state variable for the list of alerts
const [alertsList, setAlertsList] = useState<Array<{type:string,data:{title:string,list?:Array<string>,link?:string},id:string}>>([]);
useEffect(() => { // Use effect hook to update alertsList when a new alert is added
if (errorOpen && errorData) { useEffect(() => {
setErrorOpen(false); // If there is an error alert open with data, add it to the alertsList
setAlertsList((old) => { if (errorOpen && errorData) {
let newAlertsList = [ setErrorOpen(false);
...old, setAlertsList((old) => {
{ type: "error", data: _.cloneDeep(errorData), id: _.uniqueId() }, let newAlertsList = [
]; ...old,
return newAlertsList; { type: "error", data: _.cloneDeep(errorData), id: _.uniqueId() },
}); ];
} else if (noticeOpen && noticeData) { return newAlertsList;
setNoticeOpen(false); });
setAlertsList((old) => { }
let newAlertsList = [ // If there is a notice alert open with data, add it to the alertsList
...old, else if (noticeOpen && noticeData) {
{ type: "notice", data: _.cloneDeep(noticeData), id: _.uniqueId() }, setNoticeOpen(false);
]; setAlertsList((old) => {
return newAlertsList; let newAlertsList = [
}); ...old,
} else if (successOpen && successData) { { type: "notice", data: _.cloneDeep(noticeData), id: _.uniqueId() },
setSuccessOpen(false); ];
setAlertsList((old) => { return newAlertsList;
let newAlertsList = [ });
...old, }
{ type: "success", data: _.cloneDeep(successData), id: _.uniqueId() }, // If there is a success alert open with data, add it to the alertsList
]; else if (successOpen && successData) {
return newAlertsList; setSuccessOpen(false);
}); setAlertsList((old) => {
} let newAlertsList = [
}, [_, errorData, errorOpen, noticeData, noticeOpen, setErrorOpen, setNoticeOpen, setSuccessOpen, successData, successOpen]); ...old,
{ type: "success", data: _.cloneDeep(successData), id: _.uniqueId() },
];
return newAlertsList;
});
}
}, [_, errorData, errorOpen, noticeData, noticeOpen, setErrorOpen, setNoticeOpen, setSuccessOpen, successData, successOpen]);
const removeAlert = (id: string) => { const removeAlert = (id: string) => {
setAlertsList((prevAlertsList) => setAlertsList((prevAlertsList) =>
@ -82,9 +88,7 @@ export default function App() {
<Header></Header> <Header></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 />
<ExtraSidebar /> <ExtraSidebar />
{/* Main area */} {/* Main area */}
<main className="min-w-0 flex-1 border-t border-gray-200 dark:border-gray-700 flex"> <main className="min-w-0 flex-1 border-t border-gray-200 dark:border-gray-700 flex">
{/* Primary column */} {/* Primary column */}

View file

@ -25,12 +25,6 @@ export default function ExtraSidebar() {
<span className="text-gray-900 dark:text-white text-lg ml-2 font-semibold"> <span className="text-gray-900 dark:text-white text-lg ml-2 font-semibold">
{extraNavigation.title} {extraNavigation.title}
</span> </span>
<button
className="text-gray-400 flex-shrink-0 inline-flex items-center justify-center rounded-lg"
onClick={() => setIsStackedOpen(false)}
>
<ChevronLeftIcon className="h-6 w-6"></ChevronLeftIcon>
</button>
</div> </div>
<div className="flex flex-grow flex-col w-full"> <div className="flex flex-grow flex-col w-full">
{extraNavigation.options ? ( {extraNavigation.options ? (

View file

@ -4,7 +4,6 @@ import {
MoonIcon, MoonIcon,
SunIcon, SunIcon,
} from '@heroicons/react/24/outline' } from '@heroicons/react/24/outline'
import Breadcrumb from '../breadcrumbComponent'
import { alertContext } from '../../contexts/alertContext' import { alertContext } from '../../contexts/alertContext'
import { useLayer } from 'react-laag' import { useLayer } from 'react-laag'
import AlertDropdown from '../../alerts/alertDropDown' import AlertDropdown from '../../alerts/alertDropDown'
@ -25,25 +24,8 @@ export default function Header(){
const {dark, setDark} = useContext(darkContext); const {dark, setDark} = useContext(darkContext);
return ( return (
<header className="relative flex h-16 w-full shrink-0 items-center bg-white dark:bg-gray-800"> <header className="relative flex h-16 w-full shrink-0 items-center bg-white dark:bg-gray-800">
{/* Logo area */}
<div className="static shrink-0">
<a
href="/"
className="flex h-16 items-center justify-center bg-indigo-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-600 w-20"
>
<img
className="h-8 w-auto"
src="https://tailwindui.com/img/logos/mark.svg?color=white"
alt="Your Company"
/>
</a>
</div>
{/* Desktop nav area */} {/* Desktop nav area */}
<div className="flex min-w-0 flex-1 items-center justify-between"> <div className="flex min-w-0 flex-1 flex-row-reverse items-center justify-between">
<div className="min-w-0 flex-1 ml-5">
<Breadcrumb />
</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 gap-6"> <span className="inline-flex gap-6">

View file

@ -1,66 +0,0 @@
import SidebarButton from "./sidebarButton";
import { BsPlusSquare } from "react-icons/bs";
import { classNames } from "../../utils";
import { ChevronRightIcon } from "@heroicons/react/24/outline";
import { useContext } from "react";
import { sidebarNavigation } from "../../entities/sidebarNav";
import { locationContext } from "../../contexts/locationContext";
export default function Sidebar() {
let { showSideBar, isStackedOpen, setIsStackedOpen } =
useContext(locationContext);
let current = false;
return (
<div
className={
(showSideBar ? "w-20" : "w-0") +
" h-full overflow-hidden flex-col transition-all duration-500"
}
>
<div className="w-20 h-full">
<nav
aria-label="Sidebar"
className="h-full overflow-y-auto bg-gray-800 dark:bg-gray-900"
>
<div className="flex flex-col h-full justify-between">
<div className="relative flex w-20 flex-col space-y-3 p-3">
{sidebarNavigation.map((item, index) => (
<SidebarButton item={item} key={index}></SidebarButton>
))}
</div>
<div className="relative flex w-20 flex-col items-center space-y-3 align-items: center;">
<button
key="New Project"
onClick={() => {
}}
className={classNames(
current
? "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"
)}
>
<span className="sr-only">"New Project"</span>
<BsPlusSquare className="h-8 w-8" aria-hidden="true" />
</button>
<div
className={` ${
isStackedOpen ? "h-0" : "h-12"
} overflow-hidden transition-all duration-500`}
>
<div className="h-10">
<button
className="text-gray-400 flex-shrink-0 inline-flex items-center justify-center rounded-lg"
onClick={() => setIsStackedOpen(true)}
>
<ChevronRightIcon className="h-6 w-6"></ChevronRightIcon>
</button>
</div>
</div>
</div>
</div>
</nav>
</div>
</div>
);
}

View file

@ -1,24 +0,0 @@
import { classNames } from "../../../utils"
import { Link } from "react-router-dom"
import { useContext } from "react"
import { locationContext } from "../../../contexts/locationContext";
import { sidebarNavigationItemType } from "../../../types/entities";
export default function SidebarButton({item}:{item:sidebarNavigationItemType}){
let {current}= useContext(locationContext);
return (
<>
<Link
key={item.name}
to={item.href}
className={classNames(
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'
)}
>
<span className="sr-only">{item.name}</span>
<item.icon className="h-6 w-6" aria-hidden="true" />
</Link>
</>
)
}

View file

@ -1,55 +0,0 @@
import { ChevronRightIcon, HomeIcon } from '@heroicons/react/20/solid'
import { useContext } from 'react';
import { Link } from 'react-router-dom'
import { locationContext } from '../../contexts/locationContext';
const breadcrumbNameMap: { [key: string]: string } = {
'/settings': 'Settings',
'/settings/general': 'General',
'/settings/general/main': 'Main',
'/table': 'Table',
};
function getPages(atual:string[]){
let pages = [];
for(let i = 2; i <= atual.length; i++){
let to = '/' + atual.slice(1,i).join('/');
pages.push({ name: breadcrumbNameMap[to], href: to })
}
return (pages);
}
export default function Breadcrumb(){
let {current} = useContext(locationContext);
return (
<div>
<nav className="flex ml-2" aria-label="Breadcrumb">
<ol className="flex items-center space-x-4">
<li>
<div>
<Link to='/' className="text-gray-400 hover:text-gray-500">
<HomeIcon className="h-5 w-5 flex-shrink-0" aria-hidden="true" />
<span className="sr-only">Home</span>
</Link>
</div>
</li>
{getPages(current).map((page) => (
<li key={page.href}>
<div className="flex items-center">
<ChevronRightIcon className="h-5 w-5 flex-shrink-0 text-gray-400" aria-hidden="true" />
<a
href={page.href}
className="ml-4 text-sm font-medium text-gray-500 hover:text-gray-700"
>
{page.name}
</a>
</div>
</li>
))}
</ol>
</nav>
</div>
)
}

View file

@ -105,14 +105,14 @@ export default function Chat({flow, reactFlowInstance }:ChatType) {
{chatHistory.map((c, i) => ( {chatHistory.map((c, i) => (
<div key={i}> <div key={i}>
{!c.isSend ? ( {!c.isSend ? (
<div className="w-full text-end"> <div className="w-full text-start">
<div style={{backgroundColor: nodeColors['chat']}} className="text-start inline-block text-white rounded-xl p-3 overflow-hidden w-fit max-w-[280px] px-5 text-sm font-normal rounded-tr-none"> <div style={{backgroundColor: nodeColors['chat']}} className="text-start inline-block text-white rounded-xl p-3 overflow-hidden w-fit max-w-[280px] px-5 text-sm font-normal rounded-tl-none">
{c.message} {c.message}
</div> </div>
</div> </div>
) : ( ) : (
<div className="w-full text-start"> <div className="w-full text-end">
<div className="text-start inline-block rounded-xl p-3 overflow-hidden w-fit max-w-[280px] px-5 text-sm text-black dark:text-white dark:bg-gray-700 bg-gray-200 font-normal rounded-tl-none"> <div className="text-start inline-block rounded-xl p-3 overflow-hidden w-fit max-w-[280px] px-5 text-sm text-black dark:text-white dark:bg-gray-700 bg-gray-200 font-normal rounded-tr-none">
{c.message} {c.message}
</div> </div>
</div> </div>

View file

@ -73,52 +73,68 @@ export function AlertProvider({ children }:{children:ReactNode}) {
return newNotificationList; return newNotificationList;
}); });
}; };
function setErrorData(newState: { title: string; list?: Array<string> }) { /**
setErrorDataState(newState); * Sets the error data state, opens the error dialog and pushes the new error notification to the notification list
setErrorOpen(true); * @param newState An object containing the new error data, including title and optional list of error messages
if (newState.title) { */
setNotificationCenter(true); function setErrorData(newState: { title: string; list?: Array<string> }) {
pushNotificationList({ setErrorDataState(newState);
type: "error", setErrorOpen(true);
title: newState.title, if (newState.title) {
list: newState.list, setNotificationCenter(true);
id: _.uniqueId(), pushNotificationList({
}); type: "error",
} title: newState.title,
list: newState.list,
id: _.uniqueId(),
});
} }
function setNoticeData(newState: { title: string; link?: string }) { }
setNoticeDataState(newState); /**
setNoticeOpen(true); * Sets the state of the notice data and opens the notice modal, also adds a new notice to the notification center if the title is defined.
if (newState.title) { * @param newState An object containing the title of the notice and optionally a link.
setNotificationCenter(true); */
pushNotificationList({ function setNoticeData(newState: { title: string; link?: string }) {
type: "notice", setNoticeDataState(newState);
title: newState.title, setNoticeOpen(true);
link: newState.link, if (newState.title) {
id: _.uniqueId(), // Add new notice to notification center
}); setNotificationCenter(true);
} pushNotificationList({
type: "notice",
title: newState.title,
link: newState.link,
id: _.uniqueId(),
});
} }
function setSuccessData(newState: { title: string }) { }
setSuccessDataState(newState); /**
setSuccessOpen(true); * Update the success data state and show a success alert notification.
if (newState.title) { * @param newState - A state object with a "title" property to set in the success data state.
setNotificationCenter(true); */
pushNotificationList({ function setSuccessData(newState: { title: string }) {
type: "success", setSuccessDataState(newState); // update the success data state with the provided new state
title: newState.title, setSuccessOpen(true); // open the success alert
id: _.uniqueId(),
}); // If the new state has a "title" property, add a new success notification to the list
} if (newState.title) {
setNotificationCenter(true); // show the notification center
pushNotificationList({ // add the new notification to the list
type: "success",
title: newState.title,
id: _.uniqueId(),
});
} }
}
function clearNotificationList() { function clearNotificationList() {
setNotificationList([]); setNotificationList([]);
} }
function removeFromNotificationList(index: string) { function removeFromNotificationList(index: string) {
// set the notification list to a new array that filters out the alert with the matching id
setNotificationList((prevAlertsList) => setNotificationList((prevAlertsList) =>
prevAlertsList.filter((alert) => alert.id !== index) prevAlertsList.filter((alert) => alert.id !== index)
); );
} }
return ( return (
<alertContext.Provider <alertContext.Provider
value={{ value={{

View file

@ -1,10 +1,8 @@
import { createContext, useEffect, useState, useRef, ReactNode } from "react"; import { createContext, useEffect, useState, useRef, ReactNode } from "react";
import {FlowType} from "../types/flow" import { FlowType } from "../types/flow";
import { TabsContextType } from "../types/tabs"; import { TabsContextType } from "../types/tabs";
const TabsContextInitialValue: TabsContextType = {
const TabsContextInitialValue:TabsContextType = {
tabIndex: 0, tabIndex: 0,
setTabIndex: (index: number) => {}, setTabIndex: (index: number) => {},
flows: [], flows: [],
@ -20,7 +18,7 @@ export const TabsContext = createContext<TabsContextType>(
TabsContextInitialValue TabsContextInitialValue
); );
export function TabsProvider({ children }:{children:ReactNode}) { export function TabsProvider({ children }: { children: ReactNode }) {
const [tabIndex, setTabIndex] = useState(0); const [tabIndex, setTabIndex] = useState(0);
const [flows, setFlows] = useState<Array<FlowType>>([]); const [flows, setFlows] = useState<Array<FlowType>>([]);
const [id, setId] = useState(0); const [id, setId] = useState(0);
@ -31,6 +29,7 @@ export function TabsProvider({ children }:{children:ReactNode}) {
return newNodeId.current; return newNodeId.current;
} }
useEffect(() => { useEffect(() => {
//save tabs locally
if (flows.length !== 0) if (flows.length !== 0)
window.localStorage.setItem( window.localStorage.setItem(
"tabsData", "tabsData",
@ -39,6 +38,7 @@ export function TabsProvider({ children }:{children:ReactNode}) {
}, [flows, id, tabIndex, newNodeId]); }, [flows, id, tabIndex, newNodeId]);
useEffect(() => { useEffect(() => {
//get tabs locally saved
let cookie = window.localStorage.getItem("tabsData"); let cookie = window.localStorage.getItem("tabsData");
if (cookie) { if (cookie) {
let cookieObject = JSON.parse(cookie); let cookieObject = JSON.parse(cookie);
@ -49,27 +49,47 @@ export function TabsProvider({ children }:{children:ReactNode}) {
} }
}, []); }, []);
/**
* Downloads the current flow as a JSON file
*/
function downloadFlow() { function downloadFlow() {
// create a data URI with the current flow data
const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent( const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
JSON.stringify(flows[tabIndex]) JSON.stringify(flows[tabIndex])
)}`; )}`;
// create a link element and set its properties
const link = document.createElement("a"); const link = document.createElement("a");
link.href = jsonString; link.href = jsonString;
link.download = `${flows[tabIndex].name}.json`; link.download = `${flows[tabIndex].name}.json`;
// simulate a click on the link element to trigger the download
link.click(); link.click();
} }
/**
* Creates a file input and listens to a change event to upload a JSON flow file.
* If the file type is application/json, the file is read and parsed into a JSON object.
* The resulting JSON object is passed to the addFlow function.
*/
function uploadFlow() { function uploadFlow() {
// create a file input
const input = document.createElement("input"); const input = document.createElement("input");
input.type = "file"; input.type = "file";
// add a change event listener to the file input
input.onchange = (e: Event) => { input.onchange = (e: Event) => {
// check if the file type is application/json
if ((e.target as HTMLInputElement).files[0].type === "application/json") { if ((e.target as HTMLInputElement).files[0].type === "application/json") {
// get the file from the file input
const file = (e.target as HTMLInputElement).files[0]; const file = (e.target as HTMLInputElement).files[0];
// read the file as text
file.text().then((text) => { file.text().then((text) => {
// parse the text into a JSON object
addFlow(JSON.parse(text)); addFlow(JSON.parse(text));
}); });
} }
}; };
// trigger the file input click event to open the file dialog
input.click(); input.click();
} }
/** /**
@ -94,21 +114,38 @@ export function TabsProvider({ children }:{children:ReactNode}) {
return newFlows; return newFlows;
}); });
} }
/**
* Add a new flow to the list of flows.
* @param flow Optional flow to add.
*/
function addFlow(flow?: FlowType) { function addFlow(flow?: FlowType) {
// Get data from the flow or set it to null if there's no flow provided.
const data = flow?.data ? flow.data : null; const data = flow?.data ? flow.data : null;
// Create a new flow with a default name if no flow is provided.
let newFlow: FlowType = { let newFlow: FlowType = {
name: flow ? flow.name : "flow" + id, name: flow ? flow.name : "flow" + id,
id: id.toString(), id: id.toString(),
data, data,
chat: flow ? flow.chat : [], chat: flow ? flow.chat : [],
}; };
// Increment the ID counter.
setId((old) => old + 1); setId((old) => old + 1);
// Add the new flow to the list of flows.
setFlows((prevState) => { setFlows((prevState) => {
const newFlows = [...prevState, newFlow]; const newFlows = [...prevState, newFlow];
return newFlows; return newFlows;
}); });
// Set the tab index to the new flow.
setTabIndex(flows.length); setTabIndex(flows.length);
} }
/**
* Updates an existing flow with new data
* @param newFlow - The new flow object containing the updated data
*/
function updateFlow(newFlow: FlowType) { function updateFlow(newFlow: FlowType) {
setFlows((prevState) => { setFlows((prevState) => {
const newFlows = [...prevState]; const newFlows = [...prevState];

View file

@ -1,4 +0,0 @@
export enum apiEnum
{
PromptTemplate="PromptTemplate"
}

View file

@ -1,13 +0,0 @@
import {
HomeIcon,
} from '@heroicons/react/24/outline'
export const sidebarNavigation = [
{ name: 'Home', href: '/', icon: HomeIcon, current: true },
// { name: 'Table', href: '/table/', icon: TableCellsIcon, current: false },
// //{ name: 'Train', href: '#', icon: BoltIcon, current: false },
// //{ name: 'Model Details', href: '#', icon: LightBulbIcon, current: false },
// //{ name: 'Deploy', href: '#', icon: RocketLaunchIcon, current: false },
// { name: 'Settings', href: '/settings/', icon: Cog6ToothIcon, current: false },
]

View file

@ -16,19 +16,26 @@ export default function ExtraSidebar() {
useEffect(() => { useEffect(() => {
async function getTypes():Promise<void>{ async function getTypes():Promise<void>{
// Define an object with initial values for the types.
const initialValue:{[char: string]: string} = { const initialValue:{[char: string]: string} = {
str: "advanced", str: "advanced",
bool: "advanced", bool: "advanced",
chatOutput: "chat", chatOutput: "chat",
chatInput: "chat",
} }
// Make an asynchronous API call to retrieve all data.
let result = await getAll(); let result = await getAll();
// Update the state of the component with the retrieved data.
setData(result.data); setData(result.data);
// Set the types by reducing over the keys of the result data and updating the accumulator.
setTypes( setTypes(
Object.keys(result.data).reduce( Object.keys(result.data).reduce(
(acc, curr) => { (acc, curr) => {
Object.keys(result.data[curr]).forEach((c:keyof APIKindType) => { Object.keys(result.data[curr]).forEach((c:keyof APIKindType) => {
acc[c] = curr; acc[c] = curr;
// Add the base classes to the accumulator as well.
result.data[curr][c].base_classes?.forEach((b) => { result.data[curr][c].base_classes?.forEach((b) => {
acc[b] = curr; acc[b] = curr;
}); });
@ -39,11 +46,13 @@ export default function ExtraSidebar() {
) )
); );
} }
// Call the getTypes function.
getTypes(); getTypes();
}, [setTypes]); }, [setTypes]);
function onDragStart(event: React.DragEvent<any>, data:{type:string,node?:APIClassType}) { function onDragStart(event: React.DragEvent<any>, data:{type:string,node?:APIClassType}) {
//start drag event
event.dataTransfer.effectAllowed = "move"; event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData("json", JSON.stringify(data)); event.dataTransfer.setData("json", JSON.stringify(data));
} }

View file

@ -58,10 +58,6 @@ export default function TabComponent({ selected, flow, onClick }:{flow:FlowType,
> >
{flow.name} {flow.name}
</span> </span>
{/* <ArrowDownTrayIcon
onClick={() => downloadFlow()}
className="w-4 h-4 hover:text-blue-500 cursor-pointer"
/> */}
</div> </div>
)} )}
<button <button

View file

@ -7,6 +7,7 @@ import FlowPage from "../..";
export default function TabsManagerComponent() { export default function TabsManagerComponent() {
const { flows, addFlow, tabIndex, setTabIndex } = useContext(TabsContext); const { flows, addFlow, tabIndex, setTabIndex } = useContext(TabsContext);
useEffect(() => { useEffect(() => {
//create the first flow
if (flows.length === 0) { if (flows.length === 0) {
addFlow(); addFlow();
} }

View file

@ -48,7 +48,6 @@ export default function FlowPage({ flow }:{flow:FlowType}) {
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 ?? []
); );
@ -64,19 +63,18 @@ export default function FlowPage({ flow }:{flow:FlowType}) {
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [nodes, edges]); }, [nodes, edges]);
//update flow when tabs change
useEffect(() => { useEffect(() => {
console.log('oi')
setNodes(flow?.data?.nodes ?? []); setNodes(flow?.data?.nodes ?? []);
setEdges(flow?.data?.edges ?? []); setEdges(flow?.data?.edges ?? []);
if (reactFlowInstance) { if (reactFlowInstance) {
setViewport(flow?.data?.viewport ?? { x: 1, y: 0, zoom: 1 }); setViewport(flow?.data?.viewport ?? { x: 1, y: 0, zoom: 1 });
} }
}, [flow, reactFlowInstance, setEdges, setNodes, setViewport]); }, [flow, reactFlowInstance, setEdges, setNodes, setViewport]);
//set extra sidebar
useEffect(() => { useEffect(() => {
setExtraComponent(<ExtraSidebar />); setExtraComponent(<ExtraSidebar />);
setExtraNavigation({ title: "Nodes" }); setExtraNavigation({ title: "Componets" });
}, [setExtraComponent, setExtraNavigation]); }, [setExtraComponent, setExtraNavigation]);
const onEdgesChangeMod = useCallback( const onEdgesChangeMod = useCallback(
@ -111,24 +109,34 @@ export default function FlowPage({ flow }:{flow:FlowType}) {
const onDrop = useCallback( const onDrop = useCallback(
(event:React.DragEvent) => { (event:React.DragEvent) => {
event.preventDefault(); event.preventDefault();
// Helper function to generate a unique node ID
function getId() { function getId() {
return `dndnode_` + incrementNodeId(); return `dndnode_` + incrementNodeId();
} }
// Get the current bounds of the ReactFlow wrapper element
const reactflowBounds = reactFlowWrapper.current.getBoundingClientRect(); const reactflowBounds = reactFlowWrapper.current.getBoundingClientRect();
// Extract the data from the drag event and parse it as a JSON object
let data:{type:string,node?:APIClassType} = JSON.parse(event.dataTransfer.getData("json")); let data:{type:string,node?:APIClassType} = JSON.parse(event.dataTransfer.getData("json"));
// If data type is not "chatInput" or if there are no "chatInputNode" nodes present in the ReactFlow instance, create a new node
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"))
) { ) {
// Calculate the position where the node should be created
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,
}); });
// Generate a unique node ID
let newId = getId(); let newId = getId();
// Create a new node object
const newNode:NodeType = { const newNode:NodeType = {
id: newId, id: newId,
type: type:
@ -146,16 +154,21 @@ export default function FlowPage({ flow }:{flow:FlowType}) {
value: null, value: null,
}, },
}; };
// Add the new node to the list of nodes in state
setNodes((nds) => nds.concat(newNode)); setNodes((nds) => nds.concat(newNode));
} else { } else {
// If a chat input node already exists, set an error message
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."],
}); });
} }
}, },
// Specify dependencies for useCallback
[incrementNodeId, reactFlowInstance, setErrorData, setNodes] [incrementNodeId, reactFlowInstance, setErrorData, setNodes]
); );
return ( return (
<div className="w-full h-full" ref={reactFlowWrapper}> <div className="w-full h-full" ref={reactFlowWrapper}>