From dac095ef92359caf377a2c9e0f01b287c8ebe83a Mon Sep 17 00:00:00 2001 From: isaacclad Date: Sat, 4 Jul 2026 12:39:11 +0000 Subject: [PATCH] Align programme flow with Events: system-generated QR, no customizer. Replace the QR-first create and edit experience with simple programme forms that auto-generate Ladill Link codes like events do. Co-authored-by: Cursor --- .../Events/ProgrammeController.php | 8 ++ app/Http/Controllers/Qr/QrCodeController.php | 14 +++ app/Services/Qr/QrCodeManagerService.php | 2 +- .../views/events/programme-create.blade.php | 75 ++++++++++++++++ .../views/events/programme-show.blade.php | 90 +++++++++++++++++++ resources/views/layouts/user.blade.php | 5 +- routes/web.php | 2 +- tests/Feature/ProgrammeCreateTest.php | 42 +++++++++ 8 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 resources/views/events/programme-create.blade.php create mode 100644 resources/views/events/programme-show.blade.php create mode 100644 tests/Feature/ProgrammeCreateTest.php diff --git a/app/Http/Controllers/Events/ProgrammeController.php b/app/Http/Controllers/Events/ProgrammeController.php index bc34fef..b842765 100644 --- a/app/Http/Controllers/Events/ProgrammeController.php +++ b/app/Http/Controllers/Events/ProgrammeController.php @@ -4,12 +4,20 @@ namespace App\Http\Controllers\Events; use App\Http\Controllers\Controller; use App\Models\QrCode; +use App\Models\QrWallet; use App\Support\Qr\QrTypeCatalog; use Illuminate\Http\Request; use Illuminate\View\View; class ProgrammeController extends Controller { + public function create(): View + { + return view('events.programme-create', [ + 'pricePerQr' => QrWallet::pricePerQr(), + ]); + } + public function index(Request $request): View { $account = ladill_account(); diff --git a/app/Http/Controllers/Qr/QrCodeController.php b/app/Http/Controllers/Qr/QrCodeController.php index 313b3f8..6dcee50 100644 --- a/app/Http/Controllers/Qr/QrCodeController.php +++ b/app/Http/Controllers/Qr/QrCodeController.php @@ -89,6 +89,12 @@ class QrCodeController extends Controller ]); } + if ($requestedType === QrCode::TYPE_ITINERARY) { + return view('events.programme-create', [ + 'pricePerQr' => QrWallet::pricePerQr(), + ]); + } + return view('qr-codes.create', [ 'wallet' => $wallet, 'requestedType' => $requestedType, @@ -230,6 +236,14 @@ class QrCodeController extends Controller ]); } + if ($qrCode->type === QrCode::TYPE_ITINERARY) { + return view('events.programme-show', [ + 'qrCode' => $qrCode, + 'previewDataUri' => $previewDataUri, + 'linkedEventSpeakers' => app(\App\Services\Events\EventProgrammeService::class)->speakerOptionsForProgramme($qrCode), + ]); + } + return view('qr-codes.show', [ 'qrCode' => $qrCode, 'previewDataUri' => $previewDataUri, diff --git a/app/Services/Qr/QrCodeManagerService.php b/app/Services/Qr/QrCodeManagerService.php index c957ffe..b8404eb 100644 --- a/app/Services/Qr/QrCodeManagerService.php +++ b/app/Services/Qr/QrCodeManagerService.php @@ -45,7 +45,7 @@ class QrCodeManagerService $type = (string) ($data['type'] ?? ''); $validated = $this->payloadValidator->validateForCreate($type, $data); $styleInput = $data['style'] ?? null; - if ($type === QrCode::TYPE_EVENT && empty(array_filter((array) $styleInput))) { + if (in_array($type, [QrCode::TYPE_EVENT, QrCode::TYPE_ITINERARY], true) && empty(array_filter((array) $styleInput))) { $styleInput = $user->getOrCreateQrSetting()->resolvedDefaultStyle(); } $style = QrStyleDefaults::merge($styleInput); diff --git a/resources/views/events/programme-create.blade.php b/resources/views/events/programme-create.blade.php new file mode 100644 index 0000000..dbd0222 --- /dev/null +++ b/resources/views/events/programme-create.blade.php @@ -0,0 +1,75 @@ + + New programme + +
+
+ + + Programmes + +

New programme

+

Build a schedule outline you can attach to events and share with attendees. A Ladill Link QR code is generated automatically.

+
+ + @if(session('error')) +
{{ session('error') }}
+ @endif + @if($errors->any()) +
+
    + @foreach($errors->all() as $error)
  • {{ $error }}
  • @endforeach +
+
+ @endif + +
+ @csrf + + +
+ + +

Private name to find this programme in your dashboard.

+
+ +
+ +
+ {{ \App\Support\LadillLink::baseUrl() }}/ + +
+

Leave blank to auto-generate your Ladill Link.

+
+ + @include('qr-codes.partials.type-fields-create') + +

+ GHS {{ number_format($pricePerQr, 2) }} will be charged from your wallet when you create this programme. +

+ + +
+
+
diff --git a/resources/views/events/programme-show.blade.php b/resources/views/events/programme-show.blade.php new file mode 100644 index 0000000..2dfb30c --- /dev/null +++ b/resources/views/events/programme-show.blade.php @@ -0,0 +1,90 @@ + + {{ $qrCode->label }} + @php + $content = $qrCode->content(); + $publicUrl = \App\Support\LadillLink::url($qrCode->short_code); + @endphp + +
+
+
+ + + Programmes + +

{{ $content['title'] ?? $qrCode->label }}

+

+ Programme + @if($qrCode->is_active) + ● Live + @else + ● Inactive + @endif +

+
+ + View programme page + + +
+ + @foreach(['success', 'error'] as $flash) + @if(session($flash)) +
+ {{ session($flash) }} +
+ @endif + @endforeach + @if($errors->any()) +
+
    @foreach($errors->all() as $error)
  • {{ $error }}
  • @endforeach
+
+ @endif + +
+
+
+ @if($previewDataUri) + QR code + @endif +

{{ $publicUrl }}

+
+ @foreach(['png' => 'PNG', 'svg' => 'SVG', 'pdf' => 'PDF'] as $fmt => $flabel) + {{ $flabel }} + @endforeach +
+

System-generated QR code. Customize colors, logos, and frames in QR Plus if needed.

+
+
+ +
+ @csrf @method('PATCH') + +
+ + +
+ + + + @include('qr-codes.partials.type-fields-edit', ['linkedEventSpeakers' => $linkedEventSpeakers ?? []]) + + +
+
+
+
diff --git a/resources/views/layouts/user.blade.php b/resources/views/layouts/user.blade.php index aae3fcc..d0fa125 100644 --- a/resources/views/layouts/user.blade.php +++ b/resources/views/layouts/user.blade.php @@ -1,10 +1,11 @@ @php $mobileFullScreenPage = request()->routeIs('account.settings') || request()->routeIs('events.create') + || request()->routeIs('programmes.create') || request()->routeIs('events.show') || request()->routeIs('events.search'); - $qrMobilePage = request()->routeIs('events.create') || request()->routeIs('events.show'); + $qrMobilePage = request()->routeIs('events.create') || request()->routeIs('programmes.create') || request()->routeIs('events.show'); @endphp $qrMobilePage])> @@ -55,7 +56,7 @@ {{-- Mobile Bottom Navigation (hidden on QR create/show which have their own action bar) --}} - @unless(request()->routeIs('events.create') || request()->routeIs('events.show')) + @unless(request()->routeIs('events.create') || request()->routeIs('programmes.create') || request()->routeIs('events.show')) @php $navUser = auth()->user(); $navInitials = collect(explode(' ', trim((string) $navUser?->name))) diff --git a/routes/web.php b/routes/web.php index 6cedb1b..6486d05 100644 --- a/routes/web.php +++ b/routes/web.php @@ -91,7 +91,7 @@ Route::middleware(['auth', 'platform.session'])->group(function () { Route::post('/events/{event}/attendees/share-comms', [AttendeeController::class, 'shareComms'])->name('events.attendees.share-comms'); Route::get('/programmes', [ProgrammeController::class, 'index'])->name('programmes.index'); - Route::get('/programmes/create', fn () => redirect()->route('events.create', ['type' => 'itinerary']))->name('programmes.create'); + Route::get('/programmes/create', [ProgrammeController::class, 'create'])->name('programmes.create'); Route::get('/speakers', [SpeakerController::class, 'hub'])->name('speakers.index'); Route::get('/events/{event}/speakers', [SpeakerController::class, 'index'])->name('speakers.show'); diff --git a/tests/Feature/ProgrammeCreateTest.php b/tests/Feature/ProgrammeCreateTest.php new file mode 100644 index 0000000..96c3e6e --- /dev/null +++ b/tests/Feature/ProgrammeCreateTest.php @@ -0,0 +1,42 @@ +withoutMiddleware(\App\Http\Middleware\EnsurePlatformSession::class); + + Http::fake([ + config('billing.api_url').'/balance*' => Http::response(['balance_minor' => 50000]), + ]); + } + + public function test_programme_create_page_has_no_qr_customizer(): void + { + $user = User::create([ + 'public_id' => 'usr_prog_create', + 'name' => 'Host', + 'email' => 'host-prog@example.com', + ]); + + $this->actingAs($user) + ->get(route('programmes.create')) + ->assertOk() + ->assertSee('New programme') + ->assertSee('Create programme') + ->assertDontSee('qrCustomizer') + ->assertDontSee('customization-fields') + ->assertDontSee('Design your QR'); + } +}