diff --git a/app/Http/Controllers/DoublerRequestController.php b/app/Http/Controllers/DoublerRequestController.php new file mode 100644 index 0000000..a79b7e0 --- /dev/null +++ b/app/Http/Controllers/DoublerRequestController.php @@ -0,0 +1,89 @@ +user()->school->students; + $studentIds = $students->pluck('id'); + $existingRequests = DoublerRequest::whereIn('student_id', $studentIds)->get(); + $doublers = []; + foreach ($events as $event) { + $event_doublers = $doublerService->doublersForEvent($event); + $doublers[$event->id] = $event_doublers; + } + + return view('doubler_request.index', compact('events', 'doublers', 'students', 'existingRequests')); + } + + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function makeRequest() + { + foreach (request()->get('doubler_requests') as $event_id => $requests) { + if (! Event::find($event_id)->exists()) { + return to_route('doubler_request.index')->with('error', 'Invalid event id specified'); + } + $thisEvent = Event::find($event_id); + foreach ($requests as $student_id => $request) { + if (! Student::find($student_id)->exists()) { + return to_route('doubler_request.index')->with('error', 'Invalid student id specified'); + } + $thisStudent = Student::find($student_id); + if (! $request) { + $oldRequest = DoublerRequest::where('student_id', $student_id) + ->where('event_id', $event_id) + ->first(); + if ($oldRequest) { + Debugbar::info('hit'); + AuditLogEntry::create([ + 'user' => auth()->user()->email, + 'ip_address' => request()->ip(), + 'message' => 'Removed doubler request for '.$thisStudent->full_name().' in '.$thisEvent->name, + 'affected' => ['students' => [$student_id]], + ]); + $oldRequest->delete(); + } + + continue; + } + DoublerRequest::upsert([ + 'event_id' => $event_id, + 'student_id' => $student_id, + 'request' => $request, + ], + uniqueBy: ['event_id', 'student_id'], + update: ['request'] + ); + AuditLogEntry::create([ + 'user' => auth()->user()->email, + 'ip_address' => request()->ip(), + 'message' => 'Made doubler request for '.$thisStudent->full_name().' in '.$thisEvent->name.'Request: '.$request, + 'affected' => ['students' => [$student_id]], + ]); + } + } + echo 'hi'; + + return to_route('doubler_request.index')->with('success', 'Recorded doubler requests'); + } +} diff --git a/app/Models/DoublerRequest.php b/app/Models/DoublerRequest.php index 6c9d774..7181e36 100644 --- a/app/Models/DoublerRequest.php +++ b/app/Models/DoublerRequest.php @@ -10,6 +10,8 @@ class DoublerRequest extends Model { use HasFactory; + protected $guarded = []; + public function student(): BelongsTo { return $this->belongsTo(Student::class); diff --git a/app/Policies/DoublerRequestPolicy.php b/app/Policies/DoublerRequestPolicy.php new file mode 100644 index 0000000..4e75131 --- /dev/null +++ b/app/Policies/DoublerRequestPolicy.php @@ -0,0 +1,68 @@ +school_id === null + ? Response::denyWithStatus(405) + : Response::allow(); + } + + /** + * Determine whether the user can view the model. + */ + public function view(User $user, DoublerRequest $doublerRequest): bool + { + // + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + // + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, DoublerRequest $doublerRequest): bool + { + // + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, DoublerRequest $doublerRequest): bool + { + // + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, DoublerRequest $doublerRequest): bool + { + // + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, DoublerRequest $doublerRequest): bool + { + // + } +} diff --git a/bootstrap/providers.php b/bootstrap/providers.php index 9edefb0..6b20b30 100644 --- a/bootstrap/providers.php +++ b/bootstrap/providers.php @@ -4,4 +4,5 @@ return [ App\Providers\AppServiceProvider::class, App\Providers\FortifyServiceProvider::class, App\Providers\InvoiceDataServiceProvider::class, + Barryvdh\Debugbar\ServiceProvider::class, ]; diff --git a/database/migrations/2024_08_08_200522_create_doubler_request_table.php b/database/migrations/2024_08_08_200522_create_doubler_request_table.php index 2dfb323..be22dc2 100644 --- a/database/migrations/2024_08_08_200522_create_doubler_request_table.php +++ b/database/migrations/2024_08_08_200522_create_doubler_request_table.php @@ -13,11 +13,12 @@ return new class extends Migration */ public function up(): void { - Schema::create('doubler_request', function (Blueprint $table) { + Schema::create('doubler_requests', function (Blueprint $table) { $table->id(); $table->foreignIdFor(Event::class)->constrained()->cascadeOnDelete()->cascadeOnUpdate(); $table->foreignIdFor(Student::class)->constrained()->cascadeOnDelete()->cascadeOnUpdate(); $table->string('request'); + $table->unique(['student_id', 'event_id']); $table->timestamps(); }); } @@ -27,6 +28,6 @@ return new class extends Migration */ public function down(): void { - Schema::dropIfExists('doubler_request'); + Schema::dropIfExists('doubler_requests'); } }; diff --git a/resources/views/doubler_request/index.blade.php b/resources/views/doubler_request/index.blade.php new file mode 100644 index 0000000..7d2d6fa --- /dev/null +++ b/resources/views/doubler_request/index.blade.php @@ -0,0 +1,44 @@ + + Doubler Requests + + @foreach($events as $event) + + {{ $event->name }} + + + + Student Name + Entries + Request + + + + @foreach($students as $student) + @continue(! array_key_exists($student->id, $doublers[$event->id])) + @php + $existingRequest = $existingRequests + ->where('student_id',$student->id) + ->where('event_id',$event->id); + $value = $existingRequest?->first()?->request ?? ''; + @endphp + + {{ $student->full_name() }} + + @foreach($doublers[$event->id][$student->id]['entries'] as $entry) + {{$entry->audition->name}} + @endforeach + + + + + + @endforeach + + + + @endforeach + Save Doubler Requests + + diff --git a/routes/user.php b/routes/user.php index 2d84a45..a210d90 100644 --- a/routes/user.php +++ b/routes/user.php @@ -2,6 +2,7 @@ // Dashboard Related Routes use App\Http\Controllers\DashboardController; +use App\Http\Controllers\DoublerRequestController; use App\Http\Controllers\EntryController; use App\Http\Controllers\PdfInvoiceController; use App\Http\Controllers\SchoolController; @@ -18,7 +19,9 @@ Route::middleware(['auth', 'verified'])->group(function () { }); // Entry Related Routes -Route::middleware(['auth', 'verified', 'can:create,App\Models\Entry'])->controller(EntryController::class)->group(function () { +Route::middleware([ + 'auth', 'verified', 'can:create,App\Models\Entry', +])->controller(EntryController::class)->group(function () { Route::get('/entries', 'index')->name('entries.index'); Route::get('/entries/create', 'create')->name('entries.create'); Route::post('/entries', 'store')->name('entries.store'); @@ -32,7 +35,9 @@ Route::middleware(['auth', 'verified'])->controller(UserController::class)->grou }); // Student Related Routes -Route::middleware(['auth', 'verified', 'can:create,App\Models\Student'])->controller(StudentController::class)->group(function () { +Route::middleware([ + 'auth', 'verified', 'can:create,App\Models\Student', +])->controller(StudentController::class)->group(function () { Route::get('/students', 'index')->name('students.index'); Route::post('students', 'store')->name('students.store'); Route::get('/students/{student}/edit', 'edit')->name('students.edit'); @@ -48,3 +53,11 @@ Route::middleware(['auth', 'verified'])->controller(SchoolController::class)->gr Route::get('/schools/{school}', 'show')->name('schools.show'); Route::patch('/schools/{school}', 'update')->name('schools.update'); }); + +// Doubler Related Routes +Route::middleware([ + 'auth', 'verified', 'can:viewAny,App\Models\DoublerRequest', +])->controller(DoublerRequestController::class)->prefix('doubler_request')->group(function () { + Route::get('/', 'index')->name('doubler_request.index'); + Route::post('/', 'makeRequest')->name('doubler_request.make_request'); +}); diff --git a/tests/Feature/Pages/DoublerRequestsTest.php b/tests/Feature/Pages/DoublerRequestsTest.php new file mode 100644 index 0000000..db8576b --- /dev/null +++ b/tests/Feature/Pages/DoublerRequestsTest.php @@ -0,0 +1,107 @@ +assertRedirect(route('home')); +}); +it('denies access to a user with no school', function () { + // Arrange + actAsNormal(); + // Act & Assert + get(route('doubler_request.index')) + ->assertStatus(405); +}); +it('allows access to a user with a school', function () { + // Arrange + $school = School::factory()->create(); + $user = User::factory()->create(['school_id' => $school->id]); + actingAs($user); + // Act & Assert + get(route('doubler_request.index')) + ->assertOk() + ->assertSessionHasNoErrors(); +}); +it('includes a section for each event', function () { + // Arrange + $events = \App\Models\Event::factory()->count(3)->create(); + $school = School::factory()->create(); + $user = User::factory()->create(['school_id' => $school->id]); + actingAs($user); + // Act & Assert + $response = get(route('doubler_request.index')); + $response->assertOk() + ->assertViewHas('events', \App\Models\Event::all()); + foreach ($events as $event) { + $response->assertSee($event->name); + } +}); +it(/** + * @throws ManageEntryException + */ 'includes all doublers for the users school', function () { + // Arrange + $school = School::factory()->create(); + $user = User::factory()->create(['school_id' => $school->id]); + $concert_event = \App\Models\Event::create(['name' => 'Concert Band']); + $jazz_event = \App\Models\Event::create(['name' => 'Jazz Band']); + $jas_audition = Audition::factory()->create([ + 'name' => 'Jazz Alto Sax', + 'event_id' => $jazz_event->id, + 'minimum_grade' => 1, + 'maximum_grade' => 99, + ]); + $jts_audition = Audition::factory()->create([ + 'name' => 'Jazz Tenor Sax', + 'event_id' => $jazz_event->id, + 'minimum_grade' => 1, + 'maximum_grade' => 99, + ]); + $cas_audition = Audition::factory()->create([ + 'name' => 'Alto Sax', + 'event_id' => $concert_event->id, + 'minimum_grade' => 1, + 'maximum_grade' => 99, + ]); + $cts_audition = Audition::factory()->create([ + 'name' => 'Tenor Sax', + 'event_id' => $concert_event->id, + 'minimum_grade' => 1, + 'maximum_grade' => 99, + ]); + $concert_student = Student::factory()->create(['school_id' => $school->id]); + $jazz_student = Student::factory()->create(['school_id' => $school->id]); + $singleton_student = Student::factory()->create(['school_id' => $school->id]); + $other_school_student = Student::factory()->create(); + $entryCreator = new CreateEntry(); + $entryCreator->createEntry($concert_student, $cas_audition); + $entryCreator->createEntry($singleton_student, $cas_audition); + $entryCreator->createEntry($concert_student, $cts_audition); + $entryCreator->createEntry($jazz_student, $jas_audition); + $entryCreator->createEntry($jazz_student, $jts_audition); + $entryCreator->createEntry($singleton_student, $jts_audition); + $entryCreator->createEntry($other_school_student, $cas_audition); + $entryCreator->createEntry($other_school_student, $cts_audition); + $entryCreator->createEntry($other_school_student, $jts_audition); + $entryCreator->createEntry($other_school_student, $jas_audition); + actingAs($user); + // Act + $response = get(route('doubler_request.index')); + // Assert + $response->assertOk() + ->assertSee($concert_student->full_name()) + ->assertSee($jazz_student->full_name()) + ->assertDontSee($singleton_student->full_name()) + ->assertDontSee($other_school_student->full_name()); +});
{{$entry->audition->name}}