HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/ai.naniguide.com/database/seeders/Demo/Stage18_NotificationsSeeder.php
<?php

namespace Database\Seeders\Demo;

use App\Model\AppNotification;
use App\Notifications\AppNotification as Event;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

/**
 * Stage 18 — Notifications + activity logs.
 *
 * Per customer:
 *   - 8-15 direct notifications (audience='direct') with mixed severity / category
 *   - 20-50 activity log rows (mirrored to notifications by ActivityLog::booted)
 *
 * Plus one shared admin broadcast per seeded run (audience='admins') so the
 * admin bell on /rui/admin/notifications shows realistic content.
 */
class Stage18_NotificationsSeeder extends AbstractDemoSeeder
{
    protected int $fakerOffset = 18;

    public function run(): void
    {
        $f = $this->faker();
        $totalNotifs = 0;
        $totalLogs   = 0;

        // [title, severity, category, body template, optional action]
        $notifTemplates = [
            ['Campaign sent successfully',    Event::SEVERITY_SUCCESS, Event::CATEGORY_INFO,   'Your campaign "{name}" has been delivered to {n} recipients.', null],
            ['New subscriber added',          Event::SEVERITY_INFO,    Event::CATEGORY_INFO,   '{email} just joined your "{list}" list.', null],
            ['Sending server quota warning',  Event::SEVERITY_WARNING, Event::CATEGORY_ALERT,  'Your sending server has used {p}% of its hourly quota.', null],
            ['Bounce rate threshold reached', Event::SEVERITY_WARNING, Event::CATEGORY_ALERT,  'Your bounce rate ({r}%) is above the recommended threshold.', null],
            ['Subscription renewed',          Event::SEVERITY_SUCCESS, Event::CATEGORY_INFO,   'Your subscription has been renewed successfully.', null],
            ['Failed login attempt',          Event::SEVERITY_ERROR,   Event::CATEGORY_ALERT,  'Multiple failed login attempts detected from IP {ip}.', null],
            ['List import complete',          Event::SEVERITY_INFO,    Event::CATEGORY_INFO,   'Imported {n} subscribers into "{list}".', null],
            ['New form submission',           Event::SEVERITY_INFO,    Event::CATEGORY_INFO,   'You received a new submission from "{form}".', null],
            ['Trial ending soon',             Event::SEVERITY_WARNING, Event::CATEGORY_ACTION, 'Your trial period ends in {days} days.',
                ['label' => 'Renew now', 'route' => 'refactor.account.billing', 'params' => []]],
            ['DKIM verification needed',      Event::SEVERITY_WARNING, Event::CATEGORY_ACTION, 'Please verify DKIM for {domain}.', null],
        ];

        $activityTypes = [
            ['login',  'auth',       'User logged in'],
            ['create', 'campaign',   'Created campaign'],
            ['update', 'campaign',   'Updated campaign'],
            ['delete', 'campaign',   'Deleted campaign'],
            ['create', 'list',       'Created mail list'],
            ['create', 'subscriber', 'Added subscriber'],
            ['import', 'subscriber', 'Imported subscribers'],
            ['export', 'subscriber', 'Exported subscribers'],
            ['create', 'form',       'Created signup form'],
            ['update', 'settings',   'Updated settings'],
        ];

        foreach (Stage03_CustomersSeeder::$created as $key => $customer) {
            try {
                $count = $f->numberBetween(8, 15);
                $rows  = [];
                for ($i = 0; $i < $count; $i++) {
                    [$title, $severity, $category, $tpl, $action] = $notifTemplates[array_rand($notifTemplates)];
                    $body = strtr($tpl, [
                        '{name}'   => 'Campaign #' . $f->numberBetween(1, 50),
                        '{n}'      => $f->numberBetween(50, 5000),
                        '{email}'  => $f->safeEmail,
                        '{list}'   => 'Newsletter',
                        '{p}'      => $f->numberBetween(50, 95),
                        '{r}'      => $f->randomFloat(1, 1, 8),
                        '{ip}'     => $f->ipv4,
                        '{form}'   => 'Newsletter Signup',
                        '{days}'   => $f->numberBetween(1, 7),
                        '{domain}' => preg_replace('/[^a-z0-9]/', '', strtolower($customer->name)) . '.demo',
                    ]);

                    $createdAt = now()->subDays($f->numberBetween(0, 90));
                    $isRead    = $f->boolean(60);

                    $data = ['title' => $title, 'body' => $body];
                    if ($action) {
                        $data['action'] = $action;
                    }

                    $rows[] = [
                        'id'              => (string) Str::uuid(),
                        'type'            => 'demo.seed',
                        'audience'        => Event::AUDIENCE_DIRECT,
                        'notifiable_type' => $customer->getMorphClass(),
                        'notifiable_id'   => $customer->id,
                        'subject_type'    => null,
                        'subject_id'      => null,
                        'category'        => $category,
                        'severity'        => $severity,
                        'group_key'       => null,
                        'data'            => json_encode($data),
                        'read_at'         => $isRead ? $createdAt->copy()->addHours($f->numberBetween(1, 24)) : null,
                        'read_by_type'    => null,
                        'read_by_id'      => null,
                        'dismissed_at'    => null,
                        'dismissed_by_type' => null,
                        'dismissed_by_id' => null,
                        'created_at'      => $createdAt,
                        'updated_at'      => $createdAt,
                    ];
                    $totalNotifs++;
                }
                DB::table('notifications')->insert($rows);
            } catch (\Throwable $e) {
                $this->log("notifications skip for {$key}: " . $e->getMessage());
            }

            // Activity logs (mirror happens automatically in ActivityLog::booted
            // for SubscriptionEvent rows — demo activity types here are not in
            // the registry, so they stay audit-trail-only).
            try {
                $count = $f->numberBetween(20, 50);
                $logRows = [];
                for ($i = 0; $i < $count; $i++) {
                    [$type, $subjectType, $subjectName] = $activityTypes[array_rand($activityTypes)];
                    $logRows[] = [
                        'uid'          => Str::random(13),
                        'customer_id'  => $customer->id,
                        'user_id'      => $customer->user_id ?? null,
                        'type'         => $type,
                        'subject_type' => 'App\\Model\\' . ucfirst($subjectType),
                        'subject_id'   => $f->numberBetween(1, 100),
                        'subject_name' => $subjectName,
                        'data'         => json_encode(['source' => 'demo seed']),
                        'created_at'   => now()->subDays($f->numberBetween(0, 180)),
                        'updated_at'   => now()->subDays($f->numberBetween(0, 180)),
                    ];
                }
                DB::table('activity_logs')->insert($logRows);
                $totalLogs += $count;
            } catch (\Throwable $e) {
                $this->log("activity_logs skip for {$key}: " . $e->getMessage());
            }
        }

        // Shared admin broadcasts — each appears in EVERY admin's inbox via
        // the 'admins' audience, illustrating the shared-row pattern.
        $adminBroadcasts = [
            [
                'type'      => 'demo.admin-shared',
                'category'  => Event::CATEGORY_ACTION,
                'severity'  => Event::SEVERITY_WARNING,
                'group_key' => 'demo-shared-admin-banner',
                'title'     => 'Demo: invoice claimed paid',
                'body'      => 'A customer marked an offline invoice as paid. Any admin can review and approve.',
                'action'    => [
                    'label'  => 'Review',
                    'route'  => 'refactor.admin.notifications.index',
                    'params' => [],
                ],
                'created_at' => now(),
            ],
            [
                'type'      => 'plugin.installed',
                'category'  => Event::CATEGORY_ACTION,
                'severity'  => Event::SEVERITY_INFO,
                'group_key' => 'plugin-installed-acelle-ai',
                'title'     => 'New plugin: Acelle AI installed',
                'body'      => 'The Acelle AI plugin has been installed. It helps automate your daily marketing tasks — configure the AI engine to get started.',
                'action'    => [
                    'label'  => 'Configure AI engine',
                    'route'  => 'refactor.admin.ai-settings.show',
                    'params' => ['section' => 'engines'],
                ],
                'created_at' => now()->subHours(2),
            ],
        ];

        foreach ($adminBroadcasts as $b) {
            try {
                DB::table('notifications')->insert([
                    'id'              => (string) Str::uuid(),
                    'type'            => $b['type'],
                    'audience'        => Event::AUDIENCE_ADMINS,
                    'notifiable_type' => null,
                    'notifiable_id'   => null,
                    'subject_type'    => null,
                    'subject_id'      => null,
                    'category'        => $b['category'],
                    'severity'        => $b['severity'],
                    'group_key'       => $b['group_key'],
                    'data'            => json_encode([
                        'title'  => $b['title'],
                        'body'   => $b['body'],
                        'action' => $b['action'],
                    ]),
                    'read_at'         => null,
                    'read_by_type'    => null,
                    'read_by_id'      => null,
                    'dismissed_at'    => null,
                    'dismissed_by_type' => null,
                    'dismissed_by_id' => null,
                    'created_at'      => $b['created_at'],
                    'updated_at'      => $b['created_at'],
                ]);
                $totalNotifs++;
            } catch (\Throwable $e) {
                $this->log("admin broadcast '{$b['type']}' skip: " . $e->getMessage());
            }
        }

        $this->log("seeded {$totalNotifs} notifications + {$totalLogs} activity logs");
    }
}