652 lines
22 KiB
Vue
652 lines
22 KiB
Vue
<script setup>
|
|
import AppLayout from "@/Layouts/AppLayout.vue";
|
|
import { Link, useForm, router } from "@inertiajs/vue3";
|
|
import { computed, ref, watch } from "vue";
|
|
import DataTable from "@/Components/DataTable/DataTableNew2.vue";
|
|
import { Card, CardTitle } from "@/Components/ui/card";
|
|
import { Input } from "@/Components/ui/input";
|
|
import { Button } from "@/Components/ui/button";
|
|
import { Label } from "@/Components/ui/label";
|
|
import { Badge } from "@/Components/ui/badge";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/Components/ui/select";
|
|
import { AlertCircle, FileCheckCornerIcon, FilesIcon, Filter } from "lucide-vue-next";
|
|
import Checkbox from "@/Components/ui/checkbox/Checkbox.vue";
|
|
import Pagination from "@/Components/Pagination.vue";
|
|
import { watchDebounced } from "@vueuse/core";
|
|
import Dropdown from "@/Components/Dropdown.vue";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from "@/Components/ui/dropdown-menu";
|
|
import AppPopover from "@/Components/app/ui/AppPopover.vue";
|
|
import InputLabel from "@/Components/InputLabel.vue";
|
|
import AppMultiSelect from "@/Components/app/ui/AppMultiSelect.vue";
|
|
import AppCard from "@/Components/app/ui/card/AppCard.vue";
|
|
|
|
const props = defineProps({
|
|
setting: Object,
|
|
unassignedContracts: Object,
|
|
assignedContracts: Object,
|
|
users: Array,
|
|
unassignedClients: Array,
|
|
assignedClients: Array,
|
|
filters: Object,
|
|
});
|
|
|
|
const filterUnassignedContracts = ref(false);
|
|
const filterAssignedContracts = ref(false);
|
|
const filterUnassignedSelectedClient = ref(
|
|
Array.isArray(props.filters?.unassigned_client_uuids)
|
|
? props.filters.unassigned_client_uuids
|
|
: []
|
|
);
|
|
const filterAssignedSelectedClient = ref(
|
|
Array.isArray(props.filters?.assigned_client_uuids)
|
|
? props.filters.assigned_client_uuids
|
|
: []
|
|
);
|
|
|
|
const form = useForm({
|
|
contract_uuid: null,
|
|
assigned_user_id: null,
|
|
start_date: null,
|
|
end_date: null,
|
|
});
|
|
|
|
// Bulk selection form
|
|
const bulkForm = useForm({
|
|
contract_uuids: [],
|
|
assigned_user_id: null,
|
|
});
|
|
|
|
// Separate reactive state for selected UUIDs (for UI reactivity)
|
|
const selectedContractUuids = ref([]);
|
|
|
|
// Select all state for unassigned table (current page only)
|
|
const isAllUnassignedSelected = computed({
|
|
get: () => {
|
|
const pageUuids = props.unassignedContracts?.data?.map((c) => c.uuid) || [];
|
|
return (
|
|
pageUuids.length > 0 &&
|
|
pageUuids.every((uuid) => selectedContractUuids.value.includes(uuid))
|
|
);
|
|
},
|
|
set: (value) => {
|
|
const pageUuids = props.unassignedContracts?.data?.map((c) => c.uuid) || [];
|
|
if (value) {
|
|
// Add all page items to selection
|
|
selectedContractUuids.value = [
|
|
...new Set([...selectedContractUuids.value, ...pageUuids]),
|
|
];
|
|
} else {
|
|
// Remove all page items from selection
|
|
selectedContractUuids.value = selectedContractUuids.value.filter(
|
|
(uuid) => !pageUuids.includes(uuid)
|
|
);
|
|
}
|
|
},
|
|
});
|
|
|
|
// Helper to toggle contract selection
|
|
function toggleContractSelection(uuid, checked) {
|
|
if (checked) {
|
|
if (!selectedContractUuids.value.includes(uuid)) {
|
|
selectedContractUuids.value = [...selectedContractUuids.value, uuid];
|
|
}
|
|
} else {
|
|
selectedContractUuids.value = selectedContractUuids.value.filter((id) => id !== uuid);
|
|
}
|
|
console.log(selectedContractUuids.value);
|
|
}
|
|
|
|
// Format helpers (Slovenian formatting)
|
|
|
|
// Initialize search and filter from URL params
|
|
const search = ref(props.filters?.search || "");
|
|
const searchAssigned = ref(props.filters?.search_assigned || "");
|
|
const assignedFilterUserId = ref(props.filters?.assigned_user_id || "all");
|
|
|
|
// Navigation helpers
|
|
function navigateWithParams(params) {
|
|
router.visit(route("fieldjobs.index"), {
|
|
data: params,
|
|
preserveState: true,
|
|
preserveScroll: true,
|
|
only: ["unassignedContracts", "assignedContracts", "filters"],
|
|
});
|
|
}
|
|
|
|
const applySearch = async function () {
|
|
const params = Object.fromEntries(
|
|
new URLSearchParams(window.location.search).entries()
|
|
);
|
|
|
|
const term = (search.value || "").trim();
|
|
if (term) {
|
|
params.search = term;
|
|
} else {
|
|
delete params.search;
|
|
}
|
|
delete params.page;
|
|
router.get(route("fieldjobs.index"), params, {
|
|
preserveState: true,
|
|
replace: true,
|
|
preserveScroll: true,
|
|
only: ["unassignedContracts"],
|
|
});
|
|
};
|
|
|
|
watchDebounced(
|
|
() => search.value,
|
|
(val) => {
|
|
applySearch();
|
|
},
|
|
{
|
|
debounce: 200,
|
|
maxWait: 1000,
|
|
}
|
|
);
|
|
|
|
const applySearchAssigned = async function () {
|
|
const params = Object.fromEntries(
|
|
new URLSearchParams(window.location.search).entries()
|
|
);
|
|
|
|
const term = (searchAssigned.value || "").trim();
|
|
if (term) {
|
|
params.search_assigned = term;
|
|
} else {
|
|
delete params.search_assigned;
|
|
}
|
|
delete params.page_assignments;
|
|
router.get(route("fieldjobs.index"), params, {
|
|
preserveState: true,
|
|
replace: true,
|
|
preserveScroll: true,
|
|
only: ["assignedContracts", "filters"],
|
|
});
|
|
};
|
|
|
|
watchDebounced(
|
|
() => searchAssigned.value,
|
|
(val) => {
|
|
applySearchAssigned();
|
|
},
|
|
{
|
|
debounce: 200,
|
|
maxWait: 1000,
|
|
}
|
|
);
|
|
|
|
// Watch search and filter changes
|
|
/*watch(search, (value) => {
|
|
navigateWithParams({
|
|
search: value || undefined,
|
|
assigned_user_id:
|
|
assignedFilterUserId.value !== "all" ? assignedFilterUserId.value : undefined,
|
|
page_contracts: 1, // Reset to first page on search
|
|
page_assignments: 1,
|
|
});
|
|
});*/
|
|
|
|
watch(assignedFilterUserId, (value) => {
|
|
navigateWithParams({
|
|
search: search.value || undefined,
|
|
assigned_user_id: value !== "all" ? value : undefined,
|
|
unassigned_client_uuids:
|
|
filterUnassignedSelectedClient.value?.length > 0
|
|
? filterUnassignedSelectedClient.value
|
|
: undefined,
|
|
assigned_client_uuids:
|
|
filterAssignedSelectedClient.value?.length > 0
|
|
? filterAssignedSelectedClient.value
|
|
: undefined,
|
|
page_contracts: props.unassignedContracts?.current_page,
|
|
page_assignments: 1, // Reset to first page on filter change
|
|
});
|
|
});
|
|
|
|
watch(filterUnassignedSelectedClient, (value) => {
|
|
navigateWithParams({
|
|
search: search.value || undefined,
|
|
assigned_user_id:
|
|
assignedFilterUserId.value !== "all" ? assignedFilterUserId.value : undefined,
|
|
unassigned_client_uuids: value?.length > 0 ? value : undefined,
|
|
assigned_client_uuids:
|
|
filterAssignedSelectedClient.value?.length > 0
|
|
? filterAssignedSelectedClient.value
|
|
: undefined,
|
|
page_contracts: 1, // Reset to first page on filter change
|
|
page_assignments: props.assignedContracts?.current_page,
|
|
});
|
|
});
|
|
|
|
watch(filterAssignedSelectedClient, (value) => {
|
|
navigateWithParams({
|
|
search: search.value || undefined,
|
|
assigned_user_id:
|
|
assignedFilterUserId.value !== "all" ? assignedFilterUserId.value : undefined,
|
|
unassigned_client_uuids:
|
|
filterUnassignedSelectedClient.value?.length > 0
|
|
? filterUnassignedSelectedClient.value
|
|
: undefined,
|
|
assigned_client_uuids: value?.length > 0 ? value : undefined,
|
|
page_contracts: props.unassignedContracts?.current_page,
|
|
page_assignments: 1, // Reset to first page on filter change
|
|
});
|
|
});
|
|
|
|
function formatDate(value) {
|
|
if (!value) {
|
|
return "-";
|
|
}
|
|
const d = new Date(value);
|
|
if (isNaN(d)) {
|
|
return value;
|
|
}
|
|
const dd = String(d.getDate()).padStart(2, "0");
|
|
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
|
const yyyy = d.getFullYear();
|
|
return `${dd}.${mm}.${yyyy}`;
|
|
}
|
|
|
|
function formatCurrencyEUR(value) {
|
|
if (value === null || value === undefined) {
|
|
return "-";
|
|
}
|
|
const n = Number(value);
|
|
if (isNaN(n)) {
|
|
return String(value);
|
|
}
|
|
// Thousands separator as dot, decimal as comma, with € suffix
|
|
return (
|
|
n.toLocaleString("sl-SI", { minimumFractionDigits: 2, maximumFractionDigits: 2 }) +
|
|
" €"
|
|
);
|
|
}
|
|
|
|
function primaryCaseAddress(contract) {
|
|
const addrs = contract?.client_case?.person?.addresses || [];
|
|
if (!Array.isArray(addrs) || addrs.length === 0) {
|
|
return "-";
|
|
}
|
|
const a = addrs[0];
|
|
const address = a?.address || "";
|
|
const country = a?.country || "";
|
|
return [address, country].filter(Boolean).join(", ");
|
|
}
|
|
|
|
function assign(contract) {
|
|
form.contract_uuid = contract.uuid;
|
|
// minimal UX: if no user selected yet, just post will fail with error; page can be enhanced later with dropdown.
|
|
form.post(route("fieldjobs.assign"));
|
|
}
|
|
|
|
function assignSelected() {
|
|
// Use the same selected user as in the single-assign dropdown
|
|
bulkForm.assigned_user_id = form.assigned_user_id;
|
|
bulkForm.contract_uuids = selectedContractUuids.value;
|
|
bulkForm.post(route("fieldjobs.assign-bulk"), {
|
|
onSuccess: () => {
|
|
selectedContractUuids.value = [];
|
|
bulkForm.contract_uuids = [];
|
|
},
|
|
});
|
|
}
|
|
|
|
function cancelAssignment(contract) {
|
|
const payload = { contract_uuid: contract.uuid };
|
|
form.transform(() => payload).post(route("fieldjobs.cancel"));
|
|
}
|
|
|
|
// Column definitions for DataTableNew2
|
|
const unassignedColumns = [
|
|
{
|
|
key: "_select",
|
|
label: "",
|
|
sortable: false,
|
|
class: "w-8",
|
|
},
|
|
{ key: "reference", label: "Pogodba", sortable: false },
|
|
{ key: "case_person", label: "Primer", sortable: false },
|
|
{ key: "address", label: "Naslov", sortable: false },
|
|
{ key: "client_person", label: "Stranka", sortable: false },
|
|
{ key: "start_date", label: "Začetek", sortable: false },
|
|
{ key: "balance_amount", label: "Stanje", sortable: false, align: "right" },
|
|
{ key: "_actions", label: "Dejanje", sortable: false },
|
|
];
|
|
|
|
const assignedColumns = [
|
|
{ key: "reference", label: "Pogodba", sortable: false },
|
|
{ key: "case_person", label: "Primer", sortable: false },
|
|
{ key: "address", label: "Naslov", sortable: false },
|
|
{ key: "client_person", label: "Stranka", sortable: false },
|
|
{ key: "assigned_at", label: "Dodeljeno dne", sortable: false },
|
|
{ key: "assigned_to", label: "Dodeljeno komu", sortable: false },
|
|
{ key: "balance_amount", label: "Stanje", sortable: false, align: "right" },
|
|
{ key: "_actions", label: "Dejanje", sortable: false },
|
|
];
|
|
|
|
// Prepare rows with flattened fields for display
|
|
const unassignedRows = computed(() =>
|
|
(props.unassignedContracts?.data || []).map((c) => ({
|
|
...c,
|
|
balance_amount:
|
|
c?.account?.balance_amount === null || c?.account?.balance_amount === undefined
|
|
? null
|
|
: Number(c.account.balance_amount),
|
|
case_person: c.client_case?.person?.full_name || null,
|
|
client_person: c.client_case?.client?.person?.full_name || null,
|
|
address: primaryCaseAddress(c) || null,
|
|
}))
|
|
);
|
|
|
|
const assignedRows = computed(() =>
|
|
(props.assignedContracts?.data || []).map((c) => ({
|
|
...c,
|
|
balance_amount:
|
|
c?.account?.balance_amount === null || c?.account?.balance_amount === undefined
|
|
? null
|
|
: Number(c.account.balance_amount),
|
|
case_person: c.client_case?.person?.full_name || null,
|
|
client_person: c.client_case?.client?.person?.full_name || null,
|
|
address: primaryCaseAddress(c) || null,
|
|
assigned_to: c.last_field_jobs.assigned_user.name || null,
|
|
assigned_at_formatted: formatDate(c.last_field_jobs.assigned_at),
|
|
}))
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<AppLayout title="Dodeljevanje terenskih opravil">
|
|
<template #header></template>
|
|
<div class="pt-6">
|
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
|
<AppCard
|
|
title=""
|
|
padding="none"
|
|
class="p-0! gap-0"
|
|
header-class="p-3! px-4 gap-0 text-muted-foreground"
|
|
body-class=""
|
|
>
|
|
<template #header>
|
|
<div class="flex items-center gap-2">
|
|
<FilesIcon size="18" />
|
|
<CardTitle class="uppercase">Pogodbe (nedodeljene)</CardTitle>
|
|
</div>
|
|
</template>
|
|
<div
|
|
v-if="!setting"
|
|
class="mb-6 flex items-start gap-3 rounded-lg border border-yellow-200 bg-yellow-50 p-4"
|
|
>
|
|
<AlertCircle class="h-5 w-5 text-yellow-600 shrink-0 mt-0.5" />
|
|
<p class="text-sm text-yellow-800">
|
|
Nastavitev za terenska opravila ni najdena. Najprej jo ustvarite v
|
|
Nastavitve → Nastavitve terenskih opravil.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Unassigned (Assignable) Contracts -->
|
|
<div class="overflow-hidden mb-8 border-t">
|
|
<div class="p-4 flex flex-row gap-2 items-end">
|
|
<div class="space-y-2">
|
|
<Label for="assign-user">Dodeli uporabniku</Label>
|
|
<Select v-model="form.assigned_user_id">
|
|
<SelectTrigger id="assign-user" class="max-w-xs min-w-60">
|
|
<SelectValue placeholder="Izberite uporabnika" />
|
|
</SelectTrigger>
|
|
<SelectContent class="min-w-60">
|
|
<SelectItem v-for="u in users || []" :key="u.id" :value="u.id">
|
|
{{ u.name }}
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<div v-if="form.errors.assigned_user_id" class="text-red-600 text-sm">
|
|
{{ form.errors.assigned_user_id }}
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<Button
|
|
:disabled="!selectedContractUuids.length || !form.assigned_user_id"
|
|
@click="assignSelected"
|
|
>
|
|
Dodeli izbrane
|
|
<Badge
|
|
v-if="selectedContractUuids.length"
|
|
variant="secondary"
|
|
class="ml-2"
|
|
>
|
|
{{ selectedContractUuids.length }}
|
|
</Badge>
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
:disabled="!selectedContractUuids.length"
|
|
@click="selectedContractUuids = []"
|
|
>
|
|
Počisti izbor
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<DataTable
|
|
:columns="unassignedColumns"
|
|
:data="unassignedRows"
|
|
:meta="{
|
|
current_page: unassignedContracts.current_page,
|
|
per_page: unassignedContracts.per_page,
|
|
total: unassignedContracts.total,
|
|
last_page: unassignedContracts.last_page,
|
|
from: unassignedContracts.from,
|
|
to: unassignedContracts.to,
|
|
links: unassignedContracts.links,
|
|
}"
|
|
row-key="uuid"
|
|
:page-size="props.unassignedContracts?.per_page || 10"
|
|
:page-size-options="[10, 15, 25, 50, 100]"
|
|
:show-toolbar="true"
|
|
route-name="fieldjobs.index"
|
|
page-param-name="page_contracts"
|
|
per-page-param-name="per_page_contracts"
|
|
>
|
|
<template #toolbar-filters>
|
|
<div class="flex items-center gap-2 w-full">
|
|
<AppPopover
|
|
v-model:open="filterUnassignedContracts"
|
|
align="start"
|
|
content-class="w-[350px]"
|
|
>
|
|
<template #trigger>
|
|
<Button variant="outline"
|
|
><Filter class="h-4 w-4" /> Filtri
|
|
</Button>
|
|
</template>
|
|
<div class="space-y-3">
|
|
<div class="space-y-1.5">
|
|
<InputLabel>Iskanje</InputLabel>
|
|
<Input
|
|
v-model="search"
|
|
placeholder="Išči po pogodbi, primeru, stranki, naslovu..."
|
|
class="w-full"
|
|
/>
|
|
</div>
|
|
<div class="space-y-1.5">
|
|
<InputLabel>Stranke</InputLabel>
|
|
<AppMultiSelect
|
|
v-model="filterUnassignedSelectedClient"
|
|
:items="
|
|
(props.unassignedClients || []).map((client) => ({
|
|
value: client.uuid,
|
|
label: client.person.full_name,
|
|
}))
|
|
"
|
|
placeholder="Vse stranke"
|
|
search-placeholder="Išči stranko..."
|
|
empty-text="Ni strank"
|
|
chip-variant="secondary"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</AppPopover>
|
|
</div>
|
|
</template>
|
|
|
|
<template #cell-_select="{ row }">
|
|
<Checkbox
|
|
@update:model-value="
|
|
(checked) => toggleContractSelection(row.uuid, checked)
|
|
"
|
|
/>
|
|
</template>
|
|
<template #cell-case_person="{ row }">
|
|
<Link
|
|
v-if="row.client_case?.uuid"
|
|
:href="route('clientCase.show', { client_case: row.client_case.uuid })"
|
|
class="font-semibold hover:underline text-primary-700"
|
|
>
|
|
{{ row.client_case?.person?.full_name || "Primer stranke" }}
|
|
</Link>
|
|
<span v-else>{{ row.client_case?.person?.full_name || "-" }}</span>
|
|
</template>
|
|
<template #cell-start_date="{ row }">
|
|
{{ formatDate(row.start_date) }}
|
|
</template>
|
|
<template #cell-balance_amount="{ row }">
|
|
<div class="text-right">
|
|
{{ formatCurrencyEUR(row.account?.balance_amount) }}
|
|
</div>
|
|
</template>
|
|
<template #cell-_actions="{ row }">
|
|
<Button size="sm" @click="assign(row)">Dodeli</Button>
|
|
</template>
|
|
</DataTable>
|
|
</div>
|
|
</AppCard>
|
|
<!-- Assigned Contracts -->
|
|
<AppCard
|
|
title=""
|
|
padding="none"
|
|
class="p-0! gap-0"
|
|
header-class="p-3! px-4 gap-0 text-muted-foreground"
|
|
body-class=""
|
|
>
|
|
<template #header>
|
|
<div class="flex items-center gap-2">
|
|
<FileCheckCornerIcon size="18" />
|
|
<CardTitle class="uppercase">Dodeljene pogodbe</CardTitle>
|
|
</div>
|
|
</template>
|
|
<DataTable
|
|
:columns="assignedColumns"
|
|
:data="assignedRows"
|
|
:meta="{
|
|
current_page: assignedContracts.current_page,
|
|
per_page: assignedContracts.per_page,
|
|
total: assignedContracts.total,
|
|
last_page: assignedContracts.last_page,
|
|
from: assignedContracts.from,
|
|
to: assignedContracts.to,
|
|
links: assignedContracts.links,
|
|
}"
|
|
row-key="uuid"
|
|
:page-size="props.assignedContracts?.per_page || 10"
|
|
:page-size-options="[10, 15, 25, 50, 100]"
|
|
:show-toolbar="true"
|
|
route-name="fieldjobs.index"
|
|
page-param-name="page_assignments"
|
|
per-page-param-name="per_page_assignments"
|
|
>
|
|
<template #toolbar-filters>
|
|
<div class="flex items-center gap-2 w-full">
|
|
<AppPopover
|
|
v-model:open="filterAssignedContracts"
|
|
align="start"
|
|
content-class="w-[350px]"
|
|
>
|
|
<template #trigger>
|
|
<Button variant="outline"><Filter class="h-4 w-4" /> Filtri </Button>
|
|
</template>
|
|
<div class="space-y-3">
|
|
<div class="space-y-1.5">
|
|
<InputLabel>Iskanje</InputLabel>
|
|
<Input
|
|
v-model="searchAssigned"
|
|
placeholder="Išči po pogodbi, primeru, stranki, naslovu..."
|
|
class="w-full"
|
|
/>
|
|
</div>
|
|
<div class="space-y-1.5">
|
|
<InputLabel>Filter po uporabniku</InputLabel>
|
|
<Select v-model="assignedFilterUserId">
|
|
<SelectTrigger id="filter-user">
|
|
<SelectValue placeholder="Vsi" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="all">Vsi</SelectItem>
|
|
<SelectItem
|
|
v-for="u in users || []"
|
|
:key="u.id"
|
|
:value="String(u.id)"
|
|
>
|
|
{{ u.name }}
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div class="space-y-1.5">
|
|
<InputLabel>Stranke</InputLabel>
|
|
<AppMultiSelect
|
|
v-model="filterAssignedSelectedClient"
|
|
:items="
|
|
(props.assignedClients || []).map((client) => ({
|
|
value: client.uuid,
|
|
label: client.person.full_name,
|
|
}))
|
|
"
|
|
placeholder="Vse stranke"
|
|
search-placeholder="Išči stranko..."
|
|
empty-text="Ni strank"
|
|
chip-variant="secondary"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</AppPopover>
|
|
</div>
|
|
</template>
|
|
<template #cell-case_person="{ row }">
|
|
<Link
|
|
v-if="row.client_case?.uuid"
|
|
:href="route('clientCase.show', { client_case: row.client_case.uuid })"
|
|
class="font-semibold hover:underline text-primary-700"
|
|
>
|
|
{{ row.client_case?.person?.full_name || "Primer stranke" }}
|
|
</Link>
|
|
<span v-else>{{ row.client_case?.person?.full_name || "-" }}</span>
|
|
</template>
|
|
<template #cell-assigned_at="{ row }">
|
|
{{ row.assigned_at_formatted }}
|
|
</template>
|
|
<template #cell-balance_amount="{ row }">
|
|
<div class="text-right">
|
|
{{ formatCurrencyEUR(row.account?.balance_amount) }}
|
|
</div>
|
|
</template>
|
|
<template #cell-_actions="{ row }">
|
|
<Button variant="destructive" size="sm" @click="cancelAssignment(row)">
|
|
Prekliči
|
|
</Button>
|
|
</template>
|
|
</DataTable>
|
|
</AppCard>
|
|
</div>
|
|
</div>
|
|
</AppLayout>
|
|
</template>
|