Fix transfer share links and redesign recipient download email.
Deploy Ladill Transfer / deploy (push) Successful in 36s
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:
@@ -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) => [
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user