Work on doubler blocks on seating page

This commit is contained in:
Matt Young 2025-06-23 08:25:23 -05:00
parent 88ef36d8be
commit 63b60e6bf5
9 changed files with 169 additions and 113 deletions

View File

@ -6,6 +6,7 @@ use App\Actions\Tabulation\GetAuditionSeats;
use App\Actions\Tabulation\RankAuditionEntries;
use App\Http\Controllers\Controller;
use App\Models\Audition;
use App\Models\Doubler;
use Illuminate\Http\Request;
class SeatAuditionFormController extends Controller
@ -15,7 +16,7 @@ class SeatAuditionFormController extends Controller
$ranker = app(RankAuditionEntries::class);
// Get scored entries in order
$scored_entries = $ranker($audition, 'seating');
$scored_entries->load(['student.doublers', 'student.school']);
// Get unscored entries sorted by draw number
$unscored_entries = $audition->entries()
->whereDoesntHave('totalScore')
@ -49,29 +50,11 @@ class SeatAuditionFormController extends Controller
->orderBy('draw_number', 'asc')
->get();
// Get Doubler Data
$doubler_data = [];
foreach ($scored_entries as $e) {
if ($e->student->isDoublerInEvent($audition->event_id)) {
// get the other entries for this student
$doubler_data[$e->id]['entries'] = $e->student->entriesForEvent($e->audition->event_id);
// How many of this student's entries have been declined
$declined_count = $doubler_data[$e->id]['entries']->filter(function ($entry) {
return $entry->hasFlag('declined');
})->count();
// set status
// declined status is easy
if ($e->hasFlag('declined')) {
$doubler_data[$e->id]['status'] = 'declined';
} elseif ($doubler_data[$e->id]['entries']->count() - $declined_count == 1) {
$doubler_data[$e->id]['status'] = 'accepted';
} else {
$doubler_data[$e->id]['status'] = 'undecided';
}
}
}
// Get Doublers
$doublerData = Doubler::where('event_id', $audition->event_id)
->whereIn('student_id', $scored_entries->pluck('student_id'))
->get()
->keyBy('student_id');
return view('tabulation.auditionSeating',
compact('audition',
@ -79,7 +62,7 @@ class SeatAuditionFormController extends Controller
'unscored_entries',
'noshow_entries',
'failed_prelim_entries',
'doubler_data', )
'doublerData')
);
}

View File

@ -4,7 +4,6 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Collection;
class Doubler extends Model
{
@ -29,7 +28,7 @@ class Doubler extends Model
return $this->belongsTo(Event::class);
}
public function entries(): Collection
public function entries()
{
return Entry::whereIn('id', $this->entries)->get();
}

View File

@ -95,6 +95,11 @@ class Student extends Model
return $this->hasMany(DoublerRequest::class);
}
public function doublers(): HasMany
{
return $this->hasMany(Doubler::class);
}
public function isDoublerInEvent(Event|int $event): bool
{
$eventId = $event instanceof Event ? $event->id : $event;

View File

@ -0,0 +1,49 @@
<?php
namespace App\Observers;
use App\Models\Doubler;
use App\Models\EntryFlag;
class EntryFlagObserver
{
/**
* Handle the EntryFlag "created" event.
*/
public function created(EntryFlag $entryFlag): void
{
Doubler::syncDoublers();
}
/**
* Handle the EntryFlag "updated" event.
*/
public function updated(EntryFlag $entryFlag): void
{
Doubler::syncDoublers();
}
/**
* Handle the EntryFlag "deleted" event.
*/
public function deleted(EntryFlag $entryFlag): void
{
Doubler::syncDoublers();
}
/**
* Handle the EntryFlag "restored" event.
*/
public function restored(EntryFlag $entryFlag): void
{
//
}
/**
* Handle the EntryFlag "force deleted" event.
*/
public function forceDeleted(EntryFlag $entryFlag): void
{
//
}
}

View File

@ -19,6 +19,7 @@ use App\Http\Controllers\NominationEnsembles\ScobdaNominationEnsembleEntryContro
use App\Http\Controllers\NominationEnsembles\ScobdaNominationSeatingController;
use App\Models\Audition;
use App\Models\Entry;
use App\Models\EntryFlag;
use App\Models\Room;
use App\Models\RoomUser;
use App\Models\School;
@ -29,6 +30,7 @@ use App\Models\Student;
use App\Models\SubscoreDefinition;
use App\Models\User;
use App\Observers\AuditionObserver;
use App\Observers\EntryFlagObserver;
use App\Observers\EntryObserver;
use App\Observers\RoomObserver;
use App\Observers\RoomUserObserver;
@ -92,7 +94,8 @@ class AppServiceProvider extends ServiceProvider
SubscoreDefinition::observe(SubscoreDefinitionObserver::class);
User::observe(UserObserver::class);
SeatingLimit::observe(SeatingLimitObserver::class);
EntryFlag::observe(EntryFlagObserver::class);
Model::preventLazyLoading(! app()->isProduction());
// Model::preventLazyLoading(! app()->isProduction());
}
}

View File

@ -0,0 +1,55 @@
@php($doublerButtonClasses = 'hidden rounded-md bg-white px-2.5 py-1.5 text-xs text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:block')
<ul role="list" class="">
@foreach($entry['doubleData'] as $double)
@php($isopen = $double['status'] == 'undecided')
<li class="pb-2 pt-0 px-0 my-2 rounded-xl border border-gray-200 max-w-xs" x-data="{ open: {{ $isopen ? 'true':'false' }} }">
<div class="flex items-start gap-x-3 bg-gray-100 px-3 py-2 rounded-t-xl" >
<p class="text-sm font-semibold leading-6 text-gray-900">
<a href="{{ route('seating.audition', $double['audition']) }}">
{{ $double['auditionName'] }} - {{ $double['status'] }}
</a>
</p>
<div class="w-full flex justify-end" >
<div x-on:click=" open = ! open ">
<x-icons.hamburger-menu />
</div>
</div>
</div>
<div class="grid grid-cols-4" x-show="open">
<div class="mt-1 px-3 text-xs leading-5 text-gray-500 col-span-3">
<ul>
<li class="">
<p class="whitespace-nowrap">Ranked {{ $double['rank'] }}</p>
<p class="truncate">{{ $double['unscored_entries'] }} Unscored</p>
</li>
@foreach($double['seating_limits'] as $limit)
<li>{{$limit['ensemble_name']}} accepts {{ $limit['accepts'] }}</li>
@endforeach
</ul>
</div>
<div class="flex flex-col justify-end gap-y-1 pt-1">
@if ($double['status'] == 'undecided')
<form method="POST" action="{{ route('doubler.accept',['entry'=>$double['entry']]) }}">
@csrf
<button class="{{ $doublerButtonClasses }}">Accept</button>
</form>
<form method="POST" action="{{ route('doubler.decline',['entry'=>$double['entry']]) }}">
@csrf
<button class="{{ $doublerButtonClasses }}">Decline</button>
</form>
@endif
</div>
</div>
</li>
@endforeach
</ul>
{{--Complete Badge--}}
{{--<p class="mt-0.5 whitespace-nowrap rounded-md bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20">Complete</p>--}}
{{--In Progres Badge--}}
{{--<p class="mt-0.5 whitespace-nowrap rounded-md bg-yellow-50 px-1.5 py-0.5 text-xs font-medium text-yellow-800 ring-1 ring-inset ring-yellow-600/20">In Progress</p>--}}

View File

@ -1,55 +1,35 @@
@php($doublerButtonClasses = 'hidden rounded-md bg-white px-2.5 py-1.5 text-xs text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:block')
<ul role="list" class="">
@foreach($entry['doubleData'] as $double)
@php($isopen = $double['status'] == 'undecided')
<li class="pb-2 pt-0 px-0 my-2 rounded-xl border border-gray-200 max-w-xs" x-data="{ open: {{ $isopen ? 'true':'false' }} }">
<div class="flex items-start gap-x-3 bg-gray-100 px-3 py-2 rounded-t-xl" >
<p class="text-sm font-semibold leading-6 text-gray-900">
<a href="{{ route('seating.audition', $double['audition']) }}">
{{ $double['auditionName'] }} - {{ $double['status'] }}
</a>
</p>
<div class="w-full flex justify-end" >
<div x-on:click=" open = ! open ">
<x-icons.hamburger-menu />
<div class="border-2 border-gray-200 p-2 m-2"> {{-- Begin block for doubler entry --}}
<div class="font-semibold mb-2">
<a href="{{route('seating.audition',[$de->audition])}}">{{ $de->audition->name }}</a> #{{$de->draw_number}}
({{ $de->id }})
</div>
</div>
</div>
<div class="grid grid-cols-4" x-show="open">
<div class="mt-1 px-3 text-xs leading-5 text-gray-500 col-span-3">
<ul>
<li class="">
<p class="whitespace-nowrap">Ranked {{ $double['rank'] }}</p>
<p class="truncate">{{ $double['unscored_entries'] }} Unscored</p>
</li>
@foreach($double['seating_limits'] as $limit)
<li>{{$limit['ensemble_name']}} accepts {{ $limit['accepts'] }}</li>
@endforeach
</ul>
</div>
<div class="flex flex-col justify-end gap-y-1 pt-1">
@if ($double['status'] == 'undecided')
<form method="POST" action="{{ route('doubler.accept',['entry'=>$double['entry']]) }}">
@csrf
<button class="{{ $doublerButtonClasses }}">Accept</button>
</form>
<form method="POST" action="{{ route('doubler.decline',['entry'=>$double['entry']]) }}">
@csrf
<button class="{{ $doublerButtonClasses }}">Decline</button>
</form>
@if($de->hasFlag('no_show'))
<div class="text-red-500">NO-SHOW</div>
@elseif($de->hasFlag('failed_prelim'))
<div class="text-red-500">Failed Prelim</div>
@elseif($de->hasFlag('declined'))
<div class="text-red-500">Declined</div>
@else
@php($unscored = $de->audition->unscoredEntries()->count())
@if($unscored > 0)
<div>{{ $unscored }} Unscored Entries</div>
@endif
</div>
</div>
</li>
@if(! $de->rank('seating'))
<div class="text-red-500">THIS ENTRY NOT SCORED</div>
@else
<div>Ranked: {{ $de->rank('seating') }}</div>
<div class="mt-2">
Acceptance Limits<br>
@foreach ($de->audition->SeatingLimits as $limit)
{{ $limit->ensemble->name }} -> {{ $limit->maximum_accepted }}
<br>
@endforeach
</div>
<div class="mt-3">
<x-form.button>Decline {{ $de->audition->name }}</x-form.button>
</div>
@endif
@endif
</ul>
{{--Complete Badge--}}
{{--<p class="mt-0.5 whitespace-nowrap rounded-md bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20">Complete</p>--}}
{{--In Progres Badge--}}
{{--<p class="mt-0.5 whitespace-nowrap rounded-md bg-yellow-50 px-1.5 py-0.5 text-xs font-medium text-yellow-800 ring-1 ring-inset ring-yellow-600/20">In Progress</p>--}}
</div>

View File

@ -40,7 +40,7 @@
<p class="pt-3"><span class="font-semibold">Request: </span>{{$entry['doublerRequest']}}
</p>
@endif
@include('tabulation.auditionSeating-doubler-block')
@include('tabulation.auditionSeating-doubler-block-OLD')
{{-- DOUBLER<br>--}}
{{-- @foreach($entry['doubleData'] as $double)--}}
{{-- ID: {{ $double['entryId'] }} - {{ $double['name'] }} - {{ $double['rank'] }}<br>--}}

View File

@ -32,44 +32,26 @@
<x-table.td class="align-top">{{ $entry->id }}</x-table.td>
<x-table.td class="align-top">{{ $entry->draw_number }}</x-table.td>
<x-table.td class="align-top">
<div>{{ $entry->student->full_name() }}</div>
<div><a href="{{ route('admin.students.edit',[$entry->student_id]) }}">{{ $entry->student->full_name() }}</a></div>
<div class="text-xs text-gray-400">{{ $entry->student->school->name }}</div>
</x-table.td>
<x-table.td class="align-top">
@if( isset($doubler_data[$entry->id]) )
{{-- Check if this entry is a doubler --}}
@foreach($doubler_data[$entry->id]['entries'] as $de)
{{-- If it is, render doubler blocks --}}
<div class="border-2 border-gray-200 p-2 m-2">
{{-- @var \App\Models\Entry $de --}}
<div class="font-semibold mb-2">
{{ $de->audition->name }} #{{$de->draw_number}} ({{ $de->id }})
</div>
@php($unscored = $de->audition->unscoredEntries()->count())
@if($unscored > 0)
<div>{{ $unscored }} Unscored Entries</div>
@endif
@if(! $de->rank('seating'))
<div class="text-red-500">THIS ENTRY NOT SCORED</div>
@php($doubler = $doublerData->get($entry->student_id))
@if($doubler)
@if($doubler->accepted_entry == $entry->id)
ACCEPTED
@elseif($entry->hasFlag('declined'))
DECLINED
@else
<div>Ranked: {{ $de->rank('seating') }}</div>
<div class="mt-2">
Acceptance Limits<br>
@foreach ($de->audition->SeatingLimits as $limit)
{{ $limit->ensemble->name }} -> {{ $limit->maximum_accepted }}
<br>
@endforeach
</div>
<div class="mt-3">
<span class="isolate flex w-full rounded-md shadow-xs">
<button type="button" class="relative w-1/2 justify-center inline-flex items-center rounded-l-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-gray-300 ring-inset hover:bg-gray-50 focus:z-10">Accept</button>
<button type="button" class="relative -ml-px w-1/2 justify-center inline-flex items-center rounded-r-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-gray-300 ring-inset hover:bg-gray-50 focus:z-10">Decline</button>
</span>
</div>
@endif
</div>
UNDECIDED
@foreach($entry->student->entriesForEvent($entry->audition->event_id) as $de)
@include('tabulation.auditionSeating-doubler-block')
@endforeach
@endif
@endif
</x-table.td>
<x-table.td class="align-top">{{ $entry->totalScore->seating_total }}</x-table.td>
</tr>