feat: add WeeklyCalendar, QuickActions, OpenSessions, RecentActivity dashboard components
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
91
frontend/src/components/dashboard/WeeklyCalendar.tsx
Normal file
91
frontend/src/components/dashboard/WeeklyCalendar.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import { useMemo } from 'react'
|
||||
import { Calendar } from 'lucide-react'
|
||||
|
||||
interface CalendarEvent {
|
||||
id: string
|
||||
title: string
|
||||
time: string
|
||||
type: 'default' | 'maintenance'
|
||||
}
|
||||
|
||||
interface WeeklyCalendarProps {
|
||||
events?: Record<string, CalendarEvent[]>
|
||||
}
|
||||
|
||||
const DAY_NAMES = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
|
||||
|
||||
function getWeekDays(): { label: string; date: Date; dateStr: string; isToday: boolean }[] {
|
||||
const now = new Date()
|
||||
const day = now.getDay()
|
||||
const mondayOffset = day === 0 ? 6 : day - 1
|
||||
const monday = new Date(now)
|
||||
monday.setDate(now.getDate() - mondayOffset)
|
||||
|
||||
return DAY_NAMES.map((label, i) => {
|
||||
const d = new Date(monday)
|
||||
d.setDate(monday.getDate() + i)
|
||||
const dateStr = d.toISOString().split('T')[0]
|
||||
const isToday = d.toDateString() === now.toDateString()
|
||||
return { label, date: d, dateStr, isToday }
|
||||
})
|
||||
}
|
||||
|
||||
export function WeeklyCalendar({ events = {} }: WeeklyCalendarProps) {
|
||||
const days = useMemo(() => getWeekDays(), [])
|
||||
|
||||
return (
|
||||
<div className="glass-card-static flex flex-col h-full">
|
||||
<div className="flex items-center gap-2 px-5 py-3" style={{ borderBottom: '1px solid var(--glass-border)' }}>
|
||||
<Calendar size={16} className="text-muted-foreground" />
|
||||
<h3 className="font-heading text-sm font-bold text-foreground">This Week</h3>
|
||||
</div>
|
||||
<div className="flex flex-1 min-h-0">
|
||||
{days.map((day, i) => {
|
||||
const dayEvents = events[day.dateStr] || []
|
||||
return (
|
||||
<div
|
||||
key={day.dateStr}
|
||||
className="flex-1 flex flex-col min-h-0"
|
||||
style={{
|
||||
borderRight: i < 4 ? '1px solid var(--glass-border)' : undefined,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="px-2 py-2 text-center"
|
||||
style={{
|
||||
borderBottom: day.isToday ? '2px solid #06b6d4' : '1px solid var(--glass-border)',
|
||||
}}
|
||||
>
|
||||
<span className={`font-label text-[0.625rem] uppercase tracking-[0.1em] ${day.isToday ? 'text-cyan-400' : 'text-muted-foreground'}`}>
|
||||
{day.label}
|
||||
</span>
|
||||
<div className={`text-sm font-heading font-bold ${day.isToday ? 'text-foreground' : 'text-muted-foreground'}`}>
|
||||
{day.date.getDate()}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto p-1.5 space-y-1">
|
||||
{dayEvents.length === 0 ? (
|
||||
<p className="text-[0.625rem] text-[hsl(var(--text-dimmed))] text-center py-2">No events</p>
|
||||
) : (
|
||||
dayEvents.map(event => (
|
||||
<div
|
||||
key={event.id}
|
||||
className="rounded-md px-2 py-1.5 text-[0.6875rem] cursor-pointer hover:bg-accent/30 transition-colors"
|
||||
style={{
|
||||
borderLeft: `3px solid ${event.type === 'maintenance' ? '#fbbf24' : '#06b6d4'}`,
|
||||
background: 'rgba(255, 255, 255, 0.02)',
|
||||
}}
|
||||
>
|
||||
<div className="font-medium text-foreground truncate">{event.title}</div>
|
||||
<div className="font-label text-[0.5625rem] text-muted-foreground">{event.time}</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user