Brand Frontdesk Laravel mail for hosts and report recipients.
Deploy Ladill Frontdesk / deploy (push) Has been cancelled
Deploy Ladill Frontdesk / deploy (push) Has been cancelled
Pass organization logo/name through ContactMessageMail for daily reports and campaigns, and drop the Ladill product footer. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -39,7 +39,7 @@ class SendDailyReportCommand extends Command
|
|||||||
|
|
||||||
foreach ($recipients as $address) {
|
foreach ($recipients as $address) {
|
||||||
if (is_string($address) && filter_var($address, FILTER_VALIDATE_EMAIL)) {
|
if (is_string($address) && filter_var($address, FILTER_VALIDATE_EMAIL)) {
|
||||||
$email->send($address, $subject, $body);
|
$email->send($address, $subject, $body, $organization->name, null, $organization);
|
||||||
$sent++;
|
$sent++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Jobs;
|
|||||||
use App\Models\Activity;
|
use App\Models\Activity;
|
||||||
use App\Models\Campaign;
|
use App\Models\Campaign;
|
||||||
use App\Models\CampaignRecipient;
|
use App\Models\CampaignRecipient;
|
||||||
|
use App\Models\Organization;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Services\Campaigns\CampaignBillingService;
|
use App\Services\Campaigns\CampaignBillingService;
|
||||||
use App\Services\Campaigns\CampaignMessageService;
|
use App\Services\Campaigns\CampaignMessageService;
|
||||||
@@ -56,6 +57,8 @@ class SendCampaignMessageJob implements ShouldQueue
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$organization = Organization::query()->where('owner_ref', $campaign->owner_ref)->first();
|
||||||
|
|
||||||
$sent = $campaign->channel === Campaign::CHANNEL_SMS
|
$sent = $campaign->channel === Campaign::CHANNEL_SMS
|
||||||
? $sms->send((string) $recipient->recipient_phone, $body)
|
? $sms->send((string) $recipient->recipient_phone, $body)
|
||||||
: $email->send(
|
: $email->send(
|
||||||
@@ -64,6 +67,7 @@ class SendCampaignMessageJob implements ShouldQueue
|
|||||||
$body,
|
$body,
|
||||||
$this->senderName($campaign),
|
$this->senderName($campaign),
|
||||||
$this->senderEmail($campaign),
|
$this->senderEmail($campaign),
|
||||||
|
$organization,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (! $sent) {
|
if (! $sent) {
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ class ContactMessageMail extends Mailable
|
|||||||
public string $bodyText,
|
public string $bodyText,
|
||||||
public ?string $fromName = null,
|
public ?string $fromName = null,
|
||||||
public ?string $replyToAddress = null,
|
public ?string $replyToAddress = null,
|
||||||
|
public ?string $logoUrl = null,
|
||||||
|
public ?string $companyName = null,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function envelope(): Envelope
|
public function envelope(): Envelope
|
||||||
@@ -34,6 +36,8 @@ class ContactMessageMail extends Mailable
|
|||||||
with: [
|
with: [
|
||||||
'bodyText' => $this->bodyText,
|
'bodyText' => $this->bodyText,
|
||||||
'fromName' => $this->fromName,
|
'fromName' => $this->fromName,
|
||||||
|
'logoUrl' => $this->logoUrl,
|
||||||
|
'companyName' => $this->companyName,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
namespace App\Services\Comms;
|
namespace App\Services\Comms;
|
||||||
|
|
||||||
use App\Mail\ContactMessageMail;
|
use App\Mail\ContactMessageMail;
|
||||||
|
use App\Models\Organization;
|
||||||
|
use App\Support\OrganizationBranding;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
@@ -13,18 +15,31 @@ use Illuminate\Support\Facades\Mail;
|
|||||||
*/
|
*/
|
||||||
class EmailService
|
class EmailService
|
||||||
{
|
{
|
||||||
public function send(string $to, string $subject, string $body, ?string $fromName = null, ?string $replyTo = null): bool
|
public function send(
|
||||||
{
|
string $to,
|
||||||
|
string $subject,
|
||||||
|
string $body,
|
||||||
|
?string $fromName = null,
|
||||||
|
?string $replyTo = null,
|
||||||
|
?Organization $organization = null,
|
||||||
|
): bool {
|
||||||
if (! filter_var($to, FILTER_VALIDATE_EMAIL)) {
|
if (! filter_var($to, FILTER_VALIDATE_EMAIL)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$logoUrl = null;
|
||||||
|
$companyName = null;
|
||||||
|
if ($organization) {
|
||||||
|
$logoUrl = OrganizationBranding::emailLogoUrl($organization);
|
||||||
|
$companyName = $organization->name;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Mail::to($to)->send(new ContactMessageMail($subject, $body, $fromName, $replyTo));
|
Mail::to($to)->send(new ContactMessageMail($subject, $body, $fromName, $replyTo, $logoUrl, $companyName));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
Log::warning('CRM email send failed', ['to' => $to, 'error' => $e->getMessage()]);
|
Log::warning('Frontdesk email send failed', ['to' => $to, 'error' => $e->getMessage()]);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,26 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body style="margin:0;background:#f1f5f9;font-family:Arial,Helvetica,sans-serif;color:#0f172a;">
|
<body style="margin:0;background:#f8fafc;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;color:#0f172a;">
|
||||||
<div style="max-width:560px;margin:0 auto;padding:32px 16px;">
|
<div style="max-width:600px;margin:0 auto;padding:48px 16px;">
|
||||||
<div style="background:#ffffff;border-radius:16px;padding:28px;border:1px solid #e2e8f0;">
|
@if (! empty($logoUrl) || ! empty($companyName))
|
||||||
<div style="white-space:pre-wrap;font-size:14px;line-height:1.6;color:#334155;">{{ $bodyText }}</div>
|
<div style="text-align:center;padding:0 0 24px;">
|
||||||
|
@if (! empty($logoUrl))
|
||||||
|
<img src="{{ $logoUrl }}" alt="{{ $companyName }}" style="height:42px;max-height:42px;width:auto;">
|
||||||
|
@else
|
||||||
|
<div style="font-size:22px;font-weight:700;letter-spacing:-0.02em;color:#0f172a;">{{ $companyName }}</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<div style="background:#ffffff;border-radius:16px;padding:40px;border:1px solid #e2e8f0;box-shadow:0 4px 6px -1px rgba(0,0,0,0.1);">
|
||||||
|
<div style="white-space:pre-wrap;font-size:16px;line-height:1.7;color:#475569;">{{ $bodyText }}</div>
|
||||||
@if (! empty($fromName))
|
@if (! empty($fromName))
|
||||||
<p style="margin-top:24px;font-size:13px;color:#64748b;">— {{ $fromName }}</p>
|
<p style="margin-top:24px;font-size:14px;color:#64748b;">— {{ $fromName }}</p>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<p style="margin-top:16px;text-align:center;font-size:11px;color:#94a3b8;">Sent via Ladill Frontdesk</p>
|
@if (! empty($companyName))
|
||||||
|
<p style="margin-top:20px;text-align:center;font-size:13px;color:#94a3b8;">{{ $companyName }}</p>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -121,4 +121,37 @@ class FrontdeskEmailBrandingTest extends TestCase
|
|||||||
&& ! str_contains($html, 'Sent via Ladill');
|
&& ! str_contains($html, 'Sent via Ladill');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_laravel_mail_uses_organization_branding(): void
|
||||||
|
{
|
||||||
|
Storage::fake('public');
|
||||||
|
\Illuminate\Support\Facades\Mail::fake();
|
||||||
|
|
||||||
|
$path = UploadedFile::fake()->image('hq-logo.png', 400, 120)
|
||||||
|
->store('frontdesk/organizations/'.$this->organization->id, 'public');
|
||||||
|
$this->organization->update(['logo_path' => $path]);
|
||||||
|
|
||||||
|
$logoUrl = OrganizationBranding::emailLogoUrl($this->organization->fresh());
|
||||||
|
|
||||||
|
$sent = app(\App\Services\Comms\EmailService::class)->send(
|
||||||
|
'admin@example.com',
|
||||||
|
'Daily summary',
|
||||||
|
"Visitors checked in: 3\nDeliveries: 1",
|
||||||
|
$this->organization->name,
|
||||||
|
null,
|
||||||
|
$this->organization->fresh(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertTrue($sent);
|
||||||
|
|
||||||
|
\Illuminate\Support\Facades\Mail::assertSent(\App\Mail\ContactMessageMail::class, function (\App\Mail\ContactMessageMail $mail) use ($logoUrl) {
|
||||||
|
$html = $mail->render();
|
||||||
|
|
||||||
|
return $mail->logoUrl === $logoUrl
|
||||||
|
&& $mail->companyName === 'Frontdesk HQ'
|
||||||
|
&& str_contains($html, 'Frontdesk HQ')
|
||||||
|
&& str_contains($html, $logoUrl)
|
||||||
|
&& ! str_contains($html, 'Sent via Ladill');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user