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
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>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue