298 lines
9.5 KiB
Vue
298 lines
9.5 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";
|
|
|
|
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;
|
|
return new Date(item.call_back_at) < new Date();
|
|
}
|
|
|
|
function fmtDateTime(value) {
|
|
if (!value) return "-";
|
|
const d = new Date(value);
|
|
if (isNaN(d.getTime())) return value;
|
|
const day = String(d.getDate()).padStart(2, "0");
|
|
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
const year = d.getFullYear();
|
|
const hours = String(d.getHours()).padStart(2, "0");
|
|
const minutes = String(d.getMinutes()).padStart(2, "0");
|
|
return `${day}.${month}.${year} ${hours}:${minutes}`;
|
|
}
|
|
|
|
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>
|