development on management of prelims entries

This commit is contained in:
Matt Young 2025-09-11 16:53:03 -05:00
parent 7c0504ea89
commit 352897fa25
8 changed files with 299 additions and 0 deletions

View File

@ -0,0 +1,45 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\PrelimDefinitionStoreOrUpdateRequest;
use App\Models\Audition;
use App\Models\PrelimDefinition;
use App\Models\Room;
use App\Models\ScoringGuide;
use function view;
class PrelimDefinitionController extends Controller
{
public function index()
{
$prelims = PrelimDefinition::all();
return view('admin.prelim_definitions.index', compact('prelims'));
}
public function create()
{
$auditions = Audition::doesntHave('prelimDefinition')->get();
$rooms = Room::all();
$guides = ScoringGuide::all();
$action = 'create';
return view('admin.prelim_definitions.createOrUpdate', compact('auditions', 'rooms', 'guides', 'action'));
}
public function store(PrelimDefinitionStoreOrUpdateRequest $request)
{
$validated = $request->validated();
PrelimDefinition::create($validated);
return redirect()->route('admin.prelim_definitions.index');
}
public function edit(PrelimDefinition $prelimDefinition)
{
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class PrelimDefinitionStoreOrUpdateRequest extends FormRequest
{
public function rules(): array
{
return [
'audition_id' => [
'required',
'exists:auditions,id',
Rule::unique('prelim_definitions', 'audition_id')->ignore($this->prelimDefinition),
],
'room_id' => ['nullable', 'exists:rooms,id'],
'scoring_guide_id' => ['nullable', 'exists:scoring_guides,id'],
'passing_score' => ['required', 'integer', 'min:0', 'max:100'],
];
}
public function authorize(): bool
{
return auth()->user()->is_admin;
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace App\Policies;
use App\Models\PrelimDefinition;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class PrelimDefinitionPolicy
{
use HandlesAuthorization;
public function viewAny(User $user): bool
{
return $user->is_admin;
}
public function view(User $user, PrelimDefinition $prelimDefinition): bool
{
return $user->is_admin;
}
public function create(User $user): bool
{
return $user->is_admin;
}
public function update(User $user, PrelimDefinition $prelimDefinition): bool
{
return $user->is_admin;
}
public function delete(User $user, PrelimDefinition $prelimDefinition): bool
{
return $user->is_admin;
}
public function restore(User $user, PrelimDefinition $prelimDefinition): bool
{
}
public function forceDelete(User $user, PrelimDefinition $prelimDefinition): bool
{
}
}

View File

@ -0,0 +1,47 @@
<x-layout.app>
<x-slot:page_title>Manage Prelim Auditions</x-slot:page_title>
<div class="max-w-lg mx-auto">
<x-card.card class="pb-5">
<x-card.heading>Create Prelim Audition</x-card.heading>
<x-form.form method="POST" action="{{ route('admin.prelim_definitions.store') }}"
x-data="{ canSubmit: false }">
<x-form.select name="audition_id" @change="canSubmit = true">
<x-slot:label>Audition</x-slot:label>
<option value="" :disabled="canSubmit">Choose Audition</option>
@foreach($auditions as $audition)
<option value="{{ $audition->id }}">{{ $audition->name }}</option>
@endforeach
</x-form.select>
@error('audition_id')
<div class="text-red-500 text-sm">{{ $message }}</div>
@enderror
<x-form.select name="room_id">
<x-slot:label>Room</x-slot:label>
@foreach($rooms as $room)
<option value="{{ $room->id }}">{{ $room->name }}</option>
@endforeach
</x-form.select>
@error('room_id')
<div class="text-red-500 text-sm">{{ $message }}</div>
@enderror
<x-form.select name="scoring_guide_id">
<x-slot:label>Scoring Guide</x-slot:label>
@foreach($guides as $guide)
<option value="{{ $guide->id }}">{{ $guide->name }}</option>
@endforeach
</x-form.select>
@error('scoring_guide_id')
<div class="text-red-500 text-sm">{{ $message }}</div>
@enderror
<x-form.field name="passing_score" type="number" max="100" min="0" step="0.1" value=60 label_text="Passing Score" />
<x-form.footer submit-button-text="Create Prelim Audition" x-show="canSubmit" x-cloak></x-form.footer>
</x-form.form>
</x-card.card>
</div>
</x-layout.app>

View File

@ -0,0 +1,31 @@
<x-layout.app>
<x-slot:page_title>Prelim Audition Setup</x-slot:page_title>
<x-card.card>
<x-card.heading>
Preliminary Auditions
<x-slot:right_side>
<x-form.button href="{{ route('admin.prelim_definitions.create') }}">Add Prelim</x-form.button>
</x-slot:right_side>
</x-card.heading>
<x-table.table class="mt-3 mb-3">
<thead>
<tr>
<x-table.th>Audition</x-table.th>
<x-table.th>Passing Score</x-table.th>
<x-table.th>Room</x-table.th>
<x-table.th>Scoring Guide</x-table.th>
</tr>
</thead>
<x-table.body>
@foreach ($prelims as $prelim)
<tr onclick="window.location='{{ route('admin.prelim_definitions.edit', $prelim) }}';" style="cursor:pointer;">
<x-table.td>{{ $prelim->audition->name }}</x-table.td>
<x-table.td>{{ $prelim->passing_score }}</x-table.td>
<x-table.td>{{ $prelim->room->name }}</x-table.td>
<x-table.td>{{ $prelim->scoringGuide->name }}</x-table.td>
</tr>
@endforeach
</x-table.body>
</x-table.table>
</x-card.card>
</x-layout.app>

View File

@ -0,0 +1,3 @@
<div>
</div>

View File

@ -11,6 +11,7 @@ use App\Http\Controllers\Admin\EntryController;
use App\Http\Controllers\Admin\EventController;
use App\Http\Controllers\Admin\ExportEntriesController;
use App\Http\Controllers\Admin\ExportResultsController;
use App\Http\Controllers\Admin\PrelimDefinitionController;
use App\Http\Controllers\Admin\PrintCards;
use App\Http\Controllers\Admin\PrintRoomAssignmentsController;
use App\Http\Controllers\Admin\PrintSignInSheetsController;
@ -203,4 +204,13 @@ Route::middleware(['auth', 'verified', CheckIfAdmin::class])->prefix('admin/')->
// Print Room and Judge Assignment Report
Route::get('room_assignment_report',
PrintRoomAssignmentsController::class)->name('admin.print_room_assignment_report');
// PrelimDefinition Routes
Route::prefix('prelim_definitions')->controller(PrelimDefinitionController::class)->group(function () {
Route::get('/', 'index')->name('admin.prelim_definitions.index');
Route::get('/new', 'create')->name('admin.prelim_definitions.create');
Route::post('/', 'store')->name('admin.prelim_definitions.store');
Route::get('/{prelimDefinition}', 'edit')->name('admin.prelim_definitions.edit');
Route::delete('/{prelimDefinition}', 'destroy')->name('admin.prelim_definitions.destroy');
});
});

View File

@ -0,0 +1,90 @@
<?php
use App\Models\Audition;
use App\Models\PrelimDefinition;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
describe('PrelimDefinitionController::index', function () {
it('denies access to a non-admin user', function () {
$this->get(route('admin.prelim_definitions.index'))->assertRedirect(route('home'));
actAsNormal();
$this->get(route('admin.prelim_definitions.index'))->assertRedirect(route('dashboard'));
actAsTab();
$this->get(route('admin.prelim_definitions.index'))->assertRedirect(route('dashboard'));
actAsAdmin();
$this->get(route('admin.prelim_definitions.index'))->assertViewIs('admin.prelim_definitions.index');
});
it('lists existing prelim definitions', function () {
$audition = Audition::factory()->create();
$prelim = PrelimDefinition::create([
'audition_id' => $audition->id,
'room_id' => 0,
'scoring_guide_id' => 0,
'passing_score' => 75,
]);
actAsAdmin();
$this->get(route('admin.prelim_definitions.index'))
->assertViewIs('admin.prelim_definitions.index')
->assertSee($audition->name);
});
});
describe('PrelimDefinitionController::create', function () {
it('denies access to a non-admin user', function () {
$this->get(route('admin.prelim_definitions.create'))->assertRedirect(route('home'));
actAsNormal();
$this->get(route('admin.prelim_definitions.create'))->assertRedirect(route('dashboard'));
actAsTab();
$this->get(route('admin.prelim_definitions.create'))->assertRedirect(route('dashboard'));
actAsAdmin();
$this->get(route('admin.prelim_definitions.create'))->assertViewIs('admin.prelim_definitions.createOrUpdate');
});
});
describe('PrelimDefinitionController::store', function () {
beforeEach(function () {
$this->audition = Audition::factory()->create();
});
it('denies access to a non-admin user', function () {
$this->post(route('admin.prelim_definitions.store'))->assertRedirect(route('home'));
actAsNormal();
$this->post(route('admin.prelim_definitions.store'))->assertRedirect(route('dashboard'));
actAsTab();
$this->post(route('admin.prelim_definitions.store'))->assertRedirect(route('dashboard'));
});
it('can store a new prelim audition', function () {
actAsAdmin();
$response = $this->post(route('admin.prelim_definitions.store'), [
'audition_id' => $this->audition->id,
'room_id' => 0,
'scoring_guide_id' => 0,
'passing_score' => 75,
]);
$response
->assertRedirect(route('admin.prelim_definitions.index'))
->assertSessionDoesntHaveErrors();
});
it('will not allow us to create two prelims for the same audition', function () {
actAsAdmin();
PrelimDefinition::create([
'audition_id' => $this->audition->id,
'room_id' => 0,
'scoring_guide_id' => 0,
'passing_score' => 75,
]);
$response = $this->from(route('admin.prelim_definitions.create'))
->post(route('admin.prelim_definitions.store'), [
'audition_id' => $this->audition->id,
'room_id' => 0,
'scoring_guide_id' => 0,
'passing_score' => 75,
]);
$response->assertSessionHasErrors('audition_id')
->assertRedirect(route('admin.prelim_definitions.create'));
});
});