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
+276 -145
View File
@@ -2,6 +2,27 @@
import AdminLayout from "@/Layouts/AdminLayout.vue";
import { Head, Link, useForm, router } from "@inertiajs/vue3";
import { ref, computed, watch } from "vue";
import { ArrowLeftIcon, SaveIcon, MessageSquareIcon, SendIcon } 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 { Textarea } from "@/Components/ui/textarea";
import { Switch } from "@/Components/ui/switch";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/Components/ui/select";
import { Separator } from "@/Components/ui/separator";
const props = defineProps({
template: { type: Object, default: null },
@@ -104,160 +125,270 @@ async function submitTest() {
<AdminLayout :title="props.template ? 'Uredi SMS predlogo' : 'Nova SMS predloga'">
<Head :title="props.template ? 'Uredi SMS predlogo' : 'Nova SMS predloga'" />
<div class="mb-4 flex items-center justify-between">
<div class="flex items-center gap-3">
<Link :href="route('admin.sms-templates.index')" class="text-sm text-indigo-600 hover:underline"> Nazaj na seznam</Link>
<div class="text-gray-700 text-sm" v-if="props.template">#{{ props.template.id }}</div>
</div>
<button
type="button"
@click="submit"
:disabled="form.processing"
class="px-4 py-2 text-sm rounded-md bg-indigo-600 text-white hover:bg-indigo-500 disabled:opacity-50"
>
Shrani
</button>
</div>
<Card class="mb-6">
<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>{{
props.template ? "Uredi SMS predlogo" : "Nova SMS predloga"
}}</CardTitle>
<CardDescription v-if="props.template"
>#{{ props.template.id }}</CardDescription
>
</div>
</div>
<div class="flex items-center gap-2">
<Button variant="outline" size="sm" as-child>
<Link :href="route('admin.sms-templates.index')">
<ArrowLeftIcon class="h-4 w-4 mr-2" />
Nazaj
</Link>
</Button>
<Button size="sm" @click="submit" :disabled="form.processing">
<SaveIcon class="h-4 w-4 mr-2" />
Shrani
</Button>
</div>
</div>
</CardHeader>
</Card>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Form -->
<div class="lg:col-span-2 rounded-xl border bg-white/60 backdrop-blur-sm shadow-sm p-5 space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="label">Ime</label>
<input v-model="form.name" type="text" class="input" />
</div>
<div>
<label class="label">Slug</label>
<input v-model="form.slug" type="text" class="input" placeholder="npr. payment_reminder" />
</div>
<div class="md:col-span-2">
<label class="label">Vsebina</label>
<textarea v-model="form.content" rows="8" class="input" placeholder="Pozdravljen {{ person.first_name }}, ..."></textarea>
<div class="text-[11px] text-gray-500 mt-1">
Uporabite placeholderje npr. <code>{first_name}</code> ali
<code v-pre>{{ person.first_name }}</code> ob pošiljanju se vrednosti nadomestijo.
<Card class="lg:col-span-2">
<CardHeader>
<CardTitle class="text-base">Nastavitve predloge</CardTitle>
</CardHeader>
<CardContent class="space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="name">Ime</Label>
<Input id="name" v-model="form.name" type="text" />
</div>
<div class="space-y-2">
<Label for="slug">Slug</Label>
<Input
id="slug"
v-model="form.slug"
type="text"
placeholder="npr. payment_reminder"
/>
</div>
<div class="md:col-span-2 space-y-2">
<Label for="content">Vsebina</Label>
<Textarea
id="content"
v-model="form.content"
rows="8"
placeholder="Pozdravljen {{ person.first_name }}, ..."
/>
<p class="text-xs text-muted-foreground">
Uporabite placeholderje npr.
<code class="bg-muted px-1 py-0.5 rounded">{first_name}</code> ali
<code class="bg-muted px-1 py-0.5 rounded" v-pre>{{
person.first_name
}}</code>
ob pošiljanju se vrednosti nadomestijo.
</p>
</div>
<div class="md:col-span-2 space-y-2">
<div class="flex items-center gap-2">
<Switch
id="allow-custom"
:default-value="form.allow_custom_body"
@update:model-value="(val) => (form.allow_custom_body = val)"
/>
<Label for="allow-custom" class="font-normal cursor-pointer"
>Dovoli lastno besedilo</Label
>
</div>
<p class="text-xs text-muted-foreground">
Če je omogočeno, lahko pošiljatelj namesto vsebine predloge napiše
poljubno besedilo.
</p>
</div>
<div class="space-y-2">
<Label for="profile">Privzet profil</Label>
<Select v-model="form.default_profile_id">
<SelectTrigger id="profile">
<SelectValue placeholder="Izberi profil" />
</SelectTrigger>
<SelectContent>
<SelectItem :value="null">—</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="sender">Privzet sender</Label>
<Select
v-model="form.default_sender_id"
:disabled="!form.default_profile_id"
>
<SelectTrigger id="sender">
<SelectValue placeholder="Izberi sender" />
</SelectTrigger>
<SelectContent>
<SelectItem :value="null">—</SelectItem>
<SelectItem
v-for="s in currentSendersForProfile(form.default_profile_id)"
:key="s.id"
:value="s.id"
>
{{ s.sname }}
</SelectItem>
</SelectContent>
</Select>
</div>
<div class="space-y-2">
<div class="flex items-center gap-2">
<Switch
id="active"
:default-value="form.is_active"
@update:model-value="(val) => (form.is_active = val)"
/>
<Label for="active" class="font-normal cursor-pointer">Aktivno</Label>
</div>
</div>
</div>
<div class="md:col-span-2">
<label class="label">Dovoli lastno besedilo</label>
<select v-model="form.allow_custom_body" class="input">
<option :value="true">Da</option>
<option :value="false">Ne</option>
</select>
<div class="text-[11px] text-gray-500 mt-1">Če je omogočeno, lahko pošiljatelj namesto vsebine predloge napiše poljubno besedilo.</div>
</div>
<div>
<label class="label">Privzet profil</label>
<select v-model="form.default_profile_id" class="input">
<option :value="null"></option>
<option v-for="p in props.profiles" :key="p.id" :value="p.id">{{ p.name }}</option>
</select>
</div>
<div>
<label class="label">Privzet sender</label>
<select v-model="form.default_sender_id" class="input" :disabled="!form.default_profile_id">
<option :value="null"></option>
<option v-for="s in currentSendersForProfile(form.default_profile_id)" :key="s.id" :value="s.id">
{{ s.sname }}
</option>
</select>
</div>
<div>
<label class="label">Aktivno</label>
<select v-model="form.is_active" class="input">
<option :value="true">Da</option>
<option :value="false">Ne</option>
</select>
</div>
<div class="md:col-span-2 grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="label">Akcija po pošiljanju</label>
<select v-model="form.action_id" class="input">
<option :value="null">(brez)</option>
<option v-for="a in props.actions" :key="a.id" :value="a.id">{{ a.name }}</option>
</select>
</div>
<div>
<label class="label">Odločitev</label>
<select v-model="form.decision_id" class="input" :disabled="!form.action_id">
<option :value="null">(brez)</option>
<option v-for="d in (props.actions.find(x => x.id === form.action_id)?.decisions || [])" :key="d.id" :value="d.id">{{ d.name }}</option>
</select>
</div>
</div>
</div>
</div>
<!-- Test send -->
<div class="rounded-xl border bg-white/60 backdrop-blur-sm shadow-sm p-5 space-y-4">
<div class="font-semibold text-gray-800">Testno pošiljanje</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="label">Prejemnik (E.164)</label>
<input v-model="testForm.to" type="text" class="input" placeholder="+386..." />
<Separator />
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="action">Akcija po pošiljanju</Label>
<Select v-model="form.action_id">
<SelectTrigger id="action">
<SelectValue placeholder="Brez" />
</SelectTrigger>
<SelectContent>
<SelectItem :value="null">Brez</SelectItem>
<SelectItem v-for="a in props.actions" :key="a.id" :value="a.id">{{
a.name
}}</SelectItem>
</SelectContent>
</Select>
</div>
<div class="space-y-2">
<Label for="decision">Odločitev</Label>
<Select v-model="form.decision_id" :disabled="!form.action_id">
<SelectTrigger id="decision">
<SelectValue placeholder="Brez" />
</SelectTrigger>
<SelectContent>
<SelectItem :value="null">Brez</SelectItem>
<SelectItem
v-for="d in props.actions.find((x) => x.id === form.action_id)
?.decisions || []"
:key="d.id"
:value="d.id"
>{{ d.name }}</SelectItem
>
</SelectContent>
</Select>
</div>
</div>
<div>
<label class="label">Državna koda</label>
<input v-model="testForm.country_code" type="text" class="input" placeholder="SI" />
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle class="text-base">Testno pošiljanje</CardTitle>
<CardDescription>Pošljite testno SMS sporočilo</CardDescription>
</CardHeader>
<CardContent class="space-y-4">
<div class="space-y-4">
<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 currentSendersForProfile(testForm.profile_id)"
: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 v-if="form.allow_custom_body" class="space-y-2">
<Label for="test-custom">Lastno besedilo (opcijsko)</Label>
<Textarea
id="test-custom"
v-model="testForm.custom_content"
rows="4"
placeholder="Če je izpolnjeno, bo namesto predloge poslano to besedilo."
/>
</div>
</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="flex items-center justify-end">
<Button
@click="submitTest"
:disabled="testForm.processing || !props.template"
>
<SendIcon class="h-4 w-4 mr-2" />
Pošlji test
</Button>
</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 currentSendersForProfile(testForm.profile_id)" :key="s.id" :value="s.id">{{ s.sname }}</option>
</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>
<div v-if="form.allow_custom_body" class="md:col-span-2">
<label class="label">Lastno besedilo (opcijsko)</label>
<textarea v-model="testForm.custom_content" rows="4" class="input" placeholder="Če je izpolnjeno, bo namesto predloge poslano to besedilo."></textarea>
</div>
</div>
<div class="flex items-center justify-end gap-2">
<button type="button" @click="submitTest" :disabled="testForm.processing || !props.template" class="px-3 py-2 text-sm rounded-md bg-emerald-600 text-white hover:bg-emerald-500 disabled:opacity-50">Pošlji test</button>
</div>
<!-- Result details removed in favor of flash messages after redirect -->
</div>
</CardContent>
</Card>
</div>
</AdminLayout>
</template>
<style scoped>
.input {
width: 100%;
border-radius: 0.375rem;
border: 1px solid #d1d5db;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
line-height: 1.25rem;
}
.input:focus {
outline: 2px solid transparent;
outline-offset: 2px;
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>
<style scoped></style>