# Kurs-Verwaltung: Neue Felder (Zielgruppe & Termin-Ausschluss)

## 📋 Übersicht

Es wurden zwei neue Features für die Kurs-Verwaltung implementiert:

1. **Zielgruppe-Auswahl** - Ermöglicht die Zuordnung einer Zielgruppe zu einem Kurs
2. **Termin-Ausschluss** - Ermöglicht das Ausschließen einzelner Termine (z.B. Feiertage) bei Zeitraum-Kursen

---

## 🎯 Feature 1: Zielgruppe

### Backend

#### Datenbank-Migration
**Datei:** `migrations/Version20251022093909.php`
```sql
ALTER TABLE availability 
ADD target_group VARCHAR(100) DEFAULT NULL
```

#### Entity-Erweiterung
**Datei:** `src/Entity/Availability.php`

**Neue Felder:**
```php
#[ORM\Column(type: Types::STRING, length: 100, nullable: true)]
private ?string $targetGroup = null;
```

**Neue Methoden:**
- `getTargetGroup(): ?string`
- `setTargetGroup(?string $targetGroup): static`

#### Controller-Integration
**Datei:** `src/Controller/AppointmentController.php`

**In `adminAvailability()` (Zeile 892-895):**
```php
// Set targetGroup (Zielgruppe)
if (isset($data['targetGroup'])) {
    $availability->setTargetGroup($data['targetGroup']);
}
```

**In `adminAvailabilityList()` (Zeile 1080):**
```php
'targetGroup' => $availability->getTargetGroup(),
```

**In `adminAvailabilityGet()` (Zeile 1134):**
```php
'targetGroup' => $availability->getTargetGroup(),
```

### Frontend

#### HTML-Formular
**Datei:** `templates/admin/courses/index.html.twig` (Zeile 429-485)

**Vordefinierte Optionen:**
- Kinder 4-5 Jahre
- Kinder 6-8 Jahre
- Erwachsene
- Eltern-Kind-Kurs
- Eigene (mit Custom-Eingabefeld)

```html
<div class="row g-4 mb-4">
    <div class="col-md-12">
        <label class="form-label fw-semibold text-muted mb-3">
            <i class="fas fa-user-friends me-2 text-warning"></i>Zielgruppe
        </label>
        <div class="row g-3">
            <div class="col-md-3">
                <div class="form-check form-check-inline w-100">
                    <input class="form-check-input" type="radio" name="targetGroup" 
                           id="targetGroup1" value="Kinder 4-5 Jahre">
                    <label class="form-check-label" for="targetGroup1">
                        <i class="fas fa-child me-1"></i>Kinder 4-5 Jahre
                    </label>
                </div>
            </div>
            <!-- weitere Optionen... -->
            <div class="col-md-2">
                <div class="form-check form-check-inline w-100">
                    <input class="form-check-input" type="radio" name="targetGroup" 
                           id="targetGroupCustom" value="custom">
                    <label class="form-check-label" for="targetGroupCustom">
                        <i class="fas fa-edit me-1"></i>Eigene
                    </label>
                </div>
            </div>
        </div>
        <div id="customTargetGroupField" class="mt-3" style="display: none;">
            <div class="form-floating">
                <input type="text" class="form-control modern-input" 
                       id="customTargetGroup" placeholder="z.B. Senioren 65+">
                <label for="customTargetGroup" class="fw-semibold text-muted">
                    <i class="fas fa-pencil-alt me-2 text-secondary"></i>Eigene Zielgruppe
                </label>
            </div>
        </div>
    </div>
</div>
```

#### JavaScript-Logik
**Datei:** `templates/admin/courses/index.html.twig` (Zeile 3181-3210)

**Event Listener:**
```javascript
document.addEventListener('DOMContentLoaded', function() {
    const targetGroupRadios = document.getElementsByName('targetGroup');
    const customTargetGroupField = document.getElementById('customTargetGroupField');
    const customTargetGroupInput = document.getElementById('customTargetGroup');

    targetGroupRadios.forEach(radio => {
        radio.addEventListener('change', function() {
            if (this.value === 'custom') {
                customTargetGroupField.style.display = 'block';
                customTargetGroupInput.focus();
            } else {
                customTargetGroupField.style.display = 'none';
                customTargetGroupInput.value = '';
            }
        });
    });
});
```

**In `createCourse()` (Zeile 1451-1475):**
```javascript
// Zielgruppe ermitteln
let targetGroup = null;
const selectedTargetGroup = document.querySelector('input[name="targetGroup"]:checked');
if (selectedTargetGroup) {
    if (selectedTargetGroup.value === 'custom') {
        targetGroup = document.getElementById('customTargetGroup').value;
    } else {
        targetGroup = selectedTargetGroup.value;
    }
}

const courseData = {
    // ...
    targetGroup: targetGroup,
    // ...
};
```

---

## 📅 Feature 2: Termin-Ausschluss

### Backend

#### Datenbank-Migration
**Datei:** `migrations/Version20251022093909.php`
```sql
ALTER TABLE availability 
ADD excluded_dates JSON DEFAULT NULL COMMENT '(DC2Type:json)'
```

#### Entity-Erweiterung
**Datei:** `src/Entity/Availability.php`

**Neue Felder:**
```php
#[ORM\Column(type: Types::JSON, nullable: true)]
private ?array $excludedDates = null;
```

**Neue Methoden:**
- `getExcludedDates(): ?array`
- `setExcludedDates(?array $excludedDates): static`
- `isDateExcluded(\DateTimeInterface $date): bool`
- `addExcludedDate(\DateTimeInterface $date): static`
- `removeExcludedDate(\DateTimeInterface $date): static`

#### Feiertags-Service
**Datei:** `src/Service/HolidayService.php`

**Hauptfunktionen:**
- `isHoliday(\DateTimeInterface $date): bool` - Prüft ob ein Datum ein Feiertag ist
- `getHolidayName(\DateTimeInterface $date): ?string` - Gibt den Namen des Feiertags zurück
- `getAllHolidays(int $year): array` - Gibt alle Feiertage für ein Jahr zurück
- `generateCourseDates($startDate, $endDate, array $daysOfWeek): array` - Generiert alle Termine mit Feiertagsmarkierung

**Unterstützte Feiertage (NRW):**
- Neujahr (1.1.)
- Karfreitag (beweglich)
- Ostermontag (beweglich)
- Tag der Arbeit (1.5.)
- Christi Himmelfahrt (beweglich)
- Pfingstmontag (beweglich)
- Fronleichnam (beweglich)
- Tag der Deutschen Einheit (3.10.)
- Allerheiligen (1.11.)
- 1. Weihnachtstag (25.12.)
- 2. Weihnachtstag (26.12.)

#### Controller-Integration
**Datei:** `src/Controller/AppointmentController.php`

**In `adminAvailability()` (Zeile 897-900):**
```php
// Set excludedDates (Ausgeschlossene Termine)
if (isset($data['excludedDates']) && is_array($data['excludedDates'])) {
    $availability->setExcludedDates($data['excludedDates']);
}
```

**Neue Route:** `admin_courses_generate_dates`
**Datei:** `src/Controller/Admin/CoursesController.php` (Zeile 62-97)
```php
#[Route('/generate-dates', name: 'admin_courses_generate_dates', methods: ['POST'])]
public function generateDates(Request $request): JsonResponse
{
    $data = json_decode($request->getContent(), true);
    
    $startDate = new \DateTime($data['startDate']);
    $endDate = new \DateTime($data['endDate']);
    $daysOfWeek = array_map('intval', $data['daysOfWeek']);

    $dates = $this->holidayService->generateCourseDates($startDate, $endDate, $daysOfWeek);

    // Formatiere die Daten für das Frontend
    $formattedDates = array_map(function($date) {
        return [
            'dateString' => $date['dateString'],
            'dateFormatted' => $date['date']->format('d.m.Y'),
            'dayName' => $date['dayName'],
            'isHoliday' => $date['isHoliday'],
            'holidayName' => $date['holidayName'],
            'excluded' => false
        ];
    }, $dates);

    return new JsonResponse(['success' => true, 'dates' => $formattedDates]);
}
```

### Frontend

#### HTML-Container
**Datei:** `templates/admin/courses/index.html.twig` (Zeile 567-603)

**Termin-Vorschau-Card:**
```html
<div id="courseDatesPreview" style="display: none;" class="mb-4">
    <div class="card border-0 bg-light">
        <div class="card-header bg-info text-white d-flex justify-content-between align-items-center">
            <h6 class="mb-0">
                <i class="fas fa-calendar-check me-2"></i>Termin-Übersicht
                <span id="datesCount" class="badge bg-white text-info ms-2">0 Termine</span>
            </h6>
            <div>
                <button type="button" class="btn btn-sm btn-light" onclick="selectAllDates()">
                    <i class="fas fa-check-double me-1"></i>Alle
                </button>
                <button type="button" class="btn btn-sm btn-light" onclick="deselectAllDates()">
                    <i class="fas fa-times me-1"></i>Keine
                </button>
                <button type="button" class="btn btn-sm btn-light" onclick="deselectHolidays()">
                    <i class="fas fa-umbrella-beach me-1"></i>Feiertage ausschließen
                </button>
            </div>
        </div>
        <div class="card-body p-3" style="max-height: 400px; overflow-y: auto;">
            <div id="courseDatesList" class="row g-2">
                <!-- Wird dynamisch befüllt -->
            </div>
            <!-- Loading/Empty States -->
        </div>
    </div>
</div>
```

**Feiertagsmarkierung:**
- Feiertage werden mit gelbem Hintergrund (`border-warning bg-warning bg-opacity-10`) hervorgehoben
- Feiertagsname wird als Badge angezeigt
- "Feiertage ausschließen"-Button deselektiert alle Feiertage auf einmal

#### JavaScript-Logik
**Datei:** `templates/admin/courses/index.html.twig` (Zeile 3200-3352)

**Event Listener für automatische Aktualisierung:**
```javascript
document.addEventListener('DOMContentLoaded', function() {
    const startDateField = document.getElementById('startDate');
    const endDateField = document.getElementById('endDate');
    const daysOfWeek = document.getElementById('daysOfWeek');

    if (startDateField && endDateField && daysOfWeek) {
        startDateField.addEventListener('change', generateCourseDatesPreview);
        endDateField.addEventListener('change', generateCourseDatesPreview);
        daysOfWeek.addEventListener('change', generateCourseDatesPreview);
    }
});
```

**Haupt-Funktionen:**

1. `generateCourseDatesPreview()` - Generiert die Vorschau
```javascript
async function generateCourseDatesPreview() {
    const courseType = document.getElementById('courseType').value;
    const startDate = document.getElementById('startDate').value;
    const endDate = document.getElementById('endDate').value;
    const daysOfWeek = Array.from(document.getElementById('daysOfWeek').selectedOptions).map(opt => opt.value);

    // Nur anzeigen bei zeitlich begrenzten Kursen und Ferienkursen
    if (!['limited_course', 'holiday_course'].includes(courseType) || !startDate || !endDate || daysOfWeek.length === 0) {
        return;
    }

    const response = await fetch('{{ path('admin_courses_generate_dates') }}', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ startDate, endDate, daysOfWeek })
    });

    const data = await response.json();
    courseDates = data.dates;
    renderCourseDates();
}
```

2. `renderCourseDates()` - Rendert die Termin-Liste
```javascript
function renderCourseDates() {
    const container = document.getElementById('courseDatesList');
    const countBadge = document.getElementById('datesCount');
    
    const selectedCount = courseDates.filter(d => !d.excluded).length;
    countBadge.textContent = `${selectedCount} / ${courseDates.length} Termine`;

    courseDates.forEach((date, index) => {
        const isHoliday = date.isHoliday;
        const cardClass = isHoliday ? 'border-warning bg-warning bg-opacity-10' : 'border-0';
        
        // Erstelle Card mit Checkbox für jeden Termin
        // Feiertage werden hervorgehoben
    });
}
```

3. `toggleCourseDate(index, isChecked)` - Toggle einzelner Termin
4. `selectAllDates()` - Wählt alle Termine aus
5. `deselectAllDates()` - Deselektiert alle Termine
6. `deselectHolidays()` - Deselektiert nur Feiertage
7. `getExcludedDates()` - Gibt Array der ausgeschlossenen Termine zurück

**In `createCourse()` (Zeile 1474):**
```javascript
const courseData = {
    // ...
    excludedDates: getExcludedDates()
};
```

---

## 🔄 Verwendung im Frontend

### Kurs erstellen/bearbeiten

1. **Zielgruppe auswählen:**
   - Radio-Button auswählen (vordefiniert oder "Eigene")
   - Bei "Eigene": Textfeld erscheint für Custom-Eingabe

2. **Termin-Ausschluss (nur bei Zeitraum-Kursen):**
   - Start-/Enddatum und Wochentage auswählen
   - Termine-Vorschau wird automatisch generiert
   - Feiertage werden automatisch erkannt und gelb markiert
   - Einzelne Termine per Checkbox auswählen/abwählen
   - Bulk-Aktionen: "Alle", "Keine", "Feiertage ausschließen"
   - Ausgeschlossene Termine werden in DB als JSON-Array gespeichert

3. **Formular absenden:**
   - Zielgruppe und ausgeschlossene Termine werden automatisch mitgesendet

### Kurs anzeigen

- **Zielgruppe:** Wird in Kurs-Details angezeigt
- **Ausgeschlossene Termine:** Werden in Übersicht aufgelistet
- Beim Bearbeiten werden beide Felder korrekt vorausgefüllt

---

## 📊 Datenstruktur

### Zielgruppe
```json
{
  "targetGroup": "Kinder 4-5 Jahre"
}
```
Oder Custom:
```json
{
  "targetGroup": "Senioren 65+"
}
```

### Ausgeschlossene Termine
```json
{
  "excludedDates": [
    "2025-12-25",
    "2025-12-26",
    "2026-01-01"
  ]
}
```

---

## ✅ Test-Checkliste

### Zielgruppe
- [ ] Kurs mit vordefinierter Zielgruppe erstellen
- [ ] Kurs mit custom Zielgruppe erstellen
- [ ] Zielgruppe beim Bearbeiten ändern
- [ ] Kurs ohne Zielgruppe erstellen (optional)
- [ ] Zielgruppe in Kurs-Liste anzeigen

### Termin-Ausschluss
- [ ] Zeitraum-Kurs erstellen → Termine-Vorschau erscheint
- [ ] Feiertage werden automatisch erkannt und markiert
- [ ] Einzelne Termine manuell ausschließen
- [ ] "Feiertage ausschließen" Button funktioniert
- [ ] Ausgeschlossene Termine werden gespeichert
- [ ] Beim Bearbeiten werden ausgeschlossene Termine korrekt angezeigt
- [ ] Normaler/Dauerhafter Kurs → keine Termine-Vorschau

### Integration
- [ ] Beide Felder zusammen in einem Kurs nutzen
- [ ] Kurs kopieren → Felder werden mitkopiert
- [ ] API-Response enthält beide Felder
- [ ] Migration erfolgreich ausgeführt

---

## 🐛 Bekannte Einschränkungen

1. **Feiertage:** Nur deutsche Feiertage (NRW) werden erkannt
2. **Termin-Vorschau:** Funktioniert nur bei `limited_course` und `holiday_course`
3. **Ausschluss:** Ausgeschlossene Termine verhindern KEINE Buchungen (noch zu implementieren)

---

## 🔧 Zukünftige Erweiterungen

1. ✅ **TODO:** Ausgeschlossene Termine bei Buchung berücksichtigen
2. ✅ **TODO:** Feier tags-Konfiguration pro Bundesland
3. ✅ **TODO:** Import von Ferien-Kalendern
4. ✅ **TODO:** Termin-Vorschau für bestehende Kurse anzeigen

---

## 📝 Zusammenfassung

**Neue Tabellenspalten:**
- `availability.target_group` (VARCHAR(100), nullable)
- `availability.excluded_dates` (JSON, nullable)

**Neue Dateien:**
- `src/Service/HolidayService.php`
- `migrations/Version20251022093909.php`
- `KURSE_NEUE_FELDER_DOKUMENTATION.md` (diese Datei)

**Geänderte Dateien:**
- `src/Entity/Availability.php` - Neue Felder + Methoden
- `src/Controller/AppointmentController.php` - Speichern & Abrufen
- `src/Controller/Admin/CoursesController.php` - Termine generieren
- `templates/admin/courses/index.html.twig` - UI + JavaScript

