Programme sync to Meet rooms, wallet-billed mass comms, webhook lifecycle updates, and registration access bridge with join-window gating on public pages. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,19 +5,19 @@ namespace App\Http\Controllers\Events;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\QrCode;
|
||||
use App\Models\QrEventRegistration;
|
||||
use App\Services\Billing\SmsService;
|
||||
use App\Services\Events\EventEmailService;
|
||||
use App\Services\Events\EventCommsService;
|
||||
use App\Support\Events\EventBadgeZpl;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class AttendeeController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly SmsService $sms,
|
||||
private readonly EventEmailService $email,
|
||||
private readonly EventCommsService $comms,
|
||||
) {}
|
||||
|
||||
public function hub(Request $request): View
|
||||
@@ -133,7 +133,24 @@ class AttendeeController extends Controller
|
||||
'revenue' => $event->eventRegistrations()->where('status', QrEventRegistration::STATUS_CONFIRMED)->sum('amount_minor') / 100,
|
||||
];
|
||||
|
||||
$content = $event->content();
|
||||
$format = (string) ($content['format'] ?? 'in_person');
|
||||
$virtualSessions = collect((array) ($content['virtual_sessions'] ?? []))->filter(fn ($s) => is_array($s));
|
||||
|
||||
if (in_array($format, ['virtual', 'hybrid'], true) && $virtualSessions->isNotEmpty()) {
|
||||
$stats['virtual_sessions'] = $virtualSessions->count();
|
||||
$stats['virtual_live'] = $virtualSessions->where('meet_status', 'live')->count();
|
||||
$stats['virtual_ended'] = $virtualSessions->where('meet_status', 'ended')->count();
|
||||
$stats['virtual_recordings'] = $virtualSessions->filter(fn ($s) => ! empty($s['recording_url']))->count();
|
||||
}
|
||||
|
||||
$programme = $this->programmeFor($event);
|
||||
$joinUrl = $this->comms->primaryJoinUrl($event);
|
||||
$commsModes = array_values(array_filter([
|
||||
$programme ? EventCommsService::MODE_PROGRAMME : null,
|
||||
$joinUrl !== '' ? EventCommsService::MODE_JOIN : null,
|
||||
($programme && $joinUrl !== '') ? EventCommsService::MODE_BOTH : null,
|
||||
]));
|
||||
|
||||
return view('events.attendees', [
|
||||
'qrCode' => $event,
|
||||
@@ -141,60 +158,64 @@ class AttendeeController extends Controller
|
||||
'registrations' => $registrations,
|
||||
'stats' => $stats,
|
||||
'programme' => $programme,
|
||||
'commsModes' => $commsModes,
|
||||
'defaultCommsMode' => $commsModes[0] ?? EventCommsService::MODE_PROGRAMME,
|
||||
]);
|
||||
}
|
||||
|
||||
public function shareProgramme(QrCode $event): RedirectResponse
|
||||
public function previewComms(QrCode $event, Request $request): JsonResponse
|
||||
{
|
||||
$this->authorize('view', $event);
|
||||
abort_unless($event->type === QrCode::TYPE_EVENT, 404);
|
||||
|
||||
$programme = $this->programmeFor($event);
|
||||
if (! $programme) {
|
||||
return back()->with('error', 'No programme outline is attached to this event yet.');
|
||||
$mode = (string) $request->query('mode', EventCommsService::MODE_PROGRAMME);
|
||||
abort_unless(in_array($mode, [EventCommsService::MODE_PROGRAMME, EventCommsService::MODE_JOIN, EventCommsService::MODE_BOTH], true), 422);
|
||||
|
||||
return response()->json($this->comms->preview($event, $mode));
|
||||
}
|
||||
|
||||
public function shareComms(QrCode $event, Request $request): RedirectResponse
|
||||
{
|
||||
$this->authorize('view', $event);
|
||||
abort_unless($event->type === QrCode::TYPE_EVENT, 404);
|
||||
|
||||
$validated = $request->validate([
|
||||
'mode' => ['required', Rule::in([
|
||||
EventCommsService::MODE_PROGRAMME,
|
||||
EventCommsService::MODE_JOIN,
|
||||
EventCommsService::MODE_BOTH,
|
||||
])],
|
||||
]);
|
||||
|
||||
$preview = $this->comms->preview($event, $validated['mode']);
|
||||
if (! $preview['affordable']) {
|
||||
return back()->with('error', 'Insufficient wallet balance. Add funds in Billing before sending.');
|
||||
}
|
||||
|
||||
$recipients = $event->eventRegistrations()
|
||||
->where('status', QrEventRegistration::STATUS_CONFIRMED)
|
||||
->get();
|
||||
|
||||
$eventName = $event->content()['name'] ?? $event->label;
|
||||
$programmeUrl = $programme->publicUrl();
|
||||
$emailed = 0;
|
||||
$texted = 0;
|
||||
|
||||
foreach ($recipients as $reg) {
|
||||
if ($reg->attendee_email) {
|
||||
$this->email->sendProgrammeShare(
|
||||
(string) $event->user->public_id,
|
||||
$reg->attendee_email,
|
||||
$eventName,
|
||||
$programmeUrl,
|
||||
$reg->attendee_name,
|
||||
);
|
||||
$emailed++;
|
||||
}
|
||||
|
||||
if ($reg->attendee_phone) {
|
||||
$this->sms->send(
|
||||
(string) $event->user->public_id,
|
||||
$reg->attendee_phone,
|
||||
sprintf(
|
||||
'Hi %s, the programme for %s is here: %s',
|
||||
explode(' ', $reg->attendee_name ?? 'there')[0],
|
||||
$eventName,
|
||||
$programmeUrl
|
||||
)
|
||||
);
|
||||
$texted++;
|
||||
}
|
||||
if ($preview['recipients'] === 0) {
|
||||
return back()->with('error', 'No confirmed attendees to message yet.');
|
||||
}
|
||||
|
||||
if ($emailed === 0 && $texted === 0) {
|
||||
return back()->with('error', 'No confirmed attendees with contact details to share with yet.');
|
||||
$result = $this->comms->send($event, $validated['mode']);
|
||||
|
||||
if ($result['emailed'] === 0 && $result['texted'] === 0) {
|
||||
return back()->with('error', 'No messages were sent — check that attendees have email or phone on file.');
|
||||
}
|
||||
|
||||
return back()->with('success', "Programme shared — {$emailed} email(s) and {$texted} SMS sent.");
|
||||
return back()->with('success', sprintf(
|
||||
'Sent — %d email(s), %d SMS, %d skipped.',
|
||||
$result['emailed'],
|
||||
$result['texted'],
|
||||
$result['skipped'],
|
||||
));
|
||||
}
|
||||
|
||||
/** @deprecated Use shareComms with mode=programme */
|
||||
public function shareProgramme(QrCode $event): RedirectResponse
|
||||
{
|
||||
request()->merge(['mode' => EventCommsService::MODE_PROGRAMME]);
|
||||
|
||||
return $this->shareComms($event, request());
|
||||
}
|
||||
|
||||
private function programmeFor(QrCode $event): ?QrCode
|
||||
|
||||
Reference in New Issue
Block a user