Skip to content

Commit

Permalink
ui-fixes (stats)
Browse files Browse the repository at this point in the history
  • Loading branch information
sreecharan-desu committed Dec 9, 2024
1 parent d92c976 commit 7682cba
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 104 deletions.
65 changes: 49 additions & 16 deletions frontend/src/pages/user/Dashboard/Components/Addtodo.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useRecoilState } from "recoil";
import { todoDescription, todosAtom, todoTitle, messageAtom } from "../store/dashboardStore";
import { useState } from "react";
import { toast, Toaster } from 'react-hot-toast';

export function AddTodo() {
const [title, setTitle] = useRecoilState(todoTitle);
Expand Down Expand Up @@ -35,36 +36,67 @@ export function AddTodo() {
}
};

const addTodo = async () => {
if (!title.trim()) {
setMessage([{ message: 'Title cannot be empty', success: false }]);
return;
}

const handleSubmit = async (e) => {
e.preventDefault();
setIsLoading(true);
const loadingToast = toast.loading('Adding new task...', {
icon: '🚀'
});

try {
const response = await fetch('https://task-master-api-psi.vercel.app/api/v1/user/addtodo', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
authorization: 'Bearer ' + JSON.parse(localStorage.getItem('token'))
},
body: JSON.stringify({ title, description })
body: JSON.stringify({
title,
description
})
});

const data = await response.json();

if (data.success) {
if (data.todo) {
setTodos(prevTodos => [...prevTodos, data.todo]);
}
setMessage([{ message: 'Task added successfully!', success: true }]);
// Extract todo ID from the success message
const todoId = data.msg.split(':')[1].trim();

// Immediately update the todos atom with the new todo
setTodos(prevTodos => [...prevTodos, {
_id: todoId,
Title: title,
Description: description,
Completed: false,
Time: new Date().toISOString()
}]);

toast.success(`"${title}" added successfully! 🎯`, {
id: loadingToast,
duration: 3000,
position: 'top-right',
style: {
backgroundColor: '#f0fdf4',
border: '1px solid #bbf7d0',
padding: '16px',
color: '#15803d'
},
});

// Clear the form
setTitle('');
setDescription('');
} else {
setMessage([{ message: data.msg || 'Failed to add task', success: false }]);
toast.error(data.msg || 'Failed to add task', {
id: loadingToast,
icon: '❌'
});
}
} catch (error) {
setMessage([{ message: 'Error connecting to server', success: false }]);
toast.error('Network error. Please try again.', {
id: loadingToast,
icon: '⚠️'
});
} finally {
setIsLoading(false);
}
Expand All @@ -73,12 +105,13 @@ export function AddTodo() {
const handleKeyPress = (event) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
addTodo();
handleSubmit();
}
};

return (
<div className="bg-white p-6 rounded-xl shadow-sm border border-gray-100">
<Toaster position="top-right" />
<div className="space-y-4">
<div>
<input
Expand All @@ -101,7 +134,7 @@ export function AddTodo() {
</div>
<div className="flex justify-end">
<button
onClick={addTodo}
onClick={handleSubmit}
disabled={isLoading}
className={`px-6 py-2 bg-gradient-to-r from-indigo-500 to-purple-600 text-white rounded-lg
hover:from-indigo-600 hover:to-purple-700 transition-all focus:ring-2 focus:ring-indigo-500
Expand Down
39 changes: 0 additions & 39 deletions frontend/src/pages/user/Dashboard/Components/Toolbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,45 +58,6 @@ export function Toolbar({ activeView, setActiveView }) {
}
};

const fetchTodos = useCallback(async () => {
try {
const response = await fetch('https://task-master-api-psi.vercel.app/api/v1/user/gettodos', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
authorization: 'Bearer ' + JSON.parse(localStorage.getItem('token'))
}
});
const data = await response.json();
if (data.success) {
setTodos(data.todos);
prevTodosLength.current = data.todos.length;
}
} catch (error) {
console.error('Error fetching todos:', error);
// Silently fail for polling to avoid too many error toasts
}
}, [setTodos]);

// Initial fetch on mount
useEffect(() => {
fetchTodos();
}, [fetchTodos]);

// Fetch on view change
useEffect(() => {
fetchTodos();
}, [activeView, fetchTodos]);

// Setup polling every 5 seconds
useEffect(() => {
const intervalId = setInterval(() => {
fetchTodos();
}, 5000); // 5000 milliseconds = 5 seconds

// Cleanup interval on component unmount
return () => clearInterval(intervalId);
}, [fetchTodos]);

return (
<div className="bg-white p-4 rounded-xl shadow-sm border border-gray-100 mb-6">
Expand Down
136 changes: 87 additions & 49 deletions frontend/src/pages/user/Dashboard/userdashboard.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect, Suspense } from 'react';
import { useState, useEffect, Suspense, useCallback } from 'react';
import { useRecoilState, useRecoilValue } from "recoil";
import { todosAtom, Userusername } from "./store/dashboardStore";
import Todos from './Components/Todos';
Expand All @@ -15,7 +15,31 @@ export default function UserDashboard() {
const [todos, setTodos] = useRecoilState(todosAtom);
const [isLoading, setIsLoading] = useState(true);
const [isStatsLoading, setIsStatsLoading] = useState(true);

const [stats, setStats] = useState({
totalTasks: 0,
completedTasks: 0,
pendingTasks: 0,
todaysTasks: [],
thisWeekTasks: [],
todaysCompletedTasks: 0,
todaysPendingTasks: 0,
weeklyCompletedTasks: 0,
completionRate: 0
});


// authorization: 'Bearer ' + JSON.parse(localStorage.getItem('token'))
// }
// });
// const data = await response.json();
// if (data.success) {
// setTodos(data.todos);
// }
// } catch (error) {
// console.error('Error fetching todos:', error);
// }
// }

// Combined fetch for username and todos
useEffect(() => {
if(!localStorage.getItem('token')){
Expand Down Expand Up @@ -103,41 +127,55 @@ export default function UserDashboard() {
}
}, [activeView, setTodos]);

// Enhanced stats calculations with real data
const totalTasks = todos?.length || 0;
const completedTasks = todos?.filter(todo => todo.Completed)?.length || 0;
const pendingTasks = todos?.filter(todo => !todo.Completed)?.length || 0;

// Today's tasks calculations with proper date handling
const today = new Date().setHours(0, 0, 0, 0);
const todaysTasks = todos?.filter(todo => {
if (!todo.Time) return false;
const todoDate = new Date(todo.Time);
return todoDate.setHours(0, 0, 0, 0) === today;
}) || [];
// Calculate stats whenever todos changes
useEffect(() => {
const totalTasks = todos?.length || 0;
const completedTasks = todos?.filter(todo => todo.Completed)?.length || 0;
const pendingTasks = todos?.filter(todo => !todo.Completed)?.length || 0;

// Today's tasks calculations
const today = new Date().setHours(0, 0, 0, 0);
const todaysTasks = todos?.filter(todo => {
if (!todo.Time) return false;
const todoDate = new Date(todo.Time);
return todoDate.setHours(0, 0, 0, 0) === today;
}) || [];

// This week's tasks
const thisWeekStart = new Date();
thisWeekStart.setHours(0, 0, 0, 0);
thisWeekStart.setDate(thisWeekStart.getDate() - thisWeekStart.getDay());
const thisWeekTasks = todos?.filter(todo => {
if (!todo.Time) return false;
const todoDate = new Date(todo.Time);
return todoDate >= thisWeekStart;
}) || [];

// This week's tasks with proper date handling
const thisWeekStart = new Date();
thisWeekStart.setHours(0, 0, 0, 0);
thisWeekStart.setDate(thisWeekStart.getDate() - thisWeekStart.getDay());
const thisWeekTasks = todos?.filter(todo => {
if (!todo.Time) return false;
const todoDate = new Date(todo.Time);
return todoDate >= thisWeekStart;
}) || [];
// Calculate derived stats
const todaysCompletedTasks = todaysTasks.filter(todo => todo.Completed).length;
const todaysPendingTasks = todaysTasks.length - todaysCompletedTasks;
const weeklyCompletedTasks = thisWeekTasks.filter(todo => todo.Completed).length;
const completionRate = totalTasks ? Math.round((completedTasks / totalTasks) * 100) : 0;

// Calculate stats
const todaysCompletedTasks = todaysTasks.filter(todo => todo.Completed).length;
const todaysPendingTasks = todaysTasks.length - todaysCompletedTasks;
const weeklyCompletedTasks = thisWeekTasks.filter(todo => todo.Completed).length;
const completionRate = totalTasks ? Math.round((completedTasks / totalTasks) * 100) : 0;
setStats({
totalTasks,
completedTasks,
pendingTasks,
todaysTasks,
thisWeekTasks,
todaysCompletedTasks,
todaysPendingTasks,
weeklyCompletedTasks,
completionRate
});
}, [todos]);

// Get productivity message based on completion rate
const getProductivityMessage = () => {
if (completionRate >= 80) return "Outstanding productivity! 🌟";
if (completionRate >= 60) return "Great progress! Keep it up! 💪";
if (completionRate >= 40) return "You're on the right track! 🎯";
if (completionRate >= 20) return "Small steps lead to big achievements! 🌱";
if (stats.completionRate >= 80) return "Outstanding productivity! 🌟";
if (stats.completionRate >= 60) return "Great progress! Keep it up! 💪";
if (stats.completionRate >= 40) return "You're on the right track! 🎯";
if (stats.completionRate >= 20) return "Small steps lead to big achievements! 🌱";
return "Let's start achieving those goals! 🚀";
};

Expand All @@ -154,21 +192,21 @@ export default function UserDashboard() {
const getTimeBasedMessage = () => {
const hour = new Date().getHours();
if (hour < 12) {
return pendingTasks > 0
return stats.pendingTasks > 0
? "Start your day strong! 🌅"
: "Perfect morning start! ☀️";
}
if (hour < 17) {
return pendingTasks > 0
return stats.pendingTasks > 0
? "Keep the momentum going! 💪"
: "Fantastic progress today! 🌟";
}
if (hour < 21) {
return pendingTasks > 0
return stats.pendingTasks > 0
? "There's still time to achieve more! 🌙"
: "What a productive day! 🌆";
}
return pendingTasks > 0
return stats.pendingTasks > 0
? "Wrap up your day with a final push! ✨"
: "Rest well, champion! 🌙";
};
Expand Down Expand Up @@ -201,7 +239,7 @@ export default function UserDashboard() {
<div className="mt-6 flex flex-col sm:flex-row sm:items-center sm:space-x-6 space-y-2 sm:space-y-0">
<p className="text-lg text-gray-500 font-medium">
<span className="inline-block mr-2"></span>
{new Date().toLocaleDateString('en-US', {
{new Date().toLocaleDateString('en-IN', {
weekday: 'long',
year: 'numeric',
month: 'long',
Expand Down Expand Up @@ -236,16 +274,16 @@ export default function UserDashboard() {
) : (
<div className="space-y-4">
<p className="text-4xl font-bold text-gray-900 group-hover:text-indigo-600 transition-colors">
{todaysTasks.length}
{stats.todaysTasks.length}
</p>
<div className="space-y-2">
<div className="flex items-center justify-between">
<p className="text-sm font-medium text-gray-500">Completed</p>
<p className="text-sm font-bold text-green-600">{todaysCompletedTasks}</p>
<p className="text-sm font-bold text-green-600">{stats.todaysCompletedTasks}</p>
</div>
<div className="flex items-center justify-between">
<p className="text-sm font-medium text-gray-500">Pending</p>
<p className="text-sm font-bold text-orange-600">{todaysPendingTasks}</p>
<p className="text-sm font-bold text-orange-600">{stats.todaysPendingTasks}</p>
</div>
</div>
</div>
Expand All @@ -271,17 +309,17 @@ export default function UserDashboard() {
) : (
<div className="space-y-4">
<p className="text-4xl font-bold text-gray-900 group-hover:text-green-600 transition-colors">
{thisWeekTasks.length}
{stats.thisWeekTasks.length}
</p>
<div className="space-y-2">
<div className="flex items-center justify-between">
<p className="text-sm font-medium text-gray-500">Completed</p>
<p className="text-sm font-bold text-green-600">{weeklyCompletedTasks}</p>
<p className="text-sm font-bold text-green-600">{stats.weeklyCompletedTasks}</p>
</div>
<div className="flex items-center justify-between">
<p className="text-sm font-medium text-gray-500">Pending</p>
<p className="text-sm font-bold text-orange-600">
{thisWeekTasks.length - weeklyCompletedTasks}
{stats.thisWeekTasks.length - stats.weeklyCompletedTasks}
</p>
</div>
</div>
Expand All @@ -306,21 +344,21 @@ export default function UserDashboard() {
) : (
<div className="space-y-4">
<p className="text-4xl font-bold text-gray-900 group-hover:text-purple-600 transition-colors">
{completionRate}%
{stats.completionRate}%
</p>
<div className="space-y-3">
<div className="w-full bg-gray-100 rounded-full h-2">
<div
className="bg-purple-600 h-2 rounded-full transition-all duration-500 ease-out"
style={{ width: `${completionRate}%` }}
style={{ width: `${stats.completionRate}%` }}
/>
</div>
<div className="flex items-center justify-between text-sm">
<p className="font-medium text-gray-500">
{completedTasks} completed
{stats.completedTasks} completed
</p>
<p className="font-medium text-gray-500">
{totalTasks} total
{stats.totalTasks} total
</p>
</div>
</div>
Expand Down Expand Up @@ -348,9 +386,9 @@ export default function UserDashboard() {
</p>
<div className="pt-2 border-t border-gray-100">
<p className="text-sm text-gray-500 leading-relaxed">
{pendingTasks === 0
{stats.pendingTasks === 0
? "Incredible! You've completed all your tasks! Time to set new goals and keep the momentum going! 🎉"
: `You're just ${pendingTasks} task${pendingTasks === 1 ? '' : 's'} away from achieving all your goals! Keep pushing forward! 💪`
: `You're just ${stats.pendingTasks} task${stats.pendingTasks === 1 ? '' : 's'} away from achieving all your goals! Keep pushing forward! 💪`
}
</p>
</div>
Expand Down

0 comments on commit 7682cba

Please sign in to comment.