236 lines
14 KiB
PHP
236 lines
14 KiB
PHP
<x-slot:breadcrumbParent>System</x-slot:breadcrumbParent>
|
|
<x-slot:breadcrumb>Mail-Sandbox</x-slot:breadcrumb>
|
|
|
|
<div wire:poll.5s>
|
|
|
|
<div class="mbx-page-header">
|
|
<div class="mbx-page-title">
|
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
<rect x="1.5" y="3.5" width="13" height="9" rx="1.5" stroke="currentColor" stroke-width="1.3"/>
|
|
<path d="M1.5 5.5l6.5 4 6.5-4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
|
|
</svg>
|
|
Mail-Sandbox
|
|
@if($unread > 0)
|
|
<span style="font-size:10px;padding:1px 7px;border-radius:10px;background:var(--mw-v);color:#fff;font-weight:600;margin-left:2px">{{ $unread }} neu</span>
|
|
@endif
|
|
</div>
|
|
<div class="mbx-page-actions">
|
|
<div class="mbx-search-wrap">
|
|
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" style="flex-shrink:0;color:var(--mw-t4)"><circle cx="5.5" cy="5.5" r="4" stroke="currentColor" stroke-width="1.2"/><path d="M9 9l2.5 2.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
|
<input type="text" wire:model.live.debounce.250ms="search" class="mbx-search-input" placeholder="Von, An, Betreff …">
|
|
</div>
|
|
@if($mails->isNotEmpty())
|
|
<button wire:click="clearAll"
|
|
wire:confirm="Alle {{ $mails->count() }} Nachrichten löschen?"
|
|
class="mw-btn-del" style="font-size:12px;padding:5px 12px">
|
|
<svg width="11" height="11" viewBox="0 0 13 13" fill="none"><path d="M2 3h9M5 3V2h3v1M3.5 3l.5 8h5l.5-8" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
Alle löschen
|
|
</button>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Mail-Client Layout --}}
|
|
<div style="display:grid;grid-template-columns:320px 1fr;gap:0;border:1px solid var(--mw-b1);border-radius:10px;overflow:hidden;background:var(--mw-bg2);min-height:520px">
|
|
|
|
{{-- ═══ Left: Mail-Liste ═══ --}}
|
|
<div style="border-right:1px solid var(--mw-b1);overflow-y:auto;max-height:680px">
|
|
|
|
@if($mails->isEmpty())
|
|
<div style="padding:48px 16px;text-align:center">
|
|
<svg width="32" height="32" viewBox="0 0 16 16" fill="none" style="color:var(--mw-t4);margin:0 auto 12px;display:block">
|
|
<rect x="1.5" y="3.5" width="13" height="9" rx="1.5" stroke="currentColor" stroke-width="1.3"/>
|
|
<path d="M1.5 5.5l6.5 4 6.5-4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
|
|
</svg>
|
|
<div style="font-size:12.5px;color:var(--mw-t2);font-weight:500;margin-bottom:4px">
|
|
{{ $search ? 'Keine Treffer' : 'Postfach leer' }}
|
|
</div>
|
|
<div style="font-size:11.5px;color:var(--mw-t4)">
|
|
{{ $search ? 'Suche anpassen.' : 'Eingehende Mails erscheinen hier sobald der Sandbox-Transport aktiv ist.' }}
|
|
</div>
|
|
</div>
|
|
@else
|
|
@foreach($mails as $mail)
|
|
<div wire:click="select({{ $mail->id }})"
|
|
style="padding:11px 14px;border-bottom:1px solid var(--mw-b1);cursor:pointer;transition:background .1s;
|
|
{{ $selectedId === $mail->id ? 'background:var(--mw-vbg);border-left:3px solid var(--mw-v);' : 'border-left:3px solid transparent;' }}
|
|
{{ !$mail->is_read && $selectedId !== $mail->id ? 'background:rgba(124,58,237,.04);' : '' }}"
|
|
onmouseover="if({{ $selectedId !== $mail->id ? 'true' : 'false' }})this.style.background='var(--mw-bg3)'"
|
|
onmouseout="this.style.background='{{ $selectedId === $mail->id ? 'var(--mw-vbg)' : (!$mail->is_read ? 'rgba(124,58,237,.04)' : '') }}'">
|
|
|
|
<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:6px;margin-bottom:3px">
|
|
<div style="display:flex;align-items:center;gap:5px;min-width:0">
|
|
@if(!$mail->is_read)
|
|
<div style="width:6px;height:6px;border-radius:50%;background:var(--mw-v);flex-shrink:0"></div>
|
|
@endif
|
|
<span style="font-size:12px;font-weight:{{ $mail->is_read ? '400' : '600' }};color:var(--mw-t1);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
|
|
{{ $mail->from_name ?: $mail->from_address }}
|
|
</span>
|
|
</div>
|
|
<span style="font-size:10px;color:var(--mw-t4);flex-shrink:0">{{ $mail->received_at->format('H:i') }}</span>
|
|
</div>
|
|
|
|
<div style="font-size:11.5px;color:var(--mw-t2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-bottom:2px;font-weight:{{ $mail->is_read ? '400' : '500' }}">
|
|
{{ $mail->subject ?: '(kein Betreff)' }}
|
|
</div>
|
|
<div style="font-size:10.5px;color:var(--mw-t4);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
|
|
An: {{ $mail->to_preview }}
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
@endif
|
|
|
|
</div>
|
|
|
|
{{-- ═══ Right: Mail-Detail ═══ --}}
|
|
<div style="overflow-y:auto;max-height:680px">
|
|
|
|
@if(!$selected)
|
|
<div style="display:flex;align-items:center;justify-content:center;height:100%;min-height:400px;flex-direction:column;gap:12px">
|
|
<svg width="36" height="36" viewBox="0 0 16 16" fill="none" style="color:var(--mw-b2)">
|
|
<rect x="1.5" y="3.5" width="13" height="9" rx="1.5" stroke="currentColor" stroke-width="1.3"/>
|
|
<path d="M1.5 5.5l6.5 4 6.5-4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
|
|
</svg>
|
|
<div style="font-size:12px;color:var(--mw-t4)">Nachricht auswählen</div>
|
|
</div>
|
|
@else
|
|
{{-- Header --}}
|
|
<div style="padding:16px 20px;border-bottom:1px solid var(--mw-b1)">
|
|
<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:12px">
|
|
<h2 style="font-size:14px;font-weight:600;color:var(--mw-t1);margin:0;line-height:1.4">{{ $selected->subject ?: '(kein Betreff)' }}</h2>
|
|
<button wire:click="deleteOne({{ $selected->id }})"
|
|
class="mbx-act-btn mbx-act-danger" title="Löschen" style="flex-shrink:0">
|
|
<svg width="12" height="12" viewBox="0 0 13 13" fill="none"><path d="M2 3h9M5 3V2h3v1M3.5 3l.5 8h5l.5-8" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
</button>
|
|
</div>
|
|
|
|
<div style="display:flex;flex-direction:column;gap:5px;font-size:11.5px">
|
|
<div style="display:flex;gap:8px">
|
|
<span style="color:var(--mw-t4);width:34px;flex-shrink:0">Von</span>
|
|
<span style="color:var(--mw-t2);font-family:monospace;font-size:11px">{{ $selected->sender }}</span>
|
|
</div>
|
|
<div style="display:flex;gap:8px">
|
|
<span style="color:var(--mw-t4);width:34px;flex-shrink:0">An</span>
|
|
<span style="color:var(--mw-t2);font-family:monospace;font-size:11px">{{ implode(', ', $selected->to_addresses) }}</span>
|
|
</div>
|
|
<div style="display:flex;gap:8px">
|
|
<span style="color:var(--mw-t4);width:34px;flex-shrink:0">Zeit</span>
|
|
<span style="color:var(--mw-t3)">{{ $selected->received_at->format('d.m.Y H:i:s') }}</span>
|
|
</div>
|
|
@if($selected->message_id)
|
|
<div style="display:flex;gap:8px">
|
|
<span style="color:var(--mw-t4);width:34px;flex-shrink:0">ID</span>
|
|
<span style="color:var(--mw-t4);font-family:monospace;font-size:10px">{{ $selected->message_id }}</span>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Body Tabs --}}
|
|
<div x-data="{tab: '{{ $selected->body_html ? 'html' : 'text' }}'}" style="display:flex;flex-direction:column;height:100%">
|
|
|
|
<div style="display:flex;gap:0;border-bottom:1px solid var(--mw-b1);padding:0 20px">
|
|
@if($selected->body_html)
|
|
<button @click="tab='html'"
|
|
:style="tab==='html' ? 'border-bottom:2px solid var(--mw-v);color:var(--mw-v2);font-weight:600' : 'border-bottom:2px solid transparent;color:var(--mw-t4)'"
|
|
style="padding:8px 14px;background:none;border:none;border-top:none;border-left:none;border-right:none;font-size:11.5px;cursor:pointer;transition:all .1s">
|
|
HTML
|
|
</button>
|
|
@endif
|
|
@if($selected->body_text)
|
|
<button @click="tab='text'"
|
|
:style="tab==='text' ? 'border-bottom:2px solid var(--mw-v);color:var(--mw-v2);font-weight:600' : 'border-bottom:2px solid transparent;color:var(--mw-t4)'"
|
|
style="padding:8px 14px;background:none;border:none;border-top:none;border-left:none;border-right:none;font-size:11.5px;cursor:pointer;transition:all .1s">
|
|
Plain Text
|
|
</button>
|
|
@endif
|
|
<button @click="tab='headers'"
|
|
:style="tab==='headers' ? 'border-bottom:2px solid var(--mw-v);color:var(--mw-v2);font-weight:600' : 'border-bottom:2px solid transparent;color:var(--mw-t4)'"
|
|
style="padding:8px 14px;background:none;border:none;border-top:none;border-left:none;border-right:none;font-size:11.5px;cursor:pointer;transition:all .1s">
|
|
Header
|
|
</button>
|
|
</div>
|
|
|
|
@if($selected->body_html)
|
|
<div x-show="tab==='html'" x-cloak style="flex:1">
|
|
<iframe srcdoc="{{ htmlspecialchars($selected->body_html) }}"
|
|
style="width:100%;min-height:420px;border:none;display:block"
|
|
sandbox="allow-same-origin"></iframe>
|
|
</div>
|
|
@endif
|
|
|
|
@if($selected->body_text)
|
|
<div x-show="tab==='text'" x-cloak style="padding:16px 20px">
|
|
<pre style="font-family:ui-monospace,monospace;font-size:11.5px;color:var(--mw-t2);white-space:pre-wrap;word-break:break-word;margin:0;line-height:1.7">{{ $selected->body_text }}</pre>
|
|
</div>
|
|
@endif
|
|
|
|
<div x-show="tab==='headers'" x-cloak style="padding:16px 20px">
|
|
<pre style="font-family:ui-monospace,monospace;font-size:10.5px;color:var(--mw-t3);white-space:pre-wrap;word-break:break-all;margin:0;line-height:1.7;background:var(--mw-bg4);padding:12px;border-radius:6px">{{ $selected->raw_headers }}</pre>
|
|
</div>
|
|
|
|
@if(!$selected->body_html && !$selected->body_text)
|
|
<div style="padding:32px;text-align:center;color:var(--mw-t4);font-size:12px">Kein Inhalt</div>
|
|
@endif
|
|
|
|
</div>
|
|
@endif
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{{-- Setup Guide --}}
|
|
<div class="mbx-sections" style="margin-top:14px">
|
|
<div class="mbx-section">
|
|
<div class="mbx-domain-head">
|
|
<div class="mbx-domain-info">
|
|
<span class="mbx-badge-mute">Postfix-Konfiguration</span>
|
|
<span style="font-size:11px;color:var(--mw-t4);margin-left:6px">— Sandbox-Transport aktivieren</span>
|
|
</div>
|
|
</div>
|
|
<div style="padding:16px 18px;display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px">
|
|
|
|
<div>
|
|
<div style="font-size:11px;color:var(--mw-t4);margin-bottom:6px;font-weight:500">1. master.cf — Transport anlegen</div>
|
|
<pre style="font-family:monospace;font-size:10px;color:var(--mw-t3);background:var(--mw-bg4);padding:10px 12px;border-radius:6px;overflow-x:auto;margin:0;line-height:1.7">sandbox unix - n n - - pipe
|
|
flags=Rq user=www-data
|
|
argv=/usr/bin/php
|
|
{{ base_path('artisan') }}
|
|
sandbox:receive
|
|
--to=${recipient}</pre>
|
|
</div>
|
|
|
|
<div>
|
|
<div style="font-size:11px;color:var(--mw-t4);margin-bottom:6px;font-weight:500">2. main.cf — Transport aktivieren</div>
|
|
<pre style="font-family:monospace;font-size:10px;color:var(--mw-t3);background:var(--mw-bg4);padding:10px 12px;border-radius:6px;overflow-x:auto;margin:0;line-height:1.7"># Alle Mails abfangen:
|
|
default_transport = sandbox
|
|
|
|
# Oder nur bestimmte Domains:
|
|
transport_maps =
|
|
hash:/etc/postfix/transport
|
|
|
|
# /etc/postfix/transport:
|
|
# example.com sandbox:</pre>
|
|
</div>
|
|
|
|
<div>
|
|
<div style="font-size:11px;color:var(--mw-t4);margin-bottom:6px;font-weight:500">3. Reload & Test</div>
|
|
<pre style="font-family:monospace;font-size:10px;color:var(--mw-t3);background:var(--mw-bg4);padding:10px 12px;border-radius:6px;overflow-x:auto;margin:0;line-height:1.7">postmap /etc/postfix/transport
|
|
postfix reload
|
|
|
|
# Test:
|
|
echo "Test" | mail \
|
|
-s "Sandbox-Test" \
|
|
user@example.com</pre>
|
|
<div style="margin-top:8px;font-size:11px;color:var(--mw-t4)">
|
|
Die Mail erscheint nach wenigen Sekunden hier (Auto-Refresh alle 5s).
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|