823 lines
29 KiB
Vue
823 lines
29 KiB
Vue
<script setup>
|
|
import AdminLayout from "@/Layouts/AdminLayout.vue";
|
|
import { Link, router, useForm } from "@inertiajs/vue3";
|
|
import { ref, computed } from "vue";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/Components/ui/card";
|
|
import { Button } from "@/Components/ui/button";
|
|
import { Input } from "@/Components/ui/input";
|
|
import { Label } from "@/Components/ui/label";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/Components/ui/select";
|
|
import { Textarea } from "@/Components/ui/textarea";
|
|
import { Checkbox } from "@/Components/ui/checkbox";
|
|
import { Badge } from "@/Components/ui/badge";
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "@/Components/ui/table";
|
|
import { Separator } from "@/Components/ui/separator";
|
|
import DataTableNew2 from "@/Components/DataTable/DataTableNew2.vue";
|
|
import Pagination from "@/Components/Pagination.vue";
|
|
import {
|
|
PackageIcon,
|
|
PlusIcon,
|
|
XIcon,
|
|
SearchIcon,
|
|
Trash2Icon,
|
|
EyeIcon,
|
|
} from "lucide-vue-next";
|
|
import AppCard from "@/Components/app/ui/card/AppCard.vue";
|
|
|
|
const props = defineProps({
|
|
packages: { type: Object, required: true },
|
|
profiles: { type: Array, default: () => [] },
|
|
senders: { type: Array, default: () => [] },
|
|
templates: { type: Array, default: () => [] },
|
|
segments: { type: Array, default: () => [] },
|
|
clients: { type: Array, default: () => [] },
|
|
});
|
|
|
|
const deletingId = ref(null);
|
|
const creatingFromContracts = ref(false);
|
|
|
|
const columns = [
|
|
{ accessorKey: "id", header: "ID" },
|
|
{ accessorKey: "uuid", header: "UUID" },
|
|
{ accessorKey: "name", header: "Ime" },
|
|
{ accessorKey: "type", header: "Tip" },
|
|
{ accessorKey: "status", header: "Status" },
|
|
{ accessorKey: "total_items", header: "Skupaj" },
|
|
{ accessorKey: "sent_count", header: "Poslano" },
|
|
{ accessorKey: "failed_count", header: "Neuspešno" },
|
|
{ accessorKey: "finished_at", header: "Zaključeno" },
|
|
{ accessorKey: "actions", header: "", enableSorting: false },
|
|
];
|
|
|
|
function getStatusVariant(status) {
|
|
if (["queued", "running"].includes(status)) return "secondary";
|
|
if (status === "completed") return "default";
|
|
if (status === "failed") return "destructive";
|
|
return "outline";
|
|
}
|
|
|
|
function goShow(id) {
|
|
router.visit(route("admin.packages.show", id));
|
|
}
|
|
|
|
const showCreate = ref(false);
|
|
const createMode = ref("numbers"); // 'numbers' | 'contracts'
|
|
const form = useForm({
|
|
type: "sms",
|
|
name: "",
|
|
description: "",
|
|
profile_id: null,
|
|
sender_id: null,
|
|
template_id: null,
|
|
delivery_report: false,
|
|
body: "",
|
|
numbers: "", // one per line
|
|
});
|
|
|
|
const filteredSenders = computed(() => {
|
|
if (!form.profile_id) return props.senders;
|
|
return props.senders.filter((s) => s.profile_id === form.profile_id);
|
|
});
|
|
|
|
function onTemplateChange() {
|
|
const template = props.templates.find((t) => t.id === form.template_id);
|
|
if (template?.content) {
|
|
form.body = template.content;
|
|
} else {
|
|
form.body = "";
|
|
}
|
|
}
|
|
|
|
function submitCreate() {
|
|
const lines = (form.numbers || "")
|
|
.split(/\r?\n/)
|
|
.map((s) => s.trim())
|
|
.filter(Boolean);
|
|
if (!lines.length) return;
|
|
if (!form.profile_id && !form.template_id) {
|
|
// require profile if no template/default profile resolution available
|
|
alert("Izberi SMS profil ali predlogo.");
|
|
return;
|
|
}
|
|
if (!form.template_id && !form.body) {
|
|
alert("Vnesi vsebino sporočila ali izberi predlogo.");
|
|
return;
|
|
}
|
|
|
|
const payload = {
|
|
type: "sms",
|
|
name: form.name || `SMS paket ${new Date().toLocaleString()}`,
|
|
description: form.description || "",
|
|
items: lines.map((number) => ({
|
|
number,
|
|
payload: {
|
|
profile_id: form.profile_id,
|
|
sender_id: form.sender_id,
|
|
template_id: form.template_id,
|
|
delivery_report: !!form.delivery_report,
|
|
body: form.body && form.body.trim() ? form.body.trim() : null,
|
|
},
|
|
})),
|
|
};
|
|
|
|
router.post(route("admin.packages.store"), payload, {
|
|
onSuccess: () => {
|
|
form.reset();
|
|
showCreate.value = false;
|
|
router.reload({ only: ["packages"] });
|
|
},
|
|
});
|
|
}
|
|
|
|
// Contracts mode state & actions
|
|
const contracts = ref({
|
|
data: [],
|
|
meta: { current_page: 1, last_page: 1, per_page: 25, total: 0 },
|
|
});
|
|
const segmentId = ref(null);
|
|
const search = ref("");
|
|
const clientId = ref(null);
|
|
const startDateFrom = ref("");
|
|
const startDateTo = ref("");
|
|
const promiseDateFrom = ref("");
|
|
const promiseDateTo = ref("");
|
|
const onlyMobile = ref(false);
|
|
const onlyValidated = ref(false);
|
|
const loadingContracts = ref(false);
|
|
const selectedContractIds = ref(new Set());
|
|
const perPage = ref(25);
|
|
|
|
async function loadContracts(url = null) {
|
|
loadingContracts.value = true;
|
|
try {
|
|
const params = new URLSearchParams();
|
|
if (segmentId.value) params.append("segment_id", segmentId.value);
|
|
if (search.value) params.append("q", search.value);
|
|
if (clientId.value) params.append("client_id", clientId.value);
|
|
if (startDateFrom.value) params.append("start_date_from", startDateFrom.value);
|
|
if (startDateTo.value) params.append("start_date_to", startDateTo.value);
|
|
if (promiseDateFrom.value) params.append("promise_date_from", promiseDateFrom.value);
|
|
if (promiseDateTo.value) params.append("promise_date_to", promiseDateTo.value);
|
|
if (onlyMobile.value) params.append("only_mobile", "1");
|
|
if (onlyValidated.value) params.append("only_validated", "1");
|
|
params.append("per_page", perPage.value);
|
|
|
|
const target = url || `${route("admin.packages.contracts")}?${params.toString()}`;
|
|
const res = await fetch(target, {
|
|
headers: { "X-Requested-With": "XMLHttpRequest" },
|
|
});
|
|
const json = await res.json();
|
|
contracts.value = {
|
|
data: json.data || [],
|
|
meta: json.meta || { current_page: 1, last_page: 1, per_page: 25, total: 0 },
|
|
};
|
|
} finally {
|
|
loadingContracts.value = false;
|
|
}
|
|
}
|
|
|
|
function toggleSelectContract(id) {
|
|
const s = selectedContractIds.value;
|
|
if (s.has(id)) {
|
|
s.delete(id);
|
|
} else {
|
|
s.add(id);
|
|
}
|
|
// force reactivity
|
|
selectedContractIds.value = new Set(Array.from(s));
|
|
}
|
|
|
|
function clearSelection() {
|
|
selectedContractIds.value = new Set();
|
|
}
|
|
|
|
function deletePackage(pkg) {
|
|
if (!pkg || pkg.status !== "draft") return;
|
|
if (!confirm(`Izbrišem paket #${pkg.id}?`)) return;
|
|
deletingId.value = pkg.id;
|
|
router.delete(route("admin.packages.destroy", pkg.id), {
|
|
onSuccess: () => {
|
|
router.reload({ only: ["packages"] });
|
|
},
|
|
onFinish: () => {
|
|
deletingId.value = null;
|
|
},
|
|
});
|
|
}
|
|
|
|
function toggleSelectAll() {
|
|
const currentPageIds = contracts.value.data.map((c) => c.id);
|
|
const allSelected = currentPageIds.every((id) => selectedContractIds.value.has(id));
|
|
|
|
if (allSelected) {
|
|
// Deselect all on current page
|
|
currentPageIds.forEach((id) => selectedContractIds.value.delete(id));
|
|
} else {
|
|
// Select all on current page
|
|
currentPageIds.forEach((id) => selectedContractIds.value.add(id));
|
|
}
|
|
|
|
// Force reactivity
|
|
selectedContractIds.value = new Set(Array.from(selectedContractIds.value));
|
|
}
|
|
|
|
const allCurrentPageSelected = computed(() => {
|
|
if (!contracts.value.data.length) return false;
|
|
return contracts.value.data.every((c) => selectedContractIds.value.has(c.id));
|
|
});
|
|
|
|
const someCurrentPageSelected = computed(() => {
|
|
if (!contracts.value.data.length) return false;
|
|
return (
|
|
contracts.value.data.some((c) => selectedContractIds.value.has(c.id)) &&
|
|
!allCurrentPageSelected.value
|
|
);
|
|
});
|
|
|
|
function goContractsPage(delta) {
|
|
const { current_page } = contracts.value.meta;
|
|
const nextPage = current_page + delta;
|
|
if (nextPage < 1 || nextPage > contracts.value.meta.last_page) return;
|
|
|
|
const params = new URLSearchParams();
|
|
if (segmentId.value) params.append("segment_id", segmentId.value);
|
|
if (search.value) params.append("q", search.value);
|
|
if (clientId.value) params.append("client_id", clientId.value);
|
|
if (startDateFrom.value) params.append("start_date_from", startDateFrom.value);
|
|
if (startDateTo.value) params.append("start_date_to", startDateTo.value);
|
|
if (promiseDateFrom.value) params.append("promise_date_from", promiseDateFrom.value);
|
|
if (promiseDateTo.value) params.append("promise_date_to", promiseDateTo.value);
|
|
if (onlyMobile.value) params.append("only_mobile", "1");
|
|
if (onlyValidated.value) params.append("only_validated", "1");
|
|
params.append("per_page", perPage.value);
|
|
params.append("page", nextPage);
|
|
|
|
const base = `${route("admin.packages.contracts")}?${params.toString()}`;
|
|
loadContracts(base);
|
|
}
|
|
|
|
function submitCreateFromContracts() {
|
|
const ids = Array.from(selectedContractIds.value);
|
|
if (!ids.length) return;
|
|
// Optional quick client-side sanity: if all selected are from current page and none have phones, warn early.
|
|
const visibleById = new Map((contracts.value.data || []).map((c) => [c.id, c]));
|
|
const selectedVisible = ids.map((id) => visibleById.get(id)).filter(Boolean);
|
|
if (selectedVisible.length && selectedVisible.every((c) => !c?.selected_phone)) {
|
|
alert("Za izbrane pogodbe ni mogoče najti prejemnikov (telefonov).");
|
|
return;
|
|
}
|
|
const payload = {
|
|
type: "sms",
|
|
name: form.name || `SMS paket (segment) ${new Date().toLocaleString()}`,
|
|
description: form.description || "",
|
|
payload: {
|
|
profile_id: form.profile_id,
|
|
sender_id: form.sender_id,
|
|
template_id: form.template_id,
|
|
delivery_report: !!form.delivery_report,
|
|
body: form.body && form.body.trim() ? form.body.trim() : null,
|
|
},
|
|
contract_ids: ids,
|
|
};
|
|
|
|
creatingFromContracts.value = true;
|
|
router.post(route("admin.packages.store-from-contracts"), payload, {
|
|
onSuccess: () => {
|
|
clearSelection();
|
|
showCreate.value = false;
|
|
router.reload({ only: ["packages"] });
|
|
},
|
|
onError: (errors) => {
|
|
// Show the first validation error if present
|
|
const first = errors && Object.values(errors)[0];
|
|
if (first) {
|
|
alert(String(first));
|
|
}
|
|
},
|
|
onFinish: () => {
|
|
creatingFromContracts.value = false;
|
|
},
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<AdminLayout title="SMS paketi">
|
|
<Card class="mb-4">
|
|
<CardHeader>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center gap-2">
|
|
<PackageIcon class="h-5 w-5 text-muted-foreground" />
|
|
<CardTitle>SMS paketi</CardTitle>
|
|
</div>
|
|
<Button
|
|
@click="showCreate = !showCreate"
|
|
:variant="showCreate ? 'outline' : 'default'"
|
|
>
|
|
<component :is="showCreate ? XIcon : PlusIcon" class="h-4 w-4 mr-2" />
|
|
{{ showCreate ? "Zapri" : "Nov paket" }}
|
|
</Button>
|
|
</div>
|
|
</CardHeader>
|
|
</Card>
|
|
|
|
<Card v-if="showCreate" class="mb-6">
|
|
<CardContent class="pt-6">
|
|
<div class="mb-4 flex items-center gap-4">
|
|
<Label class="flex items-center gap-2 cursor-pointer">
|
|
<input
|
|
type="radio"
|
|
value="numbers"
|
|
v-model="createMode"
|
|
class="rounded-full"
|
|
/>
|
|
Vnos številk
|
|
</Label>
|
|
<Label class="flex items-center gap-2 cursor-pointer">
|
|
<input
|
|
type="radio"
|
|
value="contracts"
|
|
v-model="createMode"
|
|
class="rounded-full"
|
|
/>
|
|
Iz pogodb (segment)
|
|
</Label>
|
|
</div>
|
|
|
|
<div class="grid sm:grid-cols-3 gap-4">
|
|
<div class="space-y-2">
|
|
<Label>Profil</Label>
|
|
<Select v-model="form.profile_id">
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="—" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem :value="null">—</SelectItem>
|
|
<SelectItem v-for="p in profiles" :key="p.id" :value="p.id">{{
|
|
p.name
|
|
}}</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label>Pošiljatelj</Label>
|
|
<Select v-model="form.sender_id">
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="—" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem :value="null">—</SelectItem>
|
|
<SelectItem v-for="s in filteredSenders" :key="s.id" :value="s.id">
|
|
{{ s.sname }} <span v-if="s.phone_number">({{ s.phone_number }})</span>
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label>Predloga</Label>
|
|
<Select v-model="form.template_id" @update:model-value="onTemplateChange">
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="—" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem :value="null">—</SelectItem>
|
|
<SelectItem v-for="t in templates" :key="t.id" :value="t.id">{{
|
|
t.name
|
|
}}</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div class="sm:col-span-3 space-y-2">
|
|
<Label>Vsebina (če ni predloge)</Label>
|
|
<Textarea v-model="form.body" rows="3" placeholder="Sporočilo..." />
|
|
<div class="flex items-center gap-2">
|
|
<Checkbox
|
|
:checked="form.delivery_report"
|
|
@update:checked="(val) => (form.delivery_report = val)"
|
|
id="delivery-report"
|
|
/>
|
|
<Label for="delivery-report" class="cursor-pointer"
|
|
>Zahtevaj delivery report</Label
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Numbers mode -->
|
|
<template v-if="createMode === 'numbers'">
|
|
<div class="sm:col-span-3 space-y-2">
|
|
<Label>Telefonske številke (ena na vrstico)</Label>
|
|
<Textarea
|
|
v-model="form.numbers"
|
|
rows="4"
|
|
placeholder="+38640123456 +38640123457"
|
|
/>
|
|
</div>
|
|
<div class="sm:col-span-3 flex items-center justify-end gap-2">
|
|
<Button @click="submitCreate"> Ustvari paket </Button>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Contracts mode -->
|
|
<template v-else>
|
|
<div class="sm:col-span-3 space-y-4">
|
|
<Separator />
|
|
<!-- Basic filters -->
|
|
<div class="grid sm:grid-cols-3 gap-4">
|
|
<div class="space-y-2">
|
|
<Label>Segment</Label>
|
|
<Select v-model="segmentId" @update:model-value="loadContracts()">
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Vsi segmenti" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem :value="null">Vsi segmenti</SelectItem>
|
|
<SelectItem v-for="s in segments" :key="s.id" :value="s.id">{{
|
|
s.name
|
|
}}</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label>Stranka</Label>
|
|
<Select v-model="clientId" @update:model-value="loadContracts()">
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Vse stranke" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem :value="null">Vse stranke</SelectItem>
|
|
<SelectItem v-for="c in clients" :key="c.id" :value="c.id">{{
|
|
c.name
|
|
}}</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label>Iskanje po referenci</Label>
|
|
<Input
|
|
v-model="search"
|
|
@keyup.enter="loadContracts()"
|
|
type="text"
|
|
placeholder="Vnesi referenco..."
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Date range filters -->
|
|
<Separator />
|
|
<div>
|
|
<h4 class="text-sm font-semibold mb-3">Datumski filtri</h4>
|
|
<div class="space-y-4">
|
|
<div>
|
|
<div class="text-sm font-medium text-muted-foreground mb-2">
|
|
Datum začetka pogodbe
|
|
</div>
|
|
<div class="grid grid-cols-2 gap-2">
|
|
<div class="space-y-2">
|
|
<Label>Od</Label>
|
|
<Input
|
|
v-model="startDateFrom"
|
|
@change="loadContracts()"
|
|
type="date"
|
|
/>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label>Do</Label>
|
|
<Input
|
|
v-model="startDateTo"
|
|
@change="loadContracts()"
|
|
type="date"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-sm font-medium text-muted-foreground mb-2">
|
|
Datum obljube plačila
|
|
</div>
|
|
<div class="grid grid-cols-2 gap-2">
|
|
<div class="space-y-2">
|
|
<Label>Od</Label>
|
|
<Input
|
|
v-model="promiseDateFrom"
|
|
@change="loadContracts()"
|
|
type="date"
|
|
/>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label>Do</Label>
|
|
<Input
|
|
v-model="promiseDateTo"
|
|
@change="loadContracts()"
|
|
type="date"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Phone filters -->
|
|
<Separator />
|
|
<div>
|
|
<h4 class="text-sm font-semibold mb-3">Telefonski filtri</h4>
|
|
<div class="flex items-center gap-6">
|
|
<div class="flex items-center gap-2">
|
|
<Checkbox
|
|
:checked="onlyMobile"
|
|
@update:checked="
|
|
(val) => {
|
|
onlyMobile = val;
|
|
loadContracts();
|
|
}
|
|
"
|
|
id="only-mobile"
|
|
/>
|
|
<Label for="only-mobile" class="cursor-pointer"
|
|
>Samo mobilne številke</Label
|
|
>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<Checkbox
|
|
:checked="onlyValidated"
|
|
@update:checked="
|
|
(val) => {
|
|
onlyValidated = val;
|
|
loadContracts();
|
|
}
|
|
"
|
|
id="only-validated"
|
|
/>
|
|
<Label for="only-validated" class="cursor-pointer"
|
|
>Samo potrjene številke</Label
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action buttons -->
|
|
<div class="flex items-center gap-2">
|
|
<Button @click="loadContracts()">
|
|
<SearchIcon class="h-4 w-4 mr-2" />
|
|
Išči pogodbe
|
|
</Button>
|
|
<Button
|
|
@click="
|
|
segmentId = null;
|
|
clientId = null;
|
|
search = '';
|
|
startDateFrom = '';
|
|
startDateTo = '';
|
|
promiseDateFrom = '';
|
|
promiseDateTo = '';
|
|
onlyMobile = false;
|
|
onlyValidated = false;
|
|
contracts.value = {
|
|
data: [],
|
|
meta: { current_page: 1, last_page: 1, per_page: 25, total: 0 },
|
|
};
|
|
"
|
|
variant="outline"
|
|
>
|
|
Počisti filtre
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Results table -->
|
|
<div class="sm:col-span-3">
|
|
<Card>
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>
|
|
<input
|
|
type="checkbox"
|
|
:checked="allCurrentPageSelected"
|
|
:indeterminate="someCurrentPageSelected"
|
|
@change="toggleSelectAll"
|
|
:disabled="!contracts.data.length"
|
|
class="rounded"
|
|
title="Izberi vse na tej strani"
|
|
/>
|
|
</TableHead>
|
|
<TableHead>Pogodba</TableHead>
|
|
<TableHead>Primer</TableHead>
|
|
<TableHead>Stranka</TableHead>
|
|
<TableHead>Datum začetka</TableHead>
|
|
<TableHead>Zadnja obljuba</TableHead>
|
|
<TableHead>Izbrana številka</TableHead>
|
|
<TableHead>Opomba</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody v-if="!loadingContracts">
|
|
<TableRow v-for="c in contracts.data" :key="c.id">
|
|
<TableCell>
|
|
<input
|
|
type="checkbox"
|
|
:checked="selectedContractIds.has(c.id)"
|
|
@change="toggleSelectContract(c.id)"
|
|
class="rounded"
|
|
/>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div class="font-mono text-xs text-muted-foreground">
|
|
{{ c.uuid }}
|
|
</div>
|
|
<a
|
|
v-if="c.case?.uuid"
|
|
:href="route('clientCase.show', c.case.uuid)"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="text-xs font-medium text-primary hover:underline"
|
|
>
|
|
{{ c.reference }}
|
|
</a>
|
|
<div v-else class="text-xs font-medium">{{ c.reference }}</div>
|
|
</TableCell>
|
|
<TableCell class="text-xs">
|
|
{{ c.person?.full_name || "—" }}
|
|
</TableCell>
|
|
<TableCell class="text-xs">{{ c.client?.name || "—" }}</TableCell>
|
|
<TableCell class="text-xs">{{
|
|
c.start_date
|
|
? new Date(c.start_date).toLocaleDateString("sl-SI")
|
|
: "—"
|
|
}}</TableCell>
|
|
<TableCell class="text-xs">{{
|
|
c.promise_date
|
|
? new Date(c.promise_date).toLocaleDateString("sl-SI")
|
|
: "—"
|
|
}}</TableCell>
|
|
<TableCell>
|
|
<div v-if="c.selected_phone" class="text-xs">
|
|
{{ c.selected_phone.number }}
|
|
<Badge
|
|
v-if="c.selected_phone.is_mobile"
|
|
variant="secondary"
|
|
class="ml-1"
|
|
>mobitel</Badge
|
|
>
|
|
<Badge
|
|
v-if="c.selected_phone.is_validated"
|
|
variant="default"
|
|
class="ml-1"
|
|
>potrjen</Badge
|
|
>
|
|
</div>
|
|
<div v-else class="text-xs text-muted-foreground">—</div>
|
|
</TableCell>
|
|
<TableCell class="text-xs text-muted-foreground">
|
|
{{ c.no_phone_reason || "—" }}
|
|
</TableCell>
|
|
</TableRow>
|
|
<TableRow v-if="!contracts.data?.length">
|
|
<TableCell colspan="8" class="text-center text-muted-foreground h-24">
|
|
Ni rezultatov. Kliknite "Išči pogodbe" za prikaz.
|
|
</TableCell>
|
|
</TableRow>
|
|
</TableBody>
|
|
<TableBody v-else>
|
|
<TableRow
|
|
><TableCell colspan="8" class="text-center text-muted-foreground h-24"
|
|
>Nalaganje...</TableCell
|
|
></TableRow
|
|
>
|
|
</TableBody>
|
|
</Table>
|
|
</Card>
|
|
<div class="mt-3 flex items-center justify-between">
|
|
<div class="text-sm text-muted-foreground flex items-center gap-4">
|
|
<span v-if="contracts.data.length">
|
|
Prikazano stran {{ contracts.meta.current_page }} od
|
|
{{ contracts.meta.last_page }} (skupaj {{ contracts.meta.total }})
|
|
</span>
|
|
<div class="flex items-center gap-2">
|
|
<Label class="text-xs">Na stran:</Label>
|
|
<Select v-model="perPage" @update:model-value="loadContracts()">
|
|
<SelectTrigger class="w-20 h-8">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem :value="10">10</SelectItem>
|
|
<SelectItem :value="25">25</SelectItem>
|
|
<SelectItem :value="50">50</SelectItem>
|
|
<SelectItem :value="100">100</SelectItem>
|
|
<SelectItem :value="200">200</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<Button
|
|
@click="goContractsPage(-1)"
|
|
:disabled="contracts.meta.current_page <= 1"
|
|
variant="outline"
|
|
size="sm"
|
|
>Nazaj</Button
|
|
>
|
|
<Button
|
|
@click="goContractsPage(1)"
|
|
:disabled="contracts.meta.current_page >= contracts.meta.last_page"
|
|
variant="outline"
|
|
size="sm"
|
|
>Naprej</Button
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Separator class="sm:col-span-3" />
|
|
<div class="sm:col-span-3 flex items-center justify-between gap-2">
|
|
<div class="text-sm">
|
|
<span class="font-medium">Izbrano: {{ selectedContractIds.size }}</span>
|
|
<span v-if="selectedContractIds.size > 0" class="ml-2 text-muted-foreground"
|
|
>({{
|
|
selectedContractIds.size === 1
|
|
? "1 pogodba"
|
|
: `${selectedContractIds.size} pogodb`
|
|
}})</span
|
|
>
|
|
</div>
|
|
<Button
|
|
@click="submitCreateFromContracts"
|
|
:disabled="selectedContractIds.size === 0 || creatingFromContracts"
|
|
>Ustvari paket</Button
|
|
>
|
|
</div>
|
|
</template>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<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">
|
|
<PackageIcon size="18" />
|
|
<CardTitle class="uppercase">Uvozi</CardTitle>
|
|
</div>
|
|
</template>
|
|
<DataTableNew2
|
|
:columns="columns"
|
|
:data="packages.data"
|
|
:meta="packages"
|
|
route-name="admin.packages.index"
|
|
>
|
|
<template #cell-uuid="{ row }">
|
|
<span class="font-mono text-xs text-muted-foreground">{{ row.uuid }}</span>
|
|
</template>
|
|
|
|
<template #cell-name="{ row }">
|
|
<span class="text-sm">{{ row.name ?? "—" }}</span>
|
|
</template>
|
|
|
|
<template #cell-type="{ row }">
|
|
<Badge variant="outline" class="uppercase">{{ row.type }}</Badge>
|
|
</template>
|
|
|
|
<template #cell-status="{ row }">
|
|
<Badge :variant="getStatusVariant(row.status)">{{ row.status }}</Badge>
|
|
</template>
|
|
|
|
<template #cell-finished_at="{ row }">
|
|
<span class="text-xs text-muted-foreground">{{ row.finished_at ?? "—" }}</span>
|
|
</template>
|
|
|
|
<template #cell-actions="{ row }">
|
|
<div class="flex justify-end gap-2">
|
|
<Button @click="goShow(row.id)" variant="ghost" size="sm">
|
|
<EyeIcon class="h-4 w-4" />
|
|
</Button>
|
|
<Button
|
|
v-if="row.status === 'draft'"
|
|
@click="deletePackage(row)"
|
|
:disabled="deletingId === row.id"
|
|
variant="ghost"
|
|
size="sm"
|
|
>
|
|
<Trash2Icon class="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</template>
|
|
</DataTableNew2>
|
|
</AppCard>
|
|
</AdminLayout>
|
|
</template>
|