Teren-app/resources/js/Pages/Admin/SmsTemplates/Edit.vue
Simon Pocrnjič 930ac83604 SMS service
2025-10-24 21:39:10 +02:00

264 lines
9.7 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import AdminLayout from "@/Layouts/AdminLayout.vue";
import { Head, Link, useForm, router } from "@inertiajs/vue3";
import { ref, computed, watch } from "vue";
const props = defineProps({
template: { type: Object, default: null },
profiles: { type: Array, default: () => [] },
senders: { type: Array, default: () => [] },
actions: { type: Array, default: () => [] },
});
const form = useForm({
name: props.template?.name ?? "",
slug: props.template?.slug ?? "",
content: props.template?.content ?? "",
variables_json: props.template?.variables_json ?? {},
is_active: props.template?.is_active ?? true,
default_profile_id: props.template?.default_profile_id ?? null,
default_sender_id: props.template?.default_sender_id ?? null,
allow_custom_body: props.template?.allow_custom_body ?? false,
action_id: props.template?.action_id ?? null,
decision_id: props.template?.decision_id ?? null,
});
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;
});
function currentSendersForProfile(pid) {
if (!pid) return [];
return (sendersByProfile.value[pid] || []).filter((s) => s.active);
}
function submit() {
if (props.template?.id) {
form.put(route("admin.sms-templates.update", props.template.id));
} else {
form.post(route("admin.sms-templates.store"), {
onSuccess: (page) => {
// If backend redirected, nothing to do. If returned JSON, navigate to edit.
try {
const id = page?.props?.template?.id; // in case of redirect props
if (id) {
router.visit(route("admin.sms-templates.edit", id));
}
} catch {}
},
onFinish: () => {},
preserveScroll: true,
});
}
}
// Test send panel
const testForm = useForm({
to: "",
variables: {},
profile_id: props.template?.default_profile_id ?? null,
sender_id: props.template?.default_sender_id ?? null,
country_code: null,
delivery_report: true,
custom_content: "",
});
const testResult = ref(null);
watch(
() => testForm.profile_id,
() => {
// reset sender when profile changes
testForm.sender_id = null;
}
);
async function submitTest() {
if (!props.template?.id) {
alert("Najprej shranite predlogo.");
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,
custom_content: testForm.custom_content || null,
};
await router.post(route("admin.sms-templates.send-test", props.template.id), payload, {
preserveScroll: true,
onSuccess: () => {
testResult.value = null;
},
});
}
</script>
<template>
<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>
<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.
</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..." />
</div>
<div>
<label class="label">Državna koda</label>
<input 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>
<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>
</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>