Entry of doulber requests by users working. #70

Merged
okorpheus merged 3 commits from auditionadmin-63 into master 2024-08-10 21:37:41 +00:00
8 changed files with 329 additions and 4 deletions
Showing only changes of commit 8395a560a5 - Show all commits

View File

@ -0,0 +1,89 @@
<?php
namespace App\Http\Controllers;
use App\Models\AuditLogEntry;
use App\Models\DoublerRequest;
use App\Models\Event;
use App\Models\Student;
use App\Services\DoublerService;
use Barryvdh\Debugbar\Facades\Debugbar;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use function auth;
use function compact;
use function request;
use function to_route;
class DoublerRequestController extends Controller
{
public function index(DoublerService $doublerService)
{
$events = Event::all();
$students = auth()->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.'<br>Request: '.$request,
'affected' => ['students' => [$student_id]],
]);
}
}
echo 'hi';
return to_route('doubler_request.index')->with('success', 'Recorded doubler requests');
}
}

View File

@ -10,6 +10,8 @@ class DoublerRequest extends Model
{ {
use HasFactory; use HasFactory;
protected $guarded = [];
public function student(): BelongsTo public function student(): BelongsTo
{ {
return $this->belongsTo(Student::class); return $this->belongsTo(Student::class);

View File

@ -0,0 +1,68 @@
<?php
namespace App\Policies;
use App\Models\DoublerRequest;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class DoublerRequestPolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): Response
{
return $user->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
{
//
}
}

View File

@ -4,4 +4,5 @@ return [
App\Providers\AppServiceProvider::class, App\Providers\AppServiceProvider::class,
App\Providers\FortifyServiceProvider::class, App\Providers\FortifyServiceProvider::class,
App\Providers\InvoiceDataServiceProvider::class, App\Providers\InvoiceDataServiceProvider::class,
Barryvdh\Debugbar\ServiceProvider::class,
]; ];

View File

@ -13,11 +13,12 @@ return new class extends Migration
*/ */
public function up(): void public function up(): void
{ {
Schema::create('doubler_request', function (Blueprint $table) { Schema::create('doubler_requests', function (Blueprint $table) {
$table->id(); $table->id();
$table->foreignIdFor(Event::class)->constrained()->cascadeOnDelete()->cascadeOnUpdate(); $table->foreignIdFor(Event::class)->constrained()->cascadeOnDelete()->cascadeOnUpdate();
$table->foreignIdFor(Student::class)->constrained()->cascadeOnDelete()->cascadeOnUpdate(); $table->foreignIdFor(Student::class)->constrained()->cascadeOnDelete()->cascadeOnUpdate();
$table->string('request'); $table->string('request');
$table->unique(['student_id', 'event_id']);
$table->timestamps(); $table->timestamps();
}); });
} }
@ -27,6 +28,6 @@ return new class extends Migration
*/ */
public function down(): void public function down(): void
{ {
Schema::dropIfExists('doubler_request'); Schema::dropIfExists('doubler_requests');
} }
}; };

View File

@ -0,0 +1,44 @@
<x-layout.app>
<x-slot:page_title>Doubler Requests</x-slot:page_title>
<x-form.form method="POST" action="{{route('doubler_request.make_request')}}">
@foreach($events as $event)
<x-card.card class="mb-5">
<x-card.heading>{{ $event->name }}</x-card.heading>
<x-table.table>
<thead>
<tr>
<x-table.th>Student Name</x-table.th>
<x-table.th>Entries</x-table.th>
<x-table.th>Request</x-table.th>
</tr>
</thead>
<x-table.body>
@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
<tr>
<x-table.td>{{ $student->full_name() }}</x-table.td>
<x-table.td>
@foreach($doublers[$event->id][$student->id]['entries'] as $entry)
<p>{{$entry->audition->name}}</p>
@endforeach
</x-table.td>
<x-table.td>
<x-form.field
value="{{$value}}"
name="doubler_requests[{{$event->id}}][{{$student->id}}]"/>
</x-table.td>
</tr>
@endforeach
</x-table.body>
</x-table.table>
</x-card.card>
@endforeach
<x-form.button type="submit">Save Doubler Requests</x-form.button>
</x-form.form>
</x-layout.app>

View File

@ -2,6 +2,7 @@
// Dashboard Related Routes // Dashboard Related Routes
use App\Http\Controllers\DashboardController; use App\Http\Controllers\DashboardController;
use App\Http\Controllers\DoublerRequestController;
use App\Http\Controllers\EntryController; use App\Http\Controllers\EntryController;
use App\Http\Controllers\PdfInvoiceController; use App\Http\Controllers\PdfInvoiceController;
use App\Http\Controllers\SchoolController; use App\Http\Controllers\SchoolController;
@ -18,7 +19,9 @@ Route::middleware(['auth', 'verified'])->group(function () {
}); });
// Entry Related Routes // 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', 'index')->name('entries.index');
Route::get('/entries/create', 'create')->name('entries.create'); Route::get('/entries/create', 'create')->name('entries.create');
Route::post('/entries', 'store')->name('entries.store'); Route::post('/entries', 'store')->name('entries.store');
@ -32,7 +35,9 @@ Route::middleware(['auth', 'verified'])->controller(UserController::class)->grou
}); });
// Student Related Routes // 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::get('/students', 'index')->name('students.index');
Route::post('students', 'store')->name('students.store'); Route::post('students', 'store')->name('students.store');
Route::get('/students/{student}/edit', 'edit')->name('students.edit'); 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::get('/schools/{school}', 'show')->name('schools.show');
Route::patch('/schools/{school}', 'update')->name('schools.update'); 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');
});

View File

@ -0,0 +1,107 @@
<?php
use App\Actions\Entries\CreateEntry;
use App\Exceptions\ManageEntryException;
use App\Models\Audition;
use App\Models\School;
use App\Models\Student;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Laravel\actingAs;
use function Pest\Laravel\get;
uses(RefreshDatabase::class);
it('denies a guest', function () {
$request = get(route('doubler_request.index'));
$request->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());
});