Fix transfer share links and redesign recipient download email.
Deploy Ladill Transfer / deploy (push) Successful in 36s

Use transfer.ladill.com share URLs in notifications and APIs, add the email
logo, and show file details with a prominent download button.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-08 15:56:06 +00:00
co-authored by Cursor
parent a0fce19402
commit c396c97ce2
7 changed files with 61 additions and 20 deletions
@@ -139,7 +139,7 @@ class ChunkedUploadController extends Controller
'data' => [ 'data' => [
'id' => $transfer->id, 'id' => $transfer->id,
'title' => $transfer->title, 'title' => $transfer->title,
'public_url' => $transfer->qrCode?->publicUrl(), 'public_url' => $transfer->qrCode?->shareUrl(),
'paid_until' => $transfer->paid_until?->toIso8601String(), 'paid_until' => $transfer->paid_until?->toIso8601String(),
'expires_at' => $transfer->paid_until?->toIso8601String(), 'expires_at' => $transfer->paid_until?->toIso8601String(),
'files' => $transfer->files->map(fn ($file) => [ 'files' => $transfer->files->map(fn ($file) => [
@@ -68,7 +68,7 @@ class TransferController extends Controller
'data' => [ 'data' => [
'id' => $transfer->id, 'id' => $transfer->id,
'title' => $transfer->title, 'title' => $transfer->title,
'public_url' => $transfer->qrCode?->publicUrl(), 'public_url' => $transfer->qrCode?->shareUrl(),
'paid_until' => $transfer->paid_until?->toIso8601String(), 'paid_until' => $transfer->paid_until?->toIso8601String(),
'expires_at' => $transfer->paid_until?->toIso8601String(), 'expires_at' => $transfer->paid_until?->toIso8601String(),
'files' => $transfer->files->map(fn ($file) => [ 'files' => $transfer->files->map(fn ($file) => [
+8
View File
@@ -86,6 +86,14 @@ class QrCode extends Model
return self::publicBaseUrl() . '/q/' . $this->short_code; return self::publicBaseUrl() . '/q/' . $this->short_code;
} }
/** Direct link for emails and share UI — always on the Transfer app host. */
public function shareUrl(): string
{
$base = rtrim((string) config('app.url'), '/');
return $base . '/q/' . $this->short_code;
}
/** /**
* Base URL for public QR links. Always the short platform domain * Base URL for public QR links. Always the short platform domain
* (ladill.com) never the signed-in account/product host so printed * (ladill.com) never the signed-in account/product host so printed
@@ -24,7 +24,7 @@ class TransferRecipientNotification extends Notification
public function toMail(mixed $notifiable): MailMessage public function toMail(mixed $notifiable): MailMessage
{ {
$transfer = $this->transfer->loadMissing(['files', 'qrCode', 'user']); $transfer = $this->transfer->loadMissing(['files', 'qrCode', 'user']);
$publicUrl = $transfer->qrCode?->publicUrl() ?? ''; $publicUrl = $transfer->qrCode?->shareUrl() ?? '';
return (new MailMessage()) return (new MailMessage())
->subject($this->subjectLine()) ->subject($this->subjectLine())
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

@@ -367,7 +367,11 @@
<div class="email-container"> <div class="email-container">
{{-- Header with Logo --}} {{-- Header with Logo --}}
<div class="email-header"> <div class="email-header">
<img src="{{ config('app.url') }}/images/logo/ladill-logo-white.png" alt="Ladill" class="email-logo"> @hasSection('email-header')
@yield('email-header')
@else
<img src="{{ config('app.url') }}/images/logo/ladill-logo-white.png" alt="Ladill" class="email-logo">
@endif
</div> </div>
@hasSection('icon-header') @hasSection('icon-header')
@@ -1,25 +1,34 @@
@extends('mail.notifications.layout') @extends('mail.notifications.layout')
@section('email-header')
<img src="{{ config('app.url') }}/images/logo/ladilltransfer-logo-email.png" alt="Ladill Transfer" class="email-logo" style="max-height:44px;">
@endsection
@section('content') @section('content')
@php @php
$fileCount = $transfer->files->count(); $fileCount = $transfer->files->count();
$availableUntil = $transfer->isInGracePeriod() $availableUntil = $transfer->isInGracePeriod()
? $transfer->grace_ends_at ? $transfer->grace_ends_at
: $transfer->paid_until; : $transfer->paid_until;
@endphp $fmtBytes = function (int $bytes) {
if ($bytes >= 1048576) {
return number_format($bytes / 1048576, 1).' MB';
}
if ($bytes >= 1024) {
return number_format($bytes / 1024, 0).' KB';
}
<div class="highlight-box highlight-box-success"> return $bytes.' B';
<p class="highlight-box-text">📁 File transfer</p> };
<p class="highlight-box-subtext">{{ $transfer->title }}</p> @endphp
</div>
@if($milestone === 'created') @if($milestone === 'created')
<h1 class="email-title">You have files to download</h1> <h1 class="email-title">You have files to download</h1>
<p class="email-text"> <p class="email-text">
@if($senderName) @if($senderName)
<strong>{{ $senderName }}</strong> shared {{ $fileCount === 1 ? 'a file' : $fileCount.' files' }} with you via Ladill Transfer. <strong>{{ $senderName }}</strong> shared {{ $fileCount === 1 ? 'a file' : $fileCount.' files' }} with you through Ladill Transfer.
@else @else
Someone shared {{ $fileCount === 1 ? 'a file' : $fileCount.' files' }} with you via Ladill Transfer. Someone shared {{ $fileCount === 1 ? 'a file' : $fileCount.' files' }} with you through Ladill Transfer.
@endif @endif
</p> </p>
@elseif($milestone === 'grace') @elseif($milestone === 'grace')
@@ -47,34 +56,54 @@
</p> </p>
@endif @endif
<div class="email-details" style="margin-top:20px;">
<p class="email-details-title">Transfer</p>
<div class="email-details-row">
<p class="email-details-label">Title</p>
<p class="email-details-value">{{ $transfer->title }}</p>
</div>
<div class="email-details-row">
<p class="email-details-label">Files</p>
<p class="email-details-value">{{ $fileCount }} {{ $fileCount === 1 ? 'file' : 'files' }} · {{ $fmtBytes($transfer->storage_bytes) }}</p>
</div>
@foreach($transfer->files as $file)
<div class="email-details-row">
<p class="email-details-label">{{ $file->original_name }}</p>
<p class="email-details-value" style="font-weight:500;color:#475569;">{{ $fmtBytes($file->size_bytes) }}</p>
</div>
@endforeach
</div>
@if($transfer->message) @if($transfer->message)
<p class="email-text" style="background:#f8fafc;border-radius:10px;padding:14px 16px;color:#475569;"> <p class="email-text" style="background:#f8fafc;border-radius:10px;padding:14px 16px;color:#475569;border-left:4px solid #4f46e5;">
<span style="display:block;font-size:12px;font-weight:600;color:#64748b;text-transform:uppercase;letter-spacing:0.04em;margin-bottom:6px;">Message from sender</span>
{{ $transfer->message }} {{ $transfer->message }}
</p> </p>
@endif @endif
@if($transfer->isPasswordProtected()) @if($transfer->isPasswordProtected())
<p class="email-text" style="font-size:13px;color:#b45309;"> <p class="email-text" style="font-size:13px;color:#b45309;background:#fffbeb;border-radius:10px;padding:12px 14px;border:1px solid #fde68a;">
This transfer is password protected. The sender will need to share the password with you separately. This transfer is password protected. The sender will share the password with you separately.
</p> </p>
@endif @endif
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin:24px 0;"> <table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin:28px 0 20px;">
<tr> <tr>
<td align="center"> <td align="center">
<a href="{{ $publicUrl }}" class="email-button" style="display:inline-block;background:#4f46e5;color:#ffffff;text-decoration:none;padding:12px 28px;border-radius:10px;font-size:14px;font-weight:600;"> <a href="{{ $publicUrl }}" class="email-button" style="display:inline-block;background:linear-gradient(135deg,#4f46e5 0%,#7c3aed 100%);color:#ffffff;text-decoration:none;padding:16px 36px;border-radius:12px;font-size:16px;font-weight:700;box-shadow:0 8px 24px rgba(79,70,229,0.35);">
Download files Download {{ $fileCount === 1 ? 'file' : 'files' }}
</a> </a>
</td> </td>
</tr> </tr>
</table> </table>
<p class="email-text" style="font-size:13px;color:#6b7280;"> <p class="email-text" style="font-size:13px;color:#6b7280;text-align:center;">
Or open this link: <a href="{{ $publicUrl }}" style="color:#4f46e5;">{{ $publicUrl }}</a> Or copy this link into your browser:<br>
<a href="{{ $publicUrl }}" style="color:#4f46e5;word-break:break-all;">{{ $publicUrl }}</a>
</p> </p>
@if($availableUntil && $milestone !== 'grace') @if($availableUntil && $milestone !== 'grace')
<p class="email-text" style="font-size:13px;color:#6b7280;"> <p class="email-text" style="font-size:13px;color:#6b7280;text-align:center;">
Available until {{ $availableUntil->format('F j, Y') }}. Available until {{ $availableUntil->format('F j, Y') }}.
</p> </p>
@endif @endif