347 lines
11 KiB
Vue
347 lines
11 KiB
Vue
<script setup>
|
|
import AdminLayout from "@/Layouts/AdminLayout.vue";
|
|
import { Head, useForm, Link, router } from "@inertiajs/vue3";
|
|
import { computed, ref } from "vue";
|
|
import { PlusIcon, PencilIcon, Trash2Icon, SendIcon, MessageSquareIcon, 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 { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/Components/ui/dialog';
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/Components/ui/select';
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from '@/Components/ui/dropdown-menu';
|
|
|
|
const props = defineProps({
|
|
initialTemplates: { type: Array, default: () => [] },
|
|
profiles: { type: Array, default: () => [] },
|
|
senders: { type: Array, default: () => [] },
|
|
});
|
|
|
|
const templates = computed(() => props.initialTemplates || []);
|
|
const profilesById = computed(() =>
|
|
Object.fromEntries((props.profiles || []).map((p) => [p.id, p]))
|
|
);
|
|
const sendersById = computed(() =>
|
|
Object.fromEntries((props.senders || []).map((s) => [s.id, s]))
|
|
);
|
|
const sendersByProfile = computed(() => {
|
|
const map = {};
|
|
(props.senders || []).forEach((s) => {
|
|
if (!map[s.profile_id]) map[s.profile_id] = [];
|
|
map[s.profile_id].push(s);
|
|
});
|
|
return map;
|
|
});
|
|
|
|
// No manual reload; Inertia visits will refresh props
|
|
|
|
// Create/Edit modal
|
|
const editOpen = ref(false);
|
|
const editing = ref(null);
|
|
const form = useForm({
|
|
name: "",
|
|
slug: "",
|
|
content: "",
|
|
variables_json: {},
|
|
is_active: true,
|
|
default_profile_id: null,
|
|
default_sender_id: null,
|
|
});
|
|
|
|
function openCreate() {
|
|
editing.value = null;
|
|
form.reset();
|
|
form.is_active = true;
|
|
editOpen.value = true;
|
|
}
|
|
|
|
function openEdit(t) {
|
|
editing.value = t;
|
|
form.reset();
|
|
form.name = t.name;
|
|
form.slug = t.slug;
|
|
form.content = t.content;
|
|
form.variables_json = t.variables_json || {};
|
|
form.is_active = !!t.is_active;
|
|
form.default_profile_id = t.default_profile_id || null;
|
|
form.default_sender_id = t.default_sender_id || null;
|
|
editOpen.value = true;
|
|
}
|
|
|
|
async function submitEdit() {
|
|
try {
|
|
form.processing = true;
|
|
const payload = {
|
|
name: form.name,
|
|
slug: form.slug,
|
|
content: form.content,
|
|
variables_json: form.variables_json || {},
|
|
is_active: !!form.is_active,
|
|
default_profile_id: form.default_profile_id || null,
|
|
default_sender_id: form.default_sender_id || null,
|
|
};
|
|
if (editing.value) {
|
|
await router.put(route("admin.sms-templates.update", editing.value.id), payload, {
|
|
preserveScroll: true,
|
|
});
|
|
} else {
|
|
await router.post(route("admin.sms-templates.store"), payload, {
|
|
preserveScroll: true,
|
|
});
|
|
}
|
|
editOpen.value = false;
|
|
} finally {
|
|
form.processing = false;
|
|
}
|
|
}
|
|
|
|
async function destroyTemplate(t) {
|
|
if (!confirm(`Izbrišem SMS predlogo "${t.name}"?`)) return;
|
|
await router.delete(route("admin.sms-templates.destroy", t.id), {
|
|
preserveScroll: true,
|
|
});
|
|
}
|
|
|
|
// Test send modal
|
|
const testOpen = ref(false);
|
|
const testTarget = ref(null);
|
|
const testForm = useForm({
|
|
to: "",
|
|
variables: {},
|
|
profile_id: null,
|
|
sender_id: null,
|
|
country_code: null,
|
|
delivery_report: true,
|
|
});
|
|
const testResult = ref(null);
|
|
|
|
function openTest(t) {
|
|
testTarget.value = t;
|
|
testForm.reset();
|
|
testForm.delivery_report = true;
|
|
testForm.profile_id = t.default_profile_id || null;
|
|
testForm.sender_id = t.default_sender_id || null;
|
|
testResult.value = null;
|
|
testOpen.value = true;
|
|
}
|
|
|
|
async function submitTest() {
|
|
if (!testTarget.value) return;
|
|
const payload = {
|
|
to: testForm.to,
|
|
variables: testForm.variables || {},
|
|
profile_id: testForm.profile_id || null,
|
|
sender_id: testForm.sender_id || null,
|
|
country_code: testForm.country_code || null,
|
|
delivery_report: !!testForm.delivery_report,
|
|
};
|
|
await router.post(
|
|
route("admin.sms-templates.send-test", testTarget.value.id),
|
|
payload,
|
|
{
|
|
preserveScroll: true,
|
|
onSuccess: () => {
|
|
testOpen.value = false;
|
|
testResult.value = null;
|
|
},
|
|
}
|
|
);
|
|
}
|
|
|
|
function currentSenders() {
|
|
const pid = form.default_profile_id;
|
|
if (!pid) return [];
|
|
return (sendersByProfile.value[pid] || []).filter((s) => s.active);
|
|
}
|
|
|
|
function currentSendersForTest() {
|
|
const pid = testForm.profile_id;
|
|
if (!pid) return [];
|
|
return (sendersByProfile.value[pid] || []).filter((s) => s.active);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<AdminLayout title="SMS predloge">
|
|
<Head title="SMS predloge" />
|
|
|
|
<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">
|
|
<MessageSquareIcon class="h-5 w-5" />
|
|
</div>
|
|
<div>
|
|
<CardTitle>SMS predloge</CardTitle>
|
|
<CardDescription>Upravljajte predloge za SMS sporočila</CardDescription>
|
|
</div>
|
|
</div>
|
|
<Button as-child>
|
|
<Link :href="route('admin.sms-templates.create')">
|
|
<PlusIcon class="h-4 w-4 mr-2" />
|
|
Nova predloga
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>Ime</TableHead>
|
|
<TableHead>Slug</TableHead>
|
|
<TableHead>Privzet profil/sender</TableHead>
|
|
<TableHead class="text-center">Aktivno</TableHead>
|
|
<TableHead class="text-right">Akcije</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
<TableRow v-for="t in templates" :key="t.id">
|
|
<TableCell class="font-medium">{{ t.name }}</TableCell>
|
|
<TableCell>
|
|
<Badge variant="outline">{{ t.slug }}</Badge>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div class="text-sm">
|
|
<span>{{ profilesById[t.default_profile_id]?.name || "—" }}</span>
|
|
<span v-if="t.default_sender_id" class="text-muted-foreground">
|
|
/ {{ sendersById[t.default_sender_id]?.sname }}
|
|
</span>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell class="text-center">
|
|
<Badge v-if="t.is_active" variant="default" class="bg-green-100 text-green-800 hover:bg-green-100">Da</Badge>
|
|
<Badge v-else variant="secondary">Ne</Badge>
|
|
</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 as-child>
|
|
<Link :href="route('admin.sms-templates.edit', t.id)">
|
|
<PencilIcon class="h-4 w-4 mr-2" />
|
|
Uredi
|
|
</Link>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem @click="openTest(t)">
|
|
<SendIcon class="h-4 w-4 mr-2" />
|
|
Testno pošiljanje
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem @click="destroyTemplate(t)" class="text-destructive">
|
|
<Trash2Icon class="h-4 w-4 mr-2" />
|
|
Izbriši
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</TableCell>
|
|
</TableRow>
|
|
</TableBody>
|
|
</Table>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Dialog :open="testOpen" @update:open="(val) => testOpen = val">
|
|
<DialogContent class="max-w-2xl">
|
|
<DialogHeader>
|
|
<DialogTitle>Testno pošiljanje</DialogTitle>
|
|
<DialogDescription>Pošljite testno SMS sporočilo</DialogDescription>
|
|
</DialogHeader>
|
|
<div class="space-y-4">
|
|
<div class="grid gap-4 grid-cols-2">
|
|
<div class="space-y-2">
|
|
<Label for="test-to">Prejemnik (E.164)</Label>
|
|
<Input
|
|
id="test-to"
|
|
v-model="testForm.to"
|
|
type="text"
|
|
placeholder="+386..."
|
|
/>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label for="test-country">Državna koda</Label>
|
|
<Input
|
|
id="test-country"
|
|
v-model="testForm.country_code"
|
|
type="text"
|
|
placeholder="SI"
|
|
/>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label for="test-profile">Profil</Label>
|
|
<Select v-model="testForm.profile_id">
|
|
<SelectTrigger id="test-profile">
|
|
<SelectValue placeholder="Privzeti" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem :value="null">Privzeti</SelectItem>
|
|
<SelectItem v-for="p in props.profiles" :key="p.id" :value="p.id">
|
|
{{ p.name }}
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label for="test-sender">Sender</Label>
|
|
<Select v-model="testForm.sender_id" :disabled="!testForm.profile_id">
|
|
<SelectTrigger id="test-sender">
|
|
<SelectValue placeholder="Privzeti" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem :value="null">Privzeti</SelectItem>
|
|
<SelectItem v-for="s in currentSendersForTest()" :key="s.id" :value="s.id">
|
|
{{ s.sname }}
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label for="test-delivery">Dostavna poročila</Label>
|
|
<Select v-model="testForm.delivery_report">
|
|
<SelectTrigger id="test-delivery">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem :value="true">Da</SelectItem>
|
|
<SelectItem :value="false">Ne</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<DialogFooter>
|
|
<Button variant="outline" @click="() => testOpen = false">Zapri</Button>
|
|
<Button
|
|
@click="submitTest"
|
|
:disabled="testForm.processing || !testTarget"
|
|
>
|
|
<SendIcon class="h-4 w-4 mr-2" />
|
|
Pošlji test
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</AdminLayout>
|
|
</template>
|
|
|
|
<style scoped>
|
|
</style>
|