Teren-app/resources/js/Pages/Cases/Index.vue
2025-12-14 20:57:39 +01:00

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>