Merge branch 'dev'
This commit is contained in:
commit
8e42e59a80
13 changed files with 610 additions and 260 deletions
|
@ -73,6 +73,16 @@ const MarkChoreComplete = (id, note, completedDate) => {
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SkipChore = id => {
|
||||||
|
return Fetch(`${API_URL}/chores/${id}/skip`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({}),
|
||||||
|
})
|
||||||
|
}
|
||||||
const CreateChore = chore => {
|
const CreateChore = chore => {
|
||||||
return Fetch(`${API_URL}/chores/`, {
|
return Fetch(`${API_URL}/chores/`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -277,6 +287,7 @@ export {
|
||||||
SaveChore,
|
SaveChore,
|
||||||
SaveThing,
|
SaveThing,
|
||||||
signUp,
|
signUp,
|
||||||
|
SkipChore,
|
||||||
UpdateThingState,
|
UpdateThingState,
|
||||||
UpdateUserDetails,
|
UpdateUserDetails,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,48 +3,53 @@ import {
|
||||||
CancelScheduleSend,
|
CancelScheduleSend,
|
||||||
Check,
|
Check,
|
||||||
Checklist,
|
Checklist,
|
||||||
Note,
|
History,
|
||||||
PeopleAlt,
|
PeopleAlt,
|
||||||
Person,
|
Person,
|
||||||
|
SwitchAccessShortcut,
|
||||||
|
Timelapse,
|
||||||
} from '@mui/icons-material'
|
} from '@mui/icons-material'
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
|
Chip,
|
||||||
Container,
|
Container,
|
||||||
FormControl,
|
FormControl,
|
||||||
Grid,
|
Grid,
|
||||||
Input,
|
Input,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemContent,
|
ListItemContent,
|
||||||
ListItemDecorator,
|
|
||||||
Sheet,
|
Sheet,
|
||||||
Snackbar,
|
Snackbar,
|
||||||
styled,
|
styled,
|
||||||
Typography,
|
Typography,
|
||||||
} from '@mui/joy'
|
} from '@mui/joy'
|
||||||
|
import { Divider } from '@mui/material'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useParams, useSearchParams } from 'react-router-dom'
|
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
|
||||||
import {
|
import {
|
||||||
GetAllUsers,
|
GetAllUsers,
|
||||||
GetChoreDetailById,
|
GetChoreDetailById,
|
||||||
MarkChoreComplete,
|
MarkChoreComplete,
|
||||||
|
SkipChore,
|
||||||
} from '../../utils/Fetcher'
|
} from '../../utils/Fetcher'
|
||||||
|
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
|
||||||
const IconCard = styled('div')({
|
const IconCard = styled('div')({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
backgroundColor: '#f0f0f0', // Adjust the background color as needed
|
backgroundColor: '#f0f0f0', // Adjust the background color as needed
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
minWidth: '50px',
|
minWidth: '40px',
|
||||||
height: '50px',
|
height: '40px',
|
||||||
marginRight: '16px',
|
marginRight: '16px',
|
||||||
})
|
})
|
||||||
const ChoreView = () => {
|
const ChoreView = () => {
|
||||||
const [chore, setChore] = useState({})
|
const [chore, setChore] = useState({})
|
||||||
|
const navigate = useNavigate()
|
||||||
const [performers, setPerformers] = useState([])
|
const [performers, setPerformers] = useState([])
|
||||||
const [infoCards, setInfoCards] = useState([])
|
const [infoCards, setInfoCards] = useState([])
|
||||||
const { choreId } = useParams()
|
const { choreId } = useParams()
|
||||||
|
@ -56,6 +61,8 @@ const ChoreView = () => {
|
||||||
const [timeoutId, setTimeoutId] = useState(null)
|
const [timeoutId, setTimeoutId] = useState(null)
|
||||||
const [secondsLeftToCancel, setSecondsLeftToCancel] = useState(null)
|
const [secondsLeftToCancel, setSecondsLeftToCancel] = useState(null)
|
||||||
const [completedDate, setCompletedDate] = useState(null)
|
const [completedDate, setCompletedDate] = useState(null)
|
||||||
|
const [confirmModelConfig, setConfirmModelConfig] = useState({})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
GetChoreDetailById(choreId).then(resp => {
|
GetChoreDetailById(choreId).then(resp => {
|
||||||
|
@ -85,20 +92,20 @@ const ChoreView = () => {
|
||||||
const generateInfoCards = chore => {
|
const generateInfoCards = chore => {
|
||||||
const cards = [
|
const cards = [
|
||||||
{
|
{
|
||||||
icon: <CalendarMonth />,
|
size: 6,
|
||||||
text: 'Due Date',
|
|
||||||
subtext: moment(chore.nextDueDate).format('MM/DD/YYYY hh:mm A'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <PeopleAlt />,
|
icon: <PeopleAlt />,
|
||||||
text: 'Assigned To',
|
text: 'Assigned To',
|
||||||
subtext: performers.find(p => p.id === chore.assignedTo)?.displayName,
|
subtext: performers.find(p => p.id === chore.assignedTo)?.displayName,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <Person />,
|
size: 6,
|
||||||
text: 'Created By',
|
icon: <CalendarMonth />,
|
||||||
subtext: performers.find(p => p.id === chore.createdBy)?.displayName,
|
text: 'Due Date',
|
||||||
|
subtext: chore.nextDueDate
|
||||||
|
? moment(chore.nextDueDate).fromNow()
|
||||||
|
: 'N/A',
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// icon: <TextFields />,
|
// icon: <TextFields />,
|
||||||
// text: 'Frequency',
|
// text: 'Frequency',
|
||||||
|
@ -107,35 +114,42 @@ const ChoreView = () => {
|
||||||
// chore.frequencyType.slice(1),
|
// chore.frequencyType.slice(1),
|
||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
|
size: 6,
|
||||||
icon: <Checklist />,
|
icon: <Checklist />,
|
||||||
text: 'Total Completed',
|
text: 'Total Completed',
|
||||||
subtext: `${chore.totalCompletedCount}`,
|
subtext: `${chore.totalCompletedCount} times`,
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// icon: <Timelapse />,
|
size: 6,
|
||||||
// text: 'Last Completed',
|
icon: <Timelapse />,
|
||||||
// subtext:
|
text: 'Last Completed',
|
||||||
|
subtext:
|
||||||
// chore.lastCompletedDate &&
|
// chore.lastCompletedDate &&
|
||||||
// moment(chore.lastCompletedDate).format('MM/DD/YYYY hh:mm A'),
|
// moment(chore.lastCompletedDate).format('MM/DD/YYYY hh:mm A'),
|
||||||
// },
|
chore.lastCompletedDate && moment(chore.lastCompletedDate).fromNow(),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
|
size: 6,
|
||||||
icon: <Person />,
|
icon: <Person />,
|
||||||
text: 'Last Completed',
|
text: 'Last Performer',
|
||||||
subtext: chore.lastCompletedDate
|
subtext: chore.lastCompletedDate
|
||||||
? `${
|
? `${
|
||||||
chore.lastCompletedDate &&
|
|
||||||
moment(chore.lastCompletedDate).fromNow()
|
|
||||||
// moment(chore.lastCompletedDate).format('MM/DD/YYYY hh:mm A'))
|
|
||||||
}(${
|
|
||||||
performers.find(p => p.id === chore.lastCompletedBy)?.displayName
|
performers.find(p => p.id === chore.lastCompletedBy)?.displayName
|
||||||
})`
|
}`
|
||||||
: 'Never',
|
: '--',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <Note />,
|
size: 6,
|
||||||
text: 'Recent Note',
|
icon: <Person />,
|
||||||
subtext: chore.notes || '--',
|
text: 'Created By',
|
||||||
|
subtext: performers.find(p => p.id === chore.createdBy)?.displayName,
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// size: 12,
|
||||||
|
// icon: <Note />,
|
||||||
|
// text: 'Recent Note',
|
||||||
|
// subtext: chore.notes || '--',
|
||||||
|
// },
|
||||||
]
|
]
|
||||||
setInfoCards(cards)
|
setInfoCards(cards)
|
||||||
}
|
}
|
||||||
|
@ -184,7 +198,16 @@ const ChoreView = () => {
|
||||||
|
|
||||||
setTimeoutId(id)
|
setTimeoutId(id)
|
||||||
}
|
}
|
||||||
|
const handleSkippingTask = () => {
|
||||||
|
SkipChore(choreId).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
response.json().then(data => {
|
||||||
|
const newChore = data.res
|
||||||
|
setChore(newChore)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
maxWidth='sm'
|
maxWidth='sm'
|
||||||
|
@ -195,46 +218,88 @@ const ChoreView = () => {
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Typography
|
<Typography
|
||||||
level='h3'
|
level='h3'
|
||||||
textAlign={'center'}
|
// textAlign={'center'}
|
||||||
sx={{
|
sx={{
|
||||||
mt: 2,
|
mt: 1,
|
||||||
mb: 4,
|
mb: 0.5,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{chore.name}
|
{chore.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Chip startDecorator={<CalendarMonth />} size='lg' sx={{ mb: 4 }}>
|
||||||
|
{chore.nextDueDate
|
||||||
|
? `Due at ${moment(chore.nextDueDate).format('MM/DD/YYYY hh:mm A')}`
|
||||||
|
: 'N/A'}
|
||||||
|
</Chip>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography level='title-md' sx={{ mb: 0.5 }}>
|
||||||
|
Details
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Sheet
|
||||||
|
sx={{
|
||||||
|
mb: 1,
|
||||||
|
borderRadius: 'lg',
|
||||||
|
p: 2,
|
||||||
|
}}
|
||||||
|
variant='outlined'
|
||||||
|
>
|
||||||
<Grid container spacing={1}>
|
<Grid container spacing={1}>
|
||||||
{infoCards.map((info, index) => (
|
{infoCards.map((detail, index) => (
|
||||||
<Grid key={index} item xs={12} sm={6}>
|
<Grid item xs={4} key={index}>
|
||||||
<Sheet sx={{ mb: 1, borderRadius: 'md', p: 1, boxShadow: 'sm' }}>
|
{/* divider between the list items: */}
|
||||||
<ListItem>
|
|
||||||
<ListItemDecorator>
|
<ListItem key={index}>
|
||||||
<IconCard>{info.icon}</IconCard>
|
|
||||||
</ListItemDecorator>
|
|
||||||
<ListItemContent>
|
<ListItemContent>
|
||||||
<Typography level='body1' sx={{ fontWeight: 'md' }}>
|
<Typography level='body-xs' sx={{ fontWeight: 'md' }}>
|
||||||
{info.text}
|
{detail.text}
|
||||||
</Typography>
|
|
||||||
<Typography level='body1' color='text.tertiary'>
|
|
||||||
{info.subtext ? info.subtext : '--'}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Chip
|
||||||
|
color='primary'
|
||||||
|
size='md'
|
||||||
|
startDecorator={detail.icon}
|
||||||
|
>
|
||||||
|
{detail.subtext ? detail.subtext : '--'}
|
||||||
|
</Chip>
|
||||||
</ListItemContent>
|
</ListItemContent>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</Sheet>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Sheet>
|
||||||
|
{chore.notes && (
|
||||||
|
<>
|
||||||
|
<Typography level='title-md' sx={{ mb: 1 }}>
|
||||||
|
Previous note:
|
||||||
|
</Typography>
|
||||||
|
<Sheet variant='outlined' sx={{ p: 2, borderRadius: 'lg' }}>
|
||||||
|
<Typography level='body-md' sx={{ mb: 1 }}>
|
||||||
|
{chore.notes || '--'}
|
||||||
|
</Typography>
|
||||||
|
</Sheet>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
{/* <Divider
|
{/* <Divider
|
||||||
sx={{
|
sx={{
|
||||||
my: 2,
|
my: 2,
|
||||||
}}
|
}}
|
||||||
/> */}
|
/> */}
|
||||||
|
<Typography level='title-md' sx={{ mt: 1 }}>
|
||||||
|
Actions
|
||||||
|
</Typography>
|
||||||
<Card
|
<Card
|
||||||
sx={{
|
sx={{
|
||||||
p: 2,
|
p: 2,
|
||||||
|
@ -243,7 +308,44 @@ const ChoreView = () => {
|
||||||
mt: 2,
|
mt: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography level='title-md'>Additional Notes</Typography>
|
<Typography level='body-md' sx={{ mb: 1 }}>
|
||||||
|
Complete the task
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<FormControl size='sm'>
|
||||||
|
<Checkbox
|
||||||
|
defaultChecked={note !== null}
|
||||||
|
checked={note !== null}
|
||||||
|
value={note !== null}
|
||||||
|
size='lg'
|
||||||
|
onChange={e => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
setNote('')
|
||||||
|
} else {
|
||||||
|
setNote(null)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
overlay
|
||||||
|
sx={
|
||||||
|
{
|
||||||
|
// my: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Typography
|
||||||
|
level='body-sm'
|
||||||
|
sx={{
|
||||||
|
// center vertically
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add Additional Notes
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
{note !== null && (
|
||||||
<Input
|
<Input
|
||||||
fullWidth
|
fullWidth
|
||||||
multiline
|
multiline
|
||||||
|
@ -259,11 +361,12 @@ const ChoreView = () => {
|
||||||
}}
|
}}
|
||||||
size='md'
|
size='md'
|
||||||
sx={{
|
sx={{
|
||||||
my: 1,
|
mb: 1,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<FormControl size='sm' sx={{ width: 400 }}>
|
<FormControl size='sm'>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
defaultChecked={completedDate !== null}
|
defaultChecked={completedDate !== null}
|
||||||
checked={completedDate !== null}
|
checked={completedDate !== null}
|
||||||
|
@ -279,10 +382,23 @@ const ChoreView = () => {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
overlay
|
overlay
|
||||||
|
sx={
|
||||||
|
{
|
||||||
|
// my: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Typography
|
||||||
|
level='body-sm'
|
||||||
sx={{
|
sx={{
|
||||||
my: 1,
|
// center vertically
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
label={<Typography level='body2'>Set completion date</Typography>}
|
>
|
||||||
|
Specify completion date
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
{completedDate !== null && (
|
{completedDate !== null && (
|
||||||
|
@ -295,22 +411,10 @@ const ChoreView = () => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{completedDate === null && (
|
|
||||||
// placeholder for the completion date with margin:
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
height: 56,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
fullWidth
|
fullWidth
|
||||||
size='lg'
|
size='lg'
|
||||||
sx={{
|
|
||||||
height: 50,
|
|
||||||
mb: 2,
|
|
||||||
}}
|
|
||||||
onClick={handleTaskCompletion}
|
onClick={handleTaskCompletion}
|
||||||
disabled={isPendingCompletion}
|
disabled={isPendingCompletion}
|
||||||
color={isPendingCompletion ? 'danger' : 'success'}
|
color={isPendingCompletion ? 'danger' : 'success'}
|
||||||
|
@ -318,23 +422,54 @@ const ChoreView = () => {
|
||||||
>
|
>
|
||||||
<Box>Mark as done</Box>
|
<Box>Mark as done</Box>
|
||||||
</Button>
|
</Button>
|
||||||
{/* <Button
|
<Divider sx={{ my: 0.5 }}>or</Divider>
|
||||||
|
|
||||||
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: '32px',
|
display: 'flex',
|
||||||
mt: 1,
|
flexDirection: 'row',
|
||||||
height: 50,
|
gap: 1,
|
||||||
zIndex: 1,
|
alignContent: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
|
||||||
Navigate('/my/chores')
|
|
||||||
}}
|
|
||||||
color={isPendingCompletion ? 'danger' : 'success'}
|
|
||||||
startDecorator={isPendingCompletion ? <Close /> : <Check />}
|
|
||||||
fullWidth
|
|
||||||
>
|
>
|
||||||
<Box>Mark as {isPendingCompletion ? 'completed' : 'done'}</Box>
|
<Button
|
||||||
</Button> */}
|
fullWidth
|
||||||
</Card>
|
size='lg'
|
||||||
|
onClick={() => {
|
||||||
|
setConfirmModelConfig({
|
||||||
|
isOpen: true,
|
||||||
|
title: 'Skip Task',
|
||||||
|
|
||||||
|
message: 'Are you sure you want to skip this task?',
|
||||||
|
|
||||||
|
confirmText: 'Skip',
|
||||||
|
cancelText: 'Cancel',
|
||||||
|
onClose: confirmed => {
|
||||||
|
if (confirmed) {
|
||||||
|
handleSkippingTask()
|
||||||
|
}
|
||||||
|
setConfirmModelConfig({})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
startDecorator={<SwitchAccessShortcut />}
|
||||||
|
>
|
||||||
|
<Box>Skip</Box>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
startDecorator={<History />}
|
||||||
|
size='lg'
|
||||||
|
color='primary'
|
||||||
|
variant='outlined'
|
||||||
|
fullWidth
|
||||||
|
onClick={() => {
|
||||||
|
navigate(`/chores/${choreId}/history`)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
History
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Snackbar
|
<Snackbar
|
||||||
open={isPendingCompletion}
|
open={isPendingCompletion}
|
||||||
|
@ -361,6 +496,8 @@ const ChoreView = () => {
|
||||||
Task will be marked as completed in {secondsLeftToCancel} seconds
|
Task will be marked as completed in {secondsLeftToCancel} seconds
|
||||||
</Typography>
|
</Typography>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
|
<ConfirmationModal config={confirmModelConfig} />
|
||||||
|
</Card>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -509,7 +509,7 @@ const RepeatSection = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Is this something that should be done when a thing state changes?{' '}
|
Is this something that should be done when a thing state changes?{' '}
|
||||||
{!isPlusAccount(userProfile) && (
|
{userProfile && !isPlusAccount(userProfile) && (
|
||||||
<Chip variant='soft' color='warning'>
|
<Chip variant='soft' color='warning'>
|
||||||
Not available in Basic Plan
|
Not available in Basic Plan
|
||||||
</Chip>
|
</Chip>
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
SwitchAccessShortcut,
|
SwitchAccessShortcut,
|
||||||
TimesOneMobiledata,
|
TimesOneMobiledata,
|
||||||
Update,
|
Update,
|
||||||
|
ViewCarousel,
|
||||||
Webhook,
|
Webhook,
|
||||||
} from '@mui/icons-material'
|
} from '@mui/icons-material'
|
||||||
import {
|
import {
|
||||||
|
@ -38,7 +39,7 @@ import moment from 'moment'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { API_URL } from '../../Config'
|
import { API_URL } from '../../Config'
|
||||||
import { MarkChoreComplete } from '../../utils/Fetcher'
|
import { MarkChoreComplete, SkipChore } from '../../utils/Fetcher'
|
||||||
import { Fetch } from '../../utils/TokenManager'
|
import { Fetch } from '../../utils/TokenManager'
|
||||||
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
|
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
|
||||||
import DateModal from '../Modals/Inputs/DateModal'
|
import DateModal from '../Modals/Inputs/DateModal'
|
||||||
|
@ -107,6 +108,9 @@ const ChoreCard = ({
|
||||||
const handleEdit = () => {
|
const handleEdit = () => {
|
||||||
navigate(`/chores/${chore.id}/edit`)
|
navigate(`/chores/${chore.id}/edit`)
|
||||||
}
|
}
|
||||||
|
const handleView = () => {
|
||||||
|
navigate(`/chores/${chore.id}`)
|
||||||
|
}
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
setConfirmModelConfig({
|
setConfirmModelConfig({
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
|
@ -521,13 +525,7 @@ const ChoreCard = ({
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
Fetch(`${API_URL}/chores/${chore.id}/skip`, {
|
SkipChore(chore.id).then(response => {
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({}),
|
|
||||||
}).then(response => {
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
response.json().then(data => {
|
response.json().then(data => {
|
||||||
const newChore = data.res
|
const newChore = data.res
|
||||||
|
@ -585,6 +583,10 @@ const ChoreCard = ({
|
||||||
<Edit />
|
<Edit />
|
||||||
Edit
|
Edit
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem onClick={handleView}>
|
||||||
|
<ViewCarousel />
|
||||||
|
View
|
||||||
|
</MenuItem>
|
||||||
<MenuItem onClick={handleDelete} color='danger'>
|
<MenuItem onClick={handleDelete} color='danger'>
|
||||||
<Delete />
|
<Delete />
|
||||||
Delete
|
Delete
|
||||||
|
|
|
@ -3,7 +3,6 @@ import {
|
||||||
Badge,
|
Badge,
|
||||||
Box,
|
Box,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
CircularProgress,
|
|
||||||
Container,
|
Container,
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
|
@ -18,8 +17,8 @@ import Fuse from 'fuse.js'
|
||||||
import { useContext, useEffect, useRef, useState } from 'react'
|
import { useContext, useEffect, useRef, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { UserContext } from '../../contexts/UserContext'
|
import { UserContext } from '../../contexts/UserContext'
|
||||||
import Logo from '../../Logo'
|
|
||||||
import { GetAllUsers, GetChores, GetUserProfile } from '../../utils/Fetcher'
|
import { GetAllUsers, GetChores, GetUserProfile } from '../../utils/Fetcher'
|
||||||
|
import LoadingComponent from '../components/Loading'
|
||||||
import ChoreCard from './ChoreCard'
|
import ChoreCard from './ChoreCard'
|
||||||
|
|
||||||
const MyChores = () => {
|
const MyChores = () => {
|
||||||
|
@ -184,18 +183,7 @@ const MyChores = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userProfile === null) {
|
if (userProfile === null) {
|
||||||
return (
|
return <LoadingComponent />
|
||||||
<Container className='flex h-full items-center justify-center'>
|
|
||||||
<Box className='flex flex-col items-center justify-center'>
|
|
||||||
<CircularProgress
|
|
||||||
color='success'
|
|
||||||
sx={{ '--CircularProgress-size': '200px' }}
|
|
||||||
>
|
|
||||||
<Logo />
|
|
||||||
</CircularProgress>
|
|
||||||
</Box>
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import { Checklist, EventBusy, Timelapse } from '@mui/icons-material'
|
import { Checklist, EventBusy, Group, Timelapse } from '@mui/icons-material'
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
|
||||||
Button,
|
Button,
|
||||||
CircularProgress,
|
Chip,
|
||||||
Container,
|
Container,
|
||||||
Grid,
|
Grid,
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemContent,
|
ListItemContent,
|
||||||
ListItemDecorator,
|
|
||||||
Sheet,
|
Sheet,
|
||||||
Typography,
|
Typography,
|
||||||
} from '@mui/joy'
|
} from '@mui/joy'
|
||||||
|
@ -19,6 +17,7 @@ import { Link, useParams } from 'react-router-dom'
|
||||||
import { API_URL } from '../../Config'
|
import { API_URL } from '../../Config'
|
||||||
import { GetAllCircleMembers } from '../../utils/Fetcher'
|
import { GetAllCircleMembers } from '../../utils/Fetcher'
|
||||||
import { Fetch } from '../../utils/TokenManager'
|
import { Fetch } from '../../utils/TokenManager'
|
||||||
|
import LoadingComponent from '../components/Loading'
|
||||||
import HistoryCard from './HistoryCard'
|
import HistoryCard from './HistoryCard'
|
||||||
|
|
||||||
const ChoreHistory = () => {
|
const ChoreHistory = () => {
|
||||||
|
@ -92,59 +91,49 @@ const ChoreHistory = () => {
|
||||||
|
|
||||||
const historyInfo = [
|
const historyInfo = [
|
||||||
{
|
{
|
||||||
icon: (
|
icon: <Checklist />,
|
||||||
<Avatar>
|
text: 'Total Completed',
|
||||||
<Checklist />
|
subtext: `${histories.length} times`,
|
||||||
</Avatar>
|
|
||||||
),
|
|
||||||
text: `${histories.length} completed`,
|
|
||||||
subtext: `${Object.keys(userHistories).length} users contributed`,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: <Timelapse />,
|
||||||
<Avatar>
|
text: 'Usually Within',
|
||||||
<Timelapse />
|
subtext: moment.duration(averageDelayMoment).humanize(),
|
||||||
</Avatar>
|
|
||||||
),
|
|
||||||
text: `Completed within ${moment
|
|
||||||
.duration(averageDelayMoment)
|
|
||||||
.humanize()}`,
|
|
||||||
subtext: `Maximum delay was ${moment
|
|
||||||
.duration(maxDelayMoment)
|
|
||||||
.humanize()}`,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <Avatar></Avatar>,
|
icon: <Timelapse />,
|
||||||
text: `${
|
text: 'Maximum Delay',
|
||||||
|
subtext: moment.duration(maxDelayMoment).humanize(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <Avatar />,
|
||||||
|
text: ' Completed Most',
|
||||||
|
subtext: `${
|
||||||
performers.find(p => p.userId === Number(userCompletedByMost))
|
performers.find(p => p.userId === Number(userCompletedByMost))
|
||||||
?.displayName
|
?.displayName
|
||||||
} completed most`,
|
} `,
|
||||||
subtext: `${userHistories[userCompletedByMost]} time/s`,
|
},
|
||||||
|
// contributes:
|
||||||
|
{
|
||||||
|
icon: <Group />,
|
||||||
|
text: 'Total Performers',
|
||||||
|
subtext: `${Object.keys(userHistories).length} users`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <Avatar />,
|
||||||
|
text: 'Last Completed',
|
||||||
|
subtext: `${
|
||||||
|
performers.find(p => p.userId === Number(histories[0].completedBy))
|
||||||
|
?.displayName
|
||||||
|
}`,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
if (userCompletedByLeast !== userCompletedByMost) {
|
|
||||||
historyInfo.push({
|
|
||||||
icon: (
|
|
||||||
<Avatar>
|
|
||||||
{
|
|
||||||
performers.find(p => p.userId === userCompletedByLeast)
|
|
||||||
?.displayName
|
|
||||||
}
|
|
||||||
</Avatar>
|
|
||||||
),
|
|
||||||
text: `${
|
|
||||||
performers.find(p => p.userId === Number(userCompletedByLeast))
|
|
||||||
.displayName
|
|
||||||
} completed least`,
|
|
||||||
subtext: `${userHistories[userCompletedByLeast]} time/s`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
setHistoryInfo(historyInfo)
|
setHistoryInfo(historyInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <CircularProgress /> // Show loading indicator
|
return <LoadingComponent />
|
||||||
}
|
}
|
||||||
if (!choreHistory.length) {
|
if (!choreHistory.length) {
|
||||||
return (
|
return (
|
||||||
|
@ -184,50 +173,43 @@ const ChoreHistory = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container maxWidth='md'>
|
<Container maxWidth='md'>
|
||||||
<Typography level='h3' mb={1.5}>
|
<Typography level='title-md' mb={1.5}>
|
||||||
Summary:
|
Summary:
|
||||||
</Typography>
|
</Typography>
|
||||||
{/* <Sheet sx={{ mb: 1, borderRadius: 'sm', p: 2, boxShadow: 'md' }}>
|
<Sheet
|
||||||
<ListItem sx={{ gap: 1.5 }}>
|
// sx={{
|
||||||
<ListItemDecorator>
|
// mb: 1,
|
||||||
<Avatar>
|
// borderRadius: 'lg',
|
||||||
<AccountCircle />
|
// p: 2,
|
||||||
</Avatar>
|
// }}
|
||||||
</ListItemDecorator>
|
sx={{ borderRadius: 'sm', p: 2 }}
|
||||||
<ListItemContent>
|
variant='outlined'
|
||||||
<Typography level='body1' sx={{ fontWeight: 'md' }}>
|
>
|
||||||
{choreHistory.length} completed
|
<Grid container spacing={1}>
|
||||||
</Typography>
|
|
||||||
<Typography level='body2' color='text.tertiary'>
|
|
||||||
{Object.keys(userHistory).length} users contributed
|
|
||||||
</Typography>
|
|
||||||
</ListItemContent>
|
|
||||||
</ListItem>
|
|
||||||
</Sheet> */}
|
|
||||||
<Grid container>
|
|
||||||
{historyInfo.map((info, index) => (
|
{historyInfo.map((info, index) => (
|
||||||
<Grid key={index} item xs={12} sm={6}>
|
<Grid item xs={4} key={index}>
|
||||||
<Sheet sx={{ mb: 1, borderRadius: 'sm', p: 2, boxShadow: 'md' }}>
|
{/* divider between the list items: */}
|
||||||
<ListItem sx={{ gap: 1.5 }}>
|
|
||||||
<ListItemDecorator>{info.icon}</ListItemDecorator>
|
<ListItem key={index}>
|
||||||
<ListItemContent>
|
<ListItemContent>
|
||||||
<Typography level='body1' sx={{ fontWeight: 'md' }}>
|
<Typography level='body-xs' sx={{ fontWeight: 'md' }}>
|
||||||
{info.text}
|
{info.text}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography level='body1' color='text.tertiary'>
|
<Chip color='primary' size='md' startDecorator={info.icon}>
|
||||||
{info.subtext}
|
{info.subtext ? info.subtext : '--'}
|
||||||
</Typography>
|
</Chip>
|
||||||
</ListItemContent>
|
</ListItemContent>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</Sheet>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Sheet>
|
||||||
|
|
||||||
{/* User History Cards */}
|
{/* User History Cards */}
|
||||||
<Typography level='h3' my={1.5}>
|
<Typography level='title-md' my={1.5}>
|
||||||
History:
|
History:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ borderRadius: 'sm', p: 2, boxShadow: 'md' }}>
|
<Sheet sx={{ borderRadius: 'sm', p: 2, boxShadow: 'md' }}>
|
||||||
{/* Chore History List (Updated Style) */}
|
{/* Chore History List (Updated Style) */}
|
||||||
|
|
||||||
<List sx={{ p: 0 }}>
|
<List sx={{ p: 0 }}>
|
||||||
|
@ -241,7 +223,7 @@ const ChoreHistory = () => {
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
</Box>
|
</Sheet>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { CalendarViewDay, Check, Timelapse } from '@mui/icons-material'
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
Box,
|
||||||
|
@ -30,6 +31,50 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => {
|
||||||
|
|
||||||
return `${timeValue} ${unit}${timeValue !== 1 ? 's' : ''}`
|
return `${timeValue} ${unit}${timeValue !== 1 ? 's' : ''}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getCompletedChip = historyEntry => {
|
||||||
|
var text = 'No Due Date'
|
||||||
|
var color = 'info'
|
||||||
|
var icon = <CalendarViewDay />
|
||||||
|
// if completed few hours +-6 hours
|
||||||
|
if (
|
||||||
|
historyEntry.dueDate &&
|
||||||
|
historyEntry.completedAt > historyEntry.dueDate - 1000 * 60 * 60 * 6 &&
|
||||||
|
historyEntry.completedAt < historyEntry.dueDate + 1000 * 60 * 60 * 6
|
||||||
|
) {
|
||||||
|
text = 'On Time'
|
||||||
|
color = 'success'
|
||||||
|
icon = <Check />
|
||||||
|
} else if (
|
||||||
|
historyEntry.dueDate &&
|
||||||
|
historyEntry.completedAt < historyEntry.dueDate
|
||||||
|
) {
|
||||||
|
text = 'On Time'
|
||||||
|
color = 'success'
|
||||||
|
icon = <Check />
|
||||||
|
}
|
||||||
|
|
||||||
|
// if completed after due date then it's late
|
||||||
|
else if (
|
||||||
|
historyEntry.dueDate &&
|
||||||
|
historyEntry.completedAt > historyEntry.dueDate
|
||||||
|
) {
|
||||||
|
text = 'Late'
|
||||||
|
color = 'warning'
|
||||||
|
icon = <Timelapse />
|
||||||
|
} else {
|
||||||
|
text = 'No Due Date'
|
||||||
|
color = 'info'
|
||||||
|
icon = <CalendarViewDay />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Chip startDecorator={icon} color={color}>
|
||||||
|
{text}
|
||||||
|
</Chip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ListItem sx={{ gap: 1.5, alignItems: 'flex-start' }}>
|
<ListItem sx={{ gap: 1.5, alignItems: 'flex-start' }}>
|
||||||
|
@ -55,13 +100,7 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => {
|
||||||
<Typography level='body1' sx={{ fontWeight: 'md' }}>
|
<Typography level='body1' sx={{ fontWeight: 'md' }}>
|
||||||
{moment(historyEntry.completedAt).format('ddd MM/DD/yyyy HH:mm')}
|
{moment(historyEntry.completedAt).format('ddd MM/DD/yyyy HH:mm')}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
{getCompletedChip(historyEntry)}
|
||||||
<Chip>
|
|
||||||
{historyEntry.dueDate &&
|
|
||||||
historyEntry.completedAt > historyEntry.dueDate
|
|
||||||
? 'Late'
|
|
||||||
: 'On Time'}
|
|
||||||
</Chip>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Typography level='body2' color='text.tertiary'>
|
<Typography level='body2' color='text.tertiary'>
|
||||||
<Chip>
|
<Chip>
|
||||||
|
|
|
@ -7,7 +7,7 @@ const DemoHistory = () => {
|
||||||
{
|
{
|
||||||
id: 32,
|
id: 32,
|
||||||
choreId: 12,
|
choreId: 12,
|
||||||
completedAt: moment().format(),
|
completedAt: moment().hour(4).format(),
|
||||||
completedBy: 1,
|
completedBy: 1,
|
||||||
assignedTo: 1,
|
assignedTo: 1,
|
||||||
notes: null,
|
notes: null,
|
||||||
|
@ -25,8 +25,8 @@ const DemoHistory = () => {
|
||||||
{
|
{
|
||||||
id: 31,
|
id: 31,
|
||||||
choreId: 12,
|
choreId: 12,
|
||||||
completedAt: moment().day(-10).format(),
|
completedAt: moment().day(-10).hour(1).format(),
|
||||||
completedBy: 1,
|
completedBy: 2,
|
||||||
assignedTo: 1,
|
assignedTo: 1,
|
||||||
notes: null,
|
notes: null,
|
||||||
dueDate: moment().day(-10).format(),
|
dueDate: moment().day(-10).format(),
|
||||||
|
|
|
@ -25,44 +25,39 @@ const FeatureIcon = styled('div')({
|
||||||
const CardData = [
|
const CardData = [
|
||||||
{
|
{
|
||||||
title: 'Open Source & Transparent',
|
title: 'Open Source & Transparent',
|
||||||
headline: 'Built for the Community',
|
|
||||||
description:
|
description:
|
||||||
'Donetick is a community-driven, open-source project. Contribute, customize, and make task management truly yours.',
|
'Donetick is open source software. You can view, modify, and contribute to the code on GitHub.',
|
||||||
icon: CodeRounded,
|
icon: CodeRounded,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Circles: Your Task Hub',
|
title: 'Circles: Your Task Hub',
|
||||||
headline: 'Share & Conquer Together',
|
|
||||||
description:
|
description:
|
||||||
'Create circles for your family, friends, or team. Easily share tasks and track progress within each group.',
|
'build with sharing in mind. invite other to the circle and you can assign tasks to each other. and only see the tasks the should be shared',
|
||||||
icon: GroupRounded,
|
icon: GroupRounded,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Track Your Progress',
|
title: 'Track Your Progress',
|
||||||
headline: "See Who's Done What",
|
|
||||||
description:
|
description:
|
||||||
'View a history of task completion for each member of your circles. Celebrate successes and stay on top of your goals.',
|
'View a history of completed tasks. or use things to track simply things!',
|
||||||
icon: HistoryRounded,
|
icon: HistoryRounded,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Automated Chore Scheduling',
|
title: 'Automated Task Scheduling',
|
||||||
headline: 'Fully Customizable Recurring Tasks',
|
|
||||||
description:
|
description:
|
||||||
'Set up chores to repeat daily, weekly, or monthly. Donetick will automatically assign and track each task for you.',
|
'Set up Tasks to repeat daily, weekly, or monthly, or maybe specifc day in specifc months? Donetick have a flexible scheduling system',
|
||||||
icon: AutoAwesomeMosaicOutlined,
|
icon: AutoAwesomeMosaicOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Automated Task Assignment',
|
title: 'Automated Task Assignment',
|
||||||
headline: 'Share Responsibilities Equally',
|
|
||||||
description:
|
description:
|
||||||
'can automatically assigns tasks to each member of your circle. Randomly or based on past completion.',
|
'For shared tasks, Donetick can randomly rotate assignments or choose based on last completion or least assigned.',
|
||||||
icon: AutoAwesomeRounded,
|
icon: AutoAwesomeRounded,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Integrations & Webhooks',
|
title: 'Integrations & Webhooks',
|
||||||
headline: 'API & 3rd Party Integrations',
|
|
||||||
description:
|
description:
|
||||||
'Connect Donetick with your favorite apps and services. Trigger tasks based on events from other platforms.',
|
'Donetick can update things programmatically with API call. you can update things from other services like IFTTT, Homeassistant or even your own service',
|
||||||
icon: Webhook,
|
icon: Webhook,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -80,7 +75,7 @@ function Feature2({ icon: Icon, title, headline, description, index }) {
|
||||||
<FeatureIcon>
|
<FeatureIcon>
|
||||||
<Icon
|
<Icon
|
||||||
color='primary'
|
color='primary'
|
||||||
style={{ Width: '30px', height: '30px' }}
|
style={{ Width: '30px', height: '30px', fontSize: '30px' }}
|
||||||
stroke={1.5}
|
stroke={1.5}
|
||||||
/>
|
/>
|
||||||
</FeatureIcon>
|
</FeatureIcon>
|
||||||
|
@ -106,7 +101,7 @@ function FeaturesSection() {
|
||||||
<Feature2
|
<Feature2
|
||||||
icon={feature.icon}
|
icon={feature.icon}
|
||||||
title={feature.title}
|
title={feature.title}
|
||||||
headline={feature.headline}
|
// headline={feature.headline}
|
||||||
description={feature.description}
|
description={feature.description}
|
||||||
index={index}
|
index={index}
|
||||||
key={index}
|
key={index}
|
||||||
|
@ -128,7 +123,7 @@ function FeaturesSection() {
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
<Typography level='h4' mt={2} mb={4}>
|
<Typography level='h4' mt={2} mb={4}>
|
||||||
Features Overview
|
Why Donetick?
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Container maxWidth={'lg'} sx={{ mb: 8 }}></Container>
|
<Container maxWidth={'lg'} sx={{ mb: 8 }}></Container>
|
||||||
|
|
127
src/views/Landing/Footer.jsx
Normal file
127
src/views/Landing/Footer.jsx
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
import LogoSVG from '@/assets/logo.svg'
|
||||||
|
import { Card, Grid } from '@mui/joy'
|
||||||
|
import Box from '@mui/joy/Box'
|
||||||
|
import Link from '@mui/joy/Link'
|
||||||
|
import Typography from '@mui/joy/Typography'
|
||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
|
function Footer() {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
data-aos-landing-footer
|
||||||
|
data-aos-delay={200}
|
||||||
|
data-aos-anchor='[data-aos-landing-footer]'
|
||||||
|
data-aos='zoom-in-up'
|
||||||
|
>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
component='footer'
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-around',
|
||||||
|
p: 4,
|
||||||
|
// borderTop: '1px solid',
|
||||||
|
bottom: 0,
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<div className='logo'>
|
||||||
|
<img src={LogoSVG} alt='logo' width='64px' height='64px' />
|
||||||
|
</div>
|
||||||
|
<Box className='flex items-center gap-2'>
|
||||||
|
<Typography
|
||||||
|
level='title-lg'
|
||||||
|
sx={{
|
||||||
|
fontWeight: 700,
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Done
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color: '#06b6d4',
|
||||||
|
fontWeight: 600,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
tick✓
|
||||||
|
</span>
|
||||||
|
</Typography>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 700,
|
||||||
|
position: 'relative',
|
||||||
|
top: 12,
|
||||||
|
right: 45,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Beta
|
||||||
|
</span>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography level='body2' fontWeight='bold' mb={1}>
|
||||||
|
Github
|
||||||
|
</Typography>
|
||||||
|
<Link
|
||||||
|
href='https://github.com/donetick/core'
|
||||||
|
level='body2'
|
||||||
|
sx={{ display: 'block' }}
|
||||||
|
>
|
||||||
|
Core(Backend)
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href='https://github.com/donetick/frontend'
|
||||||
|
level='body2'
|
||||||
|
sx={{ display: 'block' }}
|
||||||
|
>
|
||||||
|
Frontend
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href='https://github.com/donetick/hassio-addons'
|
||||||
|
level='body2'
|
||||||
|
sx={{ display: 'block' }}
|
||||||
|
>
|
||||||
|
Home Assistant Addon
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href='https://github.com/orgs/Donetick/packages'
|
||||||
|
level='body2'
|
||||||
|
sx={{ display: 'block' }}
|
||||||
|
>
|
||||||
|
Packages
|
||||||
|
</Link>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography level='body2' fontWeight='bold' mb={1}>
|
||||||
|
Links
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Link disabled={true} level='body2' sx={{ display: 'block' }}>
|
||||||
|
Roadmap(soon)
|
||||||
|
</Link>
|
||||||
|
<Link disabled={true} level='body2' sx={{ display: 'block' }}>
|
||||||
|
Documentation(soon)
|
||||||
|
</Link>
|
||||||
|
<Link disabled={true} level='body2' sx={{ display: 'block' }}>
|
||||||
|
Changelog(soon)
|
||||||
|
</Link>
|
||||||
|
</Box>
|
||||||
|
{/* <Box>
|
||||||
|
<Typography level='body2' fontWeight='bold' mb={1}>
|
||||||
|
Others
|
||||||
|
</Typography>
|
||||||
|
<Link href='#' level='body2' sx={{ display: 'block' }}>
|
||||||
|
Telegram Integration
|
||||||
|
</Link>
|
||||||
|
<Link href='#' level='body2' sx={{ display: 'block' }}>
|
||||||
|
Slash Commands
|
||||||
|
</Link>
|
||||||
|
</Box> */}
|
||||||
|
</Grid>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Footer
|
|
@ -1,4 +1,4 @@
|
||||||
import { Container, Grid } from '@mui/joy'
|
import { Box, Container, Grid } from '@mui/joy'
|
||||||
import AOS from 'aos'
|
import AOS from 'aos'
|
||||||
import 'aos/dist/aos.css'
|
import 'aos/dist/aos.css'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
@ -8,6 +8,7 @@ import DemoHistory from './DemoHistory'
|
||||||
import DemoMyChore from './DemoMyChore'
|
import DemoMyChore from './DemoMyChore'
|
||||||
import DemoScheduler from './DemoScheduler'
|
import DemoScheduler from './DemoScheduler'
|
||||||
import FeaturesSection from './FeaturesSection'
|
import FeaturesSection from './FeaturesSection'
|
||||||
|
import Footer from './Footer'
|
||||||
import HomeHero from './HomeHero'
|
import HomeHero from './HomeHero'
|
||||||
const Landing = () => {
|
const Landing = () => {
|
||||||
const Navigate = useNavigate()
|
const Navigate = useNavigate()
|
||||||
|
@ -39,6 +40,17 @@ const Landing = () => {
|
||||||
</Grid>
|
</Grid>
|
||||||
<FeaturesSection />
|
<FeaturesSection />
|
||||||
{/* <PricingSection /> */}
|
{/* <PricingSection /> */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
mt: 5,
|
||||||
|
mb: 5,
|
||||||
|
}}
|
||||||
|
></Box>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
51
src/views/components/Loading.jsx
Normal file
51
src/views/components/Loading.jsx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import { Box, CircularProgress, Container } from '@mui/joy'
|
||||||
|
import { Typography } from '@mui/material'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import Logo from '../../Logo'
|
||||||
|
|
||||||
|
const LoadingComponent = () => {
|
||||||
|
const [message, setMessage] = useState('Loading...')
|
||||||
|
const [subMessage, setSubMessage] = useState('')
|
||||||
|
useEffect(() => {
|
||||||
|
// if loading took more than 5 seconds update submessage to mention there might be an error:
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
setSubMessage(
|
||||||
|
'This is taking longer than usual. There might be an issue.',
|
||||||
|
)
|
||||||
|
}, 5000)
|
||||||
|
return () => clearTimeout(timeout)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className='flex h-full items-center justify-center'>
|
||||||
|
<Box
|
||||||
|
className='flex flex-col items-center justify-center'
|
||||||
|
sx={{
|
||||||
|
minHeight: '80vh',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CircularProgress
|
||||||
|
color='success'
|
||||||
|
sx={{ '--CircularProgress-size': '200px' }}
|
||||||
|
>
|
||||||
|
<Logo />
|
||||||
|
</CircularProgress>
|
||||||
|
<Box
|
||||||
|
className='flex items-center gap-2'
|
||||||
|
sx={{
|
||||||
|
fontWeight: 700,
|
||||||
|
fontSize: 24,
|
||||||
|
mt: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{message}
|
||||||
|
</Box>
|
||||||
|
<Typography level='h2' fontWeight={500} textAlign={'center'}>
|
||||||
|
{subMessage}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoadingComponent
|
|
@ -21,7 +21,7 @@ import {
|
||||||
Typography,
|
Typography,
|
||||||
} from '@mui/joy'
|
} from '@mui/joy'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { version } from '../../../package.json'
|
import { version } from '../../../package.json'
|
||||||
import NavBarLink from './NavBarLink'
|
import NavBarLink from './NavBarLink'
|
||||||
const links = [
|
const links = [
|
||||||
|
@ -63,6 +63,7 @@ const links = [
|
||||||
]
|
]
|
||||||
|
|
||||||
const NavBar = () => {
|
const NavBar = () => {
|
||||||
|
const navigate = useNavigate()
|
||||||
const [drawerOpen, setDrawerOpen] = useState(false)
|
const [drawerOpen, setDrawerOpen] = useState(false)
|
||||||
const [openDrawer, closeDrawer] = [
|
const [openDrawer, closeDrawer] = [
|
||||||
() => setDrawerOpen(true),
|
() => setDrawerOpen(true),
|
||||||
|
@ -89,7 +90,12 @@ const NavBar = () => {
|
||||||
<IconButton size='sm' variant='plain' onClick={() => setDrawerOpen(true)}>
|
<IconButton size='sm' variant='plain' onClick={() => setDrawerOpen(true)}>
|
||||||
<MenuRounded />
|
<MenuRounded />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Box className='flex items-center gap-2'>
|
<Box
|
||||||
|
className='flex items-center gap-2'
|
||||||
|
onClick={() => {
|
||||||
|
navigate('/my/chores')
|
||||||
|
}}
|
||||||
|
>
|
||||||
<img component='img' src={Logo} width='34' />
|
<img component='img' src={Logo} width='34' />
|
||||||
<Typography
|
<Typography
|
||||||
level='title-lg'
|
level='title-lg'
|
||||||
|
|
Loading…
Add table
Reference in a new issue