Added call later, option to limit auto mail so for a client person email you can limit which decision activity will be send to that specific email and moved SMS packages from admin panel to default app view
This commit is contained in:
@@ -107,12 +107,6 @@ const cards = [
|
||||
route: "admin.sms-logs.index",
|
||||
icon: InboxIcon,
|
||||
},
|
||||
{
|
||||
title: "SMS paketi",
|
||||
description: "Kreiranje in pošiljanje serijskih SMS paketov",
|
||||
route: "admin.packages.index",
|
||||
icon: MessageSquareIcon,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
<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>
|
||||
@@ -58,6 +58,8 @@ const form = useInertiaForm({
|
||||
send_auto_mail: true,
|
||||
attach_documents: false,
|
||||
attachment_document_ids: [],
|
||||
call_back_at_date: null,
|
||||
call_back_at_time: null,
|
||||
});
|
||||
|
||||
watch(
|
||||
@@ -127,6 +129,20 @@ const store = async () => {
|
||||
|
||||
const isMultipleContracts = contractUuids && contractUuids.length > 1;
|
||||
|
||||
const buildCallBackAt = (date, time) => {
|
||||
if (!date) return null;
|
||||
const t = time || '00:00';
|
||||
const [h, m] = t.split(':');
|
||||
const d = date instanceof Date ? date : new Date(date);
|
||||
if (isNaN(d.getTime())) return null;
|
||||
const y = d.getFullYear();
|
||||
const mo = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const dy = String(d.getDate()).padStart(2, '0');
|
||||
const hh = String(Number(h || 0)).padStart(2, '0');
|
||||
const mm = String(Number(m || 0)).padStart(2, '0');
|
||||
return `${y}-${mo}-${dy} ${hh}:${mm}:00`;
|
||||
};
|
||||
|
||||
form
|
||||
.transform((data) => ({
|
||||
...data,
|
||||
@@ -138,11 +154,16 @@ const store = async () => {
|
||||
templateAllowsAttachments.value && data.attach_documents && !isMultipleContracts
|
||||
? data.attachment_document_ids
|
||||
: [],
|
||||
call_back_at: hasCallLaterEvent.value
|
||||
? buildCallBackAt(data.call_back_at_date, data.call_back_at_time)
|
||||
: null,
|
||||
call_back_at_date: undefined,
|
||||
call_back_at_time: undefined,
|
||||
}))
|
||||
.post(route("clientCase.activity.store", props.client_case), {
|
||||
onSuccess: () => {
|
||||
close();
|
||||
form.reset("due_date", "amount", "note", "contract_uuids");
|
||||
form.reset("due_date", "amount", "note", "contract_uuids", "call_back_at_date", "call_back_at_time");
|
||||
emit("saved");
|
||||
},
|
||||
});
|
||||
@@ -156,6 +177,22 @@ const currentDecision = () => {
|
||||
decisions.value.find((d) => d.id === form.decision_id) || decisions.value[0] || null
|
||||
);
|
||||
};
|
||||
|
||||
const hasCallLaterEvent = computed(() => {
|
||||
const d = currentDecision();
|
||||
if (!d) return false;
|
||||
return Array.isArray(d.events) && d.events.some((e) => e.key === 'add_call_later');
|
||||
});
|
||||
|
||||
watch(
|
||||
() => hasCallLaterEvent.value,
|
||||
(has) => {
|
||||
if (!has) {
|
||||
form.call_back_at_date = null;
|
||||
form.call_back_at_time = null;
|
||||
}
|
||||
}
|
||||
);
|
||||
const showSendAutoMail = () => {
|
||||
const d = currentDecision();
|
||||
return !!(d && d.auto_mail && d.email_template_id);
|
||||
@@ -409,6 +446,26 @@ watch(
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="hasCallLaterEvent" class="space-y-2">
|
||||
<Label>Datum in ura povratnega klica</Label>
|
||||
<div class="flex gap-2">
|
||||
<DatePicker
|
||||
v-model="form.call_back_at_date"
|
||||
format="dd.MM.yyyy"
|
||||
:error="form.errors.call_back_at"
|
||||
class="flex-1"
|
||||
/>
|
||||
<input
|
||||
v-model="form.call_back_at_time"
|
||||
type="time"
|
||||
class="flex-1 border rounded-md px-3 py-2 text-sm bg-background focus:outline-none focus:ring-2 focus:ring-ring"
|
||||
/>
|
||||
</div>
|
||||
<p v-if="form.errors.call_back_at" class="text-xs text-destructive">
|
||||
{{ form.errors.call_back_at }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="activityAmount">Znesek</Label>
|
||||
<CurrencyInput
|
||||
|
||||
+12
-12
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import AdminLayout from "@/Layouts/AdminLayout.vue";
|
||||
import AppLayout from "@/Layouts/AppLayout.vue";
|
||||
import { Link, router, useForm } from "@inertiajs/vue3";
|
||||
import { ref, computed, nextTick } from "vue";
|
||||
import axios from "axios";
|
||||
@@ -112,9 +112,9 @@ function submitCreate() {
|
||||
})),
|
||||
};
|
||||
|
||||
router.post(route("admin.packages.store"), payload, {
|
||||
router.post(route("packages.store"), payload, {
|
||||
onSuccess: () => {
|
||||
router.visit(route("admin.packages.index"));
|
||||
router.visit(route("packages.index"));
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -202,7 +202,7 @@ async function loadContracts(url = null) {
|
||||
if (onlyValidated.value) params.append("only_validated", "1");
|
||||
params.append("per_page", perPage.value);
|
||||
|
||||
const target = url || `${route("admin.packages.contracts")}?${params.toString()}`;
|
||||
const target = url || `${route("packages.contracts")}?${params.toString()}`;
|
||||
const { data: json } = await axios.get(target, {
|
||||
headers: { "X-Requested-With": "XMLHttpRequest" },
|
||||
});
|
||||
@@ -268,7 +268,7 @@ function goToPage(page) {
|
||||
params.append("per_page", perPage.value);
|
||||
params.append("page", page);
|
||||
|
||||
const url = `${route("admin.packages.contracts")}?${params.toString()}`;
|
||||
const url = `${route("packages.contracts")}?${params.toString()}`;
|
||||
loadContracts(url);
|
||||
}
|
||||
|
||||
@@ -312,9 +312,9 @@ function submitCreateFromContracts() {
|
||||
};
|
||||
|
||||
creatingFromContracts.value = true;
|
||||
router.post(route("admin.packages.store-from-contracts"), payload, {
|
||||
router.post(route("packages.store-from-contracts"), payload, {
|
||||
onSuccess: () => {
|
||||
router.visit(route("admin.packages.index"));
|
||||
router.visit(route("packages.index"));
|
||||
},
|
||||
onError: (errors) => {
|
||||
const first = errors && Object.values(errors)[0];
|
||||
@@ -337,11 +337,11 @@ const numbersCount = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AdminLayout title="Ustvari SMS paket">
|
||||
<AppLayout title="Ustvari SMS paket">
|
||||
<!-- Header -->
|
||||
<div class="mb-6">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<Link :href="route('admin.packages.index')">
|
||||
<Link :href="route('packages.index')">
|
||||
<Button variant="ghost" size="sm">
|
||||
<ArrowLeftIcon class="h-4 w-4 mr-2" />
|
||||
Nazaj
|
||||
@@ -520,7 +520,7 @@ const numbersCount = computed(() => {
|
||||
</div>
|
||||
<div class="flex justify-end gap-2">
|
||||
<Button
|
||||
@click="router.visit(route('admin.packages.index'))"
|
||||
@click="router.visit(route('packages.index'))"
|
||||
variant="outline"
|
||||
>
|
||||
Prekliči
|
||||
@@ -703,7 +703,7 @@ const numbersCount = computed(() => {
|
||||
Izbrano: {{ selectedContractIds.size }}
|
||||
</Badge>
|
||||
<Button
|
||||
@click="router.visit(route('admin.packages.index'))"
|
||||
@click="router.visit(route('packages.index'))"
|
||||
variant="outline"
|
||||
>
|
||||
Prekliči
|
||||
@@ -806,5 +806,5 @@ const numbersCount = computed(() => {
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</AdminLayout>
|
||||
</AppLayout>
|
||||
</template>
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import AdminLayout from "@/Layouts/AdminLayout.vue";
|
||||
import AppLayout from "@/Layouts/AppLayout.vue";
|
||||
import { Link, router } from "@inertiajs/vue3";
|
||||
import { ref } from "vue";
|
||||
import { Card, CardHeader, CardTitle } from "@/Components/ui/card";
|
||||
@@ -48,7 +48,7 @@ function getStatusVariant(status) {
|
||||
}
|
||||
|
||||
function goShow(id) {
|
||||
router.visit(route("admin.packages.show", id));
|
||||
router.visit(route("packages.show", id));
|
||||
}
|
||||
|
||||
function openDeleteDialog(pkg) {
|
||||
@@ -60,7 +60,7 @@ function openDeleteDialog(pkg) {
|
||||
function confirmDelete() {
|
||||
if (!packageToDelete.value) return;
|
||||
deletingId.value = packageToDelete.value.id;
|
||||
router.delete(route("admin.packages.destroy", packageToDelete.value.id), {
|
||||
router.delete(route("packages.destroy", packageToDelete.value.id), {
|
||||
onSuccess: () => {
|
||||
router.reload({ only: ["packages"] });
|
||||
},
|
||||
@@ -74,7 +74,7 @@ function confirmDelete() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AdminLayout title="SMS paketi">
|
||||
<AppLayout title="SMS paketi">
|
||||
<Card class="mb-4">
|
||||
<CardHeader>
|
||||
<div class="flex items-center justify-between">
|
||||
@@ -82,7 +82,7 @@ function confirmDelete() {
|
||||
<PackageIcon class="h-5 w-5 text-muted-foreground" />
|
||||
<CardTitle>SMS paketi</CardTitle>
|
||||
</div>
|
||||
<Link :href="route('admin.packages.create')">
|
||||
<Link :href="route('packages.create')">
|
||||
<Button>
|
||||
<PlusIcon class="h-4 w-4" />
|
||||
Nov paket
|
||||
@@ -109,7 +109,7 @@ function confirmDelete() {
|
||||
:columns="columns"
|
||||
:data="packages.data"
|
||||
:meta="packages"
|
||||
route-name="admin.packages.index"
|
||||
route-name="packages.index"
|
||||
>
|
||||
<template #cell-name="{ row }">
|
||||
<span class="text-sm">{{ row.name ?? "—" }}</span>
|
||||
@@ -172,5 +172,5 @@ function confirmDelete() {
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</AdminLayout>
|
||||
</AppLayout>
|
||||
</template>
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import AdminLayout from "@/Layouts/AdminLayout.vue";
|
||||
import AppLayout from "@/Layouts/AppLayout.vue";
|
||||
import { Link, router } from "@inertiajs/vue3";
|
||||
import { onMounted, onUnmounted, ref, computed } from "vue";
|
||||
import {
|
||||
@@ -88,14 +88,14 @@ function reload() {
|
||||
|
||||
function dispatchPkg() {
|
||||
router.post(
|
||||
route("admin.packages.dispatch", props.package.id),
|
||||
route("packages.dispatch", props.package.id),
|
||||
{},
|
||||
{ onSuccess: reload }
|
||||
);
|
||||
}
|
||||
function cancelPkg() {
|
||||
router.post(
|
||||
route("admin.packages.cancel", props.package.id),
|
||||
route("packages.cancel", props.package.id),
|
||||
{},
|
||||
{ onSuccess: reload }
|
||||
);
|
||||
@@ -132,7 +132,7 @@ async function copyText(text) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AdminLayout :title="`Paket #${package.id}`">
|
||||
<AppLayout :title="`Paket #${package.id}`">
|
||||
<Card class="mb-4">
|
||||
<CardHeader>
|
||||
<div class="flex items-center justify-between">
|
||||
@@ -147,7 +147,7 @@ async function copyText(text) {
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Button variant="ghost" size="sm" as-child>
|
||||
<Link :href="route('admin.packages.index')">
|
||||
<Link :href="route('packages.index')">
|
||||
<ArrowLeftIcon class="h-4 w-4 mr-2" />
|
||||
Nazaj
|
||||
</Link>
|
||||
@@ -281,7 +281,7 @@ async function copyText(text) {
|
||||
:columns="columns"
|
||||
:data="items.data"
|
||||
:meta="items"
|
||||
route-name="admin.packages.show"
|
||||
route-name="packages.show"
|
||||
:route-params="{ id: package.id }"
|
||||
>
|
||||
<template #cell-target="{ row }">
|
||||
@@ -333,5 +333,5 @@ async function copyText(text) {
|
||||
<div v-if="refreshing" class="mt-2 text-xs text-muted-foreground">
|
||||
Osveževanje ...
|
||||
</div>
|
||||
</AdminLayout>
|
||||
</AppLayout>
|
||||
</template>
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup>
|
||||
// flowbite-vue table imports removed; using DataTableClient
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -27,7 +26,7 @@ import { Input } from "@/Components/ui/input";
|
||||
import AppCombobox from "@/Components/app/ui/AppCombobox.vue";
|
||||
import AppMultiSelect from "@/Components/app/ui/AppMultiSelect.vue";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
import DataTableClient from "@/Components/DataTable/DataTableClient.vue";
|
||||
import DataTableNew2 from "@/Components/DataTable/DataTableNew2.vue";
|
||||
import InlineColorPicker from "@/Components/InlineColorPicker.vue";
|
||||
import AppPopover from "@/Components/app/ui/AppPopover.vue";
|
||||
import { FilterIcon, MoreHorizontal, Pencil, Trash } from "lucide-vue-next";
|
||||
@@ -60,16 +59,13 @@ const segmentOptions = computed(() =>
|
||||
}))
|
||||
);
|
||||
|
||||
// DataTable state
|
||||
const sort = ref({ key: null, direction: null });
|
||||
const page = ref(1);
|
||||
const pageSize = ref(25);
|
||||
const columns = [
|
||||
{ key: "id", label: "#", sortable: true, class: "w-16" },
|
||||
{ key: "name", label: "Ime", sortable: true },
|
||||
{ key: "color_tag", label: "Barva", sortable: false },
|
||||
{ key: "segment", label: "Segment", sortable: false },
|
||||
{ key: "decisions", label: "Odločitve", sortable: false, class: "w-32" },
|
||||
{ key: "actions", label: "", sortable: false, class: "w-12" },
|
||||
];
|
||||
|
||||
const form = useForm({
|
||||
@@ -231,18 +227,12 @@ const destroyAction = () => {
|
||||
<Button @click="openCreateDrawer">+ Dodaj akcijo</Button>
|
||||
</div>
|
||||
<div>
|
||||
<DataTableClient
|
||||
<DataTableNew2
|
||||
:columns="columns"
|
||||
:rows="filtered"
|
||||
:sort="sort"
|
||||
:search="''"
|
||||
:page="page"
|
||||
:pageSize="pageSize"
|
||||
:data="filtered"
|
||||
:pageSize="25"
|
||||
:showToolbar="false"
|
||||
:showPagination="true"
|
||||
@update:sort="(v) => (sort = v)"
|
||||
@update:page="(v) => (page = v)"
|
||||
@update:pageSize="(v) => (pageSize = v)"
|
||||
>
|
||||
<template #cell-color_tag="{ row }">
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -262,7 +252,7 @@ const destroyAction = () => {
|
||||
{{ row.segment?.name || "" }}
|
||||
</span>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<template #cell-actions="{ row }">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<Button variant="ghost" size="icon">
|
||||
@@ -285,7 +275,7 @@ const destroyAction = () => {
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</template>
|
||||
</DataTableClient>
|
||||
</DataTableNew2>
|
||||
</div>
|
||||
|
||||
<Dialog v-model:open="drawerEdit">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup>
|
||||
// flowbite-vue table imports removed; using DataTableClient
|
||||
import { EditIcon, TrashBinIcon, DottedMenu } from "@/Utilities/Icons";
|
||||
import { DottedMenu } from "@/Utilities/Icons";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -30,11 +29,11 @@ import {
|
||||
import AppCombobox from "@/Components/app/ui/AppCombobox.vue";
|
||||
import AppMultiSelect from "@/Components/app/ui/AppMultiSelect.vue";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
import DataTableClient from "@/Components/DataTable/DataTableClient.vue";
|
||||
import DataTableNew2 from "@/Components/DataTable/DataTableNew2.vue";
|
||||
import InlineColorPicker from "@/Components/InlineColorPicker.vue";
|
||||
import Dropdown from "@/Components/Dropdown.vue";
|
||||
import AppPopover from "@/Components/app/ui/AppPopover.vue";
|
||||
import { FilterIcon, Trash2, MoreHorizontal, Pencil, Trash } from "lucide-vue-next";
|
||||
import { FilterIcon, MoreHorizontal, Pencil, Trash } from "lucide-vue-next";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -64,10 +63,6 @@ const selectedEvents = ref([]);
|
||||
|
||||
const actionOptions = ref([]);
|
||||
|
||||
// DataTable state
|
||||
const sort = ref({ key: null, direction: null });
|
||||
const page = ref(1);
|
||||
const pageSize = ref(25);
|
||||
const columns = [
|
||||
{ key: "id", label: "#", sortable: true },
|
||||
{ key: "name", label: "Ime", sortable: true },
|
||||
@@ -75,6 +70,7 @@ const columns = [
|
||||
{ key: "events", label: "Dogodki", sortable: false },
|
||||
{ key: "belongs", label: "Pripada akcijam", sortable: false },
|
||||
{ key: "auto_mail", label: "Auto mail", sortable: false },
|
||||
{ key: "actions", label: "", sortable: false, class: "w-12" },
|
||||
];
|
||||
|
||||
const form = useForm({
|
||||
@@ -191,6 +187,8 @@ function defaultConfigForKey(key) {
|
||||
return { archive_setting_id: null, reactivate: false };
|
||||
case "end_field_job":
|
||||
return {};
|
||||
case "add_call_later":
|
||||
return {};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
@@ -466,18 +464,12 @@ const destroyDecision = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<DataTableClient
|
||||
<DataTableNew2
|
||||
:columns="columns"
|
||||
:rows="filtered"
|
||||
:sort="sort"
|
||||
:search="''"
|
||||
:page="page"
|
||||
:pageSize="pageSize"
|
||||
:data="filtered"
|
||||
:pageSize="25"
|
||||
:showToolbar="false"
|
||||
:showPagination="true"
|
||||
@update:sort="(v) => (sort = v)"
|
||||
@update:page="(v) => (page = v)"
|
||||
@update:pageSize="(v) => (pageSize = v)"
|
||||
>
|
||||
<template #cell-color_tag="{ row }">
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -494,14 +486,13 @@ const destroyDecision = () => {
|
||||
</template>
|
||||
<template #cell-events="{ row }">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm text-gray-600">{{ row.events?.length ?? 0 }}</span>
|
||||
<Dropdown align="left" width="64" :close-on-content-click="false">
|
||||
<template #trigger>
|
||||
<button
|
||||
type="button"
|
||||
class="p-1 rounded hover:bg-gray-100 border border-transparent hover:border-gray-200"
|
||||
>
|
||||
<DottedMenu size="sm" css="text-gray-600" />
|
||||
{{ row.events?.length ?? 0 }}
|
||||
</button>
|
||||
</template>
|
||||
<template #content>
|
||||
@@ -549,7 +540,7 @@ const destroyDecision = () => {
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<template #cell-actions="{ row }">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<Button variant="ghost" size="icon">
|
||||
@@ -572,7 +563,7 @@ const destroyDecision = () => {
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</template>
|
||||
</DataTableClient>
|
||||
</DataTableNew2>
|
||||
</div>
|
||||
<Dialog v-model:open="drawerEdit">
|
||||
<DialogContent class="max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||
@@ -752,6 +743,11 @@ const destroyDecision = () => {
|
||||
Ta dogodek nima dodatnih nastavitev.
|
||||
</p>
|
||||
</template>
|
||||
<template v-else-if="eventKey(ev) === 'add_call_later'">
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Datum in ura povratnega klica se vneseta ob ustvarjanju aktivnosti.
|
||||
</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<!-- Fallback advanced editor for unknown event keys -->
|
||||
<InputLabel :for="`cfg-${idx}`" value="Napredna nastavitev (JSON)" />
|
||||
@@ -981,6 +977,11 @@ const destroyDecision = () => {
|
||||
Ta dogodek nima dodatnih nastavitev.
|
||||
</p>
|
||||
</template>
|
||||
<template v-else-if="eventKey(ev) === 'add_call_later'">
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Datum in ura povratnega klica se vneseta ob ustvarjanju aktivnosti.
|
||||
</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<InputLabel :for="`ccfg-${idx}`" value="Napredna nastavitev (JSON)" />
|
||||
<textarea
|
||||
|
||||
Reference in New Issue
Block a user