aboutsummaryrefslogtreecommitdiffstats
path: root/src/views/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/views/components')
-rw-r--r--src/views/components/AutocompleteSelect.jsx87
-rw-r--r--src/views/components/NavBar.jsx177
-rw-r--r--src/views/components/NavBarLink.jsx31
-rw-r--r--src/views/components/NavBarMobile.jsx107
4 files changed, 402 insertions, 0 deletions
diff --git a/src/views/components/AutocompleteSelect.jsx b/src/views/components/AutocompleteSelect.jsx
new file mode 100644
index 0000000..7708214
--- /dev/null
+++ b/src/views/components/AutocompleteSelect.jsx
@@ -0,0 +1,87 @@
+import Add from '@mui/icons-material/Add'
+import Autocomplete, { createFilterOptions } from '@mui/joy/Autocomplete'
+import AutocompleteOption from '@mui/joy/AutocompleteOption'
+import FormControl from '@mui/joy/FormControl'
+import ListItemDecorator from '@mui/joy/ListItemDecorator'
+import * as React from 'react'
+
+const filter = createFilterOptions()
+
+export default function FreeSoloCreateOption({ options, onSelectChange }) {
+ React.useEffect(() => {
+ setValue(options)
+ }, [options])
+
+ const [value, setValue] = React.useState([])
+ const [selectOptions, setSelectOptions] = React.useState(
+ options ? options : [],
+ )
+ return (
+ <FormControl id='free-solo-with-text-demo'>
+ <Autocomplete
+ value={value}
+ multiple
+ size='lg'
+ on
+ onChange={(event, newValue) => {
+ if (typeof newValue === 'string') {
+ setValue({
+ title: newValue,
+ })
+ } else if (newValue && newValue.inputValue) {
+ // Create a new value from the user input
+ setValue({
+ title: newValue.inputValue,
+ })
+ } else {
+ setValue(newValue)
+ }
+ onSelectChange(newValue)
+ }}
+ filterOptions={(options, params) => {
+ const filtered = filter(options, params)
+
+ const { inputValue } = params
+ // Suggest the creation of a new value
+ const isExisting = options.some(option => inputValue === option.title)
+ if (inputValue !== '' && !isExisting) {
+ filtered.push({
+ inputValue,
+ title: `Add "${inputValue}"`,
+ })
+ }
+
+ return filtered
+ }}
+ selectOnFocus
+ clearOnBlur
+ handleHomeEndKeys
+ // freeSolo
+ options={selectOptions}
+ getOptionLabel={option => {
+ // Value selected with enter, right from the input
+ if (typeof option === 'string') {
+ return option
+ }
+ // Add "xxx" option created dynamically
+ if (option.inputValue) {
+ return option.inputValue
+ }
+ // Regular option
+ return option.title
+ }}
+ renderOption={(props, option) => (
+ <AutocompleteOption {...props}>
+ {option.title?.startsWith('Add "') && (
+ <ListItemDecorator>
+ <Add />
+ </ListItemDecorator>
+ )}
+
+ {option.title ? option.title : option}
+ </AutocompleteOption>
+ )}
+ />
+ </FormControl>
+ )
+}
diff --git a/src/views/components/NavBar.jsx b/src/views/components/NavBar.jsx
new file mode 100644
index 0000000..25463b2
--- /dev/null
+++ b/src/views/components/NavBar.jsx
@@ -0,0 +1,177 @@
+import Logo from '@/assets/logo.svg'
+import {
+ AccountBox,
+ HomeOutlined,
+ ListAltRounded,
+ Logout,
+ MenuRounded,
+ Message,
+ SettingsOutlined,
+ ShareOutlined,
+ Widgets,
+} from '@mui/icons-material'
+import {
+ Box,
+ Drawer,
+ IconButton,
+ List,
+ ListItemButton,
+ ListItemContent,
+ ListItemDecorator,
+ Typography,
+} from '@mui/joy'
+import { useState } from 'react'
+import { useLocation } from 'react-router-dom'
+import { version } from '../../../package.json'
+import NavBarLink from './NavBarLink'
+const links = [
+ {
+ to: '/my/chores',
+ label: 'Home',
+ icon: <HomeOutlined />,
+ },
+ {
+ to: '/chores',
+ label: 'Desktop View',
+ icon: <ListAltRounded />,
+ },
+ {
+ to: '/things',
+ label: 'Things',
+ icon: <Widgets />,
+ },
+ {
+ to: '/settings#sharing',
+ label: 'Sharing',
+ icon: <ShareOutlined />,
+ },
+ {
+ to: '/settings#notifications',
+ label: 'Notifications',
+ icon: <Message />,
+ },
+ {
+ to: '/settings#account',
+ label: 'Account',
+ icon: <AccountBox />,
+ },
+ {
+ to: '/settings',
+ label: 'Settings',
+ icon: <SettingsOutlined />,
+ },
+]
+
+const NavBar = () => {
+ const [drawerOpen, setDrawerOpen] = useState(false)
+ const [openDrawer, closeDrawer] = [
+ () => setDrawerOpen(true),
+ () => setDrawerOpen(false),
+ ]
+ const location = useLocation()
+ // if url has /landing then remove the navbar:
+ if (
+ ['/', '/signup', '/login', '/landing', '/forgot-password'].includes(
+ location.pathname,
+ )
+ ) {
+ return null
+ }
+
+ return (
+ <nav className='flex gap-2 p-3'>
+ <IconButton size='sm' variant='plain' onClick={() => setDrawerOpen(true)}>
+ <MenuRounded />
+ </IconButton>
+ <Box className='flex items-center gap-2'>
+ <img component='img' src={Logo} width='34' />
+ <Typography
+ level='title-lg'
+ sx={{
+ fontWeight: 700,
+ fontSize: 24,
+ }}
+ >
+ Done
+ <span
+ style={{
+ color: '#06b6d4',
+ fontWeight: 600,
+ }}
+ >
+ tick✓
+ </span>
+ </Typography>
+ </Box>
+ <Drawer
+ open={drawerOpen}
+ onClose={closeDrawer}
+ size='sm'
+ onClick={closeDrawer}
+ >
+ <div>
+ {/* <div className='align-center flex px-5 pt-4'>
+ <ModalClose size='sm' sx={{ top: 'unset', right: 20 }} />
+ </div> */}
+ <List
+ // sx={{ p: 2, height: 'min-content' }}
+ size='md'
+ onClick={openDrawer}
+ sx={{ borderRadius: 4, width: '100%', padding: 1 }}
+ >
+ {links.map((link, index) => (
+ <NavBarLink key={index} link={link} />
+ ))}
+ </List>
+ </div>
+ <div>
+ <List
+ sx={{
+ p: 2,
+ height: 'min-content',
+ position: 'absolute',
+ bottom: 0,
+ borderRadius: 4,
+ width: '100%',
+ padding: 2,
+ }}
+ size='md'
+ onClick={openDrawer}
+ >
+ <ListItemButton
+ onClick={() => {
+ localStorage.removeItem('ca_token')
+ localStorage.removeItem('ca_expiration')
+ // go to login page:
+ window.location.href = '/login'
+ }}
+ sx={{
+ py: 1.2,
+ }}
+ >
+ <ListItemDecorator>
+ <Logout />
+ </ListItemDecorator>
+ <ListItemContent>Logout</ListItemContent>
+ </ListItemButton>
+ <Typography
+ level='body-xs'
+ sx={{
+ // p: 2,
+ p: 1,
+ color: 'text.tertiary',
+ textAlign: 'center',
+ bottom: 0,
+ // mb: -2,
+ }}
+ >
+ V{version}
+ </Typography>
+ </List>
+ </div>
+ </Drawer>
+ </nav>
+ )
+}
+
+export default NavBar
diff --git a/src/views/components/NavBarLink.jsx b/src/views/components/NavBarLink.jsx
new file mode 100644
index 0000000..3fe10d2
--- /dev/null
+++ b/src/views/components/NavBarLink.jsx
@@ -0,0 +1,31 @@
+import {
+ ListItem,
+ ListItemButton,
+ ListItemContent,
+ ListItemDecorator,
+} from '@mui/joy'
+import { Link } from 'react-router-dom'
+
+const NavBarLink = ({ link }) => {
+ const { to, icon, label } = link
+ return (
+ <ListItem>
+ <ListItemButton
+ key={to}
+ component={Link}
+ to={to}
+ variant='plain'
+ color='neutral'
+ sx={{
+ borderRadius: 4,
+ py: 1.2,
+ }}
+ >
+ <ListItemDecorator>{icon}</ListItemDecorator>
+ <ListItemContent>{label}</ListItemContent>
+ </ListItemButton>
+ </ListItem>
+ )
+}
+
+export default NavBarLink
diff --git a/src/views/components/NavBarMobile.jsx b/src/views/components/NavBarMobile.jsx
new file mode 100644
index 0000000..5fb1100
--- /dev/null
+++ b/src/views/components/NavBarMobile.jsx
@@ -0,0 +1,107 @@
+import * as React from 'react'
+import Box from '@mui/joy/Box'
+import ListItemDecorator from '@mui/joy/ListItemDecorator'
+import Tabs from '@mui/joy/Tabs'
+import TabList from '@mui/joy/TabList'
+import Tab, { tabClasses } from '@mui/joy/Tab'
+import HomeRoundedIcon from '@mui/icons-material/HomeRounded'
+import FavoriteBorder from '@mui/icons-material/FavoriteBorder'
+import Search from '@mui/icons-material/Search'
+import Person from '@mui/icons-material/Person'
+
+export default function NavBarMobile() {
+ const [index, setIndex] = React.useState(0)
+ const colors = ['primary', 'danger', 'success', 'warning']
+ return (
+ <Box
+ sx={{
+ position: 'absolute',
+ width: '100%',
+ bottom: 0,
+
+ flexGrow: 1,
+
+ p: 1,
+ borderTopLeftRadius: '12px',
+ borderTopRightRadius: '12px',
+
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ }}
+ >
+ <Tabs
+ size='lg'
+ aria-label='Bottom Navigation'
+ value={index}
+ onChange={(event, value) => setIndex(value)}
+ sx={theme => ({
+ p: 1,
+ borderRadius: 16,
+ maxWidth: 500,
+ // mx: 'auto',
+ boxShadow: theme.shadow.sm,
+ '--joy-shadowChannel': theme.vars.palette[colors[index]].darkChannel,
+ [`& .${tabClasses.root}`]: {
+ py: 1,
+ flex: 1,
+ transition: '0.3s',
+ fontWeight: 'md',
+ fontSize: 'md',
+ [`&:not(.${tabClasses.selected}):not(:hover)`]: {
+ opacity: 0.7,
+ },
+ },
+ })}
+ >
+ <TabList
+ variant='plain'
+ size='sm'
+ disableUnderline
+ sx={{ borderRadius: 'lg', p: 0 }}
+ >
+ <Tab
+ disableIndicator
+ orientation='vertical'
+ {...(index === 0 && { color: colors[0] })}
+ >
+ <ListItemDecorator>
+ <HomeRoundedIcon />
+ </ListItemDecorator>
+ Home
+ </Tab>
+ <Tab
+ disableIndicator
+ orientation='vertical'
+ {...(index === 1 && { color: colors[1] })}
+ >
+ <ListItemDecorator>
+ <FavoriteBorder />
+ </ListItemDecorator>
+ Likes
+ </Tab>
+ <Tab
+ disableIndicator
+ orientation='vertical'
+ {...(index === 2 && { color: colors[2] })}
+ >
+ <ListItemDecorator>
+ <Search />
+ </ListItemDecorator>
+ Search
+ </Tab>
+ <Tab
+ disableIndicator
+ orientation='vertical'
+ {...(index === 3 && { color: colors[3] })}
+ >
+ <ListItemDecorator>
+ <Person />
+ </ListItemDecorator>
+ Profile
+ </Tab>
+ </TabList>
+ </Tabs>
+ </Box>
+ )
+}