- Boost text contrast in both themes - Strengthen border visibility (subtle borders now visible) - Convert 39 files from hardcoded dark:/light: to CSS vars - Tertiary text now more readable on both backgrounds
88 lines
3.7 KiB
TypeScript
88 lines
3.7 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { CheckCircle, Clock, AlertTriangle, List } from 'lucide-react';
|
|
import { tasksApi, Task } from '../../lib/tasksApi';
|
|
import CompleteTaskModal from './CompleteTaskModal';
|
|
|
|
interface TasksDueTodayWidgetProps {
|
|
userId: string;
|
|
}
|
|
|
|
export default function TasksDueTodayWidget({ userId }: TasksDueTodayWidgetProps) {
|
|
const [tasks, setTasks] = useState<Task[]>([]);
|
|
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
|
|
|
|
useEffect(() => {
|
|
loadTasks();
|
|
}, [userId]);
|
|
|
|
const loadTasks = async () => {
|
|
if (!userId) return;
|
|
try {
|
|
const today = new Date().toISOString().split('T')[0];
|
|
// Fetch pending tasks assigned to user due on or before today
|
|
// Note: API filtering might need adjustment if it strictly matches dates.
|
|
// For now, let's fetch pending and filter client-side for "Due <= Today"
|
|
const data = await tasksApi.getAll({
|
|
assigneeId: userId,
|
|
status: 'PENDING'
|
|
});
|
|
|
|
const dueTasks = data.filter(t => {
|
|
if (!t.dueDate) return false;
|
|
const due = new Date(t.dueDate).toISOString().split('T')[0];
|
|
return due <= today;
|
|
});
|
|
|
|
setTasks(dueTasks);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
};
|
|
|
|
if (tasks.length === 0) return null;
|
|
|
|
return (
|
|
<div className="bg-[var(--color-bg-elevated)] rounded-2xl shadow-sm border border-[var(--color-border-default)] p-6">
|
|
<div className="flex justify-between items-center mb-4">
|
|
<h3 className="text-lg font-bold text-[var(--color-text-primary)] flex items-center gap-2">
|
|
<List className="text-[var(--color-primary)]" />
|
|
Tasks Due Today
|
|
</h3>
|
|
<span className="px-2 py-1 bg-red-100 text-red-700 text-xs font-bold rounded-full">
|
|
{tasks.length} Pending
|
|
</span>
|
|
</div>
|
|
|
|
<div className="space-y-3">
|
|
{tasks.map(task => (
|
|
<div key={task.id} className="flex items-start justify-between p-3 bg-slate-50 dark:bg-slate-700/50 rounded-xl border border-slate-100 dark:border-slate-700">
|
|
<div className="flex items-start gap-3">
|
|
<button
|
|
onClick={() => setSelectedTask(task)}
|
|
className="mt-1 w-5 h-5 rounded-full border-2 border-slate-300 dark:border-slate-500 hover:border-emerald-500 hover:bg-emerald-50 dark:hover:bg-emerald-900/20 transition-colors"
|
|
/>
|
|
<div>
|
|
<h4 className="font-medium text-[var(--color-text-primary)] text-sm">{task.title}</h4>
|
|
<div className="flex gap-2 text-xs text-[var(--color-text-tertiary)] mt-1">
|
|
{task.room && <span>📍 {task.room.name}</span>}
|
|
{task.priority === 'HIGH' && <span className="text-red-500 font-bold">High Priority</span>}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{selectedTask && (
|
|
<CompleteTaskModal
|
|
task={selectedTask}
|
|
onClose={() => setSelectedTask(null)}
|
|
onSuccess={() => {
|
|
loadTasks();
|
|
setSelectedTask(null);
|
|
}}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|