'use client'; import React, { useState, useEffect } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Checkbox } from '@/components/ui/checkbox'; import { Calendar, Filter, Download, RefreshCw, TrendingUp, Clock, Users, Target, Activity, BarChart3, Settings, AlertCircle, CheckCircle } from 'lucide-react'; import { TimelineView } from '@/components/analytics/TimelineView'; import { ScoreCard, ActivityScoreCard, ContentScoreCard, TimelinessScoreCard, AggregateScoreCard } from '@/components/analytics/ScoreCard'; import { AnalysisPanel } from '@/components/analytics/AnalysisPanel'; import { TimeEntry } from '@/lib/types/database'; import { TimelineEvent, AnalyticsInsight, LLMAnalysisResponse, AggregateAnalysis } from '@/lib/types/analytics'; export default function TimeEntriesAnalyticsPage() { const [loading, setLoading] = useState(true); const [timeEntries, setTimeEntries] = useState([]); const [timelineEvents, setTimelineEvents] = useState([]); const [analysis, setAnalysis] = useState(null); const [insights, setInsights] = useState([]); const [llmAnalysis, setLlmAnalysis] = useState(undefined); // Filter states const [timeRange, setTimeRange] = useState<'hour' | 'day' | 'week' | 'month'>('day'); const [selectedResources, setSelectedResources] = useState([]); const [selectedProjects, setSelectedProjects] = useState([]); const [selectedTickets, setSelectedTickets] = useState([]); const [startDate, setStartDate] = useState(''); const [endDate, setEndDate] = useState(''); const [minHours, setMinHours] = useState(''); const [maxHours, setMaxHours] = useState(''); const [billable, setBillable] = useState(undefined); const [approved, setApproved] = useState(undefined); // Mock data for demonstration useEffect(() => { loadMockData(); }, []); const loadMockData = async () => { setLoading(true); try { // Build query parameters const params = new URLSearchParams(); if (startDate) params.append('start_date', startDate); if (endDate) params.append('end_date', endDate); if (minHours) params.append('min_hours', minHours); if (maxHours) params.append('max_hours', maxHours); if (billable !== undefined) params.append('billable', String(billable)); if (approved !== undefined) params.append('approved', String(approved)); if (selectedTickets.length > 0) params.append('ticket_id', String(selectedTickets[0])); params.append('limit', '1000'); // Get more data for analytics // Fetch real time entries from API const response = await fetch(`/api/data/time-entries?${params}`); if (!response.ok) { throw new Error('Failed to fetch time entries'); } const data = await response.json(); const fetchedEntries: TimeEntry[] = data.timeEntries || []; // Use fetched entries instead of mock data const timeEntriesData = fetchedEntries.length > 0 ? fetchedEntries : []; // Fallback mock time entries if no data const mockTimeEntries: TimeEntry[] = timeEntriesData.length > 0 ? timeEntriesData : [ { id: 1, resource_id: 1, ticket_id: 101, task_id: 201, project_id: 301, company_id: 401, entry_date: new Date('2024-01-15T09:00:00Z'), hours_worked: 2.5, notes: 'Fixed critical bug in authentication system. Updated JWT token validation logic.', title: 'Bug Fix - Authentication', type: 1, start_date_time: new Date('2024-01-15T09:00:00Z'), end_date_time: new Date('2024-01-15T11:30:00Z'), billable: true, approved: true, created_at: new Date('2024-01-15T12:00:00Z'), updated_at: new Date('2024-01-15T12:00:00Z'), synced_at: new Date('2024-01-15T12:00:00Z'), is_deleted: false, }, { id: 2, resource_id: 2, ticket_id: 102, task_id: 202, project_id: 302, company_id: 402, entry_date: new Date('2024-01-15T14:00:00Z'), hours_worked: 4.0, notes: 'Implemented new dashboard feature with React components. Added data visualization charts.', title: 'Feature Development - Dashboard', type: 2, start_date_time: new Date('2024-01-15T14:00:00Z'), end_date_time: new Date('2024-01-15T18:00:00Z'), billable: true, approved: false, created_at: new Date('2024-01-15T18:30:00Z'), updated_at: new Date('2024-01-15T18:30:00Z'), synced_at: new Date('2024-01-15T18:30:00Z'), is_deleted: false, }, // Add more mock entries as needed ]; // Mock timeline events const mockTimelineEvents: TimelineEvent[] = mockTimeEntries.map(entry => ({ id: `te-${entry.id}`, type: 'time_entry', timestamp: new Date(entry.entry_date), title: entry.title || 'Time Entry', description: entry.notes || undefined, duration: entry.hours_worked, isHumanActivity: true, importance: entry.billable ? 'high' : 'medium', score: 0.8, // Mock score })); // Mock analysis const totalHours = mockTimeEntries.reduce((sum, entry) => { const hours = typeof entry.hours_worked === 'string' ? parseFloat(entry.hours_worked) : entry.hours_worked; return sum + hours; }, 0); const mockAnalysis: AggregateAnalysis = { totalEntries: mockTimeEntries.length, totalHours: totalHours, averageHoursPerEntry: mockTimeEntries.length > 0 ? totalHours / mockTimeEntries.length : 0, dateRange: { earliest: new Date('2024-01-15'), latest: new Date('2024-01-15'), }, scores: { activity: 0.85, content: 0.78, timeliness: 0.92, overall: 0.85, }, insights: [ { type: 'success', category: 'overall', title: 'High Quality Time Tracking', description: 'Overall time entry quality is excellent.', recommendation: 'Maintain current documentation standards.', severity: 'low', actionable: false, }, { type: 'warning', category: 'billing', title: 'Pending Approvals', description: 'Some time entries are awaiting approval.', recommendation: 'Review and approve pending time entries.', severity: 'medium', actionable: true, }, ], patterns: { dayOfWeek: [0, 5, 8, 12, 6, 3, 1], hourly: [0, 1, 2, 3, 4, 2, 8, 15, 12, 8, 6, 4, 3, 5, 7, 6, 4, 2, 1, 0, 0, 0, 0, 0], }, trends: { weekly: [ { week: new Date('2024-01-08'), hours: 25, entries: 8 }, { week: new Date('2024-01-15'), hours: 32, entries: 10 }, ], }, analyzedAt: new Date(), }; // Mock LLM analysis const mockLlmAnalysis: LLMAnalysisResponse = { insights: [ 'Team shows excellent documentation practices with detailed notes', 'Consistent time entry patterns indicate good workflow discipline', ], patterns: [ { type: 'Morning Productivity', description: 'Most productive work occurs in morning hours (9 AM - 12 PM)', frequency: 8, impact: 'medium', }, ], recommendations: [ { category: 'Process Improvement', priority: 'medium', action: 'Implement automated reminders for time entry approval', expectedImpact: 'Reduce approval delays by 50%', }, ], summary: { overallQuality: 0.85, productivityLevel: 0.78, keyFindings: [ 'Strong documentation quality', 'Consistent time tracking patterns', 'Need for faster approval process', ], }, processingTime: 1250, tokensUsed: 245, }; setTimeEntries(mockTimeEntries); setTimelineEvents(mockTimelineEvents); setAnalysis(mockAnalysis); setInsights(mockAnalysis.insights); setLlmAnalysis(mockLlmAnalysis); setLoading(false); } catch (error) { console.error('Error loading time entries:', error); setTimeEntries([]); setLoading(false); } }; const handleRefresh = () => { loadMockData(); }; const handleExport = () => { // Implement export functionality console.log('Exporting analytics data...'); }; const applyFilters = () => { // Implement filter application console.log('Applying filters...'); loadMockData(); }; const clearFilters = () => { setSelectedResources([]); setSelectedProjects([]); setSelectedTickets([]); setStartDate(''); setEndDate(''); setMinHours(''); setMaxHours(''); setBillable(undefined); setApproved(undefined); loadMockData(); }; return (
{/* Header */}

Time Entries Analytics

Advanced analytics and insights for time tracking data

{/* Summary Cards */} {analysis && (
} /> } /> } /> } />
)}
{/* Filters Panel */} Filters {/* Date Range */}
setStartDate(e.target.value)} />
setEndDate(e.target.value)} />
{/* Ticket ID Filter */}
{ const ticketId = e.target.value ? parseInt(e.target.value) : null; setSelectedTickets(ticketId ? [ticketId] : []); }} />

Filter timeline to show only entries for this ticket

{/* Hours Range */}
setMinHours(e.target.value)} />
setMaxHours(e.target.value)} />
{/* Checkboxes */}
setBillable(checked === true)} />
setApproved(checked === true)} />
{/* Action Buttons */}
{/* Main Content */}
{/* Tabs */} Overview Timeline Scores AI Analysis {analysis && } {/* Additional overview content */} Recent Activity

Detailed activity overview and trends will be displayed here.

); } function cn(...classes: string[]) { return classes.filter(Boolean).join(' '); }