Dev branch

This commit is contained in:
Simon Pocrnjič
2025-11-02 12:31:01 +01:00
parent 5f879c9436
commit 63e0958b66
241 changed files with 17686 additions and 7327 deletions
@@ -1,10 +1,9 @@
<script setup>
import { ref } from "vue";
import { ref, computed } from "vue";
import { Link, router } from "@inertiajs/vue3";
import DataTable from "@/Components/DataTable/DataTable.vue";
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 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";
@@ -17,6 +16,17 @@ const props = defineProps({
edit: Boolean,
});
const columns = computed(() => [
{ 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 {
@@ -105,236 +115,212 @@ const copyToClipboard = async (text) => {
<template>
<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"
<DataTable
:columns="columns"
:rows="rows"
:show-toolbar="true"
:show-pagination="false"
:show-search="false"
:show-page-size="false"
:hoverable="true"
row-key="id"
empty-text="Ni aktivnosti."
class="border-0"
>
<thead>
<tr>
<th></th>
<th>Pogodba</th>
<th>Odločitev</th>
<th>Opomba</th>
<th>Obljuba</th>
<th>Dodal</th>
<th class="w-8" v-if="edit"></th>
</tr>
</thead>
<tbody>
<tr
v-for="row in activities.data"
:key="row.id"
class="border-b last:border-b-0"
>
<td class="py-2 decision-dot text-center">
<span
v-if="row.decision?.color_tag"
class="inline-block h-4 w-4 rounded-full ring-1 ring-gray-300 dark:ring-gray-600"
:style="{ backgroundColor: row.decision?.color_tag }"
:title="row.decision?.color_tag"
aria-hidden="true"
></span>
</td>
<td class="py-2 pr-4 align-top">
<template v-if="row.contract?.reference">
{{ row.contract.reference }}
</template>
<template v-else>
<span class="text-gray-400"></span>
</template>
</td>
<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"
aria-hidden="true"
></span>
</div>
</template>
<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 <= 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 focus:outline-none"
>
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="text-xs" />
<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>
</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-middle text-right" v-if="edit">
<Dropdown align="right" width="30">
<template #cell-contract="{ row }">
<template v-if="row.contract?.reference">
{{ row.contract.reference }}
</template>
<template v-else>
<span class="text-gray-400"></span>
</template>
</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 justify-center h-8 w-8 rounded-full hover:bg-gray-100 focus:outline-none"
:title="'Actions'"
class="inline-flex items-center text-[11px] text-indigo-600 hover:underline focus:outline-none"
>
<FontAwesomeIcon
:icon="faEllipsisVertical"
class="h-4 w-4 text-gray-700"
/>
Več
</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>
<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>
</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>
</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="Actions"
>
<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>
</div>
<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 #footer>
<SecondaryButton type="button" @click="cancelDelete">Prekliči</SecondaryButton>
<DangerButton type="button" class="ml-2" @click="confirmDeleteAction"
>Izbriši</DangerButton
>
</template>
</ConfirmationModal>
<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>
<style scoped>
.activity-scroll-wrapper {
scrollbar-gutter: stable;
}
.activity-basic-table thead th {
/* Ensure sticky header works within scroll container */
.activity-scroll-wrapper :deep(table) {
border-collapse: separate;
border-spacing: 0;
}
.activity-scroll-wrapper :deep([data-table-container]) {
overflow: visible !important;
}
.activity-scroll-wrapper :deep([data-table-container] > div) {
overflow-x: visible !important;
overflow-y: visible !important;
}
.activity-scroll-wrapper :deep(table thead) {
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 */
}
.activity-scroll-wrapper :deep(table thead tr) {
background-color: white;
}
.activity-scroll-wrapper :deep(table thead th) {
background-color: white !important;
position: sticky;
top: 0;
z-index: 20;
box-shadow: 0 1px 0 0 #e5e7eb;
border-bottom: 1px solid #e5e7eb;
}
.activity-basic-table tbody td {
vertical-align: top;
padding: 0.625rem 1rem; /* match header horizontal padding */
}
/* Center the decision dot in its column */
.activity-basic-table td.decision-dot {
vertical-align: middle;
}
/* 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: 6%;
}
.activity-basic-table th:nth-child(2),
.activity-basic-table td:nth-child(2) {
width: 14%;
}
.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>