Ensembles page working, need to add delete link
This commit is contained in:
parent
82b56a3f4f
commit
3a6ac955c0
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ensemble;
|
||||
use App\Models\Event;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class EnsembleController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$events = Event::with('ensembles')->get();
|
||||
return view('admin.ensembles.index',compact('events'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
if(! Auth::user()->is_admin) abort(403);
|
||||
request()->validate([
|
||||
'name' => 'required',
|
||||
'code' => 'required',
|
||||
'event_id' => ['required','exists:events,id']
|
||||
]);
|
||||
|
||||
Ensemble::create([
|
||||
'name' => request('name'),
|
||||
'code' => request('code'),
|
||||
'event_id' => request('event_id'),
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.ensembles.index')->with('success','Ensemble created successfully');
|
||||
}
|
||||
|
||||
public function updateEnsembleRank(Request $request)
|
||||
{
|
||||
$order = $request->input('order');
|
||||
$eventId = $request->input('event_id');
|
||||
|
||||
foreach ($order as $item) {
|
||||
Ensemble::where('id', $item['id'])
|
||||
->where('event_id', $eventId)
|
||||
->update(['rank' => $item['rank']]);
|
||||
}
|
||||
|
||||
return response()->json(['status' => 'success']);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Ensemble extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
protected $guarded = [];
|
||||
|
||||
public function event(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Event::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -15,4 +15,10 @@ class Event extends Model
|
|||
{
|
||||
return $this->hasMany(Audition::class);
|
||||
}
|
||||
|
||||
public function ensembles(): HasMany
|
||||
{
|
||||
return $this->hasMany(Ensemble::class)
|
||||
->orderBy('rank');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Event;
|
||||
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('ensembles', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignIdFor(Event::class)->constrained()->cascadeOnUpdate()->cascadeOnDelete();
|
||||
$table->string('name');
|
||||
$table->string('code');
|
||||
$table->integer('rank')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('ensembles');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<x-card.card class="mb-6 mx-auto max-w-lg">
|
||||
<x-card.heading>Ensembles for {{ $event->name }}</x-card.heading>
|
||||
<x-table.table class="ml-3 sortable-table" id="event-{{$event->id}}">
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<x-table.td> </x-table.td>
|
||||
<x-table.th>Code</x-table.th>
|
||||
<x-table.th>Name</x-table.th>
|
||||
<x-table.th> </x-table.th>
|
||||
</tr>
|
||||
</thead>
|
||||
<x-table.body>
|
||||
@foreach($event->ensembles as $ensemble)
|
||||
<tr data-id="{{ $ensemble->id }}">
|
||||
<x-table.td class="handle">
|
||||
<svg class="w-6 h-6 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M5 7h14M5 12h14M5 17h14"/>
|
||||
</svg>
|
||||
|
||||
</x-table.td>
|
||||
{{-- <x-table.td class="handle"><i class="fas fa-bars"></i></x-table.td>--}}
|
||||
<x-table.td>{{ $ensemble->code }}</x-table.td>
|
||||
<x-table.td>{{ $ensemble->name }}</x-table.td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</x-table.body>
|
||||
</x-table.table>
|
||||
</x-card.card>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<x-help-modal>
|
||||
<x-slot:title>About Ensembles in AuditionAdmin</x-slot:title>
|
||||
<ul class="text-sm text-gray-600 list-disc ml-5 space-y-2">
|
||||
{{-- move help text to a popout modal --}}
|
||||
<li>After scoring is complete, students are seated into ensembles</li>
|
||||
<li>The highest scoring entries will fill the highest ranked ensembles first</li>
|
||||
<li>An ensemble cannot be deleted if students are seated into it</li>
|
||||
<li>After creating an ensemble, you'll set the maximum number of students accepted from each audition into that ensemble</li>
|
||||
</ul>
|
||||
</x-help-modal>
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<x-layout.app>
|
||||
<x-slot:page_title>Ensembles</x-slot:page_title>
|
||||
<x-slot:title_bar_right>@include('admin.ensembles.index-help-modal')</x-slot:title_bar_right>
|
||||
<x-card.card class="mb-6 mx-auto max-w-lg">
|
||||
<x-card.heading>Create New Ensemble</x-card.heading>
|
||||
<x-form.form method="POST" action="{{ route('admin.ensembles.store') }}">
|
||||
<x-form.body-grid columns="3" class="mb-5">
|
||||
<x-form.field name="name" label_text="Name" colspan="2" />
|
||||
<x-form.field name="code" label_text="Code" colspan="1" />
|
||||
<x-form.select name="event_id" colspan="2">
|
||||
<x-slot:label>Event</x-slot:label>
|
||||
<option disabled selected>Select Event</option>
|
||||
@foreach($events as $event)
|
||||
<option value="{{ $event->id }}" class="text-gray-500">{{ $event->name }}</option>
|
||||
@endforeach
|
||||
</x-form.select>
|
||||
<x-form.button class="mt-6">Create</x-form.button>
|
||||
|
||||
</x-form.body-grid>
|
||||
</x-form.form>
|
||||
</x-card.card>
|
||||
<div id="app">
|
||||
@foreach($events as $event)
|
||||
@include('admin.ensembles.index-event-table')
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const tables = document.querySelectorAll('.sortable-table');
|
||||
|
||||
tables.forEach(table => {
|
||||
new Sortable(table.querySelector('tbody'), {
|
||||
handle: '.handle',
|
||||
animation: 150,
|
||||
onEnd: function (evt) {
|
||||
const rows = Array.from(evt.from.children);
|
||||
const order = rows.map((row, index) => ({
|
||||
id: row.dataset.id,
|
||||
rank: index + 1
|
||||
}));
|
||||
|
||||
// Send the new order to the server
|
||||
fetch('/admin/ensembles/updateEnsembleRank', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_id: table.id.split('-')[1],
|
||||
order: order
|
||||
})
|
||||
}).then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('Success:', data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</x-layout.app>
|
||||
|
|
@ -18,10 +18,12 @@
|
|||
From: "opacity-100 translate-y-0"
|
||||
To: "opacity-0 translate-y-1"
|
||||
-->
|
||||
{{-- TODO conver to named routes --}}
|
||||
<div class="absolute left-1/2 z-10 mt-5 flex w-screen max-w-min -translate-x-1/2 px-4" x-show="open" x-cloak>
|
||||
<div class="w-56 shrink rounded-xl bg-white p-4 text-sm font-semibold leading-6 text-gray-900 shadow-lg ring-1 ring-gray-900/5">
|
||||
<a href="/admin/events" class="block p-2 hover:text-indigo-600">Events</a>
|
||||
<a href="/admin/auditions" class="block p-2 hover:text-indigo-600">Auditions</a>
|
||||
<a href="{{ route('admin.ensembles.index') }}" class="block p-2 hover:text-indigo-600">Ensembles</a>
|
||||
<a href="/admin/scoring" class="block p-2 hover:text-indigo-600">Scoring</a>
|
||||
<a href="/admin/rooms" class="block p-2 hover:text-indigo-600">Rooms</a>
|
||||
<a href="/admin/rooms/judging_assignments" class="block p-2 hover:text-indigo-600">Judges</a>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
'subtitle' => false,
|
||||
'sortable' => true
|
||||
])
|
||||
@php
|
||||
if ($title) $with_title_area = true;
|
||||
@endphp
|
||||
|
||||
<div>
|
||||
@if($with_title_area)
|
||||
<div class="mb-4 mt-4 sm:px 4 sm:flex sm:items-center">
|
||||
|
|
|
|||
|
|
@ -58,6 +58,13 @@ Route::middleware(['auth','verified',CheckIfAdmin::class])->prefix('admin/')->gr
|
|||
Route::post('/auditions/roomUpdate',[\App\Http\Controllers\Admin\AuditionController::class,'roomUpdate']); // Endpoint for JS assigning auditions to rooms
|
||||
Route::post('/scoring/assign_guide_to_audition',[\App\Http\Controllers\Admin\AuditionController::class,'scoringGuideUpdate']); // Endpoint for JS assigning scoring guides to auditions
|
||||
|
||||
// Admin Ensemble Routes
|
||||
Route::prefix('ensembles')->controller(\App\Http\Controllers\Admin\EnsembleController::class)->group(function() {
|
||||
Route::get('/','index')->name('admin.ensembles.index');
|
||||
Route::post('/','store')->name('admin.ensembles.store');
|
||||
Route::post('/updateEnsembleRank','updateEnsembleRank')->name('admin.ensembles.updateEnsembleRank');
|
||||
});
|
||||
|
||||
// Admin Event Routes
|
||||
Route::prefix('events')->controller(\App\Http\Controllers\Admin\EventController::class)->group(function() {
|
||||
Route::get('/','index')->name('admin.events.index');
|
||||
|
|
|
|||
Loading…
Reference in New Issue