Teren-app/resources/js/Pages/Client/Index.vue

419 lines
13 KiB
Vue

<script setup>
import { computed, ref } from "vue";
import AppLayout from "@/Layouts/AppLayout.vue";
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 { Button } from "@/Components/ui/button";
import { Card, CardHeader, CardTitle, CardContent } from "@/Components/ui/card";
import { Input } from "@/Components/ui/input";
import ActionMenuItem from "@/Components/DataTable/ActionMenuItem.vue";
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/Components/ui/select";
import {
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/Components/ui/form";
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, Plus } from "lucide-vue-next";
import { Separator } from "@/Components/ui/separator";
const props = defineProps({
clients: Object,
filters: 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 formSchema = toTypedSchema(
z.object({
first_name: z.string().optional(),
last_name: z.string().optional(),
full_name: z.string().min(1, "Naziv je obvezen."),
tax_number: z.string().optional(),
social_security_number: z.string().optional(),
description: z.string().optional(),
address: z.object({
address: z.string().optional(),
country: z.string().optional(),
type_id: z.number().default(1),
}),
phone: z.object({
nu: z.string().optional(),
country_code: z.string().default("00386"),
type_id: z.number().default(1),
}),
})
);
const formClient = useForm({
validationSchema: formSchema,
initialValues: {
first_name: "",
last_name: "",
full_name: "",
tax_number: "",
social_security_number: "",
description: "",
address: {
address: "",
country: "",
type_id: 1,
},
phone: {
nu: "",
country_code: "00386",
type_id: 1,
},
},
});
//Create client drawer
const drawerCreateClient = ref(false);
// Initial search (passed to DataTable toolbar)
const initialSearch = ref(props.filters?.search || "");
//Open drawer create client
const openDrawerCreateClient = () => {
drawerCreateClient.value = true;
};
//Close any drawer on page
const closeDrawer = () => {
drawerCreateClient.value = false;
formClient.resetForm();
};
const processing = ref(false);
//Ajax call post to store new client
const storeClient = async () => {
processing.value = true;
const { values } = formClient;
// Ensure type_id is a number
const payload = {
...values,
address: {
...values.address,
type_id: Number(values.address.type_id),
},
phone: {
...values.phone,
type_id: Number(values.phone.type_id),
},
};
router.post(route("client.store"), payload, {
onSuccess: () => {
closeDrawer();
formClient.resetForm();
processing.value = false;
},
onError: (errors) => {
Object.keys(errors).forEach((field) => {
const errorMessages = Array.isArray(errors[field])
? errors[field]
: [errors[field]];
formClient.setFieldError(field, errorMessages[0]);
});
processing.value = false;
},
onFinish: () => {
processing.value = false;
},
});
};
const onConfirmCreate = formClient.handleSubmit(() => {
storeClient();
});
// Formatting helpers
const fmtCurrency = (v) => {
const n = Number(v ?? 0);
try {
return new Intl.NumberFormat("sl-SI", { style: "currency", currency: "EUR" }).format(
n
);
} catch (e) {
return `${n.toFixed(2)}`;
}
};
</script>
<template>
<AppLayout title="Client">
<template #header> </template>
<div class="py-6">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<Card>
<CardHeader class="p-5">
<CardTitle>Naročniki</CardTitle>
</CardHeader>
<CardContent class="p-0">
<DataTable
:columns="[
{ key: 'nu', label: 'Št.', sortable: false },
{ key: 'name', label: 'Naročnik', sortable: false },
{
key: 'cases',
label: 'Primeri z aktivnimi pogodbami',
sortable: false,
align: 'right',
},
{
key: 'balance',
label: 'Skupaj stanje',
sortable: false,
align: 'right',
},
]"
:data="clients.data || []"
:show-pagination="false"
:show-toolbar="true"
:hoverable="true"
row-key="uuid"
:striped="true"
empty-text="Ni najdenih naročnikov."
>
<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 }">
{{ row.person?.nu || "-" }}
</template>
<template #cell-name="{ row }">
<Link
:href="route('client.show', { uuid: row.uuid })"
class="font-semibold hover:underline text-primary-700"
>
{{ row.person?.full_name || "-" }}
</Link>
<div v-if="!row.person" class="mt-1">
<Button
variant="destructive"
size="sm"
@click.prevent="
router.post(route('client.emergencyPerson', { uuid: row.uuid }))
"
>Add Person</Button
>
</div>
</template>
<template #cell-cases="{ row }">
<div class="text-right">
{{ row.cases_with_active_contracts_count ?? 0 }}
</div>
</template>
<template #cell-balance="{ row }">
<div class="text-right">
{{ fmtCurrency(row.active_contracts_balance_sum) }}
</div>
</template>
</DataTable>
</CardContent>
</Card>
</div>
</div>
</AppLayout>
<CreateDialog
:show="drawerCreateClient"
title="Novi naročnik"
confirm-text="Shrani"
:processing="processing"
@close="drawerCreateClient = false"
@confirm="onConfirmCreate"
>
<form @submit.prevent="onConfirmCreate">
<div class="space-y-4">
<FormField v-slot="{ componentField }" name="full_name">
<FormItem>
<FormLabel>Naziv *</FormLabel>
<FormControl>
<Input
id="fullname"
type="text"
autocomplete="full-name"
placeholder="Naziv"
v-bind="componentField"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<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>
<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 class="flex-1">
<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>
</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="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>
<FormControl>
<Input
id="description"
type="text"
autocomplete="description"
placeholder="Opis"
v-bind="componentField"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</div>
</form>
</CreateDialog>
</template>