Implement end-to-end invitation acceptance: neutral entry route validates token and routes to register (new users), login (existing users), or auto-accepts (authenticated users). Handles 2FA token survival via session, email case-insensitive matching, and dedicated error pages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
15 lines
1.6 KiB
Markdown
15 lines
1.6 KiB
Markdown
# Deferred Work
|
|
|
|
## From: tech-spec-fix-permission-nudge-bulk-bugs (2026-03-27)
|
|
|
|
- **Null contact_email guard in BulkNotificationController** — If a client exists but has null `contact_email`, `Mail::to(null)` will throw. Add a filter for non-null email before sending.
|
|
- **Cross-page selection UX on declarations page** — Navigating between pages clears selections silently. Consider persisting selections across pages or warning the user.
|
|
- **Bulk notify count mismatch UX** — When some selected declarations are filtered out (no client), the success message count differs from the selection count with no explanation. Consider showing skipped count.
|
|
- **Nudge email template null guards** — `nudge-notification.blade.php` renders `$clientName`, `$declarationType`, `$dueDate` without null fallbacks, producing blank labels.
|
|
|
|
## From: tech-spec-team-invitation-acceptance (2026-03-27)
|
|
|
|
- **Race condition on concurrent invitation acceptance** — Two users clicking the same invitation link simultaneously could both pass `isValid()` before either sets `accepted_at`. Fix with `SELECT FOR UPDATE` or atomic `UPDATE WHERE accepted_at IS NULL`.
|
|
- **Multiple pending invitations per email/workspace** — No unique constraint on `[workspace_id, email]` in `team_invitations`. Multiple tokens can exist for the same email+workspace. Second token in `CreateNewUser` path would hit unique constraint on `workspace_user` and throw.
|
|
- **Vue cross-link URLs should come from PHP props** — Register.vue and Login.vue construct invitation-aware login/register URLs via JS string interpolation instead of receiving them as props from the controller.
|