613 lines
39 KiB
PHP
613 lines
39 KiB
PHP
<div>
|
||
@php
|
||
$folderLabels = ['INBOX'=>'Posteingang','Sent'=>'Gesendet','Drafts'=>'Entwürfe','Junk'=>'Spam','Trash'=>'Papierkorb','Archive'=>'Archiv','_starred'=>'Markiert'];
|
||
$isDrafts = $folder === 'Drafts';
|
||
$isTrash = $folder === 'Trash';
|
||
$hasTabs = in_array($folder, ['INBOX', '_starred']);
|
||
$unread = collect($messages)->where('seen', false)->count();
|
||
$mailbox = session('webmail_email');
|
||
$allUids = collect($messages)->pluck('uid')->values()->toJson();
|
||
$moveTargets = array_filter(
|
||
['INBOX'=>'Posteingang','Sent'=>'Gesendet','Archive'=>'Archiv','Junk'=>'Spam','Trash'=>'Papierkorb'],
|
||
fn($k) => $k !== $folder, ARRAY_FILTER_USE_KEY
|
||
);
|
||
$tabCounts = $hasTabs ? [
|
||
'all' => count($messages),
|
||
'general' => collect($messages)->where('category', 'general')->count(),
|
||
'promo' => collect($messages)->where('category', 'promo')->count(),
|
||
'social' => collect($messages)->where('category', 'social')->count(),
|
||
] : [];
|
||
@endphp
|
||
|
||
<div x-data="{
|
||
/* --- Auswahl --- */
|
||
selected: [],
|
||
allUids: {{ $allUids }},
|
||
get allSelected() { return this.allUids.length > 0 && this.selected.length === this.allUids.length; },
|
||
get someSelected() { return this.selected.length > 0 && !this.allSelected; },
|
||
toggleAll() { this.selected = this.allSelected ? [] : [...this.allUids]; },
|
||
toggle(uid) { const i = this.selected.indexOf(uid); i === -1 ? this.selected.push(uid) : this.selected.splice(i,1); },
|
||
clear() { this.selected = []; },
|
||
|
||
/* --- Sender-Tooltip --- */
|
||
tipShow: false, tipName: '', tipEmail: '', tipX: 0, tipY: 0,
|
||
showTip(name, email, el) {
|
||
const r = el.getBoundingClientRect();
|
||
this.tipName = name;
|
||
this.tipEmail = email;
|
||
this.tipX = r.left;
|
||
this.tipY = r.bottom + 8;
|
||
this.tipShow = true;
|
||
},
|
||
|
||
/* --- Rechtsklick-Menü --- */
|
||
ctxOpen: false, ctxUid: null, ctxSeen: true, ctxX: 0, ctxY: 0,
|
||
showCtx(uid, seen, x, y) {
|
||
this.ctxUid = uid;
|
||
this.ctxSeen = seen;
|
||
this.ctxX = Math.min(x, window.innerWidth - 205);
|
||
this.ctxY = Math.min(y, window.innerHeight - 160);
|
||
this.ctxOpen = true;
|
||
},
|
||
}"
|
||
x-init="
|
||
Livewire.hook('commit', ({ component, succeed, fail }) => {
|
||
if (component.id !== $wire.id) return;
|
||
window.dispatchEvent(new CustomEvent('mw-skel-show'));
|
||
succeed(() => { Promise.resolve().then(() => { window.dispatchEvent(new CustomEvent('mw-skel-hide')); }); });
|
||
fail(() => { window.dispatchEvent(new CustomEvent('mw-skel-hide')); });
|
||
});
|
||
"
|
||
@click.window="ctxOpen = false; tipShow = false;"
|
||
@keydown.escape.window="ctxOpen = false;">
|
||
|
||
{{-- ═══ 1. TITEL ═══ --}}
|
||
<div style="margin-bottom:18px;">
|
||
<div style="font-size:22px;font-weight:800;color:var(--mw-t1);letter-spacing:-.4px;">{{ $mailbox }}</div>
|
||
<div style="display:flex;align-items:center;gap:10px;margin-top:4px;flex-wrap:wrap;">
|
||
<span style="font-size:13px;color:var(--mw-t4);">{{ $folderLabels[$folder] ?? $folder }}</span>
|
||
@if($unread > 0)
|
||
<span style="font-size:11.5px;font-weight:700;color:var(--mw-v2);
|
||
background:var(--mw-vbg);border:1px solid var(--mw-vbd);
|
||
padding:2px 9px;border-radius:20px;">{{ $unread }} ungelesen</span>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
|
||
{{-- ═══ 2. SUCHE ═══ --}}
|
||
<div style="position:relative;margin-bottom:12px;">
|
||
<div style="display:flex;align-items:center;gap:10px;
|
||
background:var(--mw-bg3);border:1px solid var(--mw-b1);border-radius:10px;
|
||
padding:10px 14px;">
|
||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" style="color:var(--mw-t4);flex-shrink:0;">
|
||
<circle cx="6" cy="6" r="4.5" stroke="currentColor" stroke-width="1.3"/>
|
||
<path d="M9.5 9.5l3 3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
|
||
</svg>
|
||
<input wire:model.live.debounce.500ms="search" type="text"
|
||
placeholder="In {{ $folderLabels[$folder] ?? $folder }} suchen …"
|
||
style="flex:1;background:none;border:none;outline:none;font-size:13.5px;color:var(--mw-t1);">
|
||
@if($searching)
|
||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" style="animation:spin 1s linear infinite;color:var(--mw-t4);flex-shrink:0;">
|
||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2.5" stroke-dasharray="60" stroke-dashoffset="20"/>
|
||
</svg>
|
||
@elseif($search)
|
||
<button wire:click="clearSearch" style="background:none;border:none;cursor:pointer;color:var(--mw-t4);font-size:18px;line-height:1;padding:0;">×</button>
|
||
@endif
|
||
</div>
|
||
|
||
@if($search && !$searching)
|
||
<div style="position:absolute;left:0;right:0;top:calc(100% + 6px);z-index:300;
|
||
background:var(--mw-bg);border:1px solid var(--mw-b1);border-radius:10px;
|
||
box-shadow:0 8px 28px rgba(0,0,0,.3);overflow:hidden;">
|
||
@if(empty($searchResults))
|
||
<div style="padding:20px;text-align:center;font-size:13px;color:var(--mw-t4);">Keine Ergebnisse für „{{ $search }}"</div>
|
||
@else
|
||
@foreach($searchResults as $r)
|
||
<a href="{{ route('ui.webmail.view', ['uid'=>$r['uid'],'folder'=>$folder]) }}" wire:navigate
|
||
style="display:flex;align-items:center;gap:12px;padding:10px 14px;text-decoration:none;
|
||
border-bottom:1px solid var(--mw-b1);{{ !$r['seen'] ? 'font-weight:600;' : '' }}"
|
||
onmouseover="this.style.background='var(--mw-bg3)'" onmouseout="this.style.background='none'">
|
||
<div style="width:30px;height:30px;border-radius:8px;flex-shrink:0;background:var(--mw-vbg);
|
||
border:1px solid var(--mw-vbd);display:flex;align-items:center;justify-content:center;
|
||
font-size:11px;font-weight:700;color:var(--mw-v2);">
|
||
{{ strtoupper(substr($r['from_name'] ?: $r['from'], 0, 1)) }}
|
||
</div>
|
||
<div style="flex:1;min-width:0;">
|
||
<div style="font-size:12.5px;color:{{ !$r['seen'] ? 'var(--mw-v2)' : 'var(--mw-t2)' }};overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">
|
||
{{ $r['from_name'] ?: $r['from'] }}
|
||
</div>
|
||
<div style="font-size:12px;color:var(--mw-t4);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">
|
||
{{ $r['subject'] ?: '(kein Betreff)' }}
|
||
</div>
|
||
</div>
|
||
<div style="font-size:11px;color:var(--mw-t4);white-space:nowrap;">
|
||
{{ $r['date'] ? \Carbon\Carbon::parse($r['date'])->format('d.m. H:i') : '' }}
|
||
</div>
|
||
</a>
|
||
@endforeach
|
||
@endif
|
||
</div>
|
||
@endif
|
||
</div>
|
||
|
||
{{-- ═══ 3. TOOLBAR ═══ --}}
|
||
<div style="display:flex;align-items:center;gap:8px;padding:8px 12px;margin-bottom:12px;
|
||
background:var(--mw-bg3);border:1px solid var(--mw-b1);border-radius:9px;">
|
||
|
||
<input type="checkbox" class="mbx-cbx" :checked="allSelected" :indeterminate="someSelected" @change="toggleAll()">
|
||
|
||
<template x-if="!someSelected && !allSelected">
|
||
<span style="font-size:12px;color:var(--mw-t4);">Alle</span>
|
||
</template>
|
||
<template x-if="someSelected || allSelected">
|
||
<span style="font-size:12px;font-weight:600;color:var(--mw-v2);">
|
||
<span x-text="selected.length"></span> ausgewählt
|
||
</span>
|
||
</template>
|
||
|
||
<div style="width:1px;height:16px;background:var(--mw-b2);flex-shrink:0;"></div>
|
||
|
||
<template x-if="someSelected || allSelected"><div style="display:flex;flex-direction:row;align-items:center;gap:4px;">
|
||
@if(!$isDrafts)
|
||
{{-- Als ungelesen --}}
|
||
<button title="Als ungelesen markieren"
|
||
@click="$wire.bulkMarkUnseen(selected); clear()"
|
||
style="display:flex;align-items:center;justify-content:center;width:28px;height:28px;
|
||
border:1px solid var(--mw-b1);border-radius:6px;background:var(--mw-bg4);
|
||
color:var(--mw-t3);cursor:pointer;flex-shrink:0;"
|
||
onmouseover="this.style.background='var(--mw-bg3)';this.style.color='var(--mw-t1)'"
|
||
onmouseout="this.style.background='var(--mw-bg4)';this.style.color='var(--mw-t3)'">
|
||
<svg width="13" height="13" viewBox="0 0 14 14" fill="none">
|
||
<rect x=".5" y="2.5" width="13" height="9" rx="1.5" stroke="currentColor" stroke-width="1.2"/>
|
||
<path d="M.5 5l6.5 4 6.5-4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||
<circle cx="11" cy="3" r="2.2" fill="var(--mw-v2)"/>
|
||
</svg>
|
||
</button>
|
||
{{-- Als gelesen --}}
|
||
<button title="Als gelesen markieren"
|
||
@click="$wire.bulkMarkSeen(selected); clear()"
|
||
style="display:flex;align-items:center;justify-content:center;width:28px;height:28px;
|
||
border:1px solid var(--mw-b1);border-radius:6px;background:var(--mw-bg4);
|
||
color:var(--mw-t3);cursor:pointer;flex-shrink:0;"
|
||
onmouseover="this.style.background='var(--mw-bg3)';this.style.color='var(--mw-t1)'"
|
||
onmouseout="this.style.background='var(--mw-bg4)';this.style.color='var(--mw-t3)'">
|
||
<svg width="13" height="13" viewBox="0 0 14 14" fill="none">
|
||
<rect x=".5" y="2.5" width="13" height="9" rx="1.5" stroke="currentColor" stroke-width="1.2"/>
|
||
<path d="M.5 5l6.5 4 6.5-4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||
<path d="M4.5 8.5l2 2 3.5-4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
{{-- Verschieben --}}
|
||
<div style="position:relative;" x-data="{ mv:false }" @click.outside="mv=false">
|
||
<button title="Verschieben nach …"
|
||
@click="mv=!mv"
|
||
style="display:flex;align-items:center;justify-content:center;width:28px;height:28px;
|
||
border:1px solid var(--mw-b1);border-radius:6px;background:var(--mw-bg4);
|
||
color:var(--mw-t3);cursor:pointer;flex-shrink:0;"
|
||
onmouseover="this.style.background='var(--mw-bg3)';this.style.color='var(--mw-t1)'"
|
||
onmouseout="this.style.background='var(--mw-bg4)';this.style.color='var(--mw-t3)'">
|
||
<svg width="13" height="13" viewBox="0 0 14 14" fill="none">
|
||
<path d="M1 7h9M7 4l3 3-3 3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</button>
|
||
<div x-show="mv" x-cloak x-transition
|
||
style="position:absolute;left:0;top:calc(100% + 5px);z-index:200;min-width:150px;
|
||
background:var(--mw-bg);border:1px solid var(--mw-b1);border-radius:9px;
|
||
box-shadow:0 6px 20px rgba(0,0,0,.25);padding:4px;">
|
||
@foreach($moveTargets as $key => $label)
|
||
<button @click="$wire.bulkMoveTo(selected,'{{ $key }}'); clear(); mv=false"
|
||
style="display:flex;align-items:center;width:100%;text-align:left;padding:7px 11px;
|
||
border:none;background:none;cursor:pointer;border-radius:6px;font-size:12.5px;color:var(--mw-t2);"
|
||
onmouseover="this.style.background='var(--mw-bg3)'" onmouseout="this.style.background='none'">{{ $label }}</button>
|
||
@endforeach
|
||
</div>
|
||
</div>
|
||
@endif
|
||
{{-- Löschen --}}
|
||
<button title="{{ $isTrash ? 'Endgültig löschen' : 'In Papierkorb' }}"
|
||
@click="if(confirm('{{ $isTrash ? 'Endgültig löschen?' : 'In Papierkorb?' }}')) { $wire.bulkDelete(selected); clear(); }"
|
||
style="display:flex;align-items:center;justify-content:center;width:28px;height:28px;
|
||
border:1px solid var(--mw-rdbd);border-radius:6px;background:var(--mw-rdbg);
|
||
color:var(--mw-rd);cursor:pointer;flex-shrink:0;"
|
||
onmouseover="this.style.background='var(--mw-rd)';this.style.color='#fff'"
|
||
onmouseout="this.style.background='var(--mw-rdbg)';this.style.color='var(--mw-rd)'">
|
||
<svg width="12" height="12" viewBox="0 0 14 14" fill="none">
|
||
<path d="M2 3.5h10M5 3.5V2h4v1.5M5.5 6v4.5M8.5 6v4.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||
<path d="M3 3.5l.7 8h6.6l.7-8" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||
</svg>
|
||
</button>
|
||
{{-- Auswahl aufheben --}}
|
||
<button title="Auswahl aufheben"
|
||
@click="clear()"
|
||
style="display:flex;align-items:center;justify-content:center;width:28px;height:28px;
|
||
border:1px solid var(--mw-b1);border-radius:6px;background:none;
|
||
color:var(--mw-t4);cursor:pointer;flex-shrink:0;font-size:15px;line-height:1;"
|
||
onmouseover="this.style.color='var(--mw-t1)'"
|
||
onmouseout="this.style.color='var(--mw-t4)'">×</button>
|
||
</div></template>
|
||
|
||
<div style="flex:1;"></div>
|
||
|
||
@if($isTrash && $total > 0)
|
||
<button wire:click="emptyTrash" wire:confirm="Papierkorb endgültig leeren?"
|
||
style="display:flex;align-items:center;gap:5px;padding:5px 10px;border-radius:7px;
|
||
border:1px solid var(--mw-rdbd);background:var(--mw-rdbg);color:var(--mw-rd);font-size:12px;cursor:pointer;">
|
||
Leeren
|
||
</button>
|
||
@endif
|
||
|
||
<button wire:click="load" wire:loading.attr="disabled"
|
||
style="display:flex;align-items:center;justify-content:center;width:30px;height:30px;
|
||
border-radius:7px;border:1px solid var(--mw-b1);background:var(--mw-bg4);
|
||
color:var(--mw-t3);cursor:pointer;flex-shrink:0;transition:color .15s;"
|
||
onmouseover="this.style.color='var(--mw-t1)'" onmouseout="this.style.color='var(--mw-t3)'"
|
||
title="Aktualisieren">
|
||
<svg wire:loading.remove wire:target="load" width="13" height="13" viewBox="0 0 14 14" fill="none">
|
||
{{-- 270° clockwise arc from top → right → bottom → left --}}
|
||
<path d="M7 2A5 5 0 1 1 2 7" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
|
||
{{-- Arrowhead at (2,7) pointing upward (clockwise travel direction) --}}
|
||
<path d="M0 8.5L2 7L4 8.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<svg wire:loading wire:target="load" width="13" height="13" viewBox="0 0 24 24" fill="none" style="animation:spin 1s linear infinite;">
|
||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2.5" stroke-dasharray="60" stroke-dashoffset="20"/>
|
||
</svg>
|
||
</button>
|
||
|
||
<a href="{{ route('ui.webmail.compose') }}" wire:navigate class="mbx-btn-primary" style="height:30px;border-radius:7px;font-size:12px;">
|
||
<svg width="11" height="11" viewBox="0 0 14 14" fill="none">
|
||
<path d="M7 1.5v11M1.5 7h11" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
|
||
</svg>
|
||
Schreiben
|
||
</a>
|
||
</div>
|
||
|
||
{{-- ═══ 4+5. TABS + TABELLE ═══ --}}
|
||
<div class="mbx-section" style="padding:0;width:100%;box-sizing:border-box;overflow:hidden;">
|
||
|
||
@if($hasTabs && !empty($messages))
|
||
@php
|
||
$tabs = ['all'=>'Alle', 'general'=>'Allgemein', 'promo'=>'Werbung', 'social'=>'Soziale Medien'];
|
||
@endphp
|
||
<div style="display:flex;align-items:stretch;border-bottom:2px solid var(--mw-b1);padding:0 8px;background:var(--mw-bg4);">
|
||
@foreach($tabs as $tabKey => $tabLabel)
|
||
@php $cnt = $tabCounts[$tabKey] ?? 0; $isActive = $tab === $tabKey; @endphp
|
||
@if($tabKey === 'all' || $cnt > 0)
|
||
<button wire:click="switchTab('{{ $tabKey }}')"
|
||
style="display:inline-flex;align-items:center;gap:7px;padding:11px 16px;
|
||
border:none;border-bottom:2px solid {{ $isActive ? 'var(--mw-v2)' : 'transparent' }};
|
||
margin-bottom:-2px;background:none;cursor:pointer;font-size:13px;
|
||
white-space:nowrap;outline:none;
|
||
color:{{ $isActive ? 'var(--mw-v2)' : 'var(--mw-t4)' }};
|
||
font-weight:{{ $isActive ? '600' : '400' }};"
|
||
@if(!$isActive)
|
||
onmouseover="this.style.color='var(--mw-t2)'"
|
||
onmouseout="this.style.color='var(--mw-t4)'"
|
||
@endif>
|
||
{{ $tabLabel }}
|
||
<span style="display:inline-flex;align-items:center;justify-content:center;
|
||
min-width:20px;padding:1px 6px;border-radius:10px;
|
||
font-size:10.5px;font-weight:500;line-height:1.6;
|
||
background:{{ $isActive ? 'var(--mw-vbg)' : 'var(--mw-bg3)' }};
|
||
color:{{ $isActive ? 'var(--mw-v2)' : 'var(--mw-t4)' }};">{{ $cnt }}</span>
|
||
</button>
|
||
@endif
|
||
@endforeach
|
||
</div>
|
||
@endif
|
||
|
||
{{-- Skeleton: sichtbar während Livewire lädt --}}
|
||
@php $wt = 'load,switchFolder,switchTab,nextPage,prevPage,bulkDelete,bulkMoveTo,bulkMarkSeen,bulkMarkUnseen,toggleFlag,deleteDraft,emptyTrash'; @endphp
|
||
|
||
|
||
{{-- Tabelle: thead immer sichtbar, tbody wechselt zwischen Skeleton und Inhalt --}}
|
||
<table class="mbx-table" style="table-layout:fixed;width:100%;box-sizing:border-box;">
|
||
<colgroup>
|
||
<col style="width:5%">
|
||
<col style="width:1.5%">
|
||
<col style="width:2.5%">
|
||
<col style="width:18%">
|
||
<col>{{-- Betreff: nimmt den gesamten Rest --}}
|
||
<col style="width:11%">
|
||
<col style="width:5%">
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th class="mbx-th" style="padding:0 10px 0 16px;"></th>
|
||
<th class="mbx-th" style="padding:0;"></th>
|
||
<th class="mbx-th" style="padding:0 4px;"></th>
|
||
<th class="mbx-th">{{ $isDrafts ? 'An' : 'Von' }}</th>
|
||
<th class="mbx-th">Betreff</th>
|
||
<th class="mbx-th">Datum</th>
|
||
<th class="mbx-th" style="padding:0;"></th>
|
||
</tr>
|
||
</thead>
|
||
|
||
{{-- Skeleton tbody: via Custom-Events gesteuert --}}
|
||
<tbody style="display:none"
|
||
@mw-skel-show.window="$el.style.display='table-row-group'"
|
||
@mw-skel-hide.window="$el.style.display='none'">
|
||
@for($i = 0; $i < 7; $i++)
|
||
@php
|
||
$senderW = [55,75,45,82,50,70,42,78,60,68][$i];
|
||
$subjW = [72,88,55,92,65,80,60,85,70,78][$i];
|
||
$delay = round($i * 0.09, 2);
|
||
$delay2 = round($i * 0.09 + 0.12, 2);
|
||
@endphp
|
||
<tr style="border-bottom:1px solid var(--mw-b1);">
|
||
<td style="padding:11px 10px 11px 16px;">
|
||
<div style="width:15px;height:15px;border-radius:4px;background:var(--mw-b2);"></div>
|
||
</td>
|
||
<td style="padding:0;text-align:center;">
|
||
<div style="width:7px;height:7px;border-radius:50%;background:var(--mw-b1);margin:0 auto;"></div>
|
||
</td>
|
||
<td style="padding:0 4px;text-align:center;">
|
||
<div style="width:13px;height:13px;border-radius:3px;background:var(--mw-b2);margin:0 auto;"></div>
|
||
</td>
|
||
<td style="padding:11px 10px 11px 0;">
|
||
<div style="width:{{ $senderW }}%;height:11px;border-radius:5px;background:var(--mw-b2);
|
||
animation:skel-pulse 1.6s ease-in-out {{ $delay }}s infinite;"></div>
|
||
</td>
|
||
<td style="padding:11px 10px 11px 0;">
|
||
<div style="width:{{ $subjW }}%;height:11px;border-radius:5px;background:var(--mw-b2);
|
||
animation:skel-pulse 1.6s ease-in-out {{ $delay2 }}s infinite;"></div>
|
||
</td>
|
||
<td style="padding:11px 8px;">
|
||
<div style="width:75%;height:11px;border-radius:5px;background:var(--mw-b2);
|
||
animation:skel-pulse 1.6s ease-in-out {{ $delay }}s infinite;"></div>
|
||
</td>
|
||
<td style="padding:11px 6px;text-align:center;">
|
||
<div style="width:22px;height:22px;border-radius:5px;background:var(--mw-b1);margin:0 auto;"></div>
|
||
</td>
|
||
</tr>
|
||
@endfor
|
||
</tbody>
|
||
|
||
{{-- Echter Inhalt tbody --}}
|
||
<tbody @mw-skel-show.window="$el.style.display='none'"
|
||
@mw-skel-hide.window="$el.style.display='table-row-group'">
|
||
@if(empty($messages))
|
||
<tr>
|
||
<td colspan="7" style="padding:56px 24px;text-align:center;color:var(--mw-t4);font-size:13px;">
|
||
<svg width="36" height="36" viewBox="0 0 14 14" fill="none" style="display:block;margin:0 auto 14px;opacity:.2;">
|
||
<rect x=".5" y="2.5" width="13" height="9" rx="1.5" stroke="currentColor" stroke-width="1.2"/>
|
||
<path d="M.5 5l6.5 4 6.5-4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||
</svg>
|
||
Keine Nachrichten
|
||
</td>
|
||
</tr>
|
||
@else
|
||
@foreach($messages as $msg)
|
||
@php
|
||
$uid = $msg['uid'];
|
||
$unseen = !($msg['seen'] ?? true);
|
||
$msgCat = $msg['category'] ?? 'general';
|
||
if ($hasTabs && $tab !== 'all' && $msgCat !== $tab) continue;
|
||
$rowUrl = $isDrafts
|
||
? route('ui.webmail.compose', ['draftUid'=>$uid,'draftFolder'=>$folder])
|
||
: route('ui.webmail.view', ['uid'=>$uid,'folder'=>$folder]);
|
||
$sName = $msg['from_name'] ?: $msg['from'];
|
||
$sEmail = $msg['from'];
|
||
@endphp
|
||
<tr class="mbx-tr"
|
||
:class="{ 'mbx-tr-selected': selected.includes({{ $uid }}) }"
|
||
style="cursor:pointer;{{ $unseen ? 'font-weight:600;' : '' }}"
|
||
onclick="if(!event.target.closest('[data-noclick]')) Livewire.navigate('{{ $rowUrl }}')"
|
||
@contextmenu.prevent="showCtx({{ $uid }}, {{ $unseen ? 'false' : 'true' }}, $event.clientX, $event.clientY)">
|
||
|
||
{{-- Checkbox --}}
|
||
<td class="mbx-td" style="padding:0 10px 0 16px;" data-noclick>
|
||
<input type="checkbox" class="mbx-cbx"
|
||
:checked="selected.includes({{ $uid }})"
|
||
@change="toggle({{ $uid }})" @click.stop>
|
||
</td>
|
||
|
||
{{-- Ungelesen-Dot --}}
|
||
<td style="padding:0;vertical-align:middle;">
|
||
@if($unseen)
|
||
<span style="display:inline-block;width:7px;height:7px;border-radius:50%;
|
||
background:var(--mw-v2);box-shadow:0 0 6px var(--mw-v2);"></span>
|
||
@endif
|
||
</td>
|
||
|
||
{{-- Stern --}}
|
||
<td class="mbx-td" style="padding:0 4px;" data-noclick>
|
||
@if($isDrafts)
|
||
<button wire:click="deleteDraft({{ $uid }})" wire:confirm="Entwurf löschen?" @click.stop
|
||
style="background:none;border:none;cursor:pointer;padding:2px;font-size:12px;line-height:1;color:var(--mw-t4);">✕</button>
|
||
@else
|
||
<button wire:click="toggleFlag({{ $uid }})" @click.stop
|
||
style="background:none;border:none;cursor:pointer;padding:2px;font-size:13px;line-height:1;
|
||
color:{{ ($msg['flagged'] ?? false) ? '#f59e0b' : 'var(--mw-b2)' }};">★</button>
|
||
@endif
|
||
</td>
|
||
|
||
{{-- Absender (mit Hover-Tooltip nur auf dem Namen) --}}
|
||
<td class="mbx-td" data-noclick
|
||
style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer;
|
||
{{ $unseen ? 'color:var(--mw-v2);' : 'color:var(--mw-t3);font-size:12px;' }}"
|
||
onclick="Livewire.navigate('{{ $rowUrl }}')">
|
||
@if($isDrafts)
|
||
{{ $msg['to'] ?? '—' }}
|
||
@else
|
||
<span style="display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"
|
||
@mouseenter.stop="showTip(@js($sName), @js($sEmail), $event.currentTarget)"
|
||
@mouseleave.stop="tipShow = false">{{ $sName }}</span>
|
||
@endif
|
||
</td>
|
||
|
||
{{-- Betreff --}}
|
||
<td class="mbx-td" style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;{{ $unseen ? 'color:var(--mw-t1);' : '' }}">
|
||
<span style="display:inline-flex;align-items:center;gap:5px;max-width:100%;overflow:hidden;">
|
||
<span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">{{ $msg['subject'] ?: '(kein Betreff)' }}</span>
|
||
@if($msg['flagged'] ?? false)
|
||
<span style="color:#f59e0b;font-size:11px;flex-shrink:0;">★</span>
|
||
@endif
|
||
@if($msg['has_attachments'] ?? false)
|
||
<svg width="11" height="11" viewBox="0 0 14 14" fill="none" style="flex-shrink:0;opacity:.5;"
|
||
title="Hat Anhänge">
|
||
<path d="M12 6.5L6.8 11.7a3.18 3.18 0 01-4.5-4.5l5.5-5.5a2.12 2.12 0 013 3L5.3 10.2a1.06 1.06 0 01-1.5-1.5L9 3.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
@endif
|
||
</span>
|
||
</td>
|
||
|
||
{{-- Datum --}}
|
||
<td class="mbx-td mbx-td-muted" style="white-space:nowrap;font-size:11.5px;padding-right:4px;">
|
||
{{ $msg['date'] ? \Carbon\Carbon::parse($msg['date'])->format('d.m. H:i') : '—' }}
|
||
</td>
|
||
|
||
{{-- ··· Aktionen --}}
|
||
<td style="padding:0 6px 0 0;vertical-align:middle;text-align:right;" data-noclick
|
||
x-data="{ open: false, tx: 0, ty: 0 }"
|
||
@close-row-act.window="open = false"
|
||
:class="open ? 'mbx-row-acts-open' : 'mbx-row-acts'">
|
||
<button class="mbx-row-acts-btn"
|
||
@click.stop="
|
||
const btn = $event.currentTarget;
|
||
$dispatch('close-row-act');
|
||
$nextTick(() => {
|
||
const r = btn.getBoundingClientRect();
|
||
tx = Math.max(4, r.right - 190);
|
||
ty = r.bottom + 4;
|
||
open = true;
|
||
});">
|
||
<svg width="13" height="13" viewBox="0 0 14 14" fill="none">
|
||
<circle cx="2.5" cy="7" r="1.2" fill="currentColor"/>
|
||
<circle cx="7" cy="7" r="1.2" fill="currentColor"/>
|
||
<circle cx="11.5" cy="7" r="1.2" fill="currentColor"/>
|
||
</svg>
|
||
</button>
|
||
<template x-teleport="body">
|
||
<div x-show="open" x-cloak @click.outside="open=false"
|
||
x-transition:enter="transition ease-out duration-100"
|
||
x-transition:enter-start="opacity-0 scale-95"
|
||
x-transition:enter-end="opacity-100 scale-100"
|
||
x-transition:leave="transition ease-in duration-75"
|
||
x-transition:leave-end="opacity-0 scale-95"
|
||
:style="`position:fixed;top:${ty}px;left:${tx}px;z-index:9999;min-width:190px;
|
||
background:var(--mw-bg);border:1px solid var(--mw-b1);border-radius:9px;
|
||
box-shadow:0 8px 24px rgba(0,0,0,.32);padding:4px;`">
|
||
@if(!$isDrafts)
|
||
@if($unseen)
|
||
<button @click.stop="$wire.bulkMarkSeen([{{ $uid }}]); open=false"
|
||
style="display:flex;align-items:center;gap:8px;width:100%;text-align:left;padding:7px 11px;border:none;background:none;cursor:pointer;border-radius:6px;font-size:12.5px;color:var(--mw-t2);"
|
||
onmouseover="this.style.background='var(--mw-bg3)'" onmouseout="this.style.background='none'">
|
||
<svg width="11" height="11" viewBox="0 0 14 14" fill="none"><path d="M1 7l3.5 3.5L13 3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
Als gelesen markieren
|
||
</button>
|
||
@else
|
||
<button @click.stop="$wire.bulkMarkUnseen([{{ $uid }}]); open=false"
|
||
style="display:flex;align-items:center;gap:8px;width:100%;text-align:left;padding:7px 11px;border:none;background:none;cursor:pointer;border-radius:6px;font-size:12.5px;color:var(--mw-t2);"
|
||
onmouseover="this.style.background='var(--mw-bg3)'" onmouseout="this.style.background='none'">
|
||
<svg width="11" height="11" viewBox="0 0 14 14" fill="none"><rect x=".5" y="2.5" width="13" height="9" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M.5 5l6.5 4 6.5-4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||
Als ungelesen markieren
|
||
</button>
|
||
@endif
|
||
<div style="position:relative;" x-data="{ mv:false }" @click.outside.stop="mv=false">
|
||
<button @click.stop="mv=!mv"
|
||
style="display:flex;align-items:center;justify-content:space-between;width:100%;text-align:left;padding:7px 11px;border:none;background:none;cursor:pointer;border-radius:6px;font-size:12.5px;color:var(--mw-t2);"
|
||
onmouseover="this.style.background='var(--mw-bg3)'" onmouseout="this.style.background='none'">
|
||
<span style="display:flex;align-items:center;gap:8px;">
|
||
<svg width="11" height="11" viewBox="0 0 14 14" fill="none"><path d="M1 7h9M7 4l3 3-3 3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
Verschieben nach
|
||
</span>
|
||
<svg width="8" height="8" viewBox="0 0 14 14" fill="none" :style="mv?'transform:rotate(90deg)':''"><path d="M5 3l4 4-4 4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
</button>
|
||
<div x-show="mv" x-cloak x-transition
|
||
style="position:absolute;left:calc(100% + 4px);top:0;z-index:10000;min-width:150px;
|
||
background:var(--mw-bg);border:1px solid var(--mw-b1);border-radius:9px;
|
||
box-shadow:0 6px 20px rgba(0,0,0,.28);padding:4px;">
|
||
@foreach($moveTargets as $key => $label)
|
||
<button @click.stop="$wire.bulkMoveTo([{{ $uid }}],'{{ $key }}'); open=false; mv=false"
|
||
style="display:flex;align-items:center;width:100%;text-align:left;padding:7px 11px;border:none;background:none;cursor:pointer;border-radius:6px;font-size:12.5px;color:var(--mw-t2);"
|
||
onmouseover="this.style.background='var(--mw-bg3)'" onmouseout="this.style.background='none'">{{ $label }}</button>
|
||
@endforeach
|
||
</div>
|
||
</div>
|
||
@endif
|
||
<div style="height:1px;background:var(--mw-b1);margin:3px 0;"></div>
|
||
<button @click.stop="if(confirm('{{ $isTrash ? 'Endgültig löschen?' : 'In Papierkorb?' }}')) { $wire.bulkDelete([{{ $uid }}]); open=false; }"
|
||
style="display:flex;align-items:center;gap:8px;width:100%;text-align:left;padding:7px 11px;border:none;background:none;cursor:pointer;border-radius:6px;font-size:12.5px;color:var(--mw-rd);"
|
||
onmouseover="this.style.background='var(--mw-rdbg)'" onmouseout="this.style.background='none'">
|
||
<svg width="11" height="11" viewBox="0 0 14 14" fill="none"><path d="M2 3.5h10M5 3.5V2h4v1.5M5.5 6v4.5M8.5 6v4.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/><path d="M3 3.5l.7 8h6.6l.7-8" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||
{{ $isTrash ? 'Endgültig löschen' : 'In Papierkorb' }}
|
||
</button>
|
||
</div>
|
||
</template>
|
||
</td>
|
||
</tr>
|
||
@endforeach
|
||
@endif
|
||
</tbody>
|
||
</table>
|
||
|
||
@if($total > $perPage)
|
||
<div wire:loading.remove wire:target="{{ $wt }}"
|
||
style="display:flex;align-items:center;justify-content:space-between;padding:10px 16px;border-top:1px solid var(--mw-b1);">
|
||
<span style="font-size:12px;color:var(--mw-t4);">
|
||
{{ ($page-1)*$perPage+1 }}–{{ min($page*$perPage,$total) }} von {{ $total }}
|
||
</span>
|
||
<div style="display:flex;gap:6px;">
|
||
<button wire:click="prevPage" @disabled($page<=1) class="mbx-act-btn mbx-act-muted">←</button>
|
||
<button wire:click="nextPage" @disabled($page*$perPage>=$total) class="mbx-act-btn mbx-act-muted">→</button>
|
||
</div>
|
||
</div>
|
||
@endif
|
||
|
||
</div>{{-- /mbx-section --}}
|
||
|
||
{{-- ═══ SENDER-TOOLTIP (position:fixed, kein x-teleport) ═══ --}}
|
||
<div x-show="tipShow" x-cloak
|
||
style="pointer-events:none;"
|
||
:style="`position:fixed;top:${tipY}px;left:${tipX}px;z-index:8000;
|
||
background:var(--mw-bg);border:1px solid var(--mw-b1);border-radius:8px;
|
||
box-shadow:0 4px 16px rgba(0,0,0,.25);padding:8px 12px;min-width:180px;max-width:280px;`">
|
||
<div style="font-size:13px;font-weight:600;color:var(--mw-t1);" x-text="tipName"></div>
|
||
<div style="font-size:12px;color:var(--mw-t4);margin-top:2px;" x-text="tipEmail"></div>
|
||
</div>
|
||
|
||
{{-- ═══ RECHTSKLICK-MENÜ (position:fixed, kein x-teleport) ═══ --}}
|
||
<div x-show="ctxOpen" x-cloak
|
||
@click.stop
|
||
x-transition:enter="transition ease-out duration-100"
|
||
x-transition:enter-start="opacity-0 scale-95"
|
||
x-transition:enter-end="opacity-100 scale-100"
|
||
x-transition:leave="transition ease-in duration-75"
|
||
x-transition:leave-end="opacity-0 scale-95"
|
||
:style="`position:fixed;top:${ctxY}px;left:${ctxX}px;z-index:9998;min-width:190px;
|
||
background:var(--mw-bg);border:1px solid var(--mw-b1);border-radius:9px;
|
||
box-shadow:0 8px 24px rgba(0,0,0,.32);padding:4px;`">
|
||
|
||
@if(!$isDrafts)
|
||
@php $btnStyle = "display:flex;align-items:center;gap:8px;width:100%;text-align:left;padding:7px 11px;border:none;background:none;cursor:pointer;border-radius:6px;font-size:12.5px;color:var(--mw-t2);"; @endphp
|
||
<template x-if="ctxSeen">
|
||
<button @click="$wire.bulkMarkUnseen([ctxUid]); ctxOpen=false"
|
||
style="{{ $btnStyle }}"
|
||
onmouseover="this.style.background='var(--mw-bg3)'" onmouseout="this.style.background='none'">
|
||
<svg width="11" height="11" viewBox="0 0 14 14" fill="none" style="flex-shrink:0;"><rect x=".5" y="2.5" width="13" height="9" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M.5 5l6.5 4 6.5-4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||
Als ungelesen markieren
|
||
</button>
|
||
</template>
|
||
<template x-if="!ctxSeen">
|
||
<button @click="$wire.bulkMarkSeen([ctxUid]); ctxOpen=false"
|
||
style="{{ $btnStyle }}"
|
||
onmouseover="this.style.background='var(--mw-bg3)'" onmouseout="this.style.background='none'">
|
||
<svg width="11" height="11" viewBox="0 0 14 14" fill="none" style="flex-shrink:0;"><path d="M1 7l3.5 3.5L13 3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
Als gelesen markieren
|
||
</button>
|
||
</template>
|
||
@endif
|
||
|
||
<div style="height:1px;background:var(--mw-b1);margin:3px 0;"></div>
|
||
|
||
<button @click="$wire.bulkDelete([ctxUid]); ctxOpen=false"
|
||
style="display:flex;align-items:center;gap:8px;width:100%;text-align:left;padding:7px 11px;border:none;background:none;cursor:pointer;border-radius:6px;font-size:12.5px;color:var(--mw-rd);"
|
||
onmouseover="this.style.background='var(--mw-rdbg)'" onmouseout="this.style.background='none'">
|
||
<svg width="11" height="11" viewBox="0 0 14 14" fill="none"><path d="M2 3.5h10M5 3.5V2h4v1.5M5.5 6v4.5M8.5 6v4.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/><path d="M3 3.5l.7 8h6.6l.7-8" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||
{{ $isTrash ? 'Endgültig löschen' : 'In Papierkorb' }}
|
||
</button>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|