feat(events): application enrollment with form builder#383
Open
AdryanneKelly wants to merge 4 commits into
Open
feat(events): application enrollment with form builder#383AdryanneKelly wants to merge 4 commits into
AdryanneKelly wants to merge 4 commits into
Conversation
These were typed as associative arrays but are actually lists (schema is indexed by field position, data by matching index), which caused PHPStan to flag EnrollUserAction::validateApplicationData as dead code. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
10 tasks
GabrielFVDev
approved these changes
Jul 2, 2026
…or choice fields
EnrollmentsRelationManager used recordTitleAttribute('id'), which reads
plain attributes only, so the delete confirmation showed the enrollment's
UUID instead of the participant's name — switched to recordTitle() with
a closure that reads the related user.
The application form builder let organizers save select/checkbox
questions with zero options, producing an empty dropdown or checkbox
group the participant couldn't fill.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Approve/Reject action labels, modal text, and success notifications (Admin), the answers viewer (Admin: rejection reason, yes/no, no answer), and the "select an option" placeholder (App) were plain English literals, inconsistent with the rest of the module's events::pages translations. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
BrunaDomingues
approved these changes
Jul 2, 2026
BrunaDomingues
left a comment
There was a problem hiding this comment.
Só deixei o comentário das traduções mas de resto tá top!
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 the Application enrollment method: the participant fills a dynamic form defined by the organizer, the enrollment lands as
pending, and the organizer approves or rejects it from the Admin panel. ReusesEnrollUserActionas the shared entrypoint (RSVP path untouched) and adds two new domain actions for the approval/rejection decision.Flow: participant submits form →
EnrollUserAction(pending, no capacity check) → Admin approves/rejects →ApproveApplicationAction(capacity check + confirm/waitlist) orRejectApplicationAction(reason + rejected)Closes #237
What changed — app-modules/events
src/Enrollment/Actions/EnrollUserAction.phppendingenrollment, validatesapplication_dataagainst the policy's schema (per-type:required,select/checkboxoptions), skips capacity check on submission.src/Enrollment/Actions/ApproveApplicationAction.phppending, capacity check withlockForUpdate, transitions toconfirmed/waitlisted(or throws if full with no waitlist), dispatchesEnrollmentConfirmed/EnrollmentWaitlisted, audit trail (triggered_by=admin,actor_id).src/Enrollment/Actions/RejectApplicationAction.phppending, storesrejection_reason, transitions torejected, audit trail with reason.src/Enrollment/DTOs/ApproveApplicationDTO.php,RejectApplicationDTO.phpsrc/Enrollment/DTOs/EnrollUserDTO.phpapplicationDatapayload.src/Enrollment/Exceptions/EnrollmentException.phpenrollmentNotPending()andapplicationDataInvalid()factories.src/Enrollment/Models/Enrollment.php,EnrollmentPolicy.php@propertytyping forapplication_data/application_schema(list, not associative array) — removes a PHPStan type-mismatch cascade in the new validation code.database/factories/EnrollmentPolicyFactory.php,database/seeders/EventsSeeder.phpapplication()factory state + seed data covering all 4 field types (text/textarea/select/checkbox) across two Application-method events.lang/{en,pt_BR}/{exceptions,pages}.phpWhat changed — app-modules/panel-admin
Filament/Resources/Events/Schemas/EventForm.phpapplication_schema(type/label/required/options), visible only whenenrollment_method = Application.Filament/Resources/Events/RelationManagers/Actions/ApproveApplicationAction.php,RejectApplicationAction.phpFilament/Resources/Events/RelationManagers/EnrollmentsRelationManager.phpresources/views/enrollments/application-data.blade.phpWhat changed — app-modules/panel-app
Livewire/Events/EventDetail.phpcanApplycomputed prop,apply()action, pre-initializes checkbox-group state as arrays inmount().resources/views/livewire/events/event-detail.blade.phppending/rejectedstatus views showing the submitted answers / rejection reason.Design notes (the non-obvious bits)
checkbox= multi-select group, not a single boolean. The issue's schema ({type, label, required, options?}) doesn't spell this out, so it was ambiguous whethercheckboxmeant a single yes/no toggle or a group of checkboxes sharing oneoptionslist (likeselect, but multi-value). Went with the multi-select reading —checkboxnow shows the sameoptionsbuilder asselectin the Admin, and stores an array of selected values. Validation treatsrequiredas "at least one selected" and checks each selection againstoptions.Livewire needs the array pre-seeded. Multiple
<input type="checkbox" wire:model="applicationFormData.{index}" value="...">bound to the same array path only get grouped into an array by Livewire's front end if that path is already an array before the first interaction — otherwise every checkbox in the group ends up sharing one boolean and checking one checks them all.EventDetail::mount()now seedsapplicationFormData[$index] = []for everycheckboxfield up front.Filament enum-cast gotcha.
Select::make('enrollment_method')->options(EnrollmentMethod::class)auto-applies anEnumStateCast, so$get('enrollment_method')insidevisible()closures returns the enum instance, not->value. The Repeater's visibility check was comparing against the string value and silently never showed — fixed by comparing against the enum case directly.Acceptance criteria
pendingandapplication_datapopulatedconfirmed(orwaitlistedif full)rejectedwith reason storedTest plan
Results:
Scenarios covered: submit → pending + audit trail · missing
applicationData→ exception · missing required field → exception · no schema defined → pending · event past → rejected · approve → confirmed + audit trail · approve at capacity + waitlist → waitlisted · approve at capacity, no waitlist → event full exception · approve non-pending → exception · no capacity limit → always confirms · reject with reason → rejected · reject non-pending → exception · reject preservesapplication_data· apply button rendering ·canApply/canConfirmPresenceflags · pending status shows submitted answers · rejected shows reason · already applied → no apply button · missing required field → error notification.Note on PHPStan: the module already carried ~172 pre-existing errors before this branch (untyped test-helper array params, Pest fluent-call false positives from Larastan) — out of scope here. This PR's own new type mismatch (
EnrollUserAction::validateApplicationDataagainst theapplication_schema/application_datadocblocks) was fixed; remaining new findings are the same pre-existing categories, consistent with every other test file in the suite.Out of scope (separate issues)