diff --git a/src/contexts/RouterContext.jsx b/src/contexts/RouterContext.jsx
index 6077092..02df35f 100644
--- a/src/contexts/RouterContext.jsx
+++ b/src/contexts/RouterContext.jsx
@@ -8,6 +8,7 @@ import ForgotPasswordView from '../views/Authorization/ForgotPasswordView'
import LoginView from '../views/Authorization/LoginView'
import SignupView from '../views/Authorization/Signup'
import UpdatePasswordView from '../views/Authorization/UpdatePasswordView'
+import ChoreView from '../views/ChoreEdit/ChoreView'
import MyChores from '../views/Chores/MyChores'
import JoinCircleView from '../views/Circles/JoinCircle'
import ChoreHistory from '../views/History/ChoreHistory'
@@ -41,6 +42,10 @@ const Router = createBrowserRouter([
path: '/chores/:choreId/edit',
element: ,
},
+ {
+ path: '/chores/:choreId',
+ element: ,
+ },
{
path: '/chores/create',
element: ,
diff --git a/src/utils/Fetcher.jsx b/src/utils/Fetcher.jsx
index be3971c..0acc91f 100644
--- a/src/utils/Fetcher.jsx
+++ b/src/utils/Fetcher.jsx
@@ -51,6 +51,19 @@ const GetChoreByID = id => {
headers: HEADERS(),
})
}
+const GetChoreDetailById = id => {
+ return Fetch(`${API_URL}/chores/${id}/details`, {
+ method: 'GET',
+ headers: HEADERS(),
+ })
+}
+
+const MarkChoreComplete = id => {
+ return Fetch(`${API_URL}/chores/${id}/do`, {
+ method: 'POST',
+ headers: HEADERS(),
+ })
+}
const CreateChore = chore => {
return Fetch(`${API_URL}/chores/`, {
method: 'POST',
@@ -238,6 +251,7 @@ export {
GetAllCircleMembers,
GetAllUsers,
GetChoreByID,
+ GetChoreDetailById,
GetChoreHistory,
GetChores,
GetCircleMemberRequests,
@@ -250,6 +264,7 @@ export {
JoinCircle,
LeaveCircle,
login,
+ MarkChoreComplete,
SaveChore,
SaveThing,
signUp,
diff --git a/src/views/Authorization/LoginView.jsx b/src/views/Authorization/LoginView.jsx
index 2ffcef4..721a5b3 100644
--- a/src/views/Authorization/LoginView.jsx
+++ b/src/views/Authorization/LoginView.jsx
@@ -62,7 +62,6 @@ const LoginView = () => {
}
const loggedWithProvider = function (provider, data) {
- console.log(provider, data)
return fetch(API_URL + `/auth/${provider}/callback`, {
method: 'POST',
headers: {
@@ -80,8 +79,14 @@ const LoginView = () => {
return response.json().then(data => {
localStorage.setItem('ca_token', data.token)
localStorage.setItem('ca_expiration', data.expire)
- // setIsLoggedIn(true);
- getUserProfileAndNavigateToHome()
+
+ const redirectUrl = Cookies.get('ca_redirect')
+ if (redirectUrl) {
+ Cookies.remove('ca_redirect')
+ Navigate(redirectUrl)
+ } else {
+ getUserProfileAndNavigateToHome()
+ }
})
}
return response.json().then(error => {
diff --git a/src/views/ChoreEdit/ChoreView.jsx b/src/views/ChoreEdit/ChoreView.jsx
new file mode 100644
index 0000000..8116270
--- /dev/null
+++ b/src/views/ChoreEdit/ChoreView.jsx
@@ -0,0 +1,292 @@
+import {
+ CalendarMonth,
+ CancelScheduleSend,
+ Check,
+ Checklist,
+ PeopleAlt,
+ Person,
+} from '@mui/icons-material'
+import {
+ Box,
+ Button,
+ Container,
+ Grid,
+ ListItem,
+ ListItemContent,
+ ListItemDecorator,
+ Sheet,
+ Snackbar,
+ styled,
+ Typography,
+} from '@mui/joy'
+import moment from 'moment'
+import { useEffect, useState } from 'react'
+import { useParams, useSearchParams } from 'react-router-dom'
+import {
+ GetAllUsers,
+ GetChoreDetailById,
+ MarkChoreComplete,
+} from '../../utils/Fetcher'
+const IconCard = styled('div')({
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ backgroundColor: '#f0f0f0', // Adjust the background color as needed
+ borderRadius: '50%',
+ minWidth: '50px',
+ height: '50px',
+ marginRight: '16px',
+})
+const ChoreView = () => {
+ const [chore, setChore] = useState({})
+
+ const [performers, setPerformers] = useState([])
+ const [infoCards, setInfoCards] = useState([])
+ const { choreId } = useParams()
+
+ // query param `complete=true`
+
+ const [searchParams] = useSearchParams()
+
+ const [isPendingCompletion, setIsPendingCompletion] = useState(false)
+ const [timeoutId, setTimeoutId] = useState(null)
+ const [secondsLeftToCancel, setSecondsLeftToCancel] = useState(null)
+ useEffect(() => {
+ Promise.all([
+ GetChoreDetailById(choreId).then(resp => {
+ if (resp.ok) {
+ return resp.json().then(data => {
+ setChore(data.res)
+ })
+ }
+ }),
+ GetAllUsers()
+ .then(response => response.json())
+ .then(data => {
+ setPerformers(data.res)
+ }),
+ ])
+ const auto_complete = searchParams.get('auto_complete')
+ if (auto_complete === 'true') {
+ handleTaskCompletion()
+ }
+ }, [])
+ useEffect(() => {
+ if (chore && performers.length > 0) {
+ generateInfoCards(chore)
+ }
+ }, [chore, performers])
+
+ const generateInfoCards = chore => {
+ const cards = [
+ {
+ icon: ,
+ text: 'Due Date',
+ subtext: moment(chore.dueDate).format('MM/DD/YYYY hh:mm A'),
+ },
+ {
+ icon: ,
+ text: 'Assigned To',
+ subtext: performers.find(p => p.id === chore.assignedTo)?.displayName,
+ },
+ {
+ icon: ,
+ text: 'Created By',
+ subtext: performers.find(p => p.id === chore.createdBy)?.displayName,
+ },
+ // {
+ // icon: ,
+ // text: 'Frequency',
+ // subtext:
+ // chore.frequencyType.charAt(0).toUpperCase() +
+ // chore.frequencyType.slice(1),
+ // },
+ {
+ icon: ,
+ text: 'Total Completed',
+ subtext: `${chore.totalCompletedCount}`,
+ },
+ // {
+ // icon: ,
+ // text: 'Last Completed',
+ // subtext:
+ // chore.lastCompletedDate &&
+ // moment(chore.lastCompletedDate).format('MM/DD/YYYY hh:mm A'),
+ // },
+ {
+ icon: ,
+ text: 'Last Completed',
+ subtext: chore.lastCompletedDate
+ ? `${
+ chore.lastCompletedDate &&
+ moment(chore.lastCompletedDate).format('MM/DD/YYYY hh:mm A')
+ }(${
+ performers.find(p => p.id === chore.lastCompletedBy)?.displayName
+ })`
+ : 'Never',
+ },
+ ]
+ setInfoCards(cards)
+ }
+ const handleTaskCompletion = () => {
+ setIsPendingCompletion(true)
+ let seconds = 3 // Starting countdown from 3 seconds
+ setSecondsLeftToCancel(seconds)
+
+ const countdownInterval = setInterval(() => {
+ seconds -= 1
+ setSecondsLeftToCancel(seconds)
+
+ if (seconds <= 0) {
+ clearInterval(countdownInterval) // Stop the countdown when it reaches 0
+ }
+ }, 1000)
+
+ const id = setTimeout(() => {
+ MarkChoreComplete(choreId)
+ .then(resp => {
+ if (resp.ok) {
+ return resp.json().then(data => {
+ setChore(data.res)
+ })
+ }
+ })
+ .then(() => {
+ setIsPendingCompletion(false)
+ clearTimeout(id)
+ clearInterval(countdownInterval) // Ensure to clear this interval as well
+ setTimeoutId(null)
+ setSecondsLeftToCancel(null)
+ })
+ .then(() => {
+ // refetch the chore details
+ GetChoreDetailById(choreId).then(resp => {
+ if (resp.ok) {
+ return resp.json().then(data => {
+ setChore(data.res)
+ })
+ }
+ })
+ })
+ }, 3000)
+
+ setTimeoutId(id)
+ }
+
+ return (
+
+
+
+
+ {chore.name}
+
+
+
+ {infoCards.map((info, index) => (
+
+
+
+
+ {info.icon}
+
+
+
+ {info.text}
+
+
+ {info.subtext ? info.subtext : '--'}
+
+
+
+
+
+ ))}
+
+
+
+ }
+ >
+ Mark as done
+
+ {/* */}
+
+
+ {
+ if (timeoutId) {
+ clearTimeout(timeoutId)
+ setIsPendingCompletion(false)
+ setTimeoutId(null)
+ setSecondsLeftToCancel(null) // Reset or adjust as needed
+ }
+ }}
+ size='md'
+ variant='outlined'
+ color='primary'
+ startDecorator={}
+ >
+ Cancel
+
+ }
+ >
+
+ Task will be marked as completed in {secondsLeftToCancel} seconds
+
+
+
+ )
+}
+
+export default ChoreView
diff --git a/src/views/Chores/ChoreCard.jsx b/src/views/Chores/ChoreCard.jsx
index ee25458..305f7c4 100644
--- a/src/views/Chores/ChoreCard.jsx
+++ b/src/views/Chores/ChoreCard.jsx
@@ -35,12 +35,13 @@ import moment from 'moment'
import React, { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import { API_URL } from '../../Config'
-import writeToNFC from '../../service/NFCWriter'
+import { MarkChoreComplete } from '../../utils/Fetcher'
import { Fetch } from '../../utils/TokenManager'
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
import DateModal from '../Modals/Inputs/DateModal'
import SelectModal from '../Modals/Inputs/SelectModal'
import TextModal from '../Modals/Inputs/TextModal'
+import WriteNFCModal from '../Modals/Inputs/WriteNFCModal'
const ChoreCard = ({ chore, performers, onChoreUpdate, onChoreRemove, sx }) => {
const [activeUserId, setActiveUserId] = React.useState(0)
const [isChangeDueDateModalOpen, setIsChangeDueDateModalOpen] =
@@ -52,6 +53,7 @@ const ChoreCard = ({ chore, performers, onChoreUpdate, onChoreRemove, sx }) => {
const [isCompleteWithNoteModalOpen, setIsCompleteWithNoteModalOpen] =
React.useState(false)
const [confirmModelConfig, setConfirmModelConfig] = React.useState({})
+ const [isNFCModalOpen, setIsNFCModalOpen] = React.useState(false)
const [anchorEl, setAnchorEl] = React.useState(null)
const menuRef = React.useRef(null)
const navigate = useNavigate()
@@ -116,9 +118,7 @@ const ChoreCard = ({ chore, performers, onChoreUpdate, onChoreRemove, sx }) => {
}
const handleCompleteChore = () => {
- Fetch(`${API_URL}/chores/${chore.id}/do`, {
- method: 'POST',
- }).then(response => {
+ MarkChoreComplete(chore.id).then(response => {
if (response.ok) {
response.json().then(data => {
const newChore = data.res
@@ -323,7 +323,6 @@ const ChoreCard = ({ chore, performers, onChoreUpdate, onChoreRemove, sx }) => {
}}
>
{getFrequencyIcon(chore)}
-
{getRecurrentChipText(chore)}
@@ -344,7 +343,13 @@ const ChoreCard = ({ chore, performers, onChoreUpdate, onChoreRemove, sx }) => {
}}
>
-
+ {
+ navigate(`/chores/${chore.id}`)
+ }}
+ >
{/* Box in top right with Chip showing next due date */}
@@ -408,7 +413,7 @@ const ChoreCard = ({ chore, performers, onChoreUpdate, onChoreRemove, sx }) => {
disabled={isDisabled}
sx={{
borderRadius: '50%',
- width: 50,
+ minWidth: 50,
height: 50,
zIndex: 1,
}}
@@ -523,7 +528,8 @@ const ChoreCard = ({ chore, performers, onChoreUpdate, onChoreRemove, sx }) => {