Scoring Guide Assignments
This commit is contained in:
parent
28ab05d1f6
commit
73eede9fe2
|
|
@ -117,6 +117,22 @@ class AuditionController extends Controller
|
||||||
return response()->json(['status' => 'success']);
|
return response()->json(['status' => 'success']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scoringGuideUpdate(Request $request)
|
||||||
|
{
|
||||||
|
|
||||||
|
$audition = Audition::find($request->audition_id);
|
||||||
|
|
||||||
|
if ($audition) {
|
||||||
|
$audition->update([
|
||||||
|
'scoring_guide_id' => $request->new_guide_id
|
||||||
|
]);
|
||||||
|
|
||||||
|
return response()->json(['success' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json(['success' => false], 404);
|
||||||
|
}
|
||||||
|
|
||||||
public function destroy(Audition $audition)
|
public function destroy(Audition $audition)
|
||||||
{
|
{
|
||||||
if(! Auth::user()->is_admin) abort(403);
|
if(! Auth::user()->is_admin) abort(403);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use App\Models\ScoringGuide;
|
||||||
use App\Models\SubscoreDefinition;
|
use App\Models\SubscoreDefinition;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use function abort;
|
use function abort;
|
||||||
use function dd;
|
use function dd;
|
||||||
use function request;
|
use function request;
|
||||||
|
|
@ -18,7 +19,10 @@ class ScoringGuideController extends Controller
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
if (! Auth::user()->is_admin) abort(403);
|
if (! Auth::user()->is_admin) abort(403);
|
||||||
$guides = ScoringGuide::orderBy('name')->get();
|
DB::table('auditions')
|
||||||
|
->whereNull('scoring_guide_id')
|
||||||
|
->update(['scoring_guide_id' => 0]);
|
||||||
|
$guides = ScoringGuide::with('auditions')->orderBy('name')->get();
|
||||||
return view('admin.scoring.index',['guides'=>$guides]);
|
return view('admin.scoring.index',['guides'=>$guides]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,11 @@ class Audition extends Model
|
||||||
return $this->belongsTo(Room::class);
|
return $this->belongsTo(Room::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scoringGuide(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(ScoringGuide::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function dislpay_fee(): String
|
public function dislpay_fee(): String
|
||||||
{
|
{
|
||||||
return '$' . number_format($this->entry_fee / 100, 2);
|
return '$' . number_format($this->entry_fee / 100, 2);
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,15 @@ namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
class ScoringGuide extends Model
|
class ScoringGuide extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
public function auditions(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Audition::class)->orderBy('score_order');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Models\Room;
|
use App\Models\Room;
|
||||||
|
use App\Models\ScoringGuide;
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
@ -12,14 +13,15 @@ return new class extends Migration
|
||||||
*/
|
*/
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
Room::upsert([
|
if (! Room::find(0)) {
|
||||||
'id' => 0,
|
$room = Room::create([
|
||||||
'name' => 'Unassigned',
|
'id' => 0,
|
||||||
'description' => 'These auditions have no room',
|
'name' => 'No Guide Assigned'
|
||||||
],
|
]);
|
||||||
uniqueBy: ['id'],
|
$room->update([
|
||||||
update: ['name','description']
|
'id' => 0
|
||||||
);
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Room;
|
||||||
|
use App\Models\ScoringGuide;
|
||||||
|
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(ScoringGuide::class)->nullable()->constrained()->cascadeOnUpdate()->nullOnDelete();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (! ScoringGuide::find(0)) {
|
||||||
|
$sg = ScoringGuide::create([
|
||||||
|
'id' => 0,
|
||||||
|
'name' => 'No Guide Assigned'
|
||||||
|
]);
|
||||||
|
$sg->update([
|
||||||
|
'id' => 0
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('auditions', function (Blueprint $table) {
|
||||||
|
$table->dropForeignIdFor(ScoringGuide::class);
|
||||||
|
$table->dropColumn('scoring_guide_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,46 +1,50 @@
|
||||||
<div x-data="auditionManager()">
|
<div x-data="sortableData()">
|
||||||
<template x-for="room in rooms" :key="room.id">
|
@foreach($guides as $guide)
|
||||||
<div>
|
<div class="grid md:grid-cols-4 sm:grid-cols-2 mx-6 gap-2 mt-3 mb-3 guide-list" id="guide-{{ $guide->id }}" data-guide-id="{{ $guide->id }}">
|
||||||
<h2 x-text="room.name"></h2>
|
@foreach($guide->auditions as $audition)
|
||||||
<ul :id="'room-' + room.id" class="audition-list">
|
<div class="px-3 py-3 border border-indigo-300 bg-gray-100" data-id="{{ $audition->id }}">{{ $audition->name }}</div>
|
||||||
<template x-for="audition in room.auditions" :key="audition.id">
|
@endforeach
|
||||||
<li x-text="audition.name" :data-id="audition.id"></li>
|
|
||||||
</template>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function auditionManager() {
|
function sortableData() {
|
||||||
return {
|
return {
|
||||||
rooms: @json($rooms), // Pass rooms data from the controller
|
|
||||||
init() {
|
init() {
|
||||||
this.rooms.forEach(room => {
|
this.initSortable();
|
||||||
new Sortable(document.getElementById('room-' + room.id), {
|
},
|
||||||
|
initSortable() {
|
||||||
|
document.querySelectorAll('.guide-list').forEach((el) => {
|
||||||
|
Sortable.create(el, {
|
||||||
group: 'shared',
|
group: 'shared',
|
||||||
animation: 150,
|
animation: 150,
|
||||||
onEnd: this.updateOrder.bind(this)
|
onEnd: (evt) => {
|
||||||
});
|
let itemEl = evt.item; // dragged HTMLElement
|
||||||
});
|
let newGuideId = evt.to.getAttribute('data-guide-id');
|
||||||
},
|
let auditionId = itemEl.getAttribute('data-id');
|
||||||
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', {
|
// Make an AJAX request to update the audition_guide_id
|
||||||
method: 'POST',
|
fetch('/update-audition-guide', {
|
||||||
headers: {
|
method: 'POST',
|
||||||
'Content-Type': 'application/json',
|
headers: {
|
||||||
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
'Content-Type': 'application/json',
|
||||||
},
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||||
body: JSON.stringify(order)
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
audition_id: auditionId,
|
||||||
|
new_guide_id: newGuideId
|
||||||
|
})
|
||||||
|
}).then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
console.log('Audition updated successfully');
|
||||||
|
} else {
|
||||||
|
console.error('Failed to update audition');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
<div x-data="sortableData()">
|
||||||
|
@foreach($guides as $guide)
|
||||||
|
<x-card.card class="mb-4">
|
||||||
|
<x-card.heading>{{ $guide->name }}</x-card.heading>
|
||||||
|
|
||||||
|
<div class="grid md:grid-cols-4 sm:grid-cols-2 mx-6 gap-2 mt-3 mb-3 guide-list" id="guide-{{ $guide->id }}" data-guide-id="{{ $guide->id }}">
|
||||||
|
@foreach($guide->auditions as $audition)
|
||||||
|
<div class="px-3 py-3 border border-indigo-300 bg-gray-100" data-id="{{ $audition->id }}">{{ $audition->name }}</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</x-card.card>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function sortableData() {
|
||||||
|
return {
|
||||||
|
init() {
|
||||||
|
this.initSortable();
|
||||||
|
},
|
||||||
|
initSortable() {
|
||||||
|
document.querySelectorAll('.guide-list').forEach((el) => {
|
||||||
|
Sortable.create(el, {
|
||||||
|
group: 'shared',
|
||||||
|
animation: 150,
|
||||||
|
onEnd: (evt) => {
|
||||||
|
let itemEl = evt.item; // dragged HTMLElement
|
||||||
|
let newGuideId = evt.to.getAttribute('data-guide-id');
|
||||||
|
let auditionId = itemEl.getAttribute('data-id');
|
||||||
|
|
||||||
|
// Make an AJAX request to update the audition_guide_id
|
||||||
|
fetch('/admin/scoring/assign_guide_to_audition', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
audition_id: auditionId,
|
||||||
|
new_guide_id: newGuideId
|
||||||
|
})
|
||||||
|
}).then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
console.log('Audition updated successfully');
|
||||||
|
} else {
|
||||||
|
console.error('Failed to update audition');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<x-card.card>
|
||||||
|
<x-card.heading>
|
||||||
|
Scoring Guides
|
||||||
|
<x-slot:subheading>Each audition will be assigned a scoring guide.</x-slot:subheading>
|
||||||
|
</x-card.heading>
|
||||||
|
<x-table.table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<x-table.th>Name</x-table.th>
|
||||||
|
<x-table.th spacer_only></x-table.th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<x-table.body>
|
||||||
|
@foreach($guides as $guide)
|
||||||
|
@if($guide->id == 0)
|
||||||
|
@continue
|
||||||
|
@endif
|
||||||
|
<tr>
|
||||||
|
<x-table.td>{{ $guide->name }}</x-table.td>
|
||||||
|
<x-table.td class="text-right text-indigo-600"><a href="/admin/scoring/guides/{{ $guide->id }}/edit">Edit</a></x-table.td>
|
||||||
|
{{-- TODO add a link to delete if the guide is not in use--}}
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</x-table.body>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<x-form.form method="POST" action="/admin/scoring/guides" class="!px-0 !py-0">
|
||||||
|
<x-table.td>
|
||||||
|
<x-form.field name="name" label_text="Add New Scoring Guide" />
|
||||||
|
</x-table.td>
|
||||||
|
<x-table.td>
|
||||||
|
<x-form.button class="mt-6">Create</x-form.button>
|
||||||
|
</x-table.td>
|
||||||
|
</x-form.form>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</x-table.table>
|
||||||
|
</x-card.card>
|
||||||
|
|
@ -1,41 +1,10 @@
|
||||||
|
@props(['test'=>'get it'])
|
||||||
<x-layout.app>
|
<x-layout.app>
|
||||||
<x-slot:page_title>Scoring Rules</x-slot:page_title>
|
<x-slot:page_title>Scoring Rules</x-slot:page_title>
|
||||||
<div class="mx-auto max-w-lg">
|
<div class="mx-auto max-w-lg">
|
||||||
<x-card.card>
|
@include('admin.scoring.index-scoring-guide-management-card')
|
||||||
<x-card.heading>
|
</div>
|
||||||
Scoring Guides
|
<div class="mx-auto max-w-5xl mt-16">
|
||||||
<x-slot:subheading>Each audition will be assigned a scoring guide.</x-slot:subheading>
|
@include('admin.scoring.index-audition-scoring-guide-assignment-card')
|
||||||
</x-card.heading>
|
|
||||||
<x-table.table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<x-table.th>Name</x-table.th>
|
|
||||||
<x-table.th spacer_only></x-table.th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<x-table.body>
|
|
||||||
@foreach($guides as $guide)
|
|
||||||
<tr>
|
|
||||||
<x-table.td>{{ $guide->name }}</x-table.td>
|
|
||||||
<x-table.td class="text-right text-indigo-600"><a href="/admin/scoring/guides/{{ $guide->id }}/edit">Edit</a></x-table.td>
|
|
||||||
{{-- TODO add a link to delete if the guide is not in use--}}
|
|
||||||
</tr>
|
|
||||||
@endforeach
|
|
||||||
</x-table.body>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
<x-form.form method="POST" action="/admin/scoring/guides" class="!px-0 !py-0">
|
|
||||||
<x-table.td>
|
|
||||||
<x-form.field name="name" label_text="Add New Scoring Guide" />
|
|
||||||
</x-table.td>
|
|
||||||
<x-table.td>
|
|
||||||
<x-form.button class="mt-6">Create</x-form.button>
|
|
||||||
</x-table.td>
|
|
||||||
</x-form.form>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</x-table.table>
|
|
||||||
</x-card.card>
|
|
||||||
</div>
|
</div>
|
||||||
</x-layout.app>
|
</x-layout.app>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
])
|
])
|
||||||
<div>
|
<div>
|
||||||
@if($with_title_area)
|
@if($with_title_area)
|
||||||
<div class="mb-4 mt-4 sm:flex sm:items-center">
|
<div class="mb-4 mt-4 sm:px 4 sm:flex sm:items-center">
|
||||||
<div class="sm:flex-auto sm:items-center">
|
<div class="sm:flex-auto sm:items-center">
|
||||||
@if($title)<h1 {{ $title->attributes->merge(['class' => 'text-base font-semibold leading-6 text-gray-900']) }}>{{ $title }}</h1>@endif
|
@if($title)<h1 {{ $title->attributes->merge(['class' => 'text-base font-semibold leading-6 text-gray-900']) }}>{{ $title }}</h1>@endif
|
||||||
@if($subtitle)<p {{ $subtitle->attributes->merge(['class' => 'mt-2 text-sm text-gray-700']) }}>{{ $subtitle }}</p>@endif
|
@if($subtitle)<p {{ $subtitle->attributes->merge(['class' => 'mt-2 text-sm text-gray-700']) }}>{{ $subtitle }}</p>@endif
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
@php use App\Models\Audition;use App\Models\School;use App\Models\SchoolEmailDomain;use App\Models\User;use App\Settings;use Illuminate\Support\Facades\Auth;use Illuminate\Support\Facades\Session; @endphp
|
@php use App\Models\Audition;use App\Models\School;use App\Models\SchoolEmailDomain;use App\Models\ScoringGuide;use App\Models\User;use App\Settings;use Illuminate\Support\Facades\Auth;use Illuminate\Support\Facades\DB;use Illuminate\Support\Facades\Session; @endphp
|
||||||
<x-layout.app>
|
<x-layout.app>
|
||||||
<x-slot:page_title>Test Page</x-slot:page_title>
|
<x-slot:page_title>Test Page</x-slot:page_title>
|
||||||
|
|
||||||
{{ Settings::auditionName() }}
|
@php
|
||||||
|
|
||||||
|
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
|
||||||
</x-layout.app>
|
</x-layout.app>
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::get('/test',[TestController::class,'flashTest'])->middleware('auth','verified');
|
Route::get('/test',[TestController::class,'flashTest'])->middleware('auth','verified');
|
||||||
|
|
||||||
|
Route::post('/admin/scoring/assign_guide_to_audition',[\App\Http\Controllers\Admin\AuditionController::class,'scoringGuideUpdate']);
|
||||||
|
|
||||||
Route::view('/','welcome')->middleware('guest');
|
Route::view('/','welcome')->middleware('guest');
|
||||||
|
|
||||||
// Admin Routes
|
// Admin Routes
|
||||||
|
|
@ -21,6 +23,7 @@ Route::middleware(['auth','verified',CheckIfAdmin::class])->prefix('admin/')->gr
|
||||||
Route::view('/','admin.dashboard');
|
Route::view('/','admin.dashboard');
|
||||||
Route::post('/auditions/roomUpdate',[\App\Http\Controllers\Admin\AuditionController::class,'roomUpdate']);
|
Route::post('/auditions/roomUpdate',[\App\Http\Controllers\Admin\AuditionController::class,'roomUpdate']);
|
||||||
|
|
||||||
|
|
||||||
// Rooms
|
// Rooms
|
||||||
Route::prefix('rooms')->controller(\App\Http\Controllers\Admin\RoomController::class)->group(function() {
|
Route::prefix('rooms')->controller(\App\Http\Controllers\Admin\RoomController::class)->group(function() {
|
||||||
Route::get('/','index');
|
Route::get('/','index');
|
||||||
|
|
@ -136,7 +139,6 @@ Route::prefix('filters')->middleware(['auth','verified'])->controller(FilterCont
|
||||||
Route::get('/admin_entry_filter/clear','clearAdminEntryFilter');
|
Route::get('/admin_entry_filter/clear','clearAdminEntryFilter');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
//Route::get('/my_school', [SchoolController::class, 'my_school'])->middleware('auth','verified');
|
//Route::get('/my_school', [SchoolController::class, 'my_school'])->middleware('auth','verified');
|
||||||
//Route::get('/schools/create', [SchoolController::class, 'create'])->middleware('auth','verified');
|
//Route::get('/schools/create', [SchoolController::class, 'create'])->middleware('auth','verified');
|
||||||
//Route::post('/schools', [SchoolController::class, 'store'])->middleware('auth','verified');
|
//Route::post('/schools', [SchoolController::class, 'store'])->middleware('auth','verified');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue