-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathTaskList.tsx
More file actions
147 lines (129 loc) · 4.53 KB
/
Copy pathTaskList.tsx
File metadata and controls
147 lines (129 loc) · 4.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
* TaskList Component
*
* Displays a list of tasks fetched from the ObjectStack API.
* Demonstrates READ operation using @objectstack/client.
*/
import { useEffect, useState } from 'react';
import { ObjectStackClient } from '@objectstack/client';
import type { Task } from '../types';
import { TaskItem } from './TaskItem';
interface TaskListProps {
client: ObjectStackClient;
onEdit: (task: Task) => void;
refreshTrigger: number;
}
export function TaskList({ client, onEdit, refreshTrigger }: TaskListProps) {
const [tasks, setTasks] = useState<Task[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
loadTasks();
}, [refreshTrigger]);
async function loadTasks() {
try {
setLoading(true);
setError(null);
// Use ObjectStack Client to fetch tasks
const result = await client.data.find('todo_task', {
top: 100,
sort: ['priority', '-created_at']
});
// Handle { value: [] } (PaginatedResult) and [] (Raw) formats
const rawValues = Array.isArray(result) ? result : (result.value || []);
const fetchedTasks = [...rawValues] as Task[];
// Client-side sort fallback (since InMemoryDriver has limited sort support)
// Sort by Priority (Ascending) -> Created At (Descending)
fetchedTasks.sort((a, b) => {
if (a.priority !== b.priority) {
return a.priority - b.priority;
}
const timeA = a.created_at ? new Date(a.created_at).getTime() : 0;
const timeB = b.created_at ? new Date(b.created_at).getTime() : 0;
return timeB - timeA;
});
setTasks(fetchedTasks);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load tasks');
console.error('Error loading tasks:', err);
} finally {
setLoading(false);
}
}
async function handleDelete(id: string) {
if (!confirm('Are you sure you want to delete this task?')) return;
try {
// Use ObjectStack Client to delete task
await client.data.delete('todo_task', id);
// Refresh the list
await loadTasks();
} catch (err) {
alert('Failed to delete task: ' + (err instanceof Error ? err.message : 'Unknown error'));
}
}
async function handleToggleComplete(task: Task) {
try {
// Use ObjectStack Client to update task
await client.data.update('todo_task', task.id, {
is_completed: !task.is_completed
});
// Refresh the list
await loadTasks();
} catch (err) {
alert('Failed to update task: ' + (err instanceof Error ? err.message : 'Unknown error'));
}
}
if (loading) {
return (
<div className="flex flex-col items-center justify-center py-20 text-accents-4 space-y-3">
<div className="w-6 h-6 border-2 border-accents-3 border-t-foreground rounded-full animate-spin"></div>
<p className="text-sm">Loading tasks...</p>
</div>
);
}
if (error) {
return (
<div className="text-center py-12">
<div className="inline-block p-4 border border-error-light/50 bg-error-lighter rounded-lg text-error mb-4">
{error}
</div>
<div>
<button
onClick={loadTasks}
className="text-sm font-medium text-foreground hover:text-accents-5 underline transition-colors"
>
Try Again
</button>
</div>
</div>
);
}
return (
<div className="space-y-6">
<div className="flex items-center justify-between pb-4 border-b border-accents-2">
<h2 className="text-2xl font-bold tracking-tight">Your Tasks</h2>
<span className="text-sm px-2.5 py-0.5 rounded-full bg-accents-1 text-accents-5 font-medium border border-accents-2">
{tasks?.length || 0}
</span>
</div>
{tasks?.length === 0 ? (
<div className="text-center py-20 border border-dashed border-accents-3 rounded-lg bg-accents-1">
<p className="text-accents-5 font-medium">No tasks yet</p>
<p className="text-accents-4 text-sm mt-1">Create a new task to get started!</p>
</div>
) : (
<div className="grid gap-4">
{tasks.map((task) => (
<TaskItem
key={task.id}
task={task}
onEdit={() => onEdit(task)}
onDelete={() => handleDelete(task.id)}
onToggleComplete={() => handleToggleComplete(task)}
/>
))}
</div>
)}
</div>
);
}