816 lines
33 KiB
Vue
816 lines
33 KiB
Vue
<script setup>
|
||
import Modal from "@/Components/Modal.vue";
|
||
import { useEurFormat } from "../useEurFormat.js";
|
||
import { ArrowRightIcon, ArrowDownIcon, ArrowUpIcon } from "@heroicons/vue/24/solid";
|
||
import { computed, ref, watch } from "vue";
|
||
|
||
// Props expected by the template
|
||
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: () => [] },
|
||
});
|
||
|
||
// Emits
|
||
const emit = defineEmits(["close", "update:limit"]);
|
||
|
||
// Map technical entity keys to localized labels
|
||
const entityLabelMap = {
|
||
account: "računi",
|
||
payment: "plačila",
|
||
contract: "pogodbe",
|
||
person: "osebe",
|
||
client_case: "primeri",
|
||
address: "naslovi",
|
||
email: "emaili",
|
||
phone: "telefoni",
|
||
booking: "knjižbe",
|
||
activity: "aktivnosti",
|
||
};
|
||
|
||
// Formatting helpers
|
||
const { formatEur } = useEurFormat();
|
||
const fmt = (v) => formatEur(v);
|
||
function formatDate(val) {
|
||
if (!val) return "—";
|
||
try {
|
||
const d = val instanceof Date ? val : new Date(val);
|
||
if (isNaN(d.getTime())) return String(val);
|
||
return d.toLocaleDateString("sl-SI", {
|
||
year: "numeric",
|
||
month: "2-digit",
|
||
day: "2-digit",
|
||
});
|
||
} catch (_) {
|
||
return String(val);
|
||
}
|
||
}
|
||
|
||
// Localized list for header
|
||
const localizedEntities = computed(() =>
|
||
Array.isArray(props.entities) && props.entities.length
|
||
? props.entities.map((e) => entityLabelMap[e] ?? e).join(", ")
|
||
: ""
|
||
);
|
||
|
||
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));
|
||
});
|
||
|
||
const activeEntity = ref(null);
|
||
const hideChain = ref(false);
|
||
const showOnlyChanged = ref(false);
|
||
watch(
|
||
entitiesWithRows,
|
||
(val) => {
|
||
if (!val.length) {
|
||
activeEntity.value = null;
|
||
return;
|
||
}
|
||
if (!activeEntity.value || !val.includes(activeEntity.value))
|
||
activeEntity.value = val[0];
|
||
},
|
||
{ immediate: true }
|
||
);
|
||
|
||
const entityStats = computed(() => {
|
||
const stats = {};
|
||
for (const e of entitiesWithRows.value)
|
||
stats[e] = {
|
||
total_rows: 0,
|
||
create: 0,
|
||
update: 0,
|
||
missing_ref: 0,
|
||
invalid: 0,
|
||
duplicate: 0,
|
||
duplicate_db: 0,
|
||
};
|
||
for (const r of props.rows || []) {
|
||
if (!r.entities) continue;
|
||
for (const [k, ent] of Object.entries(r.entities)) {
|
||
if (!stats[k]) continue;
|
||
stats[k].total_rows++;
|
||
switch (ent.action) {
|
||
case "create":
|
||
stats[k].create++;
|
||
break;
|
||
case "update":
|
||
stats[k].update++;
|
||
break;
|
||
case "missing_ref":
|
||
stats[k].missing_ref++;
|
||
break;
|
||
case "invalid":
|
||
stats[k].invalid++;
|
||
break;
|
||
}
|
||
if (ent.duplicate) stats[k].duplicate++;
|
||
if (ent.duplicate_db) stats[k].duplicate_db++;
|
||
}
|
||
}
|
||
return stats;
|
||
});
|
||
const activeSummary = computed(() =>
|
||
activeEntity.value ? entityStats.value[activeEntity.value] : null
|
||
);
|
||
const entityHasDuplicates = (e) => {
|
||
const s = entityStats.value[e];
|
||
return s ? s.duplicate + s.duplicate_db > 0 : false;
|
||
};
|
||
const visibleRows = computed(() => {
|
||
if (!props.rows || !activeEntity.value) return [];
|
||
const eps = 0.0000001;
|
||
return props.rows
|
||
.filter((r) => {
|
||
if (!r.entities || !r.entities[activeEntity.value]) return false;
|
||
const ent = r.entities[activeEntity.value];
|
||
if (hideChain.value && ent.existing_chain) return false;
|
||
if (showOnlyChanged.value) {
|
||
// Define change criteria per entity
|
||
if (activeEntity.value === "account") {
|
||
if (ent.delta !== undefined && Math.abs(ent.delta) > eps) return true;
|
||
// new account creation counts as change
|
||
if (ent.action === "create") return true;
|
||
return false;
|
||
}
|
||
if (activeEntity.value === "payment") {
|
||
// payment with valid amount considered change
|
||
return ent.amount !== null && ent.amount !== undefined;
|
||
}
|
||
// Generic entities: any create/update considered change
|
||
if (ent.action === "create" || ent.action === "update") return true;
|
||
return false;
|
||
}
|
||
return true;
|
||
})
|
||
.slice(0, props.limit || props.rows.length);
|
||
});
|
||
function referenceOf(entityName, ent) {
|
||
if (!ent || typeof ent !== "object") return "—";
|
||
|
||
const pick = (val) => {
|
||
if (val === undefined || val === null) return null;
|
||
if (typeof val === "object") {
|
||
if (
|
||
val.normalized !== undefined &&
|
||
val.normalized !== null &&
|
||
String(val.normalized).trim() !== ""
|
||
)
|
||
return val.normalized;
|
||
if (
|
||
val.value !== undefined &&
|
||
val.value !== null &&
|
||
String(val.value).trim() !== ""
|
||
)
|
||
return val.value;
|
||
return null;
|
||
}
|
||
const s = String(val).trim();
|
||
return s === "" ? null : val;
|
||
};
|
||
|
||
// 1. direct reference
|
||
const direct = pick(ent.reference);
|
||
if (direct !== null) return direct;
|
||
|
||
// 2. other plausible keys
|
||
const candidates = [
|
||
"ref",
|
||
"code",
|
||
"number",
|
||
"identifier",
|
||
"external_id",
|
||
`${entityName}_reference`,
|
||
`${entityName}Reference`,
|
||
];
|
||
for (const k of candidates) {
|
||
if (k in ent) {
|
||
const v = pick(ent[k]);
|
||
if (v !== null) return v;
|
||
}
|
||
}
|
||
|
||
// 3. any property containing 'reference'
|
||
for (const [k, v] of Object.entries(ent)) {
|
||
if (k.toLowerCase().includes("reference")) {
|
||
const pv = pick(v);
|
||
if (pv !== null) return pv;
|
||
}
|
||
}
|
||
|
||
// 4. sources map
|
||
const sources = ent.sources;
|
||
if (sources && typeof sources === "object") {
|
||
const priority = [`${entityName}.reference`, "reference"];
|
||
for (const k of priority) {
|
||
if (k in sources) {
|
||
const pv = pick(sources[k]);
|
||
if (pv !== null) return pv;
|
||
}
|
||
}
|
||
for (const [k, v] of Object.entries(sources)) {
|
||
if (k.toLowerCase().includes("reference")) {
|
||
const pv = pick(v);
|
||
if (pv !== null) return pv;
|
||
}
|
||
}
|
||
}
|
||
|
||
return "—";
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<Modal :show="show" max-width="wide" @close="emit('close')">
|
||
<div class="p-4 space-y-4">
|
||
<div class="flex items-start justify-between gap-4">
|
||
<div>
|
||
<h2 class="text-lg font-semibold text-gray-800">Simulacija uvoza</h2>
|
||
<p v-if="localizedEntities" class="text-[12px] text-gray-500">
|
||
Entitete: {{ localizedEntities }}
|
||
</p>
|
||
</div>
|
||
<div class="flex items-center gap-2">
|
||
<label class="text-[11px] text-gray-600 flex items-center gap-1"
|
||
>Prikaži:
|
||
<select
|
||
class="border rounded px-1 py-0.5 text-[11px]"
|
||
:value="limit"
|
||
@change="onLimit"
|
||
>
|
||
<option :value="25">25</option>
|
||
<option :value="50">50</option>
|
||
<option :value="100">100</option>
|
||
<option :value="250">250</option>
|
||
</select>
|
||
</label>
|
||
<button
|
||
type="button"
|
||
class="text-[11px] px-2 py-1 rounded border bg-white hover:bg-gray-50"
|
||
@click="toggleVerbose"
|
||
>
|
||
{{ verbose ? "Manj" : "Več" }} podrobnosti
|
||
</button>
|
||
<label class="flex items-center gap-1 text-[11px] text-gray-600">
|
||
<input type="checkbox" v-model="hideChain" class="rounded border-gray-300" />
|
||
Skrij verižne
|
||
</label>
|
||
<label class="flex items-center gap-1 text-[11px] text-gray-600">
|
||
<input
|
||
type="checkbox"
|
||
v-model="showOnlyChanged"
|
||
class="rounded border-gray-300"
|
||
/>
|
||
Samo spremenjeni
|
||
</label>
|
||
<button
|
||
type="button"
|
||
class="text-[11px] px-2 py-1 rounded bg-gray-800 text-white hover:bg-gray-700"
|
||
@click="emit('close')"
|
||
>
|
||
Zapri
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="entitiesWithRows.length" class="flex flex-wrap gap-1 border-b pb-1">
|
||
<button
|
||
v-for="e in entitiesWithRows"
|
||
:key="e"
|
||
type="button"
|
||
@click="activeEntity = e"
|
||
class="relative px-2 py-1 rounded-t text-[11px] font-medium border"
|
||
:class="
|
||
activeEntity === e
|
||
? 'bg-white border-b-white text-gray-900'
|
||
: 'bg-gray-100 hover:bg-gray-200 text-gray-600'
|
||
"
|
||
>
|
||
<span class="uppercase tracking-wide">{{ e }}</span>
|
||
<span
|
||
v-if="entityHasDuplicates(e)"
|
||
class="absolute -top-1 -right-1 inline-block w-3 h-3 rounded-full bg-amber-500 ring-2 ring-white"
|
||
title="Duplikati"
|
||
></span>
|
||
</button>
|
||
</div>
|
||
|
||
<div
|
||
v-if="activeSummary"
|
||
class="text-[11px] flex flex-wrap items-center gap-3 bg-gray-50 border rounded px-2 py-1"
|
||
>
|
||
<div class="font-semibold uppercase tracking-wide text-gray-600">
|
||
{{ activeEntity }}
|
||
</div>
|
||
<div class="flex items-center gap-2">
|
||
<span class="text-gray-600"
|
||
>Vrstic:
|
||
<span class="font-medium text-gray-800">{{
|
||
activeSummary.total_rows
|
||
}}</span></span
|
||
>
|
||
<span v-if="activeSummary.create" class="text-emerald-700"
|
||
>+{{ activeSummary.create }} novo</span
|
||
>
|
||
<span v-if="activeSummary.update" class="text-blue-700"
|
||
>{{ activeSummary.update }} posodobitev</span
|
||
>
|
||
<span v-if="activeSummary.duplicate" class="text-amber-600"
|
||
>{{ activeSummary.duplicate }} duplikat</span
|
||
>
|
||
<span v-if="activeSummary.duplicate_db" class="text-amber-700"
|
||
>{{ activeSummary.duplicate_db }} obstaja</span
|
||
>
|
||
<span v-if="activeSummary.missing_ref" class="text-red-600"
|
||
>{{ activeSummary.missing_ref }} manjka referenca</span
|
||
>
|
||
<span v-if="activeSummary.invalid" class="text-red-700"
|
||
>{{ activeSummary.invalid }} neveljavnih</span
|
||
>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="activeEntity" class="border rounded bg-white">
|
||
<div class="max-h-[28rem] overflow-auto">
|
||
<table class="min-w-full text-[12px]">
|
||
<thead class="bg-gray-100 text-left sticky top-0 z-10">
|
||
<tr>
|
||
<th class="px-2 py-1 border w-14">#</th>
|
||
<th class="px-2 py-1 border">Podatki</th>
|
||
<th class="px-2 py-1 border w-48">Učinek (plačilo)</th>
|
||
<th class="px-2 py-1 border w-24">Opombe</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-if="loading">
|
||
<td :colspan="4" class="p-4 text-center text-gray-500">Nalagam…</td>
|
||
</tr>
|
||
<tr
|
||
v-for="r in visibleRows"
|
||
:key="r.index"
|
||
class="border-t"
|
||
:class="r.status !== 'ok' ? 'bg-red-50' : ''"
|
||
>
|
||
<td class="p-2 border text-gray-500 align-top">{{ r.index }}</td>
|
||
<td class="p-2 border align-top">
|
||
<div
|
||
v-if="r.entities && r.entities[activeEntity]"
|
||
class="text-[11px] border rounded p-2 bg-white/70 max-w-[360px]"
|
||
>
|
||
<div
|
||
class="font-semibold uppercase tracking-wide text-gray-600 mb-1 flex items-center justify-between"
|
||
>
|
||
<span>{{ activeEntity }}</span>
|
||
<span
|
||
v-if="r.entities[activeEntity].action_label"
|
||
:class="[
|
||
'text-[10px] px-1 py-0.5 rounded',
|
||
r.entities[activeEntity].action === 'create' && 'bg-emerald-100 text-emerald-700',
|
||
r.entities[activeEntity].action === 'update' && 'bg-blue-100 text-blue-700',
|
||
r.entities[activeEntity].action === 'reactivate' && 'bg-purple-100 text-purple-700 font-semibold',
|
||
r.entities[activeEntity].action === 'skip' && 'bg-gray-100 text-gray-600',
|
||
r.entities[activeEntity].action === 'implicit' && 'bg-teal-100 text-teal-700'
|
||
].filter(Boolean)"
|
||
>{{ r.entities[activeEntity].action_label }}</span
|
||
>
|
||
<span
|
||
v-if="r.entities[activeEntity].existing_chain"
|
||
class="ml-1 text-[9px] px-1 py-0.5 rounded bg-indigo-100 text-indigo-700"
|
||
title="Iz obstoječe verige (contract → client_case → person)"
|
||
>chain</span
|
||
>
|
||
<span
|
||
v-if="r.entities[activeEntity].inherited_reference"
|
||
class="ml-1 text-[9px] px-1 py-0.5 rounded bg-amber-100 text-amber-700"
|
||
title="Referenca podedovana"
|
||
>inh</span
|
||
>
|
||
<span
|
||
v-if="r.entities[activeEntity].action === 'implicit'"
|
||
class="ml-1 text-[9px] px-1 py-0.5 rounded bg-teal-100 text-teal-700"
|
||
title="Implicitno"
|
||
>impl</span
|
||
>
|
||
</div>
|
||
|
||
<template v-if="activeEntity === 'account'">
|
||
<div class="flex items-center gap-1">
|
||
Ref:
|
||
<span class="font-medium flex items-center gap-1">
|
||
{{ referenceOf(activeEntity, r.entities[activeEntity]) }}
|
||
<span
|
||
v-if="r.entities[activeEntity].inherited_reference"
|
||
class="text-[9px] px-1 py-0.5 rounded bg-indigo-100 text-indigo-700"
|
||
title="Podedovano iz pogodbe"
|
||
>inh</span
|
||
>
|
||
</span>
|
||
</div>
|
||
<div
|
||
v-if="r.entities[activeEntity].balance_before !== undefined"
|
||
class="mt-1 space-y-0.5"
|
||
>
|
||
<div class="flex items-center gap-1">
|
||
<span class="text-gray-500">Saldo:</span
|
||
><span>{{ fmt(r.entities[activeEntity].balance_before) }}</span>
|
||
</div>
|
||
<div
|
||
v-if="r.entities[activeEntity].balance_after !== undefined"
|
||
class="flex items-center gap-1"
|
||
>
|
||
<ArrowRightIcon
|
||
v-if="
|
||
(r.entities[activeEntity].balance_after ??
|
||
r.entities[activeEntity].balance_before) ===
|
||
r.entities[activeEntity].balance_before
|
||
"
|
||
class="h-3 w-3 text-gray-400"
|
||
/>
|
||
<ArrowDownIcon
|
||
v-else-if="
|
||
(r.entities[activeEntity].balance_after ??
|
||
r.entities[activeEntity].balance_before) <
|
||
r.entities[activeEntity].balance_before
|
||
"
|
||
class="h-3 w-3 text-emerald-500"
|
||
/>
|
||
<ArrowUpIcon v-else class="h-3 w-3 text-red-500" />
|
||
<span
|
||
:class="
|
||
(r.entities[activeEntity].balance_after ??
|
||
r.entities[activeEntity].balance_before) <
|
||
r.entities[activeEntity].balance_before
|
||
? 'text-emerald-600 font-medium'
|
||
: 'text-red-600 font-medium'
|
||
"
|
||
>{{
|
||
fmt(
|
||
r.entities[activeEntity].balance_after ??
|
||
r.entities[activeEntity].balance_before
|
||
)
|
||
}}</span
|
||
>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<template v-else-if="activeEntity === 'payment'">
|
||
<div>
|
||
Znesek:
|
||
<span class="font-medium">{{
|
||
fmt(
|
||
r.entities[activeEntity].amount ??
|
||
r.entities[activeEntity].raw_amount
|
||
)
|
||
}}</span>
|
||
</div>
|
||
<div>
|
||
Datum: {{ formatDate(r.entities[activeEntity].payment_date) }}
|
||
</div>
|
||
<div v-if="r.entities[activeEntity].reference">
|
||
Ref:
|
||
<span class="font-medium">{{
|
||
r.entities[activeEntity].reference
|
||
}}</span>
|
||
</div>
|
||
<div>
|
||
Status:
|
||
<span
|
||
:class="
|
||
r.entities[activeEntity].status === 'ok'
|
||
? 'text-emerald-600'
|
||
: r.entities[activeEntity].status === 'duplicate' ||
|
||
r.entities[activeEntity].status === 'duplicate_db'
|
||
? 'text-amber-600'
|
||
: 'text-red-600'
|
||
"
|
||
>{{
|
||
r.entities[activeEntity].status_label ||
|
||
r.entities[activeEntity].status
|
||
}}</span
|
||
>
|
||
</div>
|
||
</template>
|
||
<template v-else-if="activeEntity === 'contract'">
|
||
<div>
|
||
Ref:
|
||
<span class="font-medium">{{
|
||
referenceOf(activeEntity, r.entities[activeEntity])
|
||
}}</span>
|
||
</div>
|
||
<div>
|
||
Akcija:
|
||
<span
|
||
:class="[
|
||
'font-medium inline-flex items-center gap-1',
|
||
r.entities[activeEntity].action === 'reactivate' && 'text-purple-700'
|
||
].filter(Boolean)"
|
||
>{{
|
||
r.entities[activeEntity].action_label ||
|
||
r.entities[activeEntity].action
|
||
}}
|
||
<span
|
||
v-if="r.entities[activeEntity].reactivation"
|
||
class="text-[9px] px-1 py-0.5 rounded bg-purple-100 text-purple-700"
|
||
title="Pogodba bo reaktivirana"
|
||
>react</span
|
||
></span
|
||
>
|
||
</div>
|
||
<div v-if="r.entities[activeEntity].original_action === 'update' && r.entities[activeEntity].action === 'reactivate'" class="text-[10px] text-purple-600 mt-0.5">
|
||
(iz neaktivnega → aktivno)
|
||
</div>
|
||
</template>
|
||
<template v-else>
|
||
<div class="flex flex-wrap gap-1 mb-1">
|
||
<span
|
||
v-if="r.entities[activeEntity].identity_used"
|
||
class="px-1 py-0.5 rounded bg-indigo-50 text-indigo-700 text-[10px]"
|
||
title="Uporabljena identiteta"
|
||
>{{ r.entities[activeEntity].identity_used }}</span
|
||
>
|
||
<span
|
||
v-if="r.entities[activeEntity].duplicate"
|
||
class="px-1 py-0.5 rounded bg-amber-100 text-amber-700 text-[10px]"
|
||
title="Podvojen v tej seriji"
|
||
>duplikat</span
|
||
>
|
||
<span
|
||
v-if="r.entities[activeEntity].duplicate_db"
|
||
class="px-1 py-0.5 rounded bg-amber-200 text-amber-800 text-[10px]"
|
||
title="Že obstaja v bazi"
|
||
>obstaja v bazi</span
|
||
>
|
||
</div>
|
||
<template v-if="activeEntity === 'person'">
|
||
<div class="grid grid-cols-1 gap-0.5">
|
||
<div
|
||
v-if="
|
||
referenceOf(activeEntity, r.entities[activeEntity]) !== '—'
|
||
"
|
||
class="text-[10px] text-gray-600"
|
||
>
|
||
Ref:
|
||
<span class="font-medium text-gray-800">{{
|
||
referenceOf(activeEntity, r.entities[activeEntity])
|
||
}}</span>
|
||
</div>
|
||
<div
|
||
v-if="r.entities[activeEntity].full_name"
|
||
class="text-[10px] text-gray-600"
|
||
>
|
||
Ime:
|
||
<span class="font-medium">{{
|
||
r.entities[activeEntity].full_name
|
||
}}</span>
|
||
</div>
|
||
<div
|
||
v-else-if="
|
||
r.entities[activeEntity].first_name ||
|
||
r.entities[activeEntity].last_name
|
||
"
|
||
class="text-[10px] text-gray-600"
|
||
>
|
||
Ime:
|
||
<span class="font-medium">{{
|
||
[
|
||
r.entities[activeEntity].first_name,
|
||
r.entities[activeEntity].last_name,
|
||
]
|
||
.filter(Boolean)
|
||
.join(" ")
|
||
}}</span>
|
||
</div>
|
||
<div
|
||
v-if="r.entities[activeEntity].birthday"
|
||
class="text-[10px] text-gray-600"
|
||
>
|
||
Rojstvo:
|
||
<span class="font-medium">{{
|
||
r.entities[activeEntity].birthday
|
||
}}</span>
|
||
</div>
|
||
<div
|
||
v-if="r.entities[activeEntity].description"
|
||
class="text-[10px] text-gray-600"
|
||
>
|
||
Opis:
|
||
<span class="font-medium">{{
|
||
r.entities[activeEntity].description
|
||
}}</span>
|
||
</div>
|
||
<div
|
||
v-if="r.entities[activeEntity].identity_candidates?.length"
|
||
class="text-[10px] text-gray-600"
|
||
>
|
||
Identitete:
|
||
{{ r.entities[activeEntity].identity_candidates.join(", ") }}
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<template v-else-if="activeEntity === 'email'"
|
||
><div class="text-[10px] text-gray-600">
|
||
Email:
|
||
<span class="font-medium">{{
|
||
referenceOf(activeEntity, r.entities[activeEntity])
|
||
}}</span>
|
||
</div></template
|
||
>
|
||
<template v-else-if="activeEntity === 'phone'"
|
||
><div class="text-[10px] text-gray-600">
|
||
Telefon:
|
||
<span class="font-medium">{{
|
||
referenceOf(activeEntity, r.entities[activeEntity])
|
||
}}</span>
|
||
</div></template
|
||
>
|
||
<template v-else-if="activeEntity === 'address'">
|
||
<div class="text-[10px] text-gray-600 space-y-0.5">
|
||
<div
|
||
v-if="
|
||
referenceOf(activeEntity, r.entities[activeEntity]) !== '—'
|
||
"
|
||
>
|
||
Ref:
|
||
<span class="font-medium">{{
|
||
referenceOf(activeEntity, r.entities[activeEntity])
|
||
}}</span>
|
||
</div>
|
||
<div v-if="r.entities[activeEntity].address">
|
||
Naslov:
|
||
<span class="font-medium">{{
|
||
r.entities[activeEntity].address
|
||
}}</span>
|
||
</div>
|
||
<div
|
||
v-if="
|
||
r.entities[activeEntity].postal_code ||
|
||
r.entities[activeEntity].country
|
||
"
|
||
>
|
||
Lokacija:
|
||
<span class="font-medium">{{
|
||
[
|
||
r.entities[activeEntity].postal_code,
|
||
r.entities[activeEntity].country,
|
||
]
|
||
.filter(Boolean)
|
||
.join(" ")
|
||
}}</span>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<template v-else-if="activeEntity === 'client_case'">
|
||
<div class="text-[10px] text-gray-600 space-y-0.5">
|
||
<div
|
||
v-if="
|
||
referenceOf(activeEntity, r.entities[activeEntity]) !== '—'
|
||
"
|
||
>
|
||
Ref:
|
||
<span class="font-medium">{{
|
||
referenceOf(activeEntity, r.entities[activeEntity])
|
||
}}</span>
|
||
</div>
|
||
<div v-if="r.entities[activeEntity].title">
|
||
Naslov:
|
||
<span class="font-medium">{{
|
||
r.entities[activeEntity].title
|
||
}}</span>
|
||
</div>
|
||
<div v-if="r.entities[activeEntity].status">
|
||
Status:
|
||
<span class="font-medium">{{
|
||
r.entities[activeEntity].status
|
||
}}</span>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<template v-else>
|
||
<pre class="text-[10px] whitespace-pre-wrap">{{
|
||
r.entities[activeEntity]
|
||
}}</pre>
|
||
</template>
|
||
</template>
|
||
</div>
|
||
</td>
|
||
<td class="p-2 border align-top text-[11px]">
|
||
<div v-if="r.entities.payment">
|
||
<div class="mb-1 font-semibold text-gray-700">Učinek plačila</div>
|
||
<div v-if="r.entities.account && r.entities.payment.amount !== null">
|
||
Saldo:
|
||
<span class="inline-flex items-center gap-1 font-medium">
|
||
<ArrowDownIcon
|
||
v-if="
|
||
r.entities.account.balance_after -
|
||
r.entities.account.balance_before <
|
||
0
|
||
"
|
||
class="h-3 w-3 text-emerald-500"
|
||
/>
|
||
<ArrowUpIcon
|
||
v-else-if="
|
||
r.entities.account.balance_after -
|
||
r.entities.account.balance_before >
|
||
0
|
||
"
|
||
class="h-3 w-3 text-red-500"
|
||
/>
|
||
<ArrowRightIcon v-else class="h-3 w-3 text-gray-400" />
|
||
<span
|
||
:class="
|
||
r.entities.account.balance_after -
|
||
r.entities.account.balance_before <
|
||
0
|
||
? 'text-emerald-600'
|
||
: r.entities.account.balance_after -
|
||
r.entities.account.balance_before >
|
||
0
|
||
? 'text-red-600'
|
||
: 'text-gray-700'
|
||
"
|
||
>{{
|
||
fmt(
|
||
r.entities.account.balance_after -
|
||
r.entities.account.balance_before
|
||
)
|
||
}}</span
|
||
>
|
||
</span>
|
||
</div>
|
||
<div
|
||
v-if="r.entities.account && r.entities.account.delta !== undefined"
|
||
class="text-gray-500"
|
||
>
|
||
(pred {{ fmt(r.entities.account.balance_before) }} → po
|
||
{{ fmt(r.entities.account.balance_after) }})
|
||
</div>
|
||
<div
|
||
v-if="verbose && r.entities.payment.sources"
|
||
class="mt-2 space-y-1"
|
||
>
|
||
<div class="font-semibold text-gray-600">Učinkoviti stolpci</div>
|
||
<table class="min-w-full border text-[10px] bg-white">
|
||
<thead>
|
||
<tr class="bg-gray-50">
|
||
<th class="px-1 py-0.5 border text-left">Tarča</th>
|
||
<th class="px-1 py-0.5 border text-left">Izvorni stolpec</th>
|
||
<th class="px-1 py-0.5 border text-left">Vrednost</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="(src, key) in r.entities.payment.sources" :key="key">
|
||
<td class="px-1 py-0.5 border whitespace-nowrap">
|
||
{{ key }}
|
||
</td>
|
||
<td class="px-1 py-0.5 border">{{ src.source_column }}</td>
|
||
<td class="px-1 py-0.5 border">
|
||
<span v-if="key === 'payment.amount'"
|
||
>{{ src.value
|
||
}}<span
|
||
v-if="
|
||
src.normalized !== undefined &&
|
||
src.normalized !== src.value
|
||
"
|
||
class="text-gray-500"
|
||
>
|
||
→ {{ src.normalized }}</span
|
||
></span
|
||
><span v-else>{{ src.value ?? "—" }}</span>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td class="p-2 border text-[11px] align-top">
|
||
<div class="text-gray-400">—</div>
|
||
</td>
|
||
</tr>
|
||
<tr v-if="!loading && !visibleRows.length">
|
||
<td :colspan="4" class="p-4 text-center text-gray-500">
|
||
Ni simuliranih vrstic
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<p class="text-[11px] text-gray-500">
|
||
Samo simulacija – podatki niso bili spremenjeni. Saldi predpostavljajo zaporedno
|
||
obdelavo plačil.
|
||
</p>
|
||
</div>
|
||
</Modal>
|
||
</template>
|