147 lines
4.9 KiB
TypeScript
147 lines
4.9 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { WorkItem, WorkItemStatus } from '@/lib/types';
|
|
import { workItemApi } from '@/lib/api-client';
|
|
import WorkItemCard from '@/components/WorkItemCard';
|
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
import { faCheck } from '@fortawesome/free-solid-svg-icons';
|
|
|
|
export default function TodoPage() {
|
|
const [workItems, setWorkItems] = useState<WorkItem[]>([]);
|
|
const [selectedItems, setSelectedItems] = useState<Set<number>>(new Set());
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
loadWorkItems();
|
|
}, []);
|
|
|
|
const loadWorkItems = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const items = await workItemApi.getWorkItems();
|
|
setWorkItems(items);
|
|
setError(null);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Failed to load work items');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleToggle = (id: number) => {
|
|
const newSelected = new Set(selectedItems);
|
|
if (newSelected.has(id)) {
|
|
newSelected.delete(id);
|
|
} else {
|
|
newSelected.add(id);
|
|
}
|
|
setSelectedItems(newSelected);
|
|
};
|
|
|
|
const handleConfirm = async () => {
|
|
if (selectedItems.size > 0) {
|
|
try {
|
|
// Update each selected item
|
|
for (const id of selectedItems) {
|
|
const item = workItems.find(item => item.id === id);
|
|
if (item) {
|
|
const updatedItem = { ...item, status: WorkItemStatus.Completed };
|
|
await workItemApi.updateWorkItem(id, updatedItem);
|
|
}
|
|
}
|
|
|
|
// Reload the list
|
|
await loadWorkItems();
|
|
setSelectedItems(new Set());
|
|
alert(`${selectedItems.size} item(s) marked as completed!`);
|
|
} catch (err) {
|
|
alert('Failed to update items. Please try again.');
|
|
console.error('Error updating items:', err);
|
|
}
|
|
}
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="px-4 sm:px-0">
|
|
<div className="max-w-md mx-auto">
|
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
|
|
<div className="px-6 py-4 border-b border-gray-200 bg-gray-50">
|
|
<h1 className="text-lg font-semibold text-gray-900">Work Item List</h1>
|
|
</div>
|
|
<div className="p-6 text-center text-gray-500">
|
|
Loading work items...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="px-4 sm:px-0">
|
|
<div className="max-w-md mx-auto">
|
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
|
|
<div className="px-6 py-4 border-b border-gray-200 bg-gray-50">
|
|
<h1 className="text-lg font-semibold text-gray-900">Work Item List</h1>
|
|
</div>
|
|
<div className="p-6 text-center text-red-600">
|
|
Error: {error}
|
|
<button
|
|
onClick={loadWorkItems}
|
|
className="ml-2 px-3 py-1 bg-blue-600 text-white rounded text-sm hover:bg-blue-700"
|
|
>
|
|
Retry
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="px-4 sm:px-0">
|
|
<div className="max-w-md mx-auto">
|
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
|
|
<div className="px-6 py-4 border-b border-gray-200 bg-gray-50">
|
|
<h1 className="text-lg font-semibold text-gray-900">Work Item List</h1>
|
|
</div>
|
|
|
|
<div className="p-6">
|
|
<div className="mb-4">
|
|
<div className="grid grid-cols-2 gap-4 text-sm font-medium text-gray-700 mb-3">
|
|
<div>Title</div>
|
|
<div>Status</div>
|
|
</div>
|
|
|
|
<div className="space-y-3">
|
|
{workItems.map((item) => (
|
|
<WorkItemCard
|
|
key={item.id}
|
|
item={item}
|
|
showCheckbox={true}
|
|
selected={selectedItems.has(item.id)}
|
|
onToggle={handleToggle}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
onClick={handleConfirm}
|
|
disabled={selectedItems.size === 0}
|
|
className="w-full mt-6 inline-flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors duration-200"
|
|
>
|
|
<FontAwesomeIcon icon={faCheck} className="mr-2 h-4 w-4" />
|
|
Confirm ({selectedItems.size})
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |