diff --git a/app/Http/Controllers/NominationEnsembles/NominationEnsembleEntryController.php b/app/Http/Controllers/NominationEnsembles/NominationEnsembleEntryController.php new file mode 100644 index 0000000..4e77875 --- /dev/null +++ b/app/Http/Controllers/NominationEnsembles/NominationEnsembleEntryController.php @@ -0,0 +1,22 @@ +id] = Student::where('grade', '<=', $ensemble->maximum_grade) + ->where('grade', '>=', $ensemble->minimum_grade) + ->where('school_id', auth()->user()->school_id) + ->orderBy('last_name') + ->orderBy('first_name') + ->get(); + $availableInstruments[$ensemble->id] = $ensemble->data['instruments']; + $nominatedStudents[$ensemble->id] = $this->collapseNominations(auth()->user()->school, $ensemble, + 'nominations'); + + $nominatedStudentIds = []; + + // Removed students already nominated from available students + foreach ($nominatedStudents[$ensemble->id] as $nominatedStudent) { + $nominatedStudentIds[] = $nominatedStudent->student_id; + } + $availableStudents[$ensemble->id] = $availableStudents[$ensemble->id]->reject(function ($student) use ( + $nominatedStudentIds + ) { + return in_array($student->id, $nominatedStudentIds); + }); + + $nominationsAvailable[$ensemble->id] = $ensemble->data['max_nominations'] > count($nominatedStudents[$ensemble->id]); + + } + + return view('nomination_ensembles.scobda.entries.index', + compact('ensembles', 'availableStudents', 'availableInstruments', 'nominatedStudents', 'nominationsAvailable')); + } + + public function show(NominationEnsembleEntry $ensemble) + { + // TODO: Implement show() method. + } + + public function create() + { + // TODO: Implement create() method. + } + + public function store() + { + $validData = request()->validate([ + 'ensemble' => [ + 'required', + 'exists:App\Models\NominationEnsemble,id', + ], + 'new_student' => [ + 'required', + 'exists:App\Models\Student,id', + ], + 'new_instrument' => 'required', + ]); + + if (NominationEnsembleEntry::where('student_id', $validData['new_student']) + ->where('nomination_ensemble_id', $validData['ensemble']) + ->count() > 0) { + return redirect()->route('nomination.entry.index')->with('error', + 'Student already nominated for that ensemble'); + } + + $proposedEnsemble = NominationEnsemble::find($validData['ensemble']); + + if (! in_array($validData['new_instrument'], $proposedEnsemble->data['instruments'])) { + return redirect()->route('nomination.entry.index')->with('error', + 'Invalid Instrument specified'); + } + + $student = Student::find($validData['new_student']); + $nextRank = $this->collapseNominations($student->school, $proposedEnsemble, 'next'); + if ($nextRank > $proposedEnsemble->data['max_nominations']) { + return redirect()->route('nomination.entry.index')->with('error', + 'You have already used all of your nominations'); + } + + $entry = new NominationEnsembleEntry(); + $entry->student_id = $validData['new_student']; + $entry->nomination_ensemble_id = $validData['ensemble']; + $data = []; + $data['rank'] = $nextRank; + $data['instrument'] = $validData['new_instrument']; + $entry->data = $data; + $entry->save(); + + return redirect()->route('nomination.entry.index')->with('success', + 'Nomination Recorded'); + } + + public function edit(NominationEnsembleEntry $ensemble) + { + // TODO: Implement edit() method. + } + + public function update(NominationEnsembleEntry $ensemble) + { + // TODO: Implement update() method. + } + + public function destroy(NominationEnsembleEntry $ensemble) + { + // TODO: Implement destroy() method. + } + + /** + * Given a school and nomination ensemble, consolidate the rank valuek + * + * if returnType is next, the next available rank will be returned + * if returnType is nominations, a collection of nominations will be returned + * + * @return int|array + * + * @var returnType = next|nominations + */ + private function collapseNominations(School $school, NominationEnsemble $ensemble, $returnType) + { + $nominations = $school->nominations()->get()->where('nomination_ensemble_id', + $ensemble->id)->sortBy('data.rank'); + $n = 1; + foreach ($nominations as $nomination) { + $nomination->update(['data->rank' => $n]); + $n++; + } + + if ($returnType == 'next') { + return $n; + } + + return $nominations; + } +} diff --git a/app/Models/NominationEnsemble.php b/app/Models/NominationEnsemble.php index a26a5de..fec8ef7 100644 --- a/app/Models/NominationEnsemble.php +++ b/app/Models/NominationEnsemble.php @@ -4,6 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\HasMany; class NominationEnsemble extends Model { @@ -15,4 +16,9 @@ class NominationEnsemble extends Model 'data' => 'array', ]; } + + public function entries(): HasMany + { + return $this->hasMany(NominationEnsembleEntry::class); + } } diff --git a/app/Models/NominationEnsembleEntry.php b/app/Models/NominationEnsembleEntry.php index 1cbf2ce..f6ac68f 100644 --- a/app/Models/NominationEnsembleEntry.php +++ b/app/Models/NominationEnsembleEntry.php @@ -4,8 +4,28 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; class NominationEnsembleEntry extends Model { use HasFactory; + + protected $guarded = []; + + protected function casts(): array + { + return [ + 'data' => 'array', + ]; + } + + protected function ensemble(): BelongsTo + { + return $this->belongsTo(NominationEnsemble::class); + } + + protected function student(): BelongsTo + { + return $this->belongsTo(Student::class); + } } diff --git a/app/Models/School.php b/app/Models/School.php index 93c886e..b359b63 100644 --- a/app/Models/School.php +++ b/app/Models/School.php @@ -51,4 +51,15 @@ class School extends Model 'id', 'id'); } + + public function nominations(): HasManyThrough + { + return $this->hasManyThrough( + NominationEnsembleEntry::class, + Student::class, + 'school_id', + 'student_id', + 'id', + 'id'); + } } diff --git a/app/Models/Student.php b/app/Models/Student.php index d1e1777..925fc29 100644 --- a/app/Models/Student.php +++ b/app/Models/Student.php @@ -35,6 +35,11 @@ class Student extends Model ]; } + public function nominations(): HasMany + { + return $this->hasMany(NominationEnsembleEntry::class); + } + public function school(): BelongsTo { return $this->belongsTo(School::class); diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 0819dc3..4f88a11 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -11,7 +11,9 @@ use App\Actions\Tabulation\CalculateScoreSheetTotal; use App\Actions\Tabulation\CalculateScoreSheetTotalDivideByTotalWeights; use App\Actions\Tabulation\CalculateScoreSheetTotalDivideByWeightedPossible; use App\Http\Controllers\NominationEnsembles\NominationEnsembleController; +use App\Http\Controllers\NominationEnsembles\NominationEnsembleEntryController; use App\Http\Controllers\NominationEnsembles\ScobdaNominationEnsembleController; +use App\Http\Controllers\NominationEnsembles\ScobdaNominationEnsembleEntryController; use App\Models\Audition; use App\Models\Entry; use App\Models\Room; @@ -63,7 +65,10 @@ class AppServiceProvider extends ServiceProvider $this->app->singleton(CreateEntry::class, CreateEntry::class); $this->app->singleton(UpdateEntry::class, UpdateEntry::class); $this->app->singleton(SetHeadDirector::class, SetHeadDirector::class); + + // Nomination Ensemble $this->app->bind(NominationEnsembleController::class, ScobdaNominationEnsembleController::class); + $this->app->bind(NominationEnsembleEntryController::class, ScobdaNominationEnsembleEntryController::class); } /** diff --git a/database/factories/NominationEnsembleEntryFactory.php b/database/factories/NominationEnsembleEntryFactory.php new file mode 100644 index 0000000..1f04ba9 --- /dev/null +++ b/database/factories/NominationEnsembleEntryFactory.php @@ -0,0 +1,23 @@ + $this->faker->randomNumber(), + 'nomination_ensemble_id' => $this->faker->randomNumber(), + 'data' => $this->faker->words(), + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]; + } +} diff --git a/resources/views/components/layout/navbar/menus/my_audition.blade.php b/resources/views/components/layout/navbar/menus/my_audition.blade.php index 00ea2ba..147b5d1 100644 --- a/resources/views/components/layout/navbar/menus/my_audition.blade.php +++ b/resources/views/components/layout/navbar/menus/my_audition.blade.php @@ -29,6 +29,9 @@ @if(Auth::user()->school_id) My Students My Entries + @if(auditionSetting('nomination_ensemble_rules') !== 'disabled') + My Nominations + @endif My Doubler Requests My School @if(auditionSetting('invoicing_enabled')) diff --git a/resources/views/nomination_ensembles/scobda/index.blade.php b/resources/views/nomination_ensembles/scobda/admin/ensembles/index.blade.php similarity index 100% rename from resources/views/nomination_ensembles/scobda/index.blade.php rename to resources/views/nomination_ensembles/scobda/admin/ensembles/index.blade.php diff --git a/resources/views/nomination_ensembles/scobda/entries/index.blade.php b/resources/views/nomination_ensembles/scobda/entries/index.blade.php new file mode 100644 index 0000000..f8fb115 --- /dev/null +++ b/resources/views/nomination_ensembles/scobda/entries/index.blade.php @@ -0,0 +1,66 @@ +@php($n=1) + + Nomination Entries + + + + @foreach($ensembles as $ensemble) + + {{ $ensemble->name }} + {{ $ensemble->data['max_nominations'] }} nominations accepted + + + + Rank + Student + Instrument + + + + @foreach($nominatedStudents[$ensemble->id] as $nomination) + + {{ $nomination->data['rank'] }} + {{ $nomination->student->full_name() }} + {{ $nomination->data['instrument'] }} + + @endforeach + + {{-- LINE TO ADD A NOMINATION--}} + @if($nominationsAvailable[$ensemble->id] && $availableStudents[$ensemble->id]->count() > 0) + + + + NEW + + + @foreach($availableStudents[$ensemble->id] as $student) + + @endforeach + + + + + + @foreach($availableInstruments[$ensemble->id] as $instrument) + + @endforeach + + + + + Add + + + + @endif + + + + + @endforeach + + + + diff --git a/routes/nominationEnsemble.php b/routes/nominationEnsemble.php index 055535e..3d23dae 100644 --- a/routes/nominationEnsemble.php +++ b/routes/nominationEnsemble.php @@ -1,6 +1,7 @@ prefix('nomination }); Route::middleware(['auth', 'verified'])->prefix('nominations/')->group(function () { - + Route::controller(NominationEnsembleEntryController::class)->group(function () { + Route::get('/', 'index')->name('nomination.entry.index'); + Route::post('/', 'store')->name('nomination.entry.store'); + }); });