Admin panel updated with shadcn-vue components

This commit is contained in:
Simon Pocrnjič
2026-01-05 18:27:35 +01:00
parent 70a5d015e0
commit c4d9ecb39e
37 changed files with 5407 additions and 3740 deletions
+163 -149
View File
@@ -1,10 +1,25 @@
<script setup>
import AdminLayout from "@/Layouts/AdminLayout.vue";
import DialogModal from "@/Components/DialogModal.vue";
import { Head, useForm, Link, router } from "@inertiajs/vue3";
import { computed, ref } from "vue";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { faPlus, faPen, faTrash, faPaperPlane } from "@fortawesome/free-solid-svg-icons";
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: () => [] },
@@ -159,174 +174,173 @@ function currentSendersForTest() {
<template>
<AdminLayout title="SMS predloge">
<Head title="SMS predloge" />
<div class="flex items-center justify-between mb-6">
<h1 class="text-xl font-semibold text-gray-800">SMS predloge</h1>
<Link
:href="route('admin.sms-templates.create')"
class="inline-flex items-center gap-2 px-4 py-2 rounded-md bg-indigo-600 text-white text-sm font-medium hover:bg-indigo-500 shadow"
>
<FontAwesomeIcon :icon="faPlus" class="w-4 h-4" /> Nova predloga
</Link>
</div>
<div class="rounded-lg border bg-white overflow-hidden shadow-sm">
<table class="w-full text-sm">
<thead class="bg-gray-50 text-gray-600 text-xs uppercase tracking-wider">
<tr>
<th class="px-3 py-2 text-left">Ime</th>
<th class="px-3 py-2 text-left">Slug</th>
<th class="px-3 py-2 text-left">Privzet profil/sender</th>
<th class="px-3 py-2">Aktivno</th>
<th class="px-3 py-2">Akcije</th>
</tr>
</thead>
<tbody>
<tr
v-for="t in templates"
:key="t.id"
class="border-t last:border-b hover:bg-gray-50"
>
<td class="px-3 py-2 font-medium text-gray-800">{{ t.name }}</td>
<td class="px-3 py-2 text-gray-600">{{ t.slug }}</td>
<td class="px-3 py-2 text-gray-600">
<span>{{ profilesById[t.default_profile_id]?.name || "—" }}</span>
<span v-if="t.default_sender_id">
/ {{ sendersById[t.default_sender_id]?.sname }}</span
>
</td>
<td class="px-3 py-2 text-center">
<span :class="t.is_active ? 'text-emerald-600' : 'text-rose-600'">{{
t.is_active ? "Da" : "Ne"
}}</span>
</td>
<td class="px-3 py-2 flex items-center gap-2">
<Link
:href="route('admin.sms-templates.edit', t.id)"
class="inline-flex items-center gap-1 text-xs px-2 py-1 rounded border text-indigo-700 border-indigo-300 bg-indigo-50 hover:bg-indigo-100"
>
<FontAwesomeIcon :icon="faPen" class="w-3.5 h-3.5" /> Uredi
</Link>
<button
@click="openTest(t)"
class="inline-flex items-center gap-1 text-xs px-2 py-1 rounded border text-emerald-700 border-emerald-300 bg-emerald-50 hover:bg-emerald-100"
>
<FontAwesomeIcon :icon="faPaperPlane" class="w-3.5 h-3.5" /> Test
</button>
<button
@click="destroyTemplate(t)"
class="inline-flex items-center gap-1 text-xs px-2 py-1 rounded border text-rose-700 border-rose-300 bg-rose-50 hover:bg-rose-100"
>
<FontAwesomeIcon :icon="faTrash" class="w-3.5 h-3.5" /> Izbriši
</button>
</td>
</tr>
</tbody>
</table>
</div>
<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>
<!-- Edit/Create now handled on dedicated page -->
<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>
<!-- Test Send Modal -->
<DialogModal :show="testOpen" max-width="2xl" @close="() => (testOpen = false)">
<template #title> Testno pošiljanje </template>
<template #content>
<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>
<label class="label">Prejemnik (E.164)</label>
<input
<div class="space-y-2">
<Label for="test-to">Prejemnik (E.164)</Label>
<Input
id="test-to"
v-model="testForm.to"
type="text"
class="input"
placeholder="+386..."
/>
</div>
<div>
<label class="label">Državna koda</label>
<input
<div class="space-y-2">
<Label for="test-country">Državna koda</Label>
<Input
id="test-country"
v-model="testForm.country_code"
type="text"
class="input"
placeholder="SI"
/>
</div>
<div>
<label class="label">Profil</label>
<select v-model="testForm.profile_id" class="input">
<option :value="null">(privzeti)</option>
<option v-for="p in props.profiles" :key="p.id" :value="p.id">
{{ p.name }}
</option>
</select>
<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>
<label class="label">Sender</label>
<select
v-model="testForm.sender_id"
class="input"
:disabled="!testForm.profile_id"
>
<option :value="null">(privzeti)</option>
<option v-for="s in currentSendersForTest()" :key="s.id" :value="s.id">
{{ s.sname }}
</option>
</select>
<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>
<label class="label">Dostavna poročila</label>
<select v-model="testForm.delivery_report" class="input">
<option :value="true">Da</option>
<option :value="false">Ne</option>
</select>
<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>
<!-- Result details removed in favor of flash messages after redirect -->
</div>
</template>
<template #footer>
<button
type="button"
@click="() => (testOpen = false)"
class="px-4 py-2 text-sm rounded-md border bg-white hover:bg-gray-50"
>
Zapri
</button>
<button
type="button"
@click="submitTest"
:disabled="testForm.processing || !testTarget"
class="px-4 py-2 text-sm rounded-md bg-emerald-600 text-white hover:bg-emerald-500 disabled:opacity-50"
>
<FontAwesomeIcon :icon="faPaperPlane" class="w-3.5 h-3.5 mr-1" /> Pošlji test
</button>
</template>
</DialogModal>
<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>
.input {
width: 100%;
border-radius: 0.375rem;
border: 1px solid var(--tw-color-gray-300, #d1d5db);
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
line-height: 1.25rem;
}
.input:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-color: #6366f1;
border-color: #6366f1;
box-shadow: 0 0 0 1px #6366f1;
}
.label {
display: block;
font-size: 0.65rem;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
color: #6b7280;
margin-bottom: 0.25rem;
}
</style>