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

294 lines
9.2 KiB
Vue

<script setup>
import AdminLayout from "@/Layouts/AdminLayout.vue";
import { Head, Link, router } from "@inertiajs/vue3";
import { ref, watch, computed } from "vue";
import {
MessageSquareIcon,
FilterIcon,
XIcon,
EyeIcon,
MessageSquareTextIcon,
} 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 {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/Components/ui/select";
import DataTableNew2 from "@/Components/DataTable/DataTableNew2.vue";
import Pagination from "@/Components/Pagination.vue";
import AppCard from "@/Components/app/ui/card/AppCard.vue";
const props = defineProps({
logs: { type: Object, required: true },
profiles: { type: Array, default: () => [] },
templates: { type: Array, default: () => [] },
filters: { type: Object, default: () => ({}) },
});
const f = ref({
status: props.filters.status ?? "",
profile_id: props.filters.profile_id ?? "",
template_id: props.filters.template_id ?? "",
search: props.filters.search ?? "",
from: props.filters.from ?? "",
to: props.filters.to ?? "",
});
function reload() {
const query = Object.fromEntries(
Object.entries(f.value).filter(([_, v]) => v !== null && v !== undefined && v !== "")
);
router.get(route("admin.sms-logs.index"), query, {
preserveScroll: true,
preserveState: true,
});
}
watch(
() => [
f.value.status,
f.value.profile_id,
f.value.template_id,
f.value.from,
f.value.to,
],
() => reload()
);
function clearFilters() {
f.value = { status: "", profile_id: "", template_id: "", search: "", from: "", to: "" };
reload();
}
const columns = [
{ key: "created_at", label: "Čas", sortable: false },
{ key: "to_number", label: "Prejemnik", sortable: false },
{ key: "sender", label: "Sender", sortable: false },
{ key: "profile", label: "Profil", sortable: false },
{ key: "template", label: "Predloga", sortable: false },
{ key: "status", label: "Status", sortable: false },
{ key: "cost", label: "Cena", sortable: false },
{ key: "provider_message_id", label: "Provider ID", sortable: false },
{ key: "actions", label: "", sortable: false },
];
function getStatusVariant(status) {
if (status === "queued") return "secondary";
if (status === "sent") return "default";
if (status === "delivered") return "default";
if (status === "failed") return "destructive";
return "outline";
}
function getStatusClass(status) {
if (status === "queued") return "bg-amber-100 text-amber-800 hover:bg-amber-100";
if (status === "sent") return "bg-sky-100 text-sky-800 hover:bg-sky-100";
if (status === "delivered") return "bg-green-100 text-green-800 hover:bg-green-100";
if (status === "failed") return "bg-red-100 text-red-800 hover:bg-red-100";
return "";
}
</script>
<template>
<AdminLayout title="SMS dnevniki">
<Head title="SMS dnevniki" />
<Card class="mb-6">
<CardHeader>
<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"
>
<MessageSquareIcon class="h-5 w-5" />
</div>
<div>
<CardTitle>SMS dnevniki</CardTitle>
<CardDescription>Pregled poslanih SMS sporočil</CardDescription>
</div>
</div>
</CardHeader>
</Card>
<Card class="mb-6">
<CardHeader>
<div class="flex items-center gap-2">
<FilterIcon class="h-4 w-4" />
<CardTitle class="text-base">Filtri</CardTitle>
</div>
</CardHeader>
<CardContent>
<div class="grid grid-cols-1 md:grid-cols-6 gap-4">
<div class="space-y-2">
<Label for="filter-status">Status</Label>
<Select v-model="f.status">
<SelectTrigger id="filter-status">
<SelectValue placeholder="Vsi" />
</SelectTrigger>
<SelectContent>
<SelectItem :value="null">Vsi</SelectItem>
<SelectItem value="queued">queued</SelectItem>
<SelectItem value="sent">sent</SelectItem>
<SelectItem value="delivered">delivered</SelectItem>
<SelectItem value="failed">failed</SelectItem>
</SelectContent>
</Select>
</div>
<div class="space-y-2">
<Label for="filter-profile">Profil</Label>
<Select v-model="f.profile_id">
<SelectTrigger id="filter-profile">
<SelectValue placeholder="Vsi" />
</SelectTrigger>
<SelectContent>
<SelectItem :value="null">Vsi</SelectItem>
<SelectItem v-for="p in profiles" :key="p.id" :value="p.id">{{
p.name
}}</SelectItem>
</SelectContent>
</Select>
</div>
<div class="space-y-2">
<Label for="filter-template">Predloga</Label>
<Select v-model="f.template_id">
<SelectTrigger id="filter-template">
<SelectValue placeholder="Vse" />
</SelectTrigger>
<SelectContent>
<SelectItem :value="null">Vse</SelectItem>
<SelectItem v-for="t in templates" :key="t.id" :value="t.id">{{
t.name
}}</SelectItem>
</SelectContent>
</Select>
</div>
<div class="space-y-2">
<Label for="filter-from">Od</Label>
<Input id="filter-from" type="date" v-model="f.from" />
</div>
<div class="space-y-2">
<Label for="filter-to">Do</Label>
<Input id="filter-to" type="date" v-model="f.to" />
</div>
<div class="space-y-2">
<Label for="filter-search">Iskanje</Label>
<Input
id="filter-search"
type="text"
v-model="f.search"
placeholder="to, sender, provider id"
@keyup.enter="reload"
/>
</div>
</div>
<div class="mt-4 flex items-center gap-2">
<Button size="sm" @click="reload">
<FilterIcon class="h-4 w-4" />
Filtriraj
</Button>
<Button size="sm" variant="outline" @click="clearFilters">
<XIcon class="h-4 w-4" />
Počisti
</Button>
</div>
</CardContent>
</Card>
<AppCard
title=""
padding="none"
class="p-0! gap-0"
header-class="py-3! px-4 gap-0 text-muted-foreground"
body-class=""
>
<template #header>
<div class="flex items-center gap-2">
<MessageSquareTextIcon size="18" />
<CardTitle class="uppercase">Poslani</CardTitle>
</div>
</template>
<DataTableNew2
:columns="columns"
:data="logs.data"
:meta="logs"
:page-size="25"
:page-size-options="[10, 15, 25, 50, 100]"
route-name="admin.sms-logs.index"
>
<template #cell-created_at="{ row }">
<span class="text-sm">{{ new Date(row.created_at).toLocaleString() }}</span>
</template>
<template #cell-to_number="{ row }">
<span class="text-sm">{{ row.to_number }}</span>
</template>
<template #cell-sender="{ row }">
<span class="text-sm text-muted-foreground">{{ row.sender || "—" }}</span>
</template>
<template #cell-profile="{ row }">
<span class="text-sm">{{ row.profile?.name || "—" }}</span>
</template>
<template #cell-template="{ row }">
<Badge v-if="row.template" variant="outline">{{
row.template.slug || row.template.name
}}</Badge>
<span v-else class="text-sm text-muted-foreground">—</span>
</template>
<template #cell-status="{ row }">
<Badge
:variant="getStatusVariant(row.status)"
:class="getStatusClass(row.status)"
>
{{ row.status }}
</Badge>
</template>
<template #cell-cost="{ row }">
<span class="text-sm">
{{
row.cost != null
? Number(row.cost).toFixed(2) + " " + (row.currency || "")
: "—"
}}
</span>
</template>
<template #cell-provider_message_id="{ row }">
<span class="text-sm text-muted-foreground truncate max-w-[150px] block">{{
row.provider_message_id || "—"
}}</span>
</template>
<template #cell-actions="{ row }">
<div class="flex justify-end">
<Button variant="ghost" size="sm" as-child>
<Link :href="route('admin.sms-logs.show', row.id)">
<EyeIcon class="h-4 w-4" />
</Link>
</Button>
</div>
</template>
</DataTableNew2>
</AppCard>
</AdminLayout>
</template>
<style scoped></style>