'use client' import { useState, useTransition } from 'react' import { Plus, X, CheckCircle2 } from 'lucide-react' import { createTask, claimTask, completeTask, deleteTask } from '@/lib/actions' import type { Tables } from '@/types/database' type Task = Tables<'tasks'> interface Props { tasks: Task[] members: Pick, 'id' | 'display_name' | 'user_id'>[] currentUserId: string spaceId: string /** Per-space area names. Sourced from `space_areas` server-side. */ areas?: string[] } const DEFAULT_AREAS = ['3D Printing', 'Electronics', 'Woodshop', 'Laser', 'Metal Shop', 'Facilities', 'Admin', 'Kitchen', 'General'] const RECURRENCES = ['none', 'daily', 'weekly', 'biweekly', 'monthly'] export function TasksClient({ tasks: initialTasks, members, currentUserId, spaceId, areas }: Props) { const AREAS = areas && areas.length > 0 ? areas : DEFAULT_AREAS const [tasks, setTasks] = useState(initialTasks) const [activeTab, setActiveTab] = useState<'chores' | 'ongoing' | 'mine' | 'done'>('chores') const [filterArea, setFilterArea] = useState('') const [showCreate, setShowCreate] = useState(false) const [isPending, startTransition] = useTransition() // Create form state const [form, setForm] = useState({ title: '', description: '', type: 'task', area: '', recurrence: 'none', due_date: '', }) const [formError, setFormError] = useState('') const [formLoading, setFormLoading] = useState(false) // Ongoing = recurring tasks (recurrence != 'none') // Chores = chore type OR regular tasks without recurrence (the general open pool) // Mine = tasks claimed/assigned to me // Done = completed const isDone = (t: Task) => t.status === 'done' || t.status === 'completed' const isOpen = (t: Task) => !isDone(t) const chores = tasks.filter(t => isOpen(t) && (!t.recurrence || t.recurrence === 'none')) const ongoing = tasks.filter(t => t.recurrence && t.recurrence !== 'none' && isOpen(t)) const mine = tasks.filter(t => (t.claimed_by === currentUserId || t.assigned_to === currentUserId) && isOpen(t)) const done = tasks.filter(isDone) const filtered = (list: Task[]) => filterArea ? list.filter(t => t.area === filterArea) : list const tabData = { chores: filtered(chores), ongoing: filtered(ongoing), mine: filtered(mine), done: filtered(done) } const currentList = tabData[activeTab] async function handleCreate(e: React.FormEvent) { e.preventDefault() if (!form.title.trim()) return setFormLoading(true) setFormError('') const result = await createTask(form) if (result.error) { setFormError(result.error) setFormLoading(false) return } if (result.data) { setTasks(prev => [result.data as Task, ...prev]) } setShowCreate(false) setForm({ title: '', description: '', type: 'task', area: '', recurrence: 'none', due_date: '' }) setFormLoading(false) } async function handleClaim(taskId: string) { const result = await claimTask(taskId) if (!result.error) { const member = members.find(m => m.user_id === currentUserId) setTasks(prev => prev.map(t => t.id === taskId ? { ...t, status: 'claimed', claimed_by: currentUserId, claimed_by_name: member?.display_name } : t )) } } async function handleComplete(taskId: string) { const result = await completeTask(taskId) if (!result.error) { setTasks(prev => prev.map(t => t.id === taskId ? { ...t, status: 'completed' } : t)) } } async function handleDelete(taskId: string) { const result = await deleteTask(taskId) if (!result.error) { setTasks(prev => prev.filter(t => t.id !== taskId)) } } const tabCounts = { chores: filtered(chores).length, ongoing: filtered(ongoing).length, mine: filtered(mine).length, done: filtered(done).length, } return (

Tasks & Chores

{tasks.filter(t => !isDone(t)).length} open
{(['chores', 'ongoing', 'mine', 'done'] as const).map((tab) => ( ))}
{currentList.length > 0 ? currentList.map(task => (
{isDone(task) && ( )}

{task.title}

{[ task.area, task.recurrence && task.recurrence !== 'none' ? `${task.recurrence} recurring` : null, task.claimed_by_name ? `claimed by ${task.claimed_by_name}` : null, task.assigned_to_name ? `assigned to ${task.assigned_to_name}` : null, task.due_date ? `due ${new Date(task.due_date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}` : null, ].filter(Boolean).join(' ยท ')}

{task.status.toUpperCase().replace('_', ' ')} {task.status === 'open' && ( )} {(task.status === 'claimed' || task.status === 'in_progress') && task.claimed_by === currentUserId && ( )}
)) : (

No {activeTab === 'done' ? 'completed' : activeTab === 'chores' ? 'open' : activeTab} tasks in this view

)}
{/* Create Task Modal */} {showCreate && (

New Task

setForm(f => ({ ...f, title: e.target.value }))} placeholder="Clean the laser cutters" required autoFocus className="w-full bg-background border border-border rounded px-3 py-2 font-sans text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary transition" />