Files
WrestleDesk/docs/superpowers/plans/2026-03-23-calendar-improvements.md
Andrej Spielmann 3fefc550fe Initial commit: WrestleDesk full project
- 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
2026-03-26 13:24:57 +01:00

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 of setPopoverOpen(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