Admin nav

main
Ashelyn Dawn 4 years ago
parent fa0f6d28b0
commit 2e32f09042

@ -1,24 +1,30 @@
import React from 'react'
import { useRouter } from 'next/router'
import Link from 'next/link'
import useIsAdminPage from '~/hooks/useIsAdminPage'
import styles from './adminNav.module.css'
import NavItem from './navItem'
export default function AdminNav({}){
const {asPath: fullpath} = useRouter()
const withoutQuery = fullpath.split('?')[0]
import styles from './adminNav.module.css'
const adminRoutes = new RegExp('^/admin(/.*|$)')
export default function AdminNav({children}){
const isAdminPage = useIsAdminPage()
if(!withoutQuery.match(adminRoutes))
return null
if(!isAdminPage)
return children
return (
<div className={styles.adminBar}>
<Link href="/admin/items"><a>Items</a></Link>
<Link href="/admin/categories"><a>Categories</a></Link>
<Link href="/admin/users"><a>Users</a></Link>
<Link href="/admin/shipping"><a>Shipping</a></Link>
<div className={styles.navContainer}>
<div className={styles.nav}>
<NavItem icon="apps" href="/admin">Dashboard</NavItem>
<NavItem icon="local_offer" href="/admin/items">Items</NavItem>
<NavItem icon="category" href="/admin/categories">Categories</NavItem>
<NavItem icon="shopping_cart" href="/admin/orders">Orders</NavItem>
<NavItem icon="supervisor_account" href="/admin/users">Users</NavItem>
<NavItem icon="local_shipping" href="/admin/shipments">Shipments</NavItem>
<NavItem icon="payment" href="/admin/coupons">Coupons</NavItem>
</div>
<div className={styles.notNav}>
{children}
</div>
</div>
)
}

@ -1,28 +1,66 @@
.adminBar + header {
position: relative;
top: -40px;
.navContainer {
width: 100%;
height: calc(100vh - 115px);
display: flex;
flex-direction: row;
}
.adminBar + header::after {
background: none;
.nav {
font-family: Arial, Helvetica, sans-serif;
width: 200px;
background: white;
box-shadow: 0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12);
}
div.adminBar {
position: relative;
top: 100px;
margin-top: 0;
.notNav {
flex: 1;
font-family: Arial, Helvetica, sans-serif;
max-height: 100%;
overflow-y: auto;
}
.navItem {
display: flex;
flex-direction: row;
background: #9c27b0;
height: 40px;
width: 100%;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 200px;
height: 42px;
padding: 12px;
position: relative;
}
.navItem:hover::before {
content: ' ';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,.2);
}
.navItem.selected {
background: #e1bee7;
}
a.navItem {
text-decoration: none;
color: black;
}
a.navItem.selected {
color: white;
}
a.navItem :global(.rmwc-icon) {
font-size: 18px;
margin-right: 12px;
width: 18px;
overflow: hidden;
color: #e1bee7;
}
.adminBar > a {
a.navItem.selected :global(.rmwc-icon) {
color: white;
font-family: 'Cormorant SC', serif;
padding: 5px;
margin: 0 25px;
}

@ -0,0 +1,22 @@
import React from 'react'
import {useRouter} from 'next/router'
import Link from 'next/link'
import {Icon} from '@rmwc/icon'
import styles from './adminNav.module.css'
export default function NavItem({href, icon, children}) {
const {asPath: fullpath} = useRouter()
const withoutQuery = fullpath.split('?')[0]
const selected = (fullpath === href || (href !== '/admin' && fullpath.includes(href + '/')))
return (
<Link href={href}>
<a className={styles.navItem + (selected ? ' ' + styles.selected: '')}>
<Icon icon={icon} />
{children}
</a>
</Link>
)
}

@ -8,7 +8,7 @@ export default class ErrorBoundary extends React.Component {
this.state = {error: null};
}
getDerivedStateFromError(error){
static getDerivedStateFromError(error){
return {error}
}

@ -2,25 +2,33 @@ import Link from "next/link"
import {DateTime} from 'luxon'
import React from "react"
import useIsAdminPage from '~/hooks/useIsAdminPage'
import styles from './style.module.css'
const Footer = () => (
<footer className={styles.container}>
<div>
<h1 className={styles.title}>
<Link href="/"><a>Society <span>of</span> Socks</a></Link>
</h1>
<div className={styles.footerInfo}>
<p>© 2015 - {DateTime.utc().year} Society of Socks</p>
const Footer = () => {
const isAdminPage = useIsAdminPage()
if(isAdminPage)
return null
return (
<footer className={styles.container}>
<div>
<h1 className={styles.title}>
<Link href="/"><a>Society <span>of</span> Socks</a></Link>
</h1>
<div className={styles.footerInfo}>
<p>© 2015 - {DateTime.utc().year} Society of Socks</p>
</div>
<ul className={styles.footerNav}>
<li><Link href="#mailingList"><a onClick={(ev)=>{if(ev) ev.preventDefault(); console.log('mailing list')}}>Mailing List</a></Link></li>
<li><Link href="/contact"><a>Contact Us</a></Link></li>
<li><Link href="/privacy"><a>Privacy</a></Link></li>
</ul>
</div>
<ul className={styles.footerNav}>
<li><Link href="#mailingList"><a onClick={(ev)=>{if(ev) ev.preventDefault(); console.log('mailing list')}}>Mailing List</a></Link></li>
<li><Link href="/contact"><a>Contact Us</a></Link></li>
<li><Link href="/privacy"><a>Privacy</a></Link></li>
</ul>
</div>
</footer>
)
</footer>
)
}
Footer.propTypes = {
}

@ -2,22 +2,21 @@ import Link from "next/link"
import PropTypes from "prop-types"
import React from "react"
import useIsAdminPage from '~/hooks/useIsAdminPage'
import useUser from '~/hooks/useUser'
import useCart from '~/hooks/useCart'
import logo from './sos-logo.png'
import styles from './style.module.css'
import AdminNav from '~/components/adminNav'
const Header = () => {
const user = useUser()
const [cart] = useCart()
const cartSize = (cart && cart.items.length) ? cart.items.map(item => item.count).reduce((sum, current) => (sum + current)) : 0
const isAdminPage = useIsAdminPage()
return (
<>
<AdminNav/>
<header className={styles.container}>
<header className={styles.container + (isAdminPage? ' ' + styles.adminShadow : '')}>
<div className={styles.logo}>
<img alt="" src={logo}/>
</div>
@ -37,7 +36,7 @@ const Header = () => {
):(
<>
<li><Link href="/account"><a>Account</a></Link></li>
{user.is_admin && <li><Link href="/admin"><a>Admin</a></Link></li>}
{user.is_admin && !isAdminPage && <li><Link href="/admin"><a>Admin</a></Link></li>}
<li><Link href="/api/auth/logout"><a>Log out</a></Link></li>
</>
)

@ -22,6 +22,10 @@
z-index: 1;
}
.container.adminShadow::after {
box-shadow: 0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12);
}
.container + div {
margin-top: 15px;
}

@ -0,0 +1,10 @@
import {useRouter} from 'next/router'
export default function useIsAdminPage(){
const {asPath: fullpath} = useRouter()
const withoutQuery = fullpath.split('?')[0]
const adminRoutes = new RegExp('^/admin(/.*|$)')
return withoutQuery.match(adminRoutes)
}

60
package-lock.json generated

@ -1003,6 +1003,51 @@
"resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz",
"integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw=="
},
"@material/dom": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@material/dom/-/dom-5.1.0.tgz",
"integrity": "sha512-tBbl3hG34Auv+2sboWhtstXeZ9rx3G7hb/jEXs5xZ5KIfZHwY4mbo0KqR/fSJZKaUsvk2Nc/UEOVcUx2mTNmYg==",
"requires": {
"tslib": "^1.9.3"
}
},
"@rmwc/base": {
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@rmwc/base/-/base-6.0.12.tgz",
"integrity": "sha512-BNBur+abGvFnioNS1jffeCgPP3YD+1ChaAPeQlLF6ArGaZpez630PXjyndqJDHJW2o81tIDz8VTs4eeyEPd/9g==",
"requires": {
"@material/dom": "^5.1.0",
"@rmwc/types": "^6.0.5",
"classnames": "^2.2.6",
"hyperform": "^0.11.0",
"mutation-observer": "^1.0.3",
"tslib": "^1.10.0"
}
},
"@rmwc/icon": {
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@rmwc/icon/-/icon-6.0.12.tgz",
"integrity": "sha512-o4ZJ5ouwr1MMNB8vrCHNPtrBNab1hacdbaO9zSH6riiWHrFMW+XhQXTk509P7ehN32pWpAqW9YwzScVaKnketw==",
"requires": {
"@rmwc/base": "^6.0.12",
"@rmwc/provider": "^6.0.12",
"@rmwc/types": "^6.0.5"
}
},
"@rmwc/provider": {
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@rmwc/provider/-/provider-6.0.12.tgz",
"integrity": "sha512-w72/0ONm/uwWoBMpp2RtxJ8e29ofjnufNQZ3BEE64Afo4vxQwZ74LTaYsjClO8yt1p8aM0FSxRFfaTWNBy6R4Q==",
"requires": {
"@rmwc/base": "^6.0.12",
"@rmwc/types": "^6.0.5"
}
},
"@rmwc/types": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/@rmwc/types/-/types-6.0.5.tgz",
"integrity": "sha512-G4NZxakwsTMFxxpEWX90MOlZtVNzCwooLUg7FH7Oh5OJCnKWGmXK8ZMF59vvIEn7zCiEpVroKhOxNpMw9TIUTw=="
},
"@webassemblyjs/ast": {
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
@ -2140,6 +2185,11 @@
}
}
},
"classnames": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
},
"cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@ -3718,6 +3768,11 @@
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
},
"hyperform": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/hyperform/-/hyperform-0.11.0.tgz",
"integrity": "sha512-HFry1hgItOzFmKXNgAh/yBrULtI/j8DqqnH8V28jVQDxHFjH1cPLl+JDxoVtPoJnO8Yti+MAGubio7wGU6qXGg=="
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -4577,6 +4632,11 @@
"xtend": "^4.0.0"
}
},
"mutation-observer": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/mutation-observer/-/mutation-observer-1.0.3.tgz",
"integrity": "sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA=="
},
"nan": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",

@ -12,6 +12,7 @@
"license": "ISC",
"devDependencies": {},
"dependencies": {
"@rmwc/icon": "^6.0.12",
"axios": "^0.19.2",
"base64-async": "^2.1.3",
"bcrypt": "^3.0.8",

@ -1,4 +1,5 @@
import React, {useState} from "react"
import Head from 'next/head'
import PropTypes from "prop-types"
import axios from 'axios'
@ -6,7 +7,9 @@ import {UserContextProvider} from '~/hooks/useUser'
import {CartContextProvider} from '~/hooks/useCart'
import Header from '~/components/header'
import Footer from '~/components/footer'
import AdminNav from '~/components/adminNav'
import ErrorBoundary from '~/components/errorBoundary'
import "~/styles/layout.css"
Layout.getInitialProps = async ({Component, ctx}) => {
@ -36,17 +39,24 @@ function Layout({ Component, pageProps, user, cart: _cart }){
const cartState = useState(_cart)
return (
<CartContextProvider value={cartState}>
<UserContextProvider value={user}>
<Header/>
<main>
<ErrorBoundary>
<Component {...pageProps} />
</ErrorBoundary>
</main>
<Footer/>
</UserContextProvider>
</CartContextProvider>
<>
<Head>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
</Head>
<CartContextProvider value={cartState}>
<UserContextProvider value={user}>
<Header/>
<main>
<ErrorBoundary>
<AdminNav>
<Component {...pageProps} />
</AdminNav>
</ErrorBoundary>
</main>
<Footer/>
</UserContextProvider>
</CartContextProvider>
</>
)
}

@ -3,6 +3,5 @@ import useRequireAdmin from '~/hooks/useRequireAdmin'
export default function AdminDashboard(){
useRequireAdmin()
// TODO: button for making a stockchange transaction
return <p>Admin Dashboard</p>
return <p style={{marginLeft: '12px'}}>Admin Dashboard goes here</p>
}

@ -1,3 +1,5 @@
@import url('../node_modules/@rmwc/icon/icon.css');
html {
font-family: sans-serif;
-ms-text-size-adjust: 100%;
@ -23,7 +25,7 @@ html,body {
main {
background: #E4E4E4;
color: black;
padding: 15px 0;
padding: 15px 0 0 0;
}
main h2 {

Loading…
Cancel
Save