Importer update add support for meta data and multiple inserts for some entities like addresses and phones, updated other things

This commit is contained in:
Simon Pocrnjič
2025-10-09 22:28:48 +02:00
parent c8029c9eb0
commit 0598261cdc
27 changed files with 2517 additions and 375 deletions
@@ -24,6 +24,7 @@ import {
faBoxArchive,
faFileWord,
faSpinner,
faTags,
} from "@fortawesome/free-solid-svg-icons";
const props = defineProps({
@@ -36,7 +37,10 @@ const props = defineProps({
// Debug: log incoming contract balances (remove after fix)
try {
console.debug('Contracts received (balances):', props.contracts.map(c => ({ ref: c.reference, bal: c?.account?.balance_amount })));
console.debug(
"Contracts received (balances):",
props.contracts.map((c) => ({ ref: c.reference, bal: c?.account?.balance_amount }))
);
} catch (e) {}
const emit = defineEmits(["edit", "delete", "add-activity"]);
@@ -52,6 +56,107 @@ const hasDesc = (c) => {
return typeof d === "string" && d.trim().length > 0;
};
// Meta helpers
const formatMetaDate = (v) => {
if (!v) {
return "-";
}
const d = new Date(v);
if (isNaN(d.getTime())) {
return String(v);
}
const dd = String(d.getDate()).padStart(2, "0");
const mm = String(d.getMonth() + 1).padStart(2, "0");
const yyyy = d.getFullYear();
return `${dd}.${mm}.${yyyy}`;
};
const formatMetaNumber = (v) => {
if (v === null || v === undefined || v === "") {
return "0";
}
let n = typeof v === "number" ? v : parseFloat(String(v).replace(",", "."));
if (isNaN(n)) {
return String(v);
}
const hasDecimal = Math.abs(n % 1) > 0;
return new Intl.NumberFormat("de-DE", {
minimumFractionDigits: hasDecimal ? 2 : 0,
maximumFractionDigits: hasDecimal ? 2 : 0,
}).format(n);
};
const formatMetaValue = (entry) => {
const value = entry?.value;
const type = entry?.type;
if (value === null || value === undefined || String(value).trim() === "") {
return "-";
}
if (type === "date") {
return formatMetaDate(value);
}
if (type === "number") {
return formatMetaNumber(value);
}
if (typeof value === "number") {
return formatMetaNumber(value);
}
if (typeof value === "string") {
// Try number
const n = parseFloat(value.replace(",", "."));
if (!isNaN(n)) {
return formatMetaNumber(n);
}
// Try date
const d = new Date(value);
if (!isNaN(d.getTime())) {
return formatMetaDate(value);
}
}
return String(value);
};
const getMetaEntries = (c) => {
const meta = c?.meta;
const results = [];
const visit = (node, keyName) => {
if (node === null || node === undefined) {
return;
}
if (Array.isArray(node)) {
node.forEach((el) => visit(el));
return;
}
if (typeof node === "object") {
const hasValue = Object.prototype.hasOwnProperty.call(node, "value");
const hasTitle = Object.prototype.hasOwnProperty.call(node, "title");
if (hasValue || hasTitle) {
const title =
(node.title || keyName || "").toString().trim() || keyName || "Meta";
results.push({ title, value: node.value, type: node.type });
return;
}
for (const [k, v] of Object.entries(node)) {
visit(v, k);
}
return;
}
if (keyName) {
results.push({ title: keyName, value: node });
}
};
visit(meta, undefined);
return results.filter(
(e) =>
e.title &&
e.value !== null &&
e.value !== undefined &&
String(e.value).trim() !== ""
);
};
const hasMeta = (c) => getMetaEntries(c).length > 0;
const onEdit = (c) => emit("edit", c);
const onDelete = (c) => emit("delete", c);
const onAddActivity = (c) => emit("add-activity", c);
@@ -385,7 +490,7 @@ const closePaymentsDialog = () => {
}}</FwbTableCell>
<FwbTableCell class="text-center">
<div class="inline-flex items-center justify-center gap-0.5">
<Dropdown width="64" align="left">
<Dropdown align="right">
<template #trigger>
<button
type="button"
@@ -414,8 +519,50 @@ const closePaymentsDialog = () => {
</template>
</Dropdown>
<!-- Meta data dropdown -->
<Dropdown align="right">
<template #trigger>
<button
type="button"
class="inline-flex items-center justify-center h-5 w-5 rounded-full"
:title="'Pokaži meta'"
:disabled="!hasMeta(c)"
:class="
hasMeta(c)
? 'hover:bg-gray-100 focus:outline-none'
: 'text-gray-400'
"
>
<FontAwesomeIcon
:icon="faTags"
class="h-4 w-4"
:class="hasMeta(c) ? 'text-gray-700' : 'text-gray-400'"
/>
</button>
</template>
<template #content>
<div class="max-w-sm px-3 py-2 text-sm text-gray-700">
<template v-if="hasMeta(c)">
<div
v-for="(m, idx) in getMetaEntries(c)"
:key="idx"
class="flex items-start gap-2 py-0.5"
>
<span class="text-gray-500 whitespace-nowrap"
>{{ m.title }}:</span
>
<span class="text-gray-800">{{ formatMetaValue(m) }}</span>
</div>
</template>
<template v-else>
<div class="text-gray-500">Ni meta podatkov.</div>
</template>
</div>
</template>
</Dropdown>
<!-- Promise date indicator -->
<Dropdown width="64" align="left">
<Dropdown align="right">
<template #trigger>
<button
type="button"