'use client' import { useState, useEffect } from 'react' import { useTranslations } from 'next-intl' import { Button } from '@/components/ui/button' import { createClientLogger } from '@/lib/client-logger' const log = createClientLogger('StandupPanel') interface StandupReport { date: string generatedAt: string summary: { totalAgents: number totalCompleted: number totalInProgress: number totalAssigned: number totalReview: number totalBlocked: number totalActivity: number overdue: number } agentReports: Array<{ agent: { name: string role: string status: string last_seen?: number last_activity?: string } completedToday: Array<{ id: number title: string status: string updated_at: number }> inProgress: Array<{ id: number title: string status: string created_at: number due_date?: number }> assigned: Array<{ id: number title: string status: string created_at: number due_date?: number priority: string }> review: Array<{ id: number title: string status: string updated_at: number }> blocked: Array<{ id: number title: string status: string priority: string created_at: number metadata?: any }> activity: { actionCount: number commentsCount: number } }> teamAccomplishments: Array<{ id: number title: string agent: string updated_at: number }> teamBlockers: Array<{ id: number title: string priority: string agent: string created_at: number }> overdueTasks: Array<{ id: number title: string due_date: number status: string agent_name?: string }> } interface StandupHistory { id: number date: string generatedAt: string summary: any agentCount: number } export function StandupPanel() { const t = useTranslations('standup') const [standupReport, setStandupReport] = useState(null) const [standupHistory, setStandupHistory] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]) const [view, setView] = useState<'current' | 'history'>('current') // Generate standup report const generateStandup = async (date?: string) => { try { setLoading(true) setError(null) const response = await fetch('/api/standup', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ date: date || selectedDate }) }) if (!response.ok) throw new Error('Failed to generate standup') const data = await response.json() setStandupReport(data.standup) } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred') } finally { setLoading(false) } } // Fetch standup history const fetchHistory = async () => { try { const response = await fetch('/api/standup/history') if (!response.ok) throw new Error('Failed to fetch history') const data = await response.json() setStandupHistory(data.history || []) } catch (err) { log.error('Failed to fetch standup history:', err) } } useEffect(() => { if (view === 'history') { fetchHistory() } }, [view]) // Format date for display const formatDate = (dateStr: string) => { return new Date(dateStr).toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }) } // Format time for display const formatTime = (timestamp: number) => { return new Date(timestamp * 1000).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }) } // Get priority color const getPriorityColor = (priority: string) => { const colors: Record = { low: 'text-green-400', medium: 'text-yellow-400', high: 'text-orange-400', urgent: 'text-red-400' } return colors[priority] || 'text-muted-foreground' } // Export standup as text const exportStandup = () => { if (!standupReport) return const lines = [ `# Daily Standup - ${formatDate(standupReport.date)}`, `Generated: ${new Date(standupReport.generatedAt).toLocaleString()}`, '', '## Summary', `- **Agents Active:** ${standupReport.summary.totalAgents}`, `- **Completed Today:** ${standupReport.summary.totalCompleted}`, `- **In Progress:** ${standupReport.summary.totalInProgress}`, `- **Assigned:** ${standupReport.summary.totalAssigned}`, `- **In Review:** ${standupReport.summary.totalReview}`, `- **Blocked:** ${standupReport.summary.totalBlocked}`, `- **Overdue:** ${standupReport.summary.overdue}`, '', ] // Add team accomplishments if (standupReport.teamAccomplishments.length > 0) { lines.push('## Team Accomplishments') standupReport.teamAccomplishments.forEach(task => { lines.push(`- **${task.agent}**: ${task.title}`) }) lines.push('') } // Add team blockers if (standupReport.teamBlockers.length > 0) { lines.push('## Team Blockers') standupReport.teamBlockers.forEach(task => { lines.push(`- **${task.agent}** [${task.priority.toUpperCase()}]: ${task.title}`) }) lines.push('') } // Add individual agent reports lines.push('## Individual Reports') standupReport.agentReports.forEach(report => { lines.push(`### ${report.agent.name} (${report.agent.role})`) if (report.completedToday.length > 0) { lines.push('**Completed Today:**') report.completedToday.forEach(task => { lines.push(`- ${task.title}`) }) } if (report.inProgress.length > 0) { lines.push('**In Progress:**') report.inProgress.forEach(task => { lines.push(`- ${task.title}`) }) } if (report.blocked.length > 0) { lines.push('**Blocked:**') report.blocked.forEach(task => { lines.push(`- [${task.priority.toUpperCase()}] ${task.title}`) }) } lines.push('') }) const text = lines.join('\n') const blob = new Blob([text], { type: 'text/markdown' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `standup-${standupReport.date}.md` document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) } return (
{/* Header */}

{t('title')}

{/* View Toggle */}
{view === 'current' && ( <> setSelectedDate(e.target.value)} className="bg-surface-1 text-foreground rounded-md px-3 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-primary/50 border border-border" /> {standupReport && ( )} )}
{/* Error Display */} {error && (
{error}
)} {/* Content */}
{view === 'current' ? ( // Current Standup View standupReport ? (
{/* Report Header */}

{t('standupFor', { date: formatDate(standupReport.date) })}

{t('generatedOn', { date: new Date(standupReport.generatedAt).toLocaleString() })}

{/* Summary Stats */}
{standupReport.summary.totalCompleted}
{t('statCompleted')}
{standupReport.summary.totalInProgress}
{t('statInProgress')}
{standupReport.summary.totalBlocked}
{t('statBlocked')}
{standupReport.summary.overdue}
{t('statOverdue')}
{/* Team Accomplishments */} {standupReport.teamAccomplishments.length > 0 && (

🎉 {t('teamAccomplishments')}

{standupReport.teamAccomplishments.map(task => (
{task.title} {task.agent}
))}
)} {/* Team Blockers */} {standupReport.teamBlockers.length > 0 && (

🚫 {t('teamBlockers')}

{standupReport.teamBlockers.map(task => (
{task.title} [{task.priority.toUpperCase()}]
{task.agent}
))}
)} {/* Overdue Tasks */} {standupReport.overdueTasks.length > 0 && (

⏰ {t('overdueTasks')}

{standupReport.overdueTasks.map(task => (
{task.title} (Due: {new Date(task.due_date * 1000).toLocaleDateString()})
{task.agent_name || t('unassigned')}
))}
)} {/* Individual Agent Reports */}

👥 {t('individualReports')}

{standupReport.agentReports.map(report => (
{report.agent.name}

{report.agent.role}

{t('activitySummary', { actions: report.activity.actionCount, comments: report.activity.commentsCount })}
{report.agent.last_activity && (
{report.agent.last_activity}
)}
{/* Completed Today */}
✅ {t('sectionCompleted', { count: report.completedToday.length })}
{report.completedToday.map(task => (
{task.title}
))} {report.completedToday.length === 0 && (
{t('none')}
)}
{/* In Progress */}
🔄 {t('sectionInProgress', { count: report.inProgress.length })}
{report.inProgress.map(task => (
{task.title}
))} {report.inProgress.length === 0 && (
{t('none')}
)}
{/* Assigned */}
📋 {t('sectionAssigned', { count: report.assigned.length })}
{report.assigned.map(task => (
{task.title}
[{task.priority}]
))} {report.assigned.length === 0 && (
{t('none')}
)}
{/* Blocked */}
🚫 {t('sectionBlocked', { count: report.blocked.length })}
{report.blocked.map(task => (
{task.title}
[{task.priority}]
))} {report.blocked.length === 0 && (
{t('none')}
)}
))}
) : ( // Empty state for current view

{t('noStandupGenerated')}

{t('selectDatePrompt')}

) ) : ( // History View
{standupHistory.length === 0 ? (

{t('noHistory')}

) : (
{standupHistory.map(history => (

{formatDate(history.date)}

{t('historyGenerated', { date: new Date(history.generatedAt).toLocaleString() })}

{t('historyAgentsParticipated', { count: history.agentCount })}

{history.summary && (
{t('historyCompleted', { count: history.summary.completed || 0 })}
{t('historyInProgress', { count: history.summary.inProgress || 0 })}
{t('historyBlocked', { count: history.summary.blocked || 0 })}
)}
))}
)}
)}
) }