From 1fc1d69dae722345c7ae6d1ecbff159d04b16790 Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Sun, 15 Feb 2026 23:21:44 -0500 Subject: [PATCH] feat: add MoreVert icon button for toggle actions in multiple components --- src/views/History/ChoreHistory.jsx | 26 +++++++++++++++++++++-- src/views/History/HistoryCard.jsx | 28 ++++++++++++++++++++++--- src/views/Labels/LabelView.jsx | 33 +++++++++++++++++++++++++++--- src/views/Projects/ProjectView.jsx | 30 +++++++++++++++++++++++++-- src/views/Things/ThingsView.jsx | 31 ++++++++++++++++++++++++++-- src/views/Timer/TimerDetails.jsx | 23 +++++++++++++++++++++ 6 files changed, 159 insertions(+), 12 deletions(-) diff --git a/src/views/History/ChoreHistory.jsx b/src/views/History/ChoreHistory.jsx index ca1371e..8d62066 100644 --- a/src/views/History/ChoreHistory.jsx +++ b/src/views/History/ChoreHistory.jsx @@ -14,11 +14,19 @@ import { History, Star, Timelapse, - TrendingUp, + TrendingUp } from '@mui/icons-material' import DeleteIcon from '@mui/icons-material/Delete' import EditIcon from '@mui/icons-material/Edit' -import { Box, Button, Card, Container, Grid, Sheet, Typography } from '@mui/joy' +import { + Box, + Button, + Card, + Container, + Grid, + Sheet, + Typography +} from '@mui/joy' import moment from 'moment' import { useEffect, useState } from 'react' import { Link, useParams } from 'react-router-dom' @@ -42,6 +50,7 @@ const ChoreHistory = () => { const [isEditModalOpen, setIsEditModalOpen] = useState(false) const [editHistory, setEditHistory] = useState({}) const { confirmModalConfig, showConfirmation } = useConfirmationModal() + const [showMoreInfoId, setShowMoreInfoId] = useState(null) // React Query hooks const { data: choreHistoryData, isLoading } = useChoreHistory(choreId) @@ -298,6 +307,11 @@ const ChoreHistory = () => { {choreHistory.map((historyEntry, index) => ( { performers={performers} allHistory={choreHistory} index={index} + onToggleActions={() => { + const id = historyEntry.id || index + if (showMoreInfoId === id) { + setShowMoreInfoId(null) + } else { + setShowMoreInfoId(id) + } + }} /> ))} diff --git a/src/views/History/HistoryCard.jsx b/src/views/History/HistoryCard.jsx index aa683cd..26810ef 100644 --- a/src/views/History/HistoryCard.jsx +++ b/src/views/History/HistoryCard.jsx @@ -5,13 +5,14 @@ import { CheckCircle, EventNote, HourglassEmpty, + MoreVert, Person, Redo, ThumbDown, Timelapse, Toll, } from '@mui/icons-material' -import { Avatar, Box, Chip, Grid, Typography } from '@mui/joy' +import { Avatar, Box, Chip, Grid, IconButton, Typography } from '@mui/joy' import moment from 'moment' import { TASK_COLOR } from '../../utils/Colors.jsx' @@ -85,7 +86,13 @@ const formatTime = seconds => { /** * Compact HistoryCard component - content only */ -const HistoryCard = ({ allHistory, performers, historyEntry, index }) => { +const HistoryCard = ({ + allHistory, + performers, + historyEntry, + index, + onToggleActions, +}) => { const performer = performers.find(p => p.userId === historyEntry.completedBy) const assignedTo = performers.find(p => p.userId === historyEntry.assignedTo) @@ -149,7 +156,7 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => { borderColor: 'divider', }} > - + {/* First Row/Column: Status and Time Info */} @@ -282,6 +289,21 @@ const HistoryCard = ({ allHistory, performers, historyEntry, index }) => { + + {onToggleActions && ( + { + e.stopPropagation() + onToggleActions() + }} + > + + + )} + ) } diff --git a/src/views/Labels/LabelView.jsx b/src/views/Labels/LabelView.jsx index 4f60801..6191207 100644 --- a/src/views/Labels/LabelView.jsx +++ b/src/views/Labels/LabelView.jsx @@ -21,7 +21,7 @@ import { TrailingActions, } from '@meauxt/react-swipeable-list' import '@meauxt/react-swipeable-list/dist/styles.css' -import { Add } from '@mui/icons-material' +import { Add, MoreVert } from '@mui/icons-material' import { useQueryClient } from '@tanstack/react-query' import { useUserProfile } from '../../queries/UserQueries' import { getTextColorFromBackgroundColor } from '../../utils/Colors' @@ -30,7 +30,7 @@ import { getSafeBottomStyles } from '../../utils/SafeAreaUtils' import ConfirmationModal from '../Modals/Inputs/ConfirmationModal' import { useLabels } from './LabelQueries' -const LabelCardContent = ({ label, currentUserId }) => { +const LabelCardContent = ({ label, currentUserId, onToggleActions }) => { // Check if current user owns this label const isOwnedByCurrentUser = label.created_by === currentUserId @@ -128,6 +128,21 @@ const LabelCardContent = ({ label, currentUserId }) => { )} + + {onToggleActions && ( + { + e.stopPropagation() + onToggleActions() + }} + > + + + )} + ) } @@ -142,6 +157,7 @@ const LabelView = () => { const [currentLabel, setCurrentLabel] = useState(null) const queryClient = useQueryClient() const [confirmationModel, setConfirmationModel] = useState({}) + const [showMoreInfoId, setShowMoreInfoId] = useState(null) const handleAddLabel = () => { setCurrentLabel(null) @@ -260,6 +276,7 @@ const LabelView = () => { {userLabels.map(label => ( { } > - + { + if (showMoreInfoId === label.id) { + setShowMoreInfoId(null) + } else { + setShowMoreInfoId(label.id) + } + }} + /> ))} diff --git a/src/views/Projects/ProjectView.jsx b/src/views/Projects/ProjectView.jsx index 8552858..e702e31 100644 --- a/src/views/Projects/ProjectView.jsx +++ b/src/views/Projects/ProjectView.jsx @@ -22,7 +22,7 @@ import { TrailingActions, } from '@meauxt/react-swipeable-list' import '@meauxt/react-swipeable-list/dist/styles.css' -import { Add, Task } from '@mui/icons-material' +import { Add, MoreVert, Task } from '@mui/icons-material' import { useQueryClient } from '@tanstack/react-query' import { useChores } from '../../queries/ChoreQueries' import { useUserProfile } from '../../queries/UserQueries' @@ -38,6 +38,7 @@ const ProjectCardContent = ({ currentUserId, taskCounts = {}, onCardClick, + onToggleActions, }) => { // Check if current user owns this project const isOwnedByCurrentUser = project.created_by === currentUserId @@ -197,6 +198,21 @@ const ProjectCardContent = ({ )} + + {onToggleActions && ( + { + e.stopPropagation() + onToggleActions() + }} + > + + + )} + ) } @@ -215,6 +231,7 @@ const ProjectView = () => { const [taskCounts, setTaskCounts] = useState({}) const queryClient = useQueryClient() const [confirmationModel, setConfirmationModel] = useState({}) + const [showMoreInfoId, setShowMoreInfoId] = useState(null) const handleAddProject = () => { setCurrentProject(null) @@ -373,6 +390,9 @@ const ProjectView = () => { handleCardClick(project)} key={project.id} + swipeActionOpen={ + showMoreInfoId === project.id ? 'trailing' : null + } trailingActions={ { project={project} currentUserId={userProfile?.id} taskCounts={taskCounts} - // onCardClick={} + onToggleActions={() => { + if (showMoreInfoId === project.id) { + setShowMoreInfoId(null) + } else { + setShowMoreInfoId(project.id) + } + }} /> ))} diff --git a/src/views/Things/ThingsView.jsx b/src/views/Things/ThingsView.jsx index c63bfcd..4964f6f 100644 --- a/src/views/Things/ThingsView.jsx +++ b/src/views/Things/ThingsView.jsx @@ -11,6 +11,7 @@ import { Delete, Edit, Flip, + MoreVert, PlusOne, ToggleOff, ToggleOn, @@ -40,7 +41,7 @@ import ConfirmationModal from '../Modals/Inputs/ConfirmationModal' import CreateThingModal from '../Modals/Inputs/CreateThingModal' import EditThingStateModal from '../Modals/Inputs/EditThingState' -const ThingCardContent = ({ thing, onCardClick }) => { +const ThingCardContent = ({ thing, onCardClick, onToggleActions }) => { const getThingIcon = type => { if (type === 'text') { return @@ -183,6 +184,21 @@ const ThingCardContent = ({ thing, onCardClick }) => { + + {onToggleActions && ( + { + e.stopPropagation() + onToggleActions() + }} + > + + + )} + ) } @@ -194,6 +210,7 @@ const ThingsView = () => { const [isShowEditThingStateModal, setIsShowEditStateModal] = useState(false) const [createModalThing, setCreateModalThing] = useState(null) const [confirmModelConfig, setConfirmModelConfig] = useState({}) + const [showMoreInfoId, setShowMoreInfoId] = useState(null) const { showError, showNotification } = useNotification() useEffect(() => { @@ -388,6 +405,7 @@ const ThingsView = () => { navigate(`/things/${thing?.id}`)} key={thing.id} + swipeActionOpen={showMoreInfoId === thing.id ? 'trailing' : null} trailingActions={ { } > - + { + if (showMoreInfoId === thing.id) { + setShowMoreInfoId(null) + } else { + setShowMoreInfoId(thing.id) + } + }} + /> ))} diff --git a/src/views/Timer/TimerDetails.jsx b/src/views/Timer/TimerDetails.jsx index f54017b..9a60f58 100644 --- a/src/views/Timer/TimerDetails.jsx +++ b/src/views/Timer/TimerDetails.jsx @@ -12,6 +12,7 @@ import { BrowseGallery, Delete, Edit, + MoreVert, PauseCircle, Person, PlayArrow, @@ -56,6 +57,7 @@ const TimerDetails = () => { const [editingSessions, setEditingSessions] = useState({}) const [currentTime, setCurrentTime] = useState(new Date()) const [timerActionLoading, setTimerActionLoading] = useState(false) + const [showMoreInfoId, setShowMoreInfoId] = useState(null) const { showError, showSuccess } = useNotification() // Fetch circle members data @@ -940,6 +942,11 @@ const TimerDetails = () => { return ( { {endTime ? `→ ${endTime}` : '→ ongoing'} + + { + e.stopPropagation() + if (showMoreInfoId === pauseIndex) { + setShowMoreInfoId(null) + } else { + setShowMoreInfoId(pauseIndex) + } + }} + > + + )