Mass changes
This commit is contained in:
@@ -5,14 +5,6 @@ import Dropdown from "@/Components/Dropdown.vue";
|
||||
import ConfirmationModal from "@/Components/ConfirmationModal.vue";
|
||||
import SecondaryButton from "@/Components/SecondaryButton.vue";
|
||||
import DangerButton from "@/Components/DangerButton.vue";
|
||||
import {
|
||||
FwbTable,
|
||||
FwbTableHead,
|
||||
FwbTableHeadCell,
|
||||
FwbTableBody,
|
||||
FwbTableRow,
|
||||
FwbTableCell,
|
||||
} from "flowbite-vue";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||
import { faTrash, faEllipsisVertical } from "@fortawesome/free-solid-svg-icons";
|
||||
@@ -24,8 +16,6 @@ const props = defineProps({
|
||||
activities: Object,
|
||||
});
|
||||
|
||||
// Dropdown component manages its own open/close state
|
||||
|
||||
const fmtDate = (d) => {
|
||||
if (!d) return "";
|
||||
try {
|
||||
@@ -34,6 +24,25 @@ const fmtDate = (d) => {
|
||||
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 {
|
||||
@@ -58,7 +67,7 @@ const deleteActivity = (row) => {
|
||||
);
|
||||
};
|
||||
|
||||
// Confirmation modal state and handlers
|
||||
// Confirmation modal state
|
||||
const confirmDelete = ref(false);
|
||||
const toDeleteRow = ref(null);
|
||||
const openDelete = (row) => {
|
||||
@@ -70,89 +79,147 @@ const cancelDelete = () => {
|
||||
toDeleteRow.value = null;
|
||||
};
|
||||
const confirmDeleteAction = () => {
|
||||
if (toDeleteRow.value) {
|
||||
deleteActivity(toDeleteRow.value);
|
||||
}
|
||||
if (toDeleteRow.value) deleteActivity(toDeleteRow.value);
|
||||
confirmDelete.value = false;
|
||||
toDeleteRow.value = null;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="overflow-x-auto">
|
||||
<FwbTable hoverable striped class="min-w-full text-left text-sm">
|
||||
<FwbTableHead>
|
||||
<FwbTableHeadCell class="py-2 pr-4">Pogodba</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="py-2 pr-4">Datum</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="py-2 pr-4">Akcija</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="py-2 pr-4">Odločitev</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="py-2 pr-4">Opomba</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="py-2 pr-4">Datum zapadlosti</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="py-2 pr-4 text-right">Znesek obljube</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="py-2 pr-4">Dodal</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="py-2 pl-2 pr-2 w-8 text-right"></FwbTableHeadCell>
|
||||
</FwbTableHead>
|
||||
|
||||
<FwbTableBody>
|
||||
<FwbTableRow v-for="row in activities.data" :key="row.id" class="border-b">
|
||||
<FwbTableCell class="py-2 pr-4">{{
|
||||
row.contract?.reference || ""
|
||||
}}</FwbTableCell>
|
||||
<FwbTableCell class="py-2 pr-4">{{ fmtDate(row.created_at) }}</FwbTableCell>
|
||||
<FwbTableCell class="py-2 pr-4">{{ row.action?.name || "" }}</FwbTableCell>
|
||||
<FwbTableCell class="py-2 pr-4">{{ row.decision?.name || "" }}</FwbTableCell>
|
||||
<FwbTableCell class="py-2 pr-4">{{ row.note || "" }}</FwbTableCell>
|
||||
<FwbTableCell class="py-2 pr-4">{{ fmtDate(row.due_date) }}</FwbTableCell>
|
||||
<FwbTableCell class="py-2 pr-4 text-right">{{
|
||||
fmtCurrency(row.amount)
|
||||
}}</FwbTableCell>
|
||||
<FwbTableCell class="py-2 pr-4">{{
|
||||
row.user?.name || row.user_name || ""
|
||||
}}</FwbTableCell>
|
||||
<FwbTableCell class="py-2 pl-2 pr-2 text-right">
|
||||
<Dropdown align="right" width="30" :content-classes="['py-1', 'bg-white']">
|
||||
<template #trigger>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center w-8 h-8 rounded hover:bg-gray-100"
|
||||
aria-haspopup="menu"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', 'ellipsis-vertical']"
|
||||
class="text-gray-600 text-[20px]"
|
||||
/>
|
||||
</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="text-[16px]" />
|
||||
<span>Izbriši</span>
|
||||
</button>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</FwbTableCell>
|
||||
</FwbTableRow>
|
||||
|
||||
<FwbTableRow v-if="!activities?.data || activities.data.length === 0">
|
||||
<FwbTableCell :colspan="9" class="py-4 text-gray-500"
|
||||
>Ni aktivnosti.</FwbTableCell
|
||||
<div class="relative">
|
||||
<div class="activity-scroll-wrapper max-h-[32rem] overflow-y-auto overflow-x-auto">
|
||||
<table
|
||||
class="activity-basic-table min-w-full table-fixed text-left text-sm border-collapse"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Pogodba</th>
|
||||
<th>Odločitev</th>
|
||||
<th>Opomba</th>
|
||||
<th>Obljuba</th>
|
||||
<th>Dodal</th>
|
||||
<th class="w-8"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="row in activities.data"
|
||||
:key="row.id"
|
||||
class="border-b last:border-b-0"
|
||||
>
|
||||
</FwbTableRow>
|
||||
</FwbTableBody>
|
||||
</FwbTable>
|
||||
<td class="py-2 pr-4 align-top">{{ row.contract?.reference || "" }}</td>
|
||||
<td class="py-2 pr-4 align-top">
|
||||
<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>
|
||||
</td>
|
||||
<td class="py-2 pr-4 align-top">
|
||||
<div class="max-w-[280px] whitespace-pre-wrap break-words leading-snug">
|
||||
<template v-if="row.note && row.note.length <= 160">{{
|
||||
row.note
|
||||
}}</template>
|
||||
<template v-else-if="row.note">
|
||||
<span>{{ row.note.slice(0, 160) }}… </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 focus:outline-none"
|
||||
>
|
||||
Več
|
||||
</button>
|
||||
</template>
|
||||
<template #content>
|
||||
<div
|
||||
class="max-h-60 overflow-auto text-[12px] whitespace-pre-wrap break-words"
|
||||
>
|
||||
{{ row.note }}
|
||||
</div>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</template>
|
||||
<template v-else><span class="text-gray-400">—</span></template>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-2 pr-4 align-top">
|
||||
<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>
|
||||
</td>
|
||||
<td class="py-2 pr-4 align-top">
|
||||
<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>
|
||||
</td>
|
||||
<td class="py-2 pl-2 pr-2 align-top text-right">
|
||||
<Dropdown align="right" width="30" :content-classes="['py-1', 'bg-white']">
|
||||
<template #trigger>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center w-8 h-8 rounded hover:bg-gray-100"
|
||||
aria-haspopup="menu"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', 'ellipsis-vertical']"
|
||||
class="text-gray-600 text-[20px]"
|
||||
/>
|
||||
</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="text-[16px]" />
|
||||
<span>Izbriši</span>
|
||||
</button>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="!activities?.data || activities.data.length === 0">
|
||||
<td :colspan="6" class="py-4 text-gray-500">Ni aktivnosti.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confirm deletion modal -->
|
||||
<ConfirmationModal :show="confirmDelete" @close="cancelDelete">
|
||||
<template #title>Potrditev</template>
|
||||
<template #content>
|
||||
Ali ste prepričani, da želite izbrisati to aktivnost? Tega dejanja ni mogoče
|
||||
razveljaviti.
|
||||
</template>
|
||||
<template #content
|
||||
>Ali ste prepričani, da želite izbrisati to aktivnost? Tega dejanja ni mogoče
|
||||
razveljaviti.</template
|
||||
>
|
||||
<template #footer>
|
||||
<SecondaryButton type="button" @click="cancelDelete">Prekliči</SecondaryButton>
|
||||
<DangerButton type="button" class="ml-2" @click="confirmDeleteAction"
|
||||
@@ -161,3 +228,59 @@ const confirmDeleteAction = () => {
|
||||
</template>
|
||||
</ConfirmationModal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.activity-scroll-wrapper {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
.activity-basic-table thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 20;
|
||||
background: #ffffff;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.05em;
|
||||
color: #374151;
|
||||
padding: 0.5rem 1rem; /* unified horizontal padding */
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
.activity-basic-table tbody td {
|
||||
vertical-align: top;
|
||||
padding: 0.625rem 1rem; /* match header horizontal padding */
|
||||
}
|
||||
/* Ensure first column lines up exactly (no extra offset) */
|
||||
.activity-basic-table th:first-child,
|
||||
.activity-basic-table td:first-child {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.activity-basic-table tbody tr:hover {
|
||||
background: #f9fafb;
|
||||
}
|
||||
/* Column sizing hints (optional fine tuning) */
|
||||
.activity-basic-table th:nth-child(1),
|
||||
.activity-basic-table td:nth-child(1) {
|
||||
width: 14%;
|
||||
}
|
||||
.activity-basic-table th:nth-child(2),
|
||||
.activity-basic-table td:nth-child(2) {
|
||||
width: 16%;
|
||||
}
|
||||
.activity-basic-table th:nth-child(3),
|
||||
.activity-basic-table td:nth-child(3) {
|
||||
width: 26%;
|
||||
}
|
||||
.activity-basic-table th:nth-child(4),
|
||||
.activity-basic-table td:nth-child(4) {
|
||||
width: 14%;
|
||||
}
|
||||
.activity-basic-table th:nth-child(5),
|
||||
.activity-basic-table td:nth-child(5) {
|
||||
width: 20%;
|
||||
}
|
||||
.activity-basic-table th:nth-child(6),
|
||||
.activity-basic-table td:nth-child(6) {
|
||||
width: 10%;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user