646 lines
23 KiB
Vue
646 lines
23 KiB
Vue
<script setup>
|
||
import { Dialog, DialogContent } from "@/Components/ui/dialog";
|
||
import { Tabs, TabsList, TabsTrigger } from "@/Components/ui/tabs";
|
||
import { Badge } from "@/Components/ui/badge";
|
||
import { Button } from "@/Components/ui/button";
|
||
import { Checkbox } from "@/Components/ui/checkbox";
|
||
import { Label } from "@/Components/ui/label";
|
||
import {
|
||
Select,
|
||
SelectContent,
|
||
SelectItem,
|
||
SelectTrigger,
|
||
SelectValue,
|
||
} from "@/Components/ui/select";
|
||
import { useEurFormat } from "../useEurFormat.js";
|
||
import {
|
||
CheckCircleIcon,
|
||
ExclamationTriangleIcon,
|
||
XCircleIcon,
|
||
ChevronRightIcon,
|
||
} from "@heroicons/vue/24/outline";
|
||
import { computed, ref, watch } from "vue";
|
||
|
||
// Props
|
||
const props = defineProps({
|
||
show: { type: Boolean, default: false },
|
||
rows: { type: Array, default: () => [] },
|
||
limit: { type: Number, default: 50 },
|
||
loading: { type: Boolean, default: false },
|
||
entities: { type: Array, default: () => [] },
|
||
verbose: { type: Boolean, default: false },
|
||
});
|
||
|
||
// Emits
|
||
const emit = defineEmits(["close", "change-limit", "toggle-verbose"]);
|
||
|
||
// Entity labels
|
||
const entityLabelMap = {
|
||
account: "računi",
|
||
payment: "plačila",
|
||
contract: "pogodbe",
|
||
contracts: "pogodbe",
|
||
person: "osebe",
|
||
client_case: "primeri",
|
||
client_cases: "primeri",
|
||
address: "naslovi",
|
||
person_addresses: "naslovi",
|
||
email: "emaili",
|
||
emails: "emaili",
|
||
phone: "telefoni",
|
||
person_phones: "telefoni",
|
||
booking: "knjižbe",
|
||
activity: "aktivnosti",
|
||
activities: "aktivnosti",
|
||
};
|
||
|
||
// Formatting helpers
|
||
const { formatEur } = useEurFormat();
|
||
const fmt = (v) => formatEur(v);
|
||
|
||
// State
|
||
const activeEntity = ref(null);
|
||
const selectedRow = ref(null);
|
||
const showOnlyChanged = ref(false);
|
||
|
||
// Entities with data
|
||
const entitiesWithRows = computed(() => {
|
||
if (!props.rows?.length || !props.entities?.length) return [];
|
||
const present = new Set();
|
||
for (const r of props.rows) {
|
||
if (!r.entities) continue;
|
||
for (const k of Object.keys(r.entities)) {
|
||
if (props.entities.includes(k)) present.add(k);
|
||
}
|
||
}
|
||
return props.entities.filter((e) => present.has(e));
|
||
});
|
||
|
||
// Watch for entity changes
|
||
watch(
|
||
entitiesWithRows,
|
||
(val) => {
|
||
if (!val.length) {
|
||
activeEntity.value = null;
|
||
selectedRow.value = null;
|
||
return;
|
||
}
|
||
if (!activeEntity.value || !val.includes(activeEntity.value)) {
|
||
activeEntity.value = val[0];
|
||
selectedRow.value = null;
|
||
}
|
||
},
|
||
{ immediate: true }
|
||
);
|
||
|
||
// Entity statistics
|
||
const entityStats = computed(() => {
|
||
const stats = {};
|
||
for (const e of entitiesWithRows.value) {
|
||
stats[e] = {
|
||
total: 0,
|
||
create: 0,
|
||
update: 0,
|
||
invalid: 0,
|
||
errors: 0,
|
||
warnings: 0,
|
||
};
|
||
}
|
||
for (const r of props.rows || []) {
|
||
if (!r.entities) continue;
|
||
for (const [k, ent] of Object.entries(r.entities)) {
|
||
if (!stats[k]) continue;
|
||
|
||
// Handle both single entities and arrays
|
||
const entities = Array.isArray(ent) ? ent : [ent];
|
||
|
||
for (const e of entities) {
|
||
stats[k].total++;
|
||
if (e.action === "create") stats[k].create++;
|
||
if (e.action === "update") stats[k].update++;
|
||
if (e.action === "invalid") stats[k].invalid++;
|
||
}
|
||
|
||
if (r.errors?.length) stats[k].errors++;
|
||
if (r.warnings?.length) stats[k].warnings++;
|
||
}
|
||
}
|
||
return stats;
|
||
});
|
||
|
||
// Visible rows
|
||
const visibleRows = computed(() => {
|
||
if (!props.rows || !activeEntity.value) return [];
|
||
return props.rows
|
||
.filter((r) => {
|
||
if (!r.entities || !r.entities[activeEntity.value]) return false;
|
||
if (showOnlyChanged.value) {
|
||
const ent = r.entities[activeEntity.value];
|
||
const entities = Array.isArray(ent) ? ent : [ent];
|
||
return entities.some((e) => e.action === "create" || e.action === "update");
|
||
}
|
||
return true;
|
||
})
|
||
.map((r, idx) => ({ ...r, index: r.row_number || idx + 1 }))
|
||
.slice(0, props.limit || props.rows.length);
|
||
});
|
||
|
||
// Row status
|
||
function getRowStatus(row) {
|
||
if (!row.entities || !activeEntity.value) return "unknown";
|
||
const ent = row.entities[activeEntity.value];
|
||
const entities = Array.isArray(ent) ? ent : [ent];
|
||
|
||
if (row.errors?.length || entities.some((e) => e.action === "invalid")) return "error";
|
||
if (row.warnings?.length) return "warning";
|
||
return "success";
|
||
}
|
||
|
||
// Select row
|
||
function selectRow(row) {
|
||
selectedRow.value = row;
|
||
}
|
||
|
||
// Handlers
|
||
function onLimit(value) {
|
||
const val = Number(value ?? props.limit ?? 50);
|
||
emit("change-limit", isNaN(val) ? 50 : val);
|
||
}
|
||
|
||
function toggleVerbose() {
|
||
emit("toggle-verbose");
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<Dialog :open="show" @update:open="(val) => !val && emit('close')">
|
||
<DialogContent class="max-w-7xl max-h-[90vh] overflow-hidden flex flex-col p-0">
|
||
<!-- Header -->
|
||
<div class="px-6 py-4 border-b bg-linear-to-r from-gray-50 to-white">
|
||
<div class="flex items-center justify-between">
|
||
<div>
|
||
<h2 class="text-xl font-semibold text-gray-900">Simulacija uvoza</h2>
|
||
<p class="text-sm text-gray-500 mt-1">Preglejte podatke pred uvozom</p>
|
||
</div>
|
||
<div class="flex items-center gap-3">
|
||
<div class="flex items-center gap-2">
|
||
<Label class="text-xs text-gray-600">Prikaži:</Label>
|
||
<Select :model-value="String(limit)" @update:model-value="onLimit">
|
||
<SelectTrigger class="w-20 h-7 text-xs">
|
||
<SelectValue />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="25">25</SelectItem>
|
||
<SelectItem value="50">50</SelectItem>
|
||
<SelectItem value="100">100</SelectItem>
|
||
<SelectItem value="250">250</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</div>
|
||
<Button variant="outline" size="sm" @click="toggleVerbose" class="text-xs">
|
||
{{ props.verbose ? "Manj" : "Več" }} podrobnosti
|
||
</Button>
|
||
<div class="flex items-center gap-2">
|
||
<Checkbox
|
||
id="show-only-changed"
|
||
:checked="showOnlyChanged"
|
||
@update:checked="(val) => (showOnlyChanged = val)"
|
||
/>
|
||
<Label for="show-only-changed" class="text-xs cursor-pointer">
|
||
Samo spremenjeni
|
||
</Label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Entity Tabs -->
|
||
<Tabs
|
||
v-if="entitiesWithRows.length"
|
||
:model-value="activeEntity"
|
||
@update:model-value="
|
||
(val) => {
|
||
activeEntity = val;
|
||
selectedRow = null;
|
||
}
|
||
"
|
||
class="mt-4"
|
||
>
|
||
<TabsList class="w-full justify-start overflow-x-auto">
|
||
<TabsTrigger
|
||
v-for="e in entitiesWithRows"
|
||
:key="e"
|
||
:value="e"
|
||
class="flex items-center gap-2"
|
||
>
|
||
<span class="uppercase tracking-wide text-xs">{{
|
||
entityLabelMap[e] || e
|
||
}}</span>
|
||
<Badge variant="secondary" class="text-[10px]">
|
||
{{ entityStats[e]?.total || 0 }}
|
||
</Badge>
|
||
</TabsTrigger>
|
||
</TabsList>
|
||
</Tabs>
|
||
|
||
<!-- Stats Bar -->
|
||
<div
|
||
v-if="activeEntity && entityStats[activeEntity]"
|
||
class="flex items-center gap-4 mt-3 px-4 py-2 bg-white rounded-lg border text-xs"
|
||
>
|
||
<div class="flex items-center gap-1.5">
|
||
<CheckCircleIcon class="h-4 w-4 text-emerald-600" />
|
||
<span class="text-gray-600">Novo:</span>
|
||
<span class="font-semibold text-emerald-600">
|
||
{{ entityStats[activeEntity].create }}
|
||
</span>
|
||
</div>
|
||
<div class="flex items-center gap-1.5">
|
||
<CheckCircleIcon class="h-4 w-4 text-blue-600" />
|
||
<span class="text-gray-600">Posodobitev:</span>
|
||
<span class="font-semibold text-blue-600">
|
||
{{ entityStats[activeEntity].update }}
|
||
</span>
|
||
</div>
|
||
<div v-if="entityStats[activeEntity].invalid" class="flex items-center gap-1.5">
|
||
<XCircleIcon class="h-4 w-4 text-red-600" />
|
||
<span class="text-gray-600">Neveljavno:</span>
|
||
<span class="font-semibold text-red-600">
|
||
{{ entityStats[activeEntity].invalid }}
|
||
</span>
|
||
</div>
|
||
<div v-if="entityStats[activeEntity].errors" class="flex items-center gap-1.5">
|
||
<ExclamationTriangleIcon class="h-4 w-4 text-red-600" />
|
||
<span class="text-gray-600">Napake:</span>
|
||
<span class="font-semibold text-red-600">
|
||
{{ entityStats[activeEntity].errors }}
|
||
</span>
|
||
</div>
|
||
<div
|
||
v-if="entityStats[activeEntity].warnings"
|
||
class="flex items-center gap-1.5"
|
||
>
|
||
<ExclamationTriangleIcon class="h-4 w-4 text-amber-600" />
|
||
<span class="text-gray-600">Opozorila:</span>
|
||
<span class="font-semibold text-amber-600">
|
||
{{ entityStats[activeEntity].warnings }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Split View -->
|
||
<div class="flex-1 flex overflow-hidden">
|
||
<!-- Left Panel - Row List -->
|
||
<div class="w-96 border-r bg-gray-50 overflow-y-auto">
|
||
<div v-if="loading" class="p-8 text-center text-gray-500">
|
||
<div
|
||
class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-2"
|
||
></div>
|
||
Nalagam...
|
||
</div>
|
||
<div v-else-if="!visibleRows.length" class="p-8 text-center text-gray-500">
|
||
Ni vrstic za prikaz
|
||
</div>
|
||
<div v-else class="divide-y">
|
||
<button
|
||
v-for="row in visibleRows"
|
||
:key="row.index"
|
||
@click="selectRow(row)"
|
||
class="w-full px-4 py-3 text-left hover:bg-white transition-colors"
|
||
:class="{
|
||
'bg-white shadow-sm': selectedRow?.index === row.index,
|
||
'bg-red-50': getRowStatus(row) === 'error',
|
||
'bg-amber-50': getRowStatus(row) === 'warning',
|
||
}"
|
||
>
|
||
<div class="flex items-center justify-between gap-3">
|
||
<div class="flex items-center gap-3 flex-1 min-w-0">
|
||
<!-- Status Icon -->
|
||
<div class="flex-shrink-0">
|
||
<CheckCircleIcon
|
||
v-if="getRowStatus(row) === 'success'"
|
||
class="h-5 w-5 text-emerald-600"
|
||
/>
|
||
<ExclamationTriangleIcon
|
||
v-else-if="getRowStatus(row) === 'warning'"
|
||
class="h-5 w-5 text-amber-600"
|
||
/>
|
||
<XCircleIcon v-else class="h-5 w-5 text-red-600" />
|
||
</div>
|
||
|
||
<!-- Row Info -->
|
||
<div class="flex-1 min-w-0">
|
||
<div class="flex items-center gap-2 mb-1">
|
||
<span class="text-xs font-semibold text-gray-900">
|
||
Vrstica #{{ row.index }}
|
||
</span>
|
||
<template v-if="row.entities && row.entities[activeEntity]">
|
||
<template v-if="Array.isArray(row.entities[activeEntity])">
|
||
<Badge variant="outline" class="text-[10px]">
|
||
{{ row.entities[activeEntity].length }}x
|
||
</Badge>
|
||
</template>
|
||
<template v-else>
|
||
<Badge
|
||
:variant="
|
||
row.entities[activeEntity].action === 'create'
|
||
? 'default'
|
||
: row.entities[activeEntity].action === 'update'
|
||
? 'secondary'
|
||
: 'destructive'
|
||
"
|
||
class="text-[10px]"
|
||
>
|
||
{{ row.entities[activeEntity].action }}
|
||
</Badge>
|
||
</template>
|
||
</template>
|
||
</div>
|
||
<div class="text-xs text-gray-600 truncate">
|
||
<template v-if="row.entities && row.entities[activeEntity]">
|
||
<template v-if="Array.isArray(row.entities[activeEntity])">
|
||
{{
|
||
row.entities[activeEntity]
|
||
.map((e) =>
|
||
Object.values(e.data || {})
|
||
.filter(Boolean)
|
||
.slice(0, 1)
|
||
.join(", ")
|
||
)
|
||
.filter(Boolean)
|
||
.join(" • ") || "Brez podatkov"
|
||
}}
|
||
</template>
|
||
<template v-else-if="row.entities[activeEntity].data">
|
||
{{
|
||
Object.values(row.entities[activeEntity].data)
|
||
.filter(Boolean)
|
||
.slice(0, 2)
|
||
.join(", ") || "Brez podatkov"
|
||
}}
|
||
</template>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Arrow -->
|
||
<ChevronRightIcon class="h-4 w-4 text-gray-400 flex-shrink-0" />
|
||
</div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Right Panel - Row Details -->
|
||
<div v-if="selectedRow" class="flex-1 overflow-y-auto p-6">
|
||
<!-- Row Header -->
|
||
<div class="mb-6">
|
||
<div class="flex items-center justify-between mb-2">
|
||
<h3 class="text-lg font-semibold text-gray-900">
|
||
Vrstica #{{ selectedRow.index }}
|
||
</h3>
|
||
<Badge
|
||
v-if="selectedRow.entities && selectedRow.entities[activeEntity]"
|
||
:variant="
|
||
selectedRow.entities[activeEntity].action === 'create'
|
||
? 'default'
|
||
: selectedRow.entities[activeEntity].action === 'update'
|
||
? 'secondary'
|
||
: 'destructive'
|
||
"
|
||
class="text-xs"
|
||
>
|
||
{{ selectedRow.entities[activeEntity].action }}
|
||
</Badge>
|
||
</div>
|
||
<p class="text-sm text-gray-500">
|
||
{{ entityLabelMap[activeEntity] || activeEntity }}
|
||
</p>
|
||
</div>
|
||
|
||
<!-- Errors -->
|
||
<div
|
||
v-if="selectedRow.errors && selectedRow.errors.length"
|
||
class="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg"
|
||
>
|
||
<div class="flex items-start gap-2">
|
||
<XCircleIcon class="h-5 w-5 text-red-600 flex-shrink-0 mt-0.5" />
|
||
<div class="flex-1">
|
||
<h4 class="font-semibold text-red-900 mb-2">Napake</h4>
|
||
<ul class="space-y-1">
|
||
<li
|
||
v-for="(error, idx) in selectedRow.errors"
|
||
:key="idx"
|
||
class="text-sm text-red-700"
|
||
>
|
||
• {{ error }}
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Warnings -->
|
||
<div
|
||
v-if="selectedRow.warnings && selectedRow.warnings.length"
|
||
class="mb-6 p-4 bg-amber-50 border border-amber-200 rounded-lg"
|
||
>
|
||
<div class="flex items-start gap-2">
|
||
<ExclamationTriangleIcon
|
||
class="h-5 w-5 text-amber-600 flex-shrink-0 mt-0.5"
|
||
/>
|
||
<div class="flex-1">
|
||
<h4 class="font-semibold text-amber-900 mb-2">Opozorila</h4>
|
||
<ul class="space-y-1">
|
||
<li
|
||
v-for="(warn, idx) in selectedRow.warnings"
|
||
:key="idx"
|
||
class="text-sm text-amber-700"
|
||
>
|
||
• {{ warn }}
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Entity Data -->
|
||
<div
|
||
v-if="selectedRow.entities && selectedRow.entities[activeEntity]"
|
||
class="space-y-6"
|
||
>
|
||
<!-- Handle Array of Entities (Groups) -->
|
||
<template v-if="Array.isArray(selectedRow.entities[activeEntity])">
|
||
<div
|
||
v-for="(entity, idx) in selectedRow.entities[activeEntity]"
|
||
:key="idx"
|
||
class="border rounded-lg p-4 bg-white"
|
||
>
|
||
<div class="flex items-center justify-between mb-3">
|
||
<h4 class="font-semibold text-gray-900">Instanca #{{ idx + 1 }}</h4>
|
||
<Badge
|
||
:variant="
|
||
entity.action === 'create'
|
||
? 'default'
|
||
: entity.action === 'update'
|
||
? 'secondary'
|
||
: 'destructive'
|
||
"
|
||
class="text-xs"
|
||
>
|
||
{{ entity.action }}
|
||
</Badge>
|
||
</div>
|
||
|
||
<!-- Entity Data -->
|
||
<div v-if="entity.data" class="bg-gray-50 rounded-lg p-3">
|
||
<dl class="grid grid-cols-1 gap-2">
|
||
<div
|
||
v-for="(value, field) in entity.data"
|
||
:key="field"
|
||
class="flex items-start gap-3"
|
||
>
|
||
<dt class="text-sm font-medium text-gray-600 w-32 flex-shrink-0">
|
||
{{ field }}
|
||
</dt>
|
||
<dd class="text-sm text-gray-900 font-medium flex-1">
|
||
{{ value || "—" }}
|
||
</dd>
|
||
</div>
|
||
</dl>
|
||
</div>
|
||
|
||
<!-- Entity Errors -->
|
||
<div
|
||
v-if="entity.errors && entity.errors.length"
|
||
class="mt-3 bg-red-50 rounded-lg p-3"
|
||
>
|
||
<h5 class="font-semibold text-red-900 text-sm mb-2">Napake</h5>
|
||
<ul class="space-y-1">
|
||
<li
|
||
v-for="(error, eidx) in entity.errors"
|
||
:key="eidx"
|
||
class="text-xs text-red-700"
|
||
>
|
||
• {{ error }}
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<!-- Reason for Skip -->
|
||
<div v-if="entity.reason" class="mt-3 text-sm text-gray-600">
|
||
<strong>Razlog:</strong> {{ entity.reason }}
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- Handle Single Entity -->
|
||
<template v-else>
|
||
<!-- Main Data -->
|
||
<div
|
||
v-if="selectedRow.entities[activeEntity].data"
|
||
class="bg-gray-50 rounded-lg p-4"
|
||
>
|
||
<h4 class="font-semibold text-gray-900 mb-3">Podatki</h4>
|
||
<dl class="grid grid-cols-1 gap-3">
|
||
<div
|
||
v-for="(value, field) in selectedRow.entities[activeEntity].data"
|
||
:key="field"
|
||
class="flex items-start gap-3"
|
||
>
|
||
<dt class="text-sm font-medium text-gray-600 w-40 flex-shrink-0">
|
||
{{ field }}
|
||
</dt>
|
||
<dd class="text-sm text-gray-900 font-medium flex-1">
|
||
{{ value || "—" }}
|
||
</dd>
|
||
</div>
|
||
</dl>
|
||
</div>
|
||
|
||
<!-- Reference Info (for updates) -->
|
||
<div
|
||
v-if="
|
||
selectedRow.entities[activeEntity].action === 'update' &&
|
||
(selectedRow.entities[activeEntity].reference ||
|
||
selectedRow.entities[activeEntity].existing_id)
|
||
"
|
||
class="bg-blue-50 rounded-lg p-4"
|
||
>
|
||
<h4 class="font-semibold text-blue-900 mb-3">
|
||
Informacije o posodobitvi
|
||
</h4>
|
||
<dl class="space-y-2">
|
||
<div v-if="selectedRow.entities[activeEntity].reference">
|
||
<dt class="text-sm font-medium text-blue-700">Referenca</dt>
|
||
<dd class="text-sm text-blue-900 font-medium mt-1">
|
||
{{ selectedRow.entities[activeEntity].reference }}
|
||
</dd>
|
||
</div>
|
||
<div v-if="selectedRow.entities[activeEntity].existing_id">
|
||
<dt class="text-sm font-medium text-blue-700">Obstoječi ID</dt>
|
||
<dd class="text-sm text-blue-900 font-medium mt-1">
|
||
{{ selectedRow.entities[activeEntity].existing_id }}
|
||
</dd>
|
||
</div>
|
||
</dl>
|
||
</div>
|
||
|
||
<!-- Changes (verbose mode) -->
|
||
<div
|
||
v-if="
|
||
props.verbose &&
|
||
selectedRow.entities[activeEntity].action === 'update' &&
|
||
selectedRow.entities[activeEntity].changes
|
||
"
|
||
class="bg-purple-50 rounded-lg p-4"
|
||
>
|
||
<h4 class="font-semibold text-purple-900 mb-3">Spremembe</h4>
|
||
<div class="space-y-2">
|
||
<div
|
||
v-for="(change, field) in selectedRow.entities[activeEntity].changes"
|
||
:key="field"
|
||
class="text-sm"
|
||
>
|
||
<div class="font-medium text-purple-700 mb-1">{{ field }}</div>
|
||
<div class="flex items-center gap-2 pl-3">
|
||
<span class="text-red-600 line-through">{{ change.old }}</span>
|
||
<span class="text-gray-400">→</span>
|
||
<span class="text-green-600 font-medium">{{ change.new }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Entity Errors -->
|
||
<div
|
||
v-if="
|
||
selectedRow.entities[activeEntity].errors &&
|
||
selectedRow.entities[activeEntity].errors.length
|
||
"
|
||
class="bg-red-50 rounded-lg p-4"
|
||
>
|
||
<h4 class="font-semibold text-red-900 mb-3">Napake entitete</h4>
|
||
<ul class="space-y-1">
|
||
<li
|
||
v-for="(error, idx) in selectedRow.entities[activeEntity].errors"
|
||
:key="idx"
|
||
class="text-sm text-red-700"
|
||
>
|
||
• {{ error }}
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Footer -->
|
||
<div class="px-6 py-3 border-t bg-gray-50 text-xs text-gray-500">
|
||
Samo simulacija – podatki niso bili spremenjeni. Kliknite vrstico za podrobnosti.
|
||
</div>
|
||
</DialogContent>
|
||
</Dialog>
|
||
</template>
|