HotnKen commited on
Commit
f9a6f06
·
verified ·
1 Parent(s): 629a8e0

ROLE: Act as an expert React/TypeScript developer.

Browse files

GOAL: Create a single-file ToDoList.tsx functional component. The component must handle all state, CRUD logic, and UI rendering. Using JS HTML CSS

SCHEMA:

TypeScript

{
id: string; // Unique ID
text: string;
deadline: string; // ISO date string
status: 'finished' | 'unfinished';
finishedTime: string | null;
}
REQUIREMENTS:

Implement Create, Read, Update (status toggle), and Delete functions.

Use localStorage for data persistence (luu du lieu theo nguoi dung).

Implement a dynamic Search function on the text field.

Implement a Filter control for all, finished, and unfinished status.

Implement Sorting by deadline, ensuring unfinished items are prioritized.

Use simple inline CSS styles for a clean, intuitive UI (extra points: UI).

Files changed (2) hide show
  1. README.md +7 -4
  2. ToDoList.tsx +345 -0
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Taskmaster Turbo
3
- emoji: 🚀
4
  colorFrom: gray
5
- colorTo: pink
 
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: TaskMaster Turbo 🚀
 
3
  colorFrom: gray
4
+ colorTo: green
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
ToDoList.tsx ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ tsx
2
+ import React, { useState, useEffect } from 'react';
3
+
4
+ type TodoStatus = 'finished' | 'unfinished';
5
+ interface Todo {
6
+ id: string;
7
+ text: string;
8
+ deadline: string;
9
+ status: TodoStatus;
10
+ finishedTime: string | null;
11
+ }
12
+
13
+ const ToDoList: React.FC = () => {
14
+ const [todos, setTodos] = useState<Todo[]>([]);
15
+ const [inputText, setInputText] = useState('');
16
+ const [deadline, setDeadline] = useState('');
17
+ const [searchTerm, setSearchTerm] = useState('');
18
+ const [filter, setFilter] = useState<'all' | TodoStatus>('all');
19
+ const [sortBy, setSortBy] = useState<'deadline' | 'status'>('deadline');
20
+
21
+ // Load todos from localStorage on component mount
22
+ useEffect(() => {
23
+ const savedTodos = localStorage.getItem('todos');
24
+ if (savedTodos) {
25
+ setTodos(JSON.parse(savedTodos));
26
+ }
27
+ }, []);
28
+
29
+ // Save todos to localStorage whenever they change
30
+ useEffect(() => {
31
+ localStorage.setItem('todos', JSON.stringify(todos));
32
+ }, [todos]);
33
+
34
+ const addTodo = () => {
35
+ if (!inputText.trim() || !deadline) return;
36
+
37
+ const newTodo: Todo = {
38
+ id: Date.now().toString(),
39
+ text: inputText,
40
+ deadline,
41
+ status: 'unfinished',
42
+ finishedTime: null,
43
+ };
44
+
45
+ setTodos([...todos, newTodo]);
46
+ setInputText('');
47
+ setDeadline('');
48
+ };
49
+
50
+ const toggleTodoStatus = (id: string) => {
51
+ setTodos(
52
+ todos.map((todo) =>
53
+ todo.id === id
54
+ ? {
55
+ ...todo,
56
+ status: todo.status === 'finished' ? 'unfinished' : 'finished',
57
+ finishedTime: todo.status === 'finished' ? null : new Date().toISOString(),
58
+ }
59
+ : todo
60
+ )
61
+ );
62
+ };
63
+
64
+ const deleteTodo = (id: string) => {
65
+ setTodos(todos.filter((todo) => todo.id !== id));
66
+ };
67
+
68
+ const filteredTodos = todos.filter((todo) => {
69
+ const matchesSearch = todo.text.toLowerCase().includes(searchTerm.toLowerCase());
70
+ const matchesFilter = filter === 'all' || todo.status === filter;
71
+ return matchesSearch && matchesFilter;
72
+ });
73
+
74
+ const sortedTodos = [...filteredTodos].sort((a, b) => {
75
+ if (sortBy === 'status') {
76
+ // Unfinished comes first
77
+ if (a.status === 'unfinished' && b.status === 'finished') return -1;
78
+ if (a.status === 'finished' && b.status === 'unfinished') return 1;
79
+ }
80
+
81
+ // Then sort by deadline
82
+ return new Date(a.deadline).getTime() - new Date(b.deadline).getTime();
83
+ });
84
+
85
+ return (
86
+ <div style={styles.container}>
87
+ <h1 style={styles.title}>TaskMaster Turbo 🚀</h1>
88
+
89
+ <div style={styles.inputContainer}>
90
+ <input
91
+ type="text"
92
+ value={inputText}
93
+ onChange={(e) => setInputText(e.target.value)}
94
+ placeholder="What needs to be done?"
95
+ style={styles.textInput}
96
+ />
97
+ <input
98
+ type="datetime-local"
99
+ value={deadline}
100
+ onChange={(e) => setDeadline(e.target.value)}
101
+ style={styles.dateInput}
102
+ />
103
+ <button onClick={addTodo} style={styles.addButton}>
104
+ Add Task
105
+ </button>
106
+ </div>
107
+
108
+ <div style={styles.controls}>
109
+ <input
110
+ type="text"
111
+ value={searchTerm}
112
+ onChange={(e) => setSearchTerm(e.target.value)}
113
+ placeholder="Search tasks..."
114
+ style={styles.searchInput}
115
+ />
116
+
117
+ <div style={styles.filterGroup}>
118
+ <label style={styles.filterLabel}>
119
+ <input
120
+ type="radio"
121
+ checked={filter === 'all'}
122
+ onChange={() => setFilter('all')}
123
+ />
124
+ All
125
+ </label>
126
+ <label style={styles.filterLabel}>
127
+ <input
128
+ type="radio"
129
+ checked={filter === 'finished'}
130
+ onChange={() => setFilter('finished')}
131
+ />
132
+ Finished
133
+ </label>
134
+ <label style={styles.filterLabel}>
135
+ <input
136
+ type="radio"
137
+ checked={filter === 'unfinished'}
138
+ onChange={() => setFilter('unfinished')}
139
+ />
140
+ Unfinished
141
+ </label>
142
+ </div>
143
+
144
+ <div style={styles.sortGroup}>
145
+ <label style={styles.sortLabel}>
146
+ Sort by:
147
+ <select
148
+ value={sortBy}
149
+ onChange={(e) => setSortBy(e.target.value as 'deadline' | 'status')}
150
+ style={styles.sortSelect}
151
+ >
152
+ <option value="deadline">Deadline</option>
153
+ <option value="status">Status</option>
154
+ </select>
155
+ </label>
156
+ </div>
157
+ </div>
158
+
159
+ <div style={styles.todoList}>
160
+ {sortedTodos.length === 0 ? (
161
+ <p style={styles.emptyMessage}>No tasks found. Add some above!</p>
162
+ ) : (
163
+ sortedTodos.map((todo) => (
164
+ <div
165
+ key={todo.id}
166
+ style={{
167
+ ...styles.todoItem,
168
+ ...(todo.status === 'finished' ? styles.finishedItem : {}),
169
+ }}
170
+ >
171
+ <div style={styles.todoContent}>
172
+ <input
173
+ type="checkbox"
174
+ checked={todo.status === 'finished'}
175
+ onChange={() => toggleTodoStatus(todo.id)}
176
+ style={styles.checkbox}
177
+ />
178
+ <div>
179
+ <p style={styles.todoText}>{todo.text}</p>
180
+ <p style={styles.todoDeadline}>
181
+ Due: {new Date(todo.deadline).toLocaleString()}
182
+ </p>
183
+ {todo.status === 'finished' && todo.finishedTime && (
184
+ <p style={styles.finishedTime}>
185
+ Completed: {new Date(todo.finishedTime).toLocaleString()}
186
+ </p>
187
+ )}
188
+ </div>
189
+ </div>
190
+ <button
191
+ onClick={() => deleteTodo(todo.id)}
192
+ style={styles.deleteButton}
193
+ >
194
+ 🗑️
195
+ </button>
196
+ </div>
197
+ ))
198
+ )}
199
+ </div>
200
+ </div>
201
+ );
202
+ };
203
+
204
+ const styles = {
205
+ container: {
206
+ maxWidth: '800px',
207
+ margin: '0 auto',
208
+ padding: '20px',
209
+ fontFamily: 'Arial, sans-serif',
210
+ backgroundColor: '#f5f5f5',
211
+ borderRadius: '8px',
212
+ boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
213
+ },
214
+ title: {
215
+ textAlign: 'center',
216
+ color: '#333',
217
+ marginBottom: '20px',
218
+ fontSize: '2rem',
219
+ },
220
+ inputContainer: {
221
+ display: 'flex',
222
+ gap: '10px',
223
+ marginBottom: '20px',
224
+ },
225
+ textInput: {
226
+ flex: '1',
227
+ padding: '10px',
228
+ borderRadius: '4px',
229
+ border: '1px solid #ddd',
230
+ fontSize: '16px',
231
+ },
232
+ dateInput: {
233
+ padding: '10px',
234
+ borderRadius: '4px',
235
+ border: '1px solid #ddd',
236
+ },
237
+ addButton: {
238
+ padding: '10px 20px',
239
+ backgroundColor: '#4CAF50',
240
+ color: 'white',
241
+ border: 'none',
242
+ borderRadius: '4px',
243
+ cursor: 'pointer',
244
+ fontWeight: 'bold',
245
+ },
246
+ controls: {
247
+ display: 'flex',
248
+ flexDirection: 'column',
249
+ gap: '15px',
250
+ marginBottom: '20px',
251
+ backgroundColor: '#fff',
252
+ padding: '15px',
253
+ borderRadius: '4px',
254
+ boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
255
+ },
256
+ searchInput: {
257
+ padding: '10px',
258
+ borderRadius: '4px',
259
+ border: '1px solid #ddd',
260
+ fontSize: '16px',
261
+ },
262
+ filterGroup: {
263
+ display: 'flex',
264
+ gap: '15px',
265
+ },
266
+ filterLabel: {
267
+ display: 'flex',
268
+ alignItems: 'center',
269
+ gap: '5px',
270
+ },
271
+ sortGroup: {
272
+ display: 'flex',
273
+ alignItems: 'center',
274
+ gap: '10px',
275
+ },
276
+ sortLabel: {
277
+ display: 'flex',
278
+ alignItems: 'center',
279
+ gap: '5px',
280
+ },
281
+ sortSelect: {
282
+ padding: '5px',
283
+ borderRadius: '4px',
284
+ border: '1px solid #ddd',
285
+ },
286
+ todoList: {
287
+ display: 'flex',
288
+ flexDirection: 'column',
289
+ gap: '10px',
290
+ },
291
+ todoItem: {
292
+ display: 'flex',
293
+ justifyContent: 'space-between',
294
+ alignItems: 'center',
295
+ padding: '15px',
296
+ backgroundColor: '#fff',
297
+ borderRadius: '4px',
298
+ boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
299
+ },
300
+ finishedItem: {
301
+ backgroundColor: '#f0f0f0',
302
+ opacity: '0.8',
303
+ },
304
+ todoContent: {
305
+ display: 'flex',
306
+ alignItems: 'center',
307
+ gap: '15px',
308
+ },
309
+ checkbox: {
310
+ transform: 'scale(1.5)',
311
+ cursor: 'pointer',
312
+ },
313
+ todoText: {
314
+ margin: '0',
315
+ fontSize: '16px',
316
+ color: '#333',
317
+ },
318
+ todoDeadline: {
319
+ margin: '5px 0 0',
320
+ fontSize: '14px',
321
+ color: '#666',
322
+ },
323
+ finishedTime: {
324
+ margin: '5px 0 0',
325
+ fontSize: '12px',
326
+ color: '#4CAF50',
327
+ fontStyle: 'italic',
328
+ },
329
+ deleteButton: {
330
+ backgroundColor: 'transparent',
331
+ border: 'none',
332
+ cursor: 'pointer',
333
+ fontSize: '16px',
334
+ color: '#ff5252',
335
+ },
336
+ emptyMessage: {
337
+ textAlign: 'center',
338
+ color: '#666',
339
+ padding: '20px',
340
+ },
341
+ };
342
+
343
+ export default ToDoList;
344
+
345
+ </html>