150 lines
4.3 KiB
PHP
150 lines
4.3 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Tasks;
|
|
|
|
use Illuminate\Contracts\View\View;
|
|
use Livewire\Attributes\Lazy;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Attributes\Url;
|
|
use Livewire\Component;
|
|
|
|
#[Layout('layouts.dashboard', ['active' => 'tasks'])]
|
|
#[Lazy]
|
|
class Run extends Component
|
|
{
|
|
public string $slug = '';
|
|
public array $task = [];
|
|
public array $questions = [];
|
|
public int $currentStep = 1;
|
|
public int $totalSteps = 0;
|
|
public bool $finished = false;
|
|
public int $correctAnswers = 0;
|
|
|
|
/** Last answer payload — varies by question type:
|
|
* - choice : ['type'=>'choice', 'index'=>int]
|
|
* - cloze : ['type'=>'cloze', 'text'=>string]
|
|
* - geo-map : ['type'=>'geo-map','region'=>string]
|
|
*/
|
|
public ?array $lastAnswer = null;
|
|
public ?bool $lastAnswerCorrect = null;
|
|
|
|
/** Active typed input for cloze questions (wire:model). */
|
|
public string $clozeInput = '';
|
|
|
|
/** Lerni AI helper panel — toggled via showLerni; can be shared via URL (?lerni=1). */
|
|
#[Url(as: 'lerni', keep: false)]
|
|
public bool $showLerni = false;
|
|
|
|
public function mount(string $slug): void
|
|
{
|
|
$task = $this->findTask($slug);
|
|
abort_if($task === null, 404, "Task '{$slug}' not found.");
|
|
|
|
$this->slug = $slug;
|
|
$this->task = $task;
|
|
$this->questions = config("task-questions.{$slug}", []);
|
|
$this->totalSteps = count($this->questions);
|
|
|
|
abort_if($this->totalSteps === 0, 404, "No questions configured for task '{$slug}'.");
|
|
}
|
|
|
|
public static function placeholder(array $params = []): View
|
|
{
|
|
return view('livewire.tasks.run-placeholder');
|
|
}
|
|
|
|
public function currentQuestion(): array
|
|
{
|
|
$q = $this->questions[$this->currentStep - 1] ?? $this->questions[0];
|
|
// Default type for legacy entries
|
|
$q['type'] = $q['type'] ?? 'choice';
|
|
return $q;
|
|
}
|
|
|
|
public function submitChoice(int $optionIndex): void
|
|
{
|
|
$q = $this->currentQuestion();
|
|
if (($q['type'] ?? 'choice') !== 'choice') {
|
|
return;
|
|
}
|
|
$isCorrect = $optionIndex === ($q['correct'] ?? -1);
|
|
$this->lastAnswer = ['type' => 'choice', 'index' => $optionIndex];
|
|
$this->lastAnswerCorrect = $isCorrect;
|
|
if ($isCorrect) {
|
|
$this->correctAnswers++;
|
|
}
|
|
}
|
|
|
|
public function submitCloze(): void
|
|
{
|
|
$q = $this->currentQuestion();
|
|
if (($q['type'] ?? null) !== 'cloze') {
|
|
return;
|
|
}
|
|
$given = trim(mb_strtolower($this->clozeInput));
|
|
$expected = trim(mb_strtolower((string) ($q['correct'] ?? '')));
|
|
$accept = array_map(fn($a) => trim(mb_strtolower((string) $a)), $q['accept'] ?? []);
|
|
|
|
$isCorrect = $given !== '' && ($given === $expected || in_array($given, $accept, true));
|
|
|
|
$this->lastAnswer = ['type' => 'cloze', 'text' => $this->clozeInput];
|
|
$this->lastAnswerCorrect = $isCorrect;
|
|
if ($isCorrect) {
|
|
$this->correctAnswers++;
|
|
}
|
|
}
|
|
|
|
public function submitRegion(string $region): void
|
|
{
|
|
$q = $this->currentQuestion();
|
|
if (($q['type'] ?? null) !== 'geo-map') {
|
|
return;
|
|
}
|
|
$isCorrect = $region === ($q['correct'] ?? '');
|
|
$this->lastAnswer = ['type' => 'geo-map', 'region' => $region];
|
|
$this->lastAnswerCorrect = $isCorrect;
|
|
if ($isCorrect) {
|
|
$this->correctAnswers++;
|
|
}
|
|
}
|
|
|
|
public function nextStep(): void
|
|
{
|
|
if ($this->currentStep >= $this->totalSteps) {
|
|
$this->finished = true;
|
|
return;
|
|
}
|
|
$this->currentStep++;
|
|
$this->lastAnswer = null;
|
|
$this->lastAnswerCorrect = null;
|
|
$this->clozeInput = '';
|
|
$this->showLerni = false;
|
|
}
|
|
|
|
public function toggleLerni(): void
|
|
{
|
|
$this->showLerni = ! $this->showLerni;
|
|
}
|
|
|
|
public function restart(): void
|
|
{
|
|
$this->currentStep = 1;
|
|
$this->finished = false;
|
|
$this->correctAnswers = 0;
|
|
$this->lastAnswer = null;
|
|
$this->lastAnswerCorrect = null;
|
|
$this->clozeInput = '';
|
|
$this->showLerni = false;
|
|
}
|
|
|
|
protected function findTask(string $slug): ?array
|
|
{
|
|
return collect(config('tasks', []))->firstWhere('slug', $slug);
|
|
}
|
|
|
|
public function render(): View
|
|
{
|
|
return view('livewire.tasks.run');
|
|
}
|
|
}
|