Changed project structure, added sidebar and alerts
This commit is contained in:
parent
39643dba22
commit
8ace199118
20 changed files with 1346 additions and 0 deletions
|
|
@ -0,0 +1,133 @@
|
|||
import { XCircleIcon, XMarkIcon, InformationCircleIcon, CheckCircleIcon } from "@heroicons/react/24/outline";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Transition } from "@headlessui/react";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function SingleAlert({ dropItem, removeAlert }) {
|
||||
const [show, setShow] = useState(true);
|
||||
const type = dropItem.type;
|
||||
|
||||
return (
|
||||
<Transition
|
||||
className="relative"
|
||||
show={show}
|
||||
appear={true}
|
||||
enter="transition-transform duration-500 ease-out"
|
||||
enterFrom={"transform translate-x-[-100%]"}
|
||||
enterTo={"transform translate-x-0"}
|
||||
leave="transition-transform duration-500 ease-in"
|
||||
leaveFrom={"transform translate-x-0"}
|
||||
leaveTo={"transform translate-x-[-100%]"}
|
||||
>
|
||||
{type === "error"?
|
||||
<div className="flex bg-red-50 rounded-md p-4 mb-2" key={dropItem.index}>
|
||||
<div className="flex-shrink-0">
|
||||
<XCircleIcon
|
||||
className="h-5 w-5 text-red-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<h3 className="text-sm font-medium text-red-800">
|
||||
{dropItem.title}
|
||||
</h3>
|
||||
{dropItem.list ? (
|
||||
<div className="mt-2 text-sm text-red-700">
|
||||
<ul className="list-disc space-y-1 pl-5">
|
||||
{dropItem.list.map((item, idx) => (
|
||||
<li key={idx}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-auto pl-3">
|
||||
<div className="-mx-1.5 -my-1.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setShow(false); setTimeout(() => {removeAlert(dropItem.index);}, 500);
|
||||
}}
|
||||
className="inline-flex rounded-md bg-red-50 p-1.5 text-red-500"
|
||||
>
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<XMarkIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
:(type === "notice" ?
|
||||
<div className="flex rounded-md p-4 bg-blue-50 mb-2" key={dropItem.index}>
|
||||
<div className="flex-shrink-0">
|
||||
<InformationCircleIcon
|
||||
className="h-5 w-5 text-blue-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3 flex-1 md:flex md:justify-between">
|
||||
<p className="text-sm text-blue-700">{dropItem.title}</p>
|
||||
<p className="mt-3 text-sm md:mt-0 md:ml-6">
|
||||
{dropItem.link ? (
|
||||
<Link
|
||||
to={dropItem.link}
|
||||
className="whitespace-nowrap font-medium text-blue-700 hover:text-blue-600"
|
||||
>
|
||||
Details
|
||||
</Link>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="ml-auto pl-3">
|
||||
<div className="-mx-1.5 -my-1.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setShow(false); setTimeout(() => {removeAlert(dropItem.index);}, 500);
|
||||
}}
|
||||
className="inline-flex rounded-md bg-blue-50 p-1.5 text-blue-500"
|
||||
>
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<XMarkIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
:
|
||||
<div className="flex bg-green-50 p-4 mb-2" key={dropItem.index}>
|
||||
<div className="flex-shrink-0">
|
||||
<CheckCircleIcon
|
||||
className="h-5 w-5 text-green-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<p className="text-sm font-medium text-green-800">
|
||||
{dropItem.title}
|
||||
</p>
|
||||
</div>
|
||||
<div className="ml-auto pl-3">
|
||||
<div className="-mx-1.5 -my-1.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setShow(false); setTimeout(() => {removeAlert(dropItem.index);}, 500);
|
||||
}}
|
||||
className="inline-flex rounded-md bg-green-50 p-1.5 text-green-500"
|
||||
>
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<XMarkIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
</Transition>
|
||||
)
|
||||
|
||||
}
|
||||
78
space_flow/src/alerts/alertDropDown/index.tsx
Normal file
78
space_flow/src/alerts/alertDropDown/index.tsx
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import { useContext } from "react";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
InformationCircleIcon,
|
||||
XCircleIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Transition } from "@headlessui/react";
|
||||
import { TrashIcon } from "@heroicons/react/24/outline";
|
||||
import SingleAlert from "./components/singleAlertComponent";
|
||||
|
||||
type AlertDropdownProps = {
|
||||
closeFunction: () => void;
|
||||
open?: boolean;
|
||||
};
|
||||
|
||||
export type alertDropdownItem = {
|
||||
type: "notice" | "error" | "success";
|
||||
title: string;
|
||||
link?: string;
|
||||
list?: Array<string>;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export default function AlertDropdown({closeFunction, open}: AlertDropdownProps) {
|
||||
const {
|
||||
notificationList,
|
||||
clearNotificationList,
|
||||
removeFromNotificationList,
|
||||
} = useContext(alertContext);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Transition
|
||||
show={open}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<div className="z-10 px-8 py-6 pb-8 rounded-md bg-white ring-1 ring-black ring-opacity-5 shadow-lg focus:outline-none overflow-hidden w-[36rem] h-[40rem] flex flex-col">
|
||||
<div className="flex flex-row justify-between text-md font-medium text-gray-800">
|
||||
Notifications
|
||||
<div className="flex gap-4">
|
||||
<button
|
||||
className="hover:text-black"
|
||||
onClick={() => {closeFunction(); setTimeout(clearNotificationList, 100)}}
|
||||
>
|
||||
<TrashIcon className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
className="hover:text-black"
|
||||
onClick={closeFunction}
|
||||
>
|
||||
<XMarkIcon className="h-6 w-6" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-6 flex flex-col overflow-y-scroll w-full h-full scrollbar-hide">
|
||||
{notificationList.length !== 0 ?
|
||||
notificationList.map((alertItem, index) => (
|
||||
<SingleAlert key={index} dropItem={alertItem} removeAlert={removeFromNotificationList} />
|
||||
))
|
||||
:
|
||||
<div className="h-full w-full pb-16 text-slate-500 flex justify-center items-center">
|
||||
No new notifications
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
18
space_flow/src/alerts/error/errorClass.ts
Normal file
18
space_flow/src/alerts/error/errorClass.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
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"
|
||||
}
|
||||
}
|
||||
63
space_flow/src/alerts/error/index.tsx
Normal file
63
space_flow/src/alerts/error/index.tsx
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import { Transition } from "@headlessui/react";
|
||||
import { XCircleIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function ErrorAlert({ title, list = [], id, removeAlert }) {
|
||||
const [show, setShow] = useState(true);
|
||||
useEffect(() => {
|
||||
if(show){
|
||||
setTimeout(() => {
|
||||
setShow(false); setTimeout(() => {removeAlert(id);}, 500);
|
||||
}, 5000);
|
||||
}
|
||||
}, [id, removeAlert, show]);
|
||||
return (
|
||||
<Transition
|
||||
className="relative"
|
||||
show={show}
|
||||
appear={true}
|
||||
enter="transition-transform duration-500 ease-out"
|
||||
enterFrom={"transform translate-x-[-100%]"}
|
||||
enterTo={"transform translate-x-0"}
|
||||
leave="transition-transform duration-500 ease-in"
|
||||
leaveFrom={"transform translate-x-0"}
|
||||
leaveTo={"transform translate-x-[-100%]"}
|
||||
>
|
||||
<div className="rounded-md w-96 mt-6 shadow-xl bg-red-50 p-4">
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<XCircleIcon className="h-5 w-5 text-red-400" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<h3 className="text-sm font-medium text-red-800">{title}</h3>
|
||||
{list.length !== 0
|
||||
?
|
||||
<div className="mt-2 text-sm text-red-700">
|
||||
<ul className="list-disc space-y-1 pl-5">
|
||||
{list.map((item, index) => (
|
||||
<li key={index} >{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
:
|
||||
<></>
|
||||
}
|
||||
|
||||
</div>
|
||||
<div className="ml-auto pl-3">
|
||||
<div className="-mx-1.5 -my-1.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={()=>{setShow(false); setTimeout(() => {removeAlert(id);}, 500);}}
|
||||
className="inline-flex rounded-md bg-red-50 p-1.5 text-red-500"
|
||||
>
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<XMarkIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
65
space_flow/src/alerts/notice/index.tsx
Normal file
65
space_flow/src/alerts/notice/index.tsx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { Transition } from "@headlessui/react";
|
||||
import { InformationCircleIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export default function NoticeAlert({ title, link = "", id, removeAlert }) {
|
||||
const [show, setShow] = useState(true);
|
||||
useEffect(() => {
|
||||
if(show){
|
||||
setTimeout(() => {
|
||||
setShow(false); setTimeout(() => {removeAlert(id);}, 500);
|
||||
}, 5000);
|
||||
}
|
||||
}, [id, removeAlert, show]);
|
||||
return (
|
||||
<Transition
|
||||
show={show}
|
||||
enter="transition-transform duration-500 ease-out"
|
||||
enterFrom={"transform translate-x-[-100%]"}
|
||||
enterTo={"transform translate-x-0"}
|
||||
leave="transition-transform duration-500 ease-in"
|
||||
leaveFrom={"transform translate-x-0"}
|
||||
leaveTo={"transform translate-x-[-100%]"}
|
||||
>
|
||||
<div className="rounded-md w-96 mt-6 shadow-xl bg-blue-50 p-4">
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<InformationCircleIcon
|
||||
className="h-5 w-5 text-blue-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3 flex-1 md:flex md:justify-between">
|
||||
<p className="text-sm text-blue-700">{title}</p>
|
||||
<p className="mt-3 text-sm md:mt-0 md:ml-6">
|
||||
{link !== ""
|
||||
?
|
||||
<Link
|
||||
to={link}
|
||||
className="whitespace-nowrap font-medium text-blue-700 hover:text-blue-600"
|
||||
>
|
||||
Details
|
||||
</Link>
|
||||
:
|
||||
<></>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<div className="ml-auto pl-3">
|
||||
<div className="-mx-1.5 -my-1.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={()=>{setShow(false); removeAlert(id);}}
|
||||
className="inline-flex rounded-md bg-blue-50 p-1.5 text-blue-500"
|
||||
>
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<XMarkIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
51
space_flow/src/alerts/success/index.tsx
Normal file
51
space_flow/src/alerts/success/index.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { Transition } from "@headlessui/react";
|
||||
import { CheckCircleIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function SuccessAlert({ title, id, removeAlert }) {
|
||||
const [show, setShow] = useState(true);
|
||||
useEffect(() => {
|
||||
if(show){
|
||||
setTimeout(() => {
|
||||
setShow(false); setTimeout(() => {removeAlert(id);}, 500);
|
||||
}, 5000);
|
||||
}
|
||||
}, [id, removeAlert, show]);
|
||||
return (
|
||||
<Transition
|
||||
show={show}
|
||||
enter="transition-transform duration-500 ease-out"
|
||||
enterFrom={"transform translate-x-[-100%]"}
|
||||
enterTo={"transform translate-x-0"}
|
||||
leave="transition-transform duration-500 ease-in"
|
||||
leaveFrom={"transform translate-x-0"}
|
||||
leaveTo={"transform translate-x-[-100%]"}
|
||||
>
|
||||
<div className="rounded-md w-96 mt-6 shadow-xl bg-green-50 p-4">
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<CheckCircleIcon
|
||||
className="h-5 w-5 text-green-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<p className="text-sm font-medium text-green-800">{title}</p>
|
||||
</div>
|
||||
<div className="ml-auto pl-3">
|
||||
<div className="-mx-1.5 -my-1.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={()=>{setShow(false); removeAlert(id);}}
|
||||
className="inline-flex rounded-md bg-green-50 p-1.5 text-green-500"
|
||||
>
|
||||
<span className="sr-only">Dismiss</span>
|
||||
<XMarkIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
130
space_flow/src/components/ExtraSidebarComponent/index.tsx
Normal file
130
space_flow/src/components/ExtraSidebarComponent/index.tsx
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import { Disclosure } from "@headlessui/react";
|
||||
import { ChevronLeftIcon } from "@heroicons/react/24/outline";
|
||||
import { useContext } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { classNames } from "../../utils";
|
||||
import { locationContext } from "../../contexts/locationContext";
|
||||
|
||||
export default function ExtraSidebar() {
|
||||
const {
|
||||
atual,
|
||||
isStackedOpen,
|
||||
setIsStackedOpen,
|
||||
extraNavigation,
|
||||
extraComponent,
|
||||
} = useContext(locationContext);
|
||||
return (
|
||||
<>
|
||||
<aside
|
||||
className={` ${
|
||||
isStackedOpen ? "w-60" : "w-0 "
|
||||
} flex-shrink-0 flex overflow-hidden flex-col border-r transition-all duration-500`}
|
||||
>
|
||||
<div className="w-60 overflow-hidden scrollbar-hide h-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">
|
||||
{extraNavigation.title}
|
||||
</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 className="flex flex-grow flex-col">
|
||||
{extraNavigation.options ? (
|
||||
<div className="p-4">
|
||||
<nav className="flex-1 space-y-1">
|
||||
{extraNavigation.options.map((item) =>
|
||||
!item.children ? (
|
||||
<div key={item.name}>
|
||||
<Link
|
||||
to={item.href}
|
||||
className={classNames(
|
||||
item.href.split("/")[2] === atual[4]
|
||||
? "bg-gray-100 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"
|
||||
)}
|
||||
>
|
||||
<item.icon
|
||||
className={classNames(
|
||||
item.href.split("/")[2] === atual[4]
|
||||
? "text-gray-500"
|
||||
: "text-gray-400 group-hover:text-gray-500",
|
||||
"mr-3 flex-shrink-0 h-6 w-6"
|
||||
)}
|
||||
/>
|
||||
{item.name}
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<Disclosure
|
||||
as="div"
|
||||
key={item.name}
|
||||
className="space-y-1"
|
||||
>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button
|
||||
className={classNames(
|
||||
item.href.split("/")[2] === atual[4]
|
||||
? "bg-gray-100 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"
|
||||
)}
|
||||
>
|
||||
<item.icon
|
||||
className="mr-3 h-6 w-6 flex-shrink-0 text-gray-400 group-hover:text-gray-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="flex-1">{item.name}</span>
|
||||
<svg
|
||||
className={classNames(
|
||||
open
|
||||
? "text-gray-400 rotate-90"
|
||||
: "text-gray-300",
|
||||
"ml-3 h-5 w-5 flex-shrink-0 transition-rotate duration-150 ease-in-out group-hover:text-gray-400"
|
||||
)}
|
||||
viewBox="0 0 20 20"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M6 6L14 10L6 14V6Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</Disclosure.Button>
|
||||
<Disclosure.Panel className="space-y-1">
|
||||
{item.children.map((subItem) => (
|
||||
<Link
|
||||
key={subItem.name}
|
||||
to={subItem.href}
|
||||
className={classNames(
|
||||
subItem.href.split("/")[3] === atual[5]
|
||||
? "bg-gray-100 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"
|
||||
)}
|
||||
>
|
||||
{subItem.name}
|
||||
</Link>
|
||||
))}
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
)
|
||||
)}
|
||||
</nav>
|
||||
</div>
|
||||
) : (
|
||||
<>{extraComponent}</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</>
|
||||
);
|
||||
}
|
||||
109
space_flow/src/components/HeaderComponent/index.tsx
Normal file
109
space_flow/src/components/HeaderComponent/index.tsx
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import { Fragment, useContext, useState } from 'react'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { MagnifyingGlassIcon } from '@heroicons/react/20/solid'
|
||||
import {
|
||||
BellIcon,
|
||||
MoonIcon,
|
||||
} from '@heroicons/react/24/outline'
|
||||
import { classNames } from '../../utils'
|
||||
import { Link } from 'react-router-dom'
|
||||
import Breadcrumb from '../breadcrumbComponent'
|
||||
import { alertContext } from '../../contexts/alertContext'
|
||||
import { useLayer,Arrow } from 'react-laag'
|
||||
import AlertDropdown from '../../alerts/alertDropDown'
|
||||
|
||||
export default function Header({user, userNavigation}){
|
||||
const {notificationCenter, setNotificationCenter} = useContext(alertContext)
|
||||
const [isOpen,setIsOpen] = useState(false)
|
||||
const {layerProps,renderLayer, triggerProps} = useLayer({
|
||||
isOpen,
|
||||
placement: "left-start",
|
||||
onOutsideClick:()=>setIsOpen(false),
|
||||
preferX: "left",
|
||||
triggerOffset: 10,
|
||||
containerOffset: 12,
|
||||
arrowOffset: 4,
|
||||
})
|
||||
return (
|
||||
<header className="relative flex h-16 w-full shrink-0 items-center bg-white">
|
||||
{/* 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 */}
|
||||
<div className="flex min-w-0 flex-1 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="flex items-center space-x-8">
|
||||
<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)}}>
|
||||
<span className="sr-only">View notifications</span>
|
||||
{notificationCenter&&<div className='absolute top-[2px] w-2 h-2 rounded-full bg-red-600 right-[7px]'></div>}
|
||||
|
||||
<BellIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>{renderLayer(<div {...layerProps}><AlertDropdown closeFunction={()=>setIsOpen(false)} open={isOpen}></AlertDropdown></div>)}
|
||||
</span>
|
||||
|
||||
<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">
|
||||
<span className="sr-only">Open user menu</span>
|
||||
<img className="h-8 w-8 rounded-full" src={user.imageUrl} alt="" />
|
||||
</Menu.Button>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||
<div className="py-1">
|
||||
{userNavigation.map((item,index) => (
|
||||
<Menu.Item key={index}>
|
||||
{({ active }) => (
|
||||
!item.href.includes("http://")?
|
||||
<Link
|
||||
to={item.href}
|
||||
className={classNames(
|
||||
active ? 'bg-gray-100' : '',
|
||||
'block px-4 py-2 text-sm text-gray-700'
|
||||
)}
|
||||
>
|
||||
{item.name}
|
||||
</Link>:
|
||||
<a
|
||||
href={item.href}
|
||||
className={classNames(
|
||||
active ? 'bg-gray-100' : '',
|
||||
'block px-4 py-2 text-sm text-gray-700'
|
||||
)}>
|
||||
{item.name}
|
||||
</a>)}
|
||||
</Menu.Item>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
68
space_flow/src/components/SidebarComponent/index.tsx
Normal file
68
space_flow/src/components/SidebarComponent/index.tsx
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import SidebarButton from "./sidebarButton";
|
||||
import { BsPlusSquare } from "react-icons/bs";
|
||||
import { classNames } from "../../utils";
|
||||
import { ChevronRightIcon } from "@heroicons/react/24/outline";
|
||||
import { useContext, useState } from "react";
|
||||
import { sidebarNavigation } from "../../entities/sidebarNav";
|
||||
import { locationContext } from "../../contexts/locationContext";
|
||||
|
||||
export default function Sidebar() {
|
||||
let { showSideBar, isStackedOpen, setIsStackedOpen } =
|
||||
useContext(locationContext);
|
||||
const [newProjectOpen, setNewProjectOpen] = useState(false);
|
||||
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"
|
||||
>
|
||||
<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={() => {
|
||||
setNewProjectOpen(true);
|
||||
}}
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { classNames } from "../../../utils"
|
||||
import { Link } from "react-router-dom"
|
||||
import { useContext } from "react"
|
||||
import { locationContext } from "../../../contexts/locationContext";
|
||||
|
||||
export default function SidebarButton({item}){
|
||||
let {atual}= useContext(locationContext);
|
||||
return (
|
||||
<>
|
||||
<Link
|
||||
key={item.name}
|
||||
to={item.href}
|
||||
className={classNames(
|
||||
item.href.split("/")[1]=== atual[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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
55
space_flow/src/components/breadcrumbComponent/index.tsx
Normal file
55
space_flow/src/components/breadcrumbComponent/index.tsx
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
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){
|
||||
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 {atual} = 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(atual).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>
|
||||
)
|
||||
}
|
||||
16
space_flow/src/components/loadingComponent/index.tsx
Normal file
16
space_flow/src/components/loadingComponent/index.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
type LoadingComponentProps={
|
||||
remSize:number
|
||||
}
|
||||
|
||||
|
||||
export default function LoadingComponent({remSize}:LoadingComponentProps){
|
||||
return(
|
||||
<div role="status" className="w-min m-auto">
|
||||
<svg aria-hidden="true" className={`w-${remSize} h-${remSize} mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600`} viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
|
||||
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
|
||||
</svg>
|
||||
<span className="animate-pulse text-blue-600 text-lg">Loading...</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
103
space_flow/src/contexts/alertContext.tsx
Normal file
103
space_flow/src/contexts/alertContext.tsx
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import { createContext, useState } from "react";
|
||||
import { alertDropdownItem } from "../alerts/alertDropDown";
|
||||
|
||||
var _ = require("lodash");
|
||||
|
||||
type alertContextType=
|
||||
{
|
||||
errorData: {title: string, list?: Array<string>};
|
||||
setErrorData:(newState:{title: string, list?: Array<string>})=>void;
|
||||
errorOpen: boolean;
|
||||
setErrorOpen:(newState:boolean)=>void;
|
||||
noticeData: {title: string, link?: string};
|
||||
setNoticeData:(newState:{title: string, link?: string})=>void;
|
||||
noticeOpen: boolean;
|
||||
setNoticeOpen:(newState:boolean)=>void;
|
||||
successData: {title: string};
|
||||
setSuccessData:(newState:{title: string})=>void;
|
||||
successOpen: boolean;
|
||||
setSuccessOpen:(newState:boolean)=>void;
|
||||
notificationCenter:boolean
|
||||
setNotificationCenter:(newState:boolean)=>void;
|
||||
notificationList:Array<alertDropdownItem>;
|
||||
pushNotificationList:(Object)=>void
|
||||
clearNotificationList:()=>void,
|
||||
removeFromNotificationList:(index:number)=>void
|
||||
}
|
||||
|
||||
const initialValue= {
|
||||
errorData: {title:"", list:[]},
|
||||
setErrorData:()=>{},
|
||||
errorOpen: false,
|
||||
setErrorOpen:()=>{},
|
||||
noticeData: {title:"", link:""},
|
||||
setNoticeData:()=>{},
|
||||
noticeOpen: false,
|
||||
setNoticeOpen:()=>{},
|
||||
successData: {title:""},
|
||||
setSuccessData:()=>{},
|
||||
successOpen: false,
|
||||
setSuccessOpen:()=>{},
|
||||
notificationCenter:false,
|
||||
setNotificationCenter:()=>{},
|
||||
notificationList:[],
|
||||
pushNotificationList:()=>{},
|
||||
clearNotificationList:()=>{},
|
||||
removeFromNotificationList:()=>{}
|
||||
}
|
||||
|
||||
export const alertContext = createContext<alertContextType>(initialValue);
|
||||
|
||||
export function AlertProvider({children}){
|
||||
const [errorData, setErrorDataState] = useState<{title:string,list?:Array<string>}>({title:"",list:[]});
|
||||
const [errorOpen, setErrorOpen] = useState(false);
|
||||
const [noticeData, setNoticeDataState] = useState<{title:string,link?:string}>({title:"",link:""});
|
||||
const [noticeOpen, setNoticeOpen] = useState(false);
|
||||
const [successData, setSuccessDataState] = useState<{title:string}>({title:""});
|
||||
const [successOpen, setSuccessOpen] = useState(false);
|
||||
const [notificationCenter,setNotificationCenter]=useState(false);
|
||||
const [notificationList,setNotificationList] = useState([])
|
||||
const pushNotificationList = (notification:alertDropdownItem)=>{
|
||||
setNotificationList((old) => {
|
||||
let newNotificationList = _.cloneDeep(old);
|
||||
newNotificationList.unshift(notification);
|
||||
return newNotificationList;
|
||||
})
|
||||
}
|
||||
function setErrorData (newState:{title: string, list?: Array<string>}){
|
||||
setErrorDataState(newState)
|
||||
setErrorOpen(true)
|
||||
if(newState.title){
|
||||
setNotificationCenter(true)
|
||||
pushNotificationList({type:"error",title:newState.title,list:newState.list, id:_.uniqueId()})
|
||||
}
|
||||
}
|
||||
function setNoticeData(newState:{title: string, link?: string}){
|
||||
setNoticeDataState(newState)
|
||||
setNoticeOpen(true)
|
||||
if(newState.title){
|
||||
setNotificationCenter(true)
|
||||
pushNotificationList({type:"notice",title:newState.title,link:newState.link, id:_.uniqueId()})
|
||||
}
|
||||
}
|
||||
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>
|
||||
)
|
||||
}
|
||||
15
space_flow/src/contexts/index.tsx
Normal file
15
space_flow/src/contexts/index.tsx
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { AlertProvider } from "./alertContext";
|
||||
import { LocationProvider } from "./locationContext";
|
||||
import PopUpProvider from "./popUpContext";
|
||||
|
||||
export default function ContextWrapper({ children }) {
|
||||
return (
|
||||
<>
|
||||
<LocationProvider>
|
||||
<PopUpProvider>
|
||||
<AlertProvider>{children}</AlertProvider>
|
||||
</PopUpProvider>
|
||||
</LocationProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
45
space_flow/src/contexts/locationContext.tsx
Normal file
45
space_flow/src/contexts/locationContext.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { createContext, useState } from "react";
|
||||
|
||||
type locationContextType=
|
||||
{
|
||||
atual:Array<string>;
|
||||
setAtual:(newState:Array<string>)=>void;
|
||||
isStackedOpen: boolean;
|
||||
setIsStackedOpen:(newState:boolean)=>void;
|
||||
showSideBar:boolean;
|
||||
setShowSideBar:(newState:boolean)=>void;
|
||||
extraNavigation:{title:string, options?:Array<{name:string, href:string, icon: any, children?:Array<any>}>};
|
||||
setExtraNavigation:(newState:{title:string, options?:Array<{name:string, href:string, icon: any, children?:Array<any>}>}) => void;
|
||||
extraComponent:any;
|
||||
setExtraComponent:(newState:any) => void;
|
||||
}
|
||||
|
||||
const initialValue= {
|
||||
atual : window.location.pathname.replace(/\/$/g, '').split("/"),
|
||||
isStackedOpen:((window.innerWidth > 1024 && window.location.pathname.split("/")[1]) ? true : false),
|
||||
setAtual: ()=>{},
|
||||
setIsStackedOpen:()=>{},
|
||||
showSideBar: window.location.pathname.split("/")[1]?true:false,
|
||||
setShowSideBar:()=>{},
|
||||
extraNavigation: {title:""},
|
||||
setExtraNavigation:()=>{},
|
||||
extraComponent: null,
|
||||
setExtraComponent:()=>{},
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const locationContext = createContext<locationContextType>(initialValue);
|
||||
|
||||
export function LocationProvider({children}){
|
||||
const [atual,setAtual] = useState(initialValue.atual)
|
||||
const [isStackedOpen,setIsStackedOpen] = useState(initialValue.isStackedOpen)
|
||||
const [showSideBar,setShowSideBar] = useState(initialValue.showSideBar)
|
||||
const [extraNavigation, setExtraNavigation] = useState({title:""})
|
||||
const [extraComponent, setExtraComponent] = useState(null)
|
||||
return (
|
||||
<locationContext.Provider value={{isStackedOpen,setIsStackedOpen,atual,setAtual, showSideBar, setShowSideBar,extraNavigation, setExtraNavigation, extraComponent, setExtraComponent}}>
|
||||
{children}
|
||||
</locationContext.Provider>
|
||||
)
|
||||
}
|
||||
32
space_flow/src/contexts/popUpContext.tsx
Normal file
32
space_flow/src/contexts/popUpContext.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { createContext } from "react";
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export const PopUpContext = createContext({
|
||||
openPopUp: (popUpElement: JSX.Element) => {},
|
||||
closePopUp: () => {}
|
||||
});
|
||||
|
||||
interface PopUpProviderProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const PopUpProvider = ({ children }: PopUpProviderProps) => {
|
||||
const [popUpElement, setPopUpElement] = useState<JSX.Element | null>(null);
|
||||
|
||||
const openPopUp = (element: JSX.Element) => {
|
||||
setPopUpElement(element);
|
||||
};
|
||||
|
||||
const closePopUp = () => {
|
||||
setPopUpElement(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<PopUpContext.Provider value={{ openPopUp, closePopUp }}>
|
||||
{children}
|
||||
{popUpElement}
|
||||
</PopUpContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default PopUpProvider;
|
||||
18
space_flow/src/entities/sidebarNav.ts
Normal file
18
space_flow/src/entities/sidebarNav.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import {
|
||||
BoltIcon,
|
||||
Cog6ToothIcon,
|
||||
HomeIcon,
|
||||
LightBulbIcon,
|
||||
RocketLaunchIcon,
|
||||
TableCellsIcon,
|
||||
} 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 },
|
||||
]
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { llm_chain } from "../../../../data_assets/llm_chain";
|
||||
|
||||
export function ExtraSidebar(){
|
||||
|
||||
function onDragStart(event:React.DragEvent<any>,nodeType){
|
||||
let json;
|
||||
event.dataTransfer.setData('application/reactflow',nodeType)
|
||||
event.dataTransfer.effectAllowed = 'move'
|
||||
if(nodeType==="promptNode"){
|
||||
json = JSON.stringify(prompt)
|
||||
}
|
||||
if(nodeType==="modelNode"){
|
||||
json = JSON.stringify(llm_chain)
|
||||
}
|
||||
event.dataTransfer.setData('json',json);
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="h-full w-48 bg-slate-200 flex flex-col">
|
||||
<div className="w-full text-center border border-black cursor-grab" draggable onDragStart={(event)=>onDragStart(event,'promptNode')}> prompt Node</div>
|
||||
<div draggable className="w-full text-center border border-black cursor-grab" onDragStart={(event)=>onDragStart(event,'modelNode')}> Model Node</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
94
space_flow/src/pages/FlowPage/index.tsx
Normal file
94
space_flow/src/pages/FlowPage/index.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
||||
import ReactFlow, {
|
||||
Background,
|
||||
Controls,
|
||||
addEdge,
|
||||
useEdgesState,
|
||||
useNodesState,
|
||||
} from "reactflow";
|
||||
import TextUpdaterNode from "../../CustomNodes/inputTextFolder";
|
||||
import PromptNode from "../../CustomNodes/PromptNode";
|
||||
import ModelNode from "../../CustomNodes/ModelNode";
|
||||
import { locationContext } from "../../contexts/locationContext";
|
||||
import { ExtraSidebar } from "./components/extraSidebarComponent";
|
||||
|
||||
export default function FlowPage() {
|
||||
// outside component to avoid render trigger
|
||||
|
||||
const reactFlowWrapper = useRef(null);
|
||||
const nodeTypes = {
|
||||
textUpdater: TextUpdaterNode,
|
||||
promptNode: PromptNode,
|
||||
modelNode: ModelNode,
|
||||
};
|
||||
|
||||
const rfStyle = {
|
||||
backgroundCOlor: "#B8CEFF",
|
||||
};
|
||||
let id = 0;
|
||||
const getId = () => `dndnode_${id++}`;
|
||||
|
||||
const { setExtraComponent } = useContext(locationContext);
|
||||
|
||||
useEffect(() => {
|
||||
setExtraComponent(ExtraSidebar);
|
||||
}, []);
|
||||
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||
const [reactFlowInstance, setReactFlowInstance] = useState(null);
|
||||
const onConnect = useCallback(
|
||||
(params) => setEdges((eds) => addEdge(params, eds)),
|
||||
[]
|
||||
);
|
||||
const onDragOver = useCallback((event) => {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = "move";
|
||||
}, []);
|
||||
const onDrop = useCallback(
|
||||
(event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const reactflowBounds = reactFlowWrapper.current.getBoundingClientRect();
|
||||
const type = event.dataTransfer.getData("application/reactflow");
|
||||
let data = JSON.parse(event.dataTransfer.getData("json"));
|
||||
// check if the dropped element is valid
|
||||
if (typeof type === "undefined" || !type) {
|
||||
return;
|
||||
}
|
||||
|
||||
const position = reactFlowInstance.project({
|
||||
x: event.clientX - reactflowBounds.left,
|
||||
y: event.clientY - reactflowBounds.top,
|
||||
});
|
||||
const newNode = {
|
||||
id: getId(),
|
||||
type,
|
||||
position,
|
||||
data: { ...data, delete: () => console.log("asdsdsadad") },
|
||||
};
|
||||
setNodes((nds) => nds.concat(newNode));
|
||||
},
|
||||
[reactFlowInstance]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full" ref={reactFlowWrapper}>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
onInit={setReactFlowInstance}
|
||||
nodeTypes={nodeTypes}
|
||||
onDragOver={onDragOver}
|
||||
onDrop={onDrop}
|
||||
fitView
|
||||
>
|
||||
<Background />
|
||||
<Controls></Controls>
|
||||
</ReactFlow>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
206
space_flow/src/utils.ts
Normal file
206
space_flow/src/utils.ts
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
export function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
export const textColors = {
|
||||
white: "text-white",
|
||||
red: "text-red-700",
|
||||
orange: "text-orange-700",
|
||||
amber: "text-amber-700",
|
||||
yellow: "text-yellow-700",
|
||||
lime: "text-lime-700",
|
||||
green: "text-green-700",
|
||||
emerald: "text-emerald-700",
|
||||
teal: "text-teal-700",
|
||||
cyan: "text-cyan-700",
|
||||
sky: "text-sky-700",
|
||||
blue: "text-blue-700",
|
||||
indigo: "text-indigo-700",
|
||||
violet: "text-violet-700",
|
||||
purple: "text-purple-700",
|
||||
fuchsia: "text-fuchsia-700",
|
||||
pink: "text-pink-700",
|
||||
rose: "text-rose-700",
|
||||
black: "text-black-700",
|
||||
gray: "bg-gray-700",
|
||||
};
|
||||
|
||||
export const bgColors = {
|
||||
white: "bg-white",
|
||||
red: "bg-red-100",
|
||||
orange: "bg-orange-100",
|
||||
amber: "bg-amber-100",
|
||||
yellow: "bg-yellow-100",
|
||||
lime: "bg-lime-100",
|
||||
green: "bg-green-100",
|
||||
emerald: "bg-emerald-100",
|
||||
teal: "bg-teal-100",
|
||||
cyan: "bg-cyan-100",
|
||||
sky: "bg-sky-100",
|
||||
blue: "bg-blue-100",
|
||||
indigo: "bg-indigo-100",
|
||||
violet: "bg-violet-100",
|
||||
purple: "bg-purple-100",
|
||||
fuchsia: "bg-fuchsia-100",
|
||||
pink: "bg-pink-100",
|
||||
rose: "bg-rose-100",
|
||||
black: "bg-black-100",
|
||||
gray: "bg-gray-100",
|
||||
};
|
||||
|
||||
export const bgColorsHover = {
|
||||
white: "hover:bg-white",
|
||||
black: "hover:bg-black-50",
|
||||
gray: "hover:bg-gray-50",
|
||||
red: "hover:bg-red-50",
|
||||
orange: "hover:bg-orange-50",
|
||||
amber: "hover:bg-amber-50",
|
||||
yellow: "hover:bg-yellow-50",
|
||||
lime: "hover:bg-lime-50",
|
||||
green: "hover:bg-green-50",
|
||||
emerald: "hover:bg-emerald-50",
|
||||
teal: "hover:bg-teal-50",
|
||||
cyan: "hover:bg-cyan-50",
|
||||
sky: "hover:bg-sky-50",
|
||||
blue: "hover:bg-blue-50",
|
||||
indigo: "hover:bg-indigo-50",
|
||||
violet: "hover:bg-violet-50",
|
||||
purple: "hover:bg-purple-50",
|
||||
fuchsia: "hover:bg-fuchsia-50",
|
||||
pink: "hover:bg-pink-50",
|
||||
rose: "hover:bg-rose-50",
|
||||
};
|
||||
|
||||
export const textColorsHex = {
|
||||
red: "rgb(185 28 28)",
|
||||
orange: "rgb(194 65 12)",
|
||||
amber: "rgb(180 83 9)",
|
||||
yellow: "rgb(161 98 7)",
|
||||
lime: "rgb(77 124 15)",
|
||||
green: "rgb(21 128 61)",
|
||||
emerald: "rgb(4 120 87)",
|
||||
teal: "rgb(15 118 110)",
|
||||
cyan: "rgb(14 116 144)",
|
||||
sky: "rgb(3 105 161)",
|
||||
blue: "rgb(29 78 216)",
|
||||
indigo: "rgb(67 56 202)",
|
||||
violet: "rgb(109 40 217)",
|
||||
purple: "rgb(126 34 206)",
|
||||
fuchsia: "rgb(162 28 175)",
|
||||
pink: "rgb(190 24 93)",
|
||||
rose: "rgb(190 18 60)",
|
||||
};
|
||||
|
||||
export const bgColorsHex = {
|
||||
red: "rgb(254 226 226)",
|
||||
orange: "rgb(255 237 213)",
|
||||
amber: "rgb(254 243 199)",
|
||||
yellow: "rgb(254 249 195)",
|
||||
lime: "rgb(236 252 203)",
|
||||
green: "rgb(220 252 231)",
|
||||
emerald: "rgb(209 250 229)",
|
||||
teal: "rgb(204 251 241)",
|
||||
cyan: "rgb(207 250 254)",
|
||||
sky: "rgb(224 242 254)",
|
||||
blue: "rgb(219 234 254)",
|
||||
indigo: "rgb(224 231 255)",
|
||||
violet: "rgb(237 233 254)",
|
||||
purple: "rgb(243 232 255)",
|
||||
fuchsia: "rgb(250 232 255)",
|
||||
pink: "rgb(252 231 243)",
|
||||
rose: "rgb(255 228 230)",
|
||||
};
|
||||
|
||||
export const taskTypeMap: { [key: string]: string } = {
|
||||
MULTICLASS_CLASSIFICATION: "Multiclass Classification",
|
||||
};
|
||||
|
||||
const charWidths = {
|
||||
" ": 0.2,
|
||||
"!": 0.2,
|
||||
'"': 0.3,
|
||||
"#": 0.5,
|
||||
"$": 0.5,
|
||||
"%": 0.5,
|
||||
"&": 0.5,
|
||||
"(": 0.2,
|
||||
")": 0.2,
|
||||
"*": 0.5,
|
||||
"+": 0.5,
|
||||
",": 0.2,
|
||||
"-": 0.2,
|
||||
".": 0.1,
|
||||
"/": 0.5,
|
||||
":": 0.2,
|
||||
";": 0.2,
|
||||
"<": 0.5,
|
||||
"=": 0.5,
|
||||
">": 0.5,
|
||||
"?": 0.2,
|
||||
"@": 0.5,
|
||||
"[": 0.2,
|
||||
"\\": 0.5,
|
||||
"]": 0.2,
|
||||
"^": 0.5,
|
||||
"_": 0.2,
|
||||
"`": 0.5,
|
||||
"{": 0.2,
|
||||
"|": 0.2,
|
||||
"}": 0.2,
|
||||
"~": 0.5,
|
||||
};
|
||||
|
||||
for (let i = 65; i <= 90; i++) {
|
||||
charWidths[String.fromCharCode(i)] = 0.6;
|
||||
}
|
||||
for (let i = 97; i <= 122; i++) {
|
||||
charWidths[String.fromCharCode(i)] = 0.5;
|
||||
}
|
||||
|
||||
export function measureTextWidth(text: string, fontSize) {
|
||||
let wordWidth = 0;
|
||||
for (let j = 0; j < text.length; j++) {
|
||||
let char = text[j];
|
||||
let charWidth = charWidths[char] || 0.5;
|
||||
wordWidth += charWidth * fontSize;
|
||||
}
|
||||
return wordWidth;
|
||||
}
|
||||
|
||||
export function measureTextHeight(text: string, width, fontSize) {
|
||||
const charHeight = fontSize;
|
||||
const lineHeight = charHeight * 1.5;
|
||||
const words = text.split(" ");
|
||||
let lineWidth = 0;
|
||||
let totalHeight = 0;
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
let word = words[i];
|
||||
let wordWidth = measureTextWidth(word, fontSize);
|
||||
if (lineWidth + wordWidth + charWidths[" "] * fontSize <= width) {
|
||||
lineWidth += wordWidth + charWidths[" "] * fontSize;
|
||||
} else {
|
||||
totalHeight += lineHeight;
|
||||
lineWidth = wordWidth;
|
||||
}
|
||||
}
|
||||
totalHeight += lineHeight;
|
||||
return totalHeight;
|
||||
}
|
||||
|
||||
export function toCamelCase(str: string){
|
||||
return str
|
||||
.split(' ')
|
||||
.map((word, index) => (index === 0 ? word.toLowerCase() : word[0].toUpperCase() + word.slice(1).toLowerCase()))
|
||||
.join('');
|
||||
};
|
||||
export function toFirstUpperCase(str: string){
|
||||
return str
|
||||
.split(' ')
|
||||
.map((word, index) => (word[0].toUpperCase() + word.slice(1).toLowerCase()))
|
||||
.join('');
|
||||
};
|
||||
|
||||
export function roundNumber(x, decimals){
|
||||
return Math.round(x * Math.pow(10, decimals))/Math.pow(10, decimals)
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue