Merge pull request #209 from everysingletear/i18n/common
i18n: extract shared strings into the existing `common` namespace (Part of #145)
This commit is contained in:
@@ -115,5 +115,37 @@
|
|||||||
"subtitle": "We've filled in an issue with your notes and version details. Nothing has been sent yet — review it and post when you're ready.",
|
"subtitle": "We've filled in an issue with your notes and version details. Nothing has been sent yet — review it and post when you're ready.",
|
||||||
"open": "Open the issue"
|
"open": "Open the issue"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"done": "Done",
|
||||||
|
"clearAll": "Clear all",
|
||||||
|
"upload": {
|
||||||
|
"quotaTitle": "Storage Quota Exceeded",
|
||||||
|
"quotaMessage": "You have exceeded your quota for uploading files.",
|
||||||
|
"tooLargeTitle": "File Too Large",
|
||||||
|
"tooLargeMessage": "The file you are trying to upload is too large.",
|
||||||
|
"deniedTitle": "Permission Denied",
|
||||||
|
"deniedMessage": "You do not have permission to upload files."
|
||||||
|
},
|
||||||
|
"getStarted": "Get Started!",
|
||||||
|
"imageLoadFailed": "Failed to load image.",
|
||||||
|
"typeHere": "Type in here…",
|
||||||
|
"summaryOfChores": "This is a summary of your chores",
|
||||||
|
"loadingOffline": "You are offline",
|
||||||
|
"loadingOfflineSub": "This not available while offline. Please check your internet connection and try again.",
|
||||||
|
"loadingSlow": "This is taking longer than usual. There might be an issue.",
|
||||||
|
"navigateBack": "Navigate Back",
|
||||||
|
"bottomNav": "Bottom Navigation",
|
||||||
|
"profile": "Profile",
|
||||||
|
"autocompletePlaceholder": "Type here...",
|
||||||
|
"errorScreen": {
|
||||||
|
"title": "Something went wrong",
|
||||||
|
"fallback": "An unexpected error occurred. Try reloading — it usually fixes it.",
|
||||||
|
"tryAgain": "Try again",
|
||||||
|
"home": "Home",
|
||||||
|
"login": "Login",
|
||||||
|
"hideDetails": "Hide error details",
|
||||||
|
"showDetails": "Show error details",
|
||||||
|
"copyToClipboard": "Copy to clipboard",
|
||||||
|
"copied": "Error details copied to clipboard"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,11 +63,14 @@ const LogoContainer = styled(Box)({
|
|||||||
marginBottom: '24px',
|
marginBottom: '24px',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
const LoadingScreen = ({
|
const LoadingScreen = ({
|
||||||
message = 'Loading...',
|
message = null,
|
||||||
showLogo = true,
|
showLogo = true,
|
||||||
size = 'lg',
|
size = 'lg',
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
return (
|
return (
|
||||||
<LoadingContainer>
|
<LoadingContainer>
|
||||||
<LoadingContent>
|
<LoadingContent>
|
||||||
@@ -82,7 +85,7 @@ const LoadingScreen = ({
|
|||||||
mb: 1,
|
mb: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Done
|
{t('done')}
|
||||||
<span style={{ color: '#06b6d4' }}>tick</span>
|
<span style={{ color: '#06b6d4' }}>tick</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
</LogoContainer>
|
</LogoContainer>
|
||||||
@@ -96,7 +99,7 @@ const LoadingScreen = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PulsingText level='body-md'>{message}</PulsingText>
|
<PulsingText level='body-md'>{message ?? t('loading')}</PulsingText>
|
||||||
</LoadingContent>
|
</LoadingContent>
|
||||||
</LoadingContainer>
|
</LoadingContainer>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Add, Close } from '@mui/icons-material'
|
import { Add, Close } from '@mui/icons-material'
|
||||||
import { Box, Button, Chip, ChipDelete, Typography } from '@mui/joy'
|
import { Box, Button, Chip, ChipDelete, Typography } from '@mui/joy'
|
||||||
|
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
const ActiveFilterChips = ({
|
const ActiveFilterChips = ({
|
||||||
chipSize = 'md',
|
chipSize = 'md',
|
||||||
chipSx,
|
chipSx,
|
||||||
@@ -18,6 +20,7 @@ const ActiveFilterChips = ({
|
|||||||
showAddChip = false,
|
showAddChip = false,
|
||||||
totalCount,
|
totalCount,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
if (!chips.length) {
|
if (!chips.length) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -161,7 +164,7 @@ const ActiveFilterChips = ({
|
|||||||
...clearButtonSx,
|
...clearButtonSx,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Clear all
|
{t('clearAll')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
const useConfirmationModal = () => {
|
const useConfirmationModal = () => {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
const [confirmModalConfig, setConfirmModalConfig] = useState({})
|
const [confirmModalConfig, setConfirmModalConfig] = useState({})
|
||||||
|
|
||||||
const showConfirmation = (
|
const showConfirmation = (
|
||||||
message,
|
message,
|
||||||
title,
|
title,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
confirmText = 'Confirm',
|
confirmText = t('confirm'),
|
||||||
cancelText = 'Cancel',
|
cancelText = t('cancel'),
|
||||||
color = 'primary',
|
color = 'primary',
|
||||||
) => {
|
) => {
|
||||||
setConfirmModalConfig({
|
setConfirmModalConfig({
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ import { useUserProfile } from '../queries/UserQueries'
|
|||||||
import { useNotification } from '../service/NotificationProvider'
|
import { useNotification } from '../service/NotificationProvider'
|
||||||
import { apiClient } from '../utils/ApiClient'
|
import { apiClient } from '../utils/ApiClient'
|
||||||
import { isPlusAccount, resolvePhotoURL } from '../utils/Helpers'
|
import { isPlusAccount, resolvePhotoURL } from '../utils/Helpers'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
export const useFileUpload = ({
|
export const useFileUpload = ({
|
||||||
draftId,
|
draftId,
|
||||||
entityId,
|
entityId,
|
||||||
entityType = 'chore_attachment',
|
entityType = 'chore_attachment',
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
const { showError } = useNotification()
|
const { showError } = useNotification()
|
||||||
const { data: userProfile } = useUserProfile()
|
const { data: userProfile } = useUserProfile()
|
||||||
|
|
||||||
@@ -58,14 +60,14 @@ export const useFileUpload = ({
|
|||||||
|
|
||||||
if (response.status === 507) {
|
if (response.status === 507) {
|
||||||
showError({
|
showError({
|
||||||
title: 'Storage Quota Exceeded',
|
title: t('upload.quotaTitle'),
|
||||||
message: 'You have exceeded your quota for uploading files.',
|
message: t('upload.quotaMessage'),
|
||||||
})
|
})
|
||||||
return null
|
return null
|
||||||
} else if (response.status === 413) {
|
} else if (response.status === 413) {
|
||||||
showError({
|
showError({
|
||||||
title: 'File Too Large',
|
title: t('upload.tooLargeTitle'),
|
||||||
message: 'The file you are trying to upload is too large.',
|
message: t('upload.tooLargeMessage'),
|
||||||
})
|
})
|
||||||
return null
|
return null
|
||||||
} else if (response.status === 403 && !isPlusAccount(userProfile)) {
|
} else if (response.status === 403 && !isPlusAccount(userProfile)) {
|
||||||
@@ -76,8 +78,8 @@ export const useFileUpload = ({
|
|||||||
return null
|
return null
|
||||||
} else if (response.status === 403) {
|
} else if (response.status === 403) {
|
||||||
showError({
|
showError({
|
||||||
title: 'Permission Denied',
|
title: t('upload.deniedTitle'),
|
||||||
message: 'You do not have permission to upload files.',
|
message: t('upload.deniedMessage'),
|
||||||
})
|
})
|
||||||
return null
|
return null
|
||||||
} else if (!response.ok) {
|
} else if (!response.ok) {
|
||||||
@@ -105,7 +107,7 @@ export const useFileUpload = ({
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[entityType, entityId, draftId, showError, userProfile],
|
[entityType, entityId, draftId, showError, userProfile, t],
|
||||||
)
|
)
|
||||||
|
|
||||||
return { uploadFile, isPlus: isPlusAccount(userProfile) }
|
return { uploadFile, isPlus: isPlusAccount(userProfile) }
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
formatErrorReport,
|
formatErrorReport,
|
||||||
} from '../service/ErrorReportService'
|
} from '../service/ErrorReportService'
|
||||||
import ErrorReportModal from './Modals/ErrorReportModal'
|
import ErrorReportModal from './Modals/ErrorReportModal'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
const getErrorKind = error => {
|
const getErrorKind = error => {
|
||||||
if (!error)
|
if (!error)
|
||||||
@@ -55,6 +56,7 @@ const safeMessage = error => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Error = () => {
|
const Error = () => {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
const error = useRouteError()
|
const error = useRouteError()
|
||||||
const [showDetails, setShowDetails] = useState(false)
|
const [showDetails, setShowDetails] = useState(false)
|
||||||
const [copied, setCopied] = useState(false)
|
const [copied, setCopied] = useState(false)
|
||||||
@@ -192,7 +194,7 @@ const Error = () => {
|
|||||||
textAlign='center'
|
textAlign='center'
|
||||||
sx={{ mb: 1.5 }}
|
sx={{ mb: 1.5 }}
|
||||||
>
|
>
|
||||||
Something went wrong
|
{t('errorScreen.title')}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{/* Error message */}
|
{/* Error message */}
|
||||||
@@ -207,8 +209,7 @@ const Error = () => {
|
|||||||
wordBreak: 'break-word',
|
wordBreak: 'break-word',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{message ??
|
{message ?? t('errorScreen.fallback')}
|
||||||
'An unexpected error occurred. Try reloading — it usually fixes it.'}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{/* Primary CTA */}
|
{/* Primary CTA */}
|
||||||
@@ -220,7 +221,7 @@ const Error = () => {
|
|||||||
onClick={() => window.location.reload()}
|
onClick={() => window.location.reload()}
|
||||||
sx={{ width: '100%', mb: 1.5 }}
|
sx={{ width: '100%', mb: 1.5 }}
|
||||||
>
|
>
|
||||||
Try again
|
{t('errorScreen.tryAgain')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* Reporting is one tap from the failure, where the context is still
|
{/* Reporting is one tap from the failure, where the context is still
|
||||||
@@ -246,7 +247,7 @@ const Error = () => {
|
|||||||
size='lg'
|
size='lg'
|
||||||
startDecorator={<HomeRounded />}
|
startDecorator={<HomeRounded />}
|
||||||
>
|
>
|
||||||
Home
|
{t('errorScreen.home')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
component={Link}
|
component={Link}
|
||||||
@@ -255,7 +256,7 @@ const Error = () => {
|
|||||||
color='neutral'
|
color='neutral'
|
||||||
size='lg'
|
size='lg'
|
||||||
>
|
>
|
||||||
Login
|
{t('errorScreen.login')}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -293,7 +294,9 @@ const Error = () => {
|
|||||||
}
|
}
|
||||||
sx={{ mb: 1 }}
|
sx={{ mb: 1 }}
|
||||||
>
|
>
|
||||||
{showDetails ? 'Hide' : 'Show'} error details
|
{showDetails
|
||||||
|
? t('errorScreen.hideDetails')
|
||||||
|
: t('errorScreen.showDetails')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{showDetails && (
|
{showDetails && (
|
||||||
@@ -312,7 +315,7 @@ const Error = () => {
|
|||||||
color='neutral'
|
color='neutral'
|
||||||
onClick={handleCopy}
|
onClick={handleCopy}
|
||||||
sx={{ position: 'absolute', top: 8, right: 8 }}
|
sx={{ position: 'absolute', top: 8, right: 8 }}
|
||||||
title='Copy to clipboard'
|
title={t('errorScreen.copyToClipboard')}
|
||||||
>
|
>
|
||||||
<ContentCopyRounded fontSize='small' />
|
<ContentCopyRounded fontSize='small' />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@@ -347,7 +350,7 @@ const Error = () => {
|
|||||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||||
size='sm'
|
size='sm'
|
||||||
>
|
>
|
||||||
Error details copied to clipboard
|
{t('errorScreen.copied')}
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Box, Button, Container, Typography } from '@mui/joy'
|
import { Box, Button, Container, Typography } from '@mui/joy'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import Logo from '../Logo'
|
import Logo from '../Logo'
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
const Navigate = useNavigate()
|
const Navigate = useNavigate()
|
||||||
const getCurrentUser = () => {
|
const getCurrentUser = () => {
|
||||||
return JSON.parse(localStorage.getItem('user'))
|
return JSON.parse(localStorage.getItem('user'))
|
||||||
@@ -36,7 +38,7 @@ const Home = () => {
|
|||||||
Navigate('/chores')
|
Navigate('/chores')
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Get Started!
|
{t('getStarted')}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Browser } from '@capacitor/browser'
|
import { Browser } from '@capacitor/browser'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Capacitor } from '@capacitor/core'
|
import { Capacitor } from '@capacitor/core'
|
||||||
import { Download } from '@mui/icons-material'
|
import { Download } from '@mui/icons-material'
|
||||||
import { Box, CircularProgress, Typography } from '@mui/joy'
|
import { Box, CircularProgress, Typography } from '@mui/joy'
|
||||||
@@ -29,6 +30,7 @@ const downloadUrl = (url, fileName) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function AttachmentViewerModal({ config }) {
|
function AttachmentViewerModal({ config }) {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
const { ResponsiveModal } = useResponsiveModal()
|
const { ResponsiveModal } = useResponsiveModal()
|
||||||
const [imgLoaded, setImgLoaded] = useState(false)
|
const [imgLoaded, setImgLoaded] = useState(false)
|
||||||
const [imgError, setImgError] = useState(false)
|
const [imgError, setImgError] = useState(false)
|
||||||
@@ -49,7 +51,7 @@ function AttachmentViewerModal({ config }) {
|
|||||||
maxHeight='92vh'
|
maxHeight='92vh'
|
||||||
footer={
|
footer={
|
||||||
<ModalActions
|
<ModalActions
|
||||||
secondary={{ label: 'Close', onClick: handleClose }}
|
secondary={{ label: t('close'), onClick: handleClose }}
|
||||||
primary={{
|
primary={{
|
||||||
label: 'Download',
|
label: 'Download',
|
||||||
startDecorator: <Download />,
|
startDecorator: <Download />,
|
||||||
@@ -73,7 +75,7 @@ function AttachmentViewerModal({ config }) {
|
|||||||
)}
|
)}
|
||||||
{imgError ? (
|
{imgError ? (
|
||||||
<Typography level='body-sm' sx={{ color: 'text.secondary' }}>
|
<Typography level='body-sm' sx={{ color: 'text.secondary' }}>
|
||||||
Failed to load image.
|
{t('imageLoadFailed')}
|
||||||
</Typography>
|
</Typography>
|
||||||
) : (
|
) : (
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import { Input } from '@mui/joy'
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import ModalActions from '../../../components/common/ModalActions'
|
import ModalActions from '../../../components/common/ModalActions'
|
||||||
import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
|
import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
function DateModal({ isOpen, onClose, onSave, current, title }) {
|
function DateModal({ isOpen, onClose, onSave, current, title }) {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
const { ResponsiveModal } = useResponsiveModal()
|
const { ResponsiveModal } = useResponsiveModal()
|
||||||
const [date, setDate] = useState(
|
const [date, setDate] = useState(
|
||||||
current ? new Date(current).toISOString().split('T')[0] : '',
|
current ? new Date(current).toISOString().split('T')[0] : '',
|
||||||
@@ -22,8 +24,8 @@ function DateModal({ isOpen, onClose, onSave, current, title }) {
|
|||||||
title={title}
|
title={title}
|
||||||
footer={
|
footer={
|
||||||
<ModalActions
|
<ModalActions
|
||||||
secondary={{ label: 'Cancel', onClick: onClose }}
|
secondary={{ label: t('cancel'), onClick: onClose }}
|
||||||
primary={{ label: 'Save', onClick: handleSave, disabled: !date }}
|
primary={{ label: t('save'), onClick: handleSave, disabled: !date }}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Textarea } from '@mui/joy'
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import ModalActions from '../../../components/common/ModalActions'
|
import ModalActions from '../../../components/common/ModalActions'
|
||||||
import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
|
import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
function TextModal({
|
function TextModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
@@ -12,6 +13,7 @@ function TextModal({
|
|||||||
okText,
|
okText,
|
||||||
cancelText,
|
cancelText,
|
||||||
}) {
|
}) {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
const { ResponsiveModal } = useResponsiveModal()
|
const { ResponsiveModal } = useResponsiveModal()
|
||||||
const [text, setText] = useState(current)
|
const [text, setText] = useState(current)
|
||||||
|
|
||||||
@@ -35,7 +37,7 @@ function TextModal({
|
|||||||
>
|
>
|
||||||
<Textarea
|
<Textarea
|
||||||
autoFocus
|
autoFocus
|
||||||
placeholder='Type in here…'
|
placeholder={t('typeHere')}
|
||||||
value={text}
|
value={text}
|
||||||
onChange={event => setText(event.target.value)}
|
onChange={event => setText(event.target.value)}
|
||||||
minRows={3}
|
minRows={3}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { Avatar, Box, List, ListItem, Typography } from '@mui/joy'
|
import { Avatar, Box, List, ListItem, Typography } from '@mui/joy'
|
||||||
import ModalActions from '../../../components/common/ModalActions'
|
import ModalActions from '../../../components/common/ModalActions'
|
||||||
import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
|
import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
const UserModal = ({ isOpen, performers = [], onSelect, onClose }) => {
|
const UserModal = ({ isOpen, performers = [], onSelect, onClose }) => {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
const { ResponsiveModal } = useResponsiveModal()
|
const { ResponsiveModal } = useResponsiveModal()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -13,7 +15,7 @@ const UserModal = ({ isOpen, performers = [], onSelect, onClose }) => {
|
|||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
title='Select User'
|
title='Select User'
|
||||||
footer={
|
footer={
|
||||||
<ModalActions secondary={{ label: 'Cancel', onClick: onClose }} />
|
<ModalActions secondary={{ label: t('cancel'), onClick: onClose }} />
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<List sx={{ mb: 2 }}>
|
<List sx={{ mb: 2 }}>
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { Card, IconButton, Typography } from '@mui/joy'
|
import { Card, IconButton, Typography } from '@mui/joy'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
const SummaryCard = () => {
|
const SummaryCard = () => {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-between'>
|
||||||
<div>
|
<div>
|
||||||
<Typography level='h2'>Summary</Typography>
|
<Typography level='h2'>Summary</Typography>
|
||||||
<Typography level='body-xs'>
|
<Typography level='body-xs'>
|
||||||
This is a summary of your chores
|
{t('summaryOfChores')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<IconButton>
|
<IconButton>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { Chip, List, ListItem, ListItemButton, Textarea } from '@mui/joy'
|
import { Chip, List, ListItem, ListItemButton, Textarea } from '@mui/joy'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import React, { useEffect, useRef, useState } from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
const AutocompleteInput = ({ options, ref, value, onChange, ...props }) => {
|
const AutocompleteInput = ({ options, ref, value, onChange, ...props }) => {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
const [filteredOptions, setFilteredOptions] = useState([])
|
const [filteredOptions, setFilteredOptions] = useState([])
|
||||||
const [menuVisible, setMenuVisible] = useState(false)
|
const [menuVisible, setMenuVisible] = useState(false)
|
||||||
const [highlightedIndex, setHighlightedIndex] = useState(-1)
|
const [highlightedIndex, setHighlightedIndex] = useState(-1)
|
||||||
@@ -82,7 +84,7 @@ const AutocompleteInput = ({ options, ref, value, onChange, ...props }) => {
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
placeholder='Type here...'
|
placeholder={t('autocompletePlaceholder')}
|
||||||
/>
|
/>
|
||||||
{menuVisible && (
|
{menuVisible && (
|
||||||
<List ref={menuRef} style={{ position: 'absolute', zIndex: 1000 }}>
|
<List ref={menuRef} style={{ position: 'absolute', zIndex: 1000 }}>
|
||||||
|
|||||||
@@ -1,31 +1,29 @@
|
|||||||
import { Box, Button, CircularProgress, Container } from '@mui/joy'
|
import { Box, Button, CircularProgress, Container } from '@mui/joy'
|
||||||
import { Typography } from '@mui/material'
|
import { Typography } from '@mui/material'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import Logo from '../../Logo'
|
import Logo from '../../Logo'
|
||||||
import { networkManager } from '../../hooks/NetworkManager'
|
import { networkManager } from '../../hooks/NetworkManager'
|
||||||
|
|
||||||
const LoadingComponent = () => {
|
const LoadingComponent = () => {
|
||||||
const [message, setMessage] = useState('Loading...')
|
const { t } = useTranslation('common')
|
||||||
|
const [message, setMessage] = useState(t('loading'))
|
||||||
const [subMessage, setSubMessage] = useState('')
|
const [subMessage, setSubMessage] = useState('')
|
||||||
const [isOnline, setIsOnline] = useState(networkManager.isOnline)
|
const [isOnline, setIsOnline] = useState(networkManager.isOnline)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOnline) {
|
if (!isOnline) {
|
||||||
setMessage('You are offline')
|
setMessage(t('loadingOffline'))
|
||||||
setSubMessage(
|
setSubMessage(t('loadingOfflineSub'))
|
||||||
'This not available while offline. Please check your internet connection and try again.',
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}, [isOnline])
|
}, [isOnline, t])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
networkManager.registerNetworkListener(isOnline => setIsOnline(isOnline))
|
networkManager.registerNetworkListener(isOnline => setIsOnline(isOnline))
|
||||||
|
|
||||||
// if loading took more than 5 seconds update submessage to mention there might be an error:
|
// if loading took more than 5 seconds update submessage to mention there might be an error:
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
if (networkManager.isOnline) {
|
if (networkManager.isOnline) {
|
||||||
setSubMessage(
|
setSubMessage(t('loadingSlow'))
|
||||||
'This is taking longer than usual. There might be an issue.',
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}, 5000)
|
}, 5000)
|
||||||
return () => clearTimeout(timeout)
|
return () => clearTimeout(timeout)
|
||||||
@@ -67,7 +65,7 @@ const LoadingComponent = () => {
|
|||||||
window.location.href = '/' // navigate back to the home page
|
window.location.href = '/' // navigate back to the home page
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Navigate Back
|
{t('navigateBack')}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import Box from '@mui/joy/Box'
|
import Box from '@mui/joy/Box'
|
||||||
import ListItemDecorator from '@mui/joy/ListItemDecorator'
|
import ListItemDecorator from '@mui/joy/ListItemDecorator'
|
||||||
import Tabs from '@mui/joy/Tabs'
|
import Tabs from '@mui/joy/Tabs'
|
||||||
@@ -10,6 +11,7 @@ import Search from '@mui/icons-material/Search'
|
|||||||
import Person from '@mui/icons-material/Person'
|
import Person from '@mui/icons-material/Person'
|
||||||
|
|
||||||
export default function NavBarMobile() {
|
export default function NavBarMobile() {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
const [index, setIndex] = React.useState(0)
|
const [index, setIndex] = React.useState(0)
|
||||||
const colors = ['primary', 'danger', 'success', 'warning']
|
const colors = ['primary', 'danger', 'success', 'warning']
|
||||||
return (
|
return (
|
||||||
@@ -32,7 +34,7 @@ export default function NavBarMobile() {
|
|||||||
>
|
>
|
||||||
<Tabs
|
<Tabs
|
||||||
size='lg'
|
size='lg'
|
||||||
aria-label='Bottom Navigation'
|
aria-label={t('bottomNav')}
|
||||||
value={index}
|
value={index}
|
||||||
onChange={(event, value) => setIndex(value)}
|
onChange={(event, value) => setIndex(value)}
|
||||||
sx={theme => ({
|
sx={theme => ({
|
||||||
@@ -98,7 +100,7 @@ export default function NavBarMobile() {
|
|||||||
<ListItemDecorator>
|
<ListItemDecorator>
|
||||||
<Person />
|
<Person />
|
||||||
</ListItemDecorator>
|
</ListItemDecorator>
|
||||||
Profile
|
{t('profile')}
|
||||||
</Tab>
|
</Tab>
|
||||||
</TabList>
|
</TabList>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|||||||
Reference in New Issue
Block a user