790 lines
27 KiB
Vue
790 lines
27 KiB
Vue
<script setup>
|
|
import { ref, computed, useSlots, watch, onMounted } from "vue";
|
|
import { router } from "@inertiajs/vue3";
|
|
import DataTable from "@/Components/DataTable/DataTableNew2.vue";
|
|
import DeleteDialog from "@/Components/Dialogs/DeleteDialog.vue";
|
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
|
import { library } from "@fortawesome/fontawesome-svg-core";
|
|
import { faTrash, faEllipsisVertical, faCopy } from "@fortawesome/free-solid-svg-icons";
|
|
import Dropdown from "@/Components/Dropdown.vue";
|
|
import { Button } from "@/Components/ui/button";
|
|
import TableActions from "@/Components/DataTable/TableActions.vue";
|
|
import ActionMenuItem from "@/Components/DataTable/ActionMenuItem.vue";
|
|
import {
|
|
Command,
|
|
CommandEmpty,
|
|
CommandGroup,
|
|
CommandInput,
|
|
CommandItem,
|
|
CommandList,
|
|
} from "@/Components/ui/command";
|
|
import { Popover, PopoverContent, PopoverTrigger } from "@/Components/ui/popover";
|
|
import { RangeCalendar } from "@/Components/ui/range-calendar";
|
|
import { CalendarIcon, X, Filter, Check, ChevronsUpDown } from "lucide-vue-next";
|
|
import { cn } from "@/lib/utils";
|
|
import { DateFormatter, getLocalTimeZone, parseDate } from "@internationalized/date";
|
|
|
|
library.add(faTrash, faEllipsisVertical, faCopy);
|
|
|
|
const props = defineProps({
|
|
client_case: Object,
|
|
activities: Object,
|
|
edit: Boolean,
|
|
actions: Array,
|
|
contracts: [Object, Array],
|
|
pageSize: {
|
|
type: Number,
|
|
default: 20,
|
|
},
|
|
});
|
|
|
|
const slots = useSlots();
|
|
|
|
// Filter state
|
|
const filters = ref({
|
|
activity_action_id: null,
|
|
activity_contract_uuid: null,
|
|
activity_user_id: null,
|
|
activity_date_from: null,
|
|
activity_date_to: null,
|
|
});
|
|
|
|
// Date range for calendar
|
|
const dateRange = ref(undefined);
|
|
|
|
// Filter popover state
|
|
const filterPopoverOpen = ref(false);
|
|
const actionComboboxOpen = ref(false);
|
|
const contractComboboxOpen = ref(false);
|
|
const userComboboxOpen = ref(false);
|
|
|
|
// Date formatter
|
|
const df = new DateFormatter("sl-SI", {
|
|
dateStyle: "medium",
|
|
});
|
|
|
|
// Get unique users from activities for filter dropdown
|
|
const uniqueUsers = computed(() => {
|
|
const users = new Map();
|
|
if (props.activities?.data) {
|
|
props.activities.data.forEach((activity) => {
|
|
if (activity.user) {
|
|
users.set(activity.user.id, activity.user);
|
|
}
|
|
});
|
|
}
|
|
return Array.from(users.values());
|
|
});
|
|
|
|
// Get contract options - handle both array and object with data property
|
|
const contractOptions = computed(() => {
|
|
if (!props.contracts) return [];
|
|
if (Array.isArray(props.contracts)) {
|
|
return props.contracts;
|
|
}
|
|
if (props.contracts.data && Array.isArray(props.contracts.data)) {
|
|
return props.contracts.data;
|
|
}
|
|
return [];
|
|
});
|
|
|
|
// Get action options with their decisions
|
|
const actionOptions = computed(() => {
|
|
if (!props.actions || !Array.isArray(props.actions)) return [];
|
|
return props.actions;
|
|
});
|
|
|
|
// Check if any filters are active
|
|
const hasActiveFilters = computed(() => {
|
|
return Object.values(filters.value).some((val) => val !== null && val !== "");
|
|
});
|
|
|
|
// Selected items for combobox display
|
|
const selectedAction = computed(() =>
|
|
actionOptions.value.find((a) => a.id === filters.value.activity_action_id)
|
|
);
|
|
|
|
const selectedContract = computed(() =>
|
|
contractOptions.value.find((c) => c.uuid === filters.value.activity_contract_uuid)
|
|
);
|
|
|
|
const selectedUser = computed(() =>
|
|
uniqueUsers.value.find((u) => u.id === filters.value.activity_user_id)
|
|
);
|
|
|
|
// Apply filters
|
|
const applyFilters = () => {
|
|
// Sync date range to filter values
|
|
if (dateRange.value?.start) {
|
|
filters.value.activity_date_from = dateRange.value.start.toString();
|
|
}
|
|
if (dateRange.value?.end) {
|
|
filters.value.activity_date_to = dateRange.value.end.toString();
|
|
}
|
|
|
|
// Build filter object with only non-null values
|
|
const filterObj = {};
|
|
if (filters.value.activity_action_id) {
|
|
filterObj.action_id = filters.value.activity_action_id;
|
|
}
|
|
if (filters.value.activity_contract_uuid) {
|
|
filterObj.contract_uuid = filters.value.activity_contract_uuid;
|
|
}
|
|
if (filters.value.activity_user_id) {
|
|
filterObj.user_id = filters.value.activity_user_id;
|
|
}
|
|
if (filters.value.activity_date_from) {
|
|
filterObj.date_from = filters.value.activity_date_from;
|
|
}
|
|
if (filters.value.activity_date_to) {
|
|
filterObj.date_to = filters.value.activity_date_to;
|
|
}
|
|
|
|
console.log("Applying filters:", filterObj);
|
|
|
|
// Build query params object
|
|
const queryParams = {};
|
|
|
|
// Preserve existing query params (like segment)
|
|
const searchParams = new URLSearchParams(window.location.search);
|
|
for (const [key, value] of searchParams.entries()) {
|
|
if (key !== "filter_activities") {
|
|
queryParams[key] = value;
|
|
}
|
|
}
|
|
|
|
// Only add filter param if there are active filters
|
|
if (Object.keys(filterObj).length > 0) {
|
|
const compressed = btoa(JSON.stringify(filterObj));
|
|
console.log("Compressed filter:", compressed);
|
|
queryParams.filter_activities = compressed;
|
|
}
|
|
|
|
console.log("Query params:", queryParams);
|
|
|
|
router.get(
|
|
route("clientCase.show", {
|
|
client_case: props.client_case.uuid,
|
|
...queryParams,
|
|
}),
|
|
{},
|
|
{
|
|
only: ["activities"],
|
|
preserveState: true,
|
|
preserveScroll: true,
|
|
}
|
|
);
|
|
filterPopoverOpen.value = false;
|
|
};
|
|
|
|
// Clear all filters
|
|
const clearFilters = () => {
|
|
filters.value = {
|
|
activity_action_id: null,
|
|
activity_contract_uuid: null,
|
|
activity_user_id: null,
|
|
activity_date_from: null,
|
|
activity_date_to: null,
|
|
};
|
|
dateRange.value = undefined;
|
|
filterPopoverOpen.value = false;
|
|
applyFilters();
|
|
};
|
|
|
|
// Load filters from URL on mount
|
|
onMounted(() => {
|
|
const searchParams = new URLSearchParams(window.location.search);
|
|
const filterParam = searchParams.get("filter_activities");
|
|
|
|
if (filterParam) {
|
|
try {
|
|
const decompressed = atob(filterParam);
|
|
const filterObj = JSON.parse(decompressed);
|
|
|
|
if (filterObj) {
|
|
filters.value.activity_action_id = filterObj.action_id || null;
|
|
filters.value.activity_contract_uuid = filterObj.contract_uuid || null;
|
|
filters.value.activity_user_id = filterObj.user_id || null;
|
|
filters.value.activity_date_from = filterObj.date_from || null;
|
|
filters.value.activity_date_to = filterObj.date_to || null;
|
|
|
|
// Parse dates for calendar
|
|
if (filterObj.date_from && filterObj.date_to) {
|
|
try {
|
|
dateRange.value = {
|
|
start: parseDate(filterObj.date_from),
|
|
end: parseDate(filterObj.date_to),
|
|
};
|
|
} catch (e) {
|
|
console.error("Failed to parse dates:", e);
|
|
}
|
|
} else if (filterObj.date_from) {
|
|
try {
|
|
dateRange.value = {
|
|
start: parseDate(filterObj.date_from),
|
|
end: parseDate(filterObj.date_from),
|
|
};
|
|
} catch (e) {
|
|
console.error("Failed to parse date_from:", e);
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error("Failed to load filters from URL:", e);
|
|
}
|
|
}
|
|
});
|
|
|
|
const columns = [
|
|
{ key: "decision_dot", label: "", sortable: false, align: "center" },
|
|
{ key: "contract", label: "Pogodba", sortable: false },
|
|
{ key: "decision", label: "Odločitev", sortable: false },
|
|
{ key: "note", label: "Opomba", sortable: false },
|
|
{ key: "promise", label: "Obljuba", sortable: false },
|
|
{ key: "user", label: "Dodal", sortable: false },
|
|
{ key: "actions", label: "", sortable: false, hideable: false, align: "center" },
|
|
];
|
|
|
|
const rows = computed(() => props.activities?.data || []);
|
|
|
|
const fmtDate = (d) => {
|
|
if (!d) return "";
|
|
try {
|
|
return new Date(d).toLocaleDateString("sl-SI");
|
|
} catch (e) {
|
|
return String(d);
|
|
}
|
|
};
|
|
|
|
const fmtDateTime = (d) => {
|
|
if (!d) return "";
|
|
try {
|
|
const dt = new Date(d);
|
|
const datePart = dt.toLocaleDateString("sl-SI", {
|
|
year: "numeric",
|
|
month: "2-digit",
|
|
day: "2-digit",
|
|
});
|
|
const timePart = dt.toLocaleTimeString("sl-SI", {
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
hour12: false,
|
|
});
|
|
return `${datePart} ${timePart}`;
|
|
} catch (e) {
|
|
return String(d);
|
|
}
|
|
};
|
|
|
|
const fmtCurrency = (v) => {
|
|
const n = Number(v ?? 0);
|
|
try {
|
|
return new Intl.NumberFormat("sl-SI", { style: "currency", currency: "EUR" }).format(
|
|
n
|
|
);
|
|
} catch {
|
|
return `${n.toFixed(2)} €`;
|
|
}
|
|
};
|
|
|
|
const deleteActivity = (row) => {
|
|
if (!row?.id) return;
|
|
router.delete(
|
|
route("clientCase.activity.delete", {
|
|
client_case: props.client_case.uuid,
|
|
activity: row.id,
|
|
}),
|
|
{ preserveScroll: true }
|
|
);
|
|
};
|
|
|
|
const confirmDelete = ref(false);
|
|
const toDeleteRow = ref(null);
|
|
|
|
const openDelete = (row) => {
|
|
toDeleteRow.value = row;
|
|
confirmDelete.value = true;
|
|
};
|
|
|
|
const cancelDelete = () => {
|
|
confirmDelete.value = false;
|
|
toDeleteRow.value = null;
|
|
};
|
|
|
|
const confirmDeleteAction = () => {
|
|
if (toDeleteRow.value) deleteActivity(toDeleteRow.value);
|
|
cancelDelete();
|
|
};
|
|
|
|
const copyToClipboard = async (text) => {
|
|
try {
|
|
await navigator.clipboard.writeText(text);
|
|
} catch (err) {
|
|
const textArea = document.createElement("textarea");
|
|
textArea.value = text;
|
|
document.body.appendChild(textArea);
|
|
textArea.select();
|
|
document.execCommand("copy");
|
|
document.body.removeChild(textArea);
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="space-y-4">
|
|
<DataTable
|
|
:columns="columns"
|
|
:data="rows"
|
|
:meta="activities"
|
|
route-name="clientCase.show"
|
|
:route-params="{ client_case: client_case.uuid }"
|
|
:only-props="['activities']"
|
|
:page-size="pageSize"
|
|
:page-size-options="[10, 15, 25, 50, 100]"
|
|
page-param-name="activities_page"
|
|
per-page-param-name="activities_per_page"
|
|
:show-pagination="false"
|
|
:show-toolbar="true"
|
|
:hoverable="true"
|
|
row-key="id"
|
|
empty-text="Ni aktivnosti."
|
|
>
|
|
<template #toolbar-filters>
|
|
<!-- Filter Popover -->
|
|
<Popover v-model:open="filterPopoverOpen">
|
|
<PopoverTrigger as-child>
|
|
<Button variant="outline" size="sm" class="gap-2">
|
|
<Filter class="h-4 w-4" />
|
|
Filtri
|
|
<span
|
|
v-if="hasActiveFilters"
|
|
class="ml-1 rounded-full bg-primary px-2 py-0.5 text-xs text-primary-foreground"
|
|
>
|
|
{{ Object.values(filters).filter((v) => v !== null && v !== "").length }}
|
|
</span>
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent class="w-[400px]" align="start">
|
|
<div class="space-y-4">
|
|
<div class="space-y-2">
|
|
<h4 class="font-medium text-sm">Filtri aktivnosti</h4>
|
|
<p class="text-sm text-muted-foreground">
|
|
Izberite filtre za prikaz aktivnosti
|
|
</p>
|
|
</div>
|
|
|
|
<div class="space-y-3">
|
|
<!-- Action Filter -->
|
|
<div class="space-y-1.5">
|
|
<label class="text-sm font-medium">Akcija</label>
|
|
<Popover v-model:open="actionComboboxOpen">
|
|
<PopoverTrigger as-child>
|
|
<Button
|
|
variant="outline"
|
|
role="combobox"
|
|
:aria-expanded="actionComboboxOpen"
|
|
class="w-full justify-between"
|
|
>
|
|
{{ selectedAction?.name || "Vse akcije" }}
|
|
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent class="w-full p-0" align="start">
|
|
<Command>
|
|
<CommandInput placeholder="Išči akcijo..." />
|
|
<CommandList>
|
|
<CommandEmpty>Akcija ni najdena.</CommandEmpty>
|
|
<CommandGroup>
|
|
<CommandItem
|
|
:value="'null'"
|
|
@select="
|
|
() => {
|
|
filters.activity_action_id = null;
|
|
actionComboboxOpen = false;
|
|
}
|
|
"
|
|
>
|
|
<Check
|
|
:class="
|
|
cn(
|
|
'mr-2 h-4 w-4',
|
|
filters.activity_action_id === null
|
|
? 'opacity-100'
|
|
: 'opacity-0'
|
|
)
|
|
"
|
|
/>
|
|
Vse akcije
|
|
</CommandItem>
|
|
<CommandItem
|
|
v-for="action in actionOptions"
|
|
:key="action.id"
|
|
:value="action.name"
|
|
@select="
|
|
() => {
|
|
filters.activity_action_id = action.id;
|
|
actionComboboxOpen = false;
|
|
}
|
|
"
|
|
>
|
|
<Check
|
|
:class="
|
|
cn(
|
|
'mr-2 h-4 w-4',
|
|
filters.activity_action_id === action.id
|
|
? 'opacity-100'
|
|
: 'opacity-0'
|
|
)
|
|
"
|
|
/>
|
|
{{ action.name }}
|
|
</CommandItem>
|
|
</CommandGroup>
|
|
</CommandList>
|
|
</Command>
|
|
</PopoverContent>
|
|
</Popover>
|
|
</div>
|
|
|
|
<!-- Contract Filter -->
|
|
<div class="space-y-1.5">
|
|
<label class="text-sm font-medium">Pogodba</label>
|
|
<Popover v-model:open="contractComboboxOpen">
|
|
<PopoverTrigger as-child>
|
|
<Button
|
|
variant="outline"
|
|
role="combobox"
|
|
:aria-expanded="contractComboboxOpen"
|
|
class="w-full justify-between"
|
|
>
|
|
{{ selectedContract?.reference || "Vse pogodbe" }}
|
|
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent class="w-full p-0" align="start">
|
|
<Command>
|
|
<CommandInput placeholder="Išči pogodbo..." />
|
|
<CommandList>
|
|
<CommandEmpty>Pogodba ni najdena.</CommandEmpty>
|
|
<CommandGroup>
|
|
<CommandItem
|
|
:value="'null'"
|
|
@select="
|
|
() => {
|
|
filters.activity_contract_uuid = null;
|
|
contractComboboxOpen = false;
|
|
}
|
|
"
|
|
>
|
|
<Check
|
|
:class="
|
|
cn(
|
|
'mr-2 h-4 w-4',
|
|
filters.activity_contract_uuid === null
|
|
? 'opacity-100'
|
|
: 'opacity-0'
|
|
)
|
|
"
|
|
/>
|
|
Vse pogodbe
|
|
</CommandItem>
|
|
<CommandItem
|
|
v-for="contract in contractOptions"
|
|
:key="contract.uuid"
|
|
:value="contract.reference"
|
|
@select="
|
|
() => {
|
|
filters.activity_contract_uuid = contract.uuid;
|
|
contractComboboxOpen = false;
|
|
}
|
|
"
|
|
>
|
|
<Check
|
|
:class="
|
|
cn(
|
|
'mr-2 h-4 w-4',
|
|
filters.activity_contract_uuid === contract.uuid
|
|
? 'opacity-100'
|
|
: 'opacity-0'
|
|
)
|
|
"
|
|
/>
|
|
{{ contract.reference }}
|
|
</CommandItem>
|
|
</CommandGroup>
|
|
</CommandList>
|
|
</Command>
|
|
</PopoverContent>
|
|
</Popover>
|
|
</div>
|
|
|
|
<!-- User Filter -->
|
|
<div class="space-y-1.5">
|
|
<label class="text-sm font-medium">Uporabnik</label>
|
|
<Popover v-model:open="userComboboxOpen">
|
|
<PopoverTrigger as-child>
|
|
<Button
|
|
variant="outline"
|
|
role="combobox"
|
|
:aria-expanded="userComboboxOpen"
|
|
class="w-full justify-between"
|
|
>
|
|
{{ selectedUser?.name || "Vsi uporabniki" }}
|
|
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent class="w-full p-0" align="start">
|
|
<Command>
|
|
<CommandInput placeholder="Išči uporabnika..." />
|
|
<CommandList>
|
|
<CommandEmpty>Uporabnik ni najden.</CommandEmpty>
|
|
<CommandGroup>
|
|
<CommandItem
|
|
:value="'null'"
|
|
@select="
|
|
() => {
|
|
filters.activity_user_id = null;
|
|
userComboboxOpen = false;
|
|
}
|
|
"
|
|
>
|
|
<Check
|
|
:class="
|
|
cn(
|
|
'mr-2 h-4 w-4',
|
|
filters.activity_user_id === null
|
|
? 'opacity-100'
|
|
: 'opacity-0'
|
|
)
|
|
"
|
|
/>
|
|
Vsi uporabniki
|
|
</CommandItem>
|
|
<CommandItem
|
|
v-for="user in uniqueUsers"
|
|
:key="user.id"
|
|
:value="user.name"
|
|
@select="
|
|
() => {
|
|
filters.activity_user_id = user.id;
|
|
userComboboxOpen = false;
|
|
}
|
|
"
|
|
>
|
|
<Check
|
|
:class="
|
|
cn(
|
|
'mr-2 h-4 w-4',
|
|
filters.activity_user_id === user.id
|
|
? 'opacity-100'
|
|
: 'opacity-0'
|
|
)
|
|
"
|
|
/>
|
|
{{ user.name }}
|
|
</CommandItem>
|
|
</CommandGroup>
|
|
</CommandList>
|
|
</Command>
|
|
</PopoverContent>
|
|
</Popover>
|
|
</div>
|
|
|
|
<!-- Date Range -->
|
|
<div class="space-y-1.5">
|
|
<label class="text-sm font-medium">Časovno obdobje</label>
|
|
<Popover>
|
|
<PopoverTrigger as-child>
|
|
<Button
|
|
variant="outline"
|
|
:class="[
|
|
'w-full justify-start text-left font-normal',
|
|
!dateRange?.start && 'text-muted-foreground',
|
|
]"
|
|
>
|
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
|
<template v-if="dateRange?.start">
|
|
<template v-if="dateRange?.end">
|
|
{{ df.format(dateRange.start.toDate(getLocalTimeZone())) }}
|
|
-
|
|
{{ df.format(dateRange.end.toDate(getLocalTimeZone())) }}
|
|
</template>
|
|
<template v-else>
|
|
{{ df.format(dateRange.start.toDate(getLocalTimeZone())) }}
|
|
</template>
|
|
</template>
|
|
<template v-else> Izberite obdobje </template>
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent class="w-auto p-0" align="start">
|
|
<RangeCalendar
|
|
v-model="dateRange"
|
|
:number-of-months="2"
|
|
locale="sl-SI"
|
|
/>
|
|
</PopoverContent>
|
|
</Popover>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Actions -->
|
|
<div class="flex items-center justify-between pt-2 border-t">
|
|
<Button
|
|
v-if="hasActiveFilters"
|
|
variant="ghost"
|
|
size="sm"
|
|
@click="clearFilters"
|
|
class="gap-2"
|
|
>
|
|
<X class="h-4 w-4" />
|
|
Počisti
|
|
</Button>
|
|
<div v-else></div>
|
|
<Button variant="default" size="sm" @click="applyFilters" class="gap-2">
|
|
Uporabi
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</PopoverContent>
|
|
</Popover>
|
|
</template>
|
|
|
|
<template #toolbar-actions>
|
|
<slot name="add" />
|
|
</template>
|
|
|
|
<template #cell-decision_dot="{ row }">
|
|
<div class="flex justify-center">
|
|
<span
|
|
v-if="row.decision?.color_tag"
|
|
class="inline-block h-4 w-4 rounded-full ring-1 ring-gray-300"
|
|
:style="{ backgroundColor: row.decision?.color_tag }"
|
|
:title="row.decision?.color_tag"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<template #cell-contract="{ row }">
|
|
<span v-if="row.contract?.reference">{{ row.contract.reference }}</span>
|
|
<span v-else class="text-gray-400">—</span>
|
|
</template>
|
|
|
|
<template #cell-decision="{ row }">
|
|
<div class="flex flex-col gap-1">
|
|
<span
|
|
v-if="row.action?.name"
|
|
class="inline-block w-fit px-2 py-0.5 rounded text-[10px] font-medium bg-indigo-100 text-indigo-700 tracking-wide uppercase"
|
|
>
|
|
{{ row.action.name }}
|
|
</span>
|
|
<span class="text-gray-800">{{ row.decision?.name || "" }}</span>
|
|
</div>
|
|
</template>
|
|
|
|
<template #cell-note="{ row }">
|
|
<div class="max-w-[280px] whitespace-pre-wrap wrap-break-word leading-snug">
|
|
<template v-if="row.note && row.note.length <= 60">
|
|
{{ row.note }}
|
|
</template>
|
|
<template v-else-if="row.note">
|
|
<span>{{ row.note.slice(0, 60) }}… </span>
|
|
<Dropdown
|
|
align="left"
|
|
width="56"
|
|
:content-classes="['p-2', 'bg-white', 'shadow', 'max-w-xs']"
|
|
>
|
|
<template #trigger>
|
|
<button
|
|
type="button"
|
|
class="inline-flex items-center text-[11px] text-indigo-600 hover:underline"
|
|
>
|
|
Več
|
|
</button>
|
|
</template>
|
|
<template #content>
|
|
<div class="relative" @click.stop>
|
|
<div
|
|
class="flex items-center justify-between p-1 border-b border-gray-200"
|
|
>
|
|
<span class="text-xs font-medium text-gray-600">Opomba</span>
|
|
<button
|
|
@click="copyToClipboard(row.note)"
|
|
class="flex items-center gap-1 px-2 py-1 text-xs text-indigo-600 hover:text-indigo-800 hover:bg-indigo-50 rounded transition-colors"
|
|
title="Kopiraj v odložišče"
|
|
>
|
|
<FontAwesomeIcon :icon="faCopy" class="w-3 h-3" />
|
|
<span>Kopiraj</span>
|
|
</button>
|
|
</div>
|
|
<div
|
|
class="max-h-60 overflow-auto text-[12px] whitespace-pre-wrap wrap-break-word p-2"
|
|
>
|
|
{{ row.note }}
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</Dropdown>
|
|
</template>
|
|
<template v-else>
|
|
<span class="text-gray-400">—</span>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<template #cell-promise="{ row }">
|
|
<div class="flex flex-col gap-1 text-[12px]">
|
|
<div v-if="row.amount && Number(row.amount) !== 0" class="leading-tight">
|
|
<span class="text-gray-500">Z:</span>
|
|
<span class="font-medium ml-1">{{ fmtCurrency(row.amount) }}</span>
|
|
</div>
|
|
<div v-if="row.due_date" class="leading-tight">
|
|
<span class="text-gray-500">D:</span>
|
|
<span class="ml-1">{{ fmtDate(row.due_date) }}</span>
|
|
</div>
|
|
<div
|
|
v-if="!row.due_date && (!row.amount || Number(row.amount) === 0)"
|
|
class="text-gray-400"
|
|
>
|
|
—
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template #cell-user="{ row }">
|
|
<div class="text-gray-800 font-medium leading-tight">
|
|
{{ row.user?.name || row.user_name || "" }}
|
|
</div>
|
|
<div v-if="row.created_at" class="mt-1">
|
|
<span
|
|
class="inline-block px-2 py-0.5 rounded text-[11px] bg-gray-100 text-gray-600 tracking-wide"
|
|
>
|
|
{{ fmtDateTime(row.created_at) }}
|
|
</span>
|
|
</div>
|
|
</template>
|
|
|
|
<template #cell-actions="{ row }" v-if="edit">
|
|
<TableActions align="right">
|
|
<template #default>
|
|
<ActionMenuItem
|
|
:icon="faTrash"
|
|
label="Izbriši"
|
|
danger
|
|
@click="openDelete(row)"
|
|
/>
|
|
</template>
|
|
</TableActions>
|
|
</template>
|
|
</DataTable>
|
|
</div>
|
|
|
|
<DeleteDialog
|
|
:show="confirmDelete"
|
|
title="Izbriši aktivnost"
|
|
message="Ali ste prepričani, da želite izbrisati to aktivnost?"
|
|
confirm-text="Izbriši"
|
|
@close="cancelDelete"
|
|
@confirm="confirmDeleteAction"
|
|
/>
|
|
</template>
|