From eded4b2e1628b00aefe7f5958bdee24237547bec Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Thu, 18 Sep 2025 01:14:51 -0400 Subject: [PATCH] feat: add nudge functionality to chore cards and action menu; enhance user notifications fix Filtering when imposing user to respect the sort and grouping order --- src/views/ChoreEdit/ChoreView.jsx | 3 +- src/views/Chores/ChoreCard.jsx | 60 +++++++++++++++++++++++- src/views/Chores/CompactChoreCard.jsx | 60 +++++++++++++++++++++++- src/views/Chores/MyChores.jsx | 9 +++- src/views/components/ChoreActionMenu.jsx | 12 +++++ 5 files changed, 137 insertions(+), 7 deletions(-) diff --git a/src/views/ChoreEdit/ChoreView.jsx b/src/views/ChoreEdit/ChoreView.jsx index a58f043..923a2e1 100644 --- a/src/views/ChoreEdit/ChoreView.jsx +++ b/src/views/ChoreEdit/ChoreView.jsx @@ -133,7 +133,8 @@ const ChoreView = () => { icon: , title: 'Assignment', text: `Assigned: ${ - performers.find(p => p.id === chore.assignedTo)?.displayName || 'N/A' + performers.find(p => p.userId === chore.assignedTo)?.displayName || + 'N/A' }`, subtext: ` Last: ${ chore.lastCompletedDate diff --git a/src/views/Chores/ChoreCard.jsx b/src/views/Chores/ChoreCard.jsx index 90dabd2..5b41740 100644 --- a/src/views/Chores/ChoreCard.jsx +++ b/src/views/Chores/ChoreCard.jsx @@ -4,6 +4,7 @@ import { Delete, Edit, HourglassEmpty, + Notifications, Pause, PlayArrow, Repeat, @@ -39,6 +40,7 @@ import { ApproveChore, DeleteChore, MarkChoreComplete, + NudgeChore, PauseChore, RejectChore, StartChore, @@ -48,6 +50,7 @@ import { import Priorities from '../../utils/Priorities' import ConfirmationModal from '../Modals/Inputs/ConfirmationModal' import DateModal from '../Modals/Inputs/DateModal' +import NudgeModal from '../Modals/Inputs/NudgeModal' import SelectModal from '../Modals/Inputs/SelectModal' import TextModal from '../Modals/Inputs/TextModal' import WriteNFCModal from '../Modals/Inputs/WriteNFCModal' @@ -75,6 +78,7 @@ const ChoreCard = ({ React.useState(false) const [confirmModelConfig, setConfirmModelConfig] = React.useState({}) const [isNFCModalOpen, setIsNFCModalOpen] = React.useState(false) + const [isNudgeModalOpen, setIsNudgeModalOpen] = React.useState(false) const navigate = useNavigate() const [isPendingCompletion, setIsPendingCompletion] = React.useState(false) @@ -84,7 +88,7 @@ const ChoreCard = ({ const { impersonatedUser } = useImpersonateUser() - const { showError } = useNotification() + const { showError, showNotification } = useNotification() // Swipe functionality state const [swipeTranslateX, setSwipeTranslateX] = React.useState(0) @@ -93,7 +97,7 @@ const ChoreCard = ({ const [hoverTimer, setHoverTimer] = React.useState(null) const [isTouchDevice, setIsTouchDevice] = React.useState(false) const swipeThreshold = 80 // Minimum swipe distance to reveal actions - const maxSwipeDistance = 220 // Maximum swipe distance + const maxSwipeDistance = 260 // Maximum swipe distance const dragStartX = React.useRef(0) const cardRef = React.useRef(null) @@ -261,6 +265,30 @@ const ChoreCard = ({ }) } + const handleNudge = async ({ choreId, message, notifyAllAssignees }) => { + try { + const response = await NudgeChore(choreId, { message, notifyAllAssignees }) + if (response.ok) { + const data = await response.json() + showNotification({ + type: 'success', + title: 'Nudge Sent!', + message: data.message || 'Nudge sent successfully', + }) + } else { + throw new Error('Failed to send nudge') + } + } catch (error) { + showError({ + title: 'Failed to Send Nudge', + message: error.message || 'Unable to send nudge at this time', + }) + } finally { + setIsNudgeModalOpen(false) + resetSwipe() + } + } + // Check if the current user can approve/reject (admin, manager, or task owner) const canApproveReject = () => { if (!performers || !chore) return false @@ -798,6 +826,24 @@ const ChoreCard = ({ + { + e.stopPropagation() + resetSwipe() + setIsNudgeModalOpen(true) + }} + sx={{ + width: 40, + height: 40, + mx: 1, + }} + > + + + setIsChangeAssigneeModalOpen(true)} onChangeDueDate={() => setIsChangeDueDateModalOpen(true)} onWriteNFC={() => setIsNFCModalOpen(true)} + onNudge={() => setIsNudgeModalOpen(true)} onDelete={handleDelete} onMouseEnter={handleMouseEnter} onOpen={() => { @@ -1259,6 +1306,15 @@ const ChoreCard = ({ }, }} /> + + setIsNudgeModalOpen(false), + onConfirm: handleNudge, + }} + /> { + try { + const response = await NudgeChore(choreId, { message, notifyAllAssignees }) + if (response.ok) { + const data = await response.json() + showNotification({ + type: 'success', + title: 'Nudge Sent!', + message: data.message || 'Nudge sent successfully', + }) + } else { + throw new Error('Failed to send nudge') + } + } catch (error) { + showError({ + title: 'Failed to Send Nudge', + message: error.message || 'Unable to send nudge at this time', + }) + } finally { + setIsNudgeModalOpen(false) + resetSwipe() + } + } + // Check if the current user can approve/reject (admin, manager, or task owner) const canApproveReject = () => { if (!circleMembersData?.res || !chore) return false @@ -795,6 +823,24 @@ const CompactChoreCard = ({ + { + e.stopPropagation() + resetSwipe() + setIsNudgeModalOpen(true) + }} + sx={{ + width: 40, + height: 40, + mx: 1, + }} + > + + + setIsChangeAssigneeModalOpen(true)} onChangeDueDate={() => setIsChangeDueDateModalOpen(true)} onWriteNFC={() => setIsNFCModalOpen(true)} + onNudge={() => setIsNudgeModalOpen(true)} onDelete={handleDelete} onMouseEnter={handleMouseEnter} // onMouseLeave={handleMouseLeave} @@ -1290,6 +1337,15 @@ const CompactChoreCard = ({ }} /> + setIsNudgeModalOpen(false), + onConfirm: handleNudge, + }} + /> + {/* Snackbar for pending completion */} { // Filter chores based on impersonated user if (impersonatedUser) { sortedChores = sortedChores.filter( - chore => chore.assignedTo === impersonatedUser.userId, + chore => + chore.assignedTo === impersonatedUser.userId || + chore.assignees?.some( + a => a.userId === impersonatedUser.userId, + ) || + chore.isPrivate === false, ) } @@ -1150,7 +1155,7 @@ const MyChores = () => { const section = ChoresGrouper( selectedChoreSection, chores, - ChoreFilters(userProfile)[filter], + ChoreFilters(impersonatedUser | userProfile)[filter], ) setChoreSections(section) setOpenChoreSectionsWithCache( diff --git a/src/views/components/ChoreActionMenu.jsx b/src/views/components/ChoreActionMenu.jsx index 201c3db..f6d2024 100644 --- a/src/views/components/ChoreActionMenu.jsx +++ b/src/views/components/ChoreActionMenu.jsx @@ -9,6 +9,7 @@ import { MoreVert, NextWeek, Nfc, + Notifications, NoteAdd, RecordVoiceOver, SwitchAccessShortcut, @@ -40,6 +41,7 @@ const ChoreActionMenu = ({ onChangeAssignee, onChangeDueDate, onWriteNFC, + onNudge, onDelete, onOpen, onMouseEnter, @@ -315,6 +317,16 @@ const ChoreActionMenu = ({ Delegate to someone else + { + e.stopPropagation() + onNudge?.() + handleMenuClose() + }} + > + + Send nudge + {