import '@meauxt/react-swipeable-list/dist/styles.css'
import {
SwipeableList,
SwipeableListItem,
SwipeAction,
TrailingActions,
Type as ListType,
} from '@meauxt/react-swipeable-list'
import { Add, MoreVert, Task } from '@mui/icons-material'
import DeleteIcon from '@mui/icons-material/Delete'
import EditIcon from '@mui/icons-material/Edit'
import {
Avatar,
Box,
Chip,
CircularProgress,
Container,
IconButton,
Stack,
Typography,
} from '@mui/joy'
import { useQueryClient } from '@tanstack/react-query'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { useChores } from '../../queries/ChoreQueries'
import { useUserProfile } from '../../queries/UserQueries'
import { getTextColorFromBackgroundColor } from '../../utils/Colors'
import { DeleteProject } from '../../utils/Fetcher'
import { getIconComponent } from '../../utils/ProjectIcons'
import { getSafeBottomStyles } from '../../utils/SafeAreaUtils'
import { useProjectFilter } from '../Chores/hooks/useProjectFilter'
import ConfirmationModal from '../Modals/Inputs/ConfirmationModal'
import ProjectModal from '../Modals/Inputs/ProjectModal'
import { useProjects } from './ProjectQueries'
const ProjectCardContent = ({
currentUserId,
onCardClick,
onToggleActions,
project,
taskCounts = {},
}) => {
const { t } = useTranslation('projects')
// Check if current user owns this project
const isOwnedByCurrentUser = project.created_by === currentUserId
const isDefaultProject = project.id === 'default'
const taskCount = taskCounts[project.id] || 0
return (
{/* Project Avatar */}
{project.icon ? (
(() => {
const IconComponent = getIconComponent(project.icon)
return (
)
})()
) : (
<>>
)}
{/* Content - Center */}
{/* Project Name */}
{project.name}
{isDefaultProject && (
{t('defaultChip')}
)}
{/* Project Info */}
{project.description && (
{project.description}
)}
}
sx={{
fontSize: 10,
height: 18,
px: 0.75,
bgcolor: 'primary.softBg',
color: 'primary.500',
}}
>
{t('tasks', { count: taskCount })}
{!isOwnedByCurrentUser && !isDefaultProject && (
{t('shared')}
)}
{onToggleActions && (
{
e.stopPropagation()
onToggleActions()
}}
>
)}
)
}
const ProjectView = () => {
const { t } = useTranslation('projects')
const { data: projects, isError, isProjectsLoading } = useProjects()
const { data: userProfile } = useUserProfile()
const { data: chores = { res: [] } } = useChores(false) // false to exclude archived
const { data: projectsData = [], isLoading: projectsLoading } = useProjects()
const { setSelectedProjectWithCache } = useProjectFilter(
projectsData,
!projectsLoading,
)
const navigate = useNavigate()
const [searchParams, setSearchParams] = useSearchParams()
const [userProjects, setUserProjects] = useState([])
const [modalOpen, setModalOpen] = useState(false)
const [currentProject, setCurrentProject] = useState(null)
const [taskCounts, setTaskCounts] = useState({})
const queryClient = useQueryClient()
const [confirmationModel, setConfirmationModel] = useState({})
const [showMoreInfoId, setShowMoreInfoId] = useState(null)
const handleAddProject = () => {
setCurrentProject(null)
setModalOpen(true)
}
const handleEditProject = project => {
setCurrentProject(project)
setModalOpen(true)
}
const handleDeleteClicked = id => {
const project = userProjects.find(p => p.id === id)
setConfirmationModel({
isOpen: true,
title: t('delete.title'),
message: t('delete.message', { name: project?.name }),
confirmText: t('common:delete'),
color: 'danger',
cancelText: t('common:cancel'),
onClose: confirmed => {
if (confirmed === true) {
handleDeleteProject(id)
}
setConfirmationModel({})
},
})
}
const handleDeleteProject = id => {
DeleteProject(id).then(() => {
const updatedProjects = userProjects.filter(project => project.id !== id)
setUserProjects(updatedProjects)
queryClient.invalidateQueries('projects')
// If the deleted project was the active project, clear it
const saved = localStorage.getItem('selectedProject')
if (saved) {
const savedProject = JSON.parse(saved)
if (savedProject && savedProject.id === id) {
setSelectedProjectWithCache(null)
}
}
})
}
const handleSaveProject = () => {
setModalOpen(false)
}
const handleCardClick = project => {
// Always navigate to MyChores with project filter when clicking on the card
// For default project, use 'default', for others use project ID
const projectIdentifier = project.id === 'default' ? 'default' : project.id
setSelectedProjectWithCache(project)
navigate(`/chores?project=${encodeURIComponent(projectIdentifier)}`)
}
useEffect(() => {
if (projects) {
setUserProjects(projects)
}
}, [projects])
// ?create=1 lets other surfaces (global search quick actions) land here with
// the create modal already open.
useEffect(() => {
if (searchParams.get('create') !== '1') return
setCurrentProject(null)
setModalOpen(true)
setSearchParams(
params => {
params.delete('create')
return params
},
{ replace: true },
)
}, [searchParams, setSearchParams])
// Calculate real task counts from chores data
useEffect(() => {
if (chores && chores.res) {
const choresList = chores.res
const realCounts = {}
// First, count tasks for the default project (tasks without a projectId)
const defaultProjectCount = choresList.filter(chore => {
const choreProjectId = chore.projectId || chore.project_id
return (
!choreProjectId ||
choreProjectId === '' ||
choreProjectId === 'default' ||
choreProjectId === null
)
}).length
realCounts['default'] = defaultProjectCount
// Then count tasks for each user project
userProjects.forEach(project => {
const choreCount = choresList.filter(chore => {
const choreProjectId = chore.projectId || chore.project_id
return choreProjectId === project.id
}).length
realCounts[project.id] = choreCount
})
setTaskCounts(realCounts)
}
}, [chores, userProjects])
if (isProjectsLoading) {
return (
)
}
if (isError) {
return (
{t('loadError')}
)
}
return (
{t('common:navigation.projects')}
{t('blurb')}
{/* Default project - not swipeable */}
handleCardClick({
id: 'default',
name: t('chores:toolbar.defaultProject'),
icon: 'FolderOpen',
color: '#1976d2',
})
}
/>
{/* User projects - swipeable */}
{userProjects.map(project => (
handleCardClick(project)}
key={project.id}
swipeActionOpen={
showMoreInfoId === project.id ? 'trailing' : null
}
trailingActions={
handleEditProject(project)}>
{t('common:edit')}
handleDeleteClicked(project.id)}
>
{t('common:delete')}
}
>
{
if (showMoreInfoId === project.id) {
setShowMoreInfoId(null)
} else {
setShowMoreInfoId(project.id)
}
}}
/>
))}
{modalOpen && (
setModalOpen(false)}
onSave={handleSaveProject}
project={currentProject}
/>
)}
)
}
export default ProjectView