1- import React , { useState } from 'react' ;
2- import AddTask from './components/AddTask' ;
3- import TaskList from './components/TaskList' ;
1+ import React , { useState , useEffect } from 'react' ;
2+ import { motion , AnimatePresence } from 'framer-motion' ;
3+ import GlobalTaskForm from './components/GlobalTaskForm' ;
4+ import TaskBoard from './components/TaskBoard' ;
5+ import { PlusIcon } from '@heroicons/react/24/outline' ;
46
57function App ( ) {
68 const [ tasks , setTasks ] = useState ( [ ] ) ;
9+ const [ tags , setTags ] = useState ( [ ] ) ;
10+ const [ showInput , setShowInput ] = useState ( false ) ;
11+ const [ stats , setStats ] = useState ( { total : 0 , completed : 0 } ) ;
12+
13+ useEffect ( ( ) => {
14+ // Update stats whenever tasks change
15+ const completed = tasks . filter ( task => task . isCompleted ) . length ;
16+ setStats ( {
17+ total : tasks . length ,
18+ completed,
19+ remaining : tasks . length - completed
20+ } ) ;
21+ } , [ tasks ] ) ;
722
823 const addTask = ( task ) => {
924 const id = Math . floor ( Math . random ( ) * 10000 ) + 1 ;
25+
26+ // Check if there are any new tags that need to be added to our tags list
27+ if ( task . tags && task . tags . length > 0 ) {
28+ const newTags = task . tags . filter ( tag => ! tags . includes ( tag ) ) ;
29+ if ( newTags . length > 0 ) {
30+ setTags ( [ ...tags , ...newTags ] ) ;
31+ }
32+ }
33+
1034 const newTask = { id, ...task } ;
1135 setTasks ( [ ...tasks , newTask ] ) ;
36+ setShowInput ( false ) ;
1237 } ;
1338
1439 const toggleTask = ( id ) => {
@@ -23,23 +48,141 @@ function App() {
2348 setTasks ( tasks . filter ( ( task ) => task . id !== id ) ) ;
2449 } ;
2550
26- const completeAllTasks = ( ) => {
27- setTasks ( tasks . map ( task => ( { ...task , isCompleted : true } ) ) ) ;
51+ const completeAllTasks = ( taskIds = null ) => {
52+ if ( taskIds ) {
53+ // Complete specific tasks (for task lists)
54+ setTasks ( tasks . map ( task =>
55+ taskIds . includes ( task . id ) ? { ...task , isCompleted : true } : task
56+ ) ) ;
57+ } else {
58+ // Complete all tasks
59+ setTasks ( tasks . map ( task => ( { ...task , isCompleted : true } ) ) ) ;
60+ }
61+ } ;
62+
63+ const deleteCompletedTasks = ( taskIds = null ) => {
64+ if ( taskIds && taskIds . length > 0 ) {
65+ // Delete specific completed tasks (for task lists)
66+ setTasks ( tasks . filter ( task => ! taskIds . includes ( task . id ) ) ) ;
67+ } else {
68+ // Delete all completed tasks
69+ setTasks ( tasks . filter ( task => ! task . isCompleted ) ) ;
70+ }
2871 } ;
2972
30- const deleteCompletedTasks = ( ) => {
31- setTasks ( tasks . filter ( task => ! task . isCompleted ) ) ;
73+ // Handle tag management operations
74+ const handleManageTags = ( operation , oldTag , newTag = null ) => {
75+ switch ( operation ) {
76+ case 'add' :
77+ // Add a new tag if it doesn't already exist
78+ if ( ! tags . includes ( oldTag ) ) {
79+ setTags ( [ ...tags , oldTag ] ) ;
80+ }
81+ break ;
82+
83+ case 'edit' :
84+ // Update the tag in our tags list
85+ setTags ( tags . map ( tag => tag === oldTag ? newTag : tag ) ) ;
86+
87+ // Update all tasks that contain the old tag
88+ setTasks ( tasks . map ( task => {
89+ if ( task . tags && task . tags . includes ( oldTag ) ) {
90+ const updatedTags = task . tags . map ( tag =>
91+ tag === oldTag ? newTag : tag
92+ ) ;
93+ return { ...task , tags : updatedTags } ;
94+ }
95+ return task ;
96+ } ) ) ;
97+ break ;
98+
99+ case 'delete' :
100+ // Remove the tag from our tags list
101+ setTags ( tags . filter ( tag => tag !== oldTag ) ) ;
102+
103+ // Remove the tag from all tasks
104+ setTasks ( tasks . map ( task => {
105+ if ( task . tags && task . tags . includes ( oldTag ) ) {
106+ const updatedTags = task . tags . filter ( tag => tag !== oldTag ) ;
107+ return { ...task , tags : updatedTags } ;
108+ }
109+ return task ;
110+ } ) ) ;
111+ break ;
112+
113+ default :
114+ break ;
115+ }
32116 } ;
33117
34118 return (
35- < div className = "App bg-slate-300 h-screen text-center" >
36- < header className = "App-header" >
37- < h1 className = "text-4xl py-5" > Todo List</ h1 >
38- < AddTask onAdd = { addTask } />
39- < button className = "bg-green-500 bg-green-700 my-3 mx-2 text-white p-2 rounded" onClick = { completeAllTasks } > Complete All</ button >
40- < button className = "bg-red-500 bg-red-700 my-3 mx-2 text-white p-2 rounded" onClick = { deleteCompletedTasks } > Delete Completed</ button >
41- < TaskList tasks = { tasks } toggleTask = { toggleTask } deleteTask = { deleteTask } />
42- </ header >
119+ < div className = "App min-h-screen bg-gradient-to-br from-primary-50 to-secondary-50 flex flex-col items-center py-12 px-4" >
120+ < div className = "w-full max-w-6xl" >
121+ < motion . div
122+ className = "mb-6 bg-white rounded-2xl shadow-soft p-6"
123+ initial = { { opacity : 0 , y : - 20 } }
124+ animate = { { opacity : 1 , y : 0 } }
125+ transition = { { duration : 0.5 } }
126+ >
127+ < div className = "flex justify-between items-center mb-6" >
128+ < h1 className = "text-3xl font-bold text-neutral-800 tracking-tight" > Task Dashboard</ h1 >
129+ < div className = "flex items-center gap-1.5 bg-primary-50 px-3 py-1.5 rounded-full" >
130+ < span className = "text-primary-600 text-sm font-medium" > { stats . completed } /{ stats . total } done</ span >
131+ </ div >
132+ </ div >
133+
134+ < AnimatePresence >
135+ { showInput ? (
136+ < motion . div
137+ initial = { { opacity : 0 , height : 0 } }
138+ animate = { { opacity : 1 , height : 'auto' } }
139+ exit = { { opacity : 0 , height : 0 } }
140+ className = "overflow-hidden"
141+ >
142+ < GlobalTaskForm
143+ onAdd = { addTask }
144+ onCancel = { ( ) => setShowInput ( false ) }
145+ availableTags = { tags }
146+ />
147+ </ motion . div >
148+ ) : (
149+ < motion . button
150+ className = "flex items-center justify-center w-full py-3 px-4 bg-primary-500 hover:bg-primary-600 text-white rounded-xl font-medium transition-colors"
151+ onClick = { ( ) => setShowInput ( true ) }
152+ whileTap = { { scale : 0.97 } }
153+ initial = { { opacity : 0 } }
154+ animate = { { opacity : 1 } }
155+ >
156+ < PlusIcon className = "h-5 w-5 mr-2" />
157+ Add New Task
158+ </ motion . button >
159+ ) }
160+ </ AnimatePresence >
161+ </ motion . div >
162+
163+ { /* Task board with multiple task lists */ }
164+ < TaskBoard
165+ tasks = { tasks }
166+ tags = { tags }
167+ toggleTask = { toggleTask }
168+ deleteTask = { deleteTask }
169+ completeAllTasks = { completeAllTasks }
170+ deleteCompletedTasks = { deleteCompletedTasks }
171+ onManageTags = { handleManageTags }
172+ onAddTask = { addTask }
173+ />
174+
175+ { tasks . length === 0 && ! showInput && (
176+ < motion . div
177+ className = "text-center py-10 bg-white rounded-2xl shadow-soft mt-6"
178+ initial = { { opacity : 0 } }
179+ animate = { { opacity : 1 } }
180+ transition = { { delay : 0.3 } }
181+ >
182+ < p className = "text-neutral-500 text-lg" > No tasks yet. Add one to get started!</ p >
183+ </ motion . div >
184+ ) }
185+ </ div >
43186 </ div >
44187 ) ;
45188}
0 commit comments