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
@@ -1,12 +1,21 @@
<script setup>
import ActionMessage from "@/Components/ActionMessage.vue";
import BasicButton from "@/Components/buttons/BasicButton.vue";
import DialogModal from "@/Components/DialogModal.vue";
import InputLabel from "@/Components/InputLabel.vue";
import DatePickerField from "@/Components/DatePickerField.vue";
import CreateDialog from "@/Components/Dialogs/CreateDialog.vue";
import DatePicker from "@/Components/DatePicker.vue";
import CurrencyInput from "@/Components/CurrencyInput.vue";
import { useForm, usePage } from "@inertiajs/vue3";
import { FwbTextarea } from "flowbite-vue";
import { useForm as useInertiaForm, usePage, router } from "@inertiajs/vue3";
// Note: This form uses Inertia's useForm for API calls but shadcn-vue components for UI
import { Textarea } from "@/Components/ui/textarea";
import { Label } from "@/Components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/Components/ui/select";
import { Checkbox } from "@/Components/ui/checkbox";
import { ref, watch, computed } from "vue";
const props = defineProps({
@@ -15,7 +24,6 @@ const props = defineProps({
actions: { type: Array, default: () => [] },
contractUuid: { type: String, default: null },
phoneMode: { type: Boolean, default: false },
// Prefer passing these from parent (e.g., phone view) to avoid reliance on global page props in teleports
documents: { type: Array, default: null },
contracts: { type: Array, default: null },
});
@@ -31,7 +39,8 @@ const decisions = ref(
const emit = defineEmits(["close", "saved"]);
const close = () => emit("close");
const form = useForm({
// Using Inertia's useForm for API calls, but wrapping fields with shadcn-vue components
const form = useInertiaForm({
due_date: null,
amount: null,
note: "",
@@ -123,7 +132,6 @@ const store = async () => {
onSuccess: () => {
close();
form.reset("due_date", "amount", "note");
// Notify parent to react (e.g., refresh, redirect in phone mode when no contracts left)
emit("saved");
},
});
@@ -180,11 +188,9 @@ const isToday = (val) => {
if (val instanceof Date) {
d = val;
} else if (typeof val === "string") {
// Normalize common MySQL timestamp 'YYYY-MM-DD HH:mm:ss' for Safari/iOS
const s = val.includes("T") ? val : val.replace(" ", "T");
d = new Date(s);
if (isNaN(d.getTime())) {
// Fallback: parse manually as local date
const m = val.match(/^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2})(?::(\d{2}))?/);
if (m) {
const [_, yy, mm, dd, hh, mi, ss] = m;
@@ -243,7 +249,13 @@ const pageContracts = computed(() => {
if (Array.isArray(props.contracts)) {
return props.contracts;
}
if (props.contracts?.data) {
return props.contracts.data;
}
const propsVal = page?.props?.value || {};
if (propsVal.contracts?.data) {
return propsVal.contracts.data;
}
return Array.isArray(propsVal.contracts) ? propsVal.contracts : [];
});
@@ -273,143 +285,149 @@ watch(
</script>
<template>
<DialogModal :show="show" @close="close">
<template #title>Dodaj aktivnost</template>
<template #content>
<form @submit.prevent="store">
<div class="col-span-6 sm:col-span-4">
<InputLabel for="activityAction" value="Akcija" />
<select
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"
:disabled="!actions || !actions.length"
>
<option v-for="a in actions" :key="a.id" :value="a.id">{{ a.name }}</option>
</select>
</div>
<div class="col-span-6 sm:col-span-4">
<InputLabel for="activityDecision" value="Odločitev" />
<select
class="block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
id="activityDecision"
ref="activityDecisionSelect"
v-model="form.decision_id"
:disabled="!decisions || !decisions.length"
>
<option v-for="d in decisions" :key="d.id" :value="d.id">{{ d.name }}</option>
</select>
</div>
<div class="col-span-6 sm:col-span-4">
<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"
/>
</div>
<DatePickerField
id="activityDueDate"
label="Datum zapadlosti"
v-model="form.due_date"
format="dd.MM.yyyy"
:enable-time-picker="false"
:auto-position="true"
:teleport-target="'body'"
:inline="false"
:auto-apply="true"
: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" />
<CurrencyInput
id="activityAmount"
ref="activityAmountinput"
v-model="form.amount"
:precision="{ min: 0, max: 4 }"
placeholder="0,00"
/>
</div>
<div class="mt-2" v-if="showSendAutoMail()">
<div class="flex items-center justify-between">
<label class="flex items-center gap-2 text-sm">
<input
type="checkbox"
v-model="form.send_auto_mail"
:disabled="autoMailDisabled"
class="rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed"
/>
<span>Send auto email</span>
</label>
<CreateDialog
:show="show"
title="Dodaj aktivnost"
confirm-text="Shrani"
:processing="form.processing"
@close="close"
@confirm="store"
>
<form @submit.prevent="store">
<div class="space-y-4">
<div class="space-y-2">
<Label>Akcija</Label>
<Select v-model="form.action_id" :disabled="!actions || !actions.length">
<SelectTrigger>
<SelectValue placeholder="Izberi akcijo" />
</SelectTrigger>
<SelectContent>
<SelectItem v-for="a in actions" :key="a.id" :value="a.id">
{{ a.name }}
</SelectItem>
</SelectContent>
</Select>
</div>
<p v-if="autoMailDisabled" class="mt-1 text-xs text-amber-600">
{{ autoMailDisabledHint }}
</p>
<div v-if="templateAllowsAttachments && form.contract_uuid" class="mt-3">
<label class="inline-flex items-center gap-2">
<input type="checkbox" v-model="form.attach_documents" />
<span class="text-sm">Dodaj priponke iz izbrane pogodbe</span>
</label>
<div
v-if="form.attach_documents"
class="mt-2 border rounded p-2 max-h-48 overflow-auto"
>
<div class="text-xs text-gray-600 mb-2">
Izberite dokumente, ki bodo poslani kot priponke:
<div class="space-y-2">
<Label>Odločitev</Label>
<Select v-model="form.decision_id" :disabled="!decisions || !decisions.length">
<SelectTrigger>
<SelectValue placeholder="Izberi odločitev" />
</SelectTrigger>
<SelectContent>
<SelectItem v-for="d in decisions" :key="d.id" :value="d.id">
{{ d.name }}
</SelectItem>
</SelectContent>
</Select>
</div>
<div class="space-y-2">
<Label for="activityNote">Opomba</Label>
<Textarea
id="activityNote"
v-model="form.note"
class="block w-full"
placeholder="Opomba"
/>
</div>
<div class="space-y-2">
<Label for="activityDueDate">Datum zapadlosti</Label>
<DatePicker
id="activityDueDate"
v-model="form.due_date"
format="dd.MM.yyyy"
:error="form.errors.due_date"
/>
</div>
<div class="space-y-2">
<Label for="activityAmount">Znesek</Label>
<CurrencyInput
id="activityAmount"
v-model="form.amount"
:precision="{ min: 0, max: 4 }"
placeholder="0,00"
class="w-full"
/>
</div>
<div v-if="showSendAutoMail()" class="space-y-2">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-2">
<Checkbox
v-model="form.send_auto_mail"
:disabled="autoMailDisabled"
/>
<Label class="cursor-pointer">Send auto email</Label>
</div>
<div class="space-y-1">
<template v-for="c in pageContracts" :key="c.uuid || c.id">
<div v-if="c.uuid === form.contract_uuid">
<div class="font-medium text-sm text-gray-700 mb-1">
Pogodba {{ c.reference }}
</div>
<div class="space-y-1">
<div
v-for="doc in availableContractDocs"
:key="doc.uuid || doc.id"
class="flex items-center gap-2 text-sm"
>
<input
type="checkbox"
:value="doc.id"
v-model="form.attachment_document_ids"
/>
<span>{{ doc.original_name || doc.name }}</span>
<span class="text-xs text-gray-400"
>({{ doc.extension?.toUpperCase() || "" }},
{{ (doc.size / 1024 / 1024).toFixed(2) }} MB)</span
</div>
<p v-if="autoMailDisabled" class="text-xs text-amber-600">
{{ autoMailDisabledHint }}
</p>
<div v-if="templateAllowsAttachments && form.contract_uuid" class="mt-3">
<label class="inline-flex items-center gap-2">
<Checkbox v-model="form.attach_documents" />
<span class="text-sm">Dodaj priponke iz izbrane pogodbe</span>
</label>
<div
v-if="form.attach_documents"
class="mt-2 border rounded p-2 max-h-48 overflow-auto"
>
<div class="text-xs text-gray-600 mb-2">
Izberite dokumente, ki bodo poslani kot priponke:
</div>
<div class="space-y-1">
<template v-for="c in pageContracts" :key="c.uuid || c.id">
<div v-if="c.uuid === form.contract_uuid">
<div class="font-medium text-sm text-gray-700 mb-1">
Pogodba {{ c.reference }}
</div>
<div class="space-y-1">
<div
v-for="doc in availableContractDocs"
:key="doc.uuid || doc.id"
class="flex items-center gap-2 text-sm"
>
<Checkbox
:checked="form.attachment_document_ids.includes(doc.id)"
@update:checked="(checked) => {
if (checked) {
if (!form.attachment_document_ids.includes(doc.id)) {
form.attachment_document_ids.push(doc.id);
}
} else {
form.attachment_document_ids = form.attachment_document_ids.filter(id => id !== doc.id);
}
}"
/>
<span>{{ doc.original_name || doc.name }}</span>
<span class="text-xs text-gray-400"
>({{ doc.extension?.toUpperCase() || "" }},
{{ (doc.size / 1024 / 1024).toFixed(2) }} MB)</span
>
</div>
</div>
</div>
</template>
<div
v-if="availableContractDocs.length === 0"
class="text-sm text-gray-500"
>
Ni dokumentov, povezanih s to pogodbo.
</div>
</template>
<div
v-if="availableContractDocs.length === 0"
class="text-sm text-gray-500"
>
Ni dokumentov, povezanih s to pogodbo.
</div>
</div>
</div>
</div>
</div>
<div class="flex justify-end mt-4">
<ActionMessage :on="form.recentlySuccessful" class="me-3">
<ActionMessage :on="form.recentlySuccessful" class="text-sm text-green-600">
Shranjuje.
</ActionMessage>
<BasicButton
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
>
Shrani
</BasicButton>
</div>
</form>
</template>
</DialogModal>
</CreateDialog>
</template>