phase 5
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
return ['name' => 'Events'];
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema;
|
||||
return new class extends Migration { public function up(): void { Schema::create('event_venues',function(Blueprint $t): void{$t->id();$t->string('name');$t->text('address')->nullable();$t->unsignedInteger('capacity')->nullable();$t->timestamps();}); Schema::create('events',function(Blueprint $t): void{$t->id();$t->foreignId('venue_id')->nullable()->constrained('event_venues')->nullOnDelete();$t->string('title');$t->string('slug')->unique();$t->text('summary')->nullable();$t->longText('description')->nullable();$t->timestamp('starts_at')->index();$t->timestamp('ends_at')->nullable();$t->string('status')->default('draft')->index();$t->unsignedInteger('capacity')->nullable();$t->decimal('price',10,2)->default(0);$t->boolean('registration_open')->default(false);$t->timestamps();}); Schema::create('event_registrations',function(Blueprint $t): void{$t->id();$t->foreignId('event_id')->constrained()->cascadeOnDelete();$t->string('name');$t->string('email')->index();$t->unsignedInteger('quantity')->default(1);$t->string('status')->default('confirmed')->index();$t->text('notes')->nullable();$t->timestamps();}); } public function down(): void { Schema::dropIfExists('event_registrations');Schema::dropIfExists('events');Schema::dropIfExists('event_venues'); } };
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
namespace Modules\Events\Http\Controllers; use App\Http\Controllers\Controller; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\View\View; use Modules\Events\Models\Event; use Modules\Events\Models\Venue;
|
||||
final class EventController extends Controller { public function index(): View { return view('events::admin.index',['events'=>Event::with(['venue'])->withCount('registrations')->orderBy('starts_at')->paginate(20),'venues'=>Venue::orderBy('name')->get()]); } public function store(Request $r): RedirectResponse { Event::create($r->validate(['venue_id'=>['nullable','exists:event_venues,id'],'title'=>['required','string','max:255'],'slug'=>['required','string','max:255','unique:events,slug'],'summary'=>['nullable','string'],'description'=>['nullable','string'],'starts_at'=>['required','date'],'ends_at'=>['nullable','date','after:starts_at'],'status'=>['required','in:draft,published,cancelled,completed'],'capacity'=>['nullable','integer','min:1'],'price'=>['required','numeric','min:0']])+['registration_open'=>$r->boolean('registration_open')]); return back()->with('status','Event created.'); } public function destroy(Event $event): RedirectResponse { $event->delete(); return back()->with('status','Event deleted.'); } }
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
namespace Modules\Events\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
final class Event extends Model { protected $fillable=['venue_id','title','slug','summary','description','starts_at','ends_at','status','capacity','price','registration_open']; protected function casts(): array { return ['starts_at'=>'datetime','ends_at'=>'datetime','price'=>'decimal:2','registration_open'=>'boolean']; } public function venue(): BelongsTo { return $this->belongsTo(Venue::class); } public function registrations(): HasMany { return $this->hasMany(Registration::class); } }
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
namespace Modules\Events\Models; use Illuminate\Database\Eloquent\Model;
|
||||
final class Registration extends Model { protected $table='event_registrations'; protected $fillable=['event_id','name','email','quantity','status','notes']; }
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
namespace Modules\Events\Models; use Illuminate\Database\Eloquent\Model;
|
||||
final class Venue extends Model { protected $table='event_venues'; protected $fillable=['name','address','capacity']; }
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace Modules\Events\Providers;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
final class EventsServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->register(RouteServiceProvider::class);
|
||||
$this->mergeConfigFrom(module_path('Events', 'Config/config.php'), 'events');
|
||||
}
|
||||
public function boot(): void
|
||||
{
|
||||
$this->loadMigrationsFrom(module_path('Events', 'Database/Migrations'));
|
||||
$this->loadViewsFrom(module_path('Events', 'Resources/views'), 'events');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace Modules\Events\Providers;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
final class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
Route::middleware('web')->group(module_path('Events', 'Routes/web.php'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<x-layouts.app :title="__('Events')"><div class="space-y-8"><div><h1 class="text-2xl font-semibold">Events</h1><p class="text-sm text-zinc-500">Manage events, venues and registrations.</p></div>@if(session('status'))<div class="rounded border border-green-300 bg-green-50 p-3 text-green-800">{{ session('status') }}</div>@endif<form method="POST" action="{{ route('administration.events.store') }}" class="space-y-4 rounded-xl border p-5">@csrf<h2 class="font-semibold">Create event</h2><div class="grid gap-4 md:grid-cols-2"><flux:input name="title" label="Title" required/><flux:input name="slug" label="Slug" required/><flux:input type="datetime-local" name="starts_at" label="Starts" required/><flux:input type="datetime-local" name="ends_at" label="Ends"/><flux:input type="number" min="0" step="0.01" name="price" label="Price" value="0" required/><flux:input type="number" min="1" name="capacity" label="Capacity"/><flux:select name="status" label="Status"><option value="draft">Draft</option><option value="published">Published</option><option value="cancelled">Cancelled</option></flux:select></div><flux:textarea name="summary" label="Summary"/><flux:checkbox name="registration_open" value="1" label="Registration open"/><flux:button type="submit" variant="primary">Create event</flux:button></form><div class="overflow-hidden rounded-xl border"><table class="w-full text-sm"><thead class="bg-zinc-50"><tr><th class="p-3 text-left">Event</th><th class="p-3 text-left">Starts</th><th class="p-3 text-left">Status</th><th class="p-3 text-left">Registrations</th><th class="p-3"></th></tr></thead><tbody>@forelse($events as $event)<tr class="border-t"><td class="p-3 font-medium">{{ $event->title }}</td><td class="p-3">{{ $event->starts_at->format('j M Y H:i') }}</td><td class="p-3">{{ ucfirst($event->status) }}</td><td class="p-3">{{ $event->registrations_count }}</td><td class="p-3 text-right"><form method="POST" action="{{ route('administration.events.destroy',$event) }}">@csrf @method('DELETE')<flux:button size="sm" variant="danger" type="submit">Delete</flux:button></form></td></tr>@empty<tr><td colspan="5" class="p-6 text-center text-zinc-500">No events yet.</td></tr>@endforelse</tbody></table></div>{{ $events->links() }}</div></x-layouts.app>
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
use Illuminate\Support\Facades\Route; use Modules\Events\Http\Controllers\EventController;
|
||||
Route::middleware(['auth','verified'])->prefix('administration/events')->name('administration.events.')->group(function(): void { Route::get('/',[EventController::class,'index'])->name('index'); Route::post('/',[EventController::class,'store'])->name('store'); Route::delete('/{event}',[EventController::class,'destroy'])->name('destroy'); });
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace Modules\Events\Tests\Feature;
|
||||
use Tests\TestCase;
|
||||
final class ModuleBootTest extends TestCase
|
||||
{
|
||||
public function test_module_configuration_is_available(): void
|
||||
{
|
||||
$this->assertSame('Events', config('events.name'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "Events",
|
||||
"alias": "events",
|
||||
"version": "1.0.0",
|
||||
"description": "Events, venues and registration management.",
|
||||
"keywords": ["business", "events"],
|
||||
"priority": 42,
|
||||
"providers": ["Modules\\Events\\Providers\\EventsServiceProvider"],
|
||||
"aliases": {},
|
||||
"files": [],
|
||||
"requires": ["Core", "Administration"]
|
||||
}
|
||||
Reference in New Issue
Block a user