feat(mailbox): Implement tier-based dynamic expiration with real-time Alpine.js countdown
This commit is contained in:
@@ -154,18 +154,58 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-[11px] font-mono text-white break-all mb-4">{{ $currentMailbox->address }}</div>
|
||||
<div class="space-y-2">
|
||||
<div class="space-y-2"
|
||||
x-data="{
|
||||
expiresAt: '{{ $currentMailbox->expires_at?->toIso8601String() }}',
|
||||
created_at: '{{ $currentMailbox->created_at?->toIso8601String() }}',
|
||||
timeLeft: 'Never',
|
||||
percent: 100,
|
||||
init() {
|
||||
if (!this.expiresAt) return;
|
||||
|
||||
this.update();
|
||||
setInterval(() => this.update(), 1000);
|
||||
},
|
||||
update() {
|
||||
const end = new Date(this.expiresAt).getTime();
|
||||
const start = new Date(this.created_at).getTime();
|
||||
const now = new Date().getTime();
|
||||
|
||||
if (now > end) {
|
||||
this.timeLeft = 'Expired';
|
||||
this.percent = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate time string
|
||||
const diff = end - now;
|
||||
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||||
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||
const mins = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
||||
const secs = Math.floor((diff % (1000 * 60)) / 1000);
|
||||
|
||||
if (days > 0) {
|
||||
this.timeLeft = `${days}d ${hours}h ${mins}m ${secs}s`;
|
||||
} else if (hours > 0) {
|
||||
this.timeLeft = `${hours}h ${mins}m ${secs}s`;
|
||||
} else if (mins > 0) {
|
||||
this.timeLeft = `${mins}m ${secs}s`;
|
||||
} else {
|
||||
this.timeLeft = `${secs}s`;
|
||||
}
|
||||
|
||||
// Calculate percentage based on total lifespan vs remaining
|
||||
const totalLifespan = end - start;
|
||||
const timeRemaining = end - now;
|
||||
this.percent = Math.max(0, Math.min(100, (timeRemaining / totalLifespan) * 100));
|
||||
}
|
||||
}">
|
||||
<div class="flex items-center justify-between text-[10px]">
|
||||
<span class="text-zinc-500 uppercase font-black tracking-tighter">Expires In</span>
|
||||
<span class="text-pink-500 font-mono">{{ $currentMailbox->expires_at?->diffForHumans(['parts' => 2, 'short' => true]) ?? 'Never' }}</span>
|
||||
<span class="text-pink-500 font-mono" x-text="timeLeft"></span>
|
||||
</div>
|
||||
<div class="h-1 bg-white/5 rounded-full overflow-hidden">
|
||||
@php
|
||||
$percent = $currentMailbox->expires_at
|
||||
? max(0, min(100, (now()->diffInSeconds($currentMailbox->expires_at) / (86400 * 7)) * 100))
|
||||
: 100;
|
||||
@endphp
|
||||
<div class="h-full bg-gradient-to-r from-pink-500 to-emerald-500" style="width: {{ $percent }}%"></div>
|
||||
<div class="h-full bg-gradient-to-r from-pink-500 to-emerald-500 transition-all duration-1000 ease-linear" :style="`width: ${percent}%`"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -191,9 +231,43 @@
|
||||
@if($mailbox->id !== $currentMailboxId)
|
||||
<button wire:click="switchMailbox({{ $mailbox->id }})"
|
||||
class="w-full p-3 rounded-xl bg-zinc-900/40 border border-white/5 text-left group hover:border-white/20 transition-all flex items-center justify-between cursor-pointer">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="min-w-0 flex-1"
|
||||
x-data="{
|
||||
expiresAt: '{{ $mailbox->expires_at?->toIso8601String() }}',
|
||||
timeLeft: 'Never',
|
||||
init() {
|
||||
if (!this.expiresAt) return;
|
||||
this.update();
|
||||
setInterval(() => this.update(), 1000); // 1-sec interval
|
||||
},
|
||||
update() {
|
||||
const end = new Date(this.expiresAt).getTime();
|
||||
const now = new Date().getTime();
|
||||
|
||||
if (now > end) {
|
||||
this.timeLeft = 'Expired';
|
||||
return;
|
||||
}
|
||||
|
||||
const diff = end - now;
|
||||
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||||
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||
const mins = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
||||
const secs = Math.floor((diff % (1000 * 60)) / 1000);
|
||||
|
||||
if (days > 0) {
|
||||
this.timeLeft = `${days}d ${hours}h ${mins}m ${secs}s`;
|
||||
} else if (hours > 0) {
|
||||
this.timeLeft = `${hours}h ${mins}m ${secs}s`;
|
||||
} else if (mins > 0) {
|
||||
this.timeLeft = `${mins}m ${secs}s`;
|
||||
} else {
|
||||
this.timeLeft = `${secs}s`;
|
||||
}
|
||||
}
|
||||
}">
|
||||
<div class="text-[10px] font-mono text-zinc-400 truncate group-hover:text-white transition-colors">{{ $mailbox->address }}</div>
|
||||
<div class="text-[9px] text-zinc-600 font-bold uppercase mt-1">{{ $mailbox->expires_at?->diffForHumans(['short' => true]) ?? 'Never' }}</div>
|
||||
<div class="text-[9px] text-zinc-600 font-bold uppercase mt-1" x-text="timeLeft"></div>
|
||||
</div>
|
||||
<svg class="w-4 h-4 text-zinc-700 group-hover:text-pink-500 translate-x-2 opacity-0 group-hover:translate-x-0 group-hover:opacity-100 transition-all" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /></svg>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user