Teren-app/resources/js/Pages/Imports/Templates/Index.vue
2025-12-28 14:29:07 +01:00

480 lines
15 KiB
Vue

<script setup>
import AppLayout from "@/Layouts/AppLayout.vue";
import { Link, useForm, router } from "@inertiajs/vue3";
import { ref, computed } from "vue";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/Components/ui/card";
import { Button } from "@/Components/ui/button";
import { Badge } from "@/Components/ui/badge";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/Components/ui/alert-dialog";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/Components/ui/dialog";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/Components/ui/select";
import InputLabel from "@/Components/InputLabel.vue";
import { Separator } from "@/Components/ui/separator";
import AppCard from "@/Components/app/ui/card/AppCard.vue";
import { ListIndentIncreaseIcon, DownloadIcon, UploadIcon } from "lucide-vue-next";
import TableActions from "@/Components/DataTable/TableActions.vue";
import ActionMenuItem from "@/Components/DataTable/ActionMenuItem.vue";
import {
faPencil,
faTrash,
faFileExport,
faFileImport,
} from "@fortawesome/free-solid-svg-icons";
// Non-blocking confirm modal state
const confirmOpen = ref(false);
const confirmUuid = ref(null);
const deleteForm = useForm({});
// Import modal state
const importModalOpen = ref(false);
const importForm = useForm({
file: null,
client_uuid: null,
segment_id: null,
decision_id: null,
action_id: null,
activity_action_id: null,
activity_decision_id: null,
});
const fileInputRef = ref(null);
const importedData = ref(null);
function requestDelete(uuid) {
confirmUuid.value = uuid;
confirmOpen.value = true;
}
function performDelete() {
if (!confirmUuid.value) return;
deleteForm.delete(route("importTemplates.destroy", { template: confirmUuid.value }), {
preserveScroll: true,
onFinish: () => {
confirmOpen.value = false;
confirmUuid.value = null;
},
});
}
function cancelDelete() {
confirmOpen.value = false;
confirmUuid.value = null;
}
function exportTemplate(uuid) {
window.location.href = route("importTemplates.export", { template: uuid });
}
function openImportModal() {
importModalOpen.value = true;
}
function cancelImport() {
importModalOpen.value = false;
importForm.reset();
importedData.value = null;
}
function handleFileChange(event) {
const file = event.target.files[0];
importForm.file = file;
// Parse JSON to show existing IDs
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
try {
const json = JSON.parse(e.target.result);
importedData.value = json;
} catch (error) {
console.error("Failed to parse JSON:", error);
importedData.value = null;
}
};
reader.readAsText(file);
}
}
function performImport() {
importForm.post(route("importTemplates.import"), {
preserveScroll: true,
onSuccess: () => {
importModalOpen.value = false;
importForm.reset();
importedData.value = null;
},
});
}
const props = defineProps({
templates: Array,
clients: Array,
segments: Array,
decisions: Array,
actions: Array,
});
</script>
<template>
<AppLayout title="Uvozne predloge">
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">Uvozne predloge</h2>
</template>
<div class="py-6">
<div class="max-w-5xl mx-auto sm:px-6 lg:px-8">
<AppCard
title=""
padding="none"
class="p-0! gap-0"
header-class="py-3! px-4 gap-0 text-muted-foreground"
body-class=""
>
<template #header>
<div class="flex items-center gap-2">
<ListIndentIncreaseIcon size="18" />
<CardTitle class="uppercase">Predloge uvoza</CardTitle>
</div>
</template>
<div class="flex items-center justify-between border-t border-b py-2 px-4">
<div>
<CardDescription>
Skupaj {{ props.templates?.length || 0 }} predlog{{
props.templates?.length === 1 ? "a" : ""
}}
</CardDescription>
</div>
<div class="flex gap-2">
<Button variant="outline" @click="openImportModal">
<UploadIcon class="w-4 h-4 mr-2" />
Uvozi predlogo
</Button>
<Button as-child>
<Link :href="route('importTemplates.create')"> Nova predloga </Link>
</Button>
</div>
</div>
<div
v-if="!props.templates || props.templates.length === 0"
class="p-8 text-center text-muted-foreground"
>
Ni predlog uvoza.
</div>
<div v-else class="grid gap-4 p-4">
<Card
v-for="t in props.templates"
:key="t.uuid"
class="hover:shadow-md transition-shadow px-0! p-4"
>
<CardHeader>
<div class="flex items-start justify-between">
<div class="flex-1">
<CardTitle class="text-base">{{ t.name }}</CardTitle>
<CardDescription class="mt-1">
{{ t.description }}
</CardDescription>
</div>
<TableActions align="right">
<template #default>
<ActionMenuItem
:icon="faPencil"
label="Uredi"
@click="
$inertia.visit(
route('importTemplates.edit', { template: t.uuid })
)
"
/>
<ActionMenuItem
:icon="faFileExport"
label="Izvozi"
@click="exportTemplate(t.uuid)"
/>
<ActionMenuItem
:icon="faTrash"
label="Izbriši"
danger
@click="requestDelete(t.uuid)"
/>
</template>
</TableActions>
</div>
</CardHeader>
<CardContent class="pt-0">
<div class="flex items-center gap-2">
<Badge variant="outline" class="text-xs">
{{ t.client?.name || "Globalno" }}
</Badge>
<Badge variant="secondary" class="text-xs">
{{ t.source_type.toUpperCase() }}
</Badge>
<Badge :variant="t.is_active ? 'default' : 'secondary'" class="text-xs">
{{ t.is_active ? "Aktivno" : "Neaktivno" }}
</Badge>
</div>
</CardContent>
</Card>
</div>
</AppCard>
</div>
</div>
<!-- Confirm Delete Dialog -->
<AlertDialog
:open="confirmOpen"
@update:open="
(val) => {
if (!val) cancelDelete();
}
"
>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Izbrišem predlogo?</AlertDialogTitle>
<AlertDialogDescription>
Tega dejanja ni mogoče razveljaviti. Vse preslikave te predloge bodo
izbrisane.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel @click="cancelDelete" :disabled="deleteForm.processing">
Prekliči
</AlertDialogCancel>
<AlertDialogAction
@click="performDelete"
:disabled="deleteForm.processing"
class="bg-destructive hover:bg-destructive/90"
>
<span v-if="deleteForm.processing">Brisanje…</span>
<span v-else>Izbriši</span>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<!-- Import Template Dialog -->
<Dialog v-model:open="importModalOpen">
<DialogContent class="max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>Uvozi predlogo uvoza</DialogTitle>
<DialogDescription>
Izberite JSON datoteko za uvoz predloge uvoza. Po potrebi zamenjajte ID-je z
lokalnimi vrednostmi.
</DialogDescription>
</DialogHeader>
<div class="space-y-4 py-4">
<div>
<InputLabel>Datoteka</InputLabel>
<input
ref="fileInputRef"
type="file"
accept=".json,application/json"
@change="handleFileChange"
class="w-full mt-1"
/>
<p v-if="importForm.errors.file" class="text-sm text-destructive mt-2">
{{ importForm.errors.file }}
</p>
</div>
<Separator v-if="importedData?.meta" />
<!-- Show ID mapping options if file is loaded and has meta -->
<div v-if="importedData?.meta" class="space-y-4">
<p class="text-sm text-muted-foreground">
Predloga vsebuje naslednje ID-je. Izberite nove vrednosti, če želite
zamenjati originalne ID-je:
</p>
<!-- Client -->
<div v-if="importedData.meta.client_id || clients.length > 0">
<InputLabel for="client_uuid">Stranka</InputLabel>
<Select v-model="importForm.client_uuid">
<SelectTrigger id="client_uuid">
<SelectValue placeholder="Izberi stranko (opcijsko)" />
</SelectTrigger>
<SelectContent>
<SelectItem
v-for="client in clients"
:key="client.uuid"
:value="client.uuid"
>
{{ client.name }}
</SelectItem>
</SelectContent>
</Select>
</div>
<!-- Segment -->
<div v-if="importedData.meta.segment_id">
<InputLabel for="segment_id">
Segment
<span class="text-muted-foreground"
>(trenutno: {{ importedData.meta.segment_id }})</span
>
</InputLabel>
<Select v-model="importForm.segment_id">
<SelectTrigger id="segment_id">
<SelectValue placeholder="Obdrži original ali izberi nov" />
</SelectTrigger>
<SelectContent>
<SelectItem
v-for="segment in segments"
:key="segment.id"
:value="segment.id.toString()"
>
{{ segment.name }}
</SelectItem>
</SelectContent>
</Select>
</div>
<!-- Decision -->
<div v-if="importedData.meta.decision_id">
<InputLabel for="decision_id">
Odločitev
<span class="text-muted-foreground"
>(trenutno: {{ importedData.meta.decision_id }})</span
>
</InputLabel>
<Select v-model="importForm.decision_id">
<SelectTrigger id="decision_id">
<SelectValue placeholder="Obdrži original ali izberi nov" />
</SelectTrigger>
<SelectContent>
<SelectItem
v-for="decision in decisions"
:key="decision.id"
:value="decision.id.toString()"
>
{{ decision.name }}
</SelectItem>
</SelectContent>
</Select>
</div>
<!-- Action -->
<div v-if="importedData.meta.action_id">
<InputLabel for="action_id">
Akcija
<span class="text-muted-foreground"
>(trenutno: {{ importedData.meta.action_id }})</span
>
</InputLabel>
<Select v-model="importForm.action_id">
<SelectTrigger id="action_id">
<SelectValue placeholder="Obdrži original ali izberi nov" />
</SelectTrigger>
<SelectContent>
<SelectItem
v-for="action in actions"
:key="action.id"
:value="action.id.toString()"
>
{{ action.name }}
</SelectItem>
</SelectContent>
</Select>
</div>
<!-- Activity Action -->
<div v-if="importedData.meta.activity_action_id">
<InputLabel for="activity_action_id">
Aktivnost - Akcija
<span class="text-muted-foreground"
>(trenutno: {{ importedData.meta.activity_action_id }})</span
>
</InputLabel>
<Select v-model="importForm.activity_action_id">
<SelectTrigger id="activity_action_id">
<SelectValue placeholder="Obdrži original ali izberi nov" />
</SelectTrigger>
<SelectContent>
<SelectItem
v-for="action in actions"
:key="action.id"
:value="action.id.toString()"
>
{{ action.name }}
</SelectItem>
</SelectContent>
</Select>
</div>
<!-- Activity Decision -->
<div v-if="importedData.meta.activity_decision_id">
<InputLabel for="activity_decision_id">
Aktivnost - Odločitev
<span class="text-muted-foreground"
>(trenutno: {{ importedData.meta.activity_decision_id }})</span
>
</InputLabel>
<Select v-model="importForm.activity_decision_id">
<SelectTrigger id="activity_decision_id">
<SelectValue placeholder="Obdrži original ali izberi nov" />
</SelectTrigger>
<SelectContent>
<SelectItem
v-for="decision in decisions"
:key="decision.id"
:value="decision.id.toString()"
>
{{ decision.name }}
</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
<DialogFooter>
<Button
variant="outline"
@click="cancelImport"
:disabled="importForm.processing"
>
Prekliči
</Button>
<Button
@click="performImport"
:disabled="importForm.processing || !importForm.file"
>
<span v-if="importForm.processing">Uvažam</span>
<span v-else>Uvozi</span>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</AppLayout>
</template>