Decision now support auto mailing

This commit is contained in:
Simon Pocrnjič
2025-10-12 00:20:03 +02:00
parent 1b615163be
commit 3ab1c05fcc
33 changed files with 1862 additions and 548 deletions
@@ -0,0 +1,102 @@
<script setup>
import AdminLayout from '@/Layouts/AdminLayout.vue'
import { Head, Link, router } from '@inertiajs/vue3'
import { ref } from 'vue'
const props = defineProps({
logs: Object,
templates: Array,
filters: Object,
})
const form = ref({
status: props.filters?.status || '',
to: props.filters?.to || '',
subject: props.filters?.subject || '',
template_id: props.filters?.template_id || '',
date_from: props.filters?.date_from || '',
date_to: props.filters?.date_to || '',
})
function applyFilters() {
router.get(route('admin.email-logs.index'), {
...form.value,
}, { preserveState: true, preserveScroll: true })
}
</script>
<template>
<AdminLayout title="Email Logs">
<Head title="Email Logs" />
<div class="mb-4">
<h1 class="text-xl font-semibold text-gray-800">Email Logs</h1>
<div class="mt-3 grid grid-cols-1 md:grid-cols-6 gap-2">
<select v-model="form.status" class="input">
<option value="">All statuses</option>
<option value="queued">Queued</option>
<option value="sending">Sending</option>
<option value="sent">Sent</option>
<option value="failed">Failed</option>
<option value="bounced">Bounced</option>
<option value="deferred">Deferred</option>
</select>
<input v-model="form.to" placeholder="To email" class="input" />
<input v-model="form.subject" placeholder="Subject" class="input" />
<select v-model="form.template_id" class="input">
<option value="">All templates</option>
<option v-for="t in templates" :key="t.id" :value="t.id">{{ t.name }}</option>
</select>
<input v-model="form.date_from" type="date" class="input" />
<input v-model="form.date_to" type="date" class="input" />
</div>
<div class="mt-2">
<button @click="applyFilters" class="px-3 py-1.5 text-xs rounded border bg-gray-50 hover:bg-gray-100">Filter</button>
</div>
</div>
<div class="rounded-xl border bg-white/60 backdrop-blur-sm shadow-sm">
<div class="overflow-auto">
<table class="min-w-full text-sm">
<thead class="bg-gray-50 text-gray-600">
<tr>
<th class="text-left p-2">Date</th>
<th class="text-left p-2">Status</th>
<th class="text-left p-2">To</th>
<th class="text-left p-2">Subject</th>
<th class="text-left p-2">Template</th>
<th class="text-left p-2">Duration</th>
<th class="text-left p-2">\#</th>
</tr>
</thead>
<tbody>
<tr v-for="log in logs.data" :key="log.id" class="border-t">
<td class="p-2 whitespace-nowrap">{{ new Date(log.created_at).toLocaleString() }}</td>
<td class="p-2"><span class="inline-flex items-center px-2 py-0.5 rounded text-xs border" :class="{
'bg-green-50 text-green-700 border-green-200': log.status === 'sent',
'bg-amber-50 text-amber-700 border-amber-200': log.status === 'queued' || log.status === 'sending',
'bg-red-50 text-red-700 border-red-200': log.status === 'failed',
}">{{ log.status }}</span></td>
<td class="p-2 truncate max-w-[220px]">{{ log.to_email }}</td>
<td class="p-2 truncate max-w-[320px]">{{ log.subject }}</td>
<td class="p-2 truncate max-w-[220px]">{{ log.template?.name || '-' }}</td>
<td class="p-2">{{ log.duration_ms ? log.duration_ms + ' ms' : '-' }}</td>
<td class="p-2"><Link :href="route('admin.email-logs.show', log.id)" class="text-indigo-600 hover:underline">Open</Link></td>
</tr>
</tbody>
</table>
</div>
<div class="p-2 border-t text-xs text-gray-600 flex items-center justify-between">
<div>Showing {{ logs.from }}-{{ logs.to }} of {{ logs.total }}</div>
<div class="flex gap-2">
<Link v-for="link in logs.links" :key="link.url || link.label" :href="link.url || '#'" :class="['px-2 py-1 rounded border text-xs', { 'bg-indigo-600 text-white border-indigo-600': link.active, 'pointer-events-none opacity-50': !link.url } ]" v-html="link.label" />
</div>
</div>
</div>
</AdminLayout>
</template>
<style scoped>
.input { width: 100%; border-radius: 0.375rem; border: 1px solid #d1d5db; padding: 0.5rem 0.75rem; font-size: 0.875rem; line-height: 1.25rem; }
.input:focus { outline: 2px solid transparent; outline-offset: 2px; border-color: #6366f1; box-shadow: 0 0 0 1px #6366f1; }
</style>
@@ -0,0 +1,49 @@
<script setup>
import AdminLayout from '@/Layouts/AdminLayout.vue'
import { Head, Link } from '@inertiajs/vue3'
const props = defineProps({
log: Object,
})
</script>
<template>
<AdminLayout title="Email Log">
<Head title="Email Log" />
<div class="mb-3 flex items-center justify-between">
<div class="flex items-center gap-2">
<Link :href="route('admin.email-logs.index')" class="text-sm text-gray-600 hover:text-gray-800">Back</Link>
<h1 class="text-xl font-semibold text-gray-800">Email Log #{{ props.log.id }}</h1>
</div>
<div class="text-xs text-gray-600">Created: {{ new Date(props.log.created_at).toLocaleString() }}</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="rounded-xl border bg-white/60 backdrop-blur-sm shadow-sm p-4 space-y-2">
<div class="text-sm"><span class="font-semibold">Status:</span> {{ props.log.status }}</div>
<div class="text-sm"><span class="font-semibold">To:</span> {{ props.log.to_email }} {{ props.log.to_name ? '(' + props.log.to_name + ')' : '' }}</div>
<div class="text-sm"><span class="font-semibold">Subject:</span> {{ props.log.subject }}</div>
<div class="text-sm"><span class="font-semibold">Template:</span> {{ props.log.template?.name || '-' }}</div>
<div class="text-sm"><span class="font-semibold">Message ID:</span> {{ props.log.message_id || '-' }}</div>
<div class="text-sm"><span class="font-semibold">Attempts:</span> {{ props.log.attempt }}</div>
<div class="text-sm"><span class="font-semibold">Duration:</span> {{ props.log.duration_ms ? props.log.duration_ms + ' ms' : '-' }}</div>
<div v-if="props.log.error_message" class="text-sm text-red-700"><span class="font-semibold">Error:</span> {{ props.log.error_message }}</div>
</div>
<div class="rounded-xl border bg-white/60 backdrop-blur-sm shadow-sm p-4">
<div class="label">Text</div>
<pre class="text-xs whitespace-pre-wrap break-words">{{ props.log.body?.body_text || '' }}</pre>
</div>
<div class="md:col-span-2 rounded-xl border bg-white/60 backdrop-blur-sm shadow-sm p-4">
<div class="label">HTML</div>
<iframe :srcdoc="props.log.body?.body_html || ''" class="w-full h-[480px] border rounded bg-white"></iframe>
</div>
</div>
</AdminLayout>
</template>
<style scoped>
.label { display:block; font-size: 0.7rem; font-weight:600; letter-spacing:0.05em; text-transform:uppercase; color:#6b7280; margin-bottom:0.25rem; }
</style>