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

343 lines
10 KiB
Vue

<script setup>
import AdminLayout from "@/Layouts/AdminLayout.vue";
import { Head, useForm, router } from "@inertiajs/vue3";
import { ref, watch } from "vue";
import { Card, CardContent, 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 {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/Components/ui/select";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/Components/ui/table";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/Components/ui/dialog";
import { Textarea } from "@/Components/ui/textarea";
import { Badge } from "@/Components/ui/badge";
import {
MessageSquareIcon,
PlusIcon,
SendIcon,
CoinsIcon,
TagIcon,
} from "lucide-vue-next";
const props = defineProps({
initialProfiles: { type: Array, default: () => [] },
});
const profiles = ref(props.initialProfiles || []);
// Keep local ref in sync with Inertia prop changes (after router navigations)
watch(
() => props.initialProfiles,
(val) => {
profiles.value = val || [];
}
);
// Create profile modal
const createOpen = ref(false);
const createForm = useForm({
name: "",
active: true,
api_username: "",
api_password: "",
});
function openCreate() {
createForm.reset();
createForm.active = true;
createOpen.value = true;
}
async function submitCreate() {
try {
createForm.processing = true;
const payload = {
name: createForm.name,
active: !!createForm.active,
api_username: createForm.api_username,
api_password: createForm.api_password,
};
await router.post(route("admin.sms-profiles.store"), payload, {
preserveScroll: true,
onSuccess: () => {
createOpen.value = false;
},
});
} finally {
createForm.processing = false;
}
}
// Test send modal
const testOpen = ref(false);
const testTarget = ref(null);
const testResult = ref(null);
const testForm = useForm({
to: "",
message: "",
sender_id: null,
delivery_report: true,
country_code: null,
});
function openTest(p) {
testForm.reset();
testTarget.value = p;
testResult.value = null;
testOpen.value = true;
}
async function submitTest() {
if (!testTarget.value) return;
try {
testForm.processing = true;
const payload = {
to: testForm.to,
message: testForm.message,
sender_id: testForm.sender_id,
delivery_report: !!testForm.delivery_report,
country_code: testForm.country_code,
};
await router.post(
route("admin.sms-profiles.test-send", testTarget.value.id),
payload,
{
preserveScroll: true,
onSuccess: () => {
testResult.value = null;
testOpen.value = false;
},
}
);
} finally {
testForm.processing = false;
}
}
// Balance & Price
const balances = ref({});
const quotes = ref({});
function fetchBalance(p) {
window.axios.post(route("admin.sms-profiles.balance", p.id)).then((r) => {
balances.value[p.id] = r.data.balance;
});
}
function fetchPrice(p) {
window.axios.post(route("admin.sms-profiles.price", p.id)).then((r) => {
quotes.value[p.id] = r.data.quotes || [];
});
}
const formatDateTime = (s) => (s ? new Date(s).toLocaleString() : "—");
</script>
<template>
<AdminLayout title="SMS profili">
<Head title="SMS profili" />
<Card>
<CardHeader>
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<MessageSquareIcon class="h-5 w-5 text-muted-foreground" />
<CardTitle>
SMS profili
<Badge variant="secondary" class="ml-2">{{ profiles.length }}</Badge>
</CardTitle>
</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>Uporabnik</TableHead>
<TableHead>Aktiven</TableHead>
<TableHead>Pošiljatelji</TableHead>
<TableHead>Bilanca</TableHead>
<TableHead>Cena</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.api_username }}</TableCell>
<TableCell>
<Badge :variant="p.active ? 'default' : 'secondary'">
{{ p.active ? "Da" : "Ne" }}
</Badge>
</TableCell>
<TableCell class="text-muted-foreground text-xs">
<span v-if="(p.senders || []).length === 0">—</span>
<span v-else>
{{ p.senders.map((s) => s.sname).join(", ") }}
</span>
</TableCell>
<TableCell>
<div class="flex items-center gap-2">
<Button @click="fetchBalance(p)" variant="outline" size="sm">
<CoinsIcon class="h-3.5 w-3.5 mr-1" /> Pridobi
</Button>
<span class="text-xs text-muted-foreground">{{
balances[p.id] ?? "—"
}}</span>
</div>
</TableCell>
<TableCell>
<div class="flex items-center gap-2">
<Button @click="fetchPrice(p)" variant="outline" size="sm">
<TagIcon class="h-3.5 w-3.5 mr-1" /> Cene
</Button>
<span
class="text-xs text-muted-foreground truncate max-w-[200px]"
:title="(quotes[p.id] || []).join(', ')"
>
{{ (quotes[p.id] || []).join(", ") || "—" }}
</span>
</div>
</TableCell>
<TableCell class="text-right">
<Button
@click="openTest(p)"
variant="default"
size="sm"
title="Pošlji test SMS"
>
<SendIcon class="h-3.5 w-3.5 mr-1" /> Test SMS
</Button>
</TableCell>
</TableRow>
</TableBody>
</Table>
</CardContent>
</Card>
<!-- Create Profile Modal -->
<Dialog :open="createOpen" @update:open="(val) => (createOpen = val)">
<DialogContent class="max-w-2xl">
<DialogHeader>
<DialogTitle>Nov SMS profil</DialogTitle>
</DialogHeader>
<form @submit.prevent="submitCreate" id="create-sms-profile" class="space-y-4">
<div class="grid gap-4 grid-cols-2">
<div class="space-y-2">
<Label>Ime</Label>
<Input v-model="createForm.name" type="text" />
</div>
<div class="space-y-2">
<Label>Aktivno</Label>
<Select v-model="createForm.active">
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem :value="true">Da</SelectItem>
<SelectItem :value="false">Ne</SelectItem>
</SelectContent>
</Select>
</div>
<div class="space-y-2">
<Label>API uporabnik</Label>
<Input v-model="createForm.api_username" type="text" />
</div>
<div class="space-y-2">
<Label>API geslo</Label>
<Input
v-model="createForm.api_password"
type="password"
autocomplete="new-password"
/>
</div>
</div>
</form>
<DialogFooter>
<Button type="button" variant="outline" @click="() => (createOpen = false)"
>Prekliči</Button
>
<Button
form="create-sms-profile"
type="submit"
:disabled="createForm.processing"
>Shrani</Button
>
</DialogFooter>
</DialogContent>
</Dialog>
<!-- Test Send Modal -->
<Dialog :open="testOpen" @update:open="(val) => (testOpen = val)">
<DialogContent class="max-w-2xl">
<DialogHeader>
<DialogTitle>Testni SMS</DialogTitle>
</DialogHeader>
<div class="space-y-4">
<div class="grid gap-4 grid-cols-2">
<div class="space-y-2">
<Label>Prejemnik (E.164)</Label>
<Input v-model="testForm.to" type="text" placeholder="+386..." />
</div>
<div class="space-y-2">
<Label>Državna koda (opcijsko)</Label>
<Input v-model="testForm.country_code" type="text" placeholder="SI" />
</div>
<div class="space-y-2 col-span-2">
<Label>Sporočilo</Label>
<Textarea v-model="testForm.message" rows="4" />
</div>
<div class="space-y-2">
<Label>Dostavna poročila</Label>
<Select v-model="testForm.delivery_report">
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem :value="true">Da</SelectItem>
<SelectItem :value="false">Ne</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
<DialogFooter>
<Button type="button" variant="outline" @click="() => (testOpen = false)"
>Zapri</Button
>
<Button
type="button"
@click="submitTest"
:disabled="testForm.processing || !testTarget"
>
<SendIcon class="h-3.5 w-3.5 mr-2" /> Pošlji test
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</AdminLayout>
</template>