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