Skip to content

feat(events): implement numeric code check-in#297

Open
davicbtoliveira wants to merge 6 commits into
feat/eventsfrom
feat/events-numeric-code-checkin
Open

feat(events): implement numeric code check-in#297
davicbtoliveira wants to merge 6 commits into
feat/eventsfrom
feat/events-numeric-code-checkin

Conversation

@davicbtoliveira
Copy link
Copy Markdown
Member

Closes #245

Summary

Implements self-service check-in via short-lived numeric codes announced by the organizer. Participants enter the code on the event detail page to check themselves in without organizer intervention.

What was done

Domain layer

  • Added revoked_at column to events_check_in_codes (soft revoke, per ADR-0003)
  • Created NumericCodeCheckInDTO carrying enrollment, code, and eventDate
  • Created NumericCodeCheckInAction with 5-step validation pipeline: existence → date binding → expiry/revoked → max uses → atomic increment + delegation to core CheckInAction
  • Added 4 new CheckInException factory methods: invalidCheckInCode, checkInCodeExpired, checkInCodeExhausted, checkInCodeWrongDate
  • Translated error messages in en/pt_BR

Admin panel

  • Added CheckInCodesRelationManager on the Event edit page
  • Generate codes with 4 or 6 digit length selector, auto-generated read-only code
  • event_date defaults to the event's starts_at, constrained to the event date range
  • Revoke action with confirmation (sets revoked_at)

App panel

  • Created NumericCodeCheckIn Livewire component embedded in the event detail page
  • Code input visible when enrollment is confirmed or checked_in
  • Validation errors surface as inline messages below the input

Tests

  • 8 feature tests: valid check-in, invalid code, wrong date, expired, revoked, max uses exhausted, atomic increment, outside event range

Quality

  • Pint: passed
  • PHPStan: passed (0 errors)
  • All events tests: 86/86 passed (0 regressions)

ADR

  • ADR-0006 documents all design decisions

Add self-service check-in via short-lived numeric codes announced by the organizer.

- Add revoked_at column to events_check_in_codes (soft revoke)
- Create NumericCodeCheckInAction with validation pipeline (existence, date, expiry, max uses, revoked)
- Create CheckInCodesRelationManager for admin panel (generate codes with 4/6 digit length, revoke)
- Create NumericCodeCheckIn Livewire component for participant code entry
- Add 4 new CheckInException factory methods with translated error messages
- Add feature tests covering valid, invalid, expired, revoked, exhausted, and concurrent scenarios

Refs #245
Closes #296
Comment thread app-modules/panel-app/src/Livewire/Events/NumericCodeCheckIn.php
…firmed non-null at that point) and added eager-loading for `event.enrollmentPolicy` to avoid N+1.
Copy link
Copy Markdown

sirelves commented Jun 2, 2026

Fiz uma passada focada em seguranca, consistencia e comportamento em producao. A feature esta bem encaminhada, mas eu seguraria o merge por alguns pontos:

  1. starts_at do codigo nao e validado no check-in

Arquivo: app-modules/events/src/CheckIn/Actions/NumericCodeCheckInAction.php, perto das validacoes de expires_at/revoked_at.

O admin salva starts_at como inicio da janela de validade, mas NumericCodeCheckInAction so valida expires_at e revoked_at. Na pratica, um codigo criado para comecar mais tarde ja pode ser usado antes do horario se o participante souber/adivinhar o valor.

Sugestao: validar tambem starts_at, por exemplo rejeitando quando now()->lt($codeRecord->starts_at).

  1. Falta protecao contra brute force no fluxo de codigo numerico

Arquivos:

  • app-modules/panel-app/src/Livewire/Events/NumericCodeCheckIn.php, metodo checkIn()
  • app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/CheckInCodesRelationManager.php, seletor de 4/6 digitos

O componente Livewire permite tentativas repetidas de checkIn() e o admin permite codigos de 4 digitos. Mesmo exigindo inscricao confirmada, 10k combinacoes e um espaco pequeno para tentativa automatizada.

Sugestao: adicionar rate limit por user_id:event_id (e possivelmente IP) antes de consultar o codigo, alem de validar formato/tamanho do codigo no componente.

  1. Busca do codigo nao filtra por event_date

Arquivos:

  • app-modules/events/src/CheckIn/Actions/NumericCodeCheckInAction.php, query inicial de CheckInCode
  • app-modules/events/database/migrations/2026_05_16_200006_create_events_check_in_codes_table.php, constraint unique(event_id, event_date, code)

A tabela permite o mesmo code para datas diferentes do mesmo evento, mas a query busca apenas por event_id + code e usa first(). Em evento multi-dia, se o mesmo codigo existir em datas diferentes, pode pegar o registro errado e rejeitar um codigo valido para o dia atual.

Sugestao: incluir event_date na query inicial, alinhando com a constraint da tabela e com a regra de negocio.

  1. O codigo exibido no preview do admin pode nao ser o codigo salvo

Arquivo: app-modules/panel-admin/src/Filament/Resources/Events/RelationManagers/CheckInCodesRelationManager.php, campo code_preview e callback using().

code_preview gera um valor no formulario, mas esta com dehydrated(false). Depois, no using(), outro codigo e gerado novamente. Isso pode fazer o organizador ver/anunciar um codigo diferente do que foi persistido. Alem disso, o preview sempre usa 6 digitos, mesmo quando o usuario seleciona 4.

Sugestao: gerar uma unica vez o codigo que sera salvo, ou atualizar o preview de forma reativa com o mesmo valor usado na persistencia.

Tambem vale adicionar testes cobrindo esses casos: codigo antes de starts_at, colisao/reuso de codigo em evento multi-dia, rate limit/tentativas repetidas e consistencia entre preview e valor persistido.

…expiry

Set pgsql session timezone to UTC (matching APP_TIMEZONE) to prevent +3h
shift when Laravel serializes Carbon values. Use Carbon::isFuture() for
timezone-safe starts_at comparison. Add format validation on admin panel
code generation.

Closes #297
@davicbtoliveira davicbtoliveira requested review from GabrielFVDev and YuriSouzaDev and removed request for GabrielFVDev and YuriSouzaDev June 3, 2026 03:43
Copy link
Copy Markdown

sirelves commented Jun 3, 2026

Boa, os pontos anteriores parecem enderecados nesta ultima versao.

Fiquei com uma duvida sobre a mudanca em config/database.php:

'timezone' => 'UTC',

Essa alteracao muda o timezone da conexao Postgres de forma global, fora do escopo direto do check-in numerico. Como o app usa APP_TIMEZONE com default America/Sao_Paulo, vale confirmar se essa mudanca e realmente necessaria para este PR e se nao pode afetar outras queries/serializacoes de timestampTz no restante da aplicacao.

Se a intencao era apenas estabilizar os testes/validacoes desse fluxo, talvez seja melhor resolver localmente no teste ou na comparacao de datas, para evitar impacto transversal.

@davicbtoliveira
Copy link
Copy Markdown
Member Author

Corrigido, codex tava fritando ontem pra corrigir 2 testes q nao passavam e acabou trocando a timezone do banco 🤡 @sirelves

Copy link
Copy Markdown

@sirelves sirelves left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Obrigado pelos ajustes!

Copy link
Copy Markdown

@GabrielFVDev GabrielFVDev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

brabo dms!
LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants