production #1

Merged
sipo merged 45 commits from production into master 2026-01-27 18:02:44 +00:00
Showing only changes of commit f66bbbf842 - Show all commits

View File

@ -2,9 +2,14 @@
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,
@ -19,7 +24,7 @@ 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 } from "lucide-vue-next";
import { Filter, LinkIcon, FileDown } from "lucide-vue-next";
import { Card } from "@/Components/ui/card";
import { Badge } from "@/Components/ui/badge";
import { hasPermission } from "@/Services/permissions";
@ -55,6 +60,31 @@ const selectedSegments = ref(
);
const filterPopoverOpen = ref(false);
const exportDialogOpen = ref(false);
const exportScope = ref("current");
const exportColumns = ref(["reference", "customer", "start", "segment", "balance"]);
const exportError = ref("");
const isExporting = ref(false);
const exportableColumns = [
{ key: "reference", label: "Referenca" },
{ key: "customer", label: "Stranka" },
{ 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
);
function applyDateFilter() {
filterPopoverOpen.value = false;
const params = Object.fromEntries(
@ -129,6 +159,20 @@ 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 = "";
@ -151,15 +195,12 @@ async function submitExport() {
const payload = {
scope: exportScope.value,
columns: [...exportColumns.value],
from: fromDate.value || "",
to: toDate.value || "",
from: dateRange.value?.start || "",
to: dateRange.value?.end || "",
search: search.value || "",
segments:
selectedSegments.value.length > 0
? selectedSegments.value.map((s) => s.id).join(",")
: "",
page: props.contracts.current_page,
per_page: props.contracts.per_page,
segments: selectedSegments.value.length > 0 ? selectedSegments.value.join(",") : "",
page: contractsCurrentPage.value,
per_page: contractsPerPage.value,
};
const response = await axios.post(
@ -342,6 +383,7 @@ function extractFilenameFromHeaders(headers) {
:show-toolbar="true"
>
<template #toolbar-filters>
<div class="flex flex-wrap items-center gap-2">
<AppPopover
v-model:open="filterPopoverOpen"
align="start"
@ -395,7 +437,10 @@ function extractFilenameFromHeaders(headers) {
<AppMultiSelect
v-model="selectedSegments"
:items="
segments.map((s) => ({ value: String(s.id), label: s.name }))
segments.map((s) => ({
value: String(s.id),
label: s.name,
}))
"
placeholder="Vsi segmenti"
search-placeholder="Išči segment..."
@ -425,6 +470,16 @@ function extractFilenameFromHeaders(headers) {
</div>
</div>
</AppPopover>
<Button
variant="outline"
size="sm"
class="gap-2"
@click="openExportDialog"
>
<FileDown class="h-4 w-4" />
Izvozi v Excel
</Button>
</div>
</template>
<template #cell-reference="{ row }">
<Link
@ -459,5 +514,112 @@ function extractFilenameFromHeaders(headers) {
</div>
</div>
</div>
<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>
</AppLayout>
</template>