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
@@ -0,0 +1,22 @@
<?php
namespace App\Http\Controllers\Child;
use App\Http\Controllers\Controller;
use App\Models\Subject;
use App\Models\QuestionAttempt;
class DashboardController extends Controller {
public function index() {
$user = auth()->user();
$level = $user->level();
$streak = $user->currentStreak();
$subjects = Subject::withCount(['questions as total' => fn($q) => $q->where('active',true)])->get()
->map(function($s) use ($user) {
$s->correct = QuestionAttempt::whereHas('question', fn($q) => $q->where('subject_id',$s->id))
->where('user_id',$user->id)->where('is_correct',true)->count();
return $s;
});
$recentAttempts = QuestionAttempt::with('question.subject')
->where('user_id',$user->id)->latest('id')->take(5)->get();
$pendingRedemptions = $user->redemptions()->where('status','pending')->count();
return view('child.dashboard', compact('user','level','streak','subjects','recentAttempts','pendingRedemptions'));
}
}
@@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers\Child;
use App\Http\Controllers\Controller;
use App\Models\Subject;
use App\Models\Question;
use App\Models\QuestionAttempt;
use Illuminate\Http\Request;
class LearnController extends Controller {
public function subjects() {
$user = auth()->user();
$subjects = Subject::all()->map(function($s) use ($user) {
$s->correct = QuestionAttempt::whereHas('question', fn($q) => $q->where('subject_id',$s->id))
->where('user_id',$user->id)->where('is_correct',true)->count();
$s->total = $s->questions()->where('active',true)->count();
return $s;
});
return view('child.learn.subjects', compact('subjects'));
}
public function quiz(Subject $subject) {
$user = auth()->user();
// Bevorzuge Fragen die heute noch nicht beantwortet wurden
$answeredToday = QuestionAttempt::where('user_id',$user->id)
->whereDate('created_at', today())
->pluck('question_id');
$question = $subject->activeQuestions()
->whereNotIn('id', $answeredToday)
->with('answerOptions')
->inRandomOrder()
->first();
// Falls alle beantwortet: ganz random
if (!$question) {
$question = $subject->activeQuestions()->with('answerOptions')->inRandomOrder()->first();
}
if (!$question) {
return redirect()->route('learn.subjects')->with('info','Noch keine Fragen für dieses Fach.');
}
// Optionen mischen
$question->answerOptions = $question->answerOptions->shuffle();
return view('child.learn.quiz', compact('subject','question'));
}
public function answer(Request $r, Subject $subject) {
$r->validate(['question_id'=>'required|exists:questions,id','answer'=>'required']);
$user = auth()->user();
$question = Question::with('answerOptions')->findOrFail($r->question_id);
$correct = $question->answerOptions->where('is_correct',true)->first();
$isRight = (string)$r->answer === (string)$correct->id;
$earned = 0;
if ($isRight) {
$earned = $question->points_value;
// Streak-Bonus: 3+ in Folge = +5
$streak = $user->currentStreak();
if ($streak >= 2) $earned += 5;
$user->increment('points', $earned);
}
QuestionAttempt::create([
'user_id' => $user->id,
'question_id' => $question->id,
'is_correct' => $isRight,
'points_earned'=> $earned,
]);
$newStreak = $user->fresh()->currentStreak();
return view('child.learn.result', compact('subject','question','correct','isRight','earned','newStreak'));
}
}
@@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers\Child;
use App\Http\Controllers\Controller;
use App\Models\Reward;
use App\Models\RewardRedemption;
use Illuminate\Http\Request;
class RewardController extends Controller {
public function index() {
$user = auth()->user();
$rewards = Reward::where('active',true)->orderBy('points_cost')->get();
$history = RewardRedemption::with('reward')->where('user_id',$user->id)->latest()->take(20)->get();
return view('child.rewards.index', compact('user','rewards','history'));
}
public function redeem(Request $r, Reward $reward) {
$user = auth()->user();
if (!$reward->active) abort(422);
if ($user->points < $reward->points_cost) {
return back()->with('error','Nicht genug Münzen!');
}
$user->decrement('points', $reward->points_cost);
RewardRedemption::create([
'user_id' => $user->id,
'reward_id' => $reward->id,
'status' => 'pending',
'points_spent'=> $reward->points_cost,
]);
return back()->with('success','Eingelöst! Warte auf Freigabe.');
}
}