Skip to content

feat(events): module structure#249

Merged
danielhe4rt merged 14 commits into
feat/eventsfrom
feat/foundation-events
May 19, 2026
Merged

feat(events): module structure#249
danielhe4rt merged 14 commits into
feat/eventsfrom
feat/foundation-events

Conversation

@GabrielFVDev
Copy link
Copy Markdown

Resumo

  • Events domain: Event + EventType com regras de enrollment e enums de status/attendance
  • Enrollment flow: Enrollment, EnrollmentPolicy, EnrollmentTransition com factories e testes
  • Check-in flow: CheckIn, CheckInCode, QrToken + CheckInMethod
  • Admin panel: EventResource com pages de create/edit/list, relation manager de enrollments, melhorias em form/infolist/table
  • Cleanup/docs: ajuste de legado, ADRs e atualizacao de context map

Architecture

Events module
|-- Event domain (EventType + Event)
|-- Enrollment domain (EnrollmentMethod/Status/AttendanceRequirement + Enrollment/Policy/Transition)
`-- Check-in domain (CheckInMethod + CheckIn/CheckInCode/QrToken)

Admin panel
`-- Event management (EventResource + pages + relation manager + form/infolist/table)

Files (8 migrations, 7 models, 5 enums, 7 factories, 3 tests)

  • 8 migrations (inclui drop do legado)
  • 7 models e 5 enums
  • 7 factories
  • 3 testes (feature + unit)
  • Recursos do painel admin para eventos

Test plan

  • php artisan migrate
  • php artisan test --filter=EventFactoriesTest
  • php artisan test --filter=EventResourceTest
  • php artisan test --filter=EnrollmentStatusTest

@GabrielFVDev GabrielFVDev self-assigned this May 18, 2026
@GabrielFVDev GabrielFVDev added the enhancement New feature or request label May 18, 2026
Copy link
Copy Markdown

@AdryanneKelly AdryanneKelly left a comment

Choose a reason for hiding this comment

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

  • alguns campos sofreram mudanças para nomes diferentes do que está descrito na doc, porém apesar disso, aparentemente mantém o tipo adequado para a finalidade do campo. Seria interessante seguir os mesmos nomes da documentação.
  • #239
  • Marquei alguns, mas existem mais em quase todas as migrations.

Comment thread app-modules/events/database/migrations/2026_05_16_200001_create_events_table.php Outdated
Comment thread app-modules/events/database/migrations/2026_05_16_200001_create_events_table.php Outdated
$table->unsignedInteger('capacity')->nullable();
$table->boolean('waitlist_enabled')->default(false);
$table->string('attendance_requirement', 20)->default('all_days');
$table->unsignedSmallInteger('minimum_days')->nullable();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

de onde veio esse minimum_days?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

número mínimo de dias que o participante deve comparecer para receber XP/certificado (usado quando attendance_requirement = 'minimum_days')

Me parece meio redundante, acha bom remover?

$table->string('to_status', 20);
$table->foreignUuid('actor_id')->nullable()->constrained('users')->nullOnDelete();
$table->string('reason', 500)->nullable();
$table->jsonb('metadata')->nullable();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

de onde veio este metadata?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

contexto adicional para audit trail: notas do admin, job ID, código de razão, etc.

Basicamente pra ajudar a auditoria, mai foi coisa do claudinho q adiciono

@danielhe4rt
Copy link
Copy Markdown
Contributor

Review — Foundation Events (#239)

Bom trabalho na estrutura base, Gabriel! Vários pontos onde você melhorou em relação ao doc de referência (UUIDs, Filament v5 patterns, i18n, sub-namespaces DDD, jsonb, UPDATED_AT = null em write-once models). Dito isso, tem divergências que precisam ser resolvidas antes do merge pra não quebrar as slices futuras.

Concordo com todos os comentários da Adryanne — vou expandir aqui com o contexto completo.


Bloqueantes

1. Models sem $fillable — Segurança (Mass Assignment)

Nenhum dos 7 models define $fillable ou $guarded. Sem isso, qualquer atributo pode ser mass-assigned (inclusive status, waitlist_position, is_published). Adicionar $fillable em cada model com os campos que devem ser preenchidos.

2. State Machine — Pending → Waitlisted bloqueado incorretamente

No EnrollmentStatus::canTransitionTo(), o Pending não transiciona pra Waitlisted. Mas no fluxo de enrollment com capacity cheia, o usuário submete e vai pra waitlist — essa transição é essencial. O teste EnrollmentStatusTest também asserta que Pending→Waitlisted é false, então teste e implementação estão consistentes entre si, mas ambos divergem do doc.

// Atual
self::Pending => in_array($target, [self::Confirmed, self::Rejected, self::Cancelled], strict: true),

// Correto — adicionar Waitlisted
self::Pending => in_array($target, [self::Confirmed, self::Rejected, self::Cancelled, self::Waitlisted], strict: true),

Impacto: Slice 03 (Waitlist/Capacity) quebra completamente.

3. State Machine — CheckedIn → NoShow bloqueado incorretamente

CheckedIn só transiciona pra Attended, mas o cenário de conferência de 2 dias exige: fez check-in no dia 1, não veio no dia 2 → closure job marca como no_show.

// Atual
self::CheckedIn => $target === self::Attended,

// Correto
self::CheckedIn => in_array($target, [self::Attended, self::NoShow], strict: true),

Impacto: Slice 09 (Event Closure Job) quebra.

4. Campo triggered_by ausente em events_enrollment_transitions

Esse campo (user, system, admin) é essencial pro audit trail. Sem ele, não dá pra distinguir se uma transição foi automática (job), do próprio usuário (cancelamento), ou override de admin. Todas as slices de 02 a 09 gravam transições e precisam desse campo.

5. Campo is_public ausente em events_enrollments

Conforme a Adryanne já sinalizou — controla visibilidade da inscrição na lista pública de participantes. boolean, default true.

6. ->live() ausente no enrollment_method (EventForm)

O campo application_schema tem ->visible(fn (callable $get) => ...) condicionado ao enrollment_method, mas o Select não tem ->live(). Sem isso, a visibilidade nunca atualiza quando o admin troca o método — fica permanentemente escondido ou visível conforme o estado inicial.

Além disso, o type hint deveria ser Get $get (Filament v5), não callable $get, e a comparação deveria usar o enum em vez de string raw:

Select::make('enrollment_method')
    ->options(EnrollmentMethod::class)
    ->required()
    ->live(), // ← essencial

KeyValue::make('application_schema')
    ->visible(fn (Get $get): bool => $get('enrollment_method') === EnrollmentMethod::Application->value),

Melhorias Importantes

7. cancellation_deadline como timestamp vs horas

O doc define cancellation_deadline_hours (integer, default 24) — relativo ao evento. A implementação usa cancellation_deadline (timestamp) — absoluto. A abordagem relativa é melhor UX: o admin define "24 horas antes" e o sistema calcula automaticamente. Com timestamp, o admin faz a conta manualmente toda vez.

8. tenant_id denormalizado em events_enrollments

O tenant já é acessível via enrollment → event → tenant. Essa denormalização pode ficar inconsistente se o evento mudar de tenant e adiciona complexidade sem ganho claro. Remover ou justificar o caso de uso.

9. check_in_dateevent_date

Na tabela events_check_ins, o campo representa qual dia do evento o check-in se refere (dia 1, dia 2 da conferência), não quando o check-in aconteceu (isso é o created_at). O nome event_date é mais semântico.

10. Models sem scopes e helpers

O doc define scopes que facilitam as slices futuras. Sem eles, cada slice vai ter que reimplementar queries inline:

  • Event: isPast(), totalDays(), scopeActive(), scopeUpcoming()
  • Enrollment: scopeConfirmed(), scopeWaitlisted(), scopeActive()

11. Models sem $attributes defaults

Best practice: espelhar os defaults do banco no model pra que new Event() reflita o estado correto:

// Event
protected $attributes = ['is_published' => false];

// EnrollmentPolicy
protected $attributes = [
    'waitlist_enabled' => false,
    'attendance_requirement' => 'all_days',
    'xp_on_confirmed' => 0,
    'xp_on_checked_in' => 0,
    'xp_on_attended' => 0,
];

O que ficou melhor que o doc

Quero destacar decisões do PR que superaram o doc de referência — bom julgamento aqui:

  1. UUIDs em todas as PKs com HasUuids — alinhado com o padrão do projeto
  2. Sub-namespaces DDD (Event\Models, Enrollment\Enums, CheckIn\Models) — organização superior
  3. i18n nos enums via __('events::enums...') com arquivos lang en/ e pt_BR/
  4. Heroicon:: enum e Color:: arrays — Filament v5 correto (doc usava strings de v4)
  5. jsonb em vez de json — melhor pro PostgreSQL
  6. UPDATED_AT = null em models write-once (transitions, check-ins)
  7. minimum_days na enrollment_policies — necessário pro AttendanceRequirement::MinimumDays, o doc omitiu
  8. Named indexes (idx_events_tenant_slug, etc.) — profissional
  9. recorded_by em check-ins — útil pro audit trail
  10. EventInfolist — visualização read-only que o doc não incluiu
  11. newFactory() explícito — binding confiável de factory

Naming divergente (precisamos alinhar)

Doc PR Veredicto
active is_published PR melhor — mais claro semanticamente
has_waitlist waitlist_enabled Tanto faz — escolher um e manter
xp_reward_rsvp/checkin/attendance xp_on_confirmed/checked_in/attended PR melhor — alinha com enum states
application_form_schema application_schema PR ok — simplificação
valid_from/valid_until starts_at/expires_at PR ok
actor_user_id actor_id PR ok — mais limpo
EventModel (classe) Event PR funciona, mas cuidado com conflito de import com Illuminate\Support\Facades\Event

Checklist

  • Adicionar $fillable em todos os 7 models
  • Corrigir Pending → Waitlisted no state machine + corrigir teste
  • Corrigir CheckedIn → NoShow no state machine
  • Adicionar triggered_by na migration de transitions
  • Adicionar is_public na migration de enrollments
  • Adicionar ->live() no enrollment_method Select + corrigir type hint Get $get
  • Decidir: cancellation_deadline timestamp vs cancellation_deadline_hours integer
  • Adicionar scopes e helpers nos models
  • Adicionar $attributes defaults nos models
  • Renomear check_in_dateevent_date
  • Avaliar necessidade do tenant_id em enrollments

…-in date to event date, and add triggered_by enum
…ted migrations, update CheckInFactory and tests accordingly
@danielhe4rt danielhe4rt changed the title Feat/foundation events feat(events): module structure May 19, 2026
…view comments

- Add EventStatus enum (Draft → Published → Completed → Cancelled) with
  Filament HasLabel/HasIcon/HasColor, state machine transitions, and i18n
- Update Event model, migration, and factory to use status instead of active
- Add scopePublished() and redefine scopeActive() as published + upcoming
- Rename check_in to event_date in check-ins migration, model, and factory
- Replace single timestampTz(created_at) with timestampsTz() on check-ins
@danielhe4rt danielhe4rt merged commit 21cba9b into feat/events May 19, 2026
1 check passed
@danielhe4rt danielhe4rt deleted the feat/foundation-events branch May 19, 2026 16:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants