Merge branch 'dev'

This commit is contained in:
Mo Tarbin
2026-02-08 01:40:47 -05:00
16 changed files with 232 additions and 195 deletions

View File

@@ -61,6 +61,7 @@
"@tanstack/react-query": "^5.17.0", "@tanstack/react-query": "^5.17.0",
"aos": "^2.3.4", "aos": "^2.3.4",
"browser-image-compression": "^2.0.2", "browser-image-compression": "^2.0.2",
"caniuse-lite": "^1.0.30001769",
"capacitor-plugin-safe-area": "^4.0.0", "capacitor-plugin-safe-area": "^4.0.0",
"chrono-node": "^2.7.7", "chrono-node": "^2.7.7",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
@@ -95,7 +96,7 @@
"@vite-pwa/assets-generator": "^0.2.4", "@vite-pwa/assets-generator": "^0.2.4",
"@vitejs/plugin-react-swc": "^3.5.0", "@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.16", "autoprefixer": "^10.4.16",
"baseline-browser-mapping": "^2.9.16", "baseline-browser-mapping": "^2.9.19",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.2", "eslint-plugin-prettier": "^5.1.2",

View File

@@ -70,14 +70,17 @@ const MFAVerificationModal = ({
} }
return ( return (
<ResponsiveModal open={open} onClose={handleClose} size='sm'> <ResponsiveModal
open={open}
onClose={handleClose}
size='lg'
fullWidth={true}
title='Two-Factor Authentication'
>
<ModalClose /> <ModalClose />
<Box className='mb-4 text-center'> <Box className='mb-4 text-center'>
<Security sx={{ fontSize: 48, color: 'primary.main', mb: 2 }} /> <Security sx={{ fontSize: 48, color: 'primary.main', mb: 2 }} />
<Typography level='h4' sx={{ mb: 1 }}>
Two-Factor Authentication
</Typography>
<Typography level='body-md' sx={{ color: 'text.secondary' }}> <Typography level='body-md' sx={{ color: 'text.secondary' }}>
Enter the verification code from your authenticator app Enter the verification code from your authenticator app
</Typography> </Typography>

View File

@@ -64,15 +64,14 @@ function AcknowledgmentModal({ config }) {
<ResponsiveModal <ResponsiveModal
open={config?.isOpen} open={config?.isOpen}
onClose={config?.onClose} onClose={config?.onClose}
size='md' size='lg'
fullWidth={true}
unmountDelay={250} unmountDelay={250}
title={config?.title}
> >
<Box <Box
sx={{ p: 2, minWidth: { xs: '100%', sm: '400px' }, maxWidth: '500px' }} sx={{ p: 2, minWidth: { xs: '100%', sm: '400px' }, maxWidth: '500px' }}
> >
<Typography level='h4' mb={2} textAlign='center'>
{config?.title}
</Typography>
<Typography <Typography
level='body-md' level='body-md'

View File

@@ -317,8 +317,10 @@ function BackupRestoreModal({ isOpen, onClose, showNotification }) {
<ResponsiveModal <ResponsiveModal
open={isOpen} open={isOpen}
onClose={handleClose} onClose={handleClose}
size='md' size='lg'
fullWidth={true}
unmountDelay={250} unmountDelay={250}
title='🔄 Backup & Restore'
> >
{loading ? ( {loading ? (
<Box <Box
@@ -333,25 +335,19 @@ function BackupRestoreModal({ isOpen, onClose, showNotification }) {
</Typography> </Typography>
</Box> </Box>
) : ( ) : (
<> <Tabs
<Typography level='h4' mb={3}> value={activeTab}
🔄 Backup & Restore onChange={(event, newValue) => setActiveTab(newValue)}
</Typography> >
<TabList>
<Tab>Create Backup</Tab>
<Tab>Restore Backup</Tab>
</TabList>
<Tabs <TabPanel value={0}>{renderBackupTab()}</TabPanel>
value={activeTab}
onChange={(event, newValue) => setActiveTab(newValue)}
>
<TabList>
<Tab>Create Backup</Tab>
<Tab>Restore Backup</Tab>
</TabList>
<TabPanel value={0}>{renderBackupTab()}</TabPanel> <TabPanel value={1}>{renderRestoreTab()}</TabPanel>
</Tabs>
<TabPanel value={1}>{renderRestoreTab()}</TabPanel>
</Tabs>
</>
)} )}
</ResponsiveModal> </ResponsiveModal>
) )

View File

@@ -1,13 +1,13 @@
import { import {
Box, Box,
Button, Button,
FormControl, FormControl,
FormHelperText, FormHelperText,
Input, Input,
Option, Option,
Select, Select,
Textarea, Textarea,
Typography, Typography,
} from '@mui/joy' } from '@mui/joy'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useResponsiveModal } from '../../../hooks/useResponsiveModal' import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
@@ -60,10 +60,13 @@ function CreateThingModal({ isOpen, onClose, onSave, currentThing }) {
} }
return ( return (
<ResponsiveModal open={isOpen} onClose={onClose}> <ResponsiveModal
<Typography level='h4'> open={isOpen}
{currentThing?.id ? 'Edit' : 'Create'} Thing onClose={onClose}
</Typography> size='lg'
fullWidth={true}
title={`${currentThing?.id ? 'Edit' : 'Create'} Thing`}
>
<FormControl> <FormControl>
<Typography>Name</Typography> <Typography>Name</Typography>
<Textarea <Textarea

View File

@@ -72,8 +72,13 @@ function DateModal({ isOpen, onClose, onSave, current, title }) {
} }
return ( return (
<ResponsiveModal open={isOpen} onClose={onClose}> <ResponsiveModal
<Typography variant='h4'>{title}</Typography> open={isOpen}
onClose={onClose}
size='lg'
fullWidth={true}
title={title}
>
<Input <Input
sx={{ mt: 3 }} sx={{ mt: 3 }}
type='date' type='date'

View File

@@ -1,10 +1,10 @@
import { import {
Box, Box,
Button, Button,
FormControl, FormControl,
FormHelperText, FormHelperText,
Input, Input,
Typography, Typography,
} from '@mui/joy' } from '@mui/joy'
import { useState } from 'react' import { useState } from 'react'
import { useResponsiveModal } from '../../../hooks/useResponsiveModal' import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
@@ -40,9 +40,13 @@ function EditThingStateModal({ isOpen, onClose, onSave, currentThing }) {
} }
return ( return (
<ResponsiveModal open={isOpen} onClose={onClose}> <ResponsiveModal
<Typography level='h4'>Update state</Typography> open={isOpen}
onClose={onClose}
size='lg'
fullWidth={true}
title='Update state'
>
<FormControl> <FormControl>
<Typography>Value</Typography> <Typography>Value</Typography>
<Input <Input

View File

@@ -1,11 +1,11 @@
import { import {
Avatar, Avatar,
Box, Box,
Button, Button,
FormControl, FormControl,
FormLabel, FormLabel,
Grid, Grid,
Typography, Typography,
} from '@mui/joy' } from '@mui/joy'
import { useResponsiveModal } from '../../../hooks/useResponsiveModal' import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
import { getTextColorFromBackgroundColor } from '../../../utils/Colors' import { getTextColorFromBackgroundColor } from '../../../utils/Colors'
@@ -29,12 +29,11 @@ const IconPickerModal = ({
<ResponsiveModal <ResponsiveModal
open={isOpen} open={isOpen}
onClose={onClose} onClose={onClose}
size='sm' size='lg'
fullWidth={true}
unmountDelay={250} unmountDelay={250}
title='Choose Project Icon'
> >
<Typography level='h4' mb={2}>
Choose Project Icon
</Typography>
<FormControl> <FormControl>
<FormLabel>Available Icons</FormLabel> <FormLabel>Available Icons</FormLabel>

View File

@@ -91,75 +91,80 @@ function LabelModal({ isOpen, onClose, label }) {
} }
return ( return (
<ResponsiveModal open={isOpen} onClose={onClose}> <ResponsiveModal
<Typography level='title-md' mb={1}> open={isOpen}
{label ? 'Edit Label' : 'Add Label'} onClose={onClose}
</Typography> size='lg'
fullWidth={true}
title={label ? 'Edit Label' : 'Add Label'}
footer={
<Box display='flex' justifyContent='space-around' mt={1}>
<Button size='lg' onClick={handleSave} fullWidth sx={{ mr: 1 }}>
{label ? 'Save Changes' : 'Add Label'}
</Button>
<Button size='lg' onClick={onClose} variant='outlined'>
Cancel
</Button>
</Box>
}
>
<Box>
<FormControl>
<Typography gutterBottom level='body-sm' alignSelf='start'>
Name
</Typography>
<Input
fullWidth
id='labelName'
value={labelName}
onChange={e => setLabelName(e.target.value)}
/>
</FormControl>
<FormControl> <FormControl>
<Typography gutterBottom level='body-sm' alignSelf='start'> <Typography gutterBottom level='body-sm' alignSelf='start'>
Name Color
</Typography> </Typography>
<Input <Select
fullWidth value={color}
id='labelName' onChange={(e, value) => value && setColor(value)}
value={labelName} renderValue={selected => (
onChange={e => setLabelName(e.target.value)} <Typography
/> startDecorator={
</FormControl> <Box
className='size-4'
borderRadius={10}
sx={{ background: selected.value }}
/>
}
>
{selected.label}
</Typography>
)}
>
{LABEL_COLORS.map(val => (
<Option key={val.value} value={val.value}>
<Box className='flex items-center justify-between'>
<Box
width={20}
height={20}
borderRadius={10}
sx={{ background: val.value }}
/>
<Typography sx={{ ml: 1 }} variant='caption'>
{val.name}
</Typography>
</Box>
</Option>
))}
</Select>
</FormControl>
<FormControl> {error && (
<Typography gutterBottom level='body-sm' alignSelf='start'> <Typography color='warning' level='body-sm'>
Color {error}
</Typography> </Typography>
<Select )}
value={color}
onChange={(e, value) => value && setColor(value)}
renderValue={selected => (
<Typography
startDecorator={
<Box
className='size-4'
borderRadius={10}
sx={{ background: selected.value }}
/>
}
>
{selected.label}
</Typography>
)}
>
{LABEL_COLORS.map(val => (
<Option key={val.value} value={val.value}>
<Box className='flex items-center justify-between'>
<Box
width={20}
height={20}
borderRadius={10}
sx={{ background: val.value }}
/>
<Typography sx={{ ml: 1 }} variant='caption'>
{val.name}
</Typography>
</Box>
</Option>
))}
</Select>
</FormControl>
{error && (
<Typography color='warning' level='body-sm'>
{error}
</Typography>
)}
<Box display='flex' justifyContent='space-around' mt={1}>
<Button size='lg' onClick={handleSave} fullWidth sx={{ mr: 1 }}>
{label ? 'Save Changes' : 'Add Label'}
</Button>
<Button size='lg' onClick={onClose} variant='outlined'>
Cancel
</Button>
</Box> </Box>
</ResponsiveModal> </ResponsiveModal>
) )

View File

@@ -1,12 +1,12 @@
import { import {
Alert, Alert,
Box, Box,
Button, Button,
FormControl, FormControl,
FormLabel, FormLabel,
Switch, Switch,
Textarea, Textarea,
Typography, Typography,
} from '@mui/joy' } from '@mui/joy'
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import KeyboardShortcutHint from '../../../components/common/KeyboardShortcutHint' import KeyboardShortcutHint from '../../../components/common/KeyboardShortcutHint'
@@ -104,13 +104,11 @@ function NudgeModal({ config }) {
<ResponsiveModal <ResponsiveModal
open={config?.isOpen} open={config?.isOpen}
onClose={config?.onClose} onClose={config?.onClose}
size='md' size='lg'
fullWidth={true}
unmountDelay={250} unmountDelay={250}
title='Send Nudge'
> >
<Typography level='h4' mb={2}>
Send Nudge
</Typography>
<Typography level='body-md' mb={2}> <Typography level='body-md' mb={2}>
Send a gentle reminder to the assignee about this task. You can Send a gentle reminder to the assignee about this task. You can
customize the message and choose who gets notified. customize the message and choose who gets notified.

View File

@@ -1,10 +1,10 @@
import { import {
Box, Box,
Button, Button,
FormControl, FormControl,
FormHelperText, FormHelperText,
Input, Input,
Typography, Typography,
} from '@mui/joy' } from '@mui/joy'
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import { useResponsiveModal } from '../../../hooks/useResponsiveModal' import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
@@ -41,11 +41,13 @@ function PassowrdChangeModal({ isOpen, onClose }) {
} }
return ( return (
<ResponsiveModal open={isOpen} onClose={onClose}> <ResponsiveModal
<Typography level='h4' mb={1}> open={isOpen}
Change Password onClose={onClose}
</Typography> size='lg'
fullWidth={true}
title='Change Password'
>
<Typography level='body-md' gutterBottom> <Typography level='body-md' gutterBottom>
Please enter your new password. Please enter your new password.
</Typography> </Typography>

View File

@@ -1,4 +1,4 @@
import { Box, Button, Option, Select, Typography } from '@mui/joy' import { Box, Button, Option, Select } from '@mui/joy'
import React from 'react' import React from 'react'
import { useResponsiveModal } from '../../../hooks/useResponsiveModal' import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
@@ -20,8 +20,13 @@ function SelectModal({
} }
return ( return (
<ResponsiveModal open={isOpen} onClose={onClose}> <ResponsiveModal
<Typography variant='h4'>{title}</Typography> open={isOpen}
onClose={onClose}
size='lg'
fullWidth={true}
title={title}
>
<Select placeholder={placeholder}> <Select placeholder={placeholder}>
{options.map((item, index) => ( {options.map((item, index) => (
<Option <Option

View File

@@ -1,4 +1,4 @@
import { Box, Button, Textarea, Typography } from '@mui/joy' import { Box, Button, Textarea } from '@mui/joy'
import { useState } from 'react' import { useState } from 'react'
import { useResponsiveModal } from '../../../hooks/useResponsiveModal' import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
@@ -21,8 +21,13 @@ function TextModal({
} }
return ( return (
<ResponsiveModal open={isOpen} onClose={onClose}> <ResponsiveModal
<Typography variant='h4'>{title}</Typography> open={isOpen}
onClose={onClose}
size='lg'
fullWidth={true}
title={title}
>
<Textarea <Textarea
placeholder='Type in here…' placeholder='Type in here…'
value={text} value={text}

View File

@@ -344,7 +344,8 @@ function UserDeletionModal({ isOpen, onClose, userProfile }) {
<ResponsiveModal <ResponsiveModal
open={isOpen} open={isOpen}
onClose={() => handleClose(false)} onClose={() => handleClose(false)}
size='md' size='lg'
fullWidth={true}
title='Delete Account' title='Delete Account'
> >
{loading && step === 1 ? ( {loading && step === 1 ? (

View File

@@ -5,10 +5,13 @@ const UserModal = ({ isOpen, performers = [], onSelect, onClose }) => {
const { ResponsiveModal } = useResponsiveModal() const { ResponsiveModal } = useResponsiveModal()
return ( return (
<ResponsiveModal open={isOpen} onClose={onClose} size='md' fullWidth> <ResponsiveModal
<Typography level='h4' sx={{ mb: 2 }}> open={isOpen}
Select User onClose={onClose}
</Typography> size='lg'
fullWidth={true}
title='Select User'
>
<List sx={{ mb: 2 }}> <List sx={{ mb: 2 }}>
{performers.map(user => ( {performers.map(user => (
<ListItem <ListItem

View File

@@ -127,6 +127,44 @@ const NavBar = () => {
} }
}) })
}, []) }, [])
const getMenuIcon = () => {
const menuRounded = (
<IconButton size='md' variant='plain' onClick={() => setDrawerOpen(true)}>
<MenuRounded />
</IconButton>
)
if (!Capacitor.isNativePlatform()) {
return menuRounded
}
if (
['/chores', '/'].includes(location.pathname) &&
!searchParams.get('filter')
) {
return menuRounded
}
return (
<IconButton
size='md'
variant='plain'
onClick={() => {
if (location.pathname === '/chores') {
// Navigate back to calendar view
navigate('/')
} else {
// Default back navigation
navigate(-1)
}
}}
title={
searchParams.get('from') === 'calendar' ? 'Back to Calendar' : 'Back'
}
>
<ArrowBack />
</IconButton>
)
}
if ( if (
[ [
'/signup', '/signup',
@@ -172,37 +210,7 @@ const NavBar = () => {
backgroundColor: 'var(--joy-palette-background-body)', backgroundColor: 'var(--joy-palette-background-body)',
}} }}
> >
{['/chores', '/'].includes(location.pathname) && {getMenuIcon()}
!searchParams.get('filter') ? (
<IconButton
size='md'
variant='plain'
onClick={() => setDrawerOpen(true)}
>
<MenuRounded />
</IconButton>
) : (
<IconButton
size='md'
variant='plain'
onClick={() => {
if (location.pathname === '/chores') {
// Navigate back to calendar view
navigate('/')
} else {
// Default back navigation
navigate(-1)
}
}}
title={
searchParams.get('from') === 'calendar'
? 'Back to Calendar'
: 'Back'
}
>
<ArrowBack />
</IconButton>
)}
<Box className='flex-1' /> <Box className='flex-1' />
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}> <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<UserProfileAvatar /> <UserProfileAvatar />