diff --git a/src/views/Chores/ActivitesCard.jsx b/src/views/Chores/ActivitesCard.jsx
new file mode 100644
index 0000000..dbf1363
--- /dev/null
+++ b/src/views/Chores/ActivitesCard.jsx
@@ -0,0 +1,411 @@
+import {
+ CheckCircle,
+ EventNote,
+ Notes,
+ Person,
+ Refresh,
+ Toll,
+ WatchLater,
+} from '@mui/icons-material'
+import {
+ Avatar,
+ Box,
+ Chip,
+ Divider,
+ IconButton,
+ List,
+ ListItem,
+ ListItemContent,
+ ListItemDecorator,
+ Sheet,
+ Typography,
+} from '@mui/joy'
+import moment from 'moment'
+import { useChores, useChoresHistory } from '../../queries/ChoreQueries'
+import { useCircleMembers } from '../../queries/UserQueries'
+import { resolvePhotoURL } from '../../utils/Helpers'
+
+const ActivityItem = ({ activity, members }) => {
+ // Find the member who completed the activity
+ const completedByMember = members?.find(
+ member => member.userId === activity.completedBy,
+ )
+
+ const getTimeDisplay = completedAt => {
+ const now = moment()
+ const completed = moment(completedAt)
+ const diffInHours = now.diff(completed, 'hours')
+ const diffInDays = now.diff(completed, 'days')
+
+ if (diffInHours < 1) {
+ return 'Just now'
+ } else if (diffInHours < 24) {
+ return `${diffInHours}h ago`
+ } else if (diffInDays < 7) {
+ return `${diffInDays}d ago`
+ } else {
+ return completed.format('MMM DD')
+ }
+ }
+
+ const getStatusInfo = activity => {
+ if (!activity.dueDate) {
+ return {
+ color: 'neutral',
+ text: 'Completed',
+ icon: ,
+ }
+ }
+
+ const wasOnTime = moment(activity.completedAt).isSameOrBefore(
+ moment(activity.dueDate),
+ )
+
+ if (wasOnTime) {
+ return {
+ color: 'success',
+ text: 'On Time',
+ icon: ,
+ }
+ } else {
+ return {
+ color: 'warning',
+ text: 'Late',
+ icon: ,
+ }
+ }
+ }
+
+ return (
+
+
+
+ {completedByMember?.displayName?.charAt(0) ||
+ completedByMember?.name?.charAt(0) || }
+
+
+
+
+
+ {/* Activity header */}
+
+
+ {activity.choreName}
+
+
+ {getTimeDisplay(activity.completedAt)}
+
+
+
+ {/* Who completed it */}
+
+ {/* Status chip */}
+
+ {getStatusInfo(activity).text}
+
+
+ by{' '}
+ {completedByMember?.displayName ||
+ completedByMember?.name ||
+ 'Unknown'}
+
+
+
+ {/* Status, Points, and Notes */}
+
+ {/* Points chip */}
+ {activity.points && activity.points > 0 && (
+ }
+ >
+ {activity.points} pts
+
+ )}
+
+
+ {/* Notes */}
+ {activity.notes && (
+
+
+
+ {activity.notes}
+
+
+ )}
+
+
+
+ )
+}
+
+const groupActivitiesByDate = activities => {
+ const groups = {}
+
+ activities.forEach(activity => {
+ const date = moment(activity.completedAt).format('YYYY-MM-DD')
+ if (!groups[date]) {
+ groups[date] = []
+ }
+ groups[date].push(activity)
+ })
+
+ return groups
+}
+
+const ActivitiesCard = ({ title = 'Recent Activities' }) => {
+ // Use hooks to fetch data
+ const {
+ data: choresData,
+ isLoading: isChoresLoading,
+ refetch: refetchChores,
+ } = useChores(true) // Include archived chores
+
+ const {
+ data: choreHistory,
+ isLoading: isChoresHistoryLoading,
+ refetch: refetchHistory,
+ } = useChoresHistory(10, true) // Limit to 10 items, include members
+
+ const {
+ data: circleMembersData,
+ isLoading: isCircleMembersLoading,
+ refetch: refetchMembers,
+ } = useCircleMembers()
+
+ // Extract data from responses
+ const chores = choresData?.res || []
+ const members = circleMembersData?.res || []
+
+ // Refresh function to refetch all data
+ const handleRefresh = async () => {
+ await Promise.all([refetchChores(), refetchHistory(), refetchMembers()])
+ }
+
+ // Show loading state
+ if (isChoresLoading || isChoresHistoryLoading || isCircleMembersLoading) {
+ return (
+
+
+
+ {title}
+
+
+
+ Loading activities...
+
+
+
+ )
+ }
+
+ // Enrich history with chore names
+ const enrichedHistory =
+ choreHistory?.map(history => {
+ const chore = chores?.find(c => c.id === history.choreId)
+ return {
+ ...history,
+ choreName: chore?.name || 'Unknown Chore',
+ }
+ }) || []
+
+ // Sort by completion date (most recent first)
+ const sortedHistory = enrichedHistory
+ .sort(
+ (a, b) =>
+ moment(b.completedAt).valueOf() - moment(a.completedAt).valueOf(),
+ )
+ .slice(0, 10) // Show only latest 10 activities
+
+ const groupedActivities = groupActivitiesByDate(sortedHistory)
+
+ if (!sortedHistory.length) {
+ return (
+
+
+
+ {title}
+
+
+
+ No recent activities
+
+
+ )
+ }
+
+ return (
+
+ {/* Header */}
+
+
+
+
+ {title}
+
+
+
+ {sortedHistory.length}
+
+
+
+
+
+
+
+
+ {/* Scrollable activity list */}
+
+ {Object.entries(groupedActivities).map(([date, activities]) => {
+ const isToday = moment(date).isSame(moment(), 'day')
+ const isYesterday = moment(date).isSame(
+ moment().subtract(1, 'day'),
+ 'day',
+ )
+
+ let dateLabel
+ if (isToday) {
+ dateLabel = 'Today'
+ } else if (isYesterday) {
+ dateLabel = 'Yesterday'
+ } else {
+ dateLabel = moment(date).format('MMM DD')
+ }
+
+ return (
+
+ {/* Date separator */}
+
+
+
+ {dateLabel}
+
+
+
+
+ {/* Activities for this date */}
+
+ {activities.map(activity => (
+
+ ))}
+
+
+ )
+ })}
+
+
+ )
+}
+
+export default ActivitiesCard
diff --git a/src/views/History/ChoreHistory.jsx b/src/views/History/ChoreHistory.jsx
index d5b3caf..4f7595d 100644
--- a/src/views/History/ChoreHistory.jsx
+++ b/src/views/History/ChoreHistory.jsx
@@ -12,7 +12,7 @@ import {
Typography,
} from '@mui/joy'
import moment from 'moment'
-import React, { useEffect, useState } from 'react'
+import { useEffect, useState } from 'react'
import { Link, useParams } from 'react-router-dom'
import {
DeleteChoreHistory,
@@ -69,9 +69,9 @@ const ChoreHistory = () => {
const averageDelay =
histories.reduce((acc, chore) => {
- if (chore.dueDate && chore.completedAt) {
+ if (chore.dueDate && chore.performedAt) {
// Only consider chores with a due date
- return acc + moment(chore.completedAt).diff(chore.dueDate, 'hours')
+ return acc + moment(chore.performedAt).diff(chore.dueDate, 'hours')
}
return acc
}, 0) / histories.filter(chore => chore.dueDate).length
@@ -79,7 +79,7 @@ const ChoreHistory = () => {
const maximumDelay = histories.reduce((acc, chore) => {
if (chore.dueDate) {
// Only consider chores with a due date
- const delay = moment(chore.completedAt).diff(chore.dueDate, 'hours')
+ const delay = moment(chore.performedAt).diff(chore.dueDate, 'hours')
return delay > acc ? delay : acc
}
return acc
@@ -242,7 +242,7 @@ const ChoreHistory = () => {
},
onSave: updated => {
UpdateChoreHistory(choreId, editHistory.id, {
- completedAt: updated.completedAt,
+ performedAt: updated.performedAt,
dueDate: updated.dueDate,
notes: updated.notes,
}).then(res => {
diff --git a/src/views/History/HistoryCard.jsx b/src/views/History/HistoryCard.jsx
index 04e999c..5203850 100644
--- a/src/views/History/HistoryCard.jsx
+++ b/src/views/History/HistoryCard.jsx
@@ -18,15 +18,15 @@ export const getCompletedChip = historyEntry => {
// 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
+ historyEntry.performedAt > historyEntry.dueDate - 1000 * 60 * 60 * 6 &&
+ historyEntry.performedAt < historyEntry.dueDate + 1000 * 60 * 60 * 6
) {
text = 'On Time'
color = 'success'
icon =
} else if (
historyEntry.dueDate &&
- historyEntry.completedAt < historyEntry.dueDate
+ historyEntry.performedAt < historyEntry.dueDate
) {
text = 'On Time'
color = 'success'
@@ -36,7 +36,7 @@ export const getCompletedChip = historyEntry => {
// if completed after due date then it's late
else if (
historyEntry.dueDate &&
- historyEntry.completedAt > historyEntry.dueDate
+ historyEntry.performedAt > historyEntry.dueDate
) {
text = 'Late'
color = 'warning'
@@ -104,8 +104,8 @@ const HistoryCard = ({
}}
>
- {historyEntry.completedAt
- ? moment(historyEntry.completedAt).format(
+ {historyEntry.performedAt
+ ? moment(historyEntry.performedAt).format(
'ddd MM/DD/yyyy HH:mm',
)
: 'Skipped'}
@@ -150,11 +150,11 @@ const HistoryCard = ({
{/* time between two completion: */}
{index < allHistory.length - 1 &&
- allHistory[index + 1].completedAt && (
+ allHistory[index + 1].performedAt && (
{formatTimeDifference(
- historyEntry.completedAt,
- allHistory[index + 1].completedAt,
+ historyEntry.performedAt,
+ allHistory[index + 1].performedAt,
)}{' '}
before
diff --git a/src/views/Modals/EditHistoryModal.jsx b/src/views/Modals/EditHistoryModal.jsx
index 1baab49..2450759 100644
--- a/src/views/Modals/EditHistoryModal.jsx
+++ b/src/views/Modals/EditHistoryModal.jsx
@@ -14,7 +14,7 @@ import ConfirmationModal from './Inputs/ConfirmationModal'
function EditHistoryModal({ config, historyRecord }) {
useEffect(() => {
setCompletedDate(
- moment(historyRecord.completedAt).format('YYYY-MM-DDTHH:mm'),
+ moment(historyRecord.performedAt).format('YYYY-MM-DDTHH:mm'),
)
setDueDate(moment(historyRecord.dueDate).format('YYYY-MM-DDTHH:mm'))
setNotes(historyRecord.notes)
@@ -76,7 +76,7 @@ function EditHistoryModal({ config, historyRecord }) {
onClick={() =>
config.onSave({
id: historyRecord.id,
- completedAt: moment(completedDate).toISOString(),
+ performedAt: moment(completedDate).toISOString(),
dueDate: moment(dueDate).toISOString(),
notes,
})
diff --git a/src/views/User/UserActivities.jsx b/src/views/User/UserActivities.jsx
index 46a3564..d10ee0b 100644
--- a/src/views/User/UserActivities.jsx
+++ b/src/views/User/UserActivities.jsx
@@ -35,7 +35,7 @@ const groupByDate = history => {
const aggregated = {}
for (let i = 0; i < history.length; i++) {
const item = history[i]
- const date = new Date(item.completedAt).toLocaleDateString()
+ const date = new Date(item.performedAt).toLocaleDateString()
if (!aggregated[date]) {
aggregated[date] = []
}
@@ -114,7 +114,7 @@ const ChoreHistoryTimeline = ({ history }) => {
<>
{
const generateHistoryPieChartData = history => {
const totalCompleted =
- history.filter(item => item.dueDate > item.completedAt).length || 0
+ history.filter(item => item.dueDate > item.performedAt).length || 0
const totalLate =
- history.filter(item => item.dueDate < item.completedAt).length || 0
+ history.filter(item => item.dueDate < item.performedAt).length || 0
const totalNoDueDate = history.filter(item => !item.dueDate).length || 0
return [
diff --git a/src/views/User/UserPoints.jsx b/src/views/User/UserPoints.jsx
index effe8d4..8746f93 100644
--- a/src/views/User/UserPoints.jsx
+++ b/src/views/User/UserPoints.jsx
@@ -107,7 +107,7 @@ const UserPoints = () => {
})
}
history.forEach(chore => {
- const dayName = new Date(chore.completedAt).toLocaleString('en-US', {
+ const dayName = new Date(chore.performedAt).toLocaleString('en-US', {
weekday: 'short',
})
@@ -136,7 +136,7 @@ const UserPoints = () => {
})
}
history.forEach(chore => {
- const dayName = new Date(chore.completedAt).toLocaleString('en-US', {
+ const dayName = new Date(chore.performedAt).toLocaleString('en-US', {
day: 'numeric',
})
@@ -167,7 +167,7 @@ const UserPoints = () => {
})
}
history.forEach(chore => {
- const monthName = new Date(chore.completedAt).toLocaleString('en-US', {
+ const monthName = new Date(chore.performedAt).toLocaleString('en-US', {
month: 'short',
})
@@ -198,7 +198,7 @@ const UserPoints = () => {
})
}
history.forEach(chore => {
- const yearName = new Date(chore.completedAt).toLocaleString('en-US', {
+ const yearName = new Date(chore.performedAt).toLocaleString('en-US', {
year: 'numeric',
})
diff --git a/src/views/TestView/SmartTaskTitleInput.css b/src/views/components/SmartTaskTitleInput.css
similarity index 100%
rename from src/views/TestView/SmartTaskTitleInput.css
rename to src/views/components/SmartTaskTitleInput.css
diff --git a/src/views/TestView/SmartTaskTitleInput.jsx b/src/views/components/SmartTaskTitleInput.jsx
similarity index 99%
rename from src/views/TestView/SmartTaskTitleInput.jsx
rename to src/views/components/SmartTaskTitleInput.jsx
index c9fd015..1f1563b 100644
--- a/src/views/TestView/SmartTaskTitleInput.jsx
+++ b/src/views/components/SmartTaskTitleInput.jsx
@@ -1,6 +1,6 @@
import { useColorScheme } from '@mui/joy'
import { useEffect, useRef, useState } from 'react'
-import AutocompleteDropdown from './AutocompleteDropdown'
+import AutocompleteDropdown from '../TestView/AutocompleteDropdown'
import './SmartTaskTitleInput.css'
const renderHighlightedText = (text, cursorPosition) => {
const parts = []
diff --git a/src/views/components/SubTask.jsx b/src/views/components/SubTask.jsx
index 676dbc2..ae214bf 100644
--- a/src/views/components/SubTask.jsx
+++ b/src/views/components/SubTask.jsx
@@ -1,4 +1,10 @@
-import { DndContext, closestCenter } from '@dnd-kit/core'
+import {
+ DndContext,
+ PointerSensor,
+ closestCenter,
+ useSensor,
+ useSensors,
+} from '@dnd-kit/core'
import {
SortableContext,
arrayMove,
@@ -24,7 +30,7 @@ import {
ListItem,
Typography,
} from '@mui/joy'
-import React, { useState } from 'react'
+import { useState } from 'react'
import { CompleteSubTask } from '../../utils/Fetcher'
function SortableItem({
@@ -39,7 +45,17 @@ function SortableItem({
editMode,
}) {
const { attributes, listeners, setNodeRef, transform, transition } =
- useSortable({ id: task.id })
+ useSortable({
+ id: task.id,
+ // Add touch sensor options for better mobile scrolling
+ options: {
+ activationConstraint: {
+ // Require a small movement before activating drag to allow scrolling
+ delay: 250,
+ tolerance: 5,
+ },
+ },
+ })
const [isEditing, setIsEditing] = useState(false)
const [editedText, setEditedText] = useState(task.name)
@@ -58,7 +74,8 @@ function SortableItem({
alignItems: 'center',
gap: '0.5rem',
flexDirection: { xs: 'column', sm: 'row' },
- touchAction: 'none',
+ // Enable default touch behavior for scrolling
+ touchAction: 'auto',
paddingLeft: `${level * 24}px`,
}
@@ -102,7 +119,15 @@ function SortableItem({
<>
{editMode && (
-
+
)}
@@ -119,7 +144,7 @@ function SortableItem({
)}
{!hasChildren && level > 0 && (
- // Spacer for alignment not sure of better way for now it's good
+ // Spacer for alignment
)}
{
const topLevelTasks = tasks.filter(task => task.parentId === null)
+ // Create sensors for touch handling
+ const sensors = useSensors(
+ useSensor(PointerSensor, {
+ // Configure for better mobile scrolling
+ activationConstraint: {
+ delay: 100,
+ tolerance: 8,
+ },
+ }),
+ )
+
const handleToggle = taskId => {
const updatedTask = tasks.find(task => task.id === taskId)
const newCompletedAt = updatedTask.completedAt
@@ -405,9 +441,21 @@ const SubTasks = ({ editMode = true, choreId = 0, tasks = [], setTasks }) => {
return (
<>
-
+
-
+
{topLevelTasks
.sort((a, b) => a.orderId - b.orderId)
.map((task, index) => (