Notification preferences: - Create notification_preferences table (post_published, post_failed, account_disconnected booleans per user) - NotificationPreferenceController with firstOrCreate on first visit - SendNotification job respects email preferences before sending - Settings page with toggle switches, i18n in 3 languages - 8 new tests for preferences (controller + wantsEmailFor + job integration) Post published notification: - PostPublished mail + maizzle template - Notify owner on successful publish via SendNotification job - PostPublished type added to notification enum Header & Layout: - Rename AppSidebarHeader to AppHeader with left/center/right slots - showSidebarTrigger prop to hide sidebar toggle - Calendar: controls in header (left: nav, center: date, right: tabs + new post) - Fixed header with scrollable content (flex h-screen pattern) - fullWidth pages use overflow-y-auto (fixes month view scroll) UI improvements: - Action buttons moved to header-right: posts, hashtags, labels - Settings breadcrumbs: "Settings > Profile" pattern - Calendar: remove duplicate New Post button from day view - Remove size="sm" from Schedule/Publish buttons - Remove bg-background from header (inherits from SidebarInset) - Add Cancel button to labels and hashtags create/edit dialogs - Add common.cancel i18n key - Clean up orphaned Calendar breadcrumbs - Fix SocialAccountsGrid buttons to use shadcn Button ghost All 753 tests passing.
68 lines
1.8 KiB
PHP
68 lines
1.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Jobs;
|
|
|
|
use App\Enums\Notification\Channel;
|
|
use App\Enums\Notification\Type;
|
|
use App\Models\Notification;
|
|
use App\Models\User;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Queue\Queueable;
|
|
use Illuminate\Mail\Mailable;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Mail;
|
|
|
|
class SendNotification implements ShouldQueue
|
|
{
|
|
use Queueable;
|
|
|
|
public int $tries = 3;
|
|
|
|
public int $backoff = 10;
|
|
|
|
/**
|
|
* @param array<string, mixed>|null $data
|
|
*/
|
|
public function __construct(
|
|
public User $user,
|
|
public string $workspaceId,
|
|
public Type $type,
|
|
public Channel $channel,
|
|
public string $title,
|
|
public string $body,
|
|
public ?array $data = null,
|
|
public ?Mailable $mailable = null,
|
|
) {}
|
|
|
|
public function handle(): void
|
|
{
|
|
// Save in-app notification
|
|
if ($this->channel !== Channel::Email) {
|
|
Notification::create([
|
|
'user_id' => $this->user->id,
|
|
'workspace_id' => $this->workspaceId,
|
|
'type' => $this->type,
|
|
'channel' => $this->channel,
|
|
'title' => $this->title,
|
|
'body' => $this->body,
|
|
'data' => $this->data,
|
|
]);
|
|
}
|
|
|
|
// Send email (respects user preferences)
|
|
if ($this->mailable && $this->channel !== Channel::InApp && $this->user->wantsEmailFor($this->type->value)) {
|
|
Mail::to($this->user)->send($this->mailable);
|
|
}
|
|
}
|
|
|
|
public function failed(\Throwable $exception): void
|
|
{
|
|
Log::error('SendNotification job failed', [
|
|
'user_id' => $this->user->id,
|
|
'type' => $this->type->value,
|
|
'error' => $exception->getMessage(),
|
|
]);
|
|
}
|
|
}
|