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
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user