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
+113 -85
View File
@@ -7,8 +7,15 @@ import Dropdown from "@/Components/Dropdown.vue";
import DropdownLink from "@/Components/DropdownLink.vue";
import GlobalSearch from "./Partials/GlobalSearch.vue";
import NotificationsBell from "./Partials/NotificationsBell.vue";
import Breadcrumbs from "@/Components/Breadcrumbs.vue";
import ToastContainer from "@/Components/Toast/ToastContainer.vue";
import { Button } from "@/Components/ui/button";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { faDesktop } from "@fortawesome/free-solid-svg-icons";
import {
faDesktop,
faClipboardList,
faCircleCheck,
} from "@fortawesome/free-solid-svg-icons";
const props = defineProps({
title: String,
@@ -122,50 +129,63 @@ const closeSearch = () => (searchOpen.value = false);
<aside
:class="[
widthClass,
'bg-white border-r border-gray-200 transition-all duration-200 z-50',
'bg-white border-r border-gray-200 transition-all duration-300 ease-in-out z-50',
isMobile
? 'fixed inset-y-0 left-0 transform ' +
? 'fixed inset-y-0 left-0 transform shadow-strong ' +
(mobileSidebarOpen ? 'translate-x-0' : '-translate-x-full')
: 'sticky top-0 h-screen overflow-y-auto',
]"
>
<div class="h-16 px-4 flex items-center justify-between border-b">
<Link :href="route('phone.index')" class="flex items-center gap-2">
<ApplicationMark class="h-8 w-auto" />
<span v-if="showLabels" class="text-sm font-semibold">Teren</span>
<div class="h-16 px-4 flex items-center justify-between border-b border-gray-200 bg-white">
<Link
:href="route('phone.index')"
class="flex items-center gap-2 hover:opacity-80 transition-opacity"
>
<ApplicationMark />
<span
v-if="showLabels"
class="text-sm font-semibold text-gray-900 transition-opacity"
>
Teren
</span>
</Link>
</div>
<nav class="py-4">
<ul class="space-y-1">
<nav class="py-4 overflow-y-auto">
<ul class="space-y-0.5 px-2">
<!-- Assigned jobs link -->
<li>
<Link
:href="route('phone.index')"
:class="[
'flex items-center gap-3 px-4 py-2 text-sm hover:bg-gray-100',
'flex items-center gap-3 px-3 py-2.5 text-sm rounded-lg transition-all duration-150',
route().current('phone.index') ||
(route().current('phone.case') && !isCompletedMode)
? 'bg-gray-100 text-gray-900'
: 'text-gray-600',
? 'bg-primary-50 text-primary-700 font-medium shadow-sm'
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900',
]"
title="Opravila"
>
<!-- clipboard-list icon -->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
class="w-5 h-5"
<FontAwesomeIcon
:icon="faClipboardList"
:class="[
'w-5 h-5 flex-shrink-0 transition-colors',
route().current('phone.index') ||
(route().current('phone.case') && !isCompletedMode)
? 'text-primary-600'
: 'text-gray-500',
]"
/>
<span
v-if="showLabels"
class="truncate transition-opacity"
:class="{
'font-medium':
route().current('phone.index') ||
(route().current('phone.case') && !isCompletedMode),
}"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9 6.75H7.5A2.25 2.25 0 005.25 9v9A2.25 2.25 0 007.5 20.25h9A2.25 2.25 0 0018.75 18v-9A2.25 2.25 0 0016.5 6.75H15M9 6.75A1.5 1.5 0 0010.5 5.25h3A1.5 1.5 0 0015 6.75M9 6.75A1.5 1.5 0 0110.5 8.25h3A1.5 1.5 0 0015 6.75M9 12h.008v.008H9V12zm0 3h.008v.008H9V15zm3-3h3m-3 3h3"
/>
</svg>
<span v-if="showLabels">Opravila</span>
Opravila
</span>
</Link>
</li>
<!-- Completed today link -->
@@ -173,30 +193,35 @@ const closeSearch = () => (searchOpen.value = false);
<Link
:href="route('phone.completed')"
:class="[
'flex items-center gap-3 px-4 py-2 text-sm hover:bg-gray-100',
'flex items-center gap-3 px-3 py-2.5 text-sm rounded-lg transition-all duration-150',
route().current('phone.completed') ||
(route().current('phone.case') && isCompletedMode)
? 'bg-gray-100 text-gray-900'
: 'text-gray-600',
? 'bg-primary-50 text-primary-700 font-medium shadow-sm'
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900',
]"
title="Zaključeno danes"
>
<!-- check-circle icon -->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
class="w-5 h-5"
<FontAwesomeIcon
:icon="faCircleCheck"
:class="[
'w-5 h-5 flex-shrink-0 transition-colors',
route().current('phone.completed') ||
(route().current('phone.case') && isCompletedMode)
? 'text-primary-600'
: 'text-gray-500',
]"
/>
<span
v-if="showLabels"
class="truncate transition-opacity"
:class="{
'font-medium':
route().current('phone.completed') ||
(route().current('phone.case') && isCompletedMode),
}"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span v-if="showLabels">Zaključeno danes</span>
Zaključeno danes
</span>
</Link>
</li>
</ul>
@@ -207,13 +232,14 @@ const closeSearch = () => (searchOpen.value = false);
<div class="flex-1 flex flex-col min-w-0">
<!-- Top bar -->
<div
class="h-16 bg-white border-b border-gray-100 px-4 flex items-center justify-between sticky top-0 z-30"
class="h-16 bg-white border-b border-gray-200 px-4 flex items-center justify-between sticky top-0 z-30 backdrop-blur-sm bg-white/95 shadow-sm"
>
<div class="flex items-center gap-2">
<div class="flex items-center gap-3">
<!-- Sidebar toggle -->
<button
<Button
variant="ghost"
size="icon"
@click="handleSidebarToggleClick()"
class="inline-flex items-center justify-center w-9 h-9 rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-100"
:title="sidebarCollapsed ? 'Razširi meni' : 'Skrči meni'"
aria-label="Toggle sidebar"
>
@@ -231,11 +257,13 @@ const closeSearch = () => (searchOpen.value = false);
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
/>
</svg>
</button>
</Button>
<!-- Search trigger -->
<button
<Button
variant="outline"
size="default"
@click="openSearch"
class="inline-flex items-center gap-2 px-3 py-2 text-sm rounded-md border border-gray-200 text-gray-500 hover:text-gray-700 hover:border-gray-300"
class="gap-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -243,7 +271,7 @@ const closeSearch = () => (searchOpen.value = false);
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-5 h-5"
class="w-4 h-4"
>
<path
stroke-linecap="round"
@@ -251,46 +279,52 @@ const closeSearch = () => (searchOpen.value = false);
d="M21 21l-4.35-4.35m0 0A7.5 7.5 0 1010.5 18.5a7.5 7.5 0 006.15-1.85z"
/>
</svg>
<span class="hidden sm:inline">Globalni iskalnik</span>
<span class="hidden sm:inline text-sm font-medium">Globalni iskalnik</span>
<kbd
class="hidden sm:inline ml-2 text-[10px] px-1.5 py-0.5 rounded border bg-gray-50"
class="hidden sm:inline ml-2 text-[10px] px-1.5 py-0.5 rounded border border-gray-300 bg-gray-100 text-gray-600 font-medium"
>Ctrl K</kbd
>
</button>
</Button>
</div>
<!-- Notifications + User drop menu + Desktop switch button -->
<div class="flex items-center">
<NotificationsBell class="mr-2" />
<!-- Desktop page quick access button -->
<Link
:href="route('clientCase')"
class="inline-flex items-center justify-center w-9 h-9 rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-100 mr-2"
<Button
variant="ghost"
size="icon"
:as-child="true"
class="mr-2"
title="Desktop"
>
<FontAwesomeIcon :icon="faDesktop" class="h-5 w-5" />
</Link>
<Link :href="route('clientCase')">
<FontAwesomeIcon :icon="faDesktop" class="h-5 w-5" />
</Link>
</Button>
<div class="ms-3 relative">
<Dropdown align="right" width="48">
<template #trigger>
<button
v-if="$page.props.jetstream.managesProfilePhotos"
class="flex text-sm border-2 border-transparent rounded-full focus:outline-none focus:border-gray-300 transition"
class="flex text-sm border-2 border-transparent rounded-full focus:outline-none focus:border-primary-500 focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 transition-all hover:ring-2 hover:ring-gray-200"
>
<img
class="h-8 w-8 rounded-full object-cover"
class="h-8 w-8 rounded-full object-cover ring-2 ring-gray-100"
:src="$page.props.auth.user.profile_photo_url"
:alt="$page.props.auth.user.name"
/>
</button>
<span v-else class="inline-flex rounded-md">
<button
<span v-else class="inline-flex">
<Button
variant="outline"
size="default"
type="button"
class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 bg-white hover:text-gray-700 focus:outline-none focus:bg-gray-50 active:bg-gray-50 transition ease-in-out duration-150"
class="gap-2"
>
{{ $page.props.auth.user.name }}
<svg
class="ms-2 -me-0.5 h-4 w-4"
class="h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
@@ -303,7 +337,7 @@ const closeSearch = () => (searchOpen.value = false);
d="M19.5 8.25l-7.5 7.5-7.5-7.5"
/>
</svg>
</button>
</Button>
</span>
</template>
@@ -331,14 +365,21 @@ const closeSearch = () => (searchOpen.value = false);
</div>
<!-- Page Heading -->
<header v-if="$slots.header" class="bg-white border-b shadow-sm">
<header
v-if="$slots.header"
class="bg-white border-b border-gray-200 shadow-sm"
>
<div class="max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8 space-y-2">
<Breadcrumbs
v-if="$page.props.breadcrumbs && $page.props.breadcrumbs.length"
:breadcrumbs="$page.props.breadcrumbs"
/>
<slot name="header" />
</div>
</header>
<!-- Page Content -->
<main class="p-4">
<main class="flex-1 p-4 sm:p-6">
<slot />
</main>
</div>
@@ -347,20 +388,7 @@ const closeSearch = () => (searchOpen.value = false);
<!-- Global Search Modal -->
<GlobalSearch :open="searchOpen" @update:open="(v) => (searchOpen = v)" />
<!-- Simple Toast -->
<transition name="fade">
<div
v-if="showToast"
class="fixed bottom-4 right-4 z-[100] px-4 py-3 rounded shadow-lg text-white"
:class="{
'bg-emerald-600': toastType === 'success',
'bg-red-600': toastType === 'error',
'bg-amber-500': toastType === 'warning',
'bg-blue-600': toastType === 'info',
}"
>
{{ toastMessage }}
</div>
</transition>
<!-- Toast Notification Container -->
<ToastContainer />
</div>
</template>