Teren-app/resources/js/Pages/CallLaters/Index.vue
2026-03-09 19:19:43 +01:00

289 lines
9.3 KiB
Vue

<script setup>
import AppLayout from "@/Layouts/AppLayout.vue";
import { Link, router } from "@inertiajs/vue3";
import { computed, ref } from "vue";
import DataTable from "@/Components/DataTable/DataTableNew2.vue";
import AppCard from "@/Components/app/ui/card/AppCard.vue";
import CardTitle from "@/Components/ui/card/CardTitle.vue";
import { Button } from "@/Components/ui/button";
import { Input } from "@/Components/ui/input";
import InputLabel from "@/Components/InputLabel.vue";
import Pagination from "@/Components/Pagination.vue";
import {
PhoneCallIcon,
CheckIcon,
Filter,
ExternalLinkIcon,
MoreHorizontalIcon,
} from "lucide-vue-next";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/Components/ui/dropdown-menu";
import AppPopover from "@/Components/app/ui/AppPopover.vue";
import { fmtDateTime } from "@/Utilities/functions";
const props = defineProps({
callLaters: Object,
filters: Object,
});
const search = ref(props.filters?.search || "");
const dateFrom = ref(props.filters?.date_from || "");
const dateTo = ref(props.filters?.date_to || "");
const filterPopoverOpen = ref(false);
const appliedFilterCount = computed(() => {
let count = 0;
if (search.value?.trim()) count += 1;
if (dateFrom.value) count += 1;
if (dateTo.value) count += 1;
return count;
});
function applyFilters() {
filterPopoverOpen.value = false;
const params = {};
if (search.value?.trim()) {
params.search = search.value.trim();
}
if (dateFrom.value) {
params.date_from = dateFrom.value;
}
if (dateTo.value) {
params.date_to = dateTo.value;
}
router.get(route("callLaters.index"), params, {
preserveState: true,
replace: true,
preserveScroll: true,
});
}
function clearFilters() {
search.value = "";
dateFrom.value = "";
dateTo.value = "";
applyFilters();
}
function markDone(item) {
router.patch(
route("callLaters.complete", item.id),
{},
{
preserveScroll: true,
}
);
}
function openAndComplete(item) {
router.patch(
route("callLaters.complete", item.id),
{},
{
preserveScroll: false,
onSuccess: () => {
if (item.client_case?.uuid) {
router.visit(route("clientCase.show", { client_case: item.client_case.uuid }));
}
},
}
);
}
function isOverdue(item) {
if (!item.call_back_at) return false;
// Strip Z so the value is parsed as local time (datetimes are stored as local time with a wrong Z suffix)
const localDateStr = item.call_back_at.replace("Z", "").replace("T", " ");
return new Date(localDateStr) < new Date();
}
const columns = [
{ key: "person", label: "Stranka / Primer", sortable: false },
{ key: "contract", label: "Pogodba", sortable: false },
{ key: "call_back_at", label: "Datum klica", sortable: false },
{ key: "user", label: "Agent", sortable: false },
{ key: "note", label: "Opomba", sortable: false },
{ key: "actions", label: "", sortable: false, class: "w-12" },
];
</script>
<template>
<AppLayout title="Pokliči kasneje">
<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">
<PhoneCallIcon :size="18" />
<CardTitle class="uppercase">Pokliči kasneje</CardTitle>
</div>
</template>
<DataTable
:columns="columns"
:data="callLaters.data || []"
:meta="callLaters"
:search="search"
route-name="callLaters.index"
:show-toolbar="true"
:show-pagination="false"
:hoverable="true"
row-key="id"
empty-text="Ni zakazanih klicev."
:row-class="(row) => (isOverdue(row) ? 'bg-red-50 dark:bg-red-950/20' : '')"
>
<template #toolbar-filters>
<AppPopover
v-model:open="filterPopoverOpen"
align="start"
content-class="w-[420px]"
>
<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 klicev</h4>
</div>
<div class="space-y-3">
<div class="space-y-1.5">
<InputLabel>Iskanje (stranka)</InputLabel>
<Input
v-model="search"
type="text"
placeholder="Ime stranke..."
@keydown.enter="applyFilters"
/>
</div>
<div class="space-y-1.5">
<InputLabel>Datum od</InputLabel>
<Input v-model="dateFrom" type="date" />
</div>
<div class="space-y-1.5">
<InputLabel>Datum do</InputLabel>
<Input v-model="dateTo" type="date" />
</div>
<div class="flex justify-end gap-2 pt-2 border-t">
<Button
type="button"
variant="outline"
size="sm"
:disabled="appliedFilterCount === 0"
@click="clearFilters"
>
Počisti
</Button>
<Button type="button" size="sm" @click="applyFilters">
Uporabi
</Button>
</div>
</div>
</div>
</AppPopover>
</template>
<template #cell-person="{ row }">
<div>
<Link
v-if="row.client_case"
:href="route('clientCase.show', { client_case: row.client_case.uuid })"
class="font-medium text-indigo-600 hover:underline"
>
{{ row.client_case.person?.full_name || "-" }}
</Link>
<span v-else class="text-muted-foreground">-</span>
</div>
</template>
<template #cell-contract="{ row }">
<span v-if="row.contract">{{ row.contract.reference }}</span>
<span v-else class="text-muted-foreground">-</span>
</template>
<template #cell-call_back_at="{ row }">
<span
:class="[
'font-medium',
isOverdue(row) ? 'text-red-600 dark:text-red-400' : '',
]"
>
{{ fmtDateTime(row.call_back_at) }}
</span>
<span v-if="isOverdue(row)" class="ml-2 text-xs text-red-500 font-semibold">
Zamuda
</span>
</template>
<template #cell-user="{ row }">
<span v-if="row.user">{{ row.user.name }}</span>
<span v-else class="text-muted-foreground">-</span>
</template>
<template #cell-note="{ row }">
<span class="line-clamp-2 text-sm text-muted-foreground">
{{ row.activity?.note || "-" }}
</span>
</template>
<template #cell-actions="{ row }">
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button size="icon" variant="ghost" class="h-8 w-8">
<MoreHorizontalIcon class="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem @click="markDone(row)">
<CheckIcon class="mr-2 h-4 w-4" />
Opravljeno
</DropdownMenuItem>
<DropdownMenuItem
v-if="row.client_case?.uuid"
@click="openAndComplete(row)"
>
<ExternalLinkIcon class="mr-2 h-4 w-4" />
Odpri in opravi
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</template>
</DataTable>
<div class="border-t border-gray-200 p-4">
<Pagination
:links="callLaters.links"
:from="callLaters.from"
:to="callLaters.to"
:total="callLaters.total"
:per-page="callLaters.per_page || 50"
:last-page="callLaters.last_page"
:current-page="callLaters.current_page"
/>
</div>
</AppCard>
</div>
</div>
</AppLayout>
</template>