senderOptions = $senderOptions; $this->recipients = $recipients; $this->composer = $composer; $this->dispatch = $dispatch; $this->imageService = $imageService; } public function options(): JsonResponse { return response()->json([ 'ok' => true, 'parents' => $this->recipients->parentsWithEmails(), 'fromOptions' => $this->senderOptions->listOptions(), ]); } public function send(Request $request): JsonResponse { $mode = (string) ($request->input('mode') ?? 'standard'); $subject = trim((string) ($request->input('subject') ?? '')); $fromKey = trim((string) ($request->input('from_key') ?? 'general')); $body = (string) ($request->input('body_html') ?? ''); $wrapLayout = (bool) $request->boolean('wrap_layout'); $preheader = (string) ($request->input('preheader') ?? ''); $ctaText = (string) ($request->input('cta_text') ?? ''); $ctaUrl = (string) ($request->input('cta_url') ?? ''); $testEmail = trim((string) ($request->input('test_email') ?? '')); $isTestOnly = (bool) $request->boolean('send_test_only'); $body = $this->composer->sanitizeHtml($body); if ($subject === '' || $body === '') { return response()->json([ 'ok' => false, 'message' => 'Subject and Body are required.', ], 422); } $personalize = $mode === 'personalized'; if ($isTestOnly) { if ($testEmail === '') { return response()->json([ 'ok' => false, 'message' => 'Provide a test email address.', ], 422); } $result = $this->dispatch->sendTest([ 'wrap_layout' => $wrapLayout, 'subject' => $subject, 'body_html' => $body, 'recipient_name' => 'Parent', 'preheader' => $preheader, 'cta_text' => $ctaText, 'cta_url' => $ctaUrl, 'personalize' => $personalize, 'test_email' => $testEmail, 'from_key' => $fromKey, ]); return response()->json($result, $result['ok'] ? 200 : 500); } $rawIds = $request->input('parent_ids', []); $ids = $this->normalizeIds($rawIds); if (empty($ids)) { return response()->json([ 'ok' => false, 'message' => 'Please select at least one parent.', ], 422); } $recipients = $this->recipients->recipientsByIds($ids); if (empty($recipients)) { return response()->json([ 'ok' => false, 'message' => 'No valid parent emails found.', ], 422); } $stats = $this->dispatch->sendBroadcast([ 'wrap_layout' => $wrapLayout, 'subject' => $subject, 'body_html' => $body, 'preheader' => $preheader, 'cta_text' => $ctaText, 'cta_url' => $ctaUrl, 'personalize' => $personalize, 'from_key' => $fromKey, 'mode' => $mode, ], $recipients); $message = sprintf( 'Broadcast finished. Mode: %s. Sent: %d/%d. Failures: %d.', $stats['mode'], $stats['sent'], $stats['attempted'], $stats['failed'] ); return response()->json([ 'ok' => $stats['failed'] === 0, 'stats' => $stats, 'message' => $message, ]); } public function uploadImage(Request $request): JsonResponse { $file = $request->file('image'); if (!$file || !$file->isValid()) { return response()->json([ 'success' => false, 'error' => 'No image uploaded', ], 400); } $result = $this->imageService->store($file); if (!$result['ok']) { return response()->json([ 'success' => false, 'error' => $result['error'] ?? 'Upload failed', ], $result['status'] ?? 400); } $newHash = function_exists('csrf_hash') ? csrf_hash() : csrf_token(); return response()->json([ 'success' => true, 'url' => $result['url'], 'csrf_hash' => $newHash, ])->header('X-CSRF-HASH', (string) $newHash); } private function normalizeIds($rawIds): array { $ids = []; $raw = is_array($rawIds) ? $rawIds : [$rawIds]; foreach ($raw as $value) { if (is_string($value) && strpos($value, ',') !== false) { foreach (explode(',', $value) as $piece) { $ids[] = (int) $piece; } continue; } $ids[] = (int) $value; } return array_values(array_unique(array_filter($ids))); } }