-
- {t('profile.description')}
-
+
{t('profile.description')}
{
maxWidth: 400,
}}
>
-
+
-
{
setShowCropper(false)
setSelectedFile(null)
}}
- >
-
-
-
-
-
-
-
-
-
-
+ },
+ }}
+ primary={{
+ label: t('profile.save'),
+ loading: isUploading,
+ onClick: handleCropSave,
+ }}
+ />
+ }
+ >
+
+
+
+
{t('profile.displayName')}
diff --git a/src/views/components/AddTaskModal.jsx b/src/views/components/AddTaskModal.jsx
index 65f6a40..63b1b34 100644
--- a/src/views/components/AddTaskModal.jsx
+++ b/src/views/components/AddTaskModal.jsx
@@ -24,6 +24,7 @@ import {
import SmartTaskTitleInput from './SmartTaskTitleInput'
import KeyboardShortcutHint from '../../components/common/KeyboardShortcutHint'
+import ModalActions from '../../components/common/ModalActions'
import { useDocumentScanner } from '../../hooks/useDocumentScanner'
import { localAIService } from '../../service/LocalAIService'
import { voiceInputService } from '../../service/VoiceInputService'
@@ -831,16 +832,8 @@ const TaskInput = ({ onChoreUpdate, isModalOpen, onClose, initialMode }) => {
fullWidth={true}
title='Create new task'
footer={
-
+
+
}
>
{!showScan && !showVoice && (
diff --git a/src/views/components/DueDatePickerField.jsx b/src/views/components/DueDatePickerField.jsx
index af6ba3a..7ef048d 100644
--- a/src/views/components/DueDatePickerField.jsx
+++ b/src/views/components/DueDatePickerField.jsx
@@ -24,6 +24,7 @@ import {
import moment from 'moment'
import { useEffect, useMemo, useState } from 'react'
import Calendar from 'react-calendar'
+import ModalActions from '../../components/common/ModalActions'
import { useLocalization } from '../../contexts/LocalizationContext'
import { useResponsiveModal } from '../../hooks/useResponsiveModal'
@@ -202,6 +203,7 @@ const DueDatePickerField = ({
{hasDueDate && onClear && (
- {hasDueDate && (
- {
- onClear?.()
- setIsOpen(false)
- }}
- sx={{ mr: 'auto' }}
- >
- Remove
-
- )}
- setIsOpen(false)}
- >
- Cancel
-
-
- Apply
-
-
+
{
+ onClear?.()
+ setIsOpen(false)
+ },
+ }
+ : undefined
+ }
+ secondary={{ label: 'Cancel', onClick: () => setIsOpen(false) }}
+ primary={{ label: 'Apply', onClick: handleSave }}
+ />
}
>
diff --git a/src/views/components/NotificationPickerField.jsx b/src/views/components/NotificationPickerField.jsx
index cb51134..f78ff05 100644
--- a/src/views/components/NotificationPickerField.jsx
+++ b/src/views/components/NotificationPickerField.jsx
@@ -1,6 +1,7 @@
import { Close, NotificationsNone } from '@mui/icons-material'
import { Box, Button, IconButton, Typography } from '@mui/joy'
import { useEffect, useRef, useState } from 'react'
+import ModalActions from '../../components/common/ModalActions'
import NotificationTemplate from '../../components/NotificationTemplate'
import { useResponsiveModal } from '../../hooks/useResponsiveModal'
@@ -11,8 +12,7 @@ const getDisplayLabel = templates => {
const n = templates[0]
const numericValue = Number(n.value)
if (numericValue === 0) return 'On due date'
- const unitName =
- n.unit === 'm' ? 'min' : n.unit === 'h' ? 'hr' : 'day'
+ const unitName = n.unit === 'm' ? 'min' : n.unit === 'h' ? 'hr' : 'day'
const absValue = Math.abs(numericValue)
const plural = absValue !== 1 ? 's' : ''
return `${absValue} ${unitName}${plural} ${numericValue < 0 ? 'before' : 'after'}`
@@ -48,33 +48,22 @@ const NotificationPickerField = ({
}
const footer = (
-
- {hasNotifications && (
- {
- onClear?.()
- setIsOpen(false)
- }}
- sx={{ mr: 'auto' }}
- >
- Remove all
-
- )}
- setIsOpen(false)}
- >
- Cancel
-
-
- Apply
-
-
+ {
+ onClear?.()
+ setIsOpen(false)
+ },
+ }
+ : undefined
+ }
+ secondary={{ label: 'Cancel', onClick: () => setIsOpen(false) }}
+ primary={{ label: 'Apply', onClick: handleSave }}
+ />
)
return (
@@ -116,6 +105,7 @@ const NotificationPickerField = ({
{hasNotifications && onClear && (
*/}
-
-
- Pending actions
-
+
+ }
+ >
{commands.length} action{commands.length > 1 ? 's' : ''} waiting to be
synced.
@@ -171,6 +184,7 @@ function PendingBadge({ commands, size = 'sm', sx = {} }) {
))}
-
-
-
-
-
- Close
-
-
- Cancel all
-
-
)
diff --git a/src/views/components/PhotoTaskModal.jsx b/src/views/components/PhotoTaskModal.jsx
index bc63010..67a1010 100644
--- a/src/views/components/PhotoTaskModal.jsx
+++ b/src/views/components/PhotoTaskModal.jsx
@@ -2,7 +2,6 @@ import {
ArrowBack,
CameraAlt,
CheckCircle,
- Close,
DocumentScanner,
PhotoCamera,
Replay,
@@ -12,12 +11,12 @@ import {
Box,
Button,
CircularProgress,
- IconButton,
LinearProgress,
Typography,
} from '@mui/joy'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useDocumentScanner } from '../../hooks/useDocumentScanner'
+import ModalActions from '../../components/common/ModalActions'
import { useResponsiveModal } from '../../hooks/useResponsiveModal'
import { localAIService } from '../../service/LocalAIService'
@@ -82,7 +81,10 @@ async function runNativeOCR(imageSource) {
}
const result = await Ocr.process({ image })
- return result.results.map(r => r.text).join('\n').trim()
+ return result.results
+ .map(r => r.text)
+ .join('\n')
+ .trim()
}
async function runOCR(imageSource, onProgress) {
@@ -214,7 +216,9 @@ const PhotoTaskModal = ({ open, onClose, onTaskExtracted }) => {
try {
text = await runNativeOCR(capturedImage)
} catch {
- throw new Error('Native OCR is only available on iOS and Android devices.')
+ throw new Error(
+ 'Native OCR is only available on iOS and Android devices.',
+ )
}
} else {
text = await runOCR(capturedImage, pct => setOcrProgress(pct))
@@ -231,7 +235,9 @@ const PhotoTaskModal = ({ open, onClose, onTaskExtracted }) => {
const task = await extractTaskFromOCR(text)
if (!task || !task.taskName) {
- setErrorMsg('Could not identify a task from this image. Please try a different photo.')
+ setErrorMsg(
+ 'Could not identify a task from this image. Please try a different photo.',
+ )
setPhase('error')
return
}
@@ -258,7 +264,9 @@ const PhotoTaskModal = ({ open, onClose, onTaskExtracted }) => {
const { image, cancelled, error } = await scanDocument()
if (cancelled) return
if (error || !image) {
- setErrorMsg(error ? `Scanner error: ${error}` : 'Scan cancelled or failed.')
+ setErrorMsg(
+ error ? `Scanner error: ${error}` : 'Scan cancelled or failed.',
+ )
setPhase('error')
return
}
@@ -377,7 +385,11 @@ const PhotoTaskModal = ({ open, onClose, onTaskExtracted }) => {
Reading text from image… {ocrProgress}%
-
+
>
)}
{phase === 'ocr' && ocrMethod === 'native' && (
@@ -466,7 +478,7 @@ const PhotoTaskModal = ({ open, onClose, onTaskExtracted }) => {
)}
-
+
{phase === 'capture' && (
<>
{
>
)}
-
- {!isProcessing && (
-
-
-
- )}
-
+
)
diff --git a/src/views/components/RepeatPickerField.jsx b/src/views/components/RepeatPickerField.jsx
index f4f9299..4f1c890 100644
--- a/src/views/components/RepeatPickerField.jsx
+++ b/src/views/components/RepeatPickerField.jsx
@@ -14,6 +14,7 @@ import {
} from '@mui/joy'
import moment from 'moment'
import { useEffect, useState } from 'react'
+import ModalActions from '../../components/common/ModalActions'
import { getRecurrentChipText } from '../../utils/ChoreCardHelpers'
import { useResponsiveModal } from '../../hooks/useResponsiveModal'
@@ -472,6 +473,7 @@ const RepeatPickerField = ({
{hasRepeat && onClear && (
setIsOpen(false)}
title='Repeat Schedule'
footer={
-
- {hasRepeat && (
- {
- onClear?.()
- setIsOpen(false)
- }}
- sx={{ mr: 'auto' }}
- >
- Remove
-
- )}
- setIsOpen(false)}
- >
- Cancel
-
-
- Apply
-
-
+ {
+ onClear?.()
+ setIsOpen(false)
+ },
+ }
+ : undefined
+ }
+ secondary={{ label: 'Cancel', onClick: () => setIsOpen(false) }}
+ primary={{ label: 'Apply', onClick: handleSave }}
+ />
}
>
{/* Frequency type selector */}