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
This commit is contained in:
@@ -133,7 +133,8 @@ const ChoreView = () => {
|
||||
icon: <PeopleAlt />,
|
||||
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
|
||||
|
||||
@@ -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 = ({
|
||||
<Edit sx={{ fontSize: 20 }} />
|
||||
</IconButton>
|
||||
|
||||
<IconButton
|
||||
variant='soft'
|
||||
color='warning'
|
||||
size='md'
|
||||
onClick={e => {
|
||||
e.stopPropagation()
|
||||
resetSwipe()
|
||||
setIsNudgeModalOpen(true)
|
||||
}}
|
||||
sx={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
mx: 1,
|
||||
}}
|
||||
>
|
||||
<Notifications sx={{ fontSize: 20 }} />
|
||||
</IconButton>
|
||||
|
||||
<IconButton
|
||||
variant='soft'
|
||||
color='danger'
|
||||
@@ -1192,6 +1238,7 @@ const ChoreCard = ({
|
||||
onChangeAssignee={() => setIsChangeAssigneeModalOpen(true)}
|
||||
onChangeDueDate={() => setIsChangeDueDateModalOpen(true)}
|
||||
onWriteNFC={() => setIsNFCModalOpen(true)}
|
||||
onNudge={() => setIsNudgeModalOpen(true)}
|
||||
onDelete={handleDelete}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onOpen={() => {
|
||||
@@ -1259,6 +1306,15 @@ const ChoreCard = ({
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<NudgeModal
|
||||
config={{
|
||||
isOpen: isNudgeModalOpen,
|
||||
choreId: chore.id,
|
||||
onClose: () => setIsNudgeModalOpen(false),
|
||||
onConfirm: handleNudge,
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</Box>
|
||||
<Snackbar
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
Delete,
|
||||
Edit,
|
||||
HourglassEmpty,
|
||||
Notifications,
|
||||
Pause,
|
||||
PlayArrow,
|
||||
Repeat,
|
||||
@@ -38,6 +39,7 @@ import {
|
||||
ApproveChore,
|
||||
DeleteChore,
|
||||
MarkChoreComplete,
|
||||
NudgeChore,
|
||||
PauseChore,
|
||||
RejectChore,
|
||||
StartChore,
|
||||
@@ -46,6 +48,7 @@ import {
|
||||
} from '../../utils/Fetcher'
|
||||
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'
|
||||
@@ -74,6 +77,7 @@ const CompactChoreCard = ({
|
||||
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 CompactChoreCard = ({
|
||||
|
||||
const { impersonatedUser } = useImpersonateUser()
|
||||
|
||||
const { showError } = useNotification()
|
||||
const { showError, showNotification } = useNotification()
|
||||
|
||||
// Swipe functionality state
|
||||
const [swipeTranslateX, setSwipeTranslateX] = React.useState(0)
|
||||
@@ -93,7 +97,7 @@ const CompactChoreCard = ({
|
||||
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)
|
||||
|
||||
@@ -435,6 +439,30 @@ const CompactChoreCard = ({
|
||||
})
|
||||
}
|
||||
|
||||
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 (!circleMembersData?.res || !chore) return false
|
||||
@@ -795,6 +823,24 @@ const CompactChoreCard = ({
|
||||
<Edit sx={{ fontSize: 16 }} />
|
||||
</IconButton>
|
||||
|
||||
<IconButton
|
||||
variant='soft'
|
||||
color='warning'
|
||||
size='sm'
|
||||
onClick={e => {
|
||||
e.stopPropagation()
|
||||
resetSwipe()
|
||||
setIsNudgeModalOpen(true)
|
||||
}}
|
||||
sx={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
mx: 1,
|
||||
}}
|
||||
>
|
||||
<Notifications sx={{ fontSize: 16 }} />
|
||||
</IconButton>
|
||||
|
||||
<IconButton
|
||||
variant='soft'
|
||||
color='danger'
|
||||
@@ -1220,6 +1266,7 @@ const CompactChoreCard = ({
|
||||
onChangeAssignee={() => setIsChangeAssigneeModalOpen(true)}
|
||||
onChangeDueDate={() => setIsChangeDueDateModalOpen(true)}
|
||||
onWriteNFC={() => setIsNFCModalOpen(true)}
|
||||
onNudge={() => setIsNudgeModalOpen(true)}
|
||||
onDelete={handleDelete}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
// onMouseLeave={handleMouseLeave}
|
||||
@@ -1290,6 +1337,15 @@ const CompactChoreCard = ({
|
||||
}}
|
||||
/>
|
||||
|
||||
<NudgeModal
|
||||
config={{
|
||||
isOpen: isNudgeModalOpen,
|
||||
choreId: chore.id,
|
||||
onClose: () => setIsNudgeModalOpen(false),
|
||||
onConfirm: handleNudge,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Snackbar for pending completion */}
|
||||
<Snackbar
|
||||
open={isPendingCompletion}
|
||||
|
||||
@@ -132,7 +132,12 @@ const MyChores = () => {
|
||||
// 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(
|
||||
|
||||
@@ -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 = ({
|
||||
<RecordVoiceOver />
|
||||
Delegate to someone else
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={e => {
|
||||
e.stopPropagation()
|
||||
onNudge?.()
|
||||
handleMenuClose()
|
||||
}}
|
||||
>
|
||||
<Notifications />
|
||||
Send nudge
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem
|
||||
onClick={e => {
|
||||
|
||||
Reference in New Issue
Block a user