Files
WrestleDesk/docs/superpowers/plans/2026-03-23-calendar-improvements.md
T
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

311 lines
9.3 KiB
Markdown

# 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
```css
.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
```css
.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
```typescript
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
```typescript
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
```css
.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
```typescript
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
```typescript
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
```typescript
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet"
```
- [ ] **Step 2: Replace popover state with sheet state**
Change state from popoverOpen to sheetOpen
```typescript
const [selectedDaySheetOpen, setSelectedDaySheetOpen] = useState(false)
```
- [ ] **Step 3: Replace popover JSX with Sheet component**
Replace lines 180-230 with:
```typescript
<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