Teren-app/resources/js/Pages/Admin/MailProfiles/Index.vue
2026-01-05 18:27:35 +01:00

438 lines
15 KiB
Vue

<script setup>
import AdminLayout from "@/Layouts/AdminLayout.vue";
import { Head, Link, useForm } from "@inertiajs/vue3";
import { ref, computed } from "vue";
import {
PlusIcon,
FlaskConicalIcon,
MailIcon,
PencilIcon,
SendIcon,
MoreVerticalIcon,
} from "lucide-vue-next";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/Components/ui/card";
import { Button } from "@/Components/ui/button";
import { Input } from "@/Components/ui/input";
import { Label } from "@/Components/ui/label";
import { Badge } from "@/Components/ui/badge";
import { Switch } from "@/Components/ui/switch";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/Components/ui/table";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/Components/ui/dropdown-menu";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/Components/ui/dialog";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/Components/ui/select";
const props = defineProps({
profiles: { type: Array, default: () => [] },
});
const createOpen = ref(false); // create modal
const editOpen = ref(false); // edit modal
const editTarget = ref(null); // profile being edited
const form = useForm({
name: "",
host: "",
port: 587,
encryption: "tls",
username: "",
password: "",
from_address: "",
from_name: "",
priority: 10,
});
function openCreate() {
form.reset();
createOpen.value = true;
editTarget.value = null;
}
function openEdit(p) {
// populate form with existing profile data (exclude password which is write-only)
form.reset();
form.name = p.name || "";
form.host = p.host || "";
form.port = p.port || 587;
form.encryption = p.encryption || "";
form.username = p.username || "";
form.password = ""; // empty -> keep existing unless user fills
form.from_address = p.from_address || "";
form.from_name = p.from_name || "";
form.priority = p.priority ?? 10;
editTarget.value = p;
editOpen.value = true;
}
function closeCreate() {
if (form.processing) return;
createOpen.value = false;
}
function submitCreate() {
form.post(route("admin.mail-profiles.store"), {
preserveScroll: true,
onSuccess: () => {
createOpen.value = false;
},
});
}
function closeEdit() {
if (form.processing) return;
editOpen.value = false;
editTarget.value = null;
}
function submitEdit() {
if (!editTarget.value) return;
// Build payload excluding empty password
const payload = {
name: form.name,
host: form.host,
port: form.port,
encryption: form.encryption || null,
username: form.username || null,
from_address: form.from_address,
from_name: form.from_name || null,
priority: form.priority,
};
if (form.password && form.password.trim() !== "") {
payload.password = form.password.trim();
}
form
.transform(() => payload)
.put(route("admin.mail-profiles.update", editTarget.value.id), {
preserveScroll: true,
onSuccess: () => {
editOpen.value = false;
editTarget.value = null;
},
});
}
function toggleActive(p) {
window.axios
.post(route("admin.mail-profiles.toggle", p.id))
.then(() => window.location.reload());
}
function testConnection(p) {
window.axios
.post(route("admin.mail-profiles.test", p.id))
.then(() => window.location.reload());
}
function sendTestEmail(p) {
window.axios
.post(route("admin.mail-profiles.send-test", p.id))
.then(() => window.location.reload());
}
const statusClass = (p) => {
if (p.test_status === "success") return "text-emerald-600";
if (p.test_status === "failed") return "text-rose-600";
if (p.test_status === "queued") return "text-amber-500";
return "text-gray-400";
};
</script>
<template>
<AdminLayout title="Mail profili">
<Head title="Mail profili" />
<Card>
<CardHeader>
<div class="flex items-start justify-between">
<div class="flex items-start gap-3">
<div
class="inline-flex items-center justify-center h-10 w-10 rounded-lg bg-primary/10 text-primary"
>
<MailIcon class="h-5 w-5" />
</div>
<div>
<CardTitle>Mail profili</CardTitle>
<CardDescription
>Upravljajte SMTP profile za pošiljanje e-pošte ({{
profiles.length
}})</CardDescription
>
</div>
</div>
<Button @click="openCreate">
<PlusIcon class="h-4 w-4 mr-2" />
Nov profil
</Button>
</div>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Ime</TableHead>
<TableHead>Host</TableHead>
<TableHead class="text-center">Port</TableHead>
<TableHead class="text-center">Enc</TableHead>
<TableHead class="text-center">Aktivno</TableHead>
<TableHead class="text-center">Status</TableHead>
<TableHead>Zadnji uspeh</TableHead>
<TableHead>Napaka</TableHead>
<TableHead class="text-right">Akcije</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow v-for="p in profiles" :key="p.id">
<TableCell class="font-medium">{{ p.name }}</TableCell>
<TableCell>{{ p.host }}</TableCell>
<TableCell class="text-center">{{ p.port }}</TableCell>
<TableCell class="text-center">
<Badge v-if="p.encryption" variant="outline">{{
p.encryption.toUpperCase()
}}</Badge>
<span v-else class="text-muted-foreground">—</span>
</TableCell>
<TableCell class="text-center">
<Switch
:default-value="p.active"
@update:model-value="() => toggleActive(p)"
/>
</TableCell>
<TableCell class="text-center">
<Badge
v-if="p.test_status === 'success'"
variant="default"
class="bg-green-100 text-green-800 hover:bg-green-100"
>{{ p.test_status }}</Badge
>
<Badge v-else-if="p.test_status === 'failed'" variant="destructive">{{
p.test_status
}}</Badge>
<Badge
v-else-if="p.test_status === 'queued'"
variant="secondary"
class="bg-amber-100 text-amber-800 hover:bg-amber-100"
>{{ p.test_status }}</Badge
>
<span v-else class="text-muted-foreground">—</span>
</TableCell>
<TableCell class="text-sm text-muted-foreground">
{{
p.last_success_at ? new Date(p.last_success_at).toLocaleString() : "—"
}}
</TableCell>
<TableCell
class="text-sm text-destructive max-w-[200px] truncate"
:title="p.last_error_message"
>
{{ p.last_error_message || "—" }}
</TableCell>
<TableCell class="text-right">
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button variant="ghost" size="sm">
<MoreVerticalIcon class="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Akcije</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem @click="testConnection(p)">
<FlaskConicalIcon class="h-4 w-4 mr-2" />
Test povezavo
</DropdownMenuItem>
<DropdownMenuItem @click="sendTestEmail(p)">
<SendIcon class="h-4 w-4 mr-2" />
Pošlji testni email
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem @click="openEdit(p)">
<PencilIcon class="h-4 w-4 mr-2" />
Uredi profil
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
</TableBody>
</Table>
</CardContent>
</Card>
<Dialog :open="createOpen" @update:open="(val) => (createOpen = val)">
<DialogContent class="max-w-2xl">
<DialogHeader>
<DialogTitle>Nov Mail profil</DialogTitle>
<DialogDescription
>Ustvarite nov SMTP profil za pošiljanje e-pošte</DialogDescription
>
</DialogHeader>
<form @submit.prevent="submitCreate" class="space-y-4">
<div class="grid gap-4 grid-cols-2">
<div class="space-y-2">
<Label for="create-name">Ime</Label>
<Input id="create-name" v-model="form.name" type="text" />
</div>
<div class="space-y-2">
<Label for="create-host">Host</Label>
<Input id="create-host" v-model="form.host" type="text" />
</div>
<div class="space-y-2">
<Label for="create-port">Port</Label>
<Input id="create-port" v-model.number="form.port" type="number" />
</div>
<div class="space-y-2">
<Label for="create-encryption">Encryption</Label>
<Select v-model="form.encryption">
<SelectTrigger id="create-encryption">
<SelectValue placeholder="Izberi..." />
</SelectTrigger>
<SelectContent>
<SelectItem :value="null">None</SelectItem>
<SelectItem value="tls">TLS</SelectItem>
<SelectItem value="ssl">SSL</SelectItem>
</SelectContent>
</Select>
</div>
<div class="space-y-2">
<Label for="create-username">Username</Label>
<Input id="create-username" v-model="form.username" type="text" />
</div>
<div class="space-y-2">
<Label for="create-password">Password</Label>
<Input
id="create-password"
v-model="form.password"
type="password"
autocomplete="new-password"
/>
</div>
<div class="space-y-2">
<Label for="create-from-address">From naslov</Label>
<Input id="create-from-address" v-model="form.from_address" type="email" />
</div>
<div class="space-y-2">
<Label for="create-from-name">From ime</Label>
<Input id="create-from-name" v-model="form.from_name" type="text" />
</div>
<div class="space-y-2">
<Label for="create-priority">Prioriteta</Label>
<Input id="create-priority" v-model.number="form.priority" type="number" />
</div>
</div>
</form>
<DialogFooter>
<Button variant="outline" @click="closeCreate" :disabled="form.processing"
>Prekliči</Button
>
<Button @click="submitCreate" :disabled="form.processing">Shrani</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<Dialog :open="editOpen" @update:open="(val) => (editOpen = val)">
<DialogContent class="max-w-2xl">
<DialogHeader>
<DialogTitle>Uredi Mail profil</DialogTitle>
<DialogDescription>Posodobite nastavitve SMTP profila</DialogDescription>
</DialogHeader>
<form @submit.prevent="submitEdit" class="space-y-4">
<div class="grid gap-4 grid-cols-2">
<div class="space-y-2">
<Label for="edit-name">Ime</Label>
<Input id="edit-name" v-model="form.name" type="text" />
</div>
<div class="space-y-2">
<Label for="edit-host">Host</Label>
<Input id="edit-host" v-model="form.host" type="text" />
</div>
<div class="space-y-2">
<Label for="edit-port">Port</Label>
<Input id="edit-port" v-model.number="form.port" type="number" />
</div>
<div class="space-y-2">
<Label for="edit-encryption">Encryption</Label>
<Select v-model="form.encryption">
<SelectTrigger id="edit-encryption">
<SelectValue placeholder="Izberi..." />
</SelectTrigger>
<SelectContent>
<SelectItem :value="null">None</SelectItem>
<SelectItem value="tls">TLS</SelectItem>
<SelectItem value="ssl">SSL</SelectItem>
</SelectContent>
</Select>
</div>
<div class="space-y-2">
<Label for="edit-username">Username</Label>
<Input id="edit-username" v-model="form.username" type="text" />
</div>
<div class="space-y-2">
<Label for="edit-password">Password (nova, če spreminjaš)</Label>
<Input
id="edit-password"
v-model="form.password"
type="password"
autocomplete="new-password"
/>
</div>
<div class="space-y-2">
<Label for="edit-from-address">From naslov</Label>
<Input id="edit-from-address" v-model="form.from_address" type="email" />
</div>
<div class="space-y-2">
<Label for="edit-from-name">From ime</Label>
<Input id="edit-from-name" v-model="form.from_name" type="text" />
</div>
<div class="space-y-2">
<Label for="edit-priority">Prioriteta</Label>
<Input id="edit-priority" v-model.number="form.priority" type="number" />
</div>
</div>
<p class="text-sm text-muted-foreground">
Pusti geslo prazno, če želiš obdržati obstoječe.
</p>
</form>
<DialogFooter>
<Button variant="outline" @click="closeEdit" :disabled="form.processing"
>Prekliči</Button
>
<Button @click="submitEdit" :disabled="form.processing">Shrani</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</AdminLayout>
</template>
<style scoped></style>