feat: automated seminar invitations - #4968
Conversation
There was a problem hiding this comment.
Pull request overview
Implements the initial “automated seminar invitations” workflow (TECH-329 groundwork) by introducing training seminars, invitations, attendee tracking, CTS group session syncing, and a scheduled expiry process, surfaced through a new Filament Training “Seminars” resource and member-facing invitation response pages.
Changes:
- Added Seminar/Invitation/Attendee domain models, migrations, factories, and services to send/track invitations and create attendees.
- Added member routes + controller + Blade views for accepting/declining seminar invitations, plus an expiry command scheduled daily.
- Added CTS sync support (group session + students) via a service and observers, with corresponding tests and CTS test DB schema additions.
Reviewed changes
Copilot reviewed 43 out of 43 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Unit/Training/Seminar/SeminarInvitationServiceTest.php | Unit coverage for invitation sending/accept/decline/expiry behaviors. |
| tests/Unit/Training/Seminar/SeminarCtsSyncServiceTest.php | Unit coverage for syncing seminars/attendees into CTS tables. |
| tests/Feature/Training/Seminar/SeminarInvitationControllerTest.php | Feature coverage for member invitation response endpoints and authorization. |
| tests/Feature/Training/Seminar/CheckForExpiredSeminarInvitationsCommandTest.php | Feature coverage for the scheduled expiry command behavior. |
| tests/Database/MockCtsDatabase.php | Extends mocked CTS schema to include group session tables used by the sync service. |
| routes/web-main.php | Adds member routes for seminar invitation accept/decline actions by token. |
| resources/views/training/seminar-invitation/result.blade.php | Member-facing result page for accept/decline flows. |
| resources/views/training/seminar-invitation/expired.blade.php | Member-facing page for expired/invalid/non-respondable invitations. |
| resources/views/emails/training/seminar_invitation.blade.php | Email template for seminar invitations with response links. |
| database/seeders/RolesAndPermissionsSeeder.php | Adds permissions for viewing/managing seminars in Filament. |
| database/migrations/2026_07_02_100020_create_training_seminar_attendees_table.php | Creates attendee table (incl. CTS student ID back-reference). |
| database/migrations/2026_07_02_100010_create_training_seminar_invitations_table.php | Creates invitations table with token + status + expiry fields. |
| database/migrations/2026_07_02_100000_create_training_seminars_table.php | Creates seminars table with capacity, expiry window, and CTS session ID. |
| database/factories/Training/Seminar/SeminarInvitationFactory.php | Factory + states for invitations used in tests. |
| database/factories/Training/Seminar/SeminarFactory.php | Factory + states for seminars (closed/automatic/past/CTS session). |
| database/factories/Training/Seminar/SeminarAttendeeFactory.php | Factory for seminar attendees used in tests. |
| app/Services/Training/SeminarInvitationService.php | Core invitation logic (top-up, create, accept/decline, expiry cleanup). |
| app/Services/Training/SeminarCtsSyncService.php | Syncs core seminars/attendees into legacy CTS group session tables. |
| app/Observers/Training/SeminarObserver.php | Triggers CTS sync when seminars are created/updated. |
| app/Observers/Training/SeminarAttendeeObserver.php | Triggers CTS sync when attendees are created. |
| app/Notifications/Training/SeminarInvitationNotification.php | Notification that renders the seminar invitation email. |
| app/Models/Training/WaitingList/RemovalReason.php | Adds waiting list removal reasons for seminar invitation outcomes. |
| app/Models/Training/Seminar/SeminarInvitation.php | Invitation model with enum cast and response eligibility logic. |
| app/Models/Training/Seminar/SeminarAttendee.php | Attendee model + observer binding. |
| app/Models/Training/Seminar/Seminar.php | Seminar model with capacity/cutoff calculations and relations. |
| app/Models/Mship/Concerns/HasSeminarInvitations.php | Adds invitation relationship + cannot-attend counting on Account. |
| app/Models/Mship/Account.php | Wires in the seminar invitations trait. |
| app/Models/Cts/GroupSessionStudent.php | CTS model for group session student join table. |
| app/Models/Cts/GroupSession.php | CTS model for group sessions. |
| app/Http/Controllers/Mship/Training/SeminarInvitationController.php | Member controller for responding to invitations by token. |
| app/Filament/Training/Resources/TrainingPlaces/TrainingPlaceResource.php | Adjusts navigation sort to accommodate new Seminars navigation ordering. |
| app/Filament/Training/Resources/Seminars/Widgets/SeminarStatsOverview.php | Adds per-seminar stats widget (sent/pending/declined/attendees). |
| app/Filament/Training/Resources/Seminars/SeminarResource.php | New Filament resource for creating/viewing seminars and managing relations. |
| app/Filament/Training/Resources/Seminars/RelationManagers/WaitingListRelationManager.php | Shows waiting list accounts + supports manual invitations. |
| app/Filament/Training/Resources/Seminars/RelationManagers/InvitationsRelationManager.php | Lists invitations and their statuses in Filament. |
| app/Filament/Training/Resources/Seminars/RelationManagers/AttendeesRelationManager.php | Lists attendees and supports creating Training Places from attendees. |
| app/Filament/Training/Resources/Seminars/Pages/ViewSeminar.php | Seminar view page actions (enable/disable auto invites, close seminar). |
| app/Filament/Training/Resources/Seminars/Pages/ListSeminars.php | List page for seminars. |
| app/Filament/Training/Resources/Seminars/Pages/EditSeminar.php | Edit page for seminars. |
| app/Filament/Training/Resources/Seminars/Pages/CreateSeminar.php | Create page for seminars (sets created_by to current user). |
| app/Enums/SeminarInvitationStatus.php | Enum for invitation statuses incl. label/color/responded helpers. |
| app/Console/Kernel.php | Schedules the daily seminar invitation expiry check at 11:30. |
| app/Console/Commands/Training/CheckForExpiredSeminarInvitations.php | Command wrapper to run the expiry cleanup service method. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…//github.com/CLC0609/core into tech-329-automate-obs-s1-seminar-arrangement
|
Fixes TECH-681 |
|
/review |
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
|
🔍 OpenCodeReview found 33 issue(s) in this PR.
|
|
|
||
| @section('body') | ||
|
|
||
| TEST TEXT REPLACE BEFORE PROD PLS DONT FORGET COBY |
There was a problem hiding this comment.
[bug · critical]
This line contains a placeholder/test reminder that appears to have been accidentally left in the email template. It will be rendered in every seminar invitation email sent to users, which is clearly unintended.
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…nsCommandTest.php Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…invitations_table.php Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Parts of TECH-329
Summary of changes
A fairly simple version of what was requested in TECH-329. Currently the implemented workflow looks like:
capacity - (number of pending invites + number of confirmed attendees) = 0. This means there will only ever be capacity number of potential people invited at any timeWhat it doesn't do:
The spec requested was fairly large, and with this PR already 3000+ lines, I think it's best to leave these for another one, but most of the groundwork is here to make those fairly simple.
Syncs with CTS
group_session_*tables to allow the later part of the workflow to be done on the CTS for now.Screenshots (if necessary)
Awaiting copy text