feat(events): implement qr code check-in for event enrollments (#237)#298
Open
YuriSouzaDev wants to merge 7 commits into
Open
feat(events): implement qr code check-in for event enrollments (#237)#298YuriSouzaDev wants to merge 7 commits into
YuriSouzaDev wants to merge 7 commits into
Conversation
… inscrição - Adiciona GenerateQrTokenAction: gera token único URL-safe de 64 chars por enrollment, idempotente — retorna token existente se já criado - Adiciona QrCheckInAction: valida token, enrollment e data do evento antes de delegar ao CheckInAction existente (method=qr_code, payload com token) - Adiciona QrCheckInDTO com validações de token e ator no construtor - Adiciona GenerateQrTokenOnConfirmed listener para o evento EnrollmentConfirmed - Adiciona exceções qrTokenNotFound e qrTokenExpired em CheckInException - Adiciona strings de tradução (en/pt_BR) para os novos erros de check-in QR
…erviceProvider Conecta EnrollmentConfirmed → GenerateQrTokenOnConfirmed para geração automática do token QR quando a inscrição é confirmada.
…ção de evento - Botão "Scan QR" no cabeçalho do EditEvent abre modal com campo de token - Executa QrCheckInAction e exibe notificação de sucesso com nome do participante - Em caso de erro (CheckInException ou Throwable), exibe notificação de erro - Modal reabre automaticamente via ->after() para leituras contínuas sem fechar - Botão de submit renomeado para "Check In" para evitar ambiguidade com confirmações
…sença hoje - EventDetail: adiciona computed qrToken (visível apenas com método QrCode e status confirmed/checked_in), qrCodeSvg via BaconQrCode, checkIns (histórico ordenado) e hasCheckedInToday para badge condicional - View: seção "Meu QR Code" com SVG inline, botões de copiar token e baixar SVG - Badge "Check-in feito hoje" no cabeçalho do card quando há check-in no dia atual - Seção de histórico de check-ins lista datas confirmadas abaixo do QR - Inscrição sem check-in ainda exibe instruções sem histórico - Adiciona strings de tradução en/pt_BR para histórico e badge
… QR e check-in Cobre todos os critérios de aceite da task he4rt#237: - Geração de token único por enrollment - Idempotência: segunda chamada retorna o mesmo token - Scan válido: cria CheckIn, atualiza status e dispara ParticipantCheckedIn - Token inexistente rejeitado - Token de outro evento rejeitado - Token expirado rejeitado - Scan duplicado no mesmo dia rejeitado - Token de enrollment cancelado rejeitado - Reutilização em dias diferentes cria check-ins distintos - Listener gera token automaticamente via EnrollUserAction (integração)
`mountAction` chamado em `->after()` era desfeito pelo `unmountAction` do lifecycle do Filament logo em seguida. A solução despacha um evento Livewire (`reopen-scan-qr`) dentro do `finally` da action, que é processado num request separado — após o lifecycle atual completar — e reabre o modal via `#[On]` sem conflito. Também adiciona `autofocus` no campo token para o cursor já estar posicionado a cada reabertura.
GabrielFVDev
approved these changes
Jun 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements end-to-end QR code check-in for community events. When an enrollment reaches
confirmedstatus, the system automatically generates a unique QR token. Organizers scan tokens via the admin panel to check participants in; participants view and share their QR code from the app panel.Closes #237 — blocked on #240 (RSVP enrollment, already merged).
What changed
New dependency
bacon/bacon-qr-code— SVG QR generation viaBaconQrCode\Writer+SvgRenderer. Chosen oversimplesoftwareio/simple-qrcodedue to PHP 8.4 GD extension incompatibility and Filament auto-discovery conflicts.Core —
app-modules/events| File | Role |
|---|---|
|
database/migrations/…create_events_qr_tokens_table.php|events_qr_tokenstable:enrollment_id,token(unique),expires_at(nullable) ||
src/CheckIn/Actions/GenerateQrTokenAction.php| Creates token (64-char URL-safe hex viabin2hex(random_bytes(32))). Idempotent — skips if token already exists. ||
src/CheckIn/Actions/QrCheckInAction.php| Validates token: exists, belongs to event enrollment, enrollment isconfirmed/checked_in, not expired, not duplicate for today. Delegates to existingCheckInActionwithmethod=qr_code. ||
src/CheckIn/DTOs/QrCheckInDTO.php| Input DTO:token,event_date||
src/CheckIn/Listeners/GenerateQrTokenOnConfirmed.php| Listens toEnrollmentConfirmeddomain event; dispatchesGenerateQrTokenAction. ImplementsShouldDispatchAfterCommitfor transaction safety. ||
src/EventsServiceProvider.php| RegistersEnrollmentConfirmed → GenerateQrTokenOnConfirmedlistener ||
src/CheckIn/Exceptions/CheckInException.php| AddsqrTokenNotFound,qrTokenExpiredfactory methods ||
lang/en/check_in.php,lang/pt_BR/check_in.php| i18n strings for new error states |Admin panel —
app-modules/panel-adminEditEventresource: new Scan QR header action with a continuous-scan modal. After each scan the modal auto-reopens for rapid sequential check-ins. The reopen is triggered via a Livewire event (dispatch+#[On]) so it runs in a separate request, after Filament's ownunmountActionlifecycle completes — callingmountActiondirectly inside->after()was being silently undone by that cleanup. Field hasautofocusso the cursor lands on the token input on every reopen.App panel —
app-modules/panel-appEventDetailLivewire component: renders the enrollment's QR code as inline SVG when status isconfirmedorchecked_in. Includes copy-token and download-SVG buttons. Adds computed check-in history list and a "present today" badge based on today's check-in records.Tests —
app-modules/events/tests10 feature tests covering all acceptance criteria:
generates_qr_token_on_confirmed_enrollmentEnrollmentConfirmedeventdoes_not_regenerate_existing_tokenvalid_qr_scan_creates_check_indispatches_participant_checked_in_eventrejects_unknown_tokenqrTokenNotFoundexceptionrejects_expired_tokenexpires_at < now()→qrTokenExpiredrejects_duplicate_scan_same_dayalreadyCheckedInrejects_cancelled_enrollment_tokensame_token_creates_new_check_in_each_daylistener_generates_token_after_commitAcceptance criteria
confirmedstatusmethod=qr_codewith token in payloadParticipantCheckedIndomain event dispatchedTest plan
All 74 tests pass. Pint clean.
Commits
920f0c8dep(events): adiciona biblioteca bacon/bacon-qr-code para geração de QR SVG1db9d28feat(events/check-in): implementa geração e validação de token QR por inscrição1bd4eb1feat(events): registra listener GenerateQrTokenOnConfirmed no EventsServiceProvider22b920ffeat(panel-admin): adiciona ação de scan QR contínuo na página de edição de eventod6be299feat(panel-app): exibe QR code, histórico de check-ins e badge de presença hojeca9da88test(events/check-in): adiciona suite de testes para geração de token QR e check-in1607145fix(panel-admin): corrige reabertura contínua do modal de scan QR