Bonus Score Entry

#20 Implement bonus scores
Bonus score entry by judges is complete.
This commit is contained in:
Matt Young 2024-07-15 23:32:32 -05:00
parent 7e908024d7
commit 579a0a2fc3
7 changed files with 159 additions and 4 deletions

View File

@ -0,0 +1,31 @@
<?php
namespace App\Http\Controllers\Judging;
use App\Http\Controllers\Controller;
use App\Models\BonusScore;
use App\Models\BonusScoreDefinition;
use App\Models\Entry;
use Illuminate\Support\Facades\Auth;
use function redirect;
class BonusScoreEntryController extends Controller
{
public function __invoke(Entry $entry)
{
if (BonusScore::where('entry_id', $entry->id)->where('user_id', Auth::user()->id)->exists()) {
return redirect()->route('judging.bonusScore.EntryList', $entry->audition)->with('error', 'You have already judged that entry');
}
/** @var BonusScoreDefinition $bonusScore */
$bonusScore = $entry->audition->bonusScore()->first();
if (! $bonusScore->judges->contains(auth()->id())) {
return redirect()->route('judging.index')->with('error', 'You are not assigned to judge this entry');
}
$maxScore = $bonusScore->max_score;
$bonusName = $bonusScore->name;
return view('judging.bonus_entry_score_sheet', compact('entry', 'maxScore', 'bonusName'));
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers\Judging;
use App\Actions\Tabulation\EnterBonusScore;
use App\Exceptions\ScoreEntryException;
use App\Http\Controllers\Controller;
use App\Models\Entry;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
class BonusScoreRecordController extends Controller
{
public function __invoke(Entry $entry)
{
$enterBonusScore = App::make(EnterBonusScore::class);
$validData = request()->validate([
'score' => 'required|integer',
]);
try {
$enterBonusScore(Auth::user(), $entry, $validData['score']);
} catch (ScoreEntryException $ex) {
return redirect()->back()->with('error', 'Score Entry Error - '.$ex->getMessage());
}
return redirect()->route('judging.bonusScore.EntryList', $entry->audition)->with('Score Recorded Successfully');
}
}

View File

@ -3,13 +3,14 @@
'type' => 'text', 'type' => 'text',
'label' => false, 'label' => false,
'colspan' => '1', 'colspan' => '1',
'label_text' => false 'label_text' => false,
'id'=>null
]) ])
@php @php
$label_classes = "block text-sm font-medium leading-6 text-gray-900"; $label_classes = "block text-sm font-medium leading-6 text-gray-900";
$inputClasses = "block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"; $inputClasses = "block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6";
$inputAttributes = [ $inputAttributes = [
'id' => $name, 'id' => $id ?? $name,
'name' => $name, 'name' => $name,
'type' => $type, 'type' => $type,
'class' => $inputClasses, 'class' => $inputClasses,

View File

@ -0,0 +1,16 @@
<x-layout.app>
<x-slot:page_title>Enter {{ $bonusName }} Score</x-slot:page_title>
<x-card.card class="mx-auto max-w-sm">
<x-card.heading>{{ $entry->audition->name }} {{$entry->draw_number}}</x-card.heading>
<x-form.form method="POST"
id="score-entry-for-{{$entry->id}}"
action="{{route('judging.bonusScores.recordScore', $entry)}}">
<x-form.field name="score"
class="mb-5"
label_text="{{ $bonusName }} Score"
type="number"
max="{{ $maxScore }}"/>
<x-form.button type="submit" class="mb-5">Enter {{ $bonusName }} Score</x-form.button>
</x-form.form>
</x-card.card>
</x-layout.app>

View File

@ -15,11 +15,18 @@
<x-table.body> <x-table.body>
@foreach($entries as $entry) @foreach($entries as $entry)
<tr> <tr>
<x-table.td>{{ $audition->name }} {{ $entry->draw_number }}</x-table.td>
@if($scores->has($entry->id)) @if($scores->has($entry->id))
<x-table.td>{{ $audition->name }} {{ $entry->draw_number }}</x-table.td>
<x-table.td>{{ $scores[$entry->id]->score }}</x-table.td> <x-table.td>{{ $scores[$entry->id]->score }}</x-table.td>
<x-table.td>{{ $scores[$entry->id]->originallyScoredEntry->audition->name }}</x-table.td> <x-table.td>{{ $scores[$entry->id]->originallyScoredEntry->audition->name }}</x-table.td>
<x-table.td>{{ Carbon::create($scores[$entry->id]->created_at)->setTimezone('America/Chicago')->format('m/d/y H:i') }}</x-table.td> <x-table.td>{{ Carbon::create($scores[$entry->id]->created_at)->setTimezone('America/Chicago')->format('m/d/y H:i') }}</x-table.td>
@else
<x-table.td>
<a href="{{ route('judging.bonusScore.entry', $entry) }}">
{{ $audition->name }} {{ $entry->draw_number }}
</a>
</x-table.td>
@endif @endif
</tr> </tr>

View File

@ -1,7 +1,9 @@
<?php <?php
// Judging Routes // Judging Routes
use App\Http\Controllers\Judging\BonusScoreEntryController;
use App\Http\Controllers\Judging\BonusScoreEntryListController; use App\Http\Controllers\Judging\BonusScoreEntryListController;
use App\Http\Controllers\Judging\BonusScoreRecordController;
use App\Http\Controllers\Judging\JudgingController; use App\Http\Controllers\Judging\JudgingController;
use App\Http\Middleware\CheckIfCanJudge; use App\Http\Middleware\CheckIfCanJudge;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
@ -16,5 +18,7 @@ Route::middleware(['auth', 'verified', CheckIfCanJudge::class])->prefix('judging
// Bonus score judging routes // Bonus score judging routes
Route::middleware(['auth', 'verified', CheckIfCanJudge::class])->prefix('judging/bonus_scores')->group(function () { Route::middleware(['auth', 'verified', CheckIfCanJudge::class])->prefix('judging/bonus_scores')->group(function () {
Route::get('/{audition}', BonusScoreEntryListController::class)->name('judging.bonusScore.EntryList'); Route::get('/{audition}', BonusScoreEntryListController::class)->name('judging.bonusScore.EntryList'); // List of entries in an audition
Route::get('/entries/{entry}', BonusScoreEntryController::class)->name('judging.bonusScore.entry'); // Form to enter an entries score
Route::post('/entries/{entry}', BonusScoreRecordController::class)->name('judging.bonusScores.recordScore'); // Record the score
}); });

View File

@ -0,0 +1,68 @@
<?php
use App\Models\Audition;
use App\Models\BonusScore;
use App\Models\BonusScoreDefinition;
use App\Models\Entry;
use App\Models\Room;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Sinnbeck\DomAssertions\Asserts\AssertForm;
use function Pest\Laravel\actingAs;
uses(RefreshDatabase::class);
it('denies access go a guest', function () {
$response = $this->get(route('judging.bonusScore.entry', 1));
$response->assertRedirect(route('home'));
});
it('denies access to a user not assigned to judge this bonus score', function () {
$bonusScore = BonusScoreDefinition::factory()->create();
$audition = Audition::factory()->create();
$audition->bonusScore()->attach($bonusScore->id);
$entry = Entry::factory()->create(['audition_id' => $audition->id]);
$room = Room::factory()->create();
$user = User::factory()->create();
$room->addJudge($user->id);
actingAs($user);
$this->get(route('judging.bonusScore.entry', $entry->id))
->assertRedirect(route('judging.index'))
->assertSessionHas('error', 'You are not assigned to judge this entry');
});
it('denies access if a score already exists for the entry by the user', function () {
$entry = Entry::factory()->create();
$judge = User::factory()->create();
$bonusScoreDefinition = BonusScoreDefinition::factory()->create();
$bonusScoreDefinition->judges()->attach($judge->id);
BonusScore::create([
'entry_id' => $entry->id,
'user_id' => $judge->id,
'originally_scored_entry' => $entry->id,
'score' => 42,
]);
actingAs($judge);
$this->get(route('judging.bonusScore.entry', $entry))
->assertRedirect(route('judging.bonusScore.EntryList', $entry->audition))
->assertSessionHas('error', 'You have already judged that entry');
});
it('has a proper score entry form for a valid request', function () {
// Arrange
$audition = Audition::factory()->create();
$bonusScore = BonusScoreDefinition::factory()->create(['max_score' => 100]);
$bonusScore->auditions()->attach($audition->id);
$entry = Entry::factory()->create(['audition_id' => $audition->id]);
$judge = User::factory()->create();
$bonusScore->judges()->attach($judge->id);
actingAs($judge);
// Act & Assert
$request = $this->get(route('judging.bonusScore.entry', $entry));
$request->assertOk()
->assertFormExists('#score-entry-for-'.$entry->id, function (AssertForm $form) use ($entry) {
$form->hasCSRF()
->hasMethod('POST')
->hasAction(route('judging.bonusScores.recordScore', $entry))
->containsInput(['name' => 'score'])
->containsButton(['type' => 'submit']);
});
});