Fix admin content tab retention on test mail & save, add SMTP encryption support, and provide Gmail DNS deliverability diagnostic guidance
This commit is contained in:
@@ -118,8 +118,10 @@ class ContentController extends Controller
|
||||
|
||||
Setting::flushCache();
|
||||
|
||||
return redirect()->route('admin.content.index')
|
||||
->with('success', 'Layout en teksten zijn direct opgeslagen en live bijgewerkt in de webshop!');
|
||||
$tab = $request->input('active_tab');
|
||||
$redirect = $tab ? redirect()->route('admin.content.index', ['tab' => $tab]) : redirect()->route('admin.content.index');
|
||||
|
||||
return $redirect->with('success', 'Layout en teksten zijn direct opgeslagen en live bijgewerkt in de webshop!');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,24 +166,26 @@ class ContentController extends Controller
|
||||
$recipient = $validated['test_email'];
|
||||
$defaultMailer = config('mail.default');
|
||||
$fromAddress = config('mail.from.address');
|
||||
$smtpHost = config('mail.mailers.smtp.host');
|
||||
$smtpPort = config('mail.mailers.smtp.port');
|
||||
|
||||
try {
|
||||
Mail::raw("Dit is een test e-mail vanaf Jouwdriedee.nl om de e-mail bezorging naar {$recipient} te controleren.\n\nActieve mailer: {$defaultMailer}\nAfzender: {$fromAddress}\nDatum: ".now()->format('d-m-Y H:i:s'), function ($message) use ($recipient) {
|
||||
Mail::raw("Dit is een test e-mail vanaf Jouwdriedee.nl om de e-mail bezorging naar {$recipient} te controleren.\n\nActieve mailer: {$defaultMailer}\nSMTP Server: {$smtpHost}:{$smtpPort}\nAfzender: {$fromAddress}\nDatum: ".now()->format('d-m-Y H:i:s'), function ($message) use ($recipient) {
|
||||
$message->to($recipient)
|
||||
->subject('Test E-mail vanaf Jouwdriedee.nl');
|
||||
});
|
||||
|
||||
if ($defaultMailer === 'log') {
|
||||
return redirect()->back()
|
||||
return redirect()->route('admin.content.index', ['tab' => 'email'])
|
||||
->with('warning', "De testmail is weggeschreven naar de serverlogs (MAIL_MAILER staat op 'log'). Er is GEEN echte e-mail naar {$recipient} verzonden. Stel op Coolify geldige SMTP-gegevens in (bijv. Brevo, Resend of je hosting SMTP) om echte e-mails te bezorgen.");
|
||||
}
|
||||
|
||||
return redirect()->back()
|
||||
->with('success', "Test e-mail succesvol verzonden via driver '{$defaultMailer}' naar {$recipient}! Controleer je inbox en eventueel je spambox.");
|
||||
return redirect()->route('admin.content.index', ['tab' => 'email'])
|
||||
->with('success', "Test e-mail succesvol overhandigd aan de SMTP-server ({$smtpHost}:{$smtpPort}) voor {$recipient}! Controleer je inbox en eventueel je spamfolder.");
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('Test mail error: '.$e->getMessage(), ['trace' => $e->getTraceAsString()]);
|
||||
|
||||
return redirect()->back()
|
||||
return redirect()->route('admin.content.index', ['tab' => 'email'])
|
||||
->with('error', "Fout bij verzenden van e-mail via {$defaultMailer}: ".$e->getMessage());
|
||||
}
|
||||
}
|
||||
@@ -198,7 +202,7 @@ class ContentController extends Controller
|
||||
|
||||
$statusText = $new ? 'OFFLINE gezet' : 'weer ONLINE gezet';
|
||||
|
||||
return redirect()->back()
|
||||
return redirect()->route('admin.content.index', ['tab' => 'offline'])
|
||||
->with('success', "De webshop is direct {$statusText}!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ return [
|
||||
'url' => env('MAIL_URL'),
|
||||
'host' => env('MAIL_HOST', '127.0.0.1'),
|
||||
'port' => env('MAIL_PORT', 2525),
|
||||
'encryption' => env('MAIL_ENCRYPTION'),
|
||||
'username' => env('MAIL_USERNAME'),
|
||||
'password' => env('MAIL_PASSWORD'),
|
||||
'timeout' => null,
|
||||
|
||||
@@ -220,3 +220,4 @@
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -280,3 +280,4 @@
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -3,7 +3,29 @@
|
||||
@section('title', 'Layout & Teksten Aanpassen | Jouwdriedee Beheer')
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6" x-data="{ activeTab: 'hero' }">
|
||||
<div class="space-y-6" x-data="{
|
||||
activeTab: '{{ request('tab', session('tab', 'hero')) }}',
|
||||
init() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const urlTab = urlParams.get('tab');
|
||||
const hash = window.location.hash ? window.location.hash.replace('#', '') : null;
|
||||
const validTabs = ['hero', 'usp', 'sections', 'calculator', 'announcement', 'footer', 'brand', 'email', 'offline'];
|
||||
|
||||
if (urlTab && validTabs.includes(urlTab)) {
|
||||
this.activeTab = urlTab;
|
||||
} else if (hash && validTabs.includes(hash)) {
|
||||
this.activeTab = hash;
|
||||
}
|
||||
},
|
||||
switchTab(tab) {
|
||||
this.activeTab = tab;
|
||||
if (window.history.replaceState) {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('tab', tab);
|
||||
window.history.replaceState(null, '', url.toString());
|
||||
}
|
||||
}
|
||||
}">
|
||||
|
||||
<!-- Header Section -->
|
||||
<div class="flex flex-col sm:flex-row sm:items-center justify-between pb-6 border-b border-slate-200/60 gap-4">
|
||||
@@ -32,12 +54,13 @@
|
||||
<!-- Main Form -->
|
||||
<form action="{{ route('admin.content.update') }}" method="POST" class="space-y-6">
|
||||
@csrf
|
||||
<input type="hidden" name="active_tab" :value="activeTab">
|
||||
|
||||
<!-- Tabs Navigation (Sleek Segmented Strip) -->
|
||||
<div class="flex items-center space-x-1 p-1 bg-slate-100/80 rounded-xl overflow-x-auto text-xs font-medium w-full max-w-full">
|
||||
<button
|
||||
type="button"
|
||||
@click="activeTab = 'hero'"
|
||||
@click="switchTab('hero')"
|
||||
:class="activeTab === 'hero' ? 'bg-white text-slate-900 font-semibold shadow-2xs' : 'text-slate-600 hover:text-slate-900'"
|
||||
class="px-3 py-1.5 rounded-lg transition whitespace-nowrap cursor-pointer"
|
||||
>
|
||||
@@ -46,7 +69,7 @@
|
||||
|
||||
<button
|
||||
type="button"
|
||||
@click="activeTab = 'usp'"
|
||||
@click="switchTab('usp')"
|
||||
:class="activeTab === 'usp' ? 'bg-white text-slate-900 font-semibold shadow-2xs' : 'text-slate-600 hover:text-slate-900'"
|
||||
class="px-3 py-1.5 rounded-lg transition whitespace-nowrap cursor-pointer"
|
||||
>
|
||||
@@ -55,7 +78,7 @@
|
||||
|
||||
<button
|
||||
type="button"
|
||||
@click="activeTab = 'sections'"
|
||||
@click="switchTab('sections')"
|
||||
:class="activeTab === 'sections' ? 'bg-white text-slate-900 font-semibold shadow-2xs' : 'text-slate-600 hover:text-slate-900'"
|
||||
class="px-3 py-1.5 rounded-lg transition whitespace-nowrap cursor-pointer"
|
||||
>
|
||||
@@ -64,7 +87,7 @@
|
||||
|
||||
<button
|
||||
type="button"
|
||||
@click="activeTab = 'calculator'"
|
||||
@click="switchTab('calculator')"
|
||||
:class="activeTab === 'calculator' ? 'bg-white text-slate-900 font-semibold shadow-2xs' : 'text-slate-600 hover:text-slate-900'"
|
||||
class="px-3 py-1.5 rounded-lg transition whitespace-nowrap cursor-pointer"
|
||||
>
|
||||
@@ -73,7 +96,7 @@
|
||||
|
||||
<button
|
||||
type="button"
|
||||
@click="activeTab = 'announcement'"
|
||||
@click="switchTab('announcement')"
|
||||
:class="activeTab === 'announcement' ? 'bg-white text-slate-900 font-semibold shadow-2xs' : 'text-slate-600 hover:text-slate-900'"
|
||||
class="px-3 py-1.5 rounded-lg transition whitespace-nowrap cursor-pointer"
|
||||
>
|
||||
@@ -82,7 +105,7 @@
|
||||
|
||||
<button
|
||||
type="button"
|
||||
@click="activeTab = 'footer'"
|
||||
@click="switchTab('footer')"
|
||||
:class="activeTab === 'footer' ? 'bg-white text-slate-900 font-semibold shadow-2xs' : 'text-slate-600 hover:text-slate-900'"
|
||||
class="px-3 py-1.5 rounded-lg transition whitespace-nowrap cursor-pointer"
|
||||
>
|
||||
@@ -91,7 +114,7 @@
|
||||
|
||||
<button
|
||||
type="button"
|
||||
@click="activeTab = 'brand'"
|
||||
@click="switchTab('brand')"
|
||||
:class="activeTab === 'brand' ? 'bg-white text-slate-900 font-semibold shadow-2xs' : 'text-slate-600 hover:text-slate-900'"
|
||||
class="px-3 py-1.5 rounded-lg transition whitespace-nowrap cursor-pointer"
|
||||
>
|
||||
@@ -100,7 +123,7 @@
|
||||
|
||||
<button
|
||||
type="button"
|
||||
@click="activeTab = 'email'"
|
||||
@click="switchTab('email')"
|
||||
:class="activeTab === 'email' ? 'bg-white text-slate-900 font-semibold shadow-2xs' : 'text-slate-600 hover:text-slate-900'"
|
||||
class="px-3 py-1.5 rounded-lg transition whitespace-nowrap cursor-pointer"
|
||||
>
|
||||
@@ -109,7 +132,7 @@
|
||||
|
||||
<button
|
||||
type="button"
|
||||
@click="activeTab = 'offline'"
|
||||
@click="switchTab('offline')"
|
||||
:class="activeTab === 'offline' ? 'bg-rose-500 text-white font-semibold shadow-2xs' : 'text-rose-600 hover:bg-rose-50'"
|
||||
class="px-3 py-1.5 rounded-lg transition whitespace-nowrap cursor-pointer flex items-center space-x-1.5"
|
||||
>
|
||||
@@ -657,10 +680,10 @@
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-xs">
|
||||
<div class="p-4 rounded-xl bg-slate-50/70 border border-slate-200/60 space-y-2">
|
||||
<div class="font-semibold text-slate-500 text-[11px] uppercase tracking-wider">Huidige Mail Instellingen</div>
|
||||
<div class="font-semibold text-slate-500 text-[11px] uppercase tracking-wider">Huidige Mail & SMTP Instellingen</div>
|
||||
<div class="flex justify-between py-1 border-b border-slate-200/50">
|
||||
<span class="text-slate-500">MAIL_MAILER:</span>
|
||||
<span class="font-mono text-slate-900">{{ config('mail.default') }}</span>
|
||||
<span class="font-mono font-semibold {{ config('mail.default') === 'log' ? 'text-amber-600' : 'text-emerald-700' }}">{{ config('mail.default') }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-1 border-b border-slate-200/50">
|
||||
<span class="text-slate-500">MAIL_HOST:</span>
|
||||
@@ -670,6 +693,14 @@
|
||||
<span class="text-slate-500">MAIL_PORT:</span>
|
||||
<span class="font-mono text-slate-900">{{ config('mail.mailers.smtp.port') ?: '(geen)' }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-1 border-b border-slate-200/50">
|
||||
<span class="text-slate-500">MAIL_ENCRYPTION:</span>
|
||||
<span class="font-mono text-slate-900">{{ config('mail.mailers.smtp.encryption') ?: (config('mail.mailers.smtp.scheme') ?: '(automatisch)') }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-1 border-b border-slate-200/50">
|
||||
<span class="text-slate-500">MAIL_USERNAME:</span>
|
||||
<span class="font-mono text-slate-900">{{ config('mail.mailers.smtp.username') ?: '(geen)' }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between py-1 border-b border-slate-200/50">
|
||||
<span class="text-slate-500">MAIL_FROM:</span>
|
||||
<span class="font-mono text-slate-900">{{ config('mail.from.address') }}</span>
|
||||
@@ -677,19 +708,28 @@
|
||||
</div>
|
||||
|
||||
<div class="p-4 rounded-xl bg-slate-50/70 border border-slate-200/60 space-y-2">
|
||||
<div class="font-semibold text-slate-500 text-[11px] uppercase tracking-wider">Gmail Afleverchecklist</div>
|
||||
<ul class="space-y-1.5 text-slate-600 text-[11px]">
|
||||
<div class="font-semibold text-slate-500 text-[11px] uppercase tracking-wider">Waarom komt mail niet aan op Gmail?</div>
|
||||
<p class="text-[11px] text-slate-600 leading-relaxed">
|
||||
Als je wel een groene bevestiging krijgt maar niks ontvangt in Gmail, dan accepteert de SMTP-server de mail, maar weigert of filtert Google de bezorging door <strong>ontbrekende of foutieve DNS-records</strong>:
|
||||
</p>
|
||||
<ul class="space-y-1.5 text-slate-600 text-[11px] pt-1">
|
||||
<li class="flex items-start space-x-2">
|
||||
<span class="text-slate-900 font-semibold">1.</span>
|
||||
<span><strong>SMTP Provider:</strong> Gebruik een geverifieerde mailserver.</span>
|
||||
<span class="text-rose-600 font-semibold shrink-0">⚠️ SPF Error:</span>
|
||||
<span>In je DNS staat <code class="bg-white border px-1 rounded font-mono text-[10px]">include:_spf.jouwdriedee.nl</code>, maar dit subdomein bestaat niet! Dit veroorzaakt een fatale <em>SPF PermError</em> bij Google.</span>
|
||||
</li>
|
||||
<li class="flex items-start space-x-2">
|
||||
<span class="text-slate-900 font-semibold">2.</span>
|
||||
<span><strong>SPF Record:</strong> Google vereist een SPF TXT-record op het domein.</span>
|
||||
<span class="text-slate-900 font-semibold shrink-0">1. Oplossing SPF:</span>
|
||||
<span>Zet in je Versio DNS het TXT record voor <code class="bg-white border px-1 rounded font-mono text-[10px]">@</code> op:<br>
|
||||
<code class="bg-white border px-1 py-0.5 rounded font-mono text-[10px] text-slate-900 block mt-1 select-all">v=spf1 a mx ip4:185.182.56.214 include:spf.versio.nl ~all</code></span>
|
||||
</li>
|
||||
<li class="flex items-start space-x-2">
|
||||
<span class="text-slate-900 font-semibold">3.</span>
|
||||
<span><strong>DKIM & DMARC:</strong> Noodzakelijk om spamfilters van Gmail te passeren.</span>
|
||||
<span class="text-slate-900 font-semibold shrink-0">2. DMARC toevoegen:</span>
|
||||
<span>Maak een TXT record met naam <code class="bg-white border px-1 rounded font-mono text-[10px]">_dmarc</code> en waarde:<br>
|
||||
<code class="bg-white border px-1 py-0.5 rounded font-mono text-[10px] text-slate-900 block mt-1 select-all">v=DMARC1; p=none; rua=mailto:info@jouwdriedee.nl</code></span>
|
||||
</li>
|
||||
<li class="flex items-start space-x-2">
|
||||
<span class="text-slate-900 font-semibold shrink-0">3. Spamfolder:</span>
|
||||
<span>Controleer altijd ook je Gmail Spam/Ongewenst map.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -698,6 +738,7 @@
|
||||
<!-- Test Email Form -->
|
||||
<div class="p-4 rounded-xl bg-slate-50/70 border border-slate-200/60 space-y-2">
|
||||
<span class="font-semibold text-slate-900 text-xs block">Test E-mail Verzenden</span>
|
||||
<p class="text-[11px] text-slate-500">Verzend een directe testmail om de verbinding en aflevering te verifiëren. Na verzending blijft deze pagina netjes op tabblad 8 geopend.</p>
|
||||
<div class="flex flex-col sm:flex-row gap-2 pt-1">
|
||||
<input
|
||||
type="email"
|
||||
|
||||
@@ -148,3 +148,4 @@
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -250,3 +250,4 @@
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -65,3 +65,4 @@
|
||||
</tr>
|
||||
</table>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -79,3 +79,4 @@
|
||||
</tr>
|
||||
</table>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -47,3 +47,4 @@
|
||||
</tr>
|
||||
</table>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -206,6 +206,20 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(session('warning'))
|
||||
<div class="mb-6 p-4 rounded-xl bg-amber-50/80 border border-amber-200/60 text-amber-800 text-xs font-medium flex items-center space-x-2.5 shadow-2xs">
|
||||
<i data-lucide="alert-triangle" class="w-4 h-4 text-amber-600 shrink-0"></i>
|
||||
<span>{{ session('warning') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(session('info'))
|
||||
<div class="mb-6 p-4 rounded-xl bg-blue-50/80 border border-blue-200/60 text-blue-800 text-xs font-medium flex items-center space-x-2.5 shadow-2xs">
|
||||
<i data-lucide="info" class="w-4 h-4 text-blue-600 shrink-0"></i>
|
||||
<span>{{ session('info') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(session('error'))
|
||||
<div class="mb-6 p-4 rounded-xl bg-rose-50/80 border border-rose-200/60 text-rose-800 text-xs font-medium flex items-center space-x-2.5 shadow-2xs">
|
||||
<i data-lucide="alert-circle" class="w-4 h-4 text-rose-600 shrink-0"></i>
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Models\User;
|
||||
use App\Services\PriceCalculatorService;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Tests\TestCase;
|
||||
|
||||
@@ -407,4 +408,27 @@ class AdminContentTest extends TestCase
|
||||
$this->assertEquals(45, (int) Setting::get('calc_default_markup'));
|
||||
$this->assertEquals(8, (int) Setting::get('calc_failure_rate'));
|
||||
}
|
||||
|
||||
public function test_sending_test_mail_redirects_and_stays_on_email_tab(): void
|
||||
{
|
||||
Mail::fake();
|
||||
|
||||
$response = $this->actingAs($this->admin)->post(route('admin.content.test-mail'), [
|
||||
'test_email' => 'niek.rabelink@gmail.com',
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('admin.content.index', ['tab' => 'email']));
|
||||
$response->assertSessionHas('success');
|
||||
}
|
||||
|
||||
public function test_updating_content_with_active_tab_preserves_tab(): void
|
||||
{
|
||||
$response = $this->actingAs($this->admin)->post(route('admin.content.update'), [
|
||||
'active_tab' => 'calculator',
|
||||
'site_name' => 'Jouwdriedee 3D Print Studio',
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('admin.content.index', ['tab' => 'calculator']));
|
||||
$response->assertSessionHas('success');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user