Add kiosk join/leave pages, waiting room, and meeting feedback.
Deploy Ladill Meet / deploy (push) Successful in 47s

Redesign public join flows on the Frontdesk kiosk template, enforce host
admission when waiting room is enabled, and collect star ratings after leave.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-01 13:33:48 +00:00
co-authored by Cursor
parent 5e34d71de0
commit 7ae26a467c
26 changed files with 865 additions and 124 deletions
+96 -4
View File
@@ -3,11 +3,14 @@
namespace App\Http\Controllers\Meet;
use App\Http\Controllers\Controller;
use App\Models\Participant;
use App\Models\Room;
use App\Models\Session;
use App\Services\Meet\InvitationService;
use App\Services\Meet\SecurityPolicyService;
use App\Services\Meet\SessionService;
use App\Services\Meet\WebinarService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
@@ -23,8 +26,13 @@ class JoinController extends Controller
public function show(Request $request, Room $room): View|RedirectResponse
{
$room->loadMissing('organization');
if ($room->status === 'cancelled') {
return view('meet.join.cancelled', compact('room'));
return view('meet.join.cancelled', [
'room' => $room,
'organization' => $room->organization,
]);
}
if ($room->isWebinar() && $request->query('token')) {
@@ -33,15 +41,39 @@ class JoinController extends Controller
[$allowed, $reason] = $this->security->canJoin($room, $request->user(), $request->user()?->email);
if (! $allowed && $reason !== 'passcode_required') {
return view('meet.join.denied', compact('room', 'reason'));
return view('meet.join.denied', [
'room' => $room,
'organization' => $room->organization,
'reason' => $reason,
]);
}
if ($room->passcode && ! $request->session()->get("meet.passcode.{$room->uuid}")) {
return view('meet.join.passcode', compact('room'));
return view('meet.join.passcode', [
'room' => $room,
'organization' => $room->organization,
]);
}
$session = $room->activeSession();
if ($session) {
$participantUuid = $request->session()->get("meet.participant.{$session->uuid}");
$participant = $participantUuid
? Participant::where('uuid', $participantUuid)->where('session_id', $session->id)->first()
: null;
if ($participant?->status === 'waiting') {
return redirect()->route('meet.join.waiting', $room);
}
if ($participant?->status === 'joined') {
return redirect()->route('meet.room', $session);
}
}
return view('meet.join.show', [
'room' => $room,
'organization' => $room->organization,
'user' => $request->user(),
'isWebinar' => $room->isWebinar(),
]);
@@ -130,12 +162,72 @@ class JoinController extends Controller
$displayName = $reg->display_name;
}
$participant = $this->sessions->joinParticipant($session, $user, $role, $displayName, $email);
$joinStatus = $room->requiresWaitingRoom($user, $role) ? 'waiting' : 'joined';
$participant = $this->sessions->joinParticipant($session, $user, $role, $displayName, $email, $joinStatus);
app(InvitationService::class)->markAcceptedOnJoin($room, $user, $email);
$this->sessions->rememberParticipantSession($request, $participant, $session);
if ($participant->status === 'waiting') {
return redirect()->route('meet.join.waiting', $room);
}
return redirect()->route('meet.room', $session);
}
public function waiting(Request $request, Room $room): View|RedirectResponse
{
$room->loadMissing('organization');
$session = $room->activeSession();
abort_unless($session?->isLive(), 404);
$participant = $this->participantFromSession($request, $session);
if (! $participant || $participant->status === 'removed') {
return redirect()->route('meet.join', $room);
}
if ($participant->status === 'joined') {
return redirect()->route('meet.room', $session);
}
return view('meet.join.waiting', [
'room' => $room,
'session' => $session,
'organization' => $room->organization,
'participant' => $participant,
]);
}
public function waitingStatus(Request $request, Room $room): JsonResponse
{
$session = $room->activeSession();
abort_unless($session, 404);
$participant = $this->participantFromSession($request, $session);
return response()->json([
'status' => $participant?->status ?? 'unknown',
'redirect' => ($participant && $participant->status === 'joined')
? route('meet.room', $session)
: null,
'denied' => $participant?->status === 'removed',
]);
}
protected function participantFromSession(Request $request, Session $session): ?Participant
{
$participantUuid = $request->session()->get("meet.participant.{$session->uuid}");
if (! $participantUuid) {
return null;
}
return Participant::query()
->where('uuid', $participantUuid)
->where('session_id', $session->id)
->first();
}
}