Changes to client contract view
This commit is contained in:
parent
4f605451e1
commit
f66bbbf842
|
|
@ -2,9 +2,14 @@
|
||||||
import AppLayout from "@/Layouts/AppLayout.vue";
|
import AppLayout from "@/Layouts/AppLayout.vue";
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import { Link, router, usePage } from "@inertiajs/vue3";
|
import { Link, router, usePage } from "@inertiajs/vue3";
|
||||||
|
import axios from "axios";
|
||||||
import DataTable from "@/Components/DataTable/DataTableNew2.vue";
|
import DataTable from "@/Components/DataTable/DataTableNew2.vue";
|
||||||
|
import DialogModal from "@/Components/DialogModal.vue";
|
||||||
import { Button } from "@/Components/ui/button";
|
import { Button } from "@/Components/ui/button";
|
||||||
import { Input } from "@/Components/ui/input";
|
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 {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
|
|
@ -19,7 +24,7 @@ import DateRangePicker from "@/Components/DateRangePicker.vue";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||||
import { ButtonGroup } from "@/Components/ui/button-group";
|
import { ButtonGroup } from "@/Components/ui/button-group";
|
||||||
import AppPopover from "@/Components/app/ui/AppPopover.vue";
|
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 { Card } from "@/Components/ui/card";
|
||||||
import { Badge } from "@/Components/ui/badge";
|
import { Badge } from "@/Components/ui/badge";
|
||||||
import { hasPermission } from "@/Services/permissions";
|
import { hasPermission } from "@/Services/permissions";
|
||||||
|
|
@ -55,6 +60,31 @@ const selectedSegments = ref(
|
||||||
);
|
);
|
||||||
const filterPopoverOpen = ref(false);
|
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() {
|
function applyDateFilter() {
|
||||||
filterPopoverOpen.value = false;
|
filterPopoverOpen.value = false;
|
||||||
const params = Object.fromEntries(
|
const params = Object.fromEntries(
|
||||||
|
|
@ -129,6 +159,20 @@ function toggleAllColumns(checked) {
|
||||||
exportColumns.value = checked ? exportableColumns.map((col) => col.key) : [];
|
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() {
|
function openExportDialog() {
|
||||||
exportDialogOpen.value = true;
|
exportDialogOpen.value = true;
|
||||||
exportError.value = "";
|
exportError.value = "";
|
||||||
|
|
@ -151,15 +195,12 @@ async function submitExport() {
|
||||||
const payload = {
|
const payload = {
|
||||||
scope: exportScope.value,
|
scope: exportScope.value,
|
||||||
columns: [...exportColumns.value],
|
columns: [...exportColumns.value],
|
||||||
from: fromDate.value || "",
|
from: dateRange.value?.start || "",
|
||||||
to: toDate.value || "",
|
to: dateRange.value?.end || "",
|
||||||
search: search.value || "",
|
search: search.value || "",
|
||||||
segments:
|
segments: selectedSegments.value.length > 0 ? selectedSegments.value.join(",") : "",
|
||||||
selectedSegments.value.length > 0
|
page: contractsCurrentPage.value,
|
||||||
? selectedSegments.value.map((s) => s.id).join(",")
|
per_page: contractsPerPage.value,
|
||||||
: "",
|
|
||||||
page: props.contracts.current_page,
|
|
||||||
per_page: props.contracts.per_page,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
|
|
@ -342,89 +383,103 @@ function extractFilenameFromHeaders(headers) {
|
||||||
:show-toolbar="true"
|
:show-toolbar="true"
|
||||||
>
|
>
|
||||||
<template #toolbar-filters>
|
<template #toolbar-filters>
|
||||||
<AppPopover
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
v-model:open="filterPopoverOpen"
|
<AppPopover
|
||||||
align="start"
|
v-model:open="filterPopoverOpen"
|
||||||
content-class="w-[400px]"
|
align="start"
|
||||||
>
|
content-class="w-[400px]"
|
||||||
<template #trigger>
|
>
|
||||||
<Button variant="outline" size="sm" class="gap-2">
|
<template #trigger>
|
||||||
<Filter class="h-4 w-4" />
|
<Button variant="outline" size="sm" class="gap-2">
|
||||||
Filtri
|
<Filter class="h-4 w-4" />
|
||||||
<span
|
Filtri
|
||||||
v-if="
|
<span
|
||||||
dateRange?.start || dateRange?.end || selectedSegments?.length
|
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"
|
class="ml-1 rounded-full bg-primary px-2 py-0.5 text-xs text-primary-foreground"
|
||||||
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">
|
dateRange?.start || dateRange?.end ? 1 : 0,
|
||||||
Uporabi
|
selectedSegments?.length ? 1 : 0,
|
||||||
</Button>
|
].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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AppPopover>
|
||||||
</AppPopover>
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
class="gap-2"
|
||||||
|
@click="openExportDialog"
|
||||||
|
>
|
||||||
|
<FileDown class="h-4 w-4" />
|
||||||
|
Izvozi v Excel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #cell-reference="{ row }">
|
<template #cell-reference="{ row }">
|
||||||
<Link
|
<Link
|
||||||
|
|
@ -459,5 +514,112 @@ function extractFilenameFromHeaders(headers) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
</AppLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user