279 lines
9.9 KiB
Vue
279 lines
9.9 KiB
Vue
<script setup>
|
|
import AppLayout from "@/Layouts/AppLayout.vue";
|
|
import SectionTitle from "@/Components/SectionTitle.vue";
|
|
import { Link, router } from "@inertiajs/vue3";
|
|
import { computed, ref } from "vue";
|
|
import DataTable from "@/Components/DataTable/DataTableNew2.vue";
|
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
|
import { faFolderOpen } from "@fortawesome/free-solid-svg-icons";
|
|
import Pagination from "@/Components/Pagination.vue";
|
|
import AppCard from "@/Components/app/ui/card/AppCard.vue";
|
|
import { Filter, FolderOpenIcon } from "lucide-vue-next";
|
|
import CardTitle from "@/Components/ui/card/CardTitle.vue";
|
|
import { fmtCurrency, fmtDateDMY } from "@/Utilities/functions";
|
|
import { Button } from "@/Components/ui/button";
|
|
import AppPopover from "@/Components/app/ui/AppPopover.vue";
|
|
import InputLabel from "@/Components/InputLabel.vue";
|
|
import { Input } from "@/Components/ui/input";
|
|
import DateRangePicker from "@/Components/DateRangePicker.vue";
|
|
import AppMultiSelect from "@/Components/app/ui/AppMultiSelect.vue";
|
|
|
|
const props = defineProps({
|
|
client_cases: Object,
|
|
filters: Object,
|
|
clients: {
|
|
type: Array,
|
|
default: () => [],
|
|
},
|
|
});
|
|
|
|
// Initial search for DataTable toolbar
|
|
const search = ref(props.filters?.search || "");
|
|
const dateRange = ref({
|
|
start: props.filters?.from || null,
|
|
end: props.filters?.to || null,
|
|
});
|
|
const selectedClients = ref(
|
|
Array.isArray(props.filters?.clients)
|
|
? props.filters.clients.map((value) => String(value))
|
|
: []
|
|
);
|
|
const filterPopoverOpen = ref(false);
|
|
|
|
const appliedFilterCount = computed(() => {
|
|
let count = 0;
|
|
if (search.value?.trim()) count += 1;
|
|
if (dateRange.value?.start || dateRange.value?.end) count += 1;
|
|
if (selectedClients.value.length) count += 1;
|
|
return count;
|
|
});
|
|
|
|
function applyFilters() {
|
|
filterPopoverOpen.value = false;
|
|
|
|
const params = {};
|
|
const searchParams = new URLSearchParams(window.location.search);
|
|
const currentPerPage = searchParams.get("perPage");
|
|
if (currentPerPage) {
|
|
params.perPage = currentPerPage;
|
|
}
|
|
if (search.value && search.value.trim() !== "") {
|
|
params.search = search.value.trim();
|
|
}
|
|
if (dateRange.value?.start) {
|
|
params.from = dateRange.value.start;
|
|
}
|
|
if (dateRange.value?.end) {
|
|
params.to = dateRange.value.end;
|
|
}
|
|
if (selectedClients.value.length > 0) {
|
|
params.clients = selectedClients.value.join(",");
|
|
}
|
|
|
|
router.get(route("clientCase"), params, {
|
|
preserveState: true,
|
|
replace: true,
|
|
preserveScroll: true,
|
|
});
|
|
}
|
|
|
|
function clearFilters() {
|
|
dateRange.value = { start: null, end: null };
|
|
selectedClients.value = [];
|
|
search.value = "";
|
|
applyFilters();
|
|
}
|
|
</script>
|
|
<template>
|
|
<AppLayout title="Client cases">
|
|
<template #header> </template>
|
|
<div class="py-6">
|
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
|
<AppCard
|
|
title=""
|
|
padding="none"
|
|
class="p-0! gap-0"
|
|
header-class="py-3! px-4 gap-0 text-muted-foreground"
|
|
body-class=""
|
|
>
|
|
<template #header>
|
|
<div class="flex items-center gap-2">
|
|
<FolderOpenIcon size="18" />
|
|
<CardTitle class="uppercase">Primeri</CardTitle>
|
|
</div>
|
|
</template>
|
|
<DataTable
|
|
:columns="[
|
|
{ key: 'nu', label: 'Št.', sortable: false },
|
|
{ key: 'case', label: 'Primer', sortable: false },
|
|
{ key: 'client', label: 'Stranka', sortable: false },
|
|
{ key: 'tax', label: 'Davčna', sortable: false },
|
|
{ key: 'created_at', label: 'Ustvarjeno', sortable: false },
|
|
{
|
|
key: 'active_contracts',
|
|
label: 'Aktivne pogodbe',
|
|
sortable: false,
|
|
align: 'right',
|
|
},
|
|
{
|
|
key: 'balance',
|
|
label: 'Skupaj stanje',
|
|
sortable: false,
|
|
align: 'right',
|
|
},
|
|
]"
|
|
:data="client_cases.data || []"
|
|
:meta="client_cases"
|
|
route-name="clientCase"
|
|
page-param-name="clientCasesPage"
|
|
per-page-param-name="perPage"
|
|
:page-size="client_cases.per_page"
|
|
:page-size-options="[10, 15, 25, 50, 100]"
|
|
:show-pagination="false"
|
|
:show-toolbar="true"
|
|
:hoverable="true"
|
|
row-key="uuid"
|
|
empty-text="Ni najdenih primerov."
|
|
>
|
|
<template #toolbar-filters>
|
|
<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="appliedFilterCount > 0"
|
|
class="ml-1 rounded-full bg-primary px-2 py-0.5 text-xs text-primary-foreground"
|
|
>
|
|
{{ appliedFilterCount }}
|
|
</span>
|
|
</Button>
|
|
</template>
|
|
<div class="space-y-4">
|
|
<div class="space-y-2">
|
|
<h4 class="font-medium text-sm">Filtri primerov</h4>
|
|
<p class="text-sm text-muted-foreground">
|
|
Izberite parametre za zožanje prikaza primerov.
|
|
</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 imenu, davčni številki ..."
|
|
/>
|
|
</div>
|
|
<div class="space-y-1.5">
|
|
<InputLabel>Datumski obseg (ustvarjeno)</InputLabel>
|
|
<DateRangePicker
|
|
v-model="dateRange"
|
|
format="dd.MM.yyyy"
|
|
placeholder="Izberi datume"
|
|
/>
|
|
</div>
|
|
<div class="space-y-1.5">
|
|
<InputLabel>Stranke</InputLabel>
|
|
<AppMultiSelect
|
|
v-model="selectedClients"
|
|
:items="
|
|
(props.clients || []).map((client) => ({
|
|
value: String(client.id),
|
|
label: client.name,
|
|
}))
|
|
"
|
|
placeholder="Vse stranke"
|
|
search-placeholder="Išči stranko..."
|
|
empty-text="Ni strank"
|
|
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 &&
|
|
selectedClients.length === 0 &&
|
|
search === ''
|
|
"
|
|
@click="clearFilters"
|
|
>
|
|
Počisti
|
|
</Button>
|
|
<Button type="button" size="sm" @click="applyFilters">
|
|
Uporabi
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</AppPopover>
|
|
</template>
|
|
<template #cell-nu="{ row }">
|
|
{{ row.person?.nu || "-" }}
|
|
</template>
|
|
<template #cell-case="{ row }">
|
|
<Link
|
|
:href="route('clientCase.show', { client_case: row.uuid })"
|
|
class="text-indigo-600 hover:underline"
|
|
>
|
|
{{ row.person?.full_name || "-" }}
|
|
</Link>
|
|
<button
|
|
v-if="!row.person"
|
|
@click.prevent="
|
|
router.post(
|
|
route('clientCase.emergencyPerson', { client_case: row.uuid })
|
|
)
|
|
"
|
|
class="ml-2 inline-flex items-center rounded bg-red-50 px-2 py-0.5 text-xs font-semibold text-red-600 hover:bg-red-100 border border-red-200"
|
|
title="Emergency: recreate missing person"
|
|
>
|
|
Add Person
|
|
</button>
|
|
</template>
|
|
<template #cell-client="{ row }">
|
|
{{ row.client?.person?.full_name || "-" }}
|
|
</template>
|
|
<template #cell-tax="{ row }">
|
|
{{ row.person?.tax_number || "-" }}
|
|
</template>
|
|
<template #cell-created_at="{ row }">
|
|
{{ fmtDateDMY(row.created_at) }}
|
|
</template>
|
|
<template #cell-active_contracts="{ row }">
|
|
<div class="text-right">{{ row.active_contracts_count ?? 0 }}</div>
|
|
</template>
|
|
<template #cell-balance="{ row }">
|
|
<div class="text-right">
|
|
{{ fmtCurrency(row.active_contracts_balance_sum) }}
|
|
</div>
|
|
</template>
|
|
</DataTable>
|
|
<div class="border-t border-gray-200 p-4">
|
|
<Pagination
|
|
:links="client_cases.links"
|
|
:from="client_cases.from"
|
|
:to="client_cases.to"
|
|
:total="client_cases.total"
|
|
:per-page="client_cases.per_page || 20"
|
|
:last-page="client_cases.last_page"
|
|
:current-page="client_cases.current_page"
|
|
per-page-param="perPage"
|
|
page-param="clientCasesPage"
|
|
/>
|
|
</div>
|
|
</AppCard>
|
|
</div>
|
|
<!-- Pagination handled by DataTableServer -->
|
|
</div>
|
|
</AppLayout>
|
|
</template>
|