Room assignments working for those with rooms. Need to implement for unassigned auditions
This commit is contained in:
parent
43647583e7
commit
1b0112b73f
|
|
@ -102,6 +102,21 @@ class AuditionController extends Controller
|
|||
return response()->json(['status' => 'success']);
|
||||
}
|
||||
|
||||
public function roomUpdate(Request $request)
|
||||
{
|
||||
$auditions = $request->all();
|
||||
|
||||
foreach ($auditions as $audition) {
|
||||
Audition::where('id', $audition['id'])
|
||||
->update([
|
||||
'room_id' => $audition['room_id'],
|
||||
'order_in_room' => $audition['room_order']
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json(['status' => 'success']);
|
||||
}
|
||||
|
||||
public function destroy(Audition $audition)
|
||||
{
|
||||
if(! Auth::user()->is_admin) abort(403);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Audition;
|
||||
use App\Models\Room;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class RoomController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
if(! Auth::user()->is_admin) abort(403);
|
||||
$rooms = Room::with('auditions.entries')->orderBy('name')->get();
|
||||
$unassignedAuditions = Audition::with('entries')->whereNull('room_id')->orderBy('score_order')->get();
|
||||
return view('admin.rooms.index', ['rooms' => $rooms, 'unassignedAuditions' => $unassignedAuditions]);
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,11 @@ class Audition extends Model
|
|||
return $this->hasMany(Entry::class);
|
||||
}
|
||||
|
||||
public function room(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Room::class);
|
||||
}
|
||||
|
||||
public function dislpay_fee(): String
|
||||
{
|
||||
return '$' . number_format($this->entry_fee / 100, 2);
|
||||
|
|
@ -39,5 +44,5 @@ class Audition extends Model
|
|||
|
||||
|
||||
|
||||
// TODO add order column to be able to sort in score order
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ class Entry extends Model
|
|||
|
||||
public function school(): HasOneThrough
|
||||
{
|
||||
return $this->hasOneThrough(School::class, Student::class, 'id', 'id', 'student_id', 'school_id');
|
||||
return $this->hasOneThrough(
|
||||
School::class,
|
||||
Student::class,
|
||||
'id',
|
||||
'id',
|
||||
'student_id',
|
||||
'school_id');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||
|
||||
class Room extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public function auditions(): HasMany
|
||||
{
|
||||
return $this->hasMany(Audition::class)->orderBy('order_in_room');
|
||||
}
|
||||
|
||||
public function entries(): HasManyThrough
|
||||
{
|
||||
return $this->hasManyThrough(
|
||||
Entry::class,
|
||||
Audition::class,
|
||||
'room_id', // Foreign key on the auditions table
|
||||
'audition_id', //Foreign key on the Entries table
|
||||
'id', // Local key on the rooms table
|
||||
'id' // Local key on the Auditions table
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Room>
|
||||
*/
|
||||
class RoomFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ return new class extends Migration
|
|||
$table->id();
|
||||
$table->foreignIdFor(Event::class)->constrained()->cascadeOnUpdate()->restrictOnDelete();
|
||||
$table->string('name')->unique();
|
||||
$table->integer('order')->nullable()->unique();
|
||||
$table->integer('order')->nullable();
|
||||
$table->date(('entry_deadline'));
|
||||
$table->integer('entry_fee');
|
||||
$table->integer('minimum_grade');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('rooms', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name')->unique();
|
||||
$table->text('description')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('rooms');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Room;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('auditions', function (Blueprint $table) {
|
||||
$table->foreignIdFor(Room::class)->nullable()->constrained()->cascadeOnUpdate()->nullOnDelete();
|
||||
$table->integer('order_in_room');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('auditions', function (Blueprint $table) {
|
||||
$table->dropColumn('room_id');
|
||||
$table->dropColumn('order_in_room');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class RoomSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<x-layout.app>
|
||||
<x-slot:page_title>Rooms</x-slot:page_title>
|
||||
<x-slot:title_bar_right><x-form.button href="/admin/rooms/create">New Room</x-form.button></x-slot:title_bar_right>
|
||||
<div class="grid md:grid-cols-4 gap-5" x-data="roomManager()">
|
||||
<div> {{-- Unassigned Auditions --}}
|
||||
<x-card.card>
|
||||
<x-card.heading>Unassigned Auditions</x-card.heading>
|
||||
<x-card.list.body id="room-0" class="audition-list">
|
||||
|
||||
@foreach($unassignedAuditions as $audition)
|
||||
<x-card.list.row class="!py-3" data-id="{{ $audition->id }}">
|
||||
<x-badge_pill>{{ $audition->entries->count() }}</x-badge_pill>
|
||||
<x-card.list.row-text-subtext>
|
||||
{{ $audition->name }}
|
||||
</x-card.list.row-text-subtext>
|
||||
</x-card.list.row>
|
||||
@endforeach
|
||||
|
||||
</x-card.list.body>
|
||||
</x-card.card>
|
||||
</div>
|
||||
|
||||
<div class="col-span-3"> {{-- Container for room cards --}}
|
||||
<div class="grid md:grid-cols-3 gap-3">
|
||||
|
||||
@foreach($rooms as $room)
|
||||
<x-card.card>
|
||||
<x-card.heading>
|
||||
{{ $room->name }}
|
||||
<x-slot:subheading>{{ $room->description }}</x-slot:subheading>
|
||||
<x-slot:right_side>
|
||||
<x-badge_pill>{{ $room->entries->count() }}</x-badge_pill>
|
||||
</x-slot:right_side>
|
||||
</x-card.heading>
|
||||
<x-card.list.body id="room-{{ $room->id }}" class="audition-list">
|
||||
|
||||
@foreach($room->auditions as $audition)
|
||||
<x-card.list.row class="!py-3" data-id="{{ $audition->id }}">
|
||||
<x-badge_pill>{{ $audition->entries->count() }}</x-badge_pill>
|
||||
<x-card.list.row-text-subtext>{{ $audition->name }}</x-card.list.row-text-subtext>
|
||||
</x-card.list.row>
|
||||
@endforeach
|
||||
|
||||
|
||||
</x-card.list.body>
|
||||
|
||||
</x-card.card>
|
||||
@endforeach()
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function roomManager() {
|
||||
return {
|
||||
rooms: @json($rooms), // Pass rooms data from the controller
|
||||
init() {
|
||||
this.rooms.forEach(room => {
|
||||
new Sortable(document.getElementById('room-' + room.id), {
|
||||
group: 'shared',
|
||||
animation: 150,
|
||||
onEnd: this.updateOrder.bind(this)
|
||||
});
|
||||
});
|
||||
},
|
||||
updateOrder(event) {
|
||||
let order = [];
|
||||
event.to.querySelectorAll('li').forEach((el, index) => {
|
||||
order.push({
|
||||
id: el.dataset.id,
|
||||
room_id: event.to.id.split('-')[1],
|
||||
room_order: index
|
||||
});
|
||||
});
|
||||
|
||||
fetch('/admin/auditions/roomUpdate', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
},
|
||||
body: JSON.stringify(order)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</x-layout.app>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<div x-data="auditionManager()">
|
||||
<template x-for="room in rooms" :key="room.id">
|
||||
<div>
|
||||
<h2 x-text="room.name"></h2>
|
||||
<ul :id="'room-' + room.id" class="audition-list">
|
||||
<template x-for="audition in room.auditions" :key="audition.id">
|
||||
<li x-text="audition.name" :data-id="audition.id"></li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function auditionManager() {
|
||||
return {
|
||||
rooms: @json($rooms), // Pass rooms data from the controller
|
||||
init() {
|
||||
this.rooms.forEach(room => {
|
||||
new Sortable(document.getElementById('room-' + room.id), {
|
||||
group: 'shared',
|
||||
animation: 150,
|
||||
onEnd: this.updateOrder.bind(this)
|
||||
});
|
||||
});
|
||||
},
|
||||
updateOrder(event) {
|
||||
let order = [];
|
||||
event.to.querySelectorAll('li').forEach((el, index) => {
|
||||
order.push({
|
||||
id: el.dataset.id,
|
||||
room_id: event.to.id.split('-')[1],
|
||||
room_order: index
|
||||
});
|
||||
});
|
||||
|
||||
fetch('/update-order', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
},
|
||||
body: JSON.stringify(order)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,8 +1,15 @@
|
|||
@props(['subheading' => false])
|
||||
@props(['subheading' => false, 'right_side' => false])
|
||||
|
||||
<div class="px-4 py-2 sm:px-6 border-b border-gray-100">
|
||||
<div class="px-4 py-2 sm:px-6 border-b border-gray-100 flex justify-between">
|
||||
<div>
|
||||
<h3 {{ $attributes->merge(['class' => 'text-base font-semibold leading-7 text-gray-900']) }}>{{ $slot }}</h3>
|
||||
@if($subheading)
|
||||
<p {{ $subheading->attributes->merge(['class' => 'mt-.5 max-w-2xl text-sm leading-6 text-gray-500']) }}>{{ $subheading }}</p>
|
||||
@endif
|
||||
</div>
|
||||
@if($right_side)
|
||||
<div>
|
||||
{{ $right_side }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@props(['view_all_href' => false])
|
||||
<div>
|
||||
<ul role="list" class="divide-y divide-gray-100 ">
|
||||
<ul {{ $attributes->merge(['role'=>'list','class'=>'divide-y divide-gray-100']) }}>
|
||||
{{ $slot }}
|
||||
</ul>
|
||||
@if($view_all_href)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@props(['page_title' => false])
|
||||
@props(['page_title' => false, 'title_bar_right' => false ])
|
||||
<!doctype html>
|
||||
<html lang="en" class="h-full bg-gray-100">
|
||||
<head>
|
||||
|
|
@ -26,7 +26,12 @@
|
|||
<x-layout.navbar />
|
||||
@endif
|
||||
@if($page_title)
|
||||
<x-layout.page-header>{{ $page_title }}</x-layout.page-header>
|
||||
<x-layout.page-header>
|
||||
{{ $page_title }}
|
||||
@if($title_bar_right)
|
||||
<x-slot:title_bar_right>{{ $title_bar_right }}</x-slot:title_bar_right>
|
||||
@endif
|
||||
</x-layout.page-header>
|
||||
@endif
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
<x-layout.nav-link href="/admin/entries" :active="request()->is('admin/entries')">Entries</x-layout.nav-link>
|
||||
<x-layout.nav-link href="/admin/auditions" :active="request()->is('admin/auditions')">Auditions</x-layout.nav-link>
|
||||
<x-layout.nav-link href="/admin/scoring" :active="request()->is('admin/scoring')">Scoring</x-layout.nav-link>
|
||||
<x-layout.nav-link href="/admin/rooms" :active="request()->is('admin/rooms')">Rooms</x-layout.nav-link>
|
||||
{{-- <a href="/dashboard" class="bg-indigo-700 text-white rounded-md px-3 py-2 text-sm font-medium" aria-current="page">Dashboard</a>--}}
|
||||
|
||||
{{-- <a href="/students" class="text-white hover:bg-indigo-500 hover:bg-opacity-75 rounded-md px-3 py-2 text-sm font-medium">Students</a>--}}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
@props(['title_bar_right' => false])
|
||||
<header class="bg-white shadow-sm">
|
||||
<div class="mx-auto max-w-7xl px-4 py-4 sm:px-6 lg:px-8">
|
||||
<div class="mx-auto max-w-7xl flex justify-between align-middle px-4 py-4 sm:px-6 lg:px-8">
|
||||
<div>
|
||||
<h1 class="text-lg font-semibold leading-6 text-gray-900">{{ $slot }}</h1>
|
||||
</div>
|
||||
<div>
|
||||
@if($title_bar_right)
|
||||
{{ $title_bar_right }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Admin\AuditionController;
|
||||
use App\Http\Controllers\DashboardController;
|
||||
use App\Http\Controllers\EntryController;
|
||||
use App\Http\Controllers\FilterController;
|
||||
|
|
@ -18,6 +19,17 @@ Route::view('/','welcome')->middleware('guest');
|
|||
// Admin Routes
|
||||
Route::middleware(['auth','verified',CheckIfAdmin::class])->prefix('admin/')->group(function() {
|
||||
Route::view('/','admin.dashboard');
|
||||
Route::post('/auditions/roomUpdate',[\App\Http\Controllers\Admin\AuditionController::class,'roomUpdate']);
|
||||
|
||||
// Rooms
|
||||
Route::prefix('rooms')->controller(\App\Http\Controllers\Admin\RoomController::class)->group(function() {
|
||||
Route::get('/','index');
|
||||
Route::get('/create','create');
|
||||
Route::post('/','store');
|
||||
Route::post('/{room}/edit','edit');
|
||||
Route::patch('/{room}','update');
|
||||
Route::delete('/{room}','destroy');
|
||||
});
|
||||
|
||||
// Scoring
|
||||
Route::prefix('scoring')->controller(\App\Http\Controllers\Admin\ScoringGuideController::class)->group(function() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue