changes notification added
This commit is contained in:
parent
db99a57030
commit
0e0912c81b
|
|
@ -210,6 +210,7 @@ public function storeActivity(ClientCase $clientCase, Request $request)
|
||||||
'contract_uuid' => 'nullable|uuid',
|
'contract_uuid' => 'nullable|uuid',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
// Map contract_uuid to contract_id within the same client case, if provided
|
// Map contract_uuid to contract_id within the same client case, if provided
|
||||||
$contractId = null;
|
$contractId = null;
|
||||||
if (! empty($attributes['contract_uuid'])) {
|
if (! empty($attributes['contract_uuid'])) {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,43 @@ public function share(Request $request): array
|
||||||
'warning' => fn () => $request->session()->get('warning'),
|
'warning' => fn () => $request->session()->get('warning'),
|
||||||
'info' => fn () => $request->session()->get('info'),
|
'info' => fn () => $request->session()->get('info'),
|
||||||
],
|
],
|
||||||
|
'notifications' => function () use ($request) {
|
||||||
|
try {
|
||||||
|
$user = $request->user();
|
||||||
|
if (! $user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$today = now()->toDateString();
|
||||||
|
$activities = \App\Models\Activity::query()
|
||||||
|
->with([
|
||||||
|
// Include contract uuid and reference, keep id for relation mapping, and client_case_id for nested eager load
|
||||||
|
'contract:id,uuid,reference,client_case_id',
|
||||||
|
// Include client case uuid (id required for mapping, will be hidden in JSON)
|
||||||
|
'contract.clientCase:id,uuid',
|
||||||
|
// Include account amounts; contract_id needed for relation mapping
|
||||||
|
'contract.account:contract_id,balance_amount,initial_amount',
|
||||||
|
])
|
||||||
|
->whereDate('due_date', $today)
|
||||||
|
->where('user_id', $user->id)
|
||||||
|
->orderBy('created_at')
|
||||||
|
->limit(20)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return [
|
||||||
|
'dueToday' => [
|
||||||
|
'count' => $activities->count(),
|
||||||
|
'items' => $activities,
|
||||||
|
'date' => $today,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
603
package-lock.json
generated
603
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
|
@ -16,7 +16,7 @@
|
||||||
"laravel-vite-plugin": "^1.0",
|
"laravel-vite-plugin": "^1.0",
|
||||||
"postcss": "^8.4.32",
|
"postcss": "^8.4.32",
|
||||||
"tailwindcss": "^3.4.0",
|
"tailwindcss": "^3.4.0",
|
||||||
"vite": "^5.0",
|
"vite": "^7.1.7",
|
||||||
"vue": "^3.3.13"
|
"vue": "^3.3.13"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -27,13 +27,15 @@
|
||||||
"@fortawesome/vue-fontawesome": "^3.0.8",
|
"@fortawesome/vue-fontawesome": "^3.0.8",
|
||||||
"@headlessui/vue": "^1.7.23",
|
"@headlessui/vue": "^1.7.23",
|
||||||
"@heroicons/vue": "^2.1.5",
|
"@heroicons/vue": "^2.1.5",
|
||||||
"@vuepic/vue-datepicker": "^9.0.3",
|
"@internationalized/date": "^3.9.0",
|
||||||
|
"@vuepic/vue-datepicker": "^11.0.2",
|
||||||
"apexcharts": "^4.0.0",
|
"apexcharts": "^4.0.0",
|
||||||
"flowbite": "^2.5.2",
|
"flowbite": "^2.5.2",
|
||||||
"flowbite-vue": "^0.1.6",
|
"flowbite-vue": "^0.1.6",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"material-design-icons-iconfont": "^6.7.0",
|
"material-design-icons-iconfont": "^6.7.0",
|
||||||
"preline": "^2.7.0",
|
"preline": "^2.7.0",
|
||||||
|
"reka-ui": "^2.5.1",
|
||||||
"tailwindcss-inner-border": "^0.2.0",
|
"tailwindcss-inner-border": "^0.2.0",
|
||||||
"v-calendar": "^3.1.2",
|
"v-calendar": "^3.1.2",
|
||||||
"vue-multiselect": "^3.1.0",
|
"vue-multiselect": "^3.1.0",
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import Dropdown from '@/Components/Dropdown.vue';
|
||||||
import DropdownLink from '@/Components/DropdownLink.vue';
|
import DropdownLink from '@/Components/DropdownLink.vue';
|
||||||
import Breadcrumbs from '@/Components/Breadcrumbs.vue';
|
import Breadcrumbs from '@/Components/Breadcrumbs.vue';
|
||||||
import GlobalSearch from './Partials/GlobalSearch.vue';
|
import GlobalSearch from './Partials/GlobalSearch.vue';
|
||||||
|
import NotificationsBell from './Partials/NotificationsBell.vue';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
import { faMobileScreenButton } from '@fortawesome/free-solid-svg-icons';
|
import { faMobileScreenButton } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
|
|
@ -108,6 +109,8 @@ watch(
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// No automatic daily notifications
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -241,8 +244,9 @@ watch(
|
||||||
<kbd class="hidden sm:inline ml-2 text-[10px] px-1.5 py-0.5 rounded border bg-gray-50">Ctrl K</kbd>
|
<kbd class="hidden sm:inline ml-2 text-[10px] px-1.5 py-0.5 rounded border bg-gray-50">Ctrl K</kbd>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<!-- User drop menu --->
|
<!-- Notifications + User drop menu --->
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
|
<NotificationsBell class="mr-2" />
|
||||||
<!-- Phone page quick access button -->
|
<!-- Phone page quick access button -->
|
||||||
<Link :href="route('phone.index')" 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" title="Phone">
|
<Link :href="route('phone.index')" 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" title="Phone">
|
||||||
<FontAwesomeIcon :icon="faMobileScreenButton" class="h-5 w-5" />
|
<FontAwesomeIcon :icon="faMobileScreenButton" class="h-5 w-5" />
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, onUnmounted, ref, watch, computed } from 'vue';
|
import { onMounted, onUnmounted, ref, watch, computed } from "vue";
|
||||||
import { Head, Link, router, usePage } from '@inertiajs/vue3';
|
import { Head, Link, router, usePage } from "@inertiajs/vue3";
|
||||||
import ApplicationMark from '@/Components/ApplicationMark.vue';
|
import ApplicationMark from "@/Components/ApplicationMark.vue";
|
||||||
import Banner from '@/Components/Banner.vue';
|
import Banner from "@/Components/Banner.vue";
|
||||||
import Dropdown from '@/Components/Dropdown.vue';
|
import Dropdown from "@/Components/Dropdown.vue";
|
||||||
import DropdownLink from '@/Components/DropdownLink.vue';
|
import DropdownLink from "@/Components/DropdownLink.vue";
|
||||||
import GlobalSearch from './Partials/GlobalSearch.vue';
|
import GlobalSearch from "./Partials/GlobalSearch.vue";
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
import NotificationsBell from "./Partials/NotificationsBell.vue";
|
||||||
import { faDesktop } from '@fortawesome/free-solid-svg-icons';
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||||
|
import { faDesktop } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: String,
|
title: String,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sidebar + responsive behavior (same feel as AppLayout)
|
// Sidebar + responsive behavior (same feel as AppLayout)
|
||||||
|
|
@ -19,70 +20,72 @@ const hasSavedSidebarPref = ref(false);
|
||||||
const isMobile = ref(false);
|
const isMobile = ref(false);
|
||||||
const mobileSidebarOpen = ref(false);
|
const mobileSidebarOpen = ref(false);
|
||||||
function applyAutoCollapse() {
|
function applyAutoCollapse() {
|
||||||
if (typeof window === 'undefined') return;
|
if (typeof window === "undefined") return;
|
||||||
isMobile.value = window.innerWidth < 1024;
|
isMobile.value = window.innerWidth < 1024;
|
||||||
sidebarCollapsed.value = isMobile.value;
|
sidebarCollapsed.value = isMobile.value;
|
||||||
}
|
}
|
||||||
function handleResize() {
|
function handleResize() {
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== "undefined") {
|
||||||
isMobile.value = window.innerWidth < 1024;
|
isMobile.value = window.innerWidth < 1024;
|
||||||
if (!isMobile.value) mobileSidebarOpen.value = false;
|
if (!isMobile.value) mobileSidebarOpen.value = false;
|
||||||
}
|
}
|
||||||
if (!hasSavedSidebarPref.value) applyAutoCollapse();
|
if (!hasSavedSidebarPref.value) applyAutoCollapse();
|
||||||
}
|
}
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
try {
|
try {
|
||||||
const saved = localStorage.getItem('sidebarCollapsed');
|
const saved = localStorage.getItem("sidebarCollapsed");
|
||||||
if (saved !== null) {
|
if (saved !== null) {
|
||||||
hasSavedSidebarPref.value = true;
|
hasSavedSidebarPref.value = true;
|
||||||
sidebarCollapsed.value = saved === '1';
|
sidebarCollapsed.value = saved === "1";
|
||||||
} else {
|
} else {
|
||||||
applyAutoCollapse();
|
applyAutoCollapse();
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
window.addEventListener('resize', handleResize);
|
window.addEventListener("resize", handleResize);
|
||||||
});
|
});
|
||||||
onUnmounted(() => window.removeEventListener('resize', handleResize));
|
onUnmounted(() => window.removeEventListener("resize", handleResize));
|
||||||
watch(sidebarCollapsed, (v) => {
|
watch(sidebarCollapsed, (v) => {
|
||||||
if (!hasSavedSidebarPref.value) return;
|
if (!hasSavedSidebarPref.value) return;
|
||||||
try { localStorage.setItem('sidebarCollapsed', v ? '1' : '0'); } catch {}
|
try {
|
||||||
|
localStorage.setItem("sidebarCollapsed", v ? "1" : "0");
|
||||||
|
} catch {}
|
||||||
});
|
});
|
||||||
|
|
||||||
function toggleSidebar() {
|
function toggleSidebar() {
|
||||||
hasSavedSidebarPref.value = true;
|
hasSavedSidebarPref.value = true;
|
||||||
sidebarCollapsed.value = !sidebarCollapsed.value;
|
sidebarCollapsed.value = !sidebarCollapsed.value;
|
||||||
}
|
}
|
||||||
function toggleMobileSidebar() {
|
function toggleMobileSidebar() {
|
||||||
mobileSidebarOpen.value = !mobileSidebarOpen.value;
|
mobileSidebarOpen.value = !mobileSidebarOpen.value;
|
||||||
}
|
}
|
||||||
function handleSidebarToggleClick() {
|
function handleSidebarToggleClick() {
|
||||||
if (isMobile.value) toggleMobileSidebar();
|
if (isMobile.value) toggleMobileSidebar();
|
||||||
else toggleSidebar();
|
else toggleSidebar();
|
||||||
}
|
}
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
router.post(route('logout'));
|
router.post(route("logout"));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Flash toast notifications (same as AppLayout for consistency)
|
// Flash toast notifications (same as AppLayout for consistency)
|
||||||
const page = usePage();
|
const page = usePage();
|
||||||
const flash = computed(() => page.props.flash || {});
|
const flash = computed(() => page.props.flash || {});
|
||||||
const showToast = ref(false);
|
const showToast = ref(false);
|
||||||
const toastMessage = ref('');
|
const toastMessage = ref("");
|
||||||
const toastType = ref('success');
|
const toastType = ref("success");
|
||||||
watch(
|
watch(
|
||||||
() => [flash.value.success, flash.value.error, flash.value.warning, flash.value.info],
|
() => [flash.value.success, flash.value.error, flash.value.warning, flash.value.info],
|
||||||
([s, e, w, i]) => {
|
([s, e, w, i]) => {
|
||||||
const message = s || e || w || i;
|
const message = s || e || w || i;
|
||||||
const type = s ? 'success' : e ? 'error' : w ? 'warning' : i ? 'info' : null;
|
const type = s ? "success" : e ? "error" : w ? "warning" : i ? "info" : null;
|
||||||
if (message && type) {
|
if (message && type) {
|
||||||
toastMessage.value = message;
|
toastMessage.value = message;
|
||||||
toastType.value = type;
|
toastType.value = type;
|
||||||
showToast.value = true;
|
showToast.value = true;
|
||||||
setTimeout(() => (showToast.value = false), 3000);
|
setTimeout(() => (showToast.value = false), 3000);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
// Global search modal state
|
// Global search modal state
|
||||||
|
|
@ -90,144 +93,234 @@ const searchOpen = ref(false);
|
||||||
const openSearch = () => (searchOpen.value = true);
|
const openSearch = () => (searchOpen.value = true);
|
||||||
const closeSearch = () => (searchOpen.value = false);
|
const closeSearch = () => (searchOpen.value = false);
|
||||||
|
|
||||||
|
// No automatic daily notifications
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Head :title="title" />
|
<Head :title="title" />
|
||||||
|
|
||||||
<Banner />
|
<Banner />
|
||||||
|
|
||||||
<div class="min-h-screen bg-gray-100 flex">
|
<div class="min-h-screen bg-gray-100 flex">
|
||||||
<!-- Mobile backdrop -->
|
<!-- Mobile backdrop -->
|
||||||
<div v-if="isMobile && mobileSidebarOpen" class="fixed inset-0 z-40 bg-black/30" @click="mobileSidebarOpen=false"></div>
|
<div
|
||||||
|
v-if="isMobile && mobileSidebarOpen"
|
||||||
|
class="fixed inset-0 z-40 bg-black/30"
|
||||||
|
@click="mobileSidebarOpen = false"
|
||||||
|
></div>
|
||||||
|
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<aside :class="[
|
<aside
|
||||||
sidebarCollapsed ? 'w-16' : 'w-64',
|
:class="[
|
||||||
'bg-white border-r border-gray-200 transition-all duration-200 z-50',
|
sidebarCollapsed ? 'w-16' : 'w-64',
|
||||||
isMobile
|
'bg-white border-r border-gray-200 transition-all duration-200 z-50',
|
||||||
? ('fixed inset-y-0 left-0 transform ' + (mobileSidebarOpen ? 'translate-x-0' : '-translate-x-full'))
|
isMobile
|
||||||
: 'sticky top-0 h-screen overflow-y-auto'
|
? 'fixed inset-y-0 left-0 transform ' +
|
||||||
]">
|
(mobileSidebarOpen ? 'translate-x-0' : '-translate-x-full')
|
||||||
<div class="h-16 px-4 flex items-center justify-between border-b">
|
: 'sticky top-0 h-screen overflow-y-auto',
|
||||||
<Link :href="route('phone.index')" class="flex items-center gap-2">
|
]"
|
||||||
<ApplicationMark class="h-8 w-auto" />
|
>
|
||||||
<span v-if="!sidebarCollapsed" class="text-sm font-semibold">Teren</span>
|
<div class="h-16 px-4 flex items-center justify-between border-b">
|
||||||
</Link>
|
<Link :href="route('phone.index')" class="flex items-center gap-2">
|
||||||
</div>
|
<ApplicationMark class="h-8 w-auto" />
|
||||||
<nav class="py-4">
|
<span v-if="!sidebarCollapsed" class="text-sm font-semibold">Teren</span>
|
||||||
<ul class="space-y-1">
|
</Link>
|
||||||
<!-- Single phone link only -->
|
</div>
|
||||||
<li>
|
<nav class="py-4">
|
||||||
<Link :href="route('phone.index')" :class="['flex items-center gap-3 px-4 py-2 text-sm hover:bg-gray-100', route().current('phone.index') || route().current('phone.*') ? 'bg-gray-100 text-gray-900' : 'text-gray-600']" title="Opravila">
|
<ul class="space-y-1">
|
||||||
<!-- clipboard-list icon -->
|
<!-- Single phone link only -->
|
||||||
<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">
|
<li>
|
||||||
<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" />
|
<Link
|
||||||
</svg>
|
:href="route('phone.index')"
|
||||||
<span v-if="!sidebarCollapsed">Opravila</span>
|
:class="[
|
||||||
</Link>
|
'flex items-center gap-3 px-4 py-2 text-sm hover:bg-gray-100',
|
||||||
</li>
|
route().current('phone.index') || route().current('phone.*')
|
||||||
</ul>
|
? 'bg-gray-100 text-gray-900'
|
||||||
</nav>
|
: 'text-gray-600',
|
||||||
</aside>
|
]"
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
<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="!sidebarCollapsed">Opravila</span>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
<!-- Main column -->
|
<!-- Main column -->
|
||||||
<div class="flex-1 flex flex-col min-w-0">
|
<div class="flex-1 flex flex-col min-w-0">
|
||||||
<!-- Top bar -->
|
<!-- 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">
|
<div
|
||||||
<div class="flex items-center gap-2">
|
class="h-16 bg-white border-b border-gray-100 px-4 flex items-center justify-between sticky top-0 z-30"
|
||||||
<!-- Sidebar toggle -->
|
>
|
||||||
<button
|
<div class="flex items-center gap-2">
|
||||||
@click="handleSidebarToggleClick()"
|
<!-- Sidebar toggle -->
|
||||||
class="inline-flex items-center justify-center w-9 h-9 rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-100"
|
<button
|
||||||
:title="sidebarCollapsed ? 'Razširi meni' : 'Skrči meni'"
|
@click="handleSidebarToggleClick()"
|
||||||
aria-label="Toggle sidebar"
|
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'"
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
|
aria-label="Toggle sidebar"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
>
|
||||||
</svg>
|
<svg
|
||||||
</button>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<!-- Search trigger -->
|
fill="none"
|
||||||
<button @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">
|
viewBox="0 0 24 24"
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
|
stroke-width="1.5"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-4.35-4.35m0 0A7.5 7.5 0 1010.5 18.5a7.5 7.5 0 006.15-1.85z" />
|
stroke="currentColor"
|
||||||
</svg>
|
class="w-5 h-5"
|
||||||
<span class="hidden sm:inline">Globalni iskalnik</span>
|
>
|
||||||
<kbd class="hidden sm:inline ml-2 text-[10px] px-1.5 py-0.5 rounded border bg-gray-50">Ctrl K</kbd>
|
<path
|
||||||
</button>
|
stroke-linecap="round"
|
||||||
</div>
|
stroke-linejoin="round"
|
||||||
<!-- User drop menu + Desktop switch button -->
|
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
|
||||||
<div class="flex items-center">
|
/>
|
||||||
<!-- Desktop page quick access button -->
|
</svg>
|
||||||
<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" title="Desktop">
|
</button>
|
||||||
<FontAwesomeIcon :icon="faDesktop" class="h-5 w-5" />
|
<!-- Search trigger -->
|
||||||
</Link>
|
<button
|
||||||
<div class="ms-3 relative">
|
@click="openSearch"
|
||||||
<Dropdown align="right" width="48">
|
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"
|
||||||
<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">
|
<svg
|
||||||
<img class="h-8 w-8 rounded-full object-cover" :src="$page.props.auth.user.profile_photo_url" :alt="$page.props.auth.user.name">
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
</button>
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
class="w-5 h-5"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
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>
|
||||||
|
<kbd
|
||||||
|
class="hidden sm:inline ml-2 text-[10px] px-1.5 py-0.5 rounded border bg-gray-50"
|
||||||
|
>Ctrl K</kbd
|
||||||
|
>
|
||||||
|
</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"
|
||||||
|
title="Desktop"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon :icon="faDesktop" class="h-5 w-5" />
|
||||||
|
</Link>
|
||||||
|
<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"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="h-8 w-8 rounded-full object-cover"
|
||||||
|
:src="$page.props.auth.user.profile_photo_url"
|
||||||
|
:alt="$page.props.auth.user.name"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
<span v-else class="inline-flex rounded-md">
|
<span v-else class="inline-flex rounded-md">
|
||||||
<button 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">
|
<button
|
||||||
{{ $page.props.auth.user.name }}
|
type="button"
|
||||||
<svg class="ms-2 -me-0.5 h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
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"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
|
>
|
||||||
</svg>
|
{{ $page.props.auth.user.name }}
|
||||||
</button>
|
<svg
|
||||||
</span>
|
class="ms-2 -me-0.5 h-4 w-4"
|
||||||
</template>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M19.5 8.25l-7.5 7.5-7.5-7.5"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="block px-4 py-2 text-xs text-gray-400">Nastavitve računa</div>
|
<div class="block px-4 py-2 text-xs text-gray-400">
|
||||||
|
Nastavitve računa
|
||||||
|
</div>
|
||||||
|
|
||||||
<DropdownLink :href="route('profile.show')">Profil</DropdownLink>
|
<DropdownLink :href="route('profile.show')">Profil</DropdownLink>
|
||||||
<DropdownLink v-if="$page.props.jetstream.hasApiFeatures" :href="route('api-tokens.index')">API Tokens</DropdownLink>
|
<DropdownLink
|
||||||
|
v-if="$page.props.jetstream.hasApiFeatures"
|
||||||
|
:href="route('api-tokens.index')"
|
||||||
|
>API Tokens</DropdownLink
|
||||||
|
>
|
||||||
|
|
||||||
<div class="border-t border-gray-200" />
|
<div class="border-t border-gray-200" />
|
||||||
|
|
||||||
<form @submit.prevent="logout">
|
<form @submit.prevent="logout">
|
||||||
<DropdownLink as="button">Izpis</DropdownLink>
|
<DropdownLink as="button">Izpis</DropdownLink>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Page Heading -->
|
|
||||||
<header v-if="$slots.header" class="bg-white border-b shadow-sm">
|
|
||||||
<div class="max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8 space-y-2">
|
|
||||||
<slot name="header" />
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<!-- Page Content -->
|
|
||||||
<main class="p-4">
|
|
||||||
<slot />
|
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Global Search Modal -->
|
<!-- Page Heading -->
|
||||||
<GlobalSearch :open="searchOpen" @update:open="(v)=>searchOpen=v" />
|
<header v-if="$slots.header" class="bg-white border-b shadow-sm">
|
||||||
|
<div class="max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8 space-y-2">
|
||||||
|
<slot name="header" />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
<!-- Simple Toast -->
|
<!-- Page Content -->
|
||||||
<transition name="fade">
|
<main class="p-4">
|
||||||
<div
|
<slot />
|
||||||
v-if="showToast"
|
</main>
|
||||||
class="fixed bottom-4 right-4 z-[100] px-4 py-3 rounded shadow-lg text-white"
|
</div>
|
||||||
:class="{
|
|
||||||
'bg-emerald-600': toastType==='success',
|
|
||||||
'bg-red-600': toastType==='error',
|
|
||||||
'bg-amber-500': toastType==='warning',
|
|
||||||
'bg-blue-600': toastType==='info',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ toastMessage }}
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
|
<!-- 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>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
|
||||||
107
resources/js/Layouts/Partials/NotificationsBell.vue
Normal file
107
resources/js/Layouts/Partials/NotificationsBell.vue
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted } from "vue";
|
||||||
|
import { usePage, Link } from "@inertiajs/vue3";
|
||||||
|
import Dropdown from "@/Components/Dropdown.vue";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||||
|
import { faBell } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
|
const page = usePage();
|
||||||
|
const due = computed(
|
||||||
|
() => page.props.notifications?.dueToday || { count: 0, items: [], date: null }
|
||||||
|
);
|
||||||
|
|
||||||
|
function fmtDate(d) {
|
||||||
|
if (!d) return "";
|
||||||
|
try {
|
||||||
|
return new Date(d).toLocaleDateString("sl-SI");
|
||||||
|
} catch {
|
||||||
|
return String(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fmtEUR(value) {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return "—";
|
||||||
|
}
|
||||||
|
const num = typeof value === "string" ? Number(value) : value;
|
||||||
|
if (Number.isNaN(num)) {
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
// de-DE locale: dot thousands, comma decimals, trailing Euro symbol
|
||||||
|
const formatted = new Intl.NumberFormat("de-DE", {
|
||||||
|
style: "currency",
|
||||||
|
currency: "EUR",
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
}).format(num);
|
||||||
|
// Replace non-breaking space with normal space for consistency
|
||||||
|
return formatted.replace("\u00A0", " ");
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
console.log(due.value);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dropdown
|
||||||
|
align="right"
|
||||||
|
width="72"
|
||||||
|
:content-classes="['py-1', 'bg-white', 'max-h-96', 'overflow-auto']"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="relative inline-flex items-center justify-center w-9 h-9 rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-100"
|
||||||
|
aria-label="Notifications"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon :icon="faBell" class="w-5 h-5" />
|
||||||
|
<span
|
||||||
|
v-if="due.count"
|
||||||
|
class="absolute -top-1 -right-1 inline-flex items-center justify-center h-5 min-w-[1.25rem] px-1 rounded-full text-[11px] bg-red-600 text-white"
|
||||||
|
>{{ due.count }}</span
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #content>
|
||||||
|
<div class="px-3 py-2 text-xs text-gray-400 border-b">Obljube zapadejo jutri</div>
|
||||||
|
<!-- Scrollable content area with max height -->
|
||||||
|
<div class="max-h-96 overflow-y-auto">
|
||||||
|
<div v-if="!due.count" class="px-3 py-3 text-sm text-gray-500">
|
||||||
|
Ni zapadlih aktivnosti danes.
|
||||||
|
</div>
|
||||||
|
<ul v-else class="divide-y">
|
||||||
|
<li
|
||||||
|
v-for="item in due.items"
|
||||||
|
:key="item.id"
|
||||||
|
class="px-3 py-2 text-sm flex items-start gap-2"
|
||||||
|
>
|
||||||
|
<div class="flex-1 min-w-0">
|
||||||
|
<div class="font-medium text-gray-800 truncate">
|
||||||
|
Pogodba:
|
||||||
|
<Link
|
||||||
|
v-if="item.contract?.client_case?.uuid"
|
||||||
|
:href="
|
||||||
|
route('clientCase.show', {
|
||||||
|
client_case: item.contract.client_case.uuid,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
class="text-indigo-600 hover:text-indigo-700 hover:underline"
|
||||||
|
>
|
||||||
|
{{ item.contract?.reference || "—" }}
|
||||||
|
</Link>
|
||||||
|
<span v-else>{{ item.contract?.reference || "—" }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-gray-600 truncate">
|
||||||
|
{{ fmtEUR(item.contract?.account?.balance_amount) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-xs text-gray-500 whitespace-nowrap">
|
||||||
|
{{ fmtDate(item.due_date) }}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dropdown>
|
||||||
|
</template>
|
||||||
|
|
@ -1,158 +1,185 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import ActionMessage from '@/Components/ActionMessage.vue';
|
import ActionMessage from "@/Components/ActionMessage.vue";
|
||||||
import BasicButton from '@/Components/buttons/BasicButton.vue';
|
import BasicButton from "@/Components/buttons/BasicButton.vue";
|
||||||
import DialogModal from '@/Components/DialogModal.vue';
|
import DialogModal from "@/Components/DialogModal.vue";
|
||||||
import InputLabel from '@/Components/InputLabel.vue';
|
import InputLabel from "@/Components/InputLabel.vue";
|
||||||
import DatePickerField from '@/Components/DatePickerField.vue';
|
import DatePickerField from "@/Components/DatePickerField.vue";
|
||||||
import PrimaryButton from '@/Components/PrimaryButton.vue';
|
import TextInput from "@/Components/TextInput.vue";
|
||||||
import SectionTitle from '@/Components/SectionTitle.vue';
|
import { useForm } from "@inertiajs/vue3";
|
||||||
import TextInput from '@/Components/TextInput.vue';
|
import { FwbTextarea } from "flowbite-vue";
|
||||||
import { PlusIcon } from '@/Utilities/Icons';
|
import { ref, watch } from "vue";
|
||||||
import { useForm } from '@inertiajs/vue3';
|
|
||||||
import { FwbTextarea } from 'flowbite-vue';
|
|
||||||
import { ref, watch } from 'vue';
|
|
||||||
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
show: {
|
show: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
client_case: Object,
|
client_case: Object,
|
||||||
actions: Array,
|
actions: Array,
|
||||||
// optionally pre-select a contract to attach the activity to
|
// optionally pre-select a contract to attach the activity to
|
||||||
contractUuid: { type: String, default: null },
|
contractUuid: { type: String, default: null },
|
||||||
});
|
});
|
||||||
|
|
||||||
const decisions = ref(props.actions[0].decisions);
|
const decisions = ref(props.actions[0].decisions);
|
||||||
|
|
||||||
console.log(props.actions);
|
console.log(props.actions);
|
||||||
|
|
||||||
const emit = defineEmits(['close']);
|
const emit = defineEmits(["close"]);
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
emit('close');
|
emit("close");
|
||||||
}
|
};
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
due_date: null,
|
due_date: null,
|
||||||
amount: null,
|
amount: null,
|
||||||
note: '',
|
note: "",
|
||||||
action_id: props.actions[0].id,
|
action_id: props.actions[0].id,
|
||||||
decision_id: props.actions[0].decisions[0].id,
|
decision_id: props.actions[0].decisions[0].id,
|
||||||
contract_uuid: props.contractUuid,
|
contract_uuid: props.contractUuid,
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => form.action_id,
|
() => form.action_id,
|
||||||
(action_id) => {
|
(action_id) => {
|
||||||
decisions.value = props.actions.filter((el) => el.id === action_id)[0].decisions;
|
decisions.value = props.actions.filter((el) => el.id === action_id)[0].decisions;
|
||||||
form.decision_id = decisions.value[0].id;
|
form.decision_id = decisions.value[0].id;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => form.due_date,
|
() => form.due_date,
|
||||||
(due_date) => {
|
(due_date) => {
|
||||||
if (due_date) {
|
if (due_date) {
|
||||||
let date = new Date(form.due_date).toISOString().split('T')[0];
|
let date = new Date(form.due_date).toLocaleDateString("en-CA");
|
||||||
console.table({ old: due_date, new: date });
|
console.table({ old: due_date, new: date });
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// keep contract_uuid synced if the prop changes while the drawer is open
|
// keep contract_uuid synced if the prop changes while the drawer is open
|
||||||
watch(
|
watch(
|
||||||
() => props.contractUuid,
|
() => props.contractUuid,
|
||||||
(cu) => { form.contract_uuid = cu || null; }
|
(cu) => {
|
||||||
|
form.contract_uuid = cu || null;
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const store = () => {
|
const store = async () => {
|
||||||
console.table({
|
console.table({
|
||||||
due_date: form.due_date,
|
due_date: form.due_date,
|
||||||
action_id: form.action_id,
|
action_id: form.action_id,
|
||||||
decision_id: form.decision_id,
|
decision_id: form.decision_id,
|
||||||
amount: form.amount,
|
amount: form.amount,
|
||||||
note: form.note
|
note: form.note,
|
||||||
|
});
|
||||||
|
|
||||||
|
form
|
||||||
|
.transform((data) => ({
|
||||||
|
...data,
|
||||||
|
due_date: new Date(data.due_date).toLocaleDateString("en-CA"),
|
||||||
|
}))
|
||||||
|
.post(route("clientCase.activity.store", props.client_case), {
|
||||||
|
onSuccess: () => {
|
||||||
|
close();
|
||||||
|
// Preserve selected contract across submissions; reset only user-editable fields
|
||||||
|
form.reset("due_date", "amount", "note");
|
||||||
|
},
|
||||||
|
onError: (errors) => {
|
||||||
|
console.log("Validation or server error:", errors);
|
||||||
|
},
|
||||||
|
onFinish: () => {
|
||||||
|
console.log("Request finished processing.");
|
||||||
|
},
|
||||||
});
|
});
|
||||||
form.post(route('clientCase.activity.store', props.client_case), {
|
};
|
||||||
onBefore: () => {
|
|
||||||
if (form.due_date) {
|
|
||||||
form.due_date = new Date(form.due_date).toISOString().split('T')[0];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
close();
|
|
||||||
// Preserve selected contract across submissions; reset only user-editable fields
|
|
||||||
form.reset('due_date', 'amount', 'note');
|
|
||||||
},
|
|
||||||
onError: (errors) => {
|
|
||||||
console.log('Validation or server error:', errors);
|
|
||||||
},
|
|
||||||
onFinish: () => {
|
|
||||||
console.log('Request finished processing.')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the drawer opens, always sync the current contractUuid into the form,
|
// When the drawer opens, always sync the current contractUuid into the form,
|
||||||
// even if the value hasn't changed (prevents stale/null contract_uuid after reset)
|
// even if the value hasn't changed (prevents stale/null contract_uuid after reset)
|
||||||
watch(
|
watch(
|
||||||
() => props.show,
|
() => props.show,
|
||||||
(visible) => {
|
(visible) => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
form.contract_uuid = props.contractUuid || null;
|
form.contract_uuid = props.contractUuid || null;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<DialogModal :show="show" @close="close">
|
<DialogModal :show="show" @close="close">
|
||||||
<template #title>Dodaj aktivnost</template>
|
<template #title>Dodaj aktivnost</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<form @submit.prevent="store">
|
<form @submit.prevent="store">
|
||||||
<div class="col-span-6 sm:col-span-4">
|
<div class="col-span-6 sm:col-span-4">
|
||||||
<InputLabel for="activityAction" value="Akcija" />
|
<InputLabel for="activityAction" value="Akcija" />
|
||||||
<select
|
<select
|
||||||
class="block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
|
class="block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
|
||||||
id="activityAction" ref="activityActionSelect" v-model="form.action_id">
|
id="activityAction"
|
||||||
<option v-for="a in actions" :value="a.id">{{ a.name }}</option>
|
ref="activityActionSelect"
|
||||||
<!-- ... -->
|
v-model="form.action_id"
|
||||||
</select>
|
>
|
||||||
</div>
|
<option v-for="a in actions" :value="a.id">{{ a.name }}</option>
|
||||||
<div class="col-span-6 sm:col-span-4">
|
<!-- ... -->
|
||||||
<InputLabel for="activityDecision" value="Odločitev" />
|
</select>
|
||||||
<select
|
</div>
|
||||||
class="block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
|
<div class="col-span-6 sm:col-span-4">
|
||||||
id="activityDecision" ref="activityDecisionSelect" v-model="form.decision_id">
|
<InputLabel for="activityDecision" value="Odločitev" />
|
||||||
<option v-for="d in decisions" :value="d.id">{{ d.name }}</option>
|
<select
|
||||||
<!-- ... -->
|
class="block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
|
||||||
</select>
|
id="activityDecision"
|
||||||
</div>
|
ref="activityDecisionSelect"
|
||||||
<div class="col-span-6 sm:col-span-4">
|
v-model="form.decision_id"
|
||||||
<FwbTextarea label="Opomba" id="activityNote" ref="activityNoteTextarea" v-model="form.note"
|
>
|
||||||
class="block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" />
|
<option v-for="d in decisions" :value="d.id">{{ d.name }}</option>
|
||||||
</div>
|
<!-- ... -->
|
||||||
<DatePickerField id="activityDueDate" label="Datum zapadlosti" v-model="form.due_date"
|
</select>
|
||||||
format="dd.MM.yyyy" :enable-time-picker="false" :auto-position="true" :teleport-target="'body'"
|
</div>
|
||||||
:inline="false" :auto-apply="false" :fixed="false" :close-on-auto-apply="true"
|
<div class="col-span-6 sm:col-span-4">
|
||||||
:close-on-scroll="true" />
|
<FwbTextarea
|
||||||
<div class="col-span-6 sm:col-span-4">
|
label="Opomba"
|
||||||
<InputLabel for="activityAmount" value="Znesek" />
|
id="activityNote"
|
||||||
<TextInput id="activityAmount" ref="activityAmountinput" v-model="form.amount" type="number"
|
ref="activityNoteTextarea"
|
||||||
class="mt-1 block w-full" autocomplete="0.00" />
|
v-model="form.note"
|
||||||
</div>
|
class="block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
|
||||||
<div class="flex justify-end mt-4">
|
/>
|
||||||
<ActionMessage :on="form.recentlySuccessful" class="me-3">
|
</div>
|
||||||
Shranjuje.
|
<DatePickerField
|
||||||
</ActionMessage>
|
id="activityDueDate"
|
||||||
<BasicButton :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
label="Datum zapadlosti"
|
||||||
Shrani
|
v-model="form.due_date"
|
||||||
</BasicButton>
|
format="dd.MM.yyyy"
|
||||||
</div>
|
:enable-time-picker="false"
|
||||||
</form>
|
:auto-position="true"
|
||||||
</template>
|
:teleport-target="'body'"
|
||||||
</DialogModal>
|
:inline="false"
|
||||||
|
:auto-apply="false"
|
||||||
|
:fixed="false"
|
||||||
|
:close-on-auto-apply="true"
|
||||||
|
:close-on-scroll="true"
|
||||||
|
/>
|
||||||
|
<div class="col-span-6 sm:col-span-4">
|
||||||
|
<InputLabel for="activityAmount" value="Znesek" />
|
||||||
|
<TextInput
|
||||||
|
id="activityAmount"
|
||||||
|
ref="activityAmountinput"
|
||||||
|
v-model="form.amount"
|
||||||
|
type="number"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
autocomplete="0.00"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end mt-4">
|
||||||
|
<ActionMessage :on="form.recentlySuccessful" class="me-3">
|
||||||
|
Shranjuje.
|
||||||
|
</ActionMessage>
|
||||||
|
<BasicButton
|
||||||
|
:class="{ 'opacity-25': form.processing }"
|
||||||
|
:disabled="form.processing"
|
||||||
|
>
|
||||||
|
Shrani
|
||||||
|
</BasicButton>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
</DialogModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -12,6 +12,7 @@ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
import VCalendar from 'v-calendar';
|
import VCalendar from 'v-calendar';
|
||||||
import 'v-calendar/style.css';
|
import 'v-calendar/style.css';
|
||||||
|
|
||||||
|
|
||||||
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
|
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
|
||||||
|
|
||||||
createInertiaApp({
|
createInertiaApp({
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(Tests\TestCase::class, RefreshDatabase::class);
|
||||||
|
|
||||||
it('returns client cases when searching by contract reference', function () {
|
it('returns client cases when searching by contract reference', function () {
|
||||||
// Arrange: create a user and authenticate
|
// Arrange: create a user and authenticate
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user