Teren-app/resources/js/Pages/Imports/Partials/SimulationModal.vue
2025-10-13 21:14:10 +02:00

1025 lines
43 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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: () => [] },
// passthrough verbose from parent to render extra sources in table
verbose: { type: Boolean, default: false },
});
// Emits
const emit = defineEmits(["close", "update:limit", "toggle-verbose"]);
// Local handlers for header controls
function onLimit(e) {
const val = Number(e?.target?.value ?? props.limit ?? 50);
emit("update:limit", isNaN(val) ? 50 : val);
}
function toggleVerbose() {
emit("toggle-verbose");
}
// 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);
// Show only rows skipped due to missing contract.reference in keyref mode (contract/account)
const showOnlyKeyrefSkipped = 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;
// Count one row per entity root
stats[k].total_rows++;
if (Array.isArray(ent)) {
for (const item of ent) {
switch (item.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 (item.duplicate) stats[k].duplicate++;
if (item.duplicate_db) stats[k].duplicate_db++;
}
} else {
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];
// Filter: only rows explicitly skipped due to keyref missing
if (showOnlyKeyrefSkipped.value) {
if (Array.isArray(ent)) {
const anySkipped = ent.some((i) => i && i.skipped_due_to_keyref);
if (!anySkipped) return false;
} else {
if (!ent.skipped_due_to_keyref) return false;
}
}
if (!Array.isArray(ent)) {
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 (Array.isArray(ent)) {
return ent.some((i) => i && (i.action === "create" || i.action === "update"));
}
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"
>
{{ props.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>
<label class="flex items-center gap-1 text-[11px] text-gray-600" title="Prikaži le vrstice preskočene zaradi manjkajoče contract.reference v načinu keyref (pogodbe/računi)">
<input
type="checkbox"
v-model="showOnlyKeyrefSkipped"
class="rounded border-gray-300"
/>
Samo preskočene (keyref)
</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>
<template v-if="!Array.isArray(r.entities[activeEntity])">
<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
>
</template>
</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>
<div
v-if="r.entities[activeEntity].meta"
class="mt-1 text-[10px] text-gray-700"
>
<div class="font-semibold text-gray-600">Meta</div>
<div class="space-y-1">
<div
v-for="(entries, grp) in r.entities[activeEntity].meta"
:key="grp"
class="border rounded p-1 bg-white"
>
<div class="text-[9px] text-gray-500 mb-0.5">
skupina: {{ grp }}
</div>
<div
v-for="(entry, key) in entries"
:key="key"
class="flex items-center gap-2"
>
<span class="text-gray-500">{{ key }}:</span>
<span class="text-gray-800">{{ entry?.value ?? "—" }}</span>
<span class="text-gray-400">(iz: {{ entry?.title }})</span>
</div>
</div>
</div>
</div>
</template>
<template v-else>
<!-- Multi-item rendering for grouped roots (email/phone/address) -->
<template v-if="Array.isArray(r.entities[activeEntity])">
<div class="space-y-1">
<div
v-for="(item, idx) in r.entities[activeEntity]"
:key="idx"
class="border rounded p-2 bg-white"
>
<div class="flex items-center justify-between mb-1">
<div class="flex items-center gap-1">
<span
v-if="
item.group !== undefined &&
item.group !== null &&
String(item.group) !== ''
"
class="text-[10px] px-1 py-0.5 rounded bg-gray-100 text-gray-700"
>skupina: {{ item.group }}</span
>
<span
v-if="item.identity_used"
class="px-1 py-0.5 rounded bg-indigo-50 text-indigo-700 text-[10px]"
title="Uporabljena identiteta"
>{{ item.identity_used }}</span
>
<span
v-if="item.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="item.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>
<span
v-if="item.action_label"
:class="
[
'text-[10px] px-1 py-0.5 rounded',
item.action === 'create' &&
'bg-emerald-100 text-emerald-700',
item.action === 'update' &&
'bg-blue-100 text-blue-700',
item.action === 'skip' && 'bg-gray-100 text-gray-600',
].filter(Boolean)
"
>{{ item.action_label || item.action }}</span
>
</div>
<template v-if="activeEntity === 'email'">
<div class="text-[10px] text-gray-600">
Email:
<span class="font-medium">{{
referenceOf(activeEntity, item)
}}</span>
</div>
</template>
<template v-else-if="activeEntity === 'phone'">
<div class="text-[10px] text-gray-600">
Telefon:
<span class="font-medium">{{
referenceOf(activeEntity, item)
}}</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, item) !== '—'">
Ref:
<span class="font-medium">{{
referenceOf(activeEntity, item)
}}</span>
</div>
<div v-if="item.address">
Naslov:
<span class="font-medium">{{ item.address }}</span>
</div>
<div v-if="item.postal_code || item.country">
Lokacija:
<span class="font-medium">{{
[item.postal_code, item.country]
.filter(Boolean)
.join(" ")
}}</span>
</div>
</div>
</template>
<template v-else>
<pre class="text-[10px] whitespace-pre-wrap">{{
item
}}</pre>
</template>
</div>
</div>
</template>
<!-- Single-item generic rendering (existing) -->
<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>
</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>