Updated Application icon and notifcation pagination items per page, and updated NotificationsBell
This commit is contained in:
parent
cc4c07717e
commit
068bbdf583
|
|
@ -19,7 +19,7 @@ public function unread(Request $request)
|
|||
}
|
||||
|
||||
$today = now()->toDateString();
|
||||
$perPage = max(1, min(100, (int) $request->integer('perPage', 15)));
|
||||
$perPage = max(1, min(100, (int) $request->integer('per_page', 15)));
|
||||
$search = trim((string) $request->input('search', ''));
|
||||
$clientUuid = trim((string) $request->input('client', ''));
|
||||
$clientId = null;
|
||||
|
|
|
|||
|
|
@ -251,19 +251,17 @@ function isActive(patterns) {
|
|||
: 'sticky top-0 h-screen overflow-y-auto',
|
||||
]"
|
||||
>
|
||||
<div
|
||||
class="h-16 px-4 flex items-center justify-between border-b border-gray-200 bg-white"
|
||||
>
|
||||
<div class="h-16 px-4 flex items-center border-b border-sidebar-border bg-sidebar">
|
||||
<Link
|
||||
:href="route('dashboard')"
|
||||
class="flex items-center gap-2 hover:opacity-80 transition-opacity"
|
||||
class="flex items-center gap-1 hover:opacity-80 transition-opacity"
|
||||
>
|
||||
<ApplicationMark />
|
||||
<span
|
||||
v-if="!sidebarCollapsed"
|
||||
class="text-sm font-semibold text-gray-900 transition-opacity"
|
||||
class="text-lg font-semibold text-sidebar-foreground transition-opacity"
|
||||
>
|
||||
Admin
|
||||
Administrator
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,19 +10,6 @@ import GlobalSearch from "./Partials/GlobalSearch.vue";
|
|||
import NotificationsBell from "./Partials/NotificationsBell.vue";
|
||||
import ToastContainer from "@/Components/Toast/ToastContainer.vue";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import {
|
||||
faMobileScreenButton,
|
||||
faGaugeHigh,
|
||||
faLayerGroup,
|
||||
faUserGroup,
|
||||
faFolderOpen,
|
||||
faFileImport,
|
||||
faTableList,
|
||||
faFileCirclePlus,
|
||||
faMap,
|
||||
faGear,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { MenuIcon } from "lucide-vue-next";
|
||||
import { SearchIcon } from "lucide-vue-next";
|
||||
import { ChevronDownIcon } from "lucide-vue-next";
|
||||
|
|
@ -310,18 +297,18 @@ function isActive(patterns) {
|
|||
]"
|
||||
>
|
||||
<div
|
||||
class="h-16 px-4 flex items-center justify-between border-b border-sidebar-border bg-sidebar"
|
||||
class="h-16 px-4 flex items-center border-b border-sidebar-border bg-sidebar"
|
||||
>
|
||||
<Link
|
||||
:href="route('dashboard')"
|
||||
class="flex items-center gap-2 hover:opacity-80 transition-opacity"
|
||||
class="flex items-center gap-1 hover:opacity-80 transition-opacity"
|
||||
>
|
||||
<ApplicationMark />
|
||||
<span
|
||||
v-if="!sidebarCollapsed"
|
||||
class="text-sm font-semibold text-sidebar-foreground transition-opacity"
|
||||
class="text-lg font-semibold text-sidebar-foreground transition-opacity"
|
||||
>
|
||||
Teren
|
||||
Aplikacija
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -149,14 +149,14 @@ const closeSearch = () => (searchOpen.value = false);
|
|||
>
|
||||
<Link
|
||||
:href="route('phone.index')"
|
||||
class="flex items-center gap-2 hover:opacity-80 transition-opacity"
|
||||
class="flex items-center gap-1 hover:opacity-80 transition-opacity"
|
||||
>
|
||||
<ApplicationMark />
|
||||
<span
|
||||
v-if="showLabels"
|
||||
class="text-sm font-semibold text-sidebar-foreground transition-opacity"
|
||||
class="text-lg font-semibold text-sidebar-foreground transition-opacity"
|
||||
>
|
||||
Teren
|
||||
Mobitel
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<script setup>
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { usePage, Link, router } from "@inertiajs/vue3";
|
||||
import Dropdown from "@/Components/Dropdown.vue";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { faBell } from "@fortawesome/free-solid-svg-icons";
|
||||
import { BellIcon } from "lucide-vue-next";
|
||||
import { Badge } from "@/Components/ui/badge";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/Components/ui/popover";
|
||||
import { ScrollArea } from "@/Components/ui/scroll-area";
|
||||
import { Separator } from "@/Components/ui/separator";
|
||||
|
||||
const page = usePage();
|
||||
const due = computed(
|
||||
|
|
@ -83,12 +83,8 @@ function markRead(item) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Dropdown
|
||||
align="right"
|
||||
width="72"
|
||||
:content-classes="['p-0', 'bg-white', 'max-h-96', 'overflow-hidden']"
|
||||
>
|
||||
<template #trigger>
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
<Button variant="ghost" size="default" class="relative">
|
||||
<BellIcon />
|
||||
|
||||
|
|
@ -100,32 +96,30 @@ function markRead(item) {
|
|||
{{ count }}
|
||||
</Badge>
|
||||
</Button>
|
||||
</template>
|
||||
</PopoverTrigger>
|
||||
|
||||
<template #content>
|
||||
<div
|
||||
class="px-3 py-2 text-xs text-gray-400 border-b sticky top-0 bg-white z-10 flex items-center justify-between"
|
||||
>
|
||||
<span>Zapadejo danes</span>
|
||||
<PopoverContent align="end" class="w-96 p-0">
|
||||
<div class="px-4 py-3 flex items-center justify-between border-b">
|
||||
<span class="text-sm font-medium">Zapadejo danes</span>
|
||||
<Link
|
||||
:href="route('notifications.unread')"
|
||||
class="text-indigo-600 hover:text-indigo-700"
|
||||
class="text-sm text-primary hover:underline"
|
||||
>Vsa obvestila</Link
|
||||
>
|
||||
</div>
|
||||
<!-- Scrollable content area with max height -->
|
||||
<div class="max-h-80 overflow-auto">
|
||||
<div v-if="!count" class="px-3 py-3 text-sm text-gray-500">
|
||||
Ni zapadlih aktivnosti danes.
|
||||
|
||||
<ScrollArea class="h-72">
|
||||
<div v-if="!count" class="px-4 py-8 text-center">
|
||||
<p class="text-sm text-muted-foreground">Ni zapadlih aktivnosti danes.</p>
|
||||
</div>
|
||||
<ul v-else class="divide-y">
|
||||
<li
|
||||
<div v-else class="divide-y">
|
||||
<div
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
class="px-3 py-2 text-sm flex items-start gap-2"
|
||||
class="px-4 py-3 flex items-start gap-3 hover:bg-accent/50 transition-colors"
|
||||
>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="font-medium text-gray-800 truncate">
|
||||
<div class="flex-1 min-w-0 space-y-1">
|
||||
<div class="font-medium truncate">
|
||||
<template v-if="item.contract?.uuid">
|
||||
Pogodba:
|
||||
<Link
|
||||
|
|
@ -135,7 +129,7 @@ function markRead(item) {
|
|||
client_case: item.contract.client_case.uuid,
|
||||
})
|
||||
"
|
||||
class="text-indigo-600 hover:text-indigo-700 hover:underline"
|
||||
class="text-primary hover:underline"
|
||||
>
|
||||
{{ item.contract?.reference || "—" }}
|
||||
</Link>
|
||||
|
|
@ -148,7 +142,7 @@ function markRead(item) {
|
|||
:href="
|
||||
route('clientCase.show', { client_case: item.client_case.uuid })
|
||||
"
|
||||
class="text-indigo-600 hover:text-indigo-700 hover:underline"
|
||||
class="text-primary hover:underline"
|
||||
>
|
||||
{{ item.client_case?.person?.full_name || "—" }}
|
||||
</Link>
|
||||
|
|
@ -157,37 +151,38 @@ function markRead(item) {
|
|||
</div>
|
||||
<!-- Partner / Client full name (use contract.client when available; fallback to case.client) -->
|
||||
<div
|
||||
class="text-xs text-gray-500 truncate"
|
||||
class="text-xs text-muted-foreground truncate"
|
||||
v-if="item.contract?.client?.person?.full_name"
|
||||
>
|
||||
Partner: {{ item.contract.client.person.full_name }}
|
||||
</div>
|
||||
<div
|
||||
class="text-xs text-gray-500 truncate"
|
||||
class="text-xs text-muted-foreground truncate"
|
||||
v-else-if="item.client_case?.client?.person?.full_name"
|
||||
>
|
||||
Partner: {{ item.client_case.client.person.full_name }}
|
||||
</div>
|
||||
<div class="text-gray-600 truncate" v-if="item.contract">
|
||||
<div class="text-sm truncate" v-if="item.contract">
|
||||
{{ fmtEUR(item.contract?.account?.balance_amount) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col items-end gap-1">
|
||||
<div class="text-xs text-gray-500 whitespace-nowrap">
|
||||
<div class="flex flex-col items-end gap-1.5 shrink-0">
|
||||
<div class="text-xs text-muted-foreground whitespace-nowrap">
|
||||
{{ fmtDate(item.due_date) }}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="text-[11px] text-gray-400 hover:text-gray-600"
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
class="h-6 px-2 text-xs"
|
||||
@click.stop="markRead(item)"
|
||||
title="Skrij obvestilo"
|
||||
>
|
||||
Skrij
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -265,28 +265,30 @@ const update = () => {
|
|||
|
||||
// Transform actions from array of IDs to array of objects
|
||||
const actionsPayload = form.actions
|
||||
.map(id => {
|
||||
const action = props.actions.find(a => a.id === Number(id) || a.id === id);
|
||||
.map((id) => {
|
||||
const action = props.actions.find((a) => a.id === Number(id) || a.id === id);
|
||||
if (!action) {
|
||||
console.warn('Action not found for id:', id);
|
||||
console.warn("Action not found for id:", id);
|
||||
return null;
|
||||
}
|
||||
return { id: action.id, name: action.name };
|
||||
})
|
||||
.filter(Boolean); // Remove null entries
|
||||
|
||||
form.transform((data) => ({
|
||||
...data,
|
||||
actions: actionsPayload
|
||||
})).put(route("settings.decisions.update", { id: form.id }), {
|
||||
onSuccess: () => {
|
||||
closeEditDrawer();
|
||||
},
|
||||
onError: (errors) => {
|
||||
// preserve server errors for display
|
||||
scrollToFirstEventError(form.errors, "edit");
|
||||
},
|
||||
});
|
||||
form
|
||||
.transform((data) => ({
|
||||
...data,
|
||||
actions: actionsPayload,
|
||||
}))
|
||||
.put(route("settings.decisions.update", { id: form.id }), {
|
||||
onSuccess: () => {
|
||||
closeEditDrawer();
|
||||
},
|
||||
onError: (errors) => {
|
||||
// preserve server errors for display
|
||||
scrollToFirstEventError(form.errors, "edit");
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const store = () => {
|
||||
|
|
@ -299,27 +301,29 @@ const store = () => {
|
|||
|
||||
// Transform actions from array of IDs to array of objects
|
||||
const actionsPayload = createForm.actions
|
||||
.map(id => {
|
||||
const action = props.actions.find(a => a.id === Number(id) || a.id === id);
|
||||
.map((id) => {
|
||||
const action = props.actions.find((a) => a.id === Number(id) || a.id === id);
|
||||
if (!action) {
|
||||
console.warn('Action not found for id:', id);
|
||||
console.warn("Action not found for id:", id);
|
||||
return null;
|
||||
}
|
||||
return { id: action.id, name: action.name };
|
||||
})
|
||||
.filter(Boolean); // Remove null entries
|
||||
|
||||
createForm.transform((data) => ({
|
||||
...data,
|
||||
actions: actionsPayload
|
||||
})).post(route("settings.decisions.store"), {
|
||||
onSuccess: () => {
|
||||
closeCreateDrawer();
|
||||
},
|
||||
onError: () => {
|
||||
scrollToFirstEventError(createForm.errors, "create");
|
||||
},
|
||||
});
|
||||
createForm
|
||||
.transform((data) => ({
|
||||
...data,
|
||||
actions: actionsPayload,
|
||||
}))
|
||||
.post(route("settings.decisions.store"), {
|
||||
onSuccess: () => {
|
||||
closeCreateDrawer();
|
||||
},
|
||||
onError: () => {
|
||||
scrollToFirstEventError(createForm.errors, "create");
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function validateEventsClientSide(events) {
|
||||
|
|
@ -665,7 +669,7 @@ const destroyDecision = () => {
|
|||
</div>
|
||||
<div class="flex items-center gap-2 self-end">
|
||||
<label class="flex items-center gap-2 text-sm">
|
||||
<Checkbox v-model:checked="ev.active" />
|
||||
<Checkbox v-model="ev.active" />
|
||||
Aktivno
|
||||
</label>
|
||||
<Button
|
||||
|
|
@ -703,7 +707,7 @@ const destroyDecision = () => {
|
|||
</div>
|
||||
<div class="flex items-end">
|
||||
<label class="flex items-center gap-2 text-sm mt-6">
|
||||
<Checkbox v-model:checked="ev.config.deactivate_previous" />
|
||||
<Checkbox v-model="ev.config.deactivate_previous" />
|
||||
Deaktiviraj prejšnje
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user