Teren-app/resources/js/Pages/Admin/DocumentTemplates/Index.vue
2026-01-05 18:27:35 +01:00

252 lines
8.6 KiB
Vue

<script setup>
import AdminLayout from "@/Layouts/AdminLayout.vue";
import { Link, useForm } from "@inertiajs/vue3";
import { computed, ref } from "vue";
import {
UploadIcon,
FileTextIcon,
Power,
PowerOffIcon,
PencilIcon,
CheckIcon,
XIcon,
} 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 {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/Components/ui/select";
import { Badge } from "@/Components/ui/badge";
import { Progress } from "@/Components/ui/progress";
const props = defineProps({
templates: { type: Array, default: () => [] },
});
// Upload form state
const uploadForm = useForm({ name: "", slug: "", file: null });
const selectedSlug = ref(null);
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="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]">
<div class="flex items-center gap-2 mb-2">
<h1 class="text-2xl font-semibold tracking-tight">Dokumentne predloge</h1>
<Badge variant="secondary">{{ groups.length }} skupin</Badge>
</div>
<p class="text-sm text-muted-foreground max-w-prose">
Upravljaj verzije DOCX predlog. Naloži novo verzijo obstoječega sluga ali
ustvari popolnoma novo predlogo.
</p>
</div>
<Card class="flex-1">
<CardHeader>
<div class="flex items-center justify-between">
<CardTitle class="text-base flex items-center gap-2">
<UploadIcon class="h-4 w-4" />
Nova / nova verzija
</CardTitle>
<Progress
v-if="uploadForm.progress"
:model-value="uploadForm.progress.percentage"
class="w-40 h-2"
/>
</div>
</CardHeader>
<CardContent>
<form @submit.prevent="submitUpload" class="space-y-4">
<div class="grid md:grid-cols-5 gap-4">
<div class="space-y-2">
<Label for="existing_slug">Obstoječi slug</Label>
<Select v-model="selectedSlug">
<SelectTrigger id="existing_slug">
<SelectValue placeholder="(nov)" />
</SelectTrigger>
<SelectContent>
<SelectItem v-for="s in uniqueSlugs" :key="s" :value="s">{{
s
}}</SelectItem>
</SelectContent>
</Select>
</div>
<div class="space-y-2">
<Label for="new_slug">Nov slug</Label>
<Input
id="new_slug"
v-model="uploadForm.slug"
:disabled="!!selectedSlug"
placeholder="opomin"
/>
</div>
<div class="space-y-2">
<Label for="template_name">Naziv</Label>
<Input
id="template_name"
v-model="uploadForm.name"
placeholder="Ime predloge"
/>
</div>
<div class="md:col-span-2 space-y-2">
<Label for="docx-upload-input">DOCX datoteka</Label>
<Input
id="docx-upload-input"
@change="handleFile"
type="file"
accept=".docx"
/>
</div>
</div>
<div class="flex items-center justify-end gap-3">
<span class="text-xs text-muted-foreground" v-if="!uploadForm.file">
Izberi DOCX datoteko…
</span>
<Button
type="submit"
size="sm"
:disabled="
uploadForm.processing ||
!uploadForm.file ||
(!uploadForm.slug && !selectedSlug)
"
>
<UploadIcon class="h-4 w-4 mr-2" />
{{ uploadForm.processing ? "Nalaganje…" : "Shrani verzijo" }}
</Button>
</div>
<p v-if="uploadForm.errors.file" class="text-sm text-destructive">
{{ uploadForm.errors.file }}
</p>
</form>
</CardContent>
</Card>
</div>
<!-- Groups -->
<div v-if="groups.length" class="grid gap-6 md:grid-cols-2 xl:grid-cols-3">
<Card v-for="g in groups" :key="g.slug">
<CardHeader>
<div class="flex items-start justify-between gap-3">
<div class="min-w-0 flex-1">
<CardTitle class="text-base truncate">{{ g.name }}</CardTitle>
<CardDescription class="flex flex-wrap items-center gap-2 mt-1">
<Badge variant="secondary" class="text-xs">{{ g.slug }}</Badge>
<span class="text-xs">Zadnja: v{{ g.versions[0].version }}</span>
<Badge
:variant="
g.versions.filter((v) => v.active).length ? 'default' : 'outline'
"
class="text-xs"
>
{{ g.versions.filter((v) => v.active).length }} aktivnih
</Badge>
</CardDescription>
</div>
<Button size="sm" variant="ghost" as-child>
<Link :href="route('admin.document-templates.show', g.versions[0].id)">
Detalji
</Link>
</Button>
</div>
</CardHeader>
<CardContent class="flex flex-col gap-4">
<div class="flex flex-wrap gap-2">
<div v-for="v in g.versions" :key="v.id" class="flex items-center gap-1">
<Button
size="sm"
:variant="v.active ? 'default' : 'outline'"
class="h-7 px-2 text-xs"
as-child
>
<Link :href="route('admin.document-templates.edit', v.id)">
v{{ v.version }}
</Link>
</Button>
<Button
type="button"
size="icon"
:variant="v.active ? 'destructive' : 'outline'"
class="h-7 w-7"
@click="toggle(v.id)"
>
<XIcon v-if="v.active" class="h-3 w-3" />
<CheckIcon v-else class="h-3 w-3" />
</Button>
</div>
</div>
<div class="pt-2 border-t flex justify-end">
<Button size="sm" variant="link" class="h-auto p-0" as-child>
<Link :href="route('admin.document-templates.edit', g.versions[0].id)">
Uredi zadnjo verziju →
</Link>
</Button>
</div>
</CardContent>
</Card>
</div>
<p v-else class="text-sm text-muted-foreground">Ni predlog.</p>
</div>
</AdminLayout>
</template>