[ 'class' => InactiveProfileWarning1Mail::class, 'description' => 'Inactive profile warning 1 (first warning)', 'supports' => ['user', 'organization'], ], 'inactive-warning-2' => [ 'class' => InactiveProfileWarning2Mail::class, 'description' => 'Inactive profile warning 2 (second warning)', 'supports' => ['user', 'organization'], ], 'inactive-warning-final' => [ 'class' => InactiveProfileWarningFinalMail::class, 'description' => 'Inactive Profile Final Warning (last warning)', 'supports' => ['user', 'organization'], ], 'user-deleted' => [ 'class' => UserDeletedMail::class, 'description' => 'User Deleted Notification', 'supports' => ['user'], ], 'transfer-received' => [ 'class' => TransferReceived::class, 'description' => 'Transfer/Payment Received Notification', 'supports' => ['user', 'organization'], ], 'profile-link-changed' => [ 'class' => ProfileLinkChangedMail::class, 'description' => 'Profile Link/Name Changed Notification', 'supports' => ['user', 'organization', 'admin', 'bank'], ], 'profile-edited-by-admin' => [ 'class' => ProfileEditedByAdminMail::class, 'description' => 'Profile Edited by Admin Notification', 'supports' => ['user', 'organization'], ], 'verify-email' => [ 'class' => VerifyProfileEmailMailable::class, 'description' => 'Email Verification Request', 'supports' => ['user', 'organization'], ], 'reservation-created' => [ 'class' => ReservationCreatedMail::class, 'description' => 'Reservation Created Notification', 'supports' => ['user', 'organization'], ], 'reservation-cancelled' => [ 'class' => ReservationCancelledMail::class, 'description' => 'Reservation Cancelled Notification', 'supports' => ['user', 'organization'], ], 'reservation-updated' => [ 'class' => ReservationUpdateMail::class, 'description' => 'Reservation Updated Notification', 'supports' => ['user', 'organization'], ], 'reaction-created' => [ 'class' => ReactionCreatedMail::class, 'description' => 'Reaction/Comment Created Notification', 'supports' => ['user', 'organization'], ], 'tag-added' => [ 'class' => TagAddedMail::class, 'description' => 'Tag Added to Profile Notification', 'supports' => ['user', 'organization'], ], 'call-expired' => [ 'class' => CallExpiredMail::class, 'description' => 'Call Expired Notification', 'supports' => ['user', 'organization', 'bank'], ], 'call-expiring' => [ 'class' => CallExpiringMail::class, 'description' => 'Call Expiring Soon Warning', 'supports' => ['user', 'organization', 'bank'], ], 'call-blocked' => [ 'class' => CallBlockedMail::class, 'description' => 'Call Blocked by Admin Notification', 'supports' => ['user', 'organization', 'bank'], ], ]; public function handle() { if ($this->option('list')) { return $this->listEmailTypes(); } $type = $this->option('type'); $receiverType = $this->option('receiver'); $receiverId = $this->option('id'); // Interactive mode if no options provided if (!$type || !$receiverType || !$receiverId) { return $this->interactiveMode(); } return $this->sendEmail($type, $receiverType, $receiverId); } protected function listEmailTypes() { $this->info('Available Email Types:'); $this->newLine(); foreach ($this->emailTypes as $key => $config) { $supports = implode(', ', $config['supports']); $this->line(" {$key}"); $this->line(" Description: {$config['description']}"); $this->line(" Supports: {$supports}"); $this->newLine(); } $this->info('Usage Example:'); $this->line(' php artisan email:send-test --type=inactive-warning-1 --receiver=user --id=102'); $this->newLine(); return 0; } protected function interactiveMode() { $this->info('📧 Test Email Sender - Interactive Mode'); $this->newLine(); // Select email type $typeChoices = array_map( fn ($key, $config) => "{$key} - {$config['description']}", array_keys($this->emailTypes), array_values($this->emailTypes) ); $selectedIndex = array_search( $this->choice('Select email type', $typeChoices), $typeChoices ); $type = array_keys($this->emailTypes)[$selectedIndex]; // Select receiver type $supports = $this->emailTypes[$type]['supports']; $receiverType = $this->choice('Select receiver type', $supports); // Enter receiver ID $receiverId = $this->ask('Enter receiver ID'); return $this->sendEmail($type, $receiverType, $receiverId); } protected function sendEmail($type, $receiverType, $receiverId) { if (!isset($this->emailTypes[$type])) { $this->error("Invalid email type: {$type}"); $this->info('Use --list to see all available types'); return 1; } $config = $this->emailTypes[$type]; if (!in_array($receiverType, $config['supports'])) { $this->error("Email type '{$type}' does not support receiver type '{$receiverType}'"); $this->info('Supported types: ' . implode(', ', $config['supports'])); return 1; } // Get receiver profile $receiver = $this->getReceiver($receiverType, $receiverId); if (!$receiver) { $this->error("Receiver not found: {$receiverType} #{$receiverId}"); return 1; } $this->info("Sending '{$type}' email to {$receiver->name} ({$receiver->email})"); $this->newLine(); try { $mailable = $this->buildMailable($type, $receiver, $receiverType); if ($this->option('queue')) { Mail::to($receiver->email)->queue($mailable); $this->info('✅ Email queued successfully'); $this->line('Run queue worker: php artisan queue:work --stop-when-empty'); } else { Mail::to($receiver->email)->send($mailable); $this->info('✅ Email sent successfully'); } $this->newLine(); $this->line("Recipient: {$receiver->email}"); $this->line("Profile: {$receiver->name}"); $this->line("Language: " . ($receiver->lang_preference ?? 'en')); return 0; } catch (\Exception $e) { $this->error('Failed to send email: ' . $e->getMessage()); $this->line($e->getTraceAsString()); return 1; } } protected function getReceiver($type, $id) { return match($type) { 'user' => User::find($id), 'organization' => Organization::find($id), 'admin' => Admin::find($id), 'bank' => Bank::find($id), default => null, }; } protected function buildMailable($type, $receiver, $receiverType) { // Get test data $accounts = $this->getAccountsData($receiver); $totalBalance = $this->getTotalBalance($accounts); return match($type) { 'inactive-warning-1' => new InactiveProfileWarning1Mail( $receiver, ucfirst($receiverType), '2 weeks', 14, $accounts, $totalBalance, 351 ), 'inactive-warning-2' => new InactiveProfileWarning2Mail( $receiver, ucfirst($receiverType), '1 week', 7, $accounts, $totalBalance, 358 ), 'inactive-warning-final' => new InactiveProfileWarningFinalMail( $receiver, ucfirst($receiverType), '24 hours', 1, $accounts, $totalBalance, 365 ), 'user-deleted' => new UserDeletedMail( $receiver, $accounts, $totalBalance, $this->getTransferTargetAccount() ), 'transfer-received' => $this->buildTransferReceivedMail($receiver), 'profile-link-changed' => new ProfileLinkChangedMail( $receiver, $this->getLinkedProfileForTest($receiver), 'attached' ), 'profile-edited-by-admin' => new ProfileEditedByAdminMail( $receiver, 'Test Admin', 'Updated profile information for testing purposes' ), 'verify-email' => new VerifyProfileEmailMailable( $receiver->email, url('/verify-email/' . base64_encode($receiver->email)) ), 'reservation-created' => $this->buildReservationMail($receiver, ReservationCreatedMail::class), 'reservation-cancelled' => $this->buildReservationMail($receiver, ReservationCancelledMail::class), 'reservation-updated' => $this->buildReservationMail($receiver, ReservationUpdateMail::class), 'reaction-created' => $this->buildReactionMail($receiver), 'tag-added' => $this->buildTagAddedMail($receiver), 'call-expired' => $this->buildCallExpiredMail($receiver, $receiverType), 'call-expiring' => $this->buildCallExpiringMail($receiver, $receiverType), 'call-blocked' => $this->buildCallBlockedMail($receiver, $receiverType), default => throw new \Exception("Mailable builder not implemented for type: {$type}"), }; } protected function getAccountsData($profile) { $accounts = []; $profileAccounts = $profile->accounts()->active()->notRemoved()->get(); foreach ($profileAccounts as $account) { \Cache::forget("account_balance_{$account->id}"); $accounts[] = [ 'id' => $account->id, 'name' => $account->name, 'balance' => $account->balance, 'balanceFormatted' => tbFormat($account->balance), ]; } return $accounts; } protected function getTotalBalance($accounts) { return array_sum(array_column($accounts, 'balance')); } protected function getTransferTargetAccount() { // Get a random organization account or create test data $account = Account::whereHasMorph('accountable', [Organization::class]) ->active() ->notRemoved() ->first(); return $account ? [ 'id' => $account->id, 'name' => $account->name, ] : [ 'id' => 1, 'name' => 'Test Organization Account', ]; } protected function buildTransferReceivedMail($receiver) { $senderAccount = $receiver->accounts()->active()->notRemoved()->first(); if (!$senderAccount) { throw new \Exception('Receiver has no active accounts'); } return new TransferReceived( $receiver->name, 120, // 2 hours in minutes tbFormat(120), 'Test Transfer', $senderAccount->name, $receiver->email, url('/profile/' . $receiver->name) ); } protected function buildReservationMail($receiver, $mailClass) { $postTitle = 'Test Post - ' . $mailClass; $postOwner = 'Test Post Owner'; $postUrl = url('/posts/test-post'); $reservationDate = now()->addDays(7)->format('Y-m-d H:i'); return new $mailClass( $receiver->name, $postTitle, $postOwner, $postUrl, $reservationDate ); } protected function buildReactionMail($receiver) { return new ReactionCreatedMail( $receiver->name, 'Test Commenter', 'Test Post Title', 'This is a test comment for email testing purposes.', url('/posts/test-post') ); } protected function buildTagAddedMail($receiver) { return new TagAddedMail( $receiver->name, 'Test Tag', 'Test Admin', url('/profile/' . $receiver->name) ); } protected function getTestCall($receiver): Call { return Call::where('callable_type', get_class($receiver)) ->where('callable_id', $receiver->id) ->with(['tag']) ->first() ?? Call::with(['tag'])->first() ?? throw new \Exception('No calls found for test'); } protected function buildCallExpiredMail($receiver, $receiverType): CallExpiredMail { return new CallExpiredMail($this->getTestCall($receiver), $receiver, ucfirst($receiverType)); } protected function buildCallExpiringMail($receiver, $receiverType): CallExpiringMail { return new CallExpiringMail($this->getTestCall($receiver), $receiver, ucfirst($receiverType), 7); } protected function buildCallBlockedMail($receiver, $receiverType): CallBlockedMail { return new CallBlockedMail($this->getTestCall($receiver), $receiver, ucfirst($receiverType)); } protected function getLinkedProfileForTest($receiver) { // Find a different profile type to use as the linked profile // If receiver is a User, find an Organization/Admin/Bank to link // If receiver is Organization/Admin/Bank, find a User to link if ($receiver instanceof User) { // Try to find an organization first, then admin, then bank $linked = Organization::first() ?? Admin::first() ?? Bank::first(); } else { // For Organization/Admin/Bank, find a user $linked = User::first(); } // If we can't find any other profile, just return the same receiver return $linked ?? $receiver; } }