Split attendees and badge printing; add a 3-step event create flow.
Deploy Ladill Events / deploy (push) Successful in 35s
Deploy Ladill Events / deploy (push) Successful in 35s
Give each area its own sidebar entry and hub pages, and walk new events through details, QR design, then review before publish. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
price: @js((float) $pricePerQr),
|
||||
topupUrl: @js($topupUrl),
|
||||
type: @js($createType),
|
||||
wizard: @js(! $isProgramme),
|
||||
})">
|
||||
|
||||
@if(session('error'))
|
||||
@@ -40,25 +41,53 @@
|
||||
{{ $isProgramme ? 'Programmes' : 'Events' }}
|
||||
</a>
|
||||
<h1 class="mt-2 text-2xl font-bold text-slate-900">{{ $isProgramme ? 'New programme' : 'Create event' }}</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">{{ $isProgramme ? 'Build a schedule outline and share it with attendees.' : 'Set up ticketing, registration, and your event QR.' }}</p>
|
||||
<p class="mt-1 text-sm text-slate-500">
|
||||
{{ $isProgramme ? 'Build a schedule outline and share it with attendees.' : 'Set up your event, then design the QR attendees will scan.' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@unless($isProgramme)
|
||||
{{-- Event creation wizard steps --}}
|
||||
<div class="rounded-2xl border border-slate-200 bg-white px-4 py-4 sm:px-6">
|
||||
<ol class="flex items-center justify-between gap-2">
|
||||
@foreach(['Event details', 'QR code', 'Review & create'] as $i => $label)
|
||||
@php $n = $i + 1; @endphp
|
||||
<li class="flex min-w-0 flex-1 items-center gap-2">
|
||||
<span class="flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-xs font-bold transition"
|
||||
:class="step >= {{ $n }} ? 'bg-indigo-600 text-white' : 'bg-slate-100 text-slate-400'">{{ $n }}</span>
|
||||
<span class="hidden truncate text-xs font-semibold sm:block"
|
||||
:class="step >= {{ $n }} ? 'text-slate-900' : 'text-slate-400'">{{ $label }}</span>
|
||||
@if($n < 3)
|
||||
<span class="mx-1 hidden h-px flex-1 bg-slate-200 sm:block"></span>
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ol>
|
||||
</div>
|
||||
@endunless
|
||||
|
||||
<form x-ref="createForm" action="{{ route('events.store') }}" method="POST" enctype="multipart/form-data"
|
||||
class="grid grid-cols-1 gap-8 lg:grid-cols-[380px_1fr]"
|
||||
@submit="submitOrTopup($event)">
|
||||
class="grid grid-cols-1 gap-8"
|
||||
:class="(!wizard || step >= 2) ? 'lg:grid-cols-[380px_1fr]' : ''"
|
||||
@submit="wizard ? wizardSubmit($event) : submitOrTopup($event)">
|
||||
@csrf
|
||||
<input type="hidden" name="type" value="{{ $createType }}">
|
||||
|
||||
{{-- Left: Sticky preview (desktop only) --}}
|
||||
<div class="hidden lg:block lg:sticky lg:top-6 lg:self-start">
|
||||
@include('qr-codes.partials.qr-preview-card', ['showDownloads' => false])
|
||||
{{-- QR preview (step 2 for events, always for programmes) --}}
|
||||
<div class="lg:sticky lg:top-6 lg:self-start"
|
||||
x-show="!wizard || step === 2 || step === 3"
|
||||
x-cloak
|
||||
:class="(!wizard || step >= 2) ? 'block' : 'hidden'">
|
||||
<div class="hidden lg:block">
|
||||
@include('qr-codes.partials.qr-preview-card', ['showDownloads' => false])
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Right: Controls --}}
|
||||
<div class="space-y-5 pb-4 lg:pb-0">
|
||||
<div class="space-y-5 pb-4 lg:pb-0"
|
||||
:class="wizard && step >= 2 ? 'lg:col-start-2' : (wizard && step === 1 ? 'lg:col-span-full' : '')">
|
||||
|
||||
{{-- Content first --}}
|
||||
<div class="overflow-hidden rounded-2xl border border-slate-200/80 bg-white shadow-sm">
|
||||
{{-- Step 1 / Programme: Event details --}}
|
||||
<div x-show="!wizard || step === 1" class="overflow-hidden rounded-2xl border border-slate-200/80 bg-white shadow-sm">
|
||||
<div class="border-b border-slate-100 bg-slate-50/70 px-5 py-4">
|
||||
<h2 class="text-sm font-semibold text-slate-900">{{ $isProgramme ? 'Programme details' : 'Event details' }}</h2>
|
||||
<p class="mt-0.5 text-xs text-slate-400">{{ $isProgramme ? 'Title, schedule, and venue for this outline.' : 'Name, dates, ticketing, and registration settings.' }}</p>
|
||||
@@ -72,7 +101,6 @@
|
||||
placeholder="{{ $isProgramme ? 'e.g. Annual conference programme' : 'e.g. Tech summit 2026' }}">
|
||||
</div>
|
||||
|
||||
{{-- Custom link --}}
|
||||
<div x-data="{
|
||||
slug: @js(old('custom_short_code', '')),
|
||||
status: '',
|
||||
@@ -109,27 +137,16 @@
|
||||
placeholder="my-brand">
|
||||
</div>
|
||||
<div class="mt-1.5 flex items-center gap-1.5 text-[11px]">
|
||||
<template x-if="status === 'checking'">
|
||||
<span class="text-slate-400">Checking…</span>
|
||||
</template>
|
||||
<template x-if="status === 'checking'"><span class="text-slate-400">Checking…</span></template>
|
||||
<template x-if="status === 'available'">
|
||||
<span class="flex items-center gap-1 text-green-600">
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/></svg>
|
||||
Available — your link will be <span x-text="baseUrl + '/' + slug.trim()" class="font-medium text-green-700"></span>
|
||||
Available
|
||||
</span>
|
||||
</template>
|
||||
<template x-if="status === 'taken'">
|
||||
<span class="flex items-center gap-1 text-red-500">
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12"/></svg>
|
||||
Already taken — choose another
|
||||
</span>
|
||||
</template>
|
||||
<template x-if="status === 'invalid'">
|
||||
<span class="text-red-500">3–20 chars, letters, numbers and hyphens only (no leading/trailing hyphens)</span>
|
||||
</template>
|
||||
<template x-if="status === ''">
|
||||
<span class="text-slate-400">Leave blank to auto-generate</span>
|
||||
</template>
|
||||
<template x-if="status === 'taken'"><span class="text-red-500">Already taken</span></template>
|
||||
<template x-if="status === 'invalid'"><span class="text-red-500">Invalid format</span></template>
|
||||
<template x-if="status === ''"><span class="text-slate-400">Leave blank to auto-generate</span></template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -137,28 +154,72 @@
|
||||
@include('qr-codes.partials.type-fields-create')
|
||||
</div>
|
||||
|
||||
@unless($isProgramme)
|
||||
<p class="rounded-xl bg-slate-50 px-3.5 py-2.5 text-xs leading-5 text-slate-400">
|
||||
You will design the QR code in the next step. Details can be updated anytime without reprinting.
|
||||
</p>
|
||||
@else
|
||||
<p class="rounded-xl bg-slate-50 px-3.5 py-2.5 text-xs leading-5 text-slate-400">
|
||||
Attendees scan your Ladill event link — update details anytime without reprinting the QR.
|
||||
</p>
|
||||
@endunless
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Customization --}}
|
||||
@include('qr-codes.partials.customization-fields', [
|
||||
'style' => $defaultStyle,
|
||||
'moduleStyles' => $moduleStyles,
|
||||
'cornerOuterStyles' => $cornerOuterStyles,
|
||||
'cornerInnerStyles' => $cornerInnerStyles,
|
||||
'frameStyles' => $frameStyles,
|
||||
])
|
||||
{{-- Step 2: QR customization (events) or inline (programmes) --}}
|
||||
<div x-show="!wizard || step === 2">
|
||||
@include('qr-codes.partials.customization-fields', [
|
||||
'style' => $defaultStyle,
|
||||
'moduleStyles' => $moduleStyles,
|
||||
'cornerOuterStyles' => $cornerOuterStyles,
|
||||
'cornerInnerStyles' => $cornerInnerStyles,
|
||||
'frameStyles' => $frameStyles,
|
||||
])
|
||||
</div>
|
||||
|
||||
{{-- Desktop submit button --}}
|
||||
<button type="button"
|
||||
@click="submitOrTopup($event)"
|
||||
class="hidden lg:block w-full rounded-xl bg-indigo-600 py-3 text-sm font-semibold text-white shadow-sm transition hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
|
||||
{{ $isProgramme ? 'Create programme' : 'Create event' }}
|
||||
</button>
|
||||
{{-- Step 3: Review & create (events only) --}}
|
||||
<div x-show="wizard && step === 3" x-cloak class="space-y-5">
|
||||
<div class="overflow-hidden rounded-2xl border border-slate-200/80 bg-white shadow-sm">
|
||||
<div class="border-b border-slate-100 bg-slate-50/70 px-5 py-4">
|
||||
<h2 class="text-sm font-semibold text-slate-900">Review your event</h2>
|
||||
<p class="mt-0.5 text-xs text-slate-400">Confirm details before publishing. GHS {{ number_format($pricePerQr, 2) }} will be charged from your QR balance.</p>
|
||||
</div>
|
||||
<div class="space-y-3 p-5 text-sm">
|
||||
<div class="flex justify-between gap-4 border-b border-slate-100 pb-3">
|
||||
<span class="text-slate-500">Event</span>
|
||||
<span class="font-semibold text-slate-900 text-right" x-text="reviewEventName()"></span>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4 border-b border-slate-100 pb-3">
|
||||
<span class="text-slate-500">Admin label</span>
|
||||
<span class="font-semibold text-slate-900 text-right" x-text="reviewLabel()"></span>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<span class="text-slate-500">QR balance after</span>
|
||||
<span class="font-semibold text-slate-900">GHS <span x-text="Math.max(0, balance - price).toFixed(2)"></span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lg:hidden">
|
||||
@include('qr-codes.partials.qr-preview-card', ['showDownloads' => false])
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Desktop navigation --}}
|
||||
<div class="hidden gap-3 lg:flex">
|
||||
<button type="button" x-show="wizard && step > 1" x-cloak @click="prevStep()"
|
||||
class="rounded-xl border border-slate-200 px-5 py-3 text-sm font-semibold text-slate-600 hover:bg-slate-50 transition">
|
||||
Back
|
||||
</button>
|
||||
<button type="button" x-show="wizard && step < 3" x-cloak @click="nextStep()"
|
||||
class="flex-1 rounded-xl bg-indigo-600 py-3 text-sm font-semibold text-white shadow-sm transition hover:bg-indigo-700">
|
||||
Continue
|
||||
</button>
|
||||
<button type="button" x-show="!wizard || step === 3" @click="wizardSubmit($event)"
|
||||
class="flex-1 rounded-xl bg-indigo-600 py-3 text-sm font-semibold text-white shadow-sm transition hover:bg-indigo-700">
|
||||
{{ $isProgramme ? 'Create programme' : 'Create event' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -166,8 +227,11 @@
|
||||
<div x-show="!showPreviewModal"
|
||||
class="mobile-action-bar lg:hidden">
|
||||
<div class="mobile-action-bar__toolbar">
|
||||
<button type="button"
|
||||
@click="showPreviewModal = true"
|
||||
<button type="button" x-show="wizard && step > 1" x-cloak @click="prevStep()"
|
||||
class="rounded-xl border border-slate-200 px-4 py-2.5 text-sm font-semibold text-slate-600">
|
||||
Back
|
||||
</button>
|
||||
<button type="button" x-show="wizard && (step === 2 || step === 3)" @click="showPreviewModal = true"
|
||||
class="flex flex-1 items-center justify-center gap-2 rounded-xl border border-slate-200 bg-slate-50 py-2.5 text-sm font-semibold text-slate-700 transition hover:bg-slate-100">
|
||||
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z"/>
|
||||
@@ -175,8 +239,11 @@
|
||||
</svg>
|
||||
Preview
|
||||
</button>
|
||||
<button type="button"
|
||||
@click="submitOrTopup($event)"
|
||||
<button type="button" x-show="wizard && step < 3" x-cloak @click="nextStep()"
|
||||
class="flex flex-1 items-center justify-center gap-2 rounded-xl bg-indigo-600 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-indigo-700">
|
||||
Continue
|
||||
</button>
|
||||
<button type="button" x-show="!wizard || step === 3" @click="wizardSubmit($event)"
|
||||
class="flex flex-1 items-center justify-center gap-2 rounded-xl bg-indigo-600 py-2.5 text-sm font-semibold text-white shadow-sm transition hover:bg-indigo-700">
|
||||
Create
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user