UI-Verbesserungen, PWA-Icons, Branding und Settings-Erweiterung
Invertiertes Logo für Admin-Navbar, neue PWA-Icons, Manifest-Updates, Tailwind-Config-Extraktion, Farb-/Namenseinstellungen im Admin-Bereich und diverse Layout-Optimierungen. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10
README.md
@@ -1,6 +1,12 @@
|
||||
# Handball Team Manager
|
||||
<p align="center">
|
||||
<img src="public/images/vereinos_logo.png" alt="VereinsOS Logo" width="200">
|
||||
</p>
|
||||
|
||||
**Open-Source-Webanwendung zur Verwaltung von Handball-Jugendmannschaften**
|
||||
<h1 align="center">VereinsOS</h1>
|
||||
|
||||
<p align="center">
|
||||
<strong>Open-Source-Webanwendung zur Verwaltung von Handball-Jugendmannschaften</strong>
|
||||
</p>
|
||||
|
||||
Eine moderne, mehrsprachige Web-App für Trainer, Elternvertreter und Familien, um Termine, Spieler, Fahrgemeinschaften, Statistiken und Vereinsorganisation an einem Ort zu bündeln. Optimiert für Shared Hosting (kein SSH nötig) und als Progressive Web App (PWA) auf dem Smartphone nutzbar.
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ActivityLog;
|
||||
use App\Models\FileCategory;
|
||||
use App\Models\Season;
|
||||
use App\Models\Setting;
|
||||
use App\Services\HtmlSanitizerService;
|
||||
use App\Services\SupportApiService;
|
||||
@@ -78,10 +79,12 @@ class SettingsController extends Controller
|
||||
'from_name' => config('mail.from.name'),
|
||||
];
|
||||
|
||||
$seasons = Season::orderByDesc('start_date')->get();
|
||||
|
||||
return view('admin.settings.edit', compact(
|
||||
'settings', 'eventDefaults', 'fileCategories', 'visibilitySettings',
|
||||
'isRegistered', 'installationId', 'updateInfo',
|
||||
'availableLocales', 'localeSettings', 'mailConfig'
|
||||
'availableLocales', 'localeSettings', 'mailConfig', 'seasons'
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -227,6 +227,8 @@ HTML;
|
||||
$visibilitySettings = [
|
||||
['key' => 'visibility_statistics_coach', 'label' => 'Statistik: Trainer', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_statistics_parent_rep', 'label' => 'Statistik: Elternvertretung', 'type' => 'number', 'value' => '1'],
|
||||
['key' => 'visibility_finances_coach', 'label' => 'Finanzen: Trainer', 'type' => 'number', 'value' => '1'],
|
||||
['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'],
|
||||
];
|
||||
|
||||
@@ -100,6 +100,22 @@ return [
|
||||
'rueckraum_rechts' => 'ظي',
|
||||
'kreislaeufer' => 'دو',
|
||||
],
|
||||
'finance_type' => [
|
||||
'income' => 'إيراد',
|
||||
'expense' => 'مصروف',
|
||||
],
|
||||
'finance_category' => [
|
||||
'catering' => 'تقديم الطعام',
|
||||
'sponsoring' => 'رعاية',
|
||||
'membership' => 'رسوم العضوية',
|
||||
'tournament_fees' => 'رسوم البطولات',
|
||||
'equipment' => 'معدات',
|
||||
'transport' => 'نقل',
|
||||
'venue_rental' => 'إيجار القاعة',
|
||||
'training_material' => 'مواد التدريب',
|
||||
'events' => 'فعاليات',
|
||||
'other' => 'أخرى',
|
||||
],
|
||||
],
|
||||
'locales' => [
|
||||
'de' => 'Deutsch',
|
||||
|
||||
@@ -115,6 +115,22 @@ return [
|
||||
'rueckraum_rechts' => 'RR',
|
||||
'kreislaeufer' => 'KL',
|
||||
],
|
||||
'finance_type' => [
|
||||
'income' => 'Einnahme',
|
||||
'expense' => 'Ausgabe',
|
||||
],
|
||||
'finance_category' => [
|
||||
'catering' => 'Catering',
|
||||
'sponsoring' => 'Sponsoring',
|
||||
'membership' => 'Mitgliedsbeiträge',
|
||||
'tournament_fees' => 'Turniergebühren',
|
||||
'equipment' => 'Ausrüstung',
|
||||
'transport' => 'Transport/Fahrtkosten',
|
||||
'venue_rental' => 'Hallenmiete',
|
||||
'training_material' => 'Trainingsmaterial',
|
||||
'events' => 'Veranstaltungen',
|
||||
'other' => 'Sonstiges',
|
||||
],
|
||||
],
|
||||
|
||||
// Sprachen
|
||||
|
||||
@@ -99,6 +99,22 @@ return [
|
||||
'rueckraum_rechts' => 'RB',
|
||||
'kreislaeufer' => 'PV',
|
||||
],
|
||||
'finance_type' => [
|
||||
'income' => 'Income',
|
||||
'expense' => 'Expense',
|
||||
],
|
||||
'finance_category' => [
|
||||
'catering' => 'Catering',
|
||||
'sponsoring' => 'Sponsoring',
|
||||
'membership' => 'Membership Fees',
|
||||
'tournament_fees' => 'Tournament Fees',
|
||||
'equipment' => 'Equipment',
|
||||
'transport' => 'Transport',
|
||||
'venue_rental' => 'Venue Rental',
|
||||
'training_material' => 'Training Material',
|
||||
'events' => 'Events',
|
||||
'other' => 'Other',
|
||||
],
|
||||
],
|
||||
'locales' => [
|
||||
'de' => 'Deutsch',
|
||||
|
||||
@@ -100,6 +100,22 @@ return [
|
||||
'rueckraum_rechts' => 'PR',
|
||||
'kreislaeufer' => 'KO',
|
||||
],
|
||||
'finance_type' => [
|
||||
'income' => 'Przychód',
|
||||
'expense' => 'Wydatek',
|
||||
],
|
||||
'finance_category' => [
|
||||
'catering' => 'Catering',
|
||||
'sponsoring' => 'Sponsoring',
|
||||
'membership' => 'Składki członkowskie',
|
||||
'tournament_fees' => 'Opłaty turniejowe',
|
||||
'equipment' => 'Sprzęt',
|
||||
'transport' => 'Transport',
|
||||
'venue_rental' => 'Wynajem hali',
|
||||
'training_material' => 'Materiały treningowe',
|
||||
'events' => 'Imprezy',
|
||||
'other' => 'Inne',
|
||||
],
|
||||
],
|
||||
'locales' => [
|
||||
'de' => 'Deutsch',
|
||||
|
||||
@@ -100,6 +100,22 @@ return [
|
||||
'rueckraum_rechts' => 'ПП',
|
||||
'kreislaeufer' => 'ЛН',
|
||||
],
|
||||
'finance_type' => [
|
||||
'income' => 'Доход',
|
||||
'expense' => 'Расход',
|
||||
],
|
||||
'finance_category' => [
|
||||
'catering' => 'Кейтеринг',
|
||||
'sponsoring' => 'Спонсорство',
|
||||
'membership' => 'Членские взносы',
|
||||
'tournament_fees' => 'Турнирные сборы',
|
||||
'equipment' => 'Оборудование',
|
||||
'transport' => 'Транспорт',
|
||||
'venue_rental' => 'Аренда зала',
|
||||
'training_material' => 'Тренировочный инвентарь',
|
||||
'events' => 'Мероприятия',
|
||||
'other' => 'Прочее',
|
||||
],
|
||||
],
|
||||
'locales' => [
|
||||
'de' => 'Deutsch',
|
||||
|
||||
@@ -100,6 +100,22 @@ return [
|
||||
'rueckraum_rechts' => 'SĞÇ',
|
||||
'kreislaeufer' => 'PV',
|
||||
],
|
||||
'finance_type' => [
|
||||
'income' => 'Gelir',
|
||||
'expense' => 'Gider',
|
||||
],
|
||||
'finance_category' => [
|
||||
'catering' => 'Catering',
|
||||
'sponsoring' => 'Sponsorluk',
|
||||
'membership' => 'Üyelik Aidatları',
|
||||
'tournament_fees' => 'Turnuva Ücretleri',
|
||||
'equipment' => 'Ekipman',
|
||||
'transport' => 'Ulaşım',
|
||||
'venue_rental' => 'Salon Kirası',
|
||||
'training_material' => 'Antrenman Malzemeleri',
|
||||
'events' => 'Etkinlikler',
|
||||
'other' => 'Diğer',
|
||||
],
|
||||
],
|
||||
'locales' => [
|
||||
'de' => 'Deutsch',
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 234 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 234 KiB |
BIN
public/images/vereinos_logo.png
Normal file
|
After Width: | Height: | Size: 228 KiB |
BIN
public/images/vereinos_logo_white.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
@@ -128,13 +128,16 @@ if (!file_exists($basePath . '/storage/installed')) {
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Installation — Systemcheck</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config={theme:{extend:{colors:{blue:{50:'#eef1f5',100:'#d9dfe8',200:'#b7c1d1',300:'#8e9db3',400:'#677a95',500:'#4a5e7a',600:'#2D3441',700:'#252b36',800:'#1e222b',900:'#161a21',950:'#0e1117'},green:{50:'#eef4f0',100:'#d6e7da',200:'#afd0b8',300:'#7fb38e',400:'#579469',500:'#3e7750',600:'#305f3f',700:'#284d34',800:'#223e2b',900:'#1c3324',950:'#0e1c13'},red:{50:'#f6f0f0',100:'#eddddc',200:'#dbbcba',300:'#c3918e',400:'#a86b67',500:'#8f504b',600:'#76403b',700:'#613532',800:'#502d2a',900:'#432725',950:'#241413'}}}}}
|
||||
</script>
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
</head>
|
||||
<body class="min-h-screen bg-gray-100 flex flex-col">
|
||||
<main class="flex-1 flex items-center justify-center px-4 py-12">
|
||||
<div class="w-full max-w-2xl">
|
||||
<div class="text-center mb-6">
|
||||
<img src="/images/logo_sg_woelfe.png" alt="Logo" class="mx-auto h-20 mb-3" onerror="this.style.display='none'">
|
||||
<img src="/images/vereinos_logo.png" alt="VereinsOS" class="mx-auto h-20 mb-3" onerror="this.style.display='none'">
|
||||
<h1 class="text-xl font-bold text-gray-900">Installation</h1>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "SG Wölfe Handball",
|
||||
"short_name": "SG Wölfe",
|
||||
"description": "Spieltermine und Teamorganisation",
|
||||
"name": "VereinsOS",
|
||||
"short_name": "VereinsOS",
|
||||
"description": "Vereinsorganisation und Teamverwaltung",
|
||||
"start_url": "/dashboard",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait-primary",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// ============================================================
|
||||
// Service Worker – SG Wölfe Handball WebApp
|
||||
// Service Worker – VereinsOS
|
||||
// Strategie: Lokale Assets cachen, HTML Network-First
|
||||
// ============================================================
|
||||
|
||||
const CACHE_NAME = 'handball-v3';
|
||||
const CACHE_NAME = 'vereinos-v1';
|
||||
const OFFLINE_URL = '/offline';
|
||||
|
||||
// Lokale Assets, die beim Install gecached werden
|
||||
@@ -14,7 +14,7 @@ const PRECACHE_ASSETS = [
|
||||
'/images/icon-192x192.png',
|
||||
'/images/icon-512x512.png',
|
||||
'/manifest.json',
|
||||
'/images/logo_woelfe.png'
|
||||
'/images/vereinos_logo.png'
|
||||
];
|
||||
|
||||
// ---- INSTALL ----
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</div>
|
||||
|
||||
{{-- Filter --}}
|
||||
<form method="GET" class="mb-4 flex gap-3">
|
||||
<form method="GET" class="mb-4 flex flex-wrap gap-3">
|
||||
<select name="team_id" onchange="this.form.submit()" class="px-3 py-2 border border-gray-300 rounded-md text-sm">
|
||||
<option value="">{{ __('ui.all_teams') }}</option>
|
||||
@foreach ($teams as $team)
|
||||
@@ -20,6 +20,14 @@
|
||||
<option value="{{ $status->value }}" {{ request('status') === $status->value ? 'selected' : '' }}>{{ $status->label() }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@if ($seasons->isNotEmpty())
|
||||
<select name="season_id" onchange="this.form.submit()" class="px-3 py-2 border border-gray-300 rounded-md text-sm">
|
||||
<option value="">{{ __('admin.all_seasons') }}</option>
|
||||
@foreach ($seasons as $season)
|
||||
<option value="{{ $season->id }}" {{ request('season_id') == $season->id ? 'selected' : '' }}>{{ $season->name }}{{ $season->is_current ? ' ●' : '' }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@endif
|
||||
</form>
|
||||
|
||||
{{-- Event-Karten --}}
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
color: #d1d5db;
|
||||
}
|
||||
.link {
|
||||
color: #2563eb;
|
||||
color: #4a5e7a;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,11 @@
|
||||
{{ __('admin.settings_tab_categories') }}
|
||||
</button>
|
||||
@if (auth()->user()->isAdmin())
|
||||
<button type="button" @click="tab = 'seasons'"
|
||||
:class="tab === 'seasons' ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'"
|
||||
class="whitespace-nowrap px-4 py-2.5 border-b-2 text-sm font-medium transition-colors" role="tab">
|
||||
{{ __('admin.settings_tab_seasons') }}
|
||||
</button>
|
||||
<button type="button" @click="tab = 'visibility'"
|
||||
:class="tab === 'visibility' ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'"
|
||||
class="whitespace-nowrap px-4 py-2.5 border-b-2 text-sm font-medium transition-colors" role="tab">
|
||||
@@ -438,6 +443,119 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Tab: Saisons (nur Admin) --}}
|
||||
@if (auth()->user()->isAdmin())
|
||||
<div x-show="tab === 'seasons'" role="tabpanel" x-data="{ editId: null, editName: '', editStart: '', editEnd: '', editCurrent: false }">
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">{{ __('admin.seasons_title') }}</h3>
|
||||
|
||||
{{-- Bestehende Saisons --}}
|
||||
@if ($seasons->isNotEmpty())
|
||||
<div class="overflow-x-auto mb-6">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-gray-50 border-b">
|
||||
<tr>
|
||||
<th class="text-left px-4 py-2 font-medium text-gray-600">{{ __('admin.season_name') }}</th>
|
||||
<th class="text-left px-4 py-2 font-medium text-gray-600">{{ __('admin.season_start') }}</th>
|
||||
<th class="text-left px-4 py-2 font-medium text-gray-600">{{ __('admin.season_end') }}</th>
|
||||
<th class="text-center px-4 py-2 font-medium text-gray-600">{{ __('admin.season_current') }}</th>
|
||||
<th class="text-right px-4 py-2 font-medium text-gray-600">{{ __('admin.actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
@foreach ($seasons as $season)
|
||||
<tr>
|
||||
<template x-if="editId === {{ $season->id }}">
|
||||
<td colspan="5" class="px-4 py-3">
|
||||
<form method="POST" action="{{ route('admin.seasons.update', $season) }}" class="flex flex-wrap items-end gap-3">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 mb-1">{{ __('admin.season_name') }}</label>
|
||||
<input type="text" name="name" x-model="editName" required class="px-2 py-1.5 border border-gray-300 rounded-md text-sm w-32">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 mb-1">{{ __('admin.season_start') }}</label>
|
||||
<input type="date" name="start_date" x-model="editStart" required class="px-2 py-1.5 border border-gray-300 rounded-md text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 mb-1">{{ __('admin.season_end') }}</label>
|
||||
<input type="date" name="end_date" x-model="editEnd" required class="px-2 py-1.5 border border-gray-300 rounded-md text-sm">
|
||||
</div>
|
||||
<label class="flex items-center gap-1.5 text-sm">
|
||||
<input type="hidden" name="is_current" value="0">
|
||||
<input type="checkbox" name="is_current" value="1" x-model="editCurrent" class="rounded border-gray-300">
|
||||
{{ __('admin.season_current') }}
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<button type="submit" class="px-3 py-1.5 bg-blue-600 text-white rounded-md text-sm hover:bg-blue-700">{{ __('ui.save') }}</button>
|
||||
<button type="button" @click="editId = null" class="px-3 py-1.5 bg-gray-100 text-gray-700 rounded-md text-sm hover:bg-gray-200">{{ __('ui.cancel') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</template>
|
||||
<template x-if="editId !== {{ $season->id }}">
|
||||
<td class="px-4 py-2 font-medium">{{ $season->name }}</td>
|
||||
</template>
|
||||
<template x-if="editId !== {{ $season->id }}">
|
||||
<td class="px-4 py-2">{{ $season->start_date->format('d.m.Y') }}</td>
|
||||
</template>
|
||||
<template x-if="editId !== {{ $season->id }}">
|
||||
<td class="px-4 py-2">{{ $season->end_date->format('d.m.Y') }}</td>
|
||||
</template>
|
||||
<template x-if="editId !== {{ $season->id }}">
|
||||
<td class="px-4 py-2 text-center">
|
||||
@if ($season->is_current)
|
||||
<span class="inline-block px-2 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">{{ __('admin.season_current') }}</span>
|
||||
@endif
|
||||
</td>
|
||||
</template>
|
||||
<template x-if="editId !== {{ $season->id }}">
|
||||
<td class="px-4 py-2 text-right space-x-2">
|
||||
<button type="button" @click="editId = {{ $season->id }}; editName = @js($season->name); editStart = @js($season->start_date->format('Y-m-d')); editEnd = @js($season->end_date->format('Y-m-d')); editCurrent = {{ $season->is_current ? 'true' : 'false' }}" class="text-blue-600 hover:underline text-sm">{{ __('ui.edit') }}</button>
|
||||
<form method="POST" action="{{ route('admin.seasons.destroy', $season) }}" class="inline" onsubmit="return confirm('{{ __('admin.season_confirm_delete') }}')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="text-red-600 hover:underline text-sm">{{ __('ui.delete') }}</button>
|
||||
</form>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<p class="text-sm text-gray-500 mb-6">{{ __('admin.no_seasons_yet') }}</p>
|
||||
@endif
|
||||
|
||||
{{-- Neue Saison erstellen --}}
|
||||
<h4 class="text-sm font-semibold text-gray-700 mb-3">{{ __('admin.new_season') }}</h4>
|
||||
<form method="POST" action="{{ route('admin.seasons.store') }}" class="flex flex-wrap items-end gap-3">
|
||||
@csrf
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 mb-1">{{ __('admin.season_name') }}</label>
|
||||
<input type="text" name="name" required placeholder="2025/2026" class="px-2 py-1.5 border border-gray-300 rounded-md text-sm w-32">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 mb-1">{{ __('admin.season_start') }}</label>
|
||||
<input type="date" name="start_date" required class="px-2 py-1.5 border border-gray-300 rounded-md text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-gray-500 mb-1">{{ __('admin.season_end') }}</label>
|
||||
<input type="date" name="end_date" required class="px-2 py-1.5 border border-gray-300 rounded-md text-sm">
|
||||
</div>
|
||||
<label class="flex items-center gap-1.5 text-sm">
|
||||
<input type="hidden" name="is_current" value="0">
|
||||
<input type="checkbox" name="is_current" value="1" class="rounded border-gray-300">
|
||||
{{ __('admin.season_current') }}
|
||||
</label>
|
||||
<button type="submit" class="px-3 py-1.5 bg-blue-600 text-white rounded-md text-sm hover:bg-blue-700">{{ __('ui.create') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Tab: Sichtbarkeit (nur Admin) --}}
|
||||
@if (auth()->user()->isAdmin())
|
||||
<div x-show="tab === 'visibility'" role="tabpanel">
|
||||
@@ -447,6 +565,7 @@
|
||||
@php
|
||||
$features = [
|
||||
'statistics' => __('admin.visibility_feature_statistics'),
|
||||
'finances' => __('admin.visibility_feature_finances'),
|
||||
'catering_history' => __('admin.visibility_feature_catering_history'),
|
||||
];
|
||||
$roles = [
|
||||
@@ -756,10 +875,13 @@
|
||||
],
|
||||
|
||||
init() {
|
||||
const validTabs = ['general', 'mail', 'legal', 'defaults', 'categories', 'visibility', 'license', 'maintenance'];
|
||||
const validTabs = ['general', 'mail', 'legal', 'defaults', 'categories', 'seasons', 'visibility', 'license', 'maintenance'];
|
||||
const urlTab = new URLSearchParams(window.location.search).get('tab');
|
||||
const hash = window.location.hash.replace('#', '');
|
||||
const stored = sessionStorage.getItem('settings_tab');
|
||||
if (validTabs.includes(hash)) {
|
||||
if (validTabs.includes(urlTab)) {
|
||||
this.tab = urlTab;
|
||||
} else if (validTabs.includes(hash)) {
|
||||
this.tab = hash;
|
||||
} else if (validTabs.includes(stored)) {
|
||||
this.tab = stored;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>{{ __('ui.admin') }} - {{ $title ?? \App\Models\Setting::get('app_name', config('app.name')) }}</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
@include('components.tailwind-config')
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.9/dist/cdn.min.js" integrity="sha384-9Ax3MmS9AClxJyd5/zafcXXjxmwFhZCdsT6HJoJjarvCaAkJlk5QDzjLJm+Wdx5F" crossorigin="anonymous"></script>
|
||||
@php $favicon = \App\Models\Setting::get('app_favicon'); @endphp
|
||||
@if ($favicon)
|
||||
@@ -18,7 +19,7 @@
|
||||
<meta name="theme-color" content="#1f2937">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="SG Wölfe">
|
||||
<meta name="apple-mobile-web-app-title" content="{{ \App\Models\Setting::get('app_name', 'VereinsOS') }}">
|
||||
<link rel="apple-touch-icon" href="/images/apple-touch-icon.png">
|
||||
@stack('styles')
|
||||
</head>
|
||||
@@ -29,7 +30,8 @@
|
||||
<div class="flex justify-between h-14">
|
||||
<div class="flex items-center space-x-6 rtl:space-x-reverse">
|
||||
<a href="{{ route('admin.dashboard') }}" class="flex items-center gap-2 font-bold">
|
||||
<img src="/images/logo_woelfe.png" alt="Logo" class="h-8 w-8 object-contain">
|
||||
@php $logoApp = \App\Models\Setting::get('app_logo_app'); @endphp
|
||||
<img src="{{ $logoApp ? asset('storage/' . $logoApp) : asset('images/vereinos_logo_white.png') }}" alt="Logo" class="h-8 w-8 object-contain">
|
||||
{{ __('ui.admin') }}
|
||||
</a>
|
||||
{{-- Desktop nav links (ab lg sichtbar) --}}
|
||||
@@ -38,6 +40,9 @@
|
||||
@if (\App\Models\Setting::isFeatureVisibleFor('statistics', auth()->user()))
|
||||
<a href="{{ route('admin.statistics.index') }}" class="text-sm text-gray-300 hover:text-white {{ request()->routeIs('admin.statistics.*') ? 'text-white font-semibold' : '' }}">{{ __('admin.nav_statistics') }}</a>
|
||||
@endif
|
||||
@if (\App\Models\Setting::isFeatureVisibleFor('finances', auth()->user()))
|
||||
<a href="{{ route('admin.finances.index') }}" class="text-sm text-gray-300 hover:text-white {{ request()->routeIs('admin.finances.*') ? 'text-white font-semibold' : '' }}">{{ __('admin.nav_finances') }}</a>
|
||||
@endif
|
||||
@if (auth()->user()->isStaff())
|
||||
<a href="{{ route('admin.teams.index') }}" class="text-sm text-gray-300 hover:text-white {{ request()->routeIs('admin.teams.*') ? 'text-white font-semibold' : '' }}">{{ __('admin.nav_teams') }}</a>
|
||||
<a href="{{ route('admin.players.index') }}" class="text-sm text-gray-300 hover:text-white {{ request()->routeIs('admin.players.*') ? 'text-white font-semibold' : '' }}">{{ __('admin.nav_players') }}</a>
|
||||
@@ -104,6 +109,9 @@
|
||||
@if (\App\Models\Setting::isFeatureVisibleFor('statistics', auth()->user()))
|
||||
<a href="{{ route('admin.statistics.index') }}" class="block py-2 text-sm text-gray-300">{{ __('admin.nav_statistics') }}</a>
|
||||
@endif
|
||||
@if (\App\Models\Setting::isFeatureVisibleFor('finances', auth()->user()))
|
||||
<a href="{{ route('admin.finances.index') }}" class="block py-2 text-sm text-gray-300">{{ __('admin.nav_finances') }}</a>
|
||||
@endif
|
||||
@if (auth()->user()->isStaff())
|
||||
<a href="{{ route('admin.teams.index') }}" class="block py-2 text-sm text-gray-300">{{ __('admin.nav_teams') }}</a>
|
||||
<a href="{{ route('admin.players.index') }}" class="block py-2 text-sm text-gray-300">{{ __('admin.nav_players') }}</a>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>{{ $title ?? \App\Models\Setting::get('app_name', config('app.name')) }}</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
@include('components.tailwind-config')
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.9/dist/cdn.min.js" integrity="sha384-9Ax3MmS9AClxJyd5/zafcXXjxmwFhZCdsT6HJoJjarvCaAkJlk5QDzjLJm+Wdx5F" crossorigin="anonymous"></script>
|
||||
@php $favicon = \App\Models\Setting::get('app_favicon'); @endphp
|
||||
@if ($favicon)
|
||||
@@ -18,7 +19,7 @@
|
||||
<meta name="theme-color" content="#1f2937">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="SG Wölfe">
|
||||
<meta name="apple-mobile-web-app-title" content="{{ \App\Models\Setting::get('app_name', 'VereinsOS') }}">
|
||||
<link rel="apple-touch-icon" href="/images/apple-touch-icon.png">
|
||||
@stack('styles')
|
||||
</head>
|
||||
@@ -30,7 +31,7 @@
|
||||
<div class="flex items-center space-x-6 rtl:space-x-reverse">
|
||||
@php $logoApp = \App\Models\Setting::get('app_logo_app'); @endphp
|
||||
<a href="{{ route('dashboard') }}" class="flex items-center gap-2 font-bold text-gray-900">
|
||||
<img src="{{ $logoApp ? asset('storage/' . $logoApp) : asset('images/logo_woelfe.png') }}" alt="Logo" class="h-8 w-8 object-contain">
|
||||
<img src="{{ $logoApp ? asset('storage/' . $logoApp) : asset('images/vereinos_logo.png') }}" alt="Logo" class="h-8 w-8 object-contain">
|
||||
{{ \App\Models\Setting::get('app_name', config('app.name')) }}
|
||||
</a>
|
||||
<div class="hidden sm:flex items-center space-x-6 rtl:space-x-reverse">
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>{{ $title ?? \App\Models\Setting::get('app_name', config('app.name')) }}</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
@include('components.tailwind-config')
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.9/dist/cdn.min.js" integrity="sha384-9Ax3MmS9AClxJyd5/zafcXXjxmwFhZCdsT6HJoJjarvCaAkJlk5QDzjLJm+Wdx5F" crossorigin="anonymous"></script>
|
||||
@php $favicon = \App\Models\Setting::get('app_favicon'); @endphp
|
||||
@if ($favicon)
|
||||
@@ -17,7 +18,7 @@
|
||||
<meta name="theme-color" content="#1f2937">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="SG Wölfe">
|
||||
<meta name="apple-mobile-web-app-title" content="{{ \App\Models\Setting::get('app_name', 'VereinsOS') }}">
|
||||
<link rel="apple-touch-icon" href="/images/apple-touch-icon.png">
|
||||
</head>
|
||||
<body class="min-h-screen bg-gray-100 flex flex-col">
|
||||
@@ -26,7 +27,7 @@
|
||||
<div class="text-center mb-8">
|
||||
@php $logoLogin = \App\Models\Setting::get('app_logo_login'); @endphp
|
||||
<a href="{{ auth()->check() ? route('dashboard') : route('login') }}">
|
||||
<img src="{{ $logoLogin ? asset('storage/' . $logoLogin) : asset('images/logo_sg_woelfe.png') }}" alt="Logo" class="mx-auto h-24 mb-3 object-contain">
|
||||
<img src="{{ $logoLogin ? asset('storage/' . $logoLogin) : asset('images/vereinos_logo.png') }}" alt="Logo" class="mx-auto h-24 mb-3 object-contain">
|
||||
</a>
|
||||
<h1 class="text-2xl font-bold text-gray-900">{{ \App\Models\Setting::get('app_name', config('app.name')) }}</h1>
|
||||
@php $slogan = \App\Models\Setting::get('app_slogan'); @endphp
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Installation — Handball WebApp</title>
|
||||
<title>Installation — VereinsOS</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
@include('components.tailwind-config')
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.9/dist/cdn.min.js" integrity="sha384-9Ax3MmS9AClxJyd5/zafcXXjxmwFhZCdsT6HJoJjarvCaAkJlk5QDzjLJm+Wdx5F" crossorigin="anonymous"></script>
|
||||
<link rel="icon" href="{{ asset('favicon.ico') }}">
|
||||
</head>
|
||||
@@ -13,7 +14,7 @@
|
||||
<div class="w-full max-w-2xl">
|
||||
{{-- Logo --}}
|
||||
<div class="text-center mb-6">
|
||||
<img src="/images/logo_sg_woelfe.png" alt="Logo" class="mx-auto h-20 mb-3">
|
||||
<img src="/images/vereinos_logo.png" alt="VereinsOS" class="mx-auto h-20 mb-3">
|
||||
<h1 class="text-xl font-bold text-gray-900">Installation</h1>
|
||||
</div>
|
||||
|
||||
@@ -63,7 +64,7 @@
|
||||
</div>
|
||||
</main>
|
||||
<footer class="text-center py-3 text-xs text-gray-400">
|
||||
Handball WebApp — Installation
|
||||
VereinsOS — Installation
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
115
resources/views/components/tailwind-config.blade.php
Normal file
@@ -0,0 +1,115 @@
|
||||
{{-- Zentrale Tailwind-Farbkonfiguration – VereinsOS --}}
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
blue: {
|
||||
50: '#eef1f5',
|
||||
100: '#d9dfe8',
|
||||
200: '#b7c1d1',
|
||||
300: '#8e9db3',
|
||||
400: '#677a95',
|
||||
500: '#4a5e7a',
|
||||
600: '#2D3441',
|
||||
700: '#252b36',
|
||||
800: '#1e222b',
|
||||
900: '#161a21',
|
||||
950: '#0e1117',
|
||||
},
|
||||
green: {
|
||||
50: '#eef4f0',
|
||||
100: '#d6e7da',
|
||||
200: '#afd0b8',
|
||||
300: '#7fb38e',
|
||||
400: '#579469',
|
||||
500: '#3e7750',
|
||||
600: '#305f3f',
|
||||
700: '#284d34',
|
||||
800: '#223e2b',
|
||||
900: '#1c3324',
|
||||
950: '#0e1c13',
|
||||
},
|
||||
red: {
|
||||
50: '#f6f0f0',
|
||||
100: '#eddddc',
|
||||
200: '#dbbcba',
|
||||
300: '#c3918e',
|
||||
400: '#a86b67',
|
||||
500: '#8f504b',
|
||||
600: '#76403b',
|
||||
700: '#613532',
|
||||
800: '#502d2a',
|
||||
900: '#432725',
|
||||
950: '#241413',
|
||||
},
|
||||
yellow: {
|
||||
50: '#f6f3ec',
|
||||
100: '#ebe4d0',
|
||||
200: '#d8c9a2',
|
||||
300: '#c2a86e',
|
||||
400: '#ae8e49',
|
||||
500: '#99783a',
|
||||
600: '#806130',
|
||||
700: '#674c29',
|
||||
800: '#553f26',
|
||||
900: '#483523',
|
||||
950: '#271c11',
|
||||
},
|
||||
amber: {
|
||||
50: '#f7f3ec',
|
||||
100: '#ede3cf',
|
||||
200: '#dbc7a0',
|
||||
300: '#c6a46a',
|
||||
400: '#b58a43',
|
||||
500: '#a07537',
|
||||
600: '#875e2e',
|
||||
700: '#6e4a28',
|
||||
800: '#5b3d25',
|
||||
900: '#4d3321',
|
||||
950: '#2a1b10',
|
||||
},
|
||||
purple: {
|
||||
50: '#f2f0f5',
|
||||
100: '#e2dde9',
|
||||
200: '#c7bed5',
|
||||
300: '#a596ba',
|
||||
400: '#84729e',
|
||||
500: '#6b5a84',
|
||||
600: '#57486c',
|
||||
700: '#483c59',
|
||||
800: '#3c334a',
|
||||
900: '#332b3e',
|
||||
950: '#1e1824',
|
||||
},
|
||||
indigo: {
|
||||
50: '#eff1f6',
|
||||
100: '#dbdfec',
|
||||
200: '#bac2da',
|
||||
300: '#939fc1',
|
||||
400: '#717ea6',
|
||||
500: '#57638d',
|
||||
600: '#465073',
|
||||
700: '#3a4260',
|
||||
800: '#31394f',
|
||||
900: '#2b3143',
|
||||
950: '#191d28',
|
||||
},
|
||||
teal: {
|
||||
50: '#eef3f3',
|
||||
100: '#d5e5e3',
|
||||
200: '#adccc8',
|
||||
300: '#7eada8',
|
||||
400: '#578e88',
|
||||
500: '#41746e',
|
||||
600: '#345d58',
|
||||
700: '#2c4c48',
|
||||
800: '#263f3c',
|
||||
900: '#213433',
|
||||
950: '#111e1d',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,5 +1,13 @@
|
||||
<x-layouts.app :title="__('ui.dashboard')">
|
||||
<h1 class="text-2xl font-bold mb-6">{{ __('events.hello_user', ['name' => auth()->user()->name]) }}</h1>
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-2xl font-bold">{{ __('events.hello_user', ['name' => auth()->user()->name]) }}</h1>
|
||||
@if (auth()->user()->isStaff())
|
||||
<a href="{{ route('admin.events.create') }}" class="inline-flex items-center gap-1.5 bg-blue-600 text-white px-4 py-2 rounded-md text-sm font-medium hover:bg-blue-700 transition-colors">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/></svg>
|
||||
{{ __('admin.new_event') }}
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Kalender --}}
|
||||
<div x-data="calendarApp()" class="mb-8">
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
<div>
|
||||
<label for="mail_from_name" class="block text-sm font-medium text-gray-700 mb-1">Absender-Name <span class="text-gray-400 font-normal">(optional)</span></label>
|
||||
<input type="text" name="mail_from_name" id="mail_from_name" value="{{ old('mail_from_name') }}"
|
||||
placeholder="z.B. SG Woelfe Handball"
|
||||
placeholder="z.B. Mein Verein"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Offline – SG Wölfe Handball</title>
|
||||
<title>Offline – VereinsOS</title>
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<meta name="theme-color" content="#1f2937">
|
||||
<link rel="icon" href="/images/icon-192x192.png">
|
||||
@@ -40,7 +40,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<img src="/images/icon-192x192.png" alt="SG Wölfe" class="logo">
|
||||
<img src="/images/icon-192x192.png" alt="VereinsOS" class="logo">
|
||||
<h1>Keine Internetverbindung</h1>
|
||||
<p>
|
||||
Die Seite kann gerade nicht geladen werden.
|
||||
|
||||