Dashboard final version, TODO: update main sidebar menu
This commit is contained in:
@@ -1,21 +1,30 @@
|
||||
<script setup>
|
||||
import AppLayout from "@/Layouts/AppLayout.vue";
|
||||
import { ref } from "vue";
|
||||
import { Link, router } from "@inertiajs/vue3";
|
||||
import DataTable from "@/Components/DataTable/DataTable.vue";
|
||||
import { computed, ref } from "vue";
|
||||
import { Link, router, usePage } from "@inertiajs/vue3";
|
||||
import DataTable from "@/Components/DataTable/DataTableNew2.vue";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
import { Input } from "@/Components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/Components/ui/select";
|
||||
} from "@/Components/ui/select"; // kept in case elsewhere but segment filter replaced
|
||||
import AppMultiSelect from "@/Components/app/ui/AppMultiSelect.vue";
|
||||
import PersonInfoGrid from "@/Components/PersonInfo/PersonInfoGrid.vue";
|
||||
import SectionTitle from "@/Components/SectionTitle.vue";
|
||||
import DateRangePicker from "@/Components/DateRangePicker.vue";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { ButtonGroup } from "@/Components/ui/button-group";
|
||||
import AppPopover from "@/Components/app/ui/AppPopover.vue";
|
||||
import { Filter, LinkIcon } from "lucide-vue-next";
|
||||
import { Card } from "@/Components/ui/card";
|
||||
import { Badge } from "@/Components/ui/badge";
|
||||
import { hasPermission } from "@/Services/permissions";
|
||||
import InputLabel from "@/Components/InputLabel.vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps({
|
||||
client: Object,
|
||||
@@ -25,14 +34,29 @@ const props = defineProps({
|
||||
types: Object,
|
||||
});
|
||||
|
||||
const page = usePage();
|
||||
// Expose as a callable computed: use in templates as hasPerm('permission-slug')
|
||||
const hasPerm = computed(() => (permission) =>
|
||||
hasPermission(page.props.auth?.user, permission)
|
||||
);
|
||||
|
||||
const dateRange = ref({
|
||||
start: props.filters?.from || null,
|
||||
end: props.filters?.to || null,
|
||||
});
|
||||
const search = ref(props.filters?.search || "");
|
||||
const selectedSegment = ref(props.filters?.segment || null);
|
||||
// Multi-segment selection (backwards compatible if legacy single 'segment' present)
|
||||
const selectedSegments = ref(
|
||||
Array.isArray(props.filters?.segments)
|
||||
? props.filters.segments.map((s) => String(s))
|
||||
: props.filters?.segment
|
||||
? [String(props.filters.segment)]
|
||||
: []
|
||||
);
|
||||
const filterPopoverOpen = ref(false);
|
||||
|
||||
function applyDateFilter() {
|
||||
filterPopoverOpen.value = false;
|
||||
const params = Object.fromEntries(
|
||||
new URLSearchParams(window.location.search).entries()
|
||||
);
|
||||
@@ -51,32 +75,27 @@ function applyDateFilter() {
|
||||
} else {
|
||||
delete params.search;
|
||||
}
|
||||
if (selectedSegment.value) {
|
||||
params.segment = String(selectedSegment.value);
|
||||
if (selectedSegments.value.length > 0) {
|
||||
// join as comma list for backend; adjust server parsing accordingly
|
||||
params.segments = selectedSegments.value.join(",");
|
||||
} else {
|
||||
delete params.segments;
|
||||
}
|
||||
delete params.page;
|
||||
// remove legacy single segment param if present
|
||||
delete params.segment;
|
||||
router.get(route("client.contracts", { uuid: props.client.uuid }), params, {
|
||||
preserveState: true,
|
||||
replace: true,
|
||||
preserveScroll: true,
|
||||
only: ['contracts'],
|
||||
only: ["contracts"],
|
||||
});
|
||||
}
|
||||
|
||||
function clearDateFilter() {
|
||||
dateRange.value = { start: null, end: null };
|
||||
selectedSegment.value = null;
|
||||
applyDateFilter();
|
||||
}
|
||||
|
||||
function handleDateRangeUpdate() {
|
||||
applyDateFilter();
|
||||
}
|
||||
|
||||
function handleSegmentChange(value) {
|
||||
selectedSegment.value = value;
|
||||
selectedSegments.value = [];
|
||||
search.value = "";
|
||||
applyDateFilter();
|
||||
}
|
||||
|
||||
@@ -111,132 +130,184 @@ function formatDate(value) {
|
||||
<AppLayout title="Pogodbe">
|
||||
<template #header></template>
|
||||
<!-- Header card (matches Client/Show header style) -->
|
||||
<div class="pt-12">
|
||||
<div class="pt-6">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div
|
||||
class="bg-white overflow-hidden shadow-xl sm:rounded-lg border-l-4 border-blue-400"
|
||||
>
|
||||
<div class="mx-auto max-w-4x1 p-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<SectionTitle>
|
||||
<template #title>
|
||||
{{ client.person.full_name }}
|
||||
</template>
|
||||
</SectionTitle>
|
||||
</div>
|
||||
<Card class="border-l-4 border-blue-400">
|
||||
<div class="p-3 flex justify-between items-center">
|
||||
<SectionTitle>
|
||||
<template #title>
|
||||
{{ client.person.full_name }}
|
||||
</template>
|
||||
</SectionTitle>
|
||||
<Badge class="bg-blue-500 text-white"> Naročnik </Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Client details card (separate container) -->
|
||||
<div class="pt-1">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div
|
||||
class="bg-white overflow-hidden shadow-xl sm:rounded-lg border-l-4 border-blue-400"
|
||||
>
|
||||
<div class="mx-auto max-w-4x1 px-2">
|
||||
<PersonInfoGrid :types="types" :person="client.person" />
|
||||
<Card>
|
||||
<div class="mx-auto max-w-4x1 p-3">
|
||||
<PersonInfoGrid
|
||||
:types="types"
|
||||
:person="client.person"
|
||||
:edit="hasPerm('client-edit')"
|
||||
></PersonInfoGrid>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contracts list card -->
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="px-3 bg-white overflow-hidden shadow-xl sm:rounded-lg">
|
||||
<div class="mx-auto max-w-4x1 py-3">
|
||||
<div class="mb-4">
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
as-child
|
||||
:variant="route().current('client.show') ? 'default' : 'ghost'"
|
||||
>
|
||||
<Link :href="route('client.show', { uuid: client.uuid })">
|
||||
Primeri
|
||||
</Link>
|
||||
</Button>
|
||||
<Button
|
||||
as-child
|
||||
:variant="route().current('client.contracts') ? 'default' : 'ghost'"
|
||||
>
|
||||
<Link :href="route('client.contracts', { uuid: client.uuid })">
|
||||
Pogodbe
|
||||
</Link>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
|
||||
<div class="mx-auto max-w-4x1">
|
||||
<div class="px-3 py-4 flex flex-row items-center gap-3">
|
||||
<Link
|
||||
:class="
|
||||
cn(
|
||||
'border border-gray-200 py-2 px-3 rounded-md hover:bg-accent hover:text-accent-foreground ',
|
||||
route().current('client.show')
|
||||
? 'bg-accent text-accent-foreground'
|
||||
: ''
|
||||
)
|
||||
"
|
||||
:href="route('client.show', { uuid: client.uuid })"
|
||||
>
|
||||
Primeri
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
:class="
|
||||
cn(
|
||||
'border border-gray-200 py-2 px-3 rounded-md hover:bg-accent hover:text-accent-foreground ',
|
||||
route().current('client.contracts')
|
||||
? 'bg-accent text-accent-foreground'
|
||||
: ''
|
||||
)
|
||||
"
|
||||
:href="route('client.contracts', { uuid: client.uuid })"
|
||||
>
|
||||
Pogodbe
|
||||
</Link>
|
||||
</div>
|
||||
<DataTable
|
||||
:show-search="true"
|
||||
:show-page-size="true"
|
||||
:show-filters="true"
|
||||
:has-active-filters="!!(dateRange?.start || dateRange?.end || selectedSegment)"
|
||||
:columns="[
|
||||
{ key: 'select', label: '', sortable: false, width: '50px' },
|
||||
{ key: 'reference', label: 'Referenca', sortable: false },
|
||||
{ key: 'customer', label: 'Stranka', sortable: false },
|
||||
{ key: 'start', label: 'Začetek', sortable: false },
|
||||
{ key: 'segment', label: 'Segment', sortable: false },
|
||||
{ key: 'balance', label: 'Stanje', sortable: false, align: 'right' },
|
||||
]"
|
||||
:rows="contracts.data || []"
|
||||
:meta="{ current_page: contracts.current_page, per_page: contracts.per_page, total: contracts.total, last_page: contracts.last_page, from: contracts.from, to: contracts.to, links: contracts.links }"
|
||||
:data="contracts.data || []"
|
||||
:meta="{
|
||||
current_page: contracts.current_page,
|
||||
per_page: contracts.per_page,
|
||||
total: contracts.total,
|
||||
last_page: contracts.last_page,
|
||||
from: contracts.from,
|
||||
to: contracts.to,
|
||||
links: contracts.links,
|
||||
}"
|
||||
route-name="client.contracts"
|
||||
:route-params="{ uuid: client.uuid }"
|
||||
:search="search"
|
||||
row-key="uuid"
|
||||
:only-props="['contracts']"
|
||||
:page-size-options="[10, 15, 25, 50, 100]"
|
||||
page-param-name="contracts_page"
|
||||
per-page-param-name="contracts_per_page"
|
||||
:show-toolbar="true"
|
||||
>
|
||||
<template #toolbar-filters>
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<label class="text-sm font-medium">Datumska območja</label>
|
||||
<DateRangePicker
|
||||
v-model="dateRange"
|
||||
format="dd.MM.yyyy"
|
||||
@update:model-value="handleDateRangeUpdate"
|
||||
placeholder="Izberi datumska območja"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="text-sm font-medium">Segment</label>
|
||||
<Select
|
||||
:model-value="selectedSegment"
|
||||
@update:model-value="handleSegmentChange"
|
||||
>
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue placeholder="Vsi segmenti" />
|
||||
</SelectTrigger>
|
||||
<SelectContent class="w-[var(--radix-select-trigger-width)]">
|
||||
<SelectItem :value="null">Vsi segmenti</SelectItem>
|
||||
<SelectItem
|
||||
v-for="segment in segments"
|
||||
:key="segment.id"
|
||||
:value="String(segment.id)"
|
||||
>
|
||||
{{ segment.name }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="flex justify-end pt-2 border-t">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
:disabled="!dateRange?.start && !dateRange?.end && !selectedSegment"
|
||||
@click="clearDateFilter"
|
||||
>
|
||||
Počisti
|
||||
<AppPopover
|
||||
v-model:open="filterPopoverOpen"
|
||||
align="start"
|
||||
content-class="w-[400px]"
|
||||
>
|
||||
<template #trigger>
|
||||
<Button variant="outline" size="sm" class="gap-2">
|
||||
<Filter class="h-4 w-4" />
|
||||
Filtri
|
||||
<span
|
||||
v-if="
|
||||
dateRange?.start || dateRange?.end || selectedSegments?.length
|
||||
"
|
||||
class="ml-1 rounded-full bg-primary px-2 py-0.5 text-xs text-primary-foreground"
|
||||
>
|
||||
{{
|
||||
[
|
||||
dateRange?.start || dateRange?.end ? 1 : 0,
|
||||
selectedSegments?.length ? 1 : 0,
|
||||
].reduce((a, b) => a + b, 0)
|
||||
}}
|
||||
</span>
|
||||
</Button>
|
||||
</template>
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<h4 class="font-medium text-sm">Filtri pogodb</h4>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Izberite filtre za prikaz pogodb
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<div class="space-y-1.5">
|
||||
<InputLabel>Iskanje</InputLabel>
|
||||
<Input
|
||||
v-model="search"
|
||||
type="text"
|
||||
placeholder="Išči po referenci, stranki..."
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<InputLabel>Datumska območja</InputLabel>
|
||||
<DateRangePicker
|
||||
v-model="dateRange"
|
||||
format="dd.MM.yyyy"
|
||||
placeholder="Izberi datumska območja"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<InputLabel>Segmenti</InputLabel>
|
||||
<AppMultiSelect
|
||||
v-model="selectedSegments"
|
||||
:items="
|
||||
segments.map((s) => ({ value: String(s.id), label: s.name }))
|
||||
"
|
||||
placeholder="Vsi segmenti"
|
||||
search-placeholder="Išči segment..."
|
||||
empty-text="Ni segmentov"
|
||||
chip-variant="secondary"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-end gap-2 pt-2 border-t">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
:disabled="
|
||||
!dateRange?.start &&
|
||||
!dateRange?.end &&
|
||||
selectedSegments.length === 0 &&
|
||||
search === ''
|
||||
"
|
||||
@click="clearDateFilter"
|
||||
>
|
||||
Počisti
|
||||
</Button>
|
||||
<Button type="button" size="sm" @click="applyDateFilter">
|
||||
Uporabi
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AppPopover>
|
||||
</template>
|
||||
<template #cell-reference="{ row }">
|
||||
<Link
|
||||
:href="route('clientCase.show', caseShowParams(row))"
|
||||
class="text-indigo-600 hover:underline"
|
||||
class="font-semibold hover:underline text-primary-700"
|
||||
>
|
||||
{{ row.reference }}
|
||||
</Link>
|
||||
|
||||
+139
-127
@@ -5,8 +5,6 @@ import { Link, router, usePage } from "@inertiajs/vue3";
|
||||
import CreateDialog from "@/Components/Dialogs/CreateDialog.vue";
|
||||
import DataTable from "@/Components/DataTable/DataTableNew2.vue";
|
||||
import { hasPermission } from "@/Services/permissions";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { faUserGroup } from "@fortawesome/free-solid-svg-icons";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
import { Card, CardHeader, CardTitle, CardContent } from "@/Components/ui/card";
|
||||
import { Input } from "@/Components/ui/input";
|
||||
@@ -30,7 +28,8 @@ import { useForm } from "vee-validate";
|
||||
import { toTypedSchema } from "@vee-validate/zod";
|
||||
import * as z from "zod";
|
||||
import ActionMessage from "@/Components/ActionMessage.vue";
|
||||
import { Mail, Plug2Icon } from "lucide-vue-next";
|
||||
import { Mail, Plug2Icon, Plus } from "lucide-vue-next";
|
||||
import { Separator } from "@/Components/ui/separator";
|
||||
|
||||
const props = defineProps({
|
||||
clients: Object,
|
||||
@@ -164,13 +163,13 @@ const fmtCurrency = (v) => {
|
||||
<template>
|
||||
<AppLayout title="Client">
|
||||
<template #header> </template>
|
||||
<div class="py-12">
|
||||
<div class="py-6">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardHeader class="p-5">
|
||||
<CardTitle>Naročniki</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardContent class="p-0">
|
||||
<DataTable
|
||||
:columns="[
|
||||
{ key: 'nu', label: 'Št.', sortable: false },
|
||||
@@ -193,11 +192,16 @@ const fmtCurrency = (v) => {
|
||||
:show-toolbar="true"
|
||||
:hoverable="true"
|
||||
row-key="uuid"
|
||||
:striped="true"
|
||||
empty-text="Ni najdenih naročnikov."
|
||||
>
|
||||
<template #toolbar-add>
|
||||
<Button @click="openDrawerCreateClient">
|
||||
<Plug2Icon class="w-4 h-4 mr-2" /> Dodaj
|
||||
<template #toolbar-actions>
|
||||
<Button
|
||||
variant="outline"
|
||||
v-if="hasPerm('client-edit')"
|
||||
@click="openDrawerCreateClient"
|
||||
>
|
||||
<Plus class="w-4 h-4" /> Novi naročnik
|
||||
</Button>
|
||||
</template>
|
||||
<template #cell-nu="{ row }">
|
||||
@@ -206,7 +210,7 @@ const fmtCurrency = (v) => {
|
||||
<template #cell-name="{ row }">
|
||||
<Link
|
||||
:href="route('client.show', { uuid: row.uuid })"
|
||||
class="text-indigo-600 hover:underline"
|
||||
class="font-semibold hover:underline text-primary-700"
|
||||
>
|
||||
{{ row.person?.full_name || "-" }}
|
||||
</Link>
|
||||
@@ -249,7 +253,7 @@ const fmtCurrency = (v) => {
|
||||
<div class="space-y-4">
|
||||
<FormField v-slot="{ componentField }" name="full_name">
|
||||
<FormItem>
|
||||
<FormLabel>Naziv</FormLabel>
|
||||
<FormLabel>Naziv *</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
id="fullname"
|
||||
@@ -262,129 +266,137 @@ const fmtCurrency = (v) => {
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ componentField }" name="tax_number">
|
||||
<FormItem>
|
||||
<FormLabel>Davčna</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
id="taxnumber"
|
||||
type="text"
|
||||
autocomplete="tax-number"
|
||||
placeholder="Davčna številka"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ componentField }" name="social_security_number">
|
||||
<FormItem>
|
||||
<FormLabel>Matična / Emšo</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
id="socialSecurityNumber"
|
||||
type="text"
|
||||
autocomplete="social-security-number"
|
||||
placeholder="Matična / Emšo"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ componentField }" name="address.address">
|
||||
<FormItem>
|
||||
<FormLabel>Naslov</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
id="address"
|
||||
type="text"
|
||||
autocomplete="address"
|
||||
placeholder="Naslov"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ componentField }" name="address.country">
|
||||
<FormItem>
|
||||
<FormLabel>Država</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
id="addressCountry"
|
||||
type="text"
|
||||
autocomplete="address-country"
|
||||
placeholder="Država"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ value, handleChange }" name="address.type_id">
|
||||
<FormItem>
|
||||
<FormLabel>Vrsta naslova</FormLabel>
|
||||
<Select :model-value="value" @update:model-value="handleChange">
|
||||
<CardTitle class="full">Osebni podatki</CardTitle>
|
||||
<Separator />
|
||||
<div class="flex flex-row gap-2">
|
||||
<FormField v-slot="{ componentField }" name="tax_number">
|
||||
<FormItem class="flex-1">
|
||||
<FormLabel>Davčna</FormLabel>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Izberi vrsto naslova" />
|
||||
</SelectTrigger>
|
||||
<Input
|
||||
id="taxnumber"
|
||||
type="text"
|
||||
autocomplete="tax-number"
|
||||
placeholder="Davčna številka"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem :value="1">Stalni</SelectItem>
|
||||
<SelectItem :value="2">Začasni</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ value, handleChange }" name="phone.country_code">
|
||||
<FormItem>
|
||||
<FormLabel>Koda države tel.</FormLabel>
|
||||
<Select :model-value="value" @update:model-value="handleChange">
|
||||
<FormField v-slot="{ componentField }" name="social_security_number">
|
||||
<FormItem class="flex-1">
|
||||
<FormLabel>Matična / Emšo</FormLabel>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Izberi kodo države" />
|
||||
</SelectTrigger>
|
||||
<Input
|
||||
id="socialSecurityNumber"
|
||||
type="text"
|
||||
autocomplete="social-security-number"
|
||||
placeholder="Matična / Emšo"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="00386">+386 (Slovenija)</SelectItem>
|
||||
<SelectItem value="00385">+385 (Hrvaška)</SelectItem>
|
||||
<SelectItem value="0039">+39 (Italija)</SelectItem>
|
||||
<SelectItem value="0036">+36 (Madžarska)</SelectItem>
|
||||
<SelectItem value="0043">+43 (Avstrija)</SelectItem>
|
||||
<SelectItem value="00381">+381 (Srbija)</SelectItem>
|
||||
<SelectItem value="00387">+387 (Bosna in Hercegovina)</SelectItem>
|
||||
<SelectItem value="00382">+382 (Črna gora)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</div>
|
||||
<CardTitle class="full">Naslov</CardTitle>
|
||||
<Separator />
|
||||
<div class="grid sm:grid-cols-2 gap-2">
|
||||
<FormField v-slot="{ componentField }" name="address.address">
|
||||
<FormItem>
|
||||
<FormLabel>Naslov</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
id="address"
|
||||
type="text"
|
||||
autocomplete="address"
|
||||
placeholder="Naslov"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ componentField }" name="phone.nu">
|
||||
<FormItem>
|
||||
<FormLabel>Telefonska št.</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
id="phoneNu"
|
||||
type="text"
|
||||
autocomplete="phone-nu"
|
||||
placeholder="Telefonska številka"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
<FormField v-slot="{ componentField }" name="address.country">
|
||||
<FormItem>
|
||||
<FormLabel>Država</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
id="addressCountry"
|
||||
type="text"
|
||||
autocomplete="address-country"
|
||||
placeholder="Država"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ value, handleChange }" name="address.type_id">
|
||||
<FormItem>
|
||||
<FormLabel>Vrsta naslova</FormLabel>
|
||||
<Select :model-value="value" @update:model-value="handleChange">
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Izberi vrsto naslova" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent align="end" position="popper">
|
||||
<SelectItem :value="1">Stalni</SelectItem>
|
||||
<SelectItem :value="2">Začasni</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</div>
|
||||
<CardTitle class="full">Naslov</CardTitle>
|
||||
<Separator />
|
||||
<div class="flex flex-row gap-2">
|
||||
<FormField v-slot="{ value, handleChange }" name="phone.country_code">
|
||||
<FormItem class="flex-1/3">
|
||||
<FormLabel>Koda države tel.</FormLabel>
|
||||
<Select :model-value="value" @update:model-value="handleChange">
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Izberi kodo države" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="00386">+386 (Slovenija)</SelectItem>
|
||||
<SelectItem value="00385">+385 (Hrvaška)</SelectItem>
|
||||
<SelectItem value="0039">+39 (Italija)</SelectItem>
|
||||
<SelectItem value="0036">+36 (Madžarska)</SelectItem>
|
||||
<SelectItem value="0043">+43 (Avstrija)</SelectItem>
|
||||
<SelectItem value="00381">+381 (Srbija)</SelectItem>
|
||||
<SelectItem value="00387">+387 (Bosna in Hercegovina)</SelectItem>
|
||||
<SelectItem value="00382">+382 (Črna gora)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ componentField }" name="phone.nu">
|
||||
<FormItem class="flex-2/3">
|
||||
<FormLabel>Telefonska št.</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
id="phoneNu"
|
||||
type="text"
|
||||
autocomplete="phone-nu"
|
||||
placeholder="Telefonska številka"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</div>
|
||||
<FormField v-slot="{ componentField }" name="description">
|
||||
<FormItem>
|
||||
<FormLabel>Opis</FormLabel>
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
<script setup>
|
||||
import AppLayout from "@/Layouts/AppLayout.vue";
|
||||
import { computed, ref } from "vue";
|
||||
import { Link, usePage } from "@inertiajs/vue3";
|
||||
import { Link, router, usePage } from "@inertiajs/vue3";
|
||||
import SectionTitle from "@/Components/SectionTitle.vue";
|
||||
import PersonInfoGrid from "@/Components/PersonInfo/PersonInfoGrid.vue";
|
||||
import FormCreateCase from "./Partials/FormCreateCase.vue";
|
||||
import DataTable from "@/Components/DataTable/DataTable.vue";
|
||||
import DataTable from "@/Components/DataTable/DataTableNew2.vue";
|
||||
import { hasPermission } from "@/Services/permissions";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
import { ButtonGroup } from "@/Components/ui/button-group";
|
||||
import ActionMenuItem from "@/Components/DataTable/ActionMenuItem.vue";
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { Card } from "@/Components/ui/card";
|
||||
import Badge from "@/Components/ui/badge/Badge.vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Input } from "@/Components/ui/input";
|
||||
|
||||
const props = defineProps({
|
||||
client: Object,
|
||||
@@ -34,72 +37,92 @@ const drawerCreateCase = ref(false);
|
||||
const openDrawerCreateCase = () => {
|
||||
drawerCreateCase.value = true;
|
||||
};
|
||||
|
||||
function applySearch() {
|
||||
const params = Object.fromEntries(
|
||||
new URLSearchParams(window.location.search).entries()
|
||||
);
|
||||
const term = (search.value || "").trim();
|
||||
if (term) {
|
||||
params.search = term;
|
||||
} else {
|
||||
delete params.search;
|
||||
}
|
||||
delete params.page;
|
||||
router.get(route("client.show", { uuid: props.client.uuid }), params, {
|
||||
preserveState: true,
|
||||
replace: true,
|
||||
preserveScroll: true,
|
||||
only: ["client_cases"],
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppLayout title="Client">
|
||||
<template #header></template>
|
||||
<div class="pt-12">
|
||||
<div class="pt-6">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div
|
||||
class="bg-white overflow-hidden shadow-xl sm:rounded-lg border-l-4 border-blue-400"
|
||||
>
|
||||
<div class="mx-auto max-w-4x1 p-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<SectionTitle>
|
||||
<template #title>
|
||||
{{ client.person.full_name }}
|
||||
</template>
|
||||
</SectionTitle>
|
||||
</div>
|
||||
<Card class="border-l-4 border-blue-400">
|
||||
<div class="p-3 flex justify-between items-center">
|
||||
<SectionTitle>
|
||||
<template #title>
|
||||
{{ client.person.full_name }}
|
||||
</template>
|
||||
</SectionTitle>
|
||||
<Badge class="bg-blue-500 text-white"> Naročnik </Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pt-1">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div
|
||||
class="bg-white overflow-hidden shadow-xl sm:rounded-lg border-l-4 border-blue-400"
|
||||
>
|
||||
<div class="mx-auto max-w-4x1 px-2">
|
||||
<Card>
|
||||
<div class="mx-auto max-w-4x1 p-3">
|
||||
<PersonInfoGrid
|
||||
:types="types"
|
||||
:person="client.person"
|
||||
:edit="hasPerm('client-edit')"
|
||||
></PersonInfoGrid>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="px-3 bg-white overflow-hidden shadow-xl sm:rounded-lg">
|
||||
<div class="mx-auto max-w-4x1 py-3">
|
||||
<div class="mb-4">
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
as-child
|
||||
:variant="route().current('client.show') ? 'default' : 'ghost'"
|
||||
>
|
||||
<Link :href="route('client.show', { uuid: client.uuid })">
|
||||
Primeri
|
||||
</Link>
|
||||
</Button>
|
||||
<Button
|
||||
as-child
|
||||
:variant="route().current('client.contracts') ? 'default' : 'ghost'"
|
||||
>
|
||||
<Link :href="route('client.contracts', { uuid: client.uuid })">
|
||||
Pogodbe
|
||||
</Link>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
|
||||
<div class="mx-auto max-w-4x1">
|
||||
<div class="px-3 py-4 flex flex-row items-center gap-3">
|
||||
<Link
|
||||
:class="
|
||||
cn(
|
||||
'border border-gray-200 py-2 px-3 rounded-md hover:bg-accent hover:text-accent-foreground ',
|
||||
route().current('client.show')
|
||||
? 'bg-accent text-accent-foreground'
|
||||
: ''
|
||||
)
|
||||
"
|
||||
:href="route('client.show', { uuid: client.uuid })"
|
||||
>
|
||||
Primeri
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
:class="
|
||||
cn(
|
||||
'border border-gray-200 py-2 px-3 rounded-md hover:bg-accent hover:text-accent-foreground ',
|
||||
route().current('client.contracts')
|
||||
? 'bg-accent text-accent-foreground'
|
||||
: ''
|
||||
)
|
||||
"
|
||||
:href="route('client.contracts', { uuid: client.uuid })"
|
||||
>
|
||||
Pogodbe
|
||||
</Link>
|
||||
</div>
|
||||
<DataTable
|
||||
:show-search="true"
|
||||
:show-page-size="true"
|
||||
:show-add="true"
|
||||
:columns="[
|
||||
{ key: 'nu', label: 'Št.', sortable: false, class: 'w-40' },
|
||||
{ key: 'case', label: 'Primer', sortable: false },
|
||||
@@ -117,7 +140,7 @@ const openDrawerCreateCase = () => {
|
||||
align: 'right',
|
||||
},
|
||||
]"
|
||||
:rows="client_cases.data || []"
|
||||
:data="client_cases.data || []"
|
||||
:meta="{
|
||||
current_page: client_cases.current_page,
|
||||
per_page: client_cases.per_page,
|
||||
@@ -130,16 +153,32 @@ const openDrawerCreateCase = () => {
|
||||
route-name="client.show"
|
||||
:route-params="{ uuid: client.uuid }"
|
||||
row-key="uuid"
|
||||
:search="search"
|
||||
:only-props="['client_cases']"
|
||||
:page-size-options="[10, 15, 25, 50, 100]"
|
||||
:show-toolbar="true"
|
||||
>
|
||||
<template #toolbar-add>
|
||||
<ActionMenuItem
|
||||
<template #toolbar-filters>
|
||||
<div class="flex items-center gap-2 w-full">
|
||||
<Input
|
||||
v-model="search"
|
||||
placeholder="Išči po primeru, davčni, osebi..."
|
||||
class="w-[260px]"
|
||||
@keydown.enter="applySearch"
|
||||
/>
|
||||
<Button size="sm" variant="outline" @click="applySearch">Išči</Button>
|
||||
</div>
|
||||
</template>
|
||||
<template #toolbar-actions>
|
||||
<Button
|
||||
v-if="hasPerm('case-edit')"
|
||||
label="Dodaj primer"
|
||||
:icon="faPlus"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
class="gap-2"
|
||||
@click="openDrawerCreateCase"
|
||||
/>
|
||||
>
|
||||
<FontAwesomeIcon :icon="faPlus" class="h-4 w-4" />
|
||||
Dodaj primer
|
||||
</Button>
|
||||
</template>
|
||||
<template #cell-nu="{ row }">
|
||||
{{ row.person?.nu || "-" }}
|
||||
@@ -147,7 +186,7 @@ const openDrawerCreateCase = () => {
|
||||
<template #cell-case="{ row }">
|
||||
<Link
|
||||
:href="route('clientCase.show', { client_case: row.uuid })"
|
||||
class="text-indigo-600 hover:underline"
|
||||
class="font-semibold hover:underline text-primary-700"
|
||||
>
|
||||
{{ row.person?.full_name || "-" }}
|
||||
</Link>
|
||||
|
||||
Reference in New Issue
Block a user