343 lines
10 KiB
Vue
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>
|