Teren-app/resources/js/Pages/Cases/Partials/ContractTable.vue

871 lines
31 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 {
FwbTable,
FwbTableHead,
FwbTableHeadCell,
FwbTableBody,
FwbTableRow,
FwbTableCell,
} from "flowbite-vue";
import Dropdown from "@/Components/Dropdown.vue";
import CaseObjectCreateDialog from "./CaseObjectCreateDialog.vue";
import CaseObjectsDialog from "./CaseObjectsDialog.vue";
import PaymentDialog from "./PaymentDialog.vue";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import ViewPaymentsDialog from "./ViewPaymentsDialog.vue";
import {
faCircleInfo,
faClock,
faEllipsisVertical,
faPenToSquare,
faTrash,
faListCheck,
faPlus,
faBoxArchive,
faFileWord,
faSpinner,
faTags,
} from "@fortawesome/free-solid-svg-icons";
const props = defineProps({
client_case: Object,
contract_types: Array,
contracts: { type: Array, default: () => [] },
segments: { type: Array, default: () => [] },
all_segments: { type: Array, default: () => [] },
});
// 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 }))
);
} catch (e) {}
const emit = defineEmits(["edit", "delete", "add-activity"]);
const formatDate = (d) => {
if (!d) return "-";
const dt = new Date(d);
return isNaN(dt.getTime()) ? "-" : dt.toLocaleDateString("de");
};
const hasDesc = (c) => {
const d = c?.description;
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);
// CaseObject dialog state
import { ref, computed } from "vue";
import { router, useForm } from "@inertiajs/vue3";
import axios from "axios";
// Document generation state
const generating = ref({}); // contract_uuid => boolean
const generatedDocs = ref({}); // contract_uuid => { uuid, path }
const generationError = ref({}); // contract_uuid => message
// Hard-coded slug for now; could be made a prop or dynamic select later
const templateSlug = "contract-summary";
async function generateDocument(c) {
if (!c?.uuid || generating.value[c.uuid]) return;
generating.value[c.uuid] = true;
generationError.value[c.uuid] = null;
try {
const { data } = await axios.post(
route("contracts.generate-document", { contract: c.uuid }),
{
template_slug: templateSlug,
}
);
if (data.status === "ok") {
generatedDocs.value[c.uuid] = { uuid: data.document_uuid, path: data.path };
// optimistic: reload documents list (if parent provides it) partial reload optional
router.reload({ only: ["documents"] });
} else {
generationError.value[c.uuid] = data.message || "Napaka pri generiranju.";
}
} catch (e) {
if (e?.response?.status === 422) {
generationError.value[c.uuid] = "Manjkajoči tokeni v predlogi.";
} else {
generationError.value[c.uuid] = "Neuspešno generiranje.";
}
} finally {
generating.value[c.uuid] = false;
}
}
const showObjectDialog = ref(false);
const showObjectsList = ref(false);
const selectedContract = ref(null);
const openObjectDialog = (c) => {
selectedContract.value = c;
showObjectDialog.value = true;
};
const closeObjectDialog = () => {
showObjectDialog.value = false;
selectedContract.value = null;
};
const openObjectsList = (c) => {
selectedContract.value = c;
showObjectsList.value = true;
};
const closeObjectsList = () => {
showObjectsList.value = false;
selectedContract.value = null;
};
// Promise date helpers
const todayStr = computed(() => {
const d = new Date();
const yyyy = d.getFullYear();
const mm = String(d.getMonth() + 1).padStart(2, "0");
const dd = String(d.getDate()).padStart(2, "0");
return `${yyyy}-${mm}-${dd}`;
});
const getPromiseDate = (c) => c?.account?.promise_date || null;
const promiseStatus = (c) => {
const p = getPromiseDate(c);
if (!p) return null;
if (p > todayStr.value) return "future";
if (p === todayStr.value) return "today";
return "past";
};
const promiseColorClass = (c) => {
const s = promiseStatus(c);
if (s === "future") return "text-green-600";
if (s === "today") return "text-yellow-500";
if (s === "past") return "text-red-600";
return "text-gray-400";
};
// Segment helpers
const contractActiveSegment = (c) => {
const arr = c?.segments || [];
return arr.find((s) => s.pivot?.active) || arr[0] || null;
};
const segmentName = (id) => props.segments.find((s) => s.id === id)?.name || "";
// Sorted segment lists for dropdowns
const sortedSegments = computed(() => {
const list = Array.isArray(props.segments) ? [...props.segments] : [];
return list.sort((a, b) => a.name.localeCompare(b.name, "sl", { sensitivity: "base" }));
});
const sortedAllSegments = computed(() => {
const list = Array.isArray(props.all_segments) ? [...props.all_segments] : [];
return list.sort((a, b) => a.name.localeCompare(b.name, "sl", { sensitivity: "base" }));
});
const confirmChange = ref({
show: false,
contract: null,
segmentId: null,
fromAll: false,
});
const askChangeSegment = (c, segmentId, fromAll = false) => {
// Prevent segment change for archived contracts
if (!c?.active) {
return;
}
confirmChange.value = { show: true, contract: c, segmentId, fromAll };
};
const closeConfirm = () => {
confirmChange.value = { show: false, contract: null, segmentId: null };
};
const doChangeSegment = () => {
const { contract, segmentId, fromAll } = confirmChange.value;
if (!contract || !segmentId) return closeConfirm();
if (fromAll) {
router.post(
route("clientCase.segments.attach", props.client_case),
{
segment_id: segmentId,
contract_uuid: contract.uuid,
make_active_for_contract: true,
},
{
preserveScroll: true,
only: ["contracts", "segments"],
onFinish: () => closeConfirm(),
}
);
} else {
router.post(
route("clientCase.contract.updateSegment", {
client_case: props.client_case.uuid,
uuid: contract.uuid,
}),
{ segment_id: segmentId },
{
preserveScroll: true,
only: ["contracts"],
onFinish: () => closeConfirm(),
}
);
}
};
// Add Payment modal state
const showPaymentDialog = ref(false);
const paymentContract = ref(null);
const paymentForm = useForm({
amount: null,
currency: "EUR",
paid_at: null,
reference: "",
});
const openPaymentDialog = (c) => {
paymentContract.value = c;
paymentForm.reset();
paymentForm.paid_at = todayStr.value;
showPaymentDialog.value = true;
};
const closePaymentDialog = () => {
showPaymentDialog.value = false;
paymentContract.value = null;
};
const submitPayment = () => {
if (!paymentContract.value?.account?.id) {
return;
}
const accountId = paymentContract.value.account.id;
paymentForm.post(route("accounts.payments.store", { account: accountId }), {
preserveScroll: true,
onSuccess: () => {
closePaymentDialog();
// Reload contracts and activities (new payment may create an activity)
router.reload({ only: ["contracts", "activities"] });
},
});
};
// View Payments dialog state
const showPaymentsDialog = ref(false);
const openPaymentsDialog = (c) => {
selectedContract.value = c;
showPaymentsDialog.value = true;
};
const closePaymentsDialog = () => {
showPaymentsDialog.value = false;
selectedContract.value = null;
};
</script>
<template>
<div
class="relative overflow-x-auto rounded-lg border border-gray-200 bg-white shadow-sm"
>
<FwbTable hoverable striped class="text-sm">
<FwbTableHead
class="sticky top-0 z-10 bg-gray-50/90 backdrop-blur border-b border-gray-200 shadow-sm"
>
<FwbTableHeadCell
class="uppercase text-xs font-semibold tracking-wide text-gray-700 py-3"
>Ref.
</FwbTableHeadCell>
<FwbTableHeadCell
class="uppercase text-xs font-semibold tracking-wide text-gray-700 py-3"
>Datum začetka
</FwbTableHeadCell>
<FwbTableHeadCell
class="uppercase text-xs font-semibold tracking-wide text-gray-700 py-3"
>Tip
</FwbTableHeadCell>
<FwbTableHeadCell
class="uppercase text-xs font-semibold tracking-wide text-gray-700 py-3"
>Segment
</FwbTableHeadCell>
<FwbTableHeadCell
class="uppercase text-xs font-semibold tracking-wide text-gray-700 py-3 text-right"
>
Predano</FwbTableHeadCell
>
<FwbTableHeadCell
class="uppercase text-xs font-semibold tracking-wide text-gray-700 py-3 text-right"
>
Odprto</FwbTableHeadCell
>
<FwbTableHeadCell
class="uppercase text-xs font-semibold tracking-wide text-gray-700 py-3 text-center"
>
Opis</FwbTableHeadCell
>
<FwbTableHeadCell class="w-px" />
</FwbTableHead>
<FwbTableBody>
<template v-for="(c, i) in contracts" :key="c.uuid || i">
<FwbTableRow>
<FwbTableCell>{{ c.reference }}</FwbTableCell>
<FwbTableCell>{{ formatDate(c.start_date) }}</FwbTableCell>
<FwbTableCell>{{ c?.type?.name }}</FwbTableCell>
<FwbTableCell>
<div class="flex items-center gap-2">
<span class="text-gray-700">{{
contractActiveSegment(c)?.name || "-"
}}</span>
<Dropdown align="left">
<template #trigger>
<button
type="button"
class="inline-flex items-center justify-center h-7 w-7 rounded-full hover:bg-gray-100"
:class="{
'opacity-50 cursor-not-allowed':
!segments || segments.length === 0 || !c.active,
}"
:title="
!c.active
? 'Segmenta ni mogoče spremeniti za arhivirano pogodbo'
: segments && segments.length
? 'Spremeni segment'
: 'Ni segmentov na voljo za ta primer'
"
:disabled="!c.active || !segments || !segments.length"
>
<FontAwesomeIcon
:icon="faPenToSquare"
class="h-4 w-4 text-gray-600"
/>
</button>
</template>
<template #content>
<div class="py-1">
<template v-if="segments && segments.length">
<button
v-for="s in sortedSegments"
:key="s.id"
type="button"
class="w-full px-3 py-1.5 text-left text-sm hover:bg-gray-50"
@click="askChangeSegment(c, s.id)"
>
<span>{{ s.name }}</span>
</button>
</template>
<template v-else>
<template v-if="all_segments && all_segments.length">
<div class="px-3 py-2 text-xs text-gray-500">
Ni segmentov v tem primeru. Dodaj in nastavi segment:
</div>
<button
v-for="s in sortedAllSegments"
:key="s.id"
type="button"
class="w-full px-3 py-1.5 text-left text-sm hover:bg-gray-50"
@click="askChangeSegment(c, s.id, true)"
>
<span>{{ s.name }}</span>
</button>
</template>
<template v-else>
<div class="px-3 py-2 text-sm text-gray-500">
Ni konfiguriranih segmentov.
</div>
</template>
</template>
</div>
</template>
</Dropdown>
<span
v-if="!c.active"
class="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-semibold bg-gray-200 text-gray-700 uppercase tracking-wide"
>Arhivirano</span
>
</div>
</FwbTableCell>
<FwbTableCell class="text-right">{{
Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR",
}).format(c?.account?.initial_amount ?? 0)
}}</FwbTableCell>
<FwbTableCell class="text-right">{{
Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR",
}).format(c?.account?.balance_amount ?? 0)
}}</FwbTableCell>
<FwbTableCell class="text-center">
<div class="inline-flex items-center justify-center gap-0.5">
<Dropdown align="right">
<template #trigger>
<button
type="button"
class="inline-flex items-center justify-center h-5 w-5 rounded-full"
:title="'Pokaži opis'"
:disabled="!hasDesc(c)"
:class="
hasDesc(c)
? 'hover:bg-gray-100 focus:outline-none'
: 'text-gray-400'
"
>
<FontAwesomeIcon
:icon="faCircleInfo"
class="h-4 w-4"
:class="hasDesc(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 whitespace-pre-wrap"
>
{{ c.description }}
</div>
</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 align="right">
<template #trigger>
<button
type="button"
class="inline-flex items-center justify-center h-5 w-5 rounded-full hover:bg-gray-100 focus:outline-none"
:title="
getPromiseDate(c)
? 'Obljubljen datum plačila'
: 'Ni obljubljenega datuma'
"
:disabled="!getPromiseDate(c)"
>
<FontAwesomeIcon
:icon="faClock"
class="h-4 w-4"
:class="promiseColorClass(c)"
/>
</button>
</template>
<template #content>
<div class="px-3 py-2 text-sm text-gray-700">
<div class="flex items-center gap-2">
<FontAwesomeIcon
:icon="faClock"
class="h-4 w-4"
:class="promiseColorClass(c)"
/>
<span class="font-medium">Obljubljeno plačilo</span>
</div>
<div class="mt-1">
<span class="text-gray-500">Datum:</span>
<span class="ml-1">{{ formatDate(getPromiseDate(c)) }}</span>
</div>
<div class="mt-1" v-if="promiseStatus(c) === 'future'">
<span class="text-green-600">V prihodnosti</span>
</div>
<div class="mt-1" v-else-if="promiseStatus(c) === 'today'">
<span class="text-yellow-600">Danes</span>
</div>
<div class="mt-1" v-else-if="promiseStatus(c) === 'past'">
<span class="text-red-600">Zapadlo</span>
</div>
<div class="mt-1 text-gray-500" v-else>
Ni nastavljenega datuma.
</div>
</div>
</template>
</Dropdown>
</div>
</FwbTableCell>
<FwbTableCell class="text-right whitespace-nowrap">
<Dropdown align="right" width="56">
<template #trigger>
<button
type="button"
class="inline-flex items-center justify-center h-5 w-5 rounded-full hover:bg-gray-100 focus:outline-none"
:title="'Dejanja'"
>
<FontAwesomeIcon
:icon="faEllipsisVertical"
class="h-4 w-4 text-gray-700"
/>
</button>
</template>
<template #content>
<!-- Urejanje -->
<div
class="px-3 pt-2 pb-1 text-[11px] uppercase tracking-wide text-gray-400"
>
Urejanje
</div>
<button
type="button"
class="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
v-if="c.active"
@click="onEdit(c)"
>
<FontAwesomeIcon
:icon="faPenToSquare"
class="h-4 w-4 text-gray-600"
/>
<span>Uredi</span>
</button>
<button
type="button"
class="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
v-if="c.active"
@click="onAddActivity(c)"
>
<FontAwesomeIcon :icon="faListCheck" class="h-4 w-4 text-gray-600" />
<span>Dodaj aktivnost</span>
</button>
<div class="my-1 border-t border-gray-100" />
<!-- Dokumenti -->
<div
class="px-3 pt-2 pb-1 text-[11px] uppercase tracking-wide text-gray-400"
>
Dokument
</div>
<button
type="button"
class="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
:disabled="generating[c.uuid]"
@click="generateDocument(c)"
>
<FontAwesomeIcon
:icon="generating[c.uuid] ? faSpinner : faFileWord"
class="h-4 w-4 text-gray-600"
:class="generating[c.uuid] ? 'animate-spin' : ''"
/>
<span>{{
generating[c.uuid] ? "Generiranje..." : "Generiraj povzetek"
}}</span>
</button>
<a
v-if="generatedDocs[c.uuid]?.path"
:href="'/storage/' + generatedDocs[c.uuid].path"
target="_blank"
class="w-full px-3 py-2 text-left text-sm text-indigo-600 hover:bg-indigo-50 flex items-center gap-2"
>
<FontAwesomeIcon :icon="faFileWord" class="h-4 w-4" />
<span>Prenesi zadnji</span>
</a>
<div
v-if="generationError[c.uuid]"
class="px-3 py-2 text-xs text-rose-600 whitespace-pre-wrap"
>
{{ generationError[c.uuid] }}
</div>
<div class="my-1 border-t border-gray-100" />
<!-- Predmeti -->
<div
class="px-3 pt-2 pb-1 text-[11px] uppercase tracking-wide text-gray-400"
>
Predmeti
</div>
<button
type="button"
class="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
@click="openObjectsList(c)"
>
<FontAwesomeIcon :icon="faCircleInfo" class="h-4 w-4 text-gray-600" />
<span>Seznam predmetov</span>
</button>
<button
type="button"
class="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
v-if="c.active"
@click="openObjectDialog(c)"
>
<FontAwesomeIcon :icon="faPlus" class="h-4 w-4 text-gray-600" />
<span>Dodaj predmet</span>
</button>
<div class="my-1 border-t border-gray-100" />
<!-- Plačila -->
<div
class="px-3 pt-2 pb-1 text-[11px] uppercase tracking-wide text-gray-400"
>
Plačila
</div>
<button
type="button"
class="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
@click="openPaymentsDialog(c)"
>
<FontAwesomeIcon :icon="faCircleInfo" class="h-4 w-4 text-gray-600" />
<span>Pokaži plačila</span>
</button>
<button
type="button"
class="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
v-if="c.active && c?.account"
@click="openPaymentDialog(c)"
>
<FontAwesomeIcon :icon="faPlus" class="h-4 w-4 text-gray-600" />
<span>Dodaj plačilo</span>
</button>
<div class="my-1 border-t border-gray-100" />
<!-- Arhiviranje / Ponovna aktivacija -->
<div
class="px-3 pt-2 pb-1 text-[11px] uppercase tracking-wide text-gray-400"
>
{{ c.active ? "Arhiviranje" : "Ponovna aktivacija" }}
</div>
<button
v-if="c.active"
type="button"
class="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
@click="
router.post(
route('clientCase.contract.archive', {
client_case: client_case.uuid,
uuid: c.uuid,
}),
{},
{
preserveScroll: true,
only: ['contracts', 'activities', 'documents'],
}
)
"
>
<FontAwesomeIcon :icon="faBoxArchive" class="h-4 w-4 text-gray-600" />
<span>Arhiviraj</span>
</button>
<button
v-else
type="button"
class="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
@click="
router.post(
route('clientCase.contract.archive', {
client_case: client_case.uuid,
uuid: c.uuid,
}),
{ reactivate: true },
{
preserveScroll: true,
only: ['contracts', 'activities', 'documents'],
}
)
"
>
<FontAwesomeIcon :icon="faBoxArchive" class="h-4 w-4 text-gray-600" />
<span>Ponovno aktiviraj</span>
</button>
<div class="my-1 border-t border-gray-100" />
<!-- Destruktivno -->
<button
type="button"
class="w-full px-3 py-2 text-left text-sm text-red-700 hover:bg-red-50 flex items-center gap-2"
@click="onDelete(c)"
>
<FontAwesomeIcon :icon="faTrash" class="h-4 w-4 text-red-600" />
<span>Izbriši</span>
</button>
</template>
</Dropdown>
</FwbTableCell>
</FwbTableRow>
</template>
</FwbTableBody>
</FwbTable>
<div
v-if="!contracts || contracts.length === 0"
class="p-6 text-center text-sm text-gray-500"
>
No contracts.
</div>
</div>
<!-- Confirm change segment -->
<div
v-if="confirmChange.show"
class="fixed inset-0 z-50 flex items-center justify-center bg-black/30"
>
<div class="bg-white rounded-lg shadow-lg p-4 w-full max-w-sm">
<div class="text-sm text-gray-800">
Ali želite spremeniti segment za pogodbo
<span class="font-medium">{{ confirmChange.contract?.reference }}</span
>?
</div>
<div class="mt-4 flex justify-end gap-2">
<button
class="px-4 py-2 rounded bg-gray-200 hover:bg-gray-300"
@click="closeConfirm"
>
Prekliči
</button>
<button
class="px-4 py-2 rounded bg-indigo-600 text-white hover:bg-indigo-700"
@click="doChangeSegment"
>
Potrdi
</button>
</div>
</div>
</div>
<CaseObjectCreateDialog
:show="showObjectDialog"
@close="closeObjectDialog"
:client_case="client_case"
:contract="selectedContract"
/>
<CaseObjectsDialog
:show="showObjectsList"
@close="closeObjectsList"
:client_case="client_case"
:contract="selectedContract"
/>
<PaymentDialog
:show="showPaymentDialog"
:form="paymentForm"
@close="closePaymentDialog"
@submit="submitPayment"
/>
<ViewPaymentsDialog
:show="showPaymentsDialog"
:contract="selectedContract"
@close="closePaymentsDialog"
/>
</template>