feat: Add Time Tracking page with analytics and time distribution charts
continuous-integration/drone/push Build is passing

- Add TimeTrackingPage with pie/bar charts for time spent per label
- Add task_time_logs schema for tracking time entries
- Add /api/analytics/time-distribution endpoint
- Update sidebar navigation with time tracking link
- Update App routing for new pages
- Fix routine blocker and settings card issues
- Add German translations for analytics

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-08 23:50:10 +01:00
parent 877d0c897e
commit 74ffef48d3
12 changed files with 412 additions and 62 deletions
+2 -1
View File
@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next';
import { Home, Calendar, List, LayoutGrid, Settings, CheckSquare, Target, Trophy, LogOut, Award, Bot, CalendarOff, Bell } from 'lucide-react';
import { Home, Calendar, List, LayoutGrid, Settings, CheckSquare, Target, Trophy, LogOut, Award, Bot, CalendarOff, Bell, Clock } from 'lucide-react';
import { useQueryClient, useMutation, useQuery } from '@tanstack/react-query';
import { useToast } from "@/hooks/use-toast";
import { Button } from "@/components/ui/button";
@@ -60,6 +60,7 @@ export function AppSidebar({ user, ...props }: AppSidebarProps) {
{ title: t('navigation.kanban'), id: 'kanban', path: '/kanban', icon: LayoutGrid, color: 'text-orange-500' },
{ title: t('navigation.achievements'), id: 'achievements', path: '/achievements', icon: Trophy, color: 'text-yellow-500' },
{ title: t('navigation.leaderboard'), id: 'leaderboard', path: '/leaderboard', icon: Award, color: 'text-yellow-500' },
{ title: t('analytics.timeTracking'), id: 'time-tracking', path: '/time-tracking', icon: Clock, color: 'text-teal-500' },
...(user?.aiEnabled ? [{ title: t('navigation.aiChat', 'AI Chat'), id: 'ai-chat', path: '/ai', icon: Bot, color: 'text-indigo-500' }] : []),
{ title: t('navigation.settings'), id: 'settings', path: '/settings', icon: Settings, color: 'text-gray-500' },
]
+4 -3
View File
@@ -34,7 +34,7 @@ export function useRoutineBlocker() {
// Evening Routine Check
const eveningEnabled = settings.evening_routine_enabled !== "false";
const eveningStartTime = parseTime(settings.evening_routine_time || "17:00"); // 17:00 default
const eveningStartTime = parseTime(settings.evening_routine_time || "22:00"); // 22:00 default
// If it is Evening time (>= start time), we primarily check Evening Routine.
if (eveningEnabled) {
@@ -89,7 +89,7 @@ export function useRoutineBlocker() {
const eveningEnabled = settings.evening_routine_enabled !== "false";
// Safe parse
const [eh, em] = (settings.evening_routine_time || "17:00").split(':').map(Number);
const [eh, em] = (settings.evening_routine_time || "22:00").split(':').map(Number);
const eveningStartVal = (eh || 0) * 60 + (em || 0);
const morningEnabled = settings.morning_routine_enabled !== "false";
@@ -118,7 +118,8 @@ export function useRoutineBlocker() {
const eveningEnabled = settings.evening_routine_enabled !== "false";
if (!eveningEnabled) return false;
const [eh, em] = (settings.evening_routine_time || "17:00").split(':').map(Number);
const rawTime = settings.evening_routine_time || "22:00";
const [eh, em] = rawTime.split(':').map(Number);
const startVal = (eh || 0) * 60 + (em || 0);
if (currentTimeVal >= startVal && !isSameDay(user.lastEveningRoutine, now)) return true;
@@ -18,7 +18,7 @@ export function RoutineSettingsCard() {
const [morningEnabled, setMorningEnabled] = useState(true);
const [morningTime, setMorningTime] = useState("09:00");
const [eveningEnabled, setEveningEnabled] = useState(true);
const [eveningTime, setEveningTime] = useState("17:00");
const [eveningTime, setEveningTime] = useState("22:00");
const { data: settings, isLoading } = useQuery<Record<string, string>>({
queryKey: ['/api/admin/settings'],
@@ -34,7 +34,7 @@ export function RoutineSettingsCard() {
setMorningEnabled(settings.morning_routine_enabled !== "false");
setMorningTime(settings.morning_routine_time || "09:00");
setEveningEnabled(settings.evening_routine_enabled !== "false");
setEveningTime(settings.evening_routine_time || "17:00");
setEveningTime(settings.evening_routine_time || "22:00");
}
}, [settings]);