Add inactivity reminder emails

No app/Notifications existed beyond Laravel's stock email verification
and password reset - nothing brought lapsed users back. Adds a queued
InactivityReminder mail notification and a scheduled command
(app:send-inactivity-reminders, daily at 09:00) that emails verified
users with no completed workout session in the last 5 days, at most
once per 7-day cooldown so it can't spam. Skips brand-new signups so it
doesn't nag someone who registered an hour ago.

Verified the full pipeline live: scheduled command queues the
notification (QUEUE_CONNECTION=database), a queue worker processes it,
and the rendered email lands in the log driver with a working link back
to /workouts.

Production still needs cron actually invoking `php artisan schedule:run`
every minute and a running queue worker - that's part of the pending
deployment setup, not something this change can provide on its own.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 12:32:17 +03:00
co-authored by Claude Sonnet 5
parent e8173ff90a
commit bbdf684a6a
6 changed files with 234 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class InactivityReminder extends Notification implements ShouldQueue
{
use Queueable;
/**
* Get the notification's delivery channels.
*
* @return array<int, string>
*/
public function via(object $notifiable): array
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject("We haven't seen you in a while, {$notifiable->name}")
->line('It has been a few days since your last completed workout.')
->line('A short session today is better than a perfect one you keep putting off.')
->action('Start a Workout', route('workouts.index'))
->line('See you back soon!');
}
}