Enhance modal components with consistent button sizes and improve calendar styles for better responsiveness and layout

This commit is contained in:
Mo Tarbin
2025-08-23 17:15:18 -04:00
parent 65f84b116f
commit 0f125cd63f
21 changed files with 346 additions and 30 deletions

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<!-- <link rel="icon" type="image/svg+xml" href="/logo.svg" /> -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- <title>FE Template</title> -->
<!-- <title>Donetick</title> -->
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />

View File

@@ -57,7 +57,7 @@ const PageTransition = ({ children }) => {
const previousLevel = getRouteLevel(prevLocation.current.pathname)
// Determine if we're navigating back (to a higher level in hierarchy)
isNavigatingBack.current = currentLevel < previousLevel
// isNavigatingBack.current = currentLevel < previousLevel
window.scrollTo({ top: 0, left: 0, behavior: 'instant' })
prevLocation.current = location

View File

@@ -1,6 +1,5 @@
import App from '@/App'
import ChoreEdit from '@/views/ChoreEdit/ChoreEdit'
import ChoresOverview from '@/views/ChoresOverview'
import Error from '@/views/Error'
import Settings from '@/views/Settings/Settings'
import { Capacitor } from '@capacitor/core'
@@ -12,6 +11,7 @@ import LoginView from '../views/Authorization/LoginView'
import SignupView from '../views/Authorization/Signup'
import UpdatePasswordView from '../views/Authorization/UpdatePasswordView'
import ChoreView from '../views/ChoreEdit/ChoreView'
import ArchivedTasks from '../views/Chores/ArchivedTasks'
import MyChores from '../views/Chores/MyChores'
import JoinCircleView from '../views/Circles/JoinCircle'
import ChoreHistory from '../views/History/ChoreHistory'
@@ -53,7 +53,11 @@ const Router = createBrowserRouter([
},
{
path: '/chores',
element: <ChoresOverview />,
element: <MyChores />,
},
{
path: '/archived',
element: <ArchivedTasks />,
},
{
path: '/chores/:choreId/edit',
@@ -99,6 +103,7 @@ const Router = createBrowserRouter([
path: '/signup',
element: <SignupView />,
},
{
path: '/auth/:provider',
element: <AuthenticationLoading />,
@@ -151,6 +156,7 @@ const Router = createBrowserRouter([
path: 'labels/',
element: <LabelView />,
},
{
path: '*',
element: <NotFound />,

View File

@@ -93,7 +93,9 @@ export async function Fetch(url, options) {
if (response.ok) {
const data = await response.clone().json()
const optionsHash = murmurhash.v3(JSON.stringify(options))
const optionWithoutToken = { ...options }
delete optionWithoutToken.headers.Authorization
const optionsHash = murmurhash.v3(JSON.stringify(optionWithoutToken))
await localStore.saveToCache(fullURL + optionsHash, data)
networkManager.setOnline()
} else if (response.status === 401) {

View File

@@ -137,7 +137,8 @@ const ChoreView = () => {
}`,
subtext: ` Last: ${
chore.lastCompletedDate
? performers.find(p => p.id === chore.lastCompletedBy)?.displayName
? performers.find(p => p.userId === chore.lastCompletedBy)
?.displayName
: '--'
}`,
},
@@ -165,7 +166,8 @@ const ChoreView = () => {
icon: <Person />,
title: 'Details',
subtext: `Created By: ${
performers.find(p => p.id === chore.createdBy)?.displayName || 'N/A'
performers.find(p => p.userId === chore.createdBy)?.displayName ||
'N/A'
}`,
},
]

View File

@@ -68,6 +68,7 @@ function EditHistoryModal({ config, historyRecord }) {
{/* 3 button save , cancel and delete */}
<Box display={'flex'} justifyContent={'space-around'} mt={1}>
<Button
size='lg'
onClick={() =>
config.onSave({
id: historyRecord.id,
@@ -81,10 +82,11 @@ function EditHistoryModal({ config, historyRecord }) {
>
Save
</Button>
<Button onClick={config.onClose} variant='outlined'>
<Button size='' onClick={config.onClose} variant='outlined'>
Cancel
</Button>
<Button
size='lg'
onClick={() => {
setIsDeleteModalOpen(true)
}}

View File

@@ -240,10 +240,11 @@ function BackupRestoreModal({ isOpen, onClose, showNotification }) {
)}
<Box display='flex' justifyContent='space-between' gap={2}>
<Button variant='outlined' onClick={handleClose} fullWidth>
<Button size='lg' variant='outlined' onClick={handleClose} fullWidth>
Cancel
</Button>
<Button
size='lg'
color='primary'
onClick={handleCreateBackup}
loading={loading}
@@ -295,10 +296,11 @@ function BackupRestoreModal({ isOpen, onClose, showNotification }) {
)}
<Box display='flex' justifyContent='space-between' gap={2}>
<Button variant='outlined' onClick={handleClose} fullWidth>
<Button size='lg' variant='outlined' onClick={handleClose} fullWidth>
Cancel
</Button>
<Button
size='lg'
color='warning'
onClick={handleRestore}
loading={loading}

View File

@@ -86,6 +86,7 @@ function ConfirmationModal({ config }) {
<Box display={'flex'} justifyContent={'space-around'} mt={1} gap={1}>
<Button
size='lg'
onClick={() => {
handleAction(true)
}}
@@ -99,6 +100,7 @@ function ConfirmationModal({ config }) {
</Button>
<Button
size='lg'
onClick={() => {
handleAction(false)
}}

View File

@@ -10,6 +10,7 @@ import {
Typography,
} from '@mui/joy'
import { useEffect, useState } from 'react'
import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
function CreateThingModal({ isOpen, onClose, onSave, currentThing }) {
const { ResponsiveModal } = useResponsiveModal()
@@ -125,10 +126,10 @@ function CreateThingModal({ isOpen, onClose, onSave, currentThing }) {
)}
<Box display={'flex'} justifyContent={'space-around'} mt={1}>
<Button onClick={handleSave} fullWidth sx={{ mr: 1 }}>
<Button size='lg' onClick={handleSave} fullWidth sx={{ mr: 1 }}>
{currentThing?.id ? 'Update' : 'Create'}
</Button>
<Button onClick={onClose} variant='outlined'>
<Button size='lg' onClick={onClose} variant='outlined'>
{currentThing?.id ? 'Cancel' : 'Close'}
</Button>
</Box>

View File

@@ -23,11 +23,11 @@ function DateModal({ isOpen, onClose, onSave, current, title }) {
value={date}
onChange={e => setDate(e.target.value)}
/>
<Box display={'flex'} justifyContent={'space-around'} mt={1}>
<Button onClick={handleSave} fullWidth sx={{ mr: 1 }}>
<Box display={'flex'} justifyContent={'space-around'} mt={6}>
<Button size='lg' onClick={handleSave} fullWidth sx={{ mr: 1 }}>
Save
</Button>
<Button onClick={onClose} variant='outlined'>
<Button size='lg' onClick={onClose} variant='outlined'>
Cancel
</Button>
</Box>

View File

@@ -55,10 +55,10 @@ function EditThingStateModal({ isOpen, onClose, onSave, currentThing }) {
</FormControl>
<Box display={'flex'} justifyContent={'space-around'} mt={1}>
<Button onClick={handleSave} fullWidth sx={{ mr: 1 }}>
<Button size='lg' onClick={handleSave} fullWidth sx={{ mr: 1 }}>
{currentThing?.id ? 'Update' : 'Create'}
</Button>
<Button onClick={onClose} variant='outlined'>
<Button size='lg' onClick={onClose} variant='outlined'>
{currentThing?.id ? 'Cancel' : 'Close'}
</Button>
</Box>

View File

@@ -154,10 +154,10 @@ function LabelModal({ isOpen, onClose, label }) {
)}
<Box display='flex' justifyContent='space-around' mt={1}>
<Button onClick={handleSave} fullWidth sx={{ mr: 1 }}>
<Button size='lg' onClick={handleSave} fullWidth sx={{ mr: 1 }}>
{label ? 'Save Changes' : 'Add Label'}
</Button>
<Button onClick={onClose} variant='outlined'>
<Button size='lg' onClick={onClose} variant='outlined'>
Cancel
</Button>
</Box>

View File

@@ -92,6 +92,7 @@ function PassowrdChangeModal({ isOpen, onClose }) {
</FormControl>
<Box display={'flex'} justifyContent={'space-around'} mt={1}>
<Button
size='lg'
disabled={passwordError != null}
onClick={() => {
handleAction(true)
@@ -102,6 +103,7 @@ function PassowrdChangeModal({ isOpen, onClose }) {
Change Password
</Button>
<Button
size='lg'
onClick={() => {
handleAction(false)
}}

View File

@@ -37,10 +37,10 @@ function SelectModal({
</Select>
<Box display={'flex'} justifyContent={'space-around'} mt={1}>
<Button onClick={handleSave} fullWidth sx={{ mr: 1 }}>
<Button size='lg' onClick={handleSave} fullWidth sx={{ mr: 1 }}>
Save
</Button>
<Button onClick={onClose} variant='outlined'>
<Button size='lg' onClick={onClose} variant='outlined'>
Cancel
</Button>
</Box>

View File

@@ -33,10 +33,10 @@ function TextModal({
/>
<Box display={'flex'} justifyContent={'space-around'} mt={1}>
<Button onClick={handleSave} fullWidth sx={{ mr: 1 }}>
<Button size='lg' onClick={handleSave} fullWidth sx={{ mr: 1 }}>
{okText ? okText : 'Save'}
</Button>
<Button onClick={onClose} variant='outlined'>
<Button size='lg' onClick={onClose} variant='outlined'>
{cancelText ? cancelText : 'Cancel'}
</Button>
</Box>

View File

@@ -36,7 +36,7 @@ const UserModal = ({ isOpen, performers = [], onSelect, onClose }) => {
))}
</List>
<Box sx={{ display: 'flex', justifyContent: 'flex-end', gap: 1 }}>
<Button variant='outlined' color='neutral' onClick={onClose}>
<Button size='lg' variant='outlined' color='neutral' onClick={onClose}>
Cancel
</Button>
</Box>

View File

@@ -95,6 +95,7 @@ function WriteNFCModal({ config }) {
</ListItem>
<Box display={'flex'} justifyContent={'space-around'} mt={1}>
<Button
size='lg'
onClick={() => writeToNFC(getURL())}
fullWidth
sx={{ mr: 1 }}
@@ -102,7 +103,7 @@ function WriteNFCModal({ config }) {
>
Write NFC
</Button>
<Button onClick={requestNFCAccess} variant='outlined'>
<Button size='lg' onClick={requestNFCAccess} variant='outlined'>
Request Access
</Button>
</Box>

View File

@@ -215,6 +215,7 @@ function RedeemPointsModal({ config }) {
{/* Action Buttons */}
<Stack direction='row' spacing={2}>
<Button
size='lg'
onClick={config?.onClose}
variant='outlined'
color='neutral'
@@ -228,6 +229,7 @@ function RedeemPointsModal({ config }) {
Cancel
</Button>
<Button
size='lg'
onClick={() =>
config?.onSave({
points: Number(points),

View File

@@ -759,7 +759,12 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => {
gap: 1,
}}
>
<Button variant='outlined' color='neutral' onClick={handleCloseModal}>
<Button
size='lg'
variant='outlined'
color='neutral'
onClick={handleCloseModal}
>
Cancel
{showKeyboardShortcuts && (
<KeyboardShortcutHint
@@ -769,7 +774,7 @@ const TaskInput = ({ autoFocus, onChoreUpdate, isModalOpen, onClose }) => {
/>
)}
</Button>
<Button variant='solid' color='primary' onClick={createChore}>
<Button size='lg' variant='solid' color='primary' onClick={createChore}>
Create
{showKeyboardShortcuts && (
<KeyboardShortcutHint shortcut='Enter' sx={{ ml: 1 }} />

View File

@@ -20,6 +20,20 @@
gap: 2px;
}
/* Year view - force 3 columns layout */
.react-calendar__year-view__months {
display: grid !important;
grid-template-columns: repeat(3, 1fr) !important;
gap: 8px;
}
/* Decade view - force 3 columns layout for years */
.react-calendar__decade-view__years {
display: grid !important;
grid-template-columns: repeat(3, 1fr) !important;
gap: 8px;
}
/* Mobile responsiveness */
@media (max-width: 480px) {
.react-calendar {
@@ -28,10 +42,7 @@
}
.react-calendar__tile {
min-width: 28px;
max-width: 42px;
min-height: 28px;
max-height: 42px;
font-size: clamp(0.7rem, 3vw, 0.8rem);
}
@@ -39,6 +50,18 @@
padding: 0.25em;
font-size: 0.75rem;
}
/* Ensure year view stays 3 columns on mobile */
.react-calendar__year-view__months {
grid-template-columns: repeat(3, 1fr) !important;
gap: 4px;
}
/* Ensure decade view stays 3 columns on mobile */
.react-calendar__decade-view__years {
grid-template-columns: repeat(3, 1fr) !important;
gap: 4px;
}
}
/* Add styles for weekday names */

View File

@@ -0,0 +1,266 @@
.react-calendar {
width: 100%;
max-width: min(400px, 95vw);
font-family: 'Roboto', sans-serif;
background-color: transparent;
border: none;
margin: 0 auto;
}
/* Desktop dual calendar layout */
@media (min-width: 768px) {
.react-calendar {
max-width: 350px;
min-width: 350px;
}
}
/* Hide navigation on secondary calendar */
[data-secondary='true'] .react-calendar__navigation {
display: none;
}
/* Secondary calendar header styling */
.secondary-calendar-header {
height: 30px;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: center;
}
/* Force proper 7-day grid layout */
.react-calendar__month-view__days {
display: grid !important;
grid-template-columns: repeat(7, 1fr) !important;
gap: 2px;
}
.react-calendar__month-view__weekdays {
display: grid !important;
grid-template-columns: repeat(7, 1fr) !important;
gap: 2px;
}
/* Mobile responsiveness */
@media (max-width: 480px) {
.react-calendar {
max-width: 100%;
font-size: 0.875rem;
}
.react-calendar__tile {
min-width: 28px;
max-width: 42px;
min-height: 28px;
max-height: 42px;
font-size: clamp(0.7rem, 3vw, 0.8rem);
}
.react-calendar__month-view__weekdays__weekday {
padding: 0.25em;
font-size: 0.75rem;
}
}
/* Add styles for weekday names */
.react-calendar__month-view__weekdays__weekday {
text-decoration: none;
font-weight: 500;
padding: 0.5em;
color: #666;
font-size: 0.875rem;
}
.react-calendar__month-view__weekdays__weekday abbr {
text-decoration: none;
border-bottom: none;
cursor: default;
}
/* reduce the naivation bar height */
.react-calendar__navigation {
height: 30px;
margin-bottom: 10px;
}
.react-calendar__tile {
padding: 0;
width: 100%;
aspect-ratio: 1;
text-align: center;
border-radius: 50%;
transition: all 0.2s ease-in-out;
position: relative;
font-size: clamp(0.75rem, 2vw, 0.875rem);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
border: none;
background: transparent;
}
.react-calendar__tile:enabled:hover {
background-color: var(--joy-palette-primary-softBg);
color: var(--joy-palette-primary-solidColor);
}
/* Active/Selected date */
.react-calendar__tile--active {
background-color: var(--joy-palette-primary-solidBg) !important;
color: var(--joy-palette-primary-solidColor) !important;
border: none !important;
box-shadow: 0 2px 4px var(--joy-palette-primary-shadowRing);
}
.react-calendar__tile--active:hover {
background-color: var(--joy-palette-primary-solidHoverBg) !important;
}
/* Today's date */
.react-calendar__tile--now {
background-color: transparent !important;
border: 1.5px solid var(--joy-palette-primary-solidBg) !important;
color: var(--joy-palette-primary-solidBg) !important;
font-weight: 500;
}
.react-calendar__tile--now:enabled:hover {
background-color: var(--joy-palette-primary-softBg) !important;
}
/* Disabled dates */
.react-calendar__tile:disabled {
color: var(--joy-palette-text-tertiary);
cursor: not-allowed;
background: transparent;
}
/* Weekend dates */
.react-calendar__month-view__days__day--weekend {
color: var(--joy-palette-text-secondary);
}
/* Neighboring month dates */
.react-calendar__month-view__days__day--neighboringMonth {
color: var(--joy-palette-text-tertiary);
}
.dot-container {
display: flex;
justify-content: center;
align-items: center;
height: 12px;
flex-wrap: wrap;
position: absolute;
bottom: 2px;
left: 50%;
transform: translateX(-50%);
min-height: 12px;
width: 100%;
}
.dot {
width: clamp(3px, 0.25em, 5px);
height: clamp(3px, 0.25em, 5px);
border-radius: 50%;
margin: 0 0.5px;
}
.dot-with-line {
width: clamp(12px, 1em, 16px);
height: clamp(3px, 0.25em, 5px);
border-radius: clamp(1.5px, 0.125em, 2.5px);
margin: 0 0.5px;
}
.dot-empty {
background-color: transparent;
}
.chore-tooltip {
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
background-color: #333;
color: white;
padding: 5px 10px;
border-radius: 4px;
z-index: 10;
visibility: hidden;
opacity: 0;
transition:
opacity 0.2s ease,
visibility 0.2s ease;
}
/* Year view month tiles - override circular styling */
.react-calendar__year-view__months__month {
border-radius: 8px !important; /* Override the 50% circular radius */
padding: 12px 8px !important;
min-width: 60px !important;
max-height: 50px !important;
font-size: 0.875rem;
font-weight: 500;
}
.react-calendar__year-view__months__month:enabled:hover {
background-color: var(--joy-palette-primary-softBg) !important;
color: var(--joy-palette-primary-solidColor) !important;
}
/* Decade view year tiles - similar fix */
.react-calendar__decade-view__years__year {
border-radius: 8px !important;
padding: 12px 8px !important;
min-width: 60px !important;
max-height: 50px !important;
font-size: 0.875rem;
font-weight: 500;
}
.react-calendar__decade-view__years__year:enabled:hover {
background-color: var(--joy-palette-primary-softBg) !important;
color: var(--joy-palette-primary-solidColor) !important;
}
/* Century view decade tiles */
.react-calendar__century-view__decades__decade {
border-radius: 8px !important;
padding: 12px 8px !important;
min-width: 60px !important;
max-height: 50px !important;
font-size: 0.875rem;
font-weight: 500;
}
.react-calendar__century-view__decades__decade:enabled:hover {
background-color: var(--joy-palette-primary-softBg) !important;
color: var(--joy-palette-primary-solidColor) !important;
}
/* Navigation buttons (month/year selectors and arrows) */
.react-calendar__navigation button {
border-radius: 8px !important; /* Override circular border-radius */
padding: 6px 12px !important;
min-width: auto !important;
max-height: none !important;
font-weight: 500;
background-color: transparent;
border: 1px solid transparent;
transition: all 0.2s ease-in-out;
color: var(--joy-palette-text-primary);
}
.react-calendar__navigation button:enabled:hover {
background-color: var(--joy-palette-primary-softBg) !important;
color: var(--joy-palette-primary-solidColor) !important;
border-color: var(--joy-palette-primary-outlinedBorder);
}
.react-calendar__navigation button:enabled:focus {
background-color: var(--joy-palette-primary-softActiveBg) !important;
outline: none;
box-shadow: 0 0 0 2px var(--joy-palette-primary-outlinedBorder);
}