Updated client contract table and notification table, multiselect
This commit is contained in:
@@ -4,6 +4,7 @@ import SectionTitle from "@/Components/SectionTitle.vue";
|
||||
import DataTableServer from "@/Components/DataTable/DataTableServer.vue";
|
||||
import { Link, router } from "@inertiajs/vue3";
|
||||
import { ref, computed, watch } from "vue";
|
||||
import Dropdown from "@/Components/Dropdown.vue";
|
||||
|
||||
const props = defineProps({
|
||||
activities: { type: Object, required: true },
|
||||
@@ -40,19 +41,23 @@ const selectedClient = ref(initialClient);
|
||||
|
||||
const clientOptions = computed(() => {
|
||||
// Prefer server-provided clients list; fallback to deriving from rows
|
||||
const list = Array.isArray(props.clients) && props.clients.length
|
||||
? props.clients
|
||||
: (Array.isArray(props.activities?.data) ? props.activities.data : [])
|
||||
.map((row) => {
|
||||
const client = row.contract?.client_case?.client || row.client_case?.client;
|
||||
if (!client?.uuid) return null;
|
||||
return { value: client.uuid, label: client.person?.full_name || "(neznana stranka)" };
|
||||
})
|
||||
.filter(Boolean)
|
||||
.reduce((acc, cur) => {
|
||||
if (!acc.find((x) => x.value === cur.value)) acc.push(cur);
|
||||
return acc;
|
||||
}, []);
|
||||
const list =
|
||||
Array.isArray(props.clients) && props.clients.length
|
||||
? props.clients
|
||||
: (Array.isArray(props.activities?.data) ? props.activities.data : [])
|
||||
.map((row) => {
|
||||
const client = row.contract?.client_case?.client || row.client_case?.client;
|
||||
if (!client?.uuid) return null;
|
||||
return {
|
||||
value: client.uuid,
|
||||
label: client.person?.full_name || "(neznana stranka)",
|
||||
};
|
||||
})
|
||||
.filter(Boolean)
|
||||
.reduce((acc, cur) => {
|
||||
if (!acc.find((x) => x.value === cur.value)) acc.push(cur);
|
||||
return acc;
|
||||
}, []);
|
||||
return list.sort((a, b) => (a.label || "").localeCompare(b.label || ""));
|
||||
});
|
||||
|
||||
@@ -67,12 +72,65 @@ watch(selectedClient, (val) => {
|
||||
});
|
||||
});
|
||||
|
||||
const selectedRows = ref([]);
|
||||
|
||||
function toggleSelectAll() {
|
||||
if (selectedRows.value.length === (props.activities.data?.length || 0)) {
|
||||
selectedRows.value = [];
|
||||
} else {
|
||||
selectedRows.value = (props.activities.data || []).map((row) => row.id);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleRowSelection(id) {
|
||||
const idx = selectedRows.value.indexOf(id);
|
||||
if (idx > -1) {
|
||||
selectedRows.value.splice(idx, 1);
|
||||
} else {
|
||||
selectedRows.value.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
function isRowSelected(id) {
|
||||
return selectedRows.value.includes(id);
|
||||
}
|
||||
|
||||
function isAllSelected() {
|
||||
return (
|
||||
(props.activities.data?.length || 0) > 0 &&
|
||||
selectedRows.value.length === (props.activities.data?.length || 0)
|
||||
);
|
||||
}
|
||||
|
||||
function isIndeterminate() {
|
||||
return (
|
||||
selectedRows.value.length > 0 &&
|
||||
selectedRows.value.length < (props.activities.data?.length || 0)
|
||||
);
|
||||
}
|
||||
|
||||
function markRead(id) {
|
||||
router.patch(route("notifications.activity.read"),
|
||||
router.patch(
|
||||
route("notifications.activity.read"),
|
||||
{ activity_id: id },
|
||||
{
|
||||
{
|
||||
only: ["activities"],
|
||||
preserveScroll: true
|
||||
preserveScroll: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function markReadBulk() {
|
||||
if (!selectedRows.value.length) return;
|
||||
router.patch(
|
||||
route("notifications.activity.read"),
|
||||
{ activity_ids: selectedRows.value },
|
||||
{
|
||||
only: ["activities"],
|
||||
preserveScroll: true,
|
||||
onSuccess: () => {
|
||||
selectedRows.value = [];
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -95,14 +153,20 @@ function markRead(id) {
|
||||
<!-- Filters -->
|
||||
<div class="mb-4 flex items-center gap-3">
|
||||
<div class="flex-1 max-w-sm">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Partner</label>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1"
|
||||
>Partner</label
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<select
|
||||
v-model="selectedClient"
|
||||
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm"
|
||||
>
|
||||
<option value="">Vsi partnerji</option>
|
||||
<option v-for="opt in clientOptions" :key="opt.value || opt.label" :value="opt.value">
|
||||
<option
|
||||
v-for="opt in clientOptions"
|
||||
:key="opt.value || opt.label"
|
||||
:value="opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</option>
|
||||
</select>
|
||||
@@ -120,6 +184,7 @@ function markRead(id) {
|
||||
|
||||
<DataTableServer
|
||||
:columns="[
|
||||
{ key: 'select', label: '', sortable: false, width: '50px' },
|
||||
{ key: 'what', label: 'Zadeva', sortable: false },
|
||||
{ key: 'partner', label: 'Partner', sortable: false },
|
||||
{
|
||||
@@ -143,6 +208,61 @@ function markRead(id) {
|
||||
:only-props="['activities']"
|
||||
:query="{ client: selectedClient || undefined }"
|
||||
>
|
||||
<template #toolbar-extra>
|
||||
<div v-if="selectedRows.length" class="flex items-center gap-2">
|
||||
<div class="text-sm text-gray-700">
|
||||
Izbrano: <span class="font-medium">{{ selectedRows.length }}</span>
|
||||
</div>
|
||||
<Dropdown width="48" align="left">
|
||||
<template #trigger>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center px-3 py-1.5 text-sm font-medium rounded-md border border-gray-300 text-gray-700 bg-white hover:bg-gray-50"
|
||||
>
|
||||
Akcije
|
||||
<svg
|
||||
class="ml-1 h-4 w-4"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5.23 7.21a.75.75 0 011.06.02L10 10.94l3.71-3.71a.75.75 0 111.06 1.06l-4.24 4.24a.75.75 0 01-1.06 0L5.21 8.29a.75.75 0 01.02-1.08z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</template>
|
||||
<template #content>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
|
||||
@click="markReadBulk"
|
||||
>
|
||||
Označi kot prebrano
|
||||
</button>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</template>
|
||||
<template #header-select>
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isAllSelected()"
|
||||
:indeterminate="isIndeterminate()"
|
||||
@change="toggleSelectAll"
|
||||
class="rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
|
||||
/>
|
||||
</template>
|
||||
<template #cell-select="{ row }">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isRowSelected(row.id)"
|
||||
@change="toggleRowSelection(row.id)"
|
||||
class="rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
|
||||
/>
|
||||
</template>
|
||||
<template #cell-what="{ row }">
|
||||
<div class="font-medium text-gray-800 truncate">
|
||||
<template v-if="row.contract?.uuid">
|
||||
@@ -178,9 +298,9 @@ function markRead(id) {
|
||||
<template #cell-partner="{ row }">
|
||||
<div class="truncate">
|
||||
{{
|
||||
(row.contract?.client_case?.client?.person?.full_name) ||
|
||||
(row.client_case?.client?.person?.full_name) ||
|
||||
'—'
|
||||
row.contract?.client_case?.client?.person?.full_name ||
|
||||
row.client_case?.client?.person?.full_name ||
|
||||
"—"
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user