- Django backend with DRF (clubs, wrestlers, trainers, exercises, templates, trainings, homework, locations, leistungstest) - Next.js 16 frontend with React, Shadcn UI, Tailwind - JWT authentication - Full CRUD for all entities - Calendar view for trainings - Homework management system - Leistungstest tracking
9.3 KiB
Calendar UI Improvements - Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Enhance calendar with better colors, today highlighting, group-based event colors, hover tooltips, and click-to-day-panel
Architecture: Frontend-only CSS and component changes to existing calendar-view.tsx and calendar.css
Tech Stack: React, TypeScript, Tailwind CSS, react-big-calendar, date-fns
Files Overview
| File | Purpose |
|---|---|
frontend/src/components/ui/calendar-view.tsx |
Main calendar component |
frontend/src/components/ui/calendar.css |
Calendar styling overrides |
Task 1: Enhance "Today" Highlighting
Files:
- Modify:
frontend/src/components/ui/calendar.css:68-70
Changes:
- Replace today's subtle background with light green accent
- Add subtle animation pulse effect
- Make date number bold
Steps:
- Step 1: Update .rbc-today styling Replace current implementation with light green accent
.rbc-today {
background: hsl(142, 76%, 96%);
border: 2px solid hsl(142, 76%, 45%);
border-radius: 0.5rem;
}
- Step 2: Add today date cell bold styling Make today's date number more prominent
.rbc-today .rbc-date-cell {
font-weight: 700;
color: hsl(142, 76%, 30%);
}
- Step 3: Test in browser Navigate to /trainings and verify today is highlighted
Task 2: Group-Based Event Colors
Files:
- Modify:
frontend/src/components/ui/calendar.css:87-94 - Modify:
frontend/src/components/ui/calendar-view.tsx:114-123
Changes:
- Kids: Blue (#3B82F6) with 15% opacity background
- Youth: Purple (#8B5CF6) with 15% opacity background
- Adults: Orange (#F97316) with 15% opacity background
- Text in dark version of each color for readability
Steps:
- Step 1: Define group color constants in calendar-view.tsx Add color configuration
const groupColors = {
kids: { bg: "rgba(59, 130, 246, 0.15)", text: "#1E40AF", border: "#3B82F6" },
youth: { bg: "rgba(139, 92, 246, 0.15)", text: "#5B21B6", border: "#8B5CF6" },
adults: { bg: "rgba(249, 115, 22, 0.15)", text: "#9A3412", border: "#F97316" },
all: { bg: "rgba(100, 100, 100, 0.15)", text: "#404040", border: "#666666" },
}
- Step 2: Update eventStyleGetter to use group colors Modify function at line 114
const eventStyleGetter = useCallback((event: CalendarEvent) => {
const group = event.resource.group || "all"
const colors = groupColors[group as keyof typeof groupColors]
return {
style: {
backgroundColor: colors.bg,
borderLeft: `3px solid ${colors.border}`,
borderRadius: "0.25rem",
border: "none",
color: colors.text,
}
}
}, [])
- Step 3: Update CSS for events Replace existing .rbc-event styles
.rbc-event {
background: transparent;
border: none;
border-left: 3px solid;
border-radius: 0.25rem;
padding: 0.125rem 0.375rem;
font-size: 0.75rem;
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
}
- Step 4: Test in browser Verify each group shows correct color
Task 3: Hover Tooltip
Files:
- Modify:
frontend/src/components/ui/calendar-view.tsx - Create:
frontend/src/components/ui/calendar-tooltip.tsx(optional, can be inline)
Changes:
- Show tooltip on event hover with training details
- Tooltip shows: Time, Group badge, Location, Attendee count
- Position tooltip above event
Steps:
- Step 1: Add tooltip state and handlers to calendar-view.tsx Add state for tooltip visibility, position, and content
const [tooltipInfo, setTooltipInfo] = useState<{
visible: boolean
x: number
y: number
content: { time: string, group: string, location: string, attendees: number }
} | null>(null)
- Step 2: Create custom Event component with hover Replace CustomEvent at line 125
const CustomEvent = ({ event }: { event: CalendarEvent }) => {
const [showTooltip, setShowTooltip] = useState(false)
return (
<div
className="relative"
onMouseEnter={(e) => {
setShowTooltip(true)
setTooltipInfo({
visible: true,
x: e.clientX,
y: e.clientY - 10,
content: {
time: `${event.resource.start_time} - ${event.resource.end_time}`,
group: groupConfig[event.resource.group as keyof typeof groupConfig]?.label || "",
location: event.resource.location_name || "Kein Ort",
attendees: event.resource.attendance_count || 0,
}
})
}}
onMouseLeave={() => setShowTooltip(false)}
>
<div className="flex items-center gap-1 text-xs">
<span className="font-medium">{event.resource.start_time}</span>
</div>
{showTooltip && tooltipInfo?.visible && (
<div
className="fixed z-50 bg-background border rounded-lg shadow-lg p-3 text-sm"
style={{ left: tooltipInfo.x, top: tooltipInfo.y, transform: 'translateY(-100%)' }}
>
<div className="font-medium mb-1">{tooltipInfo.content.time}</div>
<div className="flex items-center gap-2 text-muted-foreground">
<Badge className={groupConfig[event.resource.group as keyof typeof groupConfig]?.class}>
{tooltipInfo.content.group}
</Badge>
</div>
<div className="text-xs text-muted-foreground mt-1">
📍 {tooltipInfo.content.location}
</div>
<div className="text-xs text-muted-foreground">
👥 {tooltipInfo.content.attendees} Teilnehmer
</div>
</div>
)}
</div>
)
}
- Step 3: Test hover in browser Hover over training events and verify tooltip appears
Task 4: Click on Day → Trainings Panel
Files:
- Modify:
frontend/src/components/ui/calendar-view.tsx:180-230
Changes:
- Replace bottom popover with improved side panel or modal
- Show all trainings for selected day
- Better styling with group colors
- Click on training opens detail view
Steps:
- Step 1: Import Sheet component Add Sheet import at top of file
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet"
- Step 2: Replace popover state with sheet state Change state from popoverOpen to sheetOpen
const [selectedDaySheetOpen, setSelectedDaySheetOpen] = useState(false)
- Step 3: Replace popover JSX with Sheet component Replace lines 180-230 with:
<Sheet open={selectedDaySheetOpen} onOpenChange={setSelectedDaySheetOpen}>
<SheetContent side="bottom" className="h-[50vh] sm:max-w-[600px]">
<SheetHeader>
<SheetTitle>
{format(selectedDay!, "EEEE, d. MMMM yyyy", { locale: de })}
</SheetTitle>
</SheetHeader>
<div className="mt-4 space-y-3 max-h-[calc(50vh-100px)] overflow-y-auto">
{selectedDayTrainings.length === 0 ? (
<p className="text-muted-foreground text-center py-8">
Keine Trainings an diesem Tag
</p>
) : (
selectedDayTrainings.map(training => (
<div
key={training.id}
className="flex items-center justify-between p-4 rounded-xl border hover:bg-muted/50 transition-all cursor-pointer"
style={{
borderLeftWidth: 4,
borderLeftColor: groupColors[training.group as keyof typeof groupColors]?.border || "#666"
}}
onClick={() => {
onView(training)
setSelectedDaySheetOpen(false)
}}
>
<div className="flex items-center gap-4">
<div className="text-lg font-semibold">
{training.start_time} - {training.end_time}
</div>
<Badge
className={groupConfig[training.group as keyof typeof groupConfig]?.class}
>
{groupConfig[training.group as keyof typeof groupConfig]?.label}
</Badge>
{training.location_name && (
<div className="flex items-center gap-1 text-sm text-muted-foreground">
<MapPin className="h-4 w-4" />
{training.location_name}
</div>
)}
</div>
<div className="flex items-center gap-1 text-sm">
<Users className="h-4 w-4 text-muted-foreground" />
<span className="font-medium">{training.attendance_count || 0}</span>
</div>
</div>
))
)}
</div>
</SheetContent>
</Sheet>
-
Step 4: Update handleSelectSlot to use new state Change line 107-111 to set
setSelectedDaySheetOpen(true)instead ofsetPopoverOpen(true) -
Step 5: Test in browser Click on a day with trainings and verify Sheet appears
Verification Checklist
After all tasks:
- Today is highlighted with light green accent and bold date
- Training events show group colors (blue/purple/orange)
- Hover on event shows tooltip with details
- Click on day opens bottom Sheet with training list
- Click on training in Sheet opens detail view
- No console errors
- Responsive on mobile