Feature-Toggles, Administration, wiederkehrende Events und Event-Serien
- Administration & Rollenmanagement: Neuer Admin-Bereich mit Feature-Toggles und Sichtbarkeitseinstellungen pro Rolle (11 Toggles, 24 Visibility-Settings) - AdministrationController mit eigenem Settings-Tab, aus SettingsController extrahiert - Feature-Toggle-Guards in Controllers (Invitation, File, ListGenerator, Comment) und Views (events/show, events/edit, events/create) - Setting::isFeatureEnabled() und isFeatureVisibleFor() Hilfsmethoden - Wiederkehrende Trainings: Täglich/Wöchentlich/2-Wöchentlich mit Ende per Datum oder Anzahl (max. 52), Vorschau im Formular - Event-Serien: Verknüpfung über event_series_id (UUID), Modal-Dialog beim Speichern und Löschen mit Optionen "nur dieses" / "alle folgenden" - Löschen-Button direkt in der Event-Bearbeitung mit Serien-Dialog - DemoDataSeeder: 4 Trainings als Serie mit gemeinsamer event_series_id - Übersetzungen in allen 6 Sprachen (de, en, pl, ru, ar, tr) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,7 @@ use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class DemoDataSeeder extends Seeder
|
||||
{
|
||||
@@ -324,7 +325,8 @@ class DemoDataSeeder extends Seeder
|
||||
]
|
||||
);
|
||||
|
||||
// 1: Training (Zukunft)
|
||||
// 1: Training (Zukunft) — Teil einer Trainingsserie
|
||||
$trainingSeriesId = (string) Str::uuid();
|
||||
$events[] = Event::updateOrCreate(
|
||||
['title' => 'Training nächste Woche', 'team_id' => $team->id, 'start_at' => now()->next('Tuesday')->setTime(17, 0)],
|
||||
[
|
||||
@@ -338,6 +340,7 @@ class DemoDataSeeder extends Seeder
|
||||
'min_players' => 12,
|
||||
'min_catering' => 1,
|
||||
'min_timekeepers' => 1,
|
||||
'event_series_id' => $trainingSeriesId,
|
||||
'description_html' => '<p>Training mit Schwerpunkt Passspiel. Bitte pünktlich kommen!</p>',
|
||||
'created_by' => $admin->id,
|
||||
]
|
||||
@@ -571,6 +574,29 @@ class DemoDataSeeder extends Seeder
|
||||
]
|
||||
);
|
||||
|
||||
// Serien-Folgetermine: 3 weitere Trainings (gleiche event_series_id wie Event 1)
|
||||
for ($i = 1; $i <= 3; $i++) {
|
||||
$tuesday = now()->next('Tuesday')->addWeeks($i);
|
||||
Event::updateOrCreate(
|
||||
['title' => 'Training Dienstag (Serie)', 'team_id' => $team->id, 'start_at' => $tuesday->copy()->setTime(17, 0)],
|
||||
[
|
||||
'type' => EventType::Training,
|
||||
'end_at' => $tuesday->copy()->setTime(18, 30),
|
||||
'status' => EventStatus::Published,
|
||||
'location_name' => $locations[0]->name,
|
||||
'address_text' => $locations[0]->address_text,
|
||||
'location_lat' => $locations[0]->location_lat,
|
||||
'location_lng' => $locations[0]->location_lng,
|
||||
'min_players' => 12,
|
||||
'min_catering' => 1,
|
||||
'min_timekeepers' => 1,
|
||||
'event_series_id' => $trainingSeriesId,
|
||||
'description_html' => '<p>Wöchentliches Training. Bitte <strong>Hallenschuhe</strong> und ausreichend Trinken mitbringen.</p>',
|
||||
'created_by' => $admin->id,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
|
||||
@@ -223,6 +223,30 @@ HTML;
|
||||
]);
|
||||
}
|
||||
|
||||
// Globale Feature-Toggles (Master-Schalter)
|
||||
$featureToggles = [
|
||||
['key' => 'feature_statistics', 'label' => 'Feature: Statistiken'],
|
||||
['key' => 'feature_finances', 'label' => 'Feature: Finanzen'],
|
||||
['key' => 'feature_catering', 'label' => 'Feature: Catering'],
|
||||
['key' => 'feature_timekeepers', 'label' => 'Feature: Zeitnehmer'],
|
||||
['key' => 'feature_carpools', 'label' => 'Feature: Fahrgemeinschaften'],
|
||||
['key' => 'feature_comments', 'label' => 'Feature: Kommentare'],
|
||||
['key' => 'feature_files', 'label' => 'Feature: Dateien'],
|
||||
['key' => 'feature_faqs', 'label' => 'Feature: FAQs'],
|
||||
['key' => 'feature_list_generator', 'label' => 'Feature: Listenerstellung'],
|
||||
['key' => 'feature_invitations', 'label' => 'Feature: Einladungen'],
|
||||
['key' => 'feature_player_stats', 'label' => 'Feature: Spielerstatistiken'],
|
||||
];
|
||||
|
||||
foreach ($featureToggles as $toggle) {
|
||||
$existing = Setting::where('key', $toggle['key'])->first();
|
||||
if ($existing) {
|
||||
$existing->update(['label' => $toggle['label'], 'type' => 'number']);
|
||||
} else {
|
||||
$this->createSetting(array_merge($toggle, ['type' => 'number', 'value' => '1']));
|
||||
}
|
||||
}
|
||||
|
||||
// Sichtbarkeits-Einstellungen (pro Feature pro Rolle)
|
||||
$visibilitySettings = [
|
||||
['key' => 'visibility_statistics_coach', 'label' => 'Statistik: Trainer', 'type' => 'number', 'value' => '1'],
|
||||
@@ -231,6 +255,24 @@ HTML;
|
||||
['key' => 'visibility_finances_parent_rep', 'label' => 'Finanzen: Elternvertretung', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_catering_history_coach', 'label' => 'Catering-Verlauf: Trainer', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_catering_history_parent_rep', 'label' => 'Catering-Verlauf: Elternvertretung', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_catering_coach', 'label' => 'Catering: Trainer', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_catering_parent_rep', 'label' => 'Catering: Elternvertretung', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_timekeepers_coach', 'label' => 'Zeitnehmer: Trainer', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_timekeepers_parent_rep', 'label' => 'Zeitnehmer: Elternvertretung', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_carpools_coach', 'label' => 'Fahrgemeinschaften: Trainer', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_carpools_parent_rep', 'label' => 'Fahrgemeinschaften: Elternvertretung', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_comments_coach', 'label' => 'Kommentare: Trainer', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_comments_parent_rep', 'label' => 'Kommentare: Elternvertretung', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_files_coach', 'label' => 'Dateien: Trainer', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_files_parent_rep', 'label' => 'Dateien: Elternvertretung', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_faqs_coach', 'label' => 'FAQs: Trainer', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_faqs_parent_rep', 'label' => 'FAQs: Elternvertretung', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_list_generator_coach', 'label' => 'Listenerstellung: Trainer', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_list_generator_parent_rep', 'label' => 'Listenerstellung: Elternvertretung', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_invitations_coach', 'label' => 'Einladungen: Trainer', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_invitations_parent_rep', 'label' => 'Einladungen: Elternvertretung', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_player_stats_coach', 'label' => 'Spielerstatistiken: Trainer', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_player_stats_parent_rep', 'label' => 'Spielerstatistiken: Elternvertretung', 'type' => 'number', 'value' => '1'],
|
||||
];
|
||||
|
||||
foreach ($visibilitySettings as $setting) {
|
||||
|
||||
Reference in New Issue
Block a user