717 lines
25 KiB
Vue
717 lines
25 KiB
Vue
<script setup>
|
|
import AppLayout from "@/Layouts/AppLayout.vue";
|
|
import { computed, ref } from "vue";
|
|
import { Link, router, usePage } from "@inertiajs/vue3";
|
|
import axios from "axios";
|
|
import DataTable from "@/Components/DataTable/DataTableNew2.vue";
|
|
import DialogModal from "@/Components/DialogModal.vue";
|
|
import { Button } from "@/Components/ui/button";
|
|
import { Input } from "@/Components/ui/input";
|
|
import { Checkbox } from "@/Components/ui/checkbox";
|
|
import { Label } from "@/Components/ui/label";
|
|
import { Switch } from "@/Components/ui/switch";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/Components/ui/select"; // kept in case elsewhere but segment filter replaced
|
|
import AppMultiSelect from "@/Components/app/ui/AppMultiSelect.vue";
|
|
import PersonInfoGrid from "@/Components/PersonInfo/PersonInfoGrid.vue";
|
|
import SectionTitle from "@/Components/SectionTitle.vue";
|
|
import DateRangePicker from "@/Components/DateRangePicker.vue";
|
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
|
import { ButtonGroup } from "@/Components/ui/button-group";
|
|
import AppPopover from "@/Components/app/ui/AppPopover.vue";
|
|
import { Filter, LinkIcon, FileDown, LayoutIcon } from "lucide-vue-next";
|
|
import { Card } from "@/Components/ui/card";
|
|
import { Badge } from "@/Components/ui/badge";
|
|
import { hasPermission } from "@/Services/permissions";
|
|
import InputLabel from "@/Components/InputLabel.vue";
|
|
import { cn } from "@/lib/utils";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuTrigger,
|
|
} from "@/Components/ui/dropdown-menu";
|
|
import { toNumber } from "lodash";
|
|
import { FormControl, FormField, FormFieldArray, FormLabel } from "@/Components/ui/form";
|
|
import { Field, FieldLabel } from "@/Components/ui/field";
|
|
import { toTypedSchema } from "@vee-validate/zod";
|
|
import { z } from "zod";
|
|
import FormChangeSegment from "./Partials/FormChangeSegment.vue";
|
|
|
|
const props = defineProps({
|
|
client: Object,
|
|
contracts: Object,
|
|
filters: Object,
|
|
segments: Array,
|
|
types: Object,
|
|
});
|
|
|
|
const page = usePage();
|
|
// Expose as a callable computed: use in templates as hasPerm('permission-slug')
|
|
const hasPerm = computed(() => (permission) =>
|
|
hasPermission(page.props.auth?.user, permission)
|
|
);
|
|
|
|
const dateRange = ref({
|
|
start: props.filters?.from || null,
|
|
end: props.filters?.to || null,
|
|
});
|
|
const search = ref(props.filters?.search || "");
|
|
// Multi-segment selection (backwards compatible if legacy single 'segment' present)
|
|
const selectedSegments = ref(
|
|
Array.isArray(props.filters?.segments)
|
|
? props.filters.segments.map((s) => String(s))
|
|
: props.filters?.segment
|
|
? [String(props.filters.segment)]
|
|
: []
|
|
);
|
|
const filterPopoverOpen = ref(false);
|
|
const selectedContracts = ref([]);
|
|
const changeSegmentDialogOpen = ref(false);
|
|
const contractTable = ref(null);
|
|
|
|
const exportDialogOpen = ref(false);
|
|
const exportScope = ref("current");
|
|
const exportColumns = ref([
|
|
"reference",
|
|
"customer",
|
|
"address",
|
|
"start",
|
|
"segment",
|
|
"balance",
|
|
]);
|
|
const exportError = ref("");
|
|
const isExporting = ref(false);
|
|
|
|
const exportableColumns = [
|
|
{ key: "reference", label: "Referenca" },
|
|
{ key: "customer", label: "Stranka" },
|
|
{ key: "address", label: "Naslov" },
|
|
{ key: "start", label: "Začetek" },
|
|
{ key: "segment", label: "Segment" },
|
|
{ key: "balance", label: "Stanje" },
|
|
];
|
|
|
|
const contractsCurrentPage = computed(() => props.contracts?.current_page ?? 1);
|
|
const contractsPerPage = computed(() => props.contracts?.per_page ?? 15);
|
|
const totalContracts = computed(() => props.contracts?.total ?? 0);
|
|
const currentPageCount = computed(() => props.contracts?.data?.length ?? 0);
|
|
const allColumnsSelected = computed(
|
|
() => exportColumns.value.length === exportableColumns.length
|
|
);
|
|
const exportDisabled = computed(
|
|
() => exportColumns.value.length === 0 || isExporting.value
|
|
);
|
|
const segmentSelectItems = computed(() =>
|
|
props.segments.map((val, i) => ({
|
|
label: val.name,
|
|
value: val.id,
|
|
}))
|
|
);
|
|
|
|
function applyDateFilter() {
|
|
filterPopoverOpen.value = false;
|
|
const params = Object.fromEntries(
|
|
new URLSearchParams(window.location.search).entries()
|
|
);
|
|
if (dateRange.value?.start) {
|
|
params.from = dateRange.value.start;
|
|
} else {
|
|
delete params.from;
|
|
}
|
|
if (dateRange.value?.end) {
|
|
params.to = dateRange.value.end;
|
|
} else {
|
|
delete params.to;
|
|
}
|
|
if (search.value && search.value.trim() !== "") {
|
|
params.search = search.value.trim();
|
|
} else {
|
|
delete params.search;
|
|
}
|
|
if (selectedSegments.value.length > 0) {
|
|
// join as comma list for backend; adjust server parsing accordingly
|
|
params.segments = selectedSegments.value.join(",");
|
|
} else {
|
|
delete params.segments;
|
|
}
|
|
delete params.page;
|
|
// remove legacy single segment param if present
|
|
delete params.segment;
|
|
router.get(route("client.contracts", { uuid: props.client.uuid }), params, {
|
|
preserveState: true,
|
|
replace: true,
|
|
preserveScroll: true,
|
|
only: ["contracts"],
|
|
});
|
|
}
|
|
|
|
function clearDateFilter() {
|
|
dateRange.value = { start: null, end: null };
|
|
selectedSegments.value = [];
|
|
search.value = "";
|
|
applyDateFilter();
|
|
}
|
|
|
|
// Search handled by DataTableServer toolbar; keep date filter applying
|
|
|
|
// Build params for navigating to client case show, including active segment if available
|
|
function caseShowParams(contract) {
|
|
const params = { client_case: contract?.client_case?.uuid };
|
|
const segId = contract?.segments?.[0]?.id;
|
|
if (segId) {
|
|
params.segment = segId;
|
|
}
|
|
return params;
|
|
}
|
|
|
|
// Format YYYY-MM-DD (or ISO date) to dd.mm.yyyy
|
|
function formatDate(value) {
|
|
if (!value) return "-";
|
|
try {
|
|
const iso = String(value).split("T")[0];
|
|
const parts = iso.split("-");
|
|
if (parts.length !== 3) return value;
|
|
const [y, m, d] = parts;
|
|
return `${d.padStart(2, "0")}.${m.padStart(2, "0")}.${y}`;
|
|
} catch (e) {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
function toggleAllColumns(checked) {
|
|
exportColumns.value = checked ? exportableColumns.map((col) => col.key) : [];
|
|
}
|
|
|
|
function handleColumnToggle(key, checked) {
|
|
if (checked) {
|
|
if (!exportColumns.value.includes(key)) {
|
|
exportColumns.value = [...exportColumns.value, key];
|
|
}
|
|
} else {
|
|
exportColumns.value = exportColumns.value.filter((col) => col !== key);
|
|
}
|
|
}
|
|
|
|
function setExportScopeFromSwitch(checked) {
|
|
exportScope.value = checked ? "all" : "current";
|
|
}
|
|
|
|
function openExportDialog() {
|
|
exportDialogOpen.value = true;
|
|
exportError.value = "";
|
|
}
|
|
|
|
function closeExportDialog() {
|
|
exportDialogOpen.value = false;
|
|
}
|
|
|
|
async function submitExport() {
|
|
if (exportColumns.value.length === 0) {
|
|
exportError.value = "Izberi vsaj en stolpec.";
|
|
return;
|
|
}
|
|
|
|
try {
|
|
exportError.value = "";
|
|
isExporting.value = true;
|
|
|
|
const payload = {
|
|
scope: exportScope.value,
|
|
columns: [...exportColumns.value],
|
|
from: dateRange.value?.start || "",
|
|
to: dateRange.value?.end || "",
|
|
search: search.value || "",
|
|
segments: selectedSegments.value.length > 0 ? selectedSegments.value.join(",") : "",
|
|
page: contractsCurrentPage.value,
|
|
per_page: contractsPerPage.value,
|
|
};
|
|
|
|
const response = await axios.post(
|
|
route("client.contracts.export", { uuid: props.client.uuid }),
|
|
payload,
|
|
{ responseType: "blob" }
|
|
);
|
|
|
|
const blob = new Blob([response.data], {
|
|
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
});
|
|
const url = window.URL.createObjectURL(blob);
|
|
const link = document.createElement("a");
|
|
link.href = url;
|
|
const filename =
|
|
extractFilenameFromHeaders(response.headers) || buildDefaultFilename();
|
|
link.download = filename;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
exportDialogOpen.value = false;
|
|
} catch (error) {
|
|
console.error("Export error:", error);
|
|
console.error("Error response:", error.response);
|
|
|
|
let errorMessage = "Izvoz je spodletel. Poskusi znova.";
|
|
|
|
if (error.response?.status === 404) {
|
|
errorMessage = "Pot za izvoz ne obstaja. Prosim kontaktiraj administratorja.";
|
|
} else if (error.response?.status === 500) {
|
|
errorMessage = "Napaka na strežniku. Poskusi znova.";
|
|
} else if (error.response?.data) {
|
|
try {
|
|
const text = await error.response.data.text();
|
|
const json = JSON.parse(text);
|
|
errorMessage = json.message || errorMessage;
|
|
} catch (e) {
|
|
console.error("Could not parse error response:", e);
|
|
}
|
|
}
|
|
|
|
exportError.value = errorMessage;
|
|
} finally {
|
|
isExporting.value = false;
|
|
}
|
|
}
|
|
|
|
function slugify(value) {
|
|
if (!value) {
|
|
return "data";
|
|
}
|
|
const slug = value.replace(/[^a-z0-9]+/gi, "-").replace(/^-+|-+$/g, "");
|
|
return slug || "data";
|
|
}
|
|
|
|
function buildDefaultFilename() {
|
|
const now = new Date();
|
|
const dd = String(now.getDate()).padStart(2, "0");
|
|
const mm = String(now.getMonth() + 1).padStart(2, "0");
|
|
const yy = String(now.getFullYear()).slice(-2);
|
|
const clientName = props.client?.person?.full_name || "stranka";
|
|
return `${dd}${mm}${yy}_${slugify(clientName)}-Pogodbe.xlsx`;
|
|
}
|
|
|
|
function extractFilenameFromHeaders(headers) {
|
|
if (!headers) {
|
|
return null;
|
|
}
|
|
const disposition =
|
|
headers["content-disposition"] || headers["Content-Disposition"] || "";
|
|
if (!disposition) {
|
|
return null;
|
|
}
|
|
const utf8Match = disposition.match(/filename\*=UTF-8''([^;]+)/i);
|
|
if (utf8Match?.[1]) {
|
|
try {
|
|
return decodeURIComponent(utf8Match[1]);
|
|
} catch (error) {
|
|
return utf8Match[1];
|
|
}
|
|
}
|
|
const asciiMatch = disposition.match(/filename="?([^";]+)"?/i);
|
|
return asciiMatch?.[1] || null;
|
|
}
|
|
|
|
function handleSelectionChange(selectedKeys) {
|
|
selectedContracts.value = selectedKeys.map((val, i) => {
|
|
const num = toNumber(val);
|
|
|
|
return props.contracts.data[num].uuid;
|
|
});
|
|
}
|
|
|
|
function openDialogChangeSegment() {
|
|
changeSegmentDialogOpen.value = true;
|
|
}
|
|
|
|
function clearContractTableSelected() {
|
|
if (contractTable.value) {
|
|
contractTable.value.clearSelection();
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<AppLayout title="Pogodbe">
|
|
<template #header></template>
|
|
<!-- Header card (matches Client/Show header style) -->
|
|
<div class="pt-6">
|
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
|
<Card class="border-l-4 border-blue-400 p-0">
|
|
<div class="p-3 flex justify-between items-center">
|
|
<SectionTitle>
|
|
<template #title>
|
|
{{ client.person.full_name }}
|
|
</template>
|
|
</SectionTitle>
|
|
<Badge class="bg-blue-500 text-white"> Naročnik </Badge>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
<div class="pt-1">
|
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
|
<Card class="p-0!">
|
|
<div class="p-3">
|
|
<PersonInfoGrid
|
|
:types="types"
|
|
:person="client.person"
|
|
:edit="hasPerm('client-edit')"
|
|
></PersonInfoGrid>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Contracts list card -->
|
|
<div class="py-12">
|
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
|
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
|
|
<div class="mx-auto max-w-4x1">
|
|
<div class="p-2 flex flex-row items-center gap-2">
|
|
<Link
|
|
:class="
|
|
cn(
|
|
'border border-gray-200 py-2 px-3 rounded-md hover:bg-accent hover:text-accent-foreground ',
|
|
route().current('client.show')
|
|
? 'bg-accent text-accent-foreground'
|
|
: ''
|
|
)
|
|
"
|
|
:href="route('client.show', { uuid: client.uuid })"
|
|
>
|
|
Primeri
|
|
</Link>
|
|
|
|
<Link
|
|
:class="
|
|
cn(
|
|
'border border-gray-200 py-2 px-3 rounded-md hover:bg-accent hover:text-accent-foreground ',
|
|
route().current('client.contracts')
|
|
? 'bg-accent text-accent-foreground'
|
|
: ''
|
|
)
|
|
"
|
|
:href="route('client.contracts', { uuid: client.uuid })"
|
|
>
|
|
Pogodbe
|
|
</Link>
|
|
</div>
|
|
<DataTable
|
|
ref="contractTable"
|
|
:columns="[
|
|
{ key: 'reference', label: 'Referenca', sortable: false },
|
|
{ key: 'customer', label: 'Stranka', sortable: false },
|
|
{ key: 'address', label: 'Naslov', sortable: false },
|
|
{ key: 'start', label: 'Začetek', sortable: false },
|
|
{ key: 'segment', label: 'Segment', sortable: false },
|
|
{ key: 'balance', label: 'Stanje', sortable: false, align: 'right' },
|
|
]"
|
|
:data="contracts.data || []"
|
|
:meta="{
|
|
current_page: contracts.current_page,
|
|
per_page: contracts.per_page,
|
|
total: contracts.total,
|
|
last_page: contracts.last_page,
|
|
from: contracts.from,
|
|
to: contracts.to,
|
|
links: contracts.links,
|
|
}"
|
|
route-name="client.contracts"
|
|
:route-params="{ uuid: client.uuid }"
|
|
row-key="uuid"
|
|
:only-props="['contracts']"
|
|
:page-size-options="[10, 15, 25, 50, 100]"
|
|
:enable-row-selection="true"
|
|
@selection:change="handleSelectionChange"
|
|
page-param-name="contracts_page"
|
|
per-page-param-name="contracts_per_page"
|
|
:show-toolbar="true"
|
|
>
|
|
<template #toolbar-filters="{ table }">
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<AppPopover
|
|
v-model:open="filterPopoverOpen"
|
|
align="start"
|
|
content-class="w-[400px]"
|
|
>
|
|
<template #trigger>
|
|
<Button variant="outline" size="sm" class="gap-2">
|
|
<Filter class="h-4 w-4" />
|
|
Filtri
|
|
<span
|
|
v-if="
|
|
dateRange?.start || dateRange?.end || selectedSegments?.length
|
|
"
|
|
class="ml-1 rounded-full bg-primary px-2 py-0.5 text-xs text-primary-foreground"
|
|
>
|
|
{{
|
|
[
|
|
dateRange?.start || dateRange?.end ? 1 : 0,
|
|
selectedSegments?.length ? 1 : 0,
|
|
].reduce((a, b) => a + b, 0)
|
|
}}
|
|
</span>
|
|
</Button>
|
|
</template>
|
|
<div class="space-y-4">
|
|
<div class="space-y-2">
|
|
<h4 class="font-medium text-sm">Filtri pogodb</h4>
|
|
<p class="text-sm text-muted-foreground">
|
|
Izberite filtre za prikaz pogodb
|
|
</p>
|
|
</div>
|
|
<div class="space-y-3">
|
|
<div class="space-y-1.5">
|
|
<InputLabel>Iskanje</InputLabel>
|
|
<Input
|
|
v-model="search"
|
|
type="text"
|
|
placeholder="Išči po referenci, stranki..."
|
|
/>
|
|
</div>
|
|
<div class="space-y-1.5">
|
|
<InputLabel>Datumska območja</InputLabel>
|
|
<DateRangePicker
|
|
v-model="dateRange"
|
|
format="dd.MM.yyyy"
|
|
placeholder="Izberi datumska območja"
|
|
/>
|
|
</div>
|
|
<div class="space-y-1.5">
|
|
<InputLabel>Segmenti</InputLabel>
|
|
<AppMultiSelect
|
|
v-model="selectedSegments"
|
|
:items="
|
|
segments.map((s) => ({
|
|
value: String(s.id),
|
|
label: s.name,
|
|
}))
|
|
"
|
|
placeholder="Vsi segmenti"
|
|
search-placeholder="Išči segment..."
|
|
empty-text="Ni segmentov"
|
|
chip-variant="secondary"
|
|
/>
|
|
</div>
|
|
<div class="flex justify-end gap-2 pt-2 border-t">
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
:disabled="
|
|
!dateRange?.start &&
|
|
!dateRange?.end &&
|
|
selectedSegments.length === 0 &&
|
|
search === ''
|
|
"
|
|
@click="clearDateFilter"
|
|
>
|
|
Počisti
|
|
</Button>
|
|
<Button type="button" size="sm" @click="applyDateFilter">
|
|
Uporabi
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</AppPopover>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
class="gap-2"
|
|
@click="openExportDialog"
|
|
>
|
|
<FileDown class="h-4 w-4" />
|
|
Izvozi v Excel
|
|
</Button>
|
|
<DropdownMenu v-if="table.getSelectedRowModel().rows.length > 0">
|
|
<DropdownMenuTrigger as-child>
|
|
<Button class="gap-2 px-3" variant="outline">
|
|
<Badge
|
|
class="h-5 min-w-5 rounded-full font-mono tabular-nums text-accent"
|
|
variant="destructive"
|
|
>
|
|
{{ table.getSelectedRowModel().rows.length }}
|
|
</Badge>
|
|
Akcija
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="start">
|
|
<DropdownMenuItem @click="openDialogChangeSegment">
|
|
<LayoutIcon />
|
|
Spremeni segment
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
<Button
|
|
variant="outline"
|
|
@click="clearContractTableSelected"
|
|
v-if="table.getSelectedRowModel().rows.length > 0"
|
|
>
|
|
Odznači izbrane
|
|
</Button>
|
|
</div>
|
|
</template>
|
|
<template #cell-reference="{ row }">
|
|
<Link
|
|
:href="route('clientCase.show', caseShowParams(row))"
|
|
class="font-semibold hover:underline text-primary-700"
|
|
>
|
|
{{ row.reference }}
|
|
</Link>
|
|
</template>
|
|
<template #cell-customer="{ row }">
|
|
{{ row.client_case?.person?.full_name || "-" }}
|
|
</template>
|
|
<template #cell-address="{ row }">
|
|
{{ row.client_case?.person?.address?.address || "-" }}
|
|
</template>
|
|
<template #cell-start="{ row }">
|
|
{{ formatDate(row.start_date) }}
|
|
</template>
|
|
<template #cell-segment="{ row }">
|
|
{{ row.segments?.[0]?.name || "-" }}
|
|
</template>
|
|
<template #cell-balance="{ row }">
|
|
<div class="text-right">
|
|
{{
|
|
new Intl.NumberFormat("sl-SI", {
|
|
style: "currency",
|
|
currency: "EUR",
|
|
}).format(Number(row.account?.balance_amount ?? 0))
|
|
}}
|
|
</div>
|
|
</template>
|
|
</DataTable>
|
|
</div>
|
|
<!-- Pagination handled by DataTableServer -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Excel export dialog -->
|
|
<DialogModal :show="exportDialogOpen" max-width="3xl" @close="closeExportDialog">
|
|
<template #title>
|
|
<div class="space-y-1">
|
|
<h3 class="text-lg font-semibold leading-6 text-foreground">Izvoz v Excel</h3>
|
|
<p class="text-sm text-muted-foreground">
|
|
Izberi stolpce in obseg podatkov za izvoz.
|
|
</p>
|
|
</div>
|
|
</template>
|
|
<template #content>
|
|
<form id="contract-export-form" class="space-y-5" @submit.prevent="submitExport">
|
|
<div class="space-y-3 rounded-lg border bg-muted/40 p-4">
|
|
<div class="flex items-start justify-between gap-3">
|
|
<div class="space-y-1">
|
|
<p class="text-sm font-medium text-foreground">Obseg podatkov</p>
|
|
<p class="text-sm text-muted-foreground">
|
|
Preklopi, ali izvoziš samo trenutni pogled ali vse pogodbe.
|
|
</p>
|
|
</div>
|
|
<div
|
|
class="flex items-center gap-3 rounded-md bg-background px-3 py-2 shadow-sm"
|
|
>
|
|
<span class="text-xs font-medium text-muted-foreground">Stran</span>
|
|
<Switch
|
|
:model-value="exportScope === 'all'"
|
|
@update:modelValue="setExportScopeFromSwitch"
|
|
aria-label="Preklopi obseg izvoza"
|
|
/>
|
|
<span class="text-xs font-medium text-muted-foreground">Vse</span>
|
|
</div>
|
|
</div>
|
|
<div class="grid gap-2 sm:grid-cols-2">
|
|
<div class="rounded-lg border bg-background p-3 shadow-sm">
|
|
<p class="text-sm font-semibold text-foreground">Trenutna stran</p>
|
|
<p class="text-xs text-muted-foreground">
|
|
{{ currentPageCount }} zapisov
|
|
</p>
|
|
</div>
|
|
<div class="rounded-lg border bg-background p-3 shadow-sm">
|
|
<p class="text-sm font-semibold text-foreground">Vse pogodbe</p>
|
|
<p class="text-xs text-muted-foreground">{{ totalContracts }} zapisov</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-4 rounded-lg border bg-muted/40 p-4">
|
|
<div class="flex flex-wrap items-start justify-between gap-3">
|
|
<div class="space-y-1">
|
|
<p class="text-sm font-medium text-foreground">Stolpci</p>
|
|
<p class="text-sm text-muted-foreground">
|
|
Izberi, katere stolpce želiš vključiti v izvoz.
|
|
</p>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<Checkbox
|
|
id="export-columns-all"
|
|
:model-value="allColumnsSelected"
|
|
@update:modelValue="toggleAllColumns"
|
|
aria-label="Označi vse stolpce"
|
|
/>
|
|
<Label for="export-columns-all" class="text-sm text-muted-foreground">
|
|
Označi vse
|
|
</Label>
|
|
</div>
|
|
</div>
|
|
<div class="grid gap-2 sm:grid-cols-2">
|
|
<label
|
|
v-for="col in exportableColumns"
|
|
:key="col.key"
|
|
class="flex items-start gap-3 rounded-lg border bg-background px-3 py-3 text-sm shadow-sm transition hover:border-primary/40"
|
|
:for="`export-col-${col.key}`"
|
|
>
|
|
<Checkbox
|
|
:id="`export-col-${col.key}`"
|
|
:model-value="exportColumns.includes(col.key)"
|
|
:value="col.key"
|
|
@update:modelValue="(checked) => handleColumnToggle(col.key, checked)"
|
|
class="mt-0.5"
|
|
/>
|
|
<div class="space-y-0.5">
|
|
<p class="font-medium text-foreground">{{ col.label }}</p>
|
|
<p class="text-xs text-muted-foreground">Vključi stolpec v datoteko.</p>
|
|
</div>
|
|
</label>
|
|
</div>
|
|
<p v-if="exportError" class="text-sm text-destructive">{{ exportError }}</p>
|
|
</div>
|
|
</form>
|
|
</template>
|
|
<template #footer>
|
|
<div class="flex flex-row gap-2">
|
|
<Button type="button" variant="ghost" @click="closeExportDialog">
|
|
Prekliči
|
|
</Button>
|
|
<Button
|
|
type="submit"
|
|
form="contract-export-form"
|
|
:disabled="exportDisabled"
|
|
class="gap-2"
|
|
>
|
|
<span v-if="!isExporting">Prenesi Excel</span>
|
|
<span v-else>Pripravljam ...</span>
|
|
</Button>
|
|
</div>
|
|
</template>
|
|
</DialogModal>
|
|
|
|
<!-- Change segment selected contracts dialog -->
|
|
|
|
<FormChangeSegment
|
|
:show="changeSegmentDialogOpen"
|
|
@close="changeSegmentDialogOpen = false"
|
|
:segments="segmentSelectItems"
|
|
:contracts="selectedContracts"
|
|
:clear-selected-rows="clearContractTableSelected"
|
|
/>
|
|
</AppLayout>
|
|
</template>
|