Teren-app/resources/js/Pages/Cases/Partials/ActivityTable.vue
Simon Pocrnjič b7fa2d261b changes UI
2025-11-04 18:53:23 +01:00

260 lines
8.4 KiB
Vue

<script setup>
import { ref, computed } from "vue";
import { router } from "@inertiajs/vue3";
import DataTable from "@/Components/DataTable/DataTable.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";
library.add(faTrash, faEllipsisVertical, faCopy);
const props = defineProps({
client_case: Object,
activities: Object,
edit: Boolean,
});
const columns = [
{ key: "decision_dot", label: " ", class: "w-[6%]" },
{ key: "contract", label: "Pogodba", class: "w-[14%]" },
{ key: "decision", label: "Odločitev", class: "w-[26%]" },
{ key: "note", label: "Opomba", class: "w-[14%]" },
{ key: "promise", label: "Obljuba", class: "w-[20%]" },
{ key: "user", label: "Dodal", class: "w-[10%]" },
];
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="p-4">
<DataTable
:columns="columns"
:rows="rows"
:show-toolbar="true"
:show-pagination="false"
:show-search="false"
:show-page-size="false"
:show-add="!!$slots.add"
:hoverable="true"
row-key="id"
empty-text="Ni aktivnosti."
class="border-0"
>
<template #toolbar-add>
<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 break-words 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 break-words 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 #actions="{ row }" v-if="edit">
<Dropdown align="right" width="30">
<template #trigger>
<button
type="button"
class="inline-flex items-center justify-center h-8 w-8 rounded-full hover:bg-gray-100 focus:outline-none"
title="Možnosti"
>
<FontAwesomeIcon :icon="faEllipsisVertical" class="h-4 w-4 text-gray-700" />
</button>
</template>
<template #content>
<button
type="button"
class="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-red-50 text-red-600"
@click.stop="openDelete(row)"
>
<FontAwesomeIcon :icon="['fas', 'trash']" class="w-4 h-4" />
<span>Izbriši</span>
</button>
</template>
</Dropdown>
</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>