diff --git a/components/core/Shell/Footer/Footer.jsx b/components/core/Shell/Footer/Footer.jsx new file mode 100644 index 0000000..d938c49 --- /dev/null +++ b/components/core/Shell/Footer/Footer.jsx @@ -0,0 +1,24 @@ +import React from 'react'; +import styled from '@emotion/styled'; + +const FooterContainer = styled('div')` + padding-left: 10px; + position: absolute; + left: 0; + bottom: 0; + right: 0; + flex-shrink: 0; + height: 30px; + padding-top: 6px; + background: #585858; + color: white; + font-family: "WDXL Lubrifont TC"; +`; + +export const Footer = () => { + return ( + +

{`© ${new Date().getFullYear()} ShiftSync`}

+
+ ) +} \ No newline at end of file diff --git a/components/core/Shell/Footer/index.js b/components/core/Shell/Footer/index.js new file mode 100644 index 0000000..f6e9523 --- /dev/null +++ b/components/core/Shell/Footer/index.js @@ -0,0 +1 @@ +export { Footer } from './Footer'; \ No newline at end of file diff --git a/components/core/Shell/NavBar/NavBar.jsx b/components/core/Shell/NavBar/NavBar.jsx new file mode 100644 index 0000000..c18cc1e --- /dev/null +++ b/components/core/Shell/NavBar/NavBar.jsx @@ -0,0 +1,181 @@ +import React, { useEffect, useRef, useState } from 'react'; +import styled from '@emotion/styled'; +import { + useLocation, + useNavigate, + useParams +} from 'react-router'; +import { Stack } from '@mui/material'; + +const NavBox = styled(Stack)` + width: 100%; +`; + +const LeftContainer = styled(Stack)` + display: flex; + align-items: center; + flex-direction: row; + flex-flow: row wrap; +`; + +const RightContainer = styled(Stack)` + display: flex; + flex-direction: row; + flex-flow: row wrap; +`; + +const NavGroup = styled(Stack)` + display: flex; + background: #ffffff + width: 100%; + height: 60px; + padding: 4px; + justify-content: space-between !important +`; + +const Title = styled('p')` + &:hover { + cursor: pointer; + } +`; + +const Tab = styled('div')` + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-end; + height: 100%; + padding: 6px 16px; + font-size: 20px; + font-weight: 800; + color: grey; + background: none; + border: none; + cursor: pointer; + font-family: "WDXL Lubrifont TC"; + letter-spacing: .05em; + word-spacing: .2em; +`; + +const SlidingIndicator = styled('div')` + position: absolute; + bottom: -6px; + height: 2px; + background-color: red; + transition: left 0.3s ease, width 0.3s ease; +`; + +const UserButton = styled(Stack)` + width: 100%; +`; + +const Border = styled('div')` + height: 2px; + width: 100%; + background-color: #A8A8A8; +`; + +export const NavBar = ({ notifications, disableNav, settings }) => { + const { pathname } = useLocation(); + const navigate = useNavigate(); + const routeParams = useParams(); + + const [indicatorStyle, setIndicatorStyle] = useState({ left: 0, width: 0 }); + const tabRefs = useRef({}); + + const isAdmin = true; // to be changed to check + + const navItems = [ + { + name: 'Home', + id: 'home', + href: '/', + matches: ['', 'home'] + }, + { + name: 'About Us', + id: 'about', + href: '/about', + matches: ['about', 'aboutus'] + }, + { + name: 'Administrator Panel', + id: 'admin', + href: '/admin', + matches: ['admin'], + hidden: !isAdmin + } + ]; + + let activeTab = navItems.find((tab) => { + return tab.matches.some((item) => { + return item === pathname?.split('/')[1]; + }); + }); + + const onShiftSyncClick = () => { + navigate('/'); + } + + const onNavButtonClick = (tab) => { + activeTab = tab; + navigate(tab.href); + }; + + const onUserIconClick = () => { + navigate('/settings'); + }; + + useEffect(() => { + if (activeTab?.id && tabRefs.current[activeTab.id]) { + const el = tabRefs.current[activeTab.id]; + const rect = el.getBoundingClientRect(); + const containerRect = el.parentNode.getBoundingClientRect(); + setIndicatorStyle({ + left: rect.left - containerRect.left, + width: rect.width + }); + } +}, [pathname, activeTab?.id]); + + return ( + + + + {/* */} + + ShiftSync + + + + {navItems?.map((item) => { + if (!item?.hidden) { + return ( + { tabRefs.current[item.id] = el; }} + onClick={() => onNavButtonClick(item)} + > + {item?.name} + + ); + } + return null; + }).filter((item) => item !== null)} + + + + + + ) +} \ No newline at end of file diff --git a/components/core/Shell/NavBar/index.js b/components/core/Shell/NavBar/index.js new file mode 100644 index 0000000..46c69f1 --- /dev/null +++ b/components/core/Shell/NavBar/index.js @@ -0,0 +1 @@ +export { NavBar } from './NavBar'; \ No newline at end of file diff --git a/components/core/Shell/Shell.jsx b/components/core/Shell/Shell.jsx new file mode 100644 index 0000000..494505a --- /dev/null +++ b/components/core/Shell/Shell.jsx @@ -0,0 +1,70 @@ +import React, { useEffect, useState, useRef } from 'react'; +import styled from '@emotion/styled'; +import { NavBar } from './NavBar'; +import { Footer } from './Footer'; + +const minHeight = 'calc(100vh - 30px)'; + +const Body = styled.div({ + display: 'flex', + flexDirection: 'column', + position: 'relative', + overflow: 'hidden', + height: '100vh', + minHeight +}); + +const MainContainer = styled.div({ + display: 'flex', + textAlign: 'center', + width: '100%', + flexDirection: 'row', + height: minHeight +}); + +const Main = styled.div({ + background: 'linear-gradient(100deg, #f3f9ff 80%, rgba(217, 217, 217) 100%);', + display: 'flex', + flexDirection: 'column', + width: '100%', + height: minHeight +}); + +const Content = styled.div(({ cssProps }) => ({ + display: 'flex', + flexDirection: 'column', + width: '100%', + alignContent: 'center', + overflowY: 'auto', + height: cssProps?.minHeightMain || '100%' +})); + +export const Shell = ({ children }) => { + const bannerRef = useRef(); + const nonContentHeight = 92 + (bannerRef?.current?.offsetHeight || 0); + const minHeightMain = `calc(100vh - ${nonContentHeight}px)`; + + useEffect(() => { + window.scrollTo(0,0); + }, []); + + return ( + + +
+ +
+ + {children} + +
+
+