246 lines
8.9 KiB
Vue
246 lines
8.9 KiB
Vue
<script setup>
|
|
import AdminLayout from "@/Layouts/AdminLayout.vue";
|
|
import { Link, useForm } from "@inertiajs/vue3";
|
|
import { computed, ref } from "vue";
|
|
|
|
const props = defineProps({
|
|
templates: { type: Array, default: () => [] },
|
|
});
|
|
|
|
// Upload form state
|
|
const uploadForm = useForm({ name: "", slug: "", file: null });
|
|
const selectedSlug = ref("");
|
|
const uniqueSlugs = computed(() => {
|
|
const s = new Set(props.templates.map((t) => t.slug));
|
|
return Array.from(s).sort();
|
|
});
|
|
function handleFile(e) {
|
|
uploadForm.file = e.target.files[0];
|
|
}
|
|
function submitUpload() {
|
|
if (!uploadForm.file) {
|
|
return;
|
|
}
|
|
if (!uploadForm.slug && selectedSlug.value) {
|
|
uploadForm.slug = selectedSlug.value;
|
|
}
|
|
uploadForm.post(route("admin.document-templates.store"), {
|
|
forceFormData: true,
|
|
onSuccess: () => {
|
|
uploadForm.reset("file");
|
|
const input = document.getElementById("docx-upload-input");
|
|
if (input) input.value = "";
|
|
},
|
|
});
|
|
}
|
|
function toggle(templateId) {
|
|
const f = useForm({});
|
|
f.post(route("admin.document-templates.toggle", templateId), { preserveScroll: true });
|
|
}
|
|
|
|
// Group templates by slug and sort versions DESC
|
|
const groups = computed(() => {
|
|
const map = {};
|
|
for (const t of props.templates) {
|
|
if (!map[t.slug]) {
|
|
map[t.slug] = { slug: t.slug, name: t.name, versions: [] };
|
|
}
|
|
map[t.slug].versions.push(t);
|
|
}
|
|
Object.values(map).forEach((g) => {
|
|
g.versions.sort((a, b) => b.version - a.version);
|
|
// ensure display name from latest version
|
|
if (g.versions[0]) {
|
|
g.name = g.versions[0].name;
|
|
}
|
|
});
|
|
return Object.values(map).sort((a, b) => a.slug.localeCompare(b.slug));
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<AdminLayout title="Dokumentne predloge">
|
|
<div class="mb-8 space-y-6">
|
|
<!-- Header & Upload -->
|
|
<div class="flex flex-col xl:flex-row xl:items-start gap-6">
|
|
<div class="flex-1 min-w-[280px]">
|
|
<h1 class="text-2xl font-semibold tracking-tight flex items-center gap-2">
|
|
<span>Dokumentne predloge</span>
|
|
<span
|
|
class="text-xs font-medium bg-gray-200 text-gray-600 px-2 py-0.5 rounded"
|
|
>{{ groups.length }} skupin</span
|
|
>
|
|
</h1>
|
|
<p class="text-sm text-gray-500 mt-1 max-w-prose">
|
|
Upravljaj verzije DOCX predlog. Naloži novo verzijo obstoječega sluga ali
|
|
ustvari popolnoma novo predlogo.
|
|
</p>
|
|
</div>
|
|
<form
|
|
@submit.prevent="submitUpload"
|
|
class="flex-1 bg-white/70 backdrop-blur border rounded-lg shadow-sm p-4 flex flex-col gap-3"
|
|
>
|
|
<div class="flex items-center justify-between">
|
|
<h2 class="text-sm font-semibold text-gray-700 flex items-center gap-2">
|
|
<span class="i-lucide-upload-cloud w-4 h-4" /> Nova / nova verzija
|
|
</h2>
|
|
<div
|
|
v-if="uploadForm.progress"
|
|
class="w-40 h-1 bg-gray-200 rounded overflow-hidden"
|
|
>
|
|
<div
|
|
class="h-full bg-indigo-500 transition-all"
|
|
:style="{ width: uploadForm.progress.percentage + '%' }"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="grid md:grid-cols-5 gap-3 text-xs">
|
|
<div class="md:col-span-1">
|
|
<label class="block font-medium mb-1">Obstoječi slug</label>
|
|
<select
|
|
v-model="selectedSlug"
|
|
class="select select-bordered select-sm w-full"
|
|
>
|
|
<option value="">(nov)</option>
|
|
<option v-for="s in uniqueSlugs" :key="s" :value="s">{{ s }}</option>
|
|
</select>
|
|
</div>
|
|
<div class="md:col-span-1">
|
|
<label class="block font-medium mb-1">Nov slug</label>
|
|
<input
|
|
v-model="uploadForm.slug"
|
|
:disabled="selectedSlug"
|
|
type="text"
|
|
class="input input-bordered input-sm w-full"
|
|
placeholder="opomin"
|
|
/>
|
|
</div>
|
|
<div class="md:col-span-1">
|
|
<label class="block font-medium mb-1">Naziv</label>
|
|
<input
|
|
v-model="uploadForm.name"
|
|
type="text"
|
|
class="input input-bordered input-sm w-full"
|
|
placeholder="Ime predloge"
|
|
/>
|
|
</div>
|
|
<div class="md:col-span-2 flex items-end">
|
|
<label class="w-full">
|
|
<input
|
|
id="docx-upload-input"
|
|
@change="handleFile"
|
|
type="file"
|
|
accept=".docx"
|
|
class="file-input file-input-bordered file-input-sm w-full"
|
|
/>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center justify-end gap-3 pt-1">
|
|
<span class="text-[11px] text-gray-500" v-if="!uploadForm.file"
|
|
>Izberi DOCX datoteko…</span
|
|
>
|
|
<button
|
|
type="submit"
|
|
class="btn btn-sm btn-primary"
|
|
:disabled="
|
|
uploadForm.processing ||
|
|
!uploadForm.file ||
|
|
(!uploadForm.slug && !selectedSlug)
|
|
"
|
|
>
|
|
<span v-if="uploadForm.processing">Nalaganje…</span>
|
|
<span v-else>Shrani verzijo</span>
|
|
</button>
|
|
</div>
|
|
<div v-if="uploadForm.errors.file" class="text-rose-600 text-xs">
|
|
{{ uploadForm.errors.file }}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Groups -->
|
|
<div v-if="groups.length" class="grid gap-6 md:grid-cols-2 xl:grid-cols-3">
|
|
<div
|
|
v-for="g in groups"
|
|
:key="g.slug"
|
|
class="group relative flex flex-col bg-white border rounded-lg shadow-sm overflow-hidden"
|
|
>
|
|
<div
|
|
class="px-4 py-3 border-b bg-gradient-to-r from-gray-50 to-white flex items-start justify-between gap-3"
|
|
>
|
|
<div class="min-w-0">
|
|
<h3 class="font-medium text-sm leading-5 truncate">{{ g.name }}</h3>
|
|
<div
|
|
class="flex flex-wrap items-center gap-2 mt-1 text-[11px] text-gray-500"
|
|
>
|
|
<span class="px-1.5 py-0.5 bg-gray-100 rounded">{{ g.slug }}</span>
|
|
<span>Zadnja: v{{ g.versions[0].version }}</span>
|
|
<span
|
|
class="flex items-center gap-1"
|
|
:class="
|
|
g.versions.filter((v) => v.active).length
|
|
? 'text-emerald-600'
|
|
: 'text-gray-400'
|
|
"
|
|
>
|
|
<span
|
|
class="w-1.5 h-1.5 rounded-full"
|
|
:class="
|
|
g.versions.filter((v) => v.active).length
|
|
? 'bg-emerald-500'
|
|
: 'bg-gray-300'
|
|
"
|
|
/>
|
|
{{ g.versions.filter((v) => v.active).length }} aktivnih
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<Link
|
|
:href="route('admin.document-templates.show', g.versions[0].id)"
|
|
class="text-xs text-indigo-600 hover:underline whitespace-nowrap mt-1"
|
|
>Detalji</Link
|
|
>
|
|
</div>
|
|
<div class="p-3 flex-1 flex flex-col gap-2">
|
|
<div class="flex flex-wrap gap-2">
|
|
<div v-for="v in g.versions" :key="v.id" class="flex items-center gap-1">
|
|
<Link
|
|
:href="route('admin.document-templates.edit', v.id)"
|
|
class="px-2 py-0.5 rounded-md border text-[11px] font-medium transition-colors"
|
|
:class="
|
|
v.active
|
|
? 'border-emerald-500/60 bg-emerald-50 text-emerald-700 hover:bg-emerald-100'
|
|
: 'border-gray-300 bg-white text-gray-600 hover:bg-gray-50'
|
|
"
|
|
>v{{ v.version }}</Link
|
|
>
|
|
<button
|
|
type="button"
|
|
@click="toggle(v.id)"
|
|
class="rounded-md border px-1.5 py-0.5 text-[10px] font-medium transition-colors"
|
|
:class="
|
|
v.active
|
|
? 'bg-amber-500 border-amber-500 text-white hover:bg-amber-600'
|
|
: 'bg-gray-100 border-gray-300 text-gray-600 hover:bg-gray-200'
|
|
"
|
|
>
|
|
{{ v.active ? "✕" : "✓" }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="mt-auto pt-2 border-t flex justify-end">
|
|
<Link
|
|
:href="route('admin.document-templates.edit', g.versions[0].id)"
|
|
class="text-[11px] text-indigo-600 hover:underline"
|
|
>Uredi zadnjo verzijo →</Link
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<p v-else class="text-sm text-gray-500">Ni predlog.</p>
|
|
</div>
|
|
</AdminLayout>
|
|
</template>
|