Changed project structure, added sidebar and alerts

This commit is contained in:
Lucas Oliveira 2023-02-10 16:03:05 -03:00
commit 8ace199118
20 changed files with 1346 additions and 0 deletions

View file

@ -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>
)
}

View 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>
);
}

View 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"
}
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
</>
);
}

View 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>
)
}

View 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>
);
}

View file

@ -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>
</>
)
}

View 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>
)
}

View 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>
)
}

View 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>
)
}

View 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>
</>
);
}

View 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>
)
}

View 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;

View 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 },
]

View file

@ -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>
)
}

View 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
View 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)
}