feat: Lernapp mit Mathe/Deutsch/Englisch, Münzsystem und Belohnungen

This commit is contained in:
root
2026-05-05 14:41:09 +00:00
parent 21e40cd2da
commit bd1640994c
45 changed files with 1522 additions and 58 deletions
+51
View File
@@ -0,0 +1,51 @@
@extends('layouts.admin')
@section('title','Dashboard')
@section('content')
<div class="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
<div class="bg-white rounded-xl p-5 shadow-sm border border-slate-200">
<div class="text-2xl font-bold text-slate-800">{{ $children }}</div>
<div class="text-sm text-slate-500 mt-1">👦 Kinder</div>
</div>
<div class="bg-white rounded-xl p-5 shadow-sm border border-slate-200">
<div class="text-2xl font-bold text-slate-800">{{ $questions }}</div>
<div class="text-sm text-slate-500 mt-1"> Fragen</div>
</div>
<div class="bg-white rounded-xl p-5 shadow-sm border border-slate-200">
<div class="text-2xl font-bold text-slate-800">{{ $attempts_today }}</div>
<div class="text-sm text-slate-500 mt-1">📝 Antworten heute</div>
</div>
<div class="bg-white rounded-xl p-5 shadow-sm border border-slate-200 {{ $pending > 0 ? 'border-red-300 bg-red-50' : '' }}">
<div class="text-2xl font-bold {{ $pending > 0 ? 'text-red-600' : 'text-slate-800' }}">{{ $pending }}</div>
<div class="text-sm text-slate-500 mt-1">🎁 Offene Einlösungen</div>
</div>
</div>
<div class="grid lg:grid-cols-2 gap-6">
<div class="bg-white rounded-xl shadow-sm border border-slate-200 p-5">
<h2 class="font-semibold text-slate-800 mb-4">🏆 Bestenliste</h2>
@forelse($topKids as $i => $kid)
<div class="flex items-center justify-between py-2 {{ !$loop->last ? 'border-b border-slate-100' : '' }}">
<div class="flex items-center gap-3">
<span class="text-lg">{{ ['🥇','🥈','🥉','4.','5.'][$i] }}</span>
<span class="font-medium text-slate-700">{{ $kid->name }}</span>
</div>
<span class="text-amber-600 font-bold">🪙 {{ $kid->points }}</span>
</div>
@empty<p class="text-slate-400 text-sm">Noch keine Kinder angelegt.</p>@endforelse
</div>
<div class="bg-white rounded-xl shadow-sm border border-slate-200 p-5">
<h2 class="font-semibold text-slate-800 mb-4">📝 Letzte Aktivität</h2>
<div class="space-y-2">
@forelse($recentAttempts as $a)
<div class="flex items-center justify-between text-sm">
<div class="flex items-center gap-2">
<span>{{ $a->is_correct ? '✅' : '❌' }}</span>
<span class="text-slate-600">{{ $a->user->name }}</span>
<span class="text-slate-400"> {{ $a->question->subject->name }}</span>
</div>
<span class="text-xs text-slate-400">{{ $a->created_at->diffForHumans() }}</span>
</div>
@empty<p class="text-slate-400 text-sm">Noch keine Aktivität.</p>@endforelse
</div>
</div>
</div>
@endsection
@@ -0,0 +1,54 @@
@extends('layouts.admin')
@section('title','Neue Frage')
@section('content')
<div class="max-w-2xl">
<a href="{{ route('admin.questions.index') }}" class="text-sm text-slate-500 hover:text-slate-700 mb-4 inline-block"> Zurück</a>
<div class="bg-white rounded-xl shadow-sm border border-slate-200 p-6" x-data="{type:'multiple_choice'}">
<h2 class="font-semibold text-slate-800 mb-5">Neue Frage erstellen</h2>
<form method="POST" action="{{ route('admin.questions.store') }}" class="space-y-5">
@csrf
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Fach</label>
<select name="subject_id" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
@foreach($subjects as $s)<option value="{{ $s->id }}">{{ $s->icon }} {{ $s->name }}</option>@endforeach
</select>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Schwierigkeit</label>
<select name="difficulty" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
<option value="1"> Leicht (5 Münzen)</option>
<option value="2">⭐⭐ Mittel (10 Münzen)</option>
<option value="3">⭐⭐⭐ Schwer (20 Münzen)</option>
</select>
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Typ</label>
<select name="type" x-model="type" class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
<option value="multiple_choice">Multiple Choice</option>
<option value="number_input">Zahlenantwort</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Frage</label>
<textarea name="question_text" required rows="2" class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">{{ old('question_text') }}</textarea>
</div>
<div x-show="type==='multiple_choice'">
<label class="block text-sm font-medium text-slate-700 mb-2">Antwortmöglichkeiten <span class="text-slate-400">(Richtige markieren)</span></label>
@for($i=0;$i<4;$i++)
<div class="flex items-center gap-3 mb-2">
<input type="radio" name="correct" value="{{ $i }}" {{ $i===0?'checked':'' }} class="accent-violet-600">
<input type="text" name="options[]" placeholder="Antwort {{ $i+1 }}" class="flex-1 border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
@endfor
</div>
<div x-show="type==='number_input'">
<label class="block text-sm font-medium text-slate-700 mb-1">Richtige Antwort (Zahl)</label>
<input type="text" name="number_answer" class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
<button type="submit" class="w-full bg-violet-600 hover:bg-violet-700 text-white py-2 rounded-lg font-medium text-sm">Frage speichern</button>
</form>
</div>
</div>
@endsection
@@ -0,0 +1,49 @@
@extends('layouts.admin')
@section('title','Frage bearbeiten')
@section('content')
<div class="max-w-2xl">
<a href="{{ route('admin.questions.index') }}" class="text-sm text-slate-500 hover:text-slate-700 mb-4 inline-block"> Zurück</a>
<div class="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<h2 class="font-semibold text-slate-800 mb-5">Frage bearbeiten</h2>
<form method="POST" action="{{ route('admin.questions.update',$question) }}" class="space-y-5">
@csrf @method('PUT')
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Fach</label>
<select name="subject_id" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
@foreach($subjects as $s)<option value="{{ $s->id }}" {{ $s->id==$question->subject_id?'selected':'' }}>{{ $s->icon }} {{ $s->name }}</option>@endforeach
</select>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Schwierigkeit</label>
<select name="difficulty" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
@foreach([1=>'⭐ Leicht (5 Münzen)',2=>'⭐⭐ Mittel (10 Münzen)',3=>'⭐⭐⭐ Schwer (20 Münzen)'] as $v=>$l)
<option value="{{ $v }}" {{ $question->difficulty==$v?'selected':'' }}>{{ $l }}</option>
@endforeach
</select>
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Frage</label>
<textarea name="question_text" required rows="2" class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">{{ old('question_text',$question->question_text) }}</textarea>
</div>
@if($question->type==='multiple_choice')
<div>
<label class="block text-sm font-medium text-slate-700 mb-2">Antwortmöglichkeiten</label>
@foreach($question->answerOptions as $i=>$opt)
<div class="flex items-center gap-3 mb-2">
<input type="radio" name="correct" value="{{ $i }}" {{ $opt->is_correct?'checked':'' }} class="accent-violet-600">
<input type="text" name="options[]" value="{{ $opt->text }}" class="flex-1 border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
@endforeach
</div>
@endif
<div class="flex items-center gap-3">
<input type="checkbox" name="active" id="active" value="1" {{ $question->active?'checked':'' }} class="accent-violet-600">
<label for="active" class="text-sm font-medium text-slate-700">Frage aktiv</label>
</div>
<button type="submit" class="w-full bg-violet-600 hover:bg-violet-700 text-white py-2 rounded-lg font-medium text-sm">Speichern</button>
</form>
</div>
</div>
@endsection
@@ -0,0 +1,47 @@
@extends('layouts.admin')
@section('title','Fragen')
@section('content')
<div class="flex flex-wrap gap-3 items-center justify-between mb-6">
<form method="GET" class="flex gap-2">
<select name="subject" class="border border-slate-300 rounded-lg px-3 py-2 text-sm">
<option value="">Alle Fächer</option>
@foreach($subjects as $s)<option value="{{ $s->id }}" {{ request('subject')==$s->id?'selected':'' }}>{{ $s->icon }} {{ $s->name }}</option>@endforeach
</select>
<button class="bg-slate-700 text-white px-4 py-2 rounded-lg text-sm">Filtern</button>
</form>
<a href="{{ route('admin.questions.create') }}" class="bg-violet-600 hover:bg-violet-700 text-white px-4 py-2 rounded-lg text-sm font-medium">+ Neue Frage</a>
</div>
<div class="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-slate-50 border-b border-slate-200">
<tr>
<th class="text-left px-4 py-3 font-medium text-slate-600">Frage</th>
<th class="text-left px-4 py-3 font-medium text-slate-600">Fach</th>
<th class="text-center px-4 py-3 font-medium text-slate-600">Schwierigkeit</th>
<th class="text-center px-4 py-3 font-medium text-slate-600">Aktiv</th>
<th class="px-4 py-3"></th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
@forelse($questions as $q)
<tr class="hover:bg-slate-50">
<td class="px-4 py-3 text-slate-700 max-w-xs truncate">{{ $q->question_text }}</td>
<td class="px-4 py-3"><span class="text-base">{{ $q->subject->icon }}</span> {{ $q->subject->name }}</td>
<td class="px-4 py-3 text-center">{{ $q->difficultyStars() }}</td>
<td class="px-4 py-3 text-center">{{ $q->active ? '✅' : '⏸️' }}</td>
<td class="px-4 py-3 text-right whitespace-nowrap">
<a href="{{ route('admin.questions.edit',$q) }}" class="text-violet-600 hover:underline mr-3">Bearbeiten</a>
<form method="POST" action="{{ route('admin.questions.destroy',$q) }}" class="inline" onsubmit="return confirm('Löschen?')">
@csrf @method('DELETE')
<button class="text-red-500 hover:underline">Löschen</button>
</form>
</td>
</tr>
@empty
<tr><td colspan="5" class="px-4 py-8 text-center text-slate-400">Keine Fragen vorhanden.</td></tr>
@endforelse
</tbody>
</table>
</div>
<div class="mt-4">{{ $questions->links() }}</div>
@endsection
@@ -0,0 +1,63 @@
@extends('layouts.admin')
@section('title','Einlösungen')
@section('content')
@if($pending->count())
<h2 class="font-semibold text-slate-800 mb-4"> Warten auf Freigabe</h2>
<div class="space-y-3 mb-8">
@foreach($pending as $red)
<div class="bg-white rounded-xl shadow-sm border border-amber-200 p-5 flex items-center justify-between gap-4">
<div class="flex items-center gap-4">
<span class="text-3xl">{{ $red->reward->icon }}</span>
<div>
<div class="font-semibold text-slate-800">{{ $red->user->name }} möchte: {{ $red->reward->name }}</div>
<div class="text-sm text-slate-500">🪙 {{ $red->points_spent }} Münzen · {{ $red->created_at->diffForHumans() }}</div>
</div>
</div>
<div class="flex gap-2 shrink-0">
<form method="POST" action="{{ route('admin.redemptions.approve',$red) }}">
@csrf @method('PATCH')
<button class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-lg text-sm font-medium"> Freigeben</button>
</form>
<form method="POST" action="{{ route('admin.redemptions.reject',$red) }}" x-data x-on:submit.prevent="if(confirm('Ablehnen?')) $el.submit()">
@csrf @method('PATCH')
<button class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg text-sm font-medium"> Ablehnen</button>
</form>
</div>
</div>
@endforeach
</div>
@else
<div class="bg-green-50 border border-green-200 rounded-xl p-5 mb-8 text-green-700 font-medium"> Keine offenen Einlösungen.</div>
@endif
<h2 class="font-semibold text-slate-800 mb-4">📋 Verlauf</h2>
<div class="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-slate-50 border-b border-slate-200">
<tr>
<th class="text-left px-4 py-3 font-medium text-slate-600">Kind</th>
<th class="text-left px-4 py-3 font-medium text-slate-600">Belohnung</th>
<th class="text-center px-4 py-3 font-medium text-slate-600">Status</th>
<th class="text-right px-4 py-3 font-medium text-slate-600">Münzen</th>
<th class="text-right px-4 py-3 font-medium text-slate-600">Datum</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
@forelse($resolved as $red)
<tr>
<td class="px-4 py-3 font-medium text-slate-700">{{ $red->user->name }}</td>
<td class="px-4 py-3">{{ $red->reward->icon }} {{ $red->reward->name }}</td>
<td class="px-4 py-3 text-center">
<span class="{{ $red->status==='approved' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700' }} px-2 py-0.5 rounded-full text-xs font-medium">
{{ $red->status==='approved' ? 'Freigegeben' : 'Abgelehnt' }}
</span>
</td>
<td class="px-4 py-3 text-right text-amber-600 font-medium">🪙 {{ $red->points_spent }}</td>
<td class="px-4 py-3 text-right text-slate-400 text-xs">{{ $red->created_at->format('d.m.Y') }}</td>
</tr>
@empty
<tr><td colspan="5" class="px-4 py-6 text-center text-slate-400">Noch keine Einlösungen.</td></tr>
@endforelse
</tbody>
</table>
</div>
@endsection
@@ -0,0 +1,38 @@
@extends('layouts.admin')
@section('title','Neue Belohnung')
@section('content')
<div class="max-w-lg">
<a href="{{ route('admin.rewards.index') }}" class="text-sm text-slate-500 hover:text-slate-700 mb-4 inline-block"> Zurück</a>
<div class="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<h2 class="font-semibold text-slate-800 mb-5">Neue Belohnung</h2>
<form method="POST" action="{{ route('admin.rewards.store') }}" class="space-y-4">
@csrf
<div class="grid grid-cols-4 gap-3">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Icon</label>
<input name="icon" value="{{ old('icon','🎁') }}" required maxlength="4" class="w-full border border-slate-300 rounded-lg px-3 py-2 text-center text-xl focus:ring-2 focus:ring-violet-500 outline-none">
</div>
<div class="col-span-3">
<label class="block text-sm font-medium text-slate-700 mb-1">Name</label>
<input name="name" value="{{ old('name') }}" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Beschreibung</label>
<input name="description" value="{{ old('description') }}" class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">🪙 Münzenpreis</label>
<input name="points_cost" type="number" min="1" value="{{ old('points_cost') }}" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Minuten <span class="text-slate-400">(optional)</span></label>
<input name="minutes" type="number" min="1" value="{{ old('minutes') }}" class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
</div>
<button type="submit" class="w-full bg-violet-600 hover:bg-violet-700 text-white py-2 rounded-lg font-medium text-sm">Belohnung erstellen</button>
</form>
</div>
</div>
@endsection
@@ -0,0 +1,42 @@
@extends('layouts.admin')
@section('title','Belohnung bearbeiten')
@section('content')
<div class="max-w-lg">
<a href="{{ route('admin.rewards.index') }}" class="text-sm text-slate-500 hover:text-slate-700 mb-4 inline-block"> Zurück</a>
<div class="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<h2 class="font-semibold text-slate-800 mb-5">Belohnung bearbeiten</h2>
<form method="POST" action="{{ route('admin.rewards.update',$reward) }}" class="space-y-4">
@csrf @method('PUT')
<div class="grid grid-cols-4 gap-3">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Icon</label>
<input name="icon" value="{{ old('icon',$reward->icon) }}" required maxlength="4" class="w-full border border-slate-300 rounded-lg px-3 py-2 text-center text-xl focus:ring-2 focus:ring-violet-500 outline-none">
</div>
<div class="col-span-3">
<label class="block text-sm font-medium text-slate-700 mb-1">Name</label>
<input name="name" value="{{ old('name',$reward->name) }}" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Beschreibung</label>
<input name="description" value="{{ old('description',$reward->description) }}" class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">🪙 Münzenpreis</label>
<input name="points_cost" type="number" min="1" value="{{ old('points_cost',$reward->points_cost) }}" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Minuten</label>
<input name="minutes" type="number" min="1" value="{{ old('minutes',$reward->minutes) }}" class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
</div>
<div class="flex items-center gap-3">
<input type="checkbox" name="active" id="active" value="1" {{ $reward->active?'checked':'' }} class="accent-violet-600">
<label for="active" class="text-sm font-medium text-slate-700">Belohnung aktiv</label>
</div>
<button type="submit" class="w-full bg-violet-600 hover:bg-violet-700 text-white py-2 rounded-lg font-medium text-sm">Speichern</button>
</form>
</div>
</div>
@endsection
@@ -0,0 +1,33 @@
@extends('layouts.admin')
@section('title','Belohnungen')
@section('content')
<div class="flex justify-between items-center mb-6">
<h2 class="text-lg font-semibold text-slate-700">Belohnungen</h2>
<a href="{{ route('admin.rewards.create') }}" class="bg-violet-600 hover:bg-violet-700 text-white px-4 py-2 rounded-lg text-sm font-medium">+ Neue Belohnung</a>
</div>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
@forelse($rewards as $r)
<div class="bg-white rounded-xl shadow-sm border border-slate-200 p-5">
<div class="flex items-start justify-between mb-3">
<span class="text-3xl">{{ $r->icon }}</span>
<span class="{{ $r->active ? 'bg-green-100 text-green-700' : 'bg-slate-100 text-slate-500' }} text-xs px-2 py-0.5 rounded-full font-medium">{{ $r->active ? 'Aktiv' : 'Inaktiv' }}</span>
</div>
<div class="font-semibold text-slate-800">{{ $r->name }}</div>
@if($r->description)<div class="text-xs text-slate-500 mt-0.5">{{ $r->description }}</div>@endif
<div class="mt-3 flex items-center justify-between">
<span class="font-bold text-amber-600">🪙 {{ $r->points_cost }} Münzen</span>
@if($r->minutes)<span class="text-xs text-slate-500">{{ $r->minutes }} Min.</span>@endif
</div>
<div class="mt-4 flex gap-2">
<a href="{{ route('admin.rewards.edit',$r) }}" class="flex-1 text-center bg-slate-100 hover:bg-slate-200 text-slate-700 py-1.5 rounded-lg text-xs font-medium">Bearbeiten</a>
<form method="POST" action="{{ route('admin.rewards.destroy',$r) }}" onsubmit="return confirm('Löschen?')">
@csrf @method('DELETE')
<button class="bg-red-50 hover:bg-red-100 text-red-600 py-1.5 px-3 rounded-lg text-xs font-medium">Löschen</button>
</form>
</div>
</div>
@empty
<p class="text-slate-400 col-span-3">Noch keine Belohnungen.</p>
@endforelse
</div>
@endsection
@@ -0,0 +1,29 @@
@extends('layouts.admin')
@section('title','Neues Kind')
@section('content')
<div class="max-w-lg">
<a href="{{ route('admin.users.index') }}" class="text-sm text-slate-500 hover:text-slate-700 mb-4 inline-block"> Zurück</a>
<div class="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<h2 class="font-semibold text-slate-800 mb-5">Neues Kind-Konto</h2>
<form method="POST" action="{{ route('admin.users.store') }}" class="space-y-4">
@csrf
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Name</label>
<input name="name" value="{{ old('name') }}" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 focus:border-transparent outline-none">
@error('name')<p class="text-red-500 text-xs mt-1">{{ $message }}</p>@enderror
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">E-Mail</label>
<input name="email" type="email" value="{{ old('email') }}" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 focus:border-transparent outline-none">
@error('email')<p class="text-red-500 text-xs mt-1">{{ $message }}</p>@enderror
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Passwort</label>
<input name="password" type="password" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 focus:border-transparent outline-none">
@error('password')<p class="text-red-500 text-xs mt-1">{{ $message }}</p>@enderror
</div>
<button type="submit" class="w-full bg-violet-600 hover:bg-violet-700 text-white py-2 rounded-lg font-medium text-sm">Konto erstellen</button>
</form>
</div>
</div>
@endsection
@@ -0,0 +1,30 @@
@extends('layouts.admin')
@section('title','Kind bearbeiten')
@section('content')
<div class="max-w-lg">
<a href="{{ route('admin.users.index') }}" class="text-sm text-slate-500 hover:text-slate-700 mb-4 inline-block"> Zurück</a>
<div class="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<h2 class="font-semibold text-slate-800 mb-5">{{ $user->name }} bearbeiten</h2>
<form method="POST" action="{{ route('admin.users.update',$user) }}" class="space-y-4">
@csrf @method('PUT')
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Name</label>
<input name="name" value="{{ old('name',$user->name) }}" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">E-Mail</label>
<input name="email" type="email" value="{{ old('email',$user->email) }}" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Neues Passwort <span class="text-slate-400">(leer = unverändert)</span></label>
<input name="password" type="password" class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">🪙 Münzen</label>
<input name="points" type="number" min="0" value="{{ old('points',$user->points) }}" required class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-violet-500 outline-none">
</div>
<button type="submit" class="w-full bg-violet-600 hover:bg-violet-700 text-white py-2 rounded-lg font-medium text-sm">Speichern</button>
</form>
</div>
</div>
@endsection
@@ -0,0 +1,40 @@
@extends('layouts.admin')
@section('title','Kinder')
@section('content')
<div class="flex justify-between items-center mb-6">
<h2 class="text-lg font-semibold text-slate-700">Alle Kinder-Konten</h2>
<a href="{{ route('admin.users.create') }}" class="bg-violet-600 hover:bg-violet-700 text-white px-4 py-2 rounded-lg text-sm font-medium">+ Neues Kind</a>
</div>
<div class="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-slate-50 border-b border-slate-200">
<tr>
<th class="text-left px-4 py-3 font-medium text-slate-600">Name</th>
<th class="text-left px-4 py-3 font-medium text-slate-600">E-Mail</th>
<th class="text-right px-4 py-3 font-medium text-slate-600">🪙 Münzen</th>
<th class="text-right px-4 py-3 font-medium text-slate-600">Antworten</th>
<th class="px-4 py-3"></th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
@forelse($users as $u)
<tr class="hover:bg-slate-50">
<td class="px-4 py-3 font-medium text-slate-800">{{ $u->name }}</td>
<td class="px-4 py-3 text-slate-500">{{ $u->email }}</td>
<td class="px-4 py-3 text-right font-bold text-amber-600">{{ $u->points }}</td>
<td class="px-4 py-3 text-right text-slate-500">{{ $u->attempts_count }}</td>
<td class="px-4 py-3 text-right">
<a href="{{ route('admin.users.edit',$u) }}" class="text-violet-600 hover:underline mr-3">Bearbeiten</a>
<form method="POST" action="{{ route('admin.users.destroy',$u) }}" class="inline" onsubmit="return confirm('Wirklich löschen?')">
@csrf @method('DELETE')
<button class="text-red-500 hover:underline">Löschen</button>
</form>
</td>
</tr>
@empty
<tr><td colspan="5" class="px-4 py-8 text-center text-slate-400">Noch keine Kinder angelegt.</td></tr>
@endforelse
</tbody>
</table>
</div>
@endsection
+72
View File
@@ -0,0 +1,72 @@
@extends('layouts.child')
@section('content')
@php $level = auth()->user()->level() @endphp
<div class="text-center mb-8">
<div class="text-5xl mb-2">{{ $level['icon'] }}</div>
<h1 class="text-2xl font-bold text-indigo-700">Hallo, {{ auth()->user()->name }}! 👋</h1>
<p class="text-slate-500 mt-1">Du bist: <span class="{{ $level['color'] }} font-bold">{{ $level['label'] }}</span></p>
@if($streak >= 3)
<div class="mt-3 inline-flex items-center gap-2 bg-orange-100 text-orange-700 rounded-full px-4 py-1.5 text-sm font-bold">
🔥 {{ $streak }}er-Serie! +5 Bonusmünzen pro Antwort
</div>
@elseif($streak > 0)
<div class="mt-3 inline-flex items-center gap-2 bg-sky-100 text-sky-700 rounded-full px-4 py-1.5 text-sm font-medium">
{{ $streak }} richtige Antworten in Folge!
</div>
@endif
</div>
<div class="grid grid-cols-2 gap-4 mb-8">
<div class="bg-white rounded-2xl shadow-sm border border-amber-200 p-5 text-center">
<div class="text-4xl font-black text-amber-500">{{ auth()->user()->points }}</div>
<div class="text-sm text-slate-500 mt-1 font-medium">🪙 Münzen</div>
@if($level['next'])
<div class="mt-3">
<div class="text-xs text-slate-400 mb-1">Nächstes Level in {{ $level['next'] - auth()->user()->points }} Münzen</div>
<div class="bg-slate-200 rounded-full h-2"><div class="bg-amber-400 h-2 rounded-full transition-all" style="width:{{ $level['progress'] }}%"></div></div>
</div>
@endif
</div>
@if($pendingRedemptions > 0)
<a href="{{ route('rewards.index') }}" class="bg-green-50 border border-green-300 rounded-2xl p-5 text-center hover:bg-green-100 transition">
<div class="text-4xl font-black text-green-600">{{ $pendingRedemptions }}</div>
<div class="text-sm text-slate-500 mt-1 font-medium">🎁 Einlösung wartet</div>
</a>
@else
<a href="{{ route('rewards.index') }}" class="bg-white rounded-2xl shadow-sm border border-slate-200 p-5 text-center hover:border-indigo-300 transition">
<div class="text-4xl">🎁</div>
<div class="text-sm text-slate-500 mt-1 font-medium">Belohnungen</div>
</a>
@endif
</div>
<h2 class="font-bold text-slate-700 text-lg mb-4">📚 Fächer</h2>
<div class="grid grid-cols-3 gap-4 mb-8">
@foreach($subjects as $s)
@php $colors = ['green'=>'from-green-400 to-emerald-500','blue'=>'from-blue-400 to-indigo-500','orange'=>'from-orange-400 to-amber-500'] @endphp
<a href="{{ route('learn.quiz', $s->slug) }}" class="bg-gradient-to-br {{ $colors[$s->color] ?? 'from-violet-400 to-purple-500' }} rounded-2xl p-5 text-white text-center hover:scale-105 transition-transform shadow-sm">
<div class="text-3xl mb-2">{{ $s->icon }}</div>
<div class="font-bold text-sm">{{ $s->name }}</div>
<div class="text-white/80 text-xs mt-1">{{ $s->correct }} </div>
</a>
@endforeach
</div>
@if($recentAttempts->count())
<h2 class="font-bold text-slate-700 text-lg mb-4">⏱️ Zuletzt gespielt</h2>
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden">
@foreach($recentAttempts as $a)
<div class="flex items-center justify-between px-5 py-3 {{ !$loop->last ? 'border-b border-slate-100' : '' }}">
<div class="flex items-center gap-3">
<span class="text-lg">{{ $a->is_correct ? '✅' : '❌' }}</span>
<div>
<div class="text-sm font-medium text-slate-700 line-clamp-1">{{ Str::limit($a->question->question_text, 40) }}</div>
<div class="text-xs text-slate-400">{{ $a->question->subject->name }}</div>
</div>
</div>
@if($a->points_earned > 0)<span class="text-xs font-bold text-amber-500">+{{ $a->points_earned }} 🪙</span>@endif
</div>
@endforeach
</div>
@endif
@endsection
@@ -0,0 +1,34 @@
@extends('layouts.child')
@section('content')
<div class="mb-4 flex items-center gap-3">
<a href="{{ route('learn.subjects') }}" class="text-slate-400 hover:text-slate-600 text-sm"> Fächer</a>
<span class="text-lg">{{ $subject->icon }}</span>
<span class="font-bold text-slate-700">{{ $subject->name }}</span>
<span class="ml-auto text-sm text-slate-400">{{ $question->difficultyStars() }}</span>
</div>
<div class="bg-white rounded-2xl shadow-md border border-slate-200 p-6 mb-6">
<div class="text-xs text-slate-400 mb-3 font-medium">
{{ ['','⭐ Leicht — 5 Münzen','⭐⭐ Mittel — 10 Münzen','⭐⭐⭐ Schwer — 20 Münzen'][$question->difficulty] }}
</div>
<p class="text-xl font-bold text-slate-800 leading-snug">{{ $question->question_text }}</p>
</div>
<form method="POST" action="{{ route('learn.answer', $subject->slug) }}">
@csrf
<input type="hidden" name="question_id" value="{{ $question->id }}">
<div class="grid gap-3">
@foreach($question->answerOptions as $opt)
<label class="cursor-pointer">
<input type="radio" name="answer" value="{{ $opt->id }}" class="sr-only peer" required>
<div class="bg-white border-2 border-slate-200 rounded-2xl px-5 py-4 text-base font-medium text-slate-700
peer-checked:border-indigo-500 peer-checked:bg-indigo-50 peer-checked:text-indigo-700
hover:border-indigo-300 hover:bg-slate-50 transition-all">
{{ $opt->text }}
</div>
</label>
@endforeach
</div>
<button type="submit" class="mt-6 w-full bg-indigo-600 hover:bg-indigo-700 text-white text-lg font-bold py-4 rounded-2xl shadow-md transition-colors">
Antworten
</button>
</form>
@endsection
@@ -0,0 +1,41 @@
@extends('layouts.child')
@section('content')
<div class="text-center">
@if($isRight)
<div class="text-7xl mb-4 animate-bounce">🎉</div>
<h1 class="text-3xl font-black text-green-600 mb-2">Richtig!</h1>
<p class="text-slate-600 mb-4">Super gemacht!</p>
<div class="inline-flex items-center gap-2 bg-amber-100 text-amber-700 font-black text-2xl rounded-2xl px-6 py-3 mb-4">
+{{ $earned }} 🪙
</div>
@if($earned > $question->points_value)
<p class="text-orange-600 font-bold text-sm mb-4">🔥 Serien-Bonus inklusive!</p>
@endif
@if($newStreak >= 3)
<div class="bg-orange-50 border border-orange-200 rounded-xl px-4 py-2 text-orange-700 font-bold text-sm mb-4 inline-block">
🔥 {{ $newStreak }}er-Serie! Weiter so!
</div>
@endif
@else
<div class="text-7xl mb-4">😅</div>
<h1 class="text-3xl font-black text-red-500 mb-2">Leider falsch</h1>
<p class="text-slate-600 mb-4">Die richtige Antwort war:</p>
<div class="bg-green-100 border border-green-300 text-green-800 font-bold text-lg rounded-2xl px-6 py-3 mb-4 inline-block">
{{ $correct->text }}
</div>
@endif
<div class="mt-6 flex gap-3 justify-center">
<a href="{{ route('learn.quiz', $subject->slug) }}" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold px-8 py-3 rounded-2xl text-base shadow-md transition-colors">
Nächste Frage
</a>
<a href="{{ route('learn.subjects') }}" class="bg-slate-200 hover:bg-slate-300 text-slate-700 font-bold px-6 py-3 rounded-2xl text-base transition-colors">
Fächer
</a>
</div>
<div class="mt-6 text-slate-500 text-sm">
Dein Kontostand: <span class="font-bold text-amber-600">🪙 {{ auth()->user()->fresh()->points }}</span>
</div>
</div>
@endsection
@@ -0,0 +1,19 @@
@extends('layouts.child')
@section('content')
<h1 class="text-2xl font-bold text-indigo-700 mb-6 text-center">📚 Welches Fach?</h1>
<div class="grid gap-5">
@foreach($subjects as $s)
@php $colors = ['green'=>'from-green-400 to-emerald-500','blue'=>'from-blue-400 to-indigo-500','orange'=>'from-orange-400 to-amber-500'] @endphp
<a href="{{ route('learn.quiz', $s->slug) }}" class="bg-gradient-to-r {{ $colors[$s->color] ?? 'from-violet-400 to-purple-500' }} rounded-2xl p-6 text-white flex items-center justify-between hover:scale-[1.02] transition-transform shadow-md">
<div class="flex items-center gap-5">
<span class="text-5xl">{{ $s->icon }}</span>
<div>
<div class="text-2xl font-black">{{ $s->name }}</div>
<div class="text-white/80 text-sm mt-0.5">{{ $s->total }} Fragen · {{ $s->correct }} richtig beantwortet</div>
</div>
</div>
<div class="text-4xl"></div>
</a>
@endforeach
</div>
@endsection
@@ -0,0 +1,60 @@
@extends('layouts.child')
@section('content')
<div class="flex items-center justify-between mb-6">
<h1 class="text-2xl font-bold text-indigo-700">🎁 Belohnungen</h1>
<div class="bg-amber-100 text-amber-700 font-black rounded-full px-4 py-2">🪙 {{ auth()->user()->points }}</div>
</div>
<div class="grid gap-4 mb-8">
@foreach($rewards as $r)
@php $canAfford = auth()->user()->points >= $r->points_cost @endphp
<div class="bg-white rounded-2xl shadow-sm border {{ $canAfford ? 'border-slate-200 hover:border-indigo-300' : 'border-slate-100 opacity-60' }} p-5 transition-all">
<div class="flex items-center justify-between gap-4">
<div class="flex items-center gap-4">
<span class="text-4xl">{{ $r->icon }}</span>
<div>
<div class="font-bold text-slate-800 text-base">{{ $r->name }}</div>
@if($r->description)<div class="text-sm text-slate-500">{{ $r->description }}</div>@endif
@if($r->minutes)<div class="text-xs text-slate-400 mt-0.5"> {{ $r->minutes }} Minuten</div>@endif
</div>
</div>
<div class="text-right shrink-0">
<div class="font-black text-amber-600 text-lg">🪙 {{ $r->points_cost }}</div>
@if($canAfford)
<form method="POST" action="{{ route('rewards.redeem', $r) }}" onsubmit="return confirm('Möchtest du {{ $r->name }} für {{ $r->points_cost }} Münzen einlösen?')">
@csrf
<button class="mt-2 bg-indigo-600 hover:bg-indigo-700 text-white font-bold px-4 py-2 rounded-xl text-sm transition-colors">
Einlösen
</button>
</form>
@else
<div class="mt-2 text-xs text-slate-400 font-medium">Noch {{ $r->points_cost - auth()->user()->points }} fehlen</div>
@endif
</div>
</div>
</div>
@endforeach
</div>
@if($history->count())
<h2 class="font-bold text-slate-700 text-lg mb-4">📋 Meine Einlösungen</h2>
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden">
@foreach($history as $red)
<div class="flex items-center justify-between px-5 py-3 {{ !$loop->last ? 'border-b border-slate-100' : '' }}">
<div class="flex items-center gap-3">
<span class="text-xl">{{ $red->reward->icon }}</span>
<div>
<div class="text-sm font-medium text-slate-700">{{ $red->reward->name }}</div>
<div class="text-xs text-slate-400">{{ $red->created_at->format('d.m.Y') }}</div>
</div>
</div>
<span class="text-xs font-bold px-2.5 py-1 rounded-full
{{ $red->status==='approved' ? 'bg-green-100 text-green-700' :
($red->status==='rejected' ? 'bg-red-100 text-red-600' : 'bg-amber-100 text-amber-700') }}">
{{ $red->status==='approved' ? '✅ Freigegeben' : ($red->status==='rejected' ? '❌ Abgelehnt' : '⏳ Warten...') }}
</span>
</div>
@endforeach
</div>
@endif
@endsection
+58
View File
@@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="de" class="h-full">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Lernapp Admin</title>
@vite(['resources/css/app.css','resources/js/app.js'])
</head>
<body class="h-full bg-slate-100 font-sans" x-data="{open:false}">
<div class="flex h-full">
<!-- Sidebar -->
<aside class="hidden md:flex flex-col w-60 bg-slate-900 text-slate-100 shrink-0">
<div class="flex items-center gap-3 px-6 py-5 border-b border-slate-700">
<span class="text-2xl">🎓</span>
<div><div class="font-bold text-white text-sm">Lernapp</div><div class="text-xs text-slate-400">Admin-Bereich</div></div>
</div>
<nav class="flex-1 px-3 py-4 space-y-1 text-sm">
<a href="{{ route('admin.dashboard') }}" class="flex items-center gap-3 px-3 py-2 rounded-lg {{ request()->routeIs('admin.dashboard') ? 'bg-violet-600 text-white' : 'text-slate-300 hover:bg-slate-800' }}">
<span>📊</span> Dashboard
</a>
<a href="{{ route('admin.users.index') }}" class="flex items-center gap-3 px-3 py-2 rounded-lg {{ request()->routeIs('admin.users.*') ? 'bg-violet-600 text-white' : 'text-slate-300 hover:bg-slate-800' }}">
<span>👦</span> Kinder
</a>
<a href="{{ route('admin.questions.index') }}" class="flex items-center gap-3 px-3 py-2 rounded-lg {{ request()->routeIs('admin.questions.*') ? 'bg-violet-600 text-white' : 'text-slate-300 hover:bg-slate-800' }}">
<span></span> Fragen
</a>
<a href="{{ route('admin.rewards.index') }}" class="flex items-center gap-3 px-3 py-2 rounded-lg {{ request()->routeIs('admin.rewards.*') ? 'bg-violet-600 text-white' : 'text-slate-300 hover:bg-slate-800' }}">
<span>🎁</span> Belohnungen
</a>
<a href="{{ route('admin.redemptions.index') }}" class="flex items-center gap-3 px-3 py-2 rounded-lg {{ request()->routeIs('admin.redemptions.*') ? 'bg-violet-600 text-white' : 'text-slate-300 hover:bg-slate-800' }}">
<span></span> Einlösungen
@php $p = \App\Models\RewardRedemption::where('status','pending')->count() @endphp
@if($p > 0)<span class="ml-auto bg-red-500 text-white text-xs rounded-full px-1.5">{{ $p }}</span>@endif
</a>
</nav>
<div class="px-3 py-4 border-t border-slate-700">
<form method="POST" action="{{ route('logout') }}">
@csrf
<button class="flex items-center gap-3 px-3 py-2 w-full text-sm text-slate-400 hover:text-white rounded-lg hover:bg-slate-800">
<span>🚪</span> Abmelden
</button>
</form>
</div>
</aside>
<!-- Main -->
<div class="flex-1 flex flex-col overflow-hidden">
<header class="bg-white border-b border-slate-200 px-6 py-4 flex items-center justify-between">
<h1 class="font-semibold text-slate-800">@yield('title','Dashboard')</h1>
<span class="text-sm text-slate-500">{{ auth()->user()->name }}</span>
</header>
<main class="flex-1 overflow-y-auto p-6">
@if(session('success'))<div class="mb-4 bg-green-100 border border-green-300 text-green-800 rounded-lg px-4 py-3 text-sm">{{ session('success') }}</div>@endif
@if(session('error'))<div class="mb-4 bg-red-100 border border-red-300 text-red-800 rounded-lg px-4 py-3 text-sm">{{ session('error') }}</div>@endif
@yield('content')
</main>
</div>
</div>
</body>
</html>
+33
View File
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Lernapp</title>
@vite(['resources/css/app.css','resources/js/app.js'])
</head>
<body class="min-h-screen bg-gradient-to-br from-sky-100 to-indigo-100 font-sans">
<nav class="bg-white shadow-sm sticky top-0 z-10">
<div class="max-w-3xl mx-auto px-4 py-3 flex items-center justify-between">
<a href="{{ route('dashboard') }}" class="flex items-center gap-2 font-bold text-indigo-600 text-lg">
🎓 Lernapp
</a>
<div class="flex items-center gap-4">
<a href="{{ route('learn.subjects') }}" class="text-sm font-medium text-slate-600 hover:text-indigo-600">Lernen</a>
<a href="{{ route('rewards.index') }}" class="text-sm font-medium text-slate-600 hover:text-indigo-600">🪙 Belohnungen</a>
<div class="flex items-center gap-1 bg-amber-100 text-amber-700 font-bold rounded-full px-3 py-1 text-sm">
🪙 {{ auth()->user()->points }}
</div>
<form method="POST" action="{{ route('logout') }}">@csrf
<button class="text-xs text-slate-400 hover:text-slate-600">Abmelden</button>
</form>
</div>
</div>
</nav>
<main class="max-w-3xl mx-auto px-4 py-8">
@if(session('success'))<div class="mb-4 bg-green-100 border border-green-300 text-green-800 rounded-xl px-4 py-3 text-sm font-medium"> {{ session('success') }}</div>@endif
@if(session('error'))<div class="mb-4 bg-red-100 border border-red-300 text-red-800 rounded-xl px-4 py-3 text-sm font-medium"> {{ session('error') }}</div>@endif
@if(session('info'))<div class="mb-4 bg-blue-100 border border-blue-300 text-blue-800 rounded-xl px-4 py-3 text-sm font-medium"> {{ session('info') }}</div>@endif
@yield('content')
</main>
</body>
</html>