aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/utils/Fetcher.jsx11
-rw-r--r--src/views/ChoreEdit/ChoreView.jsx387
-rw-r--r--src/views/ChoreEdit/RepeatSection.jsx2
-rw-r--r--src/views/Chores/ChoreCard.jsx18
-rw-r--r--src/views/Chores/MyChores.jsx16
-rw-r--r--src/views/History/ChoreHistory.jsx140
-rw-r--r--src/views/History/HistoryCard.jsx53
-rw-r--r--src/views/Landing/DemoHistory.jsx6
-rw-r--r--src/views/Landing/FeaturesSection.jsx27
-rw-r--r--src/views/Landing/Footer.jsx127
-rw-r--r--src/views/Landing/Landing.jsx14
-rw-r--r--src/views/components/Loading.jsx51
-rw-r--r--src/views/components/NavBar.jsx10
13 files changed, 606 insertions, 256 deletions
diff --git a/src/utils/Fetcher.jsx b/src/utils/Fetcher.jsx
index afa9235..1161543 100644
--- a/src/utils/Fetcher.jsx
+++ b/src/utils/Fetcher.jsx
@@ -73,6 +73,16 @@ const MarkChoreComplete = (id, note, completedDate) => {
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 => {
return Fetch(`${API_URL}/chores/`, {
method: 'POST',
@@ -277,6 +287,7 @@ export {
SaveChore,
SaveThing,
signUp,
+ SkipChore,
UpdateThingState,
UpdateUserDetails,
}
diff --git a/src/views/ChoreEdit/ChoreView.jsx b/src/views/ChoreEdit/ChoreView.jsx
index df45aa7..172ad9b 100644
--- a/src/views/ChoreEdit/ChoreView.jsx
+++ b/src/views/ChoreEdit/ChoreView.jsx
@@ -3,48 +3,53 @@ import {
CancelScheduleSend,
Check,
Checklist,
- Note,
+ History,
PeopleAlt,
Person,
+ SwitchAccessShortcut,
+ Timelapse,
} from '@mui/icons-material'
import {
Box,
Button,
Card,
Checkbox,
+ Chip,
Container,
FormControl,
Grid,
Input,
ListItem,
ListItemContent,
- ListItemDecorator,
Sheet,
Snackbar,
styled,
Typography,
} from '@mui/joy'
+import { Divider } from '@mui/material'
import moment from 'moment'
import { useEffect, useState } from 'react'
-import { useParams, useSearchParams } from 'react-router-dom'
+import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
import {
GetAllUsers,
GetChoreDetailById,
MarkChoreComplete,
+ SkipChore,
} from '../../utils/Fetcher'
+import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
const IconCard = styled('div')({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#f0f0f0', // Adjust the background color as needed
borderRadius: '50%',
- minWidth: '50px',
- height: '50px',
+ minWidth: '40px',
+ height: '40px',
marginRight: '16px',
})
const ChoreView = () => {
const [chore, setChore] = useState({})
-
+ const navigate = useNavigate()
const [performers, setPerformers] = useState([])
const [infoCards, setInfoCards] = useState([])
const { choreId } = useParams()
@@ -56,6 +61,8 @@ const ChoreView = () => {
const [timeoutId, setTimeoutId] = useState(null)
const [secondsLeftToCancel, setSecondsLeftToCancel] = useState(null)
const [completedDate, setCompletedDate] = useState(null)
+ const [confirmModelConfig, setConfirmModelConfig] = useState({})
+
useEffect(() => {
Promise.all([
GetChoreDetailById(choreId).then(resp => {
@@ -85,20 +92,20 @@ const ChoreView = () => {
const generateInfoCards = chore => {
const cards = [
{
- icon: <CalendarMonth />,
- text: 'Due Date',
- subtext: moment(chore.nextDueDate).format('MM/DD/YYYY hh:mm A'),
- },
- {
+ size: 6,
icon: <PeopleAlt />,
text: 'Assigned To',
subtext: performers.find(p => p.id === chore.assignedTo)?.displayName,
},
{
- icon: <Person />,
- text: 'Created By',
- subtext: performers.find(p => p.id === chore.createdBy)?.displayName,
+ size: 6,
+ icon: <CalendarMonth />,
+ text: 'Due Date',
+ subtext: chore.nextDueDate
+ ? moment(chore.nextDueDate).fromNow()
+ : 'N/A',
},
+
// {
// icon: <TextFields />,
// text: 'Frequency',
@@ -107,35 +114,42 @@ const ChoreView = () => {
// chore.frequencyType.slice(1),
// },
{
+ size: 6,
icon: <Checklist />,
text: 'Total Completed',
- subtext: `${chore.totalCompletedCount}`,
+ subtext: `${chore.totalCompletedCount} times`,
},
- // {
- // icon: <Timelapse />,
- // text: 'Last Completed',
- // subtext:
- // chore.lastCompletedDate &&
- // moment(chore.lastCompletedDate).format('MM/DD/YYYY hh:mm A'),
- // },
{
- icon: <Person />,
+ size: 6,
+ icon: <Timelapse />,
text: 'Last Completed',
+ subtext:
+ // chore.lastCompletedDate &&
+ // moment(chore.lastCompletedDate).format('MM/DD/YYYY hh:mm A'),
+ chore.lastCompletedDate && moment(chore.lastCompletedDate).fromNow(),
+ },
+ {
+ size: 6,
+ icon: <Person />,
+ text: 'Last Performer',
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
- })`
- : 'Never',
+ }`
+ : '--',
},
{
- icon: <Note />,
- text: 'Recent Note',
- subtext: chore.notes || '--',
+ size: 6,
+ icon: <Person />,
+ text: 'Created By',
+ subtext: performers.find(p => p.id === chore.createdBy)?.displayName,
},
+ // {
+ // size: 12,
+ // icon: <Note />,
+ // text: 'Recent Note',
+ // subtext: chore.notes || '--',
+ // },
]
setInfoCards(cards)
}
@@ -184,7 +198,16 @@ const ChoreView = () => {
setTimeoutId(id)
}
-
+ const handleSkippingTask = () => {
+ SkipChore(choreId).then(response => {
+ if (response.ok) {
+ response.json().then(data => {
+ const newChore = data.res
+ setChore(newChore)
+ })
+ }
+ })
+ }
return (
<Container
maxWidth='sm'
@@ -195,46 +218,88 @@ const ChoreView = () => {
justifyContent: 'space-between',
}}
>
- <Box>
+ <Box
+ sx={{
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ textAlign: 'center',
+ }}
+ >
<Typography
level='h3'
- textAlign={'center'}
+ // textAlign={'center'}
sx={{
- mt: 2,
- mb: 4,
+ mt: 1,
+ mb: 0.5,
}}
>
{chore.name}
</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}>
+ {infoCards.map((detail, index) => (
+ <Grid item xs={4} key={index}>
+ {/* divider between the list items: */}
- <Grid container spacing={1}>
- {infoCards.map((info, index) => (
- <Grid key={index} item xs={12} sm={6}>
- <Sheet sx={{ mb: 1, borderRadius: 'md', p: 1, boxShadow: 'sm' }}>
- <ListItem>
- <ListItemDecorator>
- <IconCard>{info.icon}</IconCard>
- </ListItemDecorator>
+ <ListItem key={index}>
<ListItemContent>
- <Typography level='body1' sx={{ fontWeight: 'md' }}>
- {info.text}
- </Typography>
- <Typography level='body1' color='text.tertiary'>
- {info.subtext ? info.subtext : '--'}
+ <Typography level='body-xs' sx={{ fontWeight: 'md' }}>
+ {detail.text}
</Typography>
+ <Chip
+ color='primary'
+ size='md'
+ startDecorator={detail.icon}
+ >
+ {detail.subtext ? detail.subtext : '--'}
+ </Chip>
</ListItemContent>
</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>
{/* <Divider
sx={{
my: 2,
}}
/> */}
-
+ <Typography level='title-md' sx={{ mt: 1 }}>
+ Actions
+ </Typography>
<Card
sx={{
p: 2,
@@ -243,27 +308,65 @@ const ChoreView = () => {
mt: 2,
}}
>
- <Typography level='title-md'>Additional Notes</Typography>
- <Input
- fullWidth
- multiline
- label='Additional Notes'
- placeholder='note or information about the task'
- value={note || ''}
- onChange={e => {
- if (e.target.value.trim() === '') {
- setNote(null)
- return
+ <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,
+ }
}
- setNote(e.target.value)
- }}
- size='md'
- sx={{
- my: 1,
- }}
- />
+ label={
+ <Typography
+ level='body-sm'
+ sx={{
+ // center vertically
+ display: 'flex',
+ alignItems: 'center',
+ }}
+ >
+ Add Additional Notes
+ </Typography>
+ }
+ />
+ </FormControl>
+ {note !== null && (
+ <Input
+ fullWidth
+ multiline
+ label='Additional Notes'
+ placeholder='note or information about the task'
+ value={note || ''}
+ onChange={e => {
+ if (e.target.value.trim() === '') {
+ setNote(null)
+ return
+ }
+ setNote(e.target.value)
+ }}
+ size='md'
+ sx={{
+ mb: 1,
+ }}
+ />
+ )}
- <FormControl size='sm' sx={{ width: 400 }}>
+ <FormControl size='sm'>
<Checkbox
defaultChecked={completedDate !== null}
checked={completedDate !== null}
@@ -279,10 +382,23 @@ const ChoreView = () => {
}
}}
overlay
- sx={{
- my: 1,
- }}
- label={<Typography level='body2'>Set completion date</Typography>}
+ sx={
+ {
+ // my: 1,
+ }
+ }
+ label={
+ <Typography
+ level='body-sm'
+ sx={{
+ // center vertically
+ display: 'flex',
+ alignItems: 'center',
+ }}
+ >
+ Specify completion date
+ </Typography>
+ }
/>
</FormControl>
{completedDate !== null && (
@@ -295,22 +411,10 @@ const ChoreView = () => {
}}
/>
)}
- {completedDate === null && (
- // placeholder for the completion date with margin:
- <Box
- sx={{
- height: 56,
- }}
- />
- )}
<Button
fullWidth
size='lg'
- sx={{
- height: 50,
- mb: 2,
- }}
onClick={handleTaskCompletion}
disabled={isPendingCompletion}
color={isPendingCompletion ? 'danger' : 'success'}
@@ -318,49 +422,82 @@ const ChoreView = () => {
>
<Box>Mark as done</Box>
</Button>
- {/* <Button
- sx={{
- borderRadius: '32px',
- mt: 1,
- height: 50,
- zIndex: 1,
- }}
- onClick={() => {
- Navigate('/my/chores')
- }}
- color={isPendingCompletion ? 'danger' : 'success'}
- startDecorator={isPendingCompletion ? <Close /> : <Check />}
- fullWidth
- >
- <Box>Mark as {isPendingCompletion ? 'completed' : 'done'}</Box>
- </Button> */}
- </Card>
+ <Divider sx={{ my: 0.5 }}>or</Divider>
- <Snackbar
- open={isPendingCompletion}
- endDecorator={
+ <Box
+ sx={{
+ display: 'flex',
+ flexDirection: 'row',
+ gap: 1,
+ alignContent: 'center',
+ justifyContent: 'center',
+ }}
+ >
<Button
+ fullWidth
+ size='lg'
onClick={() => {
- if (timeoutId) {
- clearTimeout(timeoutId)
- setIsPendingCompletion(false)
- setTimeoutId(null)
- setSecondsLeftToCancel(null) // Reset or adjust as needed
- }
+ 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'
- color='danger'
- startDecorator={<CancelScheduleSend />}
+ fullWidth
+ onClick={() => {
+ navigate(`/chores/${choreId}/history`)
+ }}
>
- Cancel
+ History
</Button>
- }
- >
- <Typography level='body-md' textAlign={'center'}>
- Task will be marked as completed in {secondsLeftToCancel} seconds
- </Typography>
- </Snackbar>
+ </Box>
+
+ <Snackbar
+ open={isPendingCompletion}
+ endDecorator={
+ <Button
+ onClick={() => {
+ if (timeoutId) {
+ clearTimeout(timeoutId)
+ setIsPendingCompletion(false)
+ setTimeoutId(null)
+ setSecondsLeftToCancel(null) // Reset or adjust as needed
+ }
+ }}
+ size='lg'
+ variant='outlined'
+ color='danger'
+ startDecorator={<CancelScheduleSend />}
+ >
+ Cancel
+ </Button>
+ }
+ >
+ <Typography level='body-md' textAlign={'center'}>
+ Task will be marked as completed in {secondsLeftToCancel} seconds
+ </Typography>
+ </Snackbar>
+ <ConfirmationModal config={confirmModelConfig} />
+ </Card>
</Container>
)
}
diff --git a/src/views/ChoreEdit/RepeatSection.jsx b/src/views/ChoreEdit/RepeatSection.jsx
index 99f196f..bdf6738 100644
--- a/src/views/ChoreEdit/RepeatSection.jsx
+++ b/src/views/ChoreEdit/RepeatSection.jsx
@@ -509,7 +509,7 @@ const RepeatSection = ({
}}
>
Is this something that should be done when a thing state changes?{' '}
- {!isPlusAccount(userProfile) && (
+ {userProfile && !isPlusAccount(userProfile) && (
<Chip variant='soft' color='warning'>
Not available in Basic Plan
</Chip>
diff --git a/src/views/Chores/ChoreCard.jsx b/src/views/Chores/ChoreCard.jsx
index 08a5406..97e407a 100644
--- a/src/views/Chores/ChoreCard.jsx
+++ b/src/views/Chores/ChoreCard.jsx
@@ -17,6 +17,7 @@ import {
SwitchAccessShortcut,
TimesOneMobiledata,
Update,
+ ViewCarousel,
Webhook,
} from '@mui/icons-material'
import {
@@ -38,7 +39,7 @@ import moment from 'moment'
import React, { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import { API_URL } from '../../Config'
-import { MarkChoreComplete } from '../../utils/Fetcher'
+import { MarkChoreComplete, SkipChore } from '../../utils/Fetcher'
import { Fetch } from '../../utils/TokenManager'
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
import DateModal from '../Modals/Inputs/DateModal'
@@ -107,6 +108,9 @@ const ChoreCard = ({
const handleEdit = () => {
navigate(`/chores/${chore.id}/edit`)
}
+ const handleView = () => {
+ navigate(`/chores/${chore.id}`)
+ }
const handleDelete = () => {
setConfirmModelConfig({
isOpen: true,
@@ -521,13 +525,7 @@ const ChoreCard = ({
</MenuItem>
<MenuItem
onClick={() => {
- Fetch(`${API_URL}/chores/${chore.id}/skip`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({}),
- }).then(response => {
+ SkipChore(chore.id).then(response => {
if (response.ok) {
response.json().then(data => {
const newChore = data.res
@@ -585,6 +583,10 @@ const ChoreCard = ({
<Edit />
Edit
</MenuItem>
+ <MenuItem onClick={handleView}>
+ <ViewCarousel />
+ View
+ </MenuItem>
<MenuItem onClick={handleDelete} color='danger'>
<Delete />
Delete
diff --git a/src/views/Chores/MyChores.jsx b/src/views/Chores/MyChores.jsx
index cd90942..d4eed5a 100644
--- a/src/views/Chores/MyChores.jsx
+++ b/src/views/Chores/MyChores.jsx
@@ -3,7 +3,6 @@ import {
Badge,
Box,
Checkbox,
- CircularProgress,
Container,
IconButton,
Input,
@@ -18,8 +17,8 @@ import Fuse from 'fuse.js'
import { useContext, useEffect, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { UserContext } from '../../contexts/UserContext'
-import Logo from '../../Logo'
import { GetAllUsers, GetChores, GetUserProfile } from '../../utils/Fetcher'
+import LoadingComponent from '../components/Loading'
import ChoreCard from './ChoreCard'
const MyChores = () => {
@@ -184,18 +183,7 @@ const MyChores = () => {
}
if (userProfile === null) {
- return (
- <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 <LoadingComponent />
}
return (
diff --git a/src/views/History/ChoreHistory.jsx b/src/views/History/ChoreHistory.jsx
index b85e456..1db1f06 100644
--- a/src/views/History/ChoreHistory.jsx
+++ b/src/views/History/ChoreHistory.jsx
@@ -1,15 +1,13 @@
-import { Checklist, EventBusy, Timelapse } from '@mui/icons-material'
+import { Checklist, EventBusy, Group, Timelapse } from '@mui/icons-material'
import {
Avatar,
- Box,
Button,
- CircularProgress,
+ Chip,
Container,
Grid,
List,
ListItem,
ListItemContent,
- ListItemDecorator,
Sheet,
Typography,
} from '@mui/joy'
@@ -19,6 +17,7 @@ import { Link, useParams } from 'react-router-dom'
import { API_URL } from '../../Config'
import { GetAllCircleMembers } from '../../utils/Fetcher'
import { Fetch } from '../../utils/TokenManager'
+import LoadingComponent from '../components/Loading'
import HistoryCard from './HistoryCard'
const ChoreHistory = () => {
@@ -92,59 +91,49 @@ const ChoreHistory = () => {
const historyInfo = [
{
- icon: (
- <Avatar>
- <Checklist />
- </Avatar>
- ),
- text: `${histories.length} completed`,
- subtext: `${Object.keys(userHistories).length} users contributed`,
+ icon: <Checklist />,
+ text: 'Total Completed',
+ subtext: `${histories.length} times`,
},
{
- icon: (
- <Avatar>
- <Timelapse />
- </Avatar>
- ),
- text: `Completed within ${moment
- .duration(averageDelayMoment)
- .humanize()}`,
- subtext: `Maximum delay was ${moment
- .duration(maxDelayMoment)
- .humanize()}`,
+ icon: <Timelapse />,
+ text: 'Usually Within',
+ subtext: moment.duration(averageDelayMoment).humanize(),
},
{
- icon: <Avatar></Avatar>,
- text: `${
+ icon: <Timelapse />,
+ text: 'Maximum Delay',
+ subtext: moment.duration(maxDelayMoment).humanize(),
+ },
+ {
+ icon: <Avatar />,
+ text: ' Completed Most',
+ subtext: `${
performers.find(p => p.userId === Number(userCompletedByMost))
?.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)
}
if (isLoading) {
- return <CircularProgress /> // Show loading indicator
+ return <LoadingComponent />
}
if (!choreHistory.length) {
return (
@@ -184,50 +173,43 @@ const ChoreHistory = () => {
return (
<Container maxWidth='md'>
- <Typography level='h3' mb={1.5}>
+ <Typography level='title-md' mb={1.5}>
Summary:
</Typography>
- {/* <Sheet sx={{ mb: 1, borderRadius: 'sm', p: 2, boxShadow: 'md' }}>
- <ListItem sx={{ gap: 1.5 }}>
- <ListItemDecorator>
- <Avatar>
- <AccountCircle />
- </Avatar>
- </ListItemDecorator>
- <ListItemContent>
- <Typography level='body1' sx={{ fontWeight: 'md' }}>
- {choreHistory.length} completed
- </Typography>
- <Typography level='body2' color='text.tertiary'>
- {Object.keys(userHistory).length} users contributed
- </Typography>
- </ListItemContent>
- </ListItem>
- </Sheet> */}
- <Grid container>
- {historyInfo.map((info, index) => (
- <Grid key={index} item xs={12} sm={6}>
- <Sheet sx={{ mb: 1, borderRadius: 'sm', p: 2, boxShadow: 'md' }}>
- <ListItem sx={{ gap: 1.5 }}>
- <ListItemDecorator>{info.icon}</ListItemDecorator>
+ <Sheet
+ // sx={{
+ // mb: 1,
+ // borderRadius: 'lg',
+ // p: 2,
+ // }}
+ sx={{ borderRadius: 'sm', p: 2 }}
+ variant='outlined'
+ >
+ <Grid container spacing={1}>
+ {historyInfo.map((info, index) => (
+ <Grid item xs={4} key={index}>
+ {/* divider between the list items: */}
+
+ <ListItem key={index}>
<ListItemContent>
- <Typography level='body1' sx={{ fontWeight: 'md' }}>
+ <Typography level='body-xs' sx={{ fontWeight: 'md' }}>
{info.text}
</Typography>
- <Typography level='body1' color='text.tertiary'>
- {info.subtext}
- </Typography>
+ <Chip color='primary' size='md' startDecorator={info.icon}>
+ {info.subtext ? info.subtext : '--'}
+ </Chip>
</ListItemContent>
</ListItem>
- </Sheet>
- </Grid>
- ))}
- </Grid>
+ </Grid>
+ ))}
+ </Grid>
+ </Sheet>
+
{/* User History Cards */}
- <Typography level='h3' my={1.5}>
+ <Typography level='title-md' my={1.5}>
History:
</Typography>
- <Box sx={{ borderRadius: 'sm', p: 2, boxShadow: 'md' }}>
+ <Sheet sx={{ borderRadius: 'sm', p: 2, boxShadow: 'md' }}>
{/* Chore History List (Updated Style) */}
<List sx={{ p: 0 }}>
@@ -241,7 +223,7 @@ const ChoreHistory = () => {
/>
))}
</List>
- </Box>
+ </Sheet>
</Container>
)
}
diff --git a/src/views/History/HistoryCard.jsx b/src/views/History/HistoryCard.jsx
index c606fbf..5e71f52 100644
--- a/src/views/History/HistoryCard.jsx
+++ b/src/views/History/HistoryCard.jsx
@@ -1,3 +1,4 @@
+import { CalendarViewDay, Check, Timelapse } from '@mui/icons-material'
import {
Avatar,
Box,
@@ -30,6 +31,50 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => {
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 (
<>
<ListItem sx={{ gap: 1.5, alignItems: 'flex-start' }}>
@@ -55,13 +100,7 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => {
<Typography level='body1' sx={{ fontWeight: 'md' }}>
{moment(historyEntry.completedAt).format('ddd MM/DD/yyyy HH:mm')}
</Typography>
-
- <Chip>
- {historyEntry.dueDate &&
- historyEntry.completedAt > historyEntry.dueDate
- ? 'Late'
- : 'On Time'}
- </Chip>
+ {getCompletedChip(historyEntry)}
</Box>
<Typography level='body2' color='text.tertiary'>
<Chip>
diff --git a/src/views/Landing/DemoHistory.jsx b/src/views/Landing/DemoHistory.jsx
index 2c713f1..4e0f45e 100644
--- a/src/views/Landing/DemoHistory.jsx
+++ b/src/views/Landing/DemoHistory.jsx
@@ -7,7 +7,7 @@ const DemoHistory = () => {
{
id: 32,
choreId: 12,
- completedAt: moment().format(),
+ completedAt: moment().hour(4).format(),
completedBy: 1,
assignedTo: 1,
notes: null,
@@ -25,8 +25,8 @@ const DemoHistory = () => {
{
id: 31,
choreId: 12,
- completedAt: moment().day(-10).format(),
- completedBy: 1,
+ completedAt: moment().day(-10).hour(1).format(),
+ completedBy: 2,
assignedTo: 1,
notes: null,
dueDate: moment().day(-10).format(),
diff --git a/src/views/Landing/FeaturesSection.jsx b/src/views/Landing/FeaturesSection.jsx
index 4133d0a..32dcbc8 100644
--- a/src/views/Landing/FeaturesSection.jsx
+++ b/src/views/Landing/FeaturesSection.jsx
@@ -25,44 +25,39 @@ const FeatureIcon = styled('div')({
const CardData = [
{
title: 'Open Source & Transparent',
- headline: 'Built for the Community',
+
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,
},
{
title: 'Circles: Your Task Hub',
- headline: 'Share & Conquer Together',
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,
},
{
title: 'Track Your Progress',
- headline: "See Who's Done What",
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,
},
{
- title: 'Automated Chore Scheduling',
- headline: 'Fully Customizable Recurring Tasks',
+ title: 'Automated Task Scheduling',
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,
},
{
title: 'Automated Task Assignment',
- headline: 'Share Responsibilities Equally',
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,
},
{
title: 'Integrations & Webhooks',
- headline: 'API & 3rd Party Integrations',
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,
},
]
@@ -80,7 +75,7 @@ function Feature2({ icon: Icon, title, headline, description, index }) {
<FeatureIcon>
<Icon
color='primary'
- style={{ Width: '30px', height: '30px' }}
+ style={{ Width: '30px', height: '30px', fontSize: '30px' }}
stroke={1.5}
/>
</FeatureIcon>
@@ -106,7 +101,7 @@ function FeaturesSection() {
<Feature2
icon={feature.icon}
title={feature.title}
- headline={feature.headline}
+ // headline={feature.headline}
description={feature.description}
index={index}
key={index}
@@ -128,7 +123,7 @@ function FeaturesSection() {
</Container>
<Typography level='h4' mt={2} mb={4}>
- Features Overview
+ Why Donetick?
</Typography>
<Container maxWidth={'lg'} sx={{ mb: 8 }}></Container>
diff --git a/src/views/Landing/Footer.jsx b/src/views/Landing/Footer.jsx
new file mode 100644
index 0000000..11a011a
--- /dev/null
+++ b/src/views/Landing/Footer.jsx
@@ -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
diff --git a/src/views/Landing/Landing.jsx b/src/views/Landing/Landing.jsx
index a8b650d..4ca1b60 100644
--- a/src/views/Landing/Landing.jsx
+++ b/src/views/Landing/Landing.jsx
@@ -1,4 +1,4 @@
-import { Container, Grid } from '@mui/joy'
+import { Box, Container, Grid } from '@mui/joy'
import AOS from 'aos'
import 'aos/dist/aos.css'
import { useEffect } from 'react'
@@ -8,6 +8,7 @@ import DemoHistory from './DemoHistory'
import DemoMyChore from './DemoMyChore'
import DemoScheduler from './DemoScheduler'
import FeaturesSection from './FeaturesSection'
+import Footer from './Footer'
import HomeHero from './HomeHero'
const Landing = () => {
const Navigate = useNavigate()
@@ -39,6 +40,17 @@ const Landing = () => {
</Grid>
<FeaturesSection />
{/* <PricingSection /> */}
+ <Box
+ sx={{
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ mt: 5,
+ mb: 5,
+ }}
+ ></Box>
+
+ <Footer />
</Container>
)
}
diff --git a/src/views/components/Loading.jsx b/src/views/components/Loading.jsx
new file mode 100644
index 0000000..a31cf20
--- /dev/null
+++ b/src/views/components/Loading.jsx
@@ -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
diff --git a/src/views/components/NavBar.jsx b/src/views/components/NavBar.jsx
index 2da09f5..d3708ae 100644
--- a/src/views/components/NavBar.jsx
+++ b/src/views/components/NavBar.jsx
@@ -21,7 +21,7 @@ import {
Typography,
} from '@mui/joy'
import { useState } from 'react'
-import { useLocation } from 'react-router-dom'
+import { useLocation, useNavigate } from 'react-router-dom'
import { version } from '../../../package.json'
import NavBarLink from './NavBarLink'
const links = [
@@ -63,6 +63,7 @@ const links = [
]
const NavBar = () => {
+ const navigate = useNavigate()
const [drawerOpen, setDrawerOpen] = useState(false)
const [openDrawer, closeDrawer] = [
() => setDrawerOpen(true),
@@ -89,7 +90,12 @@ const NavBar = () => {
<IconButton size='sm' variant='plain' onClick={() => setDrawerOpen(true)}>
<MenuRounded />
</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' />
<Typography
level='title-lg'