Changes
This commit is contained in:
@@ -3,6 +3,7 @@ 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 PrimaryButton from '@/Components/PrimaryButton.vue';
|
||||
import SectionTitle from '@/Components/SectionTitle.vue';
|
||||
import TextInput from '@/Components/TextInput.vue';
|
||||
@@ -18,7 +19,9 @@ const props = defineProps({
|
||||
default: false
|
||||
},
|
||||
client_case: Object,
|
||||
actions: Array
|
||||
actions: Array,
|
||||
// optionally pre-select a contract to attach the activity to
|
||||
contractUuid: { type: String, default: null },
|
||||
});
|
||||
|
||||
const decisions = ref(props.actions[0].decisions);
|
||||
@@ -36,7 +39,8 @@ const form = useForm({
|
||||
amount: null,
|
||||
note: '',
|
||||
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,
|
||||
});
|
||||
|
||||
watch(
|
||||
@@ -52,14 +56,20 @@ watch(
|
||||
(due_date) => {
|
||||
if (due_date) {
|
||||
let date = new Date(form.due_date).toISOString().split('T')[0];
|
||||
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
|
||||
watch(
|
||||
() => props.contractUuid,
|
||||
(cu) => { form.contract_uuid = cu || null; }
|
||||
);
|
||||
|
||||
const store = () => {
|
||||
console.table({
|
||||
console.table({
|
||||
due_date: form.due_date,
|
||||
action_id: form.action_id,
|
||||
decision_id: form.decision_id,
|
||||
@@ -68,10 +78,10 @@ const store = () => {
|
||||
});
|
||||
form.post(route('clientCase.activity.store', props.client_case), {
|
||||
onBefore: () => {
|
||||
if(form.due_date) {
|
||||
if (form.due_date) {
|
||||
form.due_date = new Date(form.due_date).toISOString().split('T')[0];
|
||||
}
|
||||
},
|
||||
},
|
||||
onSuccess: () => {
|
||||
close();
|
||||
form.reset();
|
||||
@@ -87,75 +97,46 @@ const store = () => {
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<DialogModal
|
||||
:show="show"
|
||||
@close="close"
|
||||
>
|
||||
<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
|
||||
<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"
|
||||
>
|
||||
id="activityAction" ref="activityActionSelect" v-model="form.action_id">
|
||||
<option v-for="a in actions" :value="a.id">{{ a.name }}</option>
|
||||
<!-- ... -->
|
||||
<!-- ... -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-4">
|
||||
<InputLabel for="activityDecision" value="Odločitev"/>
|
||||
<select
|
||||
<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"
|
||||
>
|
||||
id="activityDecision" ref="activityDecisionSelect" v-model="form.decision_id">
|
||||
<option v-for="d in decisions" :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"
|
||||
/>
|
||||
<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="false" :fixed="false" :close-on-auto-apply="true"
|
||||
:close-on-scroll="true" />
|
||||
<div class="col-span-6 sm:col-span-4">
|
||||
<InputLabel for="activityDueDate" value="Datum zapadlosti"/>
|
||||
<vue-date-picker
|
||||
id="activityDueDate"
|
||||
:enable-time-picker="false"
|
||||
format="dd.MM.yyyy"
|
||||
class="mt-1 block w-full"
|
||||
v-model="form.due_date"
|
||||
/>
|
||||
</div>
|
||||
<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"
|
||||
/>
|
||||
<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"
|
||||
>
|
||||
<BasicButton :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||
Shrani
|
||||
</BasicButton>
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,7 @@ const props = defineProps({
|
||||
|
||||
|
||||
let header = [
|
||||
C_TD.make('Pogodba', 'header'),
|
||||
C_TD.make('Datum', 'header'),
|
||||
C_TD.make('Akcija', 'header'),
|
||||
C_TD.make('Odločitev', 'header'),
|
||||
@@ -26,6 +27,7 @@ const createBody = (data) => {
|
||||
const dueDate = (p.due_date) ? new Date().toLocaleDateString('de') : null;
|
||||
|
||||
const cols = [
|
||||
C_TD.make(p.contract?.reference ?? ''),
|
||||
C_TD.make(createdDate, 'body' ),
|
||||
C_TD.make(p.action.name, 'body'),
|
||||
C_TD.make(p.decision.name, 'body'),
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<script setup>
|
||||
import DialogModal from '@/Components/DialogModal.vue'
|
||||
import InputLabel from '@/Components/InputLabel.vue'
|
||||
import TextInput from '@/Components/TextInput.vue'
|
||||
import PrimaryButton from '@/Components/PrimaryButton.vue'
|
||||
import SectionTitle from '@/Components/SectionTitle.vue'
|
||||
import { useForm } from '@inertiajs/vue3'
|
||||
|
||||
const props = defineProps({
|
||||
show: { type: Boolean, default: false },
|
||||
client_case: { type: Object, required: true },
|
||||
contract: { type: Object, required: true },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close', 'created'])
|
||||
const close = () => emit('close')
|
||||
|
||||
const form = useForm({
|
||||
reference: '',
|
||||
name: '',
|
||||
description: '',
|
||||
type: '',
|
||||
})
|
||||
|
||||
const submit = () => {
|
||||
form.post(route('clientCase.contract.object.store', { client_case: props.client_case.uuid, uuid: props.contract.uuid }), {
|
||||
preserveScroll: true,
|
||||
onSuccess: () => { emit('created'); form.reset(); close() },
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DialogModal :show="show" @close="close">
|
||||
<template #title>Dodaj premet</template>
|
||||
<template #content>
|
||||
<form @submit.prevent="submit">
|
||||
<SectionTitle class="mt-2 border-b mb-4">
|
||||
<template #title>Premet</template>
|
||||
</SectionTitle>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<InputLabel for="objRef" value="Referenca" />
|
||||
<TextInput id="objRef" v-model="form.reference" type="text" class="mt-1 block w-full" />
|
||||
</div>
|
||||
<div>
|
||||
<InputLabel for="objType" value="Tip" />
|
||||
<TextInput id="objType" v-model="form.type" type="text" class="mt-1 block w-full" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<InputLabel for="objName" value="Naziv" />
|
||||
<TextInput id="objName" v-model="form.name" type="text" class="mt-1 block w-full" required />
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<InputLabel for="objDesc" value="Opis" />
|
||||
<textarea id="objDesc" v-model="form.description" class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" rows="3" />
|
||||
</div>
|
||||
<div class="flex justify-end mt-6">
|
||||
<PrimaryButton :class="{ 'opacity-25': form.processing }" :disabled="form.processing">Shrani</PrimaryButton>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
</DialogModal>
|
||||
</template>
|
||||
@@ -0,0 +1,52 @@
|
||||
<script setup>
|
||||
import DialogModal from '@/Components/DialogModal.vue'
|
||||
|
||||
const props = defineProps({
|
||||
show: { type: Boolean, default: false },
|
||||
client_case: { type: Object, required: true },
|
||||
contract: { type: Object, default: null },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
const close = () => emit('close')
|
||||
|
||||
const items = () => Array.isArray(props.contract?.objects) ? props.contract.objects : []
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DialogModal :show="show" @close="close">
|
||||
<template #title>
|
||||
Premeti
|
||||
<span v-if="contract" class="ml-2 text-sm text-gray-500">(Pogodba: {{ contract.reference }})</span>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="mt-1 max-h-[60vh] overflow-y-auto">
|
||||
<div v-if="items().length > 0" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div v-for="o in items()" :key="o.id" class="rounded-lg border border-gray-200 bg-white shadow-sm">
|
||||
<div class="p-4">
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<div class="text-xs uppercase text-gray-500">Ref.</div>
|
||||
<div class="font-semibold text-gray-900">{{ o.reference || '-' }}</div>
|
||||
</div>
|
||||
<span class="ml-3 inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-xs text-gray-700">{{ o.type || '—' }}</span>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<div class="text-xs uppercase text-gray-500">Naziv</div>
|
||||
<div class="text-gray-900">{{ o.name || '-' }}</div>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<div class="text-xs uppercase text-gray-500">Opis</div>
|
||||
<div class="text-gray-700 whitespace-pre-wrap">{{ o.description || '' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center text-gray-500 py-3">Ni predmetov.</div>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-end">
|
||||
<button type="button" class="px-4 py-2 rounded-md border border-gray-300 text-gray-700 hover:bg-gray-50" @click="close">Zapri</button>
|
||||
</div>
|
||||
</template>
|
||||
</DialogModal>
|
||||
</template>
|
||||
@@ -5,15 +5,16 @@ import InputLabel from '@/Components/InputLabel.vue';
|
||||
import PrimaryButton from '@/Components/PrimaryButton.vue';
|
||||
import SectionTitle from '@/Components/SectionTitle.vue';
|
||||
import TextInput from '@/Components/TextInput.vue';
|
||||
import DatePickerField from '@/Components/DatePickerField.vue';
|
||||
import { useForm } from '@inertiajs/vue3';
|
||||
import { watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
client_case: Object,
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
types: Array
|
||||
show: { type: Boolean, default: false },
|
||||
types: Array,
|
||||
// Optional: when provided, drawer acts as edit mode
|
||||
contract: { type: Object, default: null },
|
||||
});
|
||||
|
||||
console.log(props.types);
|
||||
@@ -24,25 +25,59 @@ const close = () => {
|
||||
emit('close');
|
||||
}
|
||||
|
||||
//store contract
|
||||
// form state for create or edit
|
||||
const formContract = useForm({
|
||||
client_case_uuid: props.client_case.uuid,
|
||||
reference: '',
|
||||
start_date: new Date().toISOString(),
|
||||
type_id: props.types[0].id
|
||||
uuid: props.contract?.uuid ?? null,
|
||||
reference: props.contract?.reference ?? '',
|
||||
start_date: props.contract?.start_date ?? new Date().toISOString(),
|
||||
type_id: (props.contract?.type_id ?? props.contract?.type?.id) ?? props.types[0].id,
|
||||
description: props.contract?.description ?? '',
|
||||
// nested account fields, if exists
|
||||
initial_amount: props.contract?.account?.initial_amount ?? null,
|
||||
balance_amount: props.contract?.account?.balance_amount ?? null,
|
||||
});
|
||||
|
||||
const storeContract = () => {
|
||||
formContract.post(route('clientCase.contract.store', props.client_case), {
|
||||
// keep form in sync when switching between create and edit
|
||||
const applyContract = (c) => {
|
||||
formContract.uuid = c?.uuid ?? null
|
||||
formContract.reference = c?.reference ?? ''
|
||||
formContract.start_date = c?.start_date ?? new Date().toISOString()
|
||||
formContract.type_id = (c?.type_id ?? c?.type?.id) ?? props.types[0].id
|
||||
formContract.description = c?.description ?? ''
|
||||
formContract.initial_amount = c?.account?.initial_amount ?? null
|
||||
formContract.balance_amount = c?.account?.balance_amount ?? null
|
||||
}
|
||||
|
||||
watch(() => props.contract, (c) => {
|
||||
applyContract(c)
|
||||
})
|
||||
|
||||
watch(() => props.show, (open) => {
|
||||
if (open && !props.contract) {
|
||||
// reset for create
|
||||
applyContract(null)
|
||||
}
|
||||
})
|
||||
|
||||
const storeOrUpdate = () => {
|
||||
const isEdit = !!formContract.uuid
|
||||
const options = {
|
||||
onBefore: () => {
|
||||
formContract.start_date = formContract.start_date;
|
||||
formContract.start_date = formContract.start_date
|
||||
},
|
||||
onSuccess: () => {
|
||||
close();
|
||||
formContract.reset();
|
||||
close()
|
||||
// keep state clean; reset to initial
|
||||
if (!isEdit) formContract.reset()
|
||||
},
|
||||
preserveScroll: true
|
||||
});
|
||||
preserveScroll: true,
|
||||
}
|
||||
if (isEdit) {
|
||||
formContract.put(route('clientCase.contract.update', { client_case: props.client_case.uuid, uuid: formContract.uuid }), options)
|
||||
} else {
|
||||
formContract.post(route('clientCase.contract.store', props.client_case), options)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -52,9 +87,9 @@ const storeContract = () => {
|
||||
:show="show"
|
||||
@close="close"
|
||||
>
|
||||
<template #title>Dodaj pogodbo</template>
|
||||
<template #title>{{ formContract.uuid ? 'Uredi pogodbo' : 'Dodaj pogodbo' }}</template>
|
||||
<template #content>
|
||||
<form @submit.prevent="storeContract">
|
||||
<form @submit.prevent="storeOrUpdate">
|
||||
<SectionTitle class="mt-4 border-b mb-4">
|
||||
<template #title>
|
||||
Pogodba
|
||||
@@ -71,10 +106,13 @@ const storeContract = () => {
|
||||
autocomplete="contract-reference"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-4">
|
||||
<InputLabel for="contractStartDate" value="Datum pričetka"/>
|
||||
<vue-date-picker id="contractStartDate" :enable-time-picker="false" format="dd.MM.yyyy" class="mt-1 block w-full" v-model="formContract.start_date"></vue-date-picker>
|
||||
</div>
|
||||
<DatePickerField
|
||||
id="contractStartDate"
|
||||
label="Datum pričetka"
|
||||
v-model="formContract.start_date"
|
||||
format="dd.MM.yyyy"
|
||||
:enable-time-picker="false"
|
||||
/>
|
||||
<div class="col-span-6 sm:col-span-4">
|
||||
<InputLabel for="contractTypeSelect" value="Tip"/>
|
||||
<select
|
||||
@@ -86,13 +124,51 @@ const storeContract = () => {
|
||||
<!-- ... -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-4 mt-4">
|
||||
<InputLabel for="contractDescription" value="Opis"/>
|
||||
<textarea
|
||||
id="contractDescription"
|
||||
v-model="formContract.description"
|
||||
class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
|
||||
rows="3"
|
||||
/>
|
||||
</div>
|
||||
<SectionTitle class="mt-6 border-b mb-4">
|
||||
<template #title>
|
||||
Račun
|
||||
</template>
|
||||
</SectionTitle>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<InputLabel for="initialAmount" value="Predani znesek"/>
|
||||
<TextInput
|
||||
id="initialAmount"
|
||||
v-model.number="formContract.initial_amount"
|
||||
type="number"
|
||||
step="0.01"
|
||||
class="mt-1 block w-full"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<InputLabel for="balanceAmount" value="Odprti znesek"/>
|
||||
<TextInput
|
||||
id="balanceAmount"
|
||||
v-model.number="formContract.balance_amount"
|
||||
type="number"
|
||||
step="0.01"
|
||||
class="mt-1 block w-full"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end mt-4">
|
||||
<ActionMessage :on="formContract.recentlySuccessful" class="me-3">
|
||||
Shranjuje.
|
||||
</ActionMessage>
|
||||
|
||||
<PrimaryButton :class="{ 'opacity-25': formContract.processing }" :disabled="formContract.processing">
|
||||
Shrani
|
||||
{{ formContract.uuid ? 'Posodobi' : 'Shrani' }}
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,87 +1,164 @@
|
||||
<script setup>
|
||||
import BasicTable from '@/Components/BasicTable.vue';
|
||||
import { LinkOptions as C_LINK, TableColumn as C_TD, TableRow as C_TR} from '@/Shared/AppObjects';
|
||||
|
||||
import { FwbTable, FwbTableHead, FwbTableHeadCell, FwbTableBody, FwbTableRow, FwbTableCell } from 'flowbite-vue'
|
||||
import Dropdown from '@/Components/Dropdown.vue'
|
||||
import CaseObjectCreateDialog from './CaseObjectCreateDialog.vue'
|
||||
import CaseObjectsDialog from './CaseObjectsDialog.vue'
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||
import { faCircleInfo, faEllipsisVertical, faPenToSquare, faTrash, faListCheck, faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
const props = defineProps({
|
||||
client_case: Object,
|
||||
contract_types: Array,
|
||||
contracts: Array
|
||||
});
|
||||
contracts: { type: Array, default: () => [] },
|
||||
})
|
||||
|
||||
//Contract table
|
||||
let tableContractHeader = [
|
||||
C_TD.make('Ref.', 'header'),
|
||||
C_TD.make('Datum začetka', 'header'),
|
||||
C_TD.make('Tip', 'header')
|
||||
];
|
||||
const emit = defineEmits(['edit', 'delete', 'add-activity'])
|
||||
|
||||
const tableOptions = {
|
||||
editor_data: {
|
||||
form: {
|
||||
route: {name: 'clientCase.contract.update', params: {uuid: props.client_case.uuid}},
|
||||
route_remove: {name: 'clientCase.contract.delete', params: {uuid: props.client_case.uuid}},
|
||||
values: {
|
||||
uuid: null,
|
||||
reference: '',
|
||||
type_id: 1
|
||||
},
|
||||
key: 'uuid',
|
||||
index: {uuid: null},
|
||||
el: [
|
||||
{
|
||||
id: 'contractRefU',
|
||||
ref: 'contractRefUInput',
|
||||
bind: 'reference',
|
||||
type: 'text',
|
||||
label: 'Referenca',
|
||||
autocomplete: 'contract-reference'
|
||||
},
|
||||
{
|
||||
id: 'contractTypeU',
|
||||
ref: 'contractTypeSelectU',
|
||||
bind: 'type_id',
|
||||
type: 'select',
|
||||
label: 'Tip',
|
||||
selectOptions: props.contract_types.map(item => new Object({val: item.id, desc: item.name}))
|
||||
}
|
||||
]
|
||||
},
|
||||
title: 'contract'
|
||||
}
|
||||
const formatDate = (d) => {
|
||||
if (!d) return '-'
|
||||
const dt = new Date(d)
|
||||
return isNaN(dt.getTime()) ? '-' : dt.toLocaleDateString('de')
|
||||
}
|
||||
|
||||
const createContractTableBody = (data) => {
|
||||
let tableContractBody = [];
|
||||
|
||||
data.forEach((p) => {
|
||||
let startDate = new Date(p.start_date).toLocaleDateString('de');
|
||||
const cols = [
|
||||
C_TD.make(p.reference, 'body' ),
|
||||
C_TD.make(startDate, 'body' ),
|
||||
C_TD.make(p.type.name, 'body' ),
|
||||
];
|
||||
|
||||
tableContractBody.push(
|
||||
C_TR.make(
|
||||
cols,
|
||||
{
|
||||
class: '',
|
||||
title: p.reference,
|
||||
edit: true,
|
||||
ref: {key: 'uuid', val: p.uuid},
|
||||
editable: {
|
||||
reference: p.reference,
|
||||
type_id: p.type.id
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
});
|
||||
|
||||
return tableContractBody;
|
||||
const hasDesc = (c) => {
|
||||
const d = c?.description
|
||||
return typeof d === 'string' && d.trim().length > 0
|
||||
}
|
||||
|
||||
const onEdit = (c) => emit('edit', c)
|
||||
const onDelete = (c) => emit('delete', c)
|
||||
const onAddActivity = (c) => emit('add-activity', c)
|
||||
|
||||
// CaseObject dialog state
|
||||
import { ref } from 'vue'
|
||||
const showObjectDialog = ref(false)
|
||||
const showObjectsList = ref(false)
|
||||
const selectedContract = ref(null)
|
||||
const openObjectDialog = (c) => { selectedContract.value = c; showObjectDialog.value = true }
|
||||
const closeObjectDialog = () => { showObjectDialog.value = false; selectedContract.value = null }
|
||||
const openObjectsList = (c) => { selectedContract.value = c; showObjectsList.value = true }
|
||||
const closeObjectsList = () => { showObjectsList.value = false; selectedContract.value = null }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicTable :options="tableOptions" :header="tableContractHeader" :editor="true" :body="createContractTableBody(contracts)" />
|
||||
<div class="relative overflow-x-auto rounded-lg border border-gray-200 bg-white shadow-sm">
|
||||
<FwbTable hoverable striped class="text-sm">
|
||||
<FwbTableHead class="sticky top-0 z-10 bg-gray-50/90 backdrop-blur border-b border-gray-200 shadow-sm">
|
||||
<FwbTableHeadCell class="uppercase text-xs font-semibold tracking-wide text-gray-700 py-3">Ref.</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="uppercase text-xs font-semibold tracking-wide text-gray-700 py-3">Datum začetka</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="uppercase text-xs font-semibold tracking-wide text-gray-700 py-3">Tip</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="uppercase text-xs font-semibold tracking-wide text-gray-700 py-3 text-right">Predano</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="uppercase text-xs font-semibold tracking-wide text-gray-700 py-3 text-right">Odprto</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="uppercase text-xs font-semibold tracking-wide text-gray-700 py-3 text-center">Opis</FwbTableHeadCell>
|
||||
<FwbTableHeadCell class="w-px" />
|
||||
</FwbTableHead>
|
||||
<FwbTableBody>
|
||||
<template v-for="(c, i) in contracts" :key="c.uuid || i">
|
||||
<FwbTableRow>
|
||||
<FwbTableCell>{{ c.reference }}</FwbTableCell>
|
||||
<FwbTableCell>{{ formatDate(c.start_date) }}</FwbTableCell>
|
||||
<FwbTableCell>{{ c?.type?.name }}</FwbTableCell>
|
||||
<FwbTableCell class="text-right">{{ Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(c?.account?.initial_amount ?? 0) }}</FwbTableCell>
|
||||
<FwbTableCell class="text-right">{{ Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(c?.account?.balance_amount ?? 0) }}</FwbTableCell>
|
||||
<FwbTableCell class="text-center">
|
||||
<Dropdown v-if="hasDesc(c)" width="64" align="left">
|
||||
<template #trigger>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center h-8 w-8 rounded-full hover:bg-gray-100 focus:outline-none"
|
||||
:title="'Pokaži opis'"
|
||||
>
|
||||
<FontAwesomeIcon :icon="faCircleInfo" class="h-4 w-4 text-gray-700" />
|
||||
</button>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="max-w-sm px-3 py-2 text-sm text-gray-700 whitespace-pre-wrap">
|
||||
{{ c.description }}
|
||||
</div>
|
||||
</template>
|
||||
</Dropdown>
|
||||
<button
|
||||
v-else
|
||||
type="button"
|
||||
disabled
|
||||
class="inline-flex items-center justify-center h-8 w-8 rounded-full text-gray-400 cursor-not-allowed"
|
||||
:title="'Ni opisa'"
|
||||
>
|
||||
<FontAwesomeIcon :icon="faCircleInfo" class="h-4 w-4" />
|
||||
</button>
|
||||
</FwbTableCell>
|
||||
<FwbTableCell class="text-right whitespace-nowrap">
|
||||
<Dropdown align="right" width="56">
|
||||
<template #trigger>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center h-8 w-8 rounded-full hover:bg-gray-100 focus:outline-none"
|
||||
:title="'Actions'"
|
||||
>
|
||||
<FontAwesomeIcon :icon="faEllipsisVertical" class="h-4 w-4 text-gray-700" />
|
||||
</button>
|
||||
</template>
|
||||
<template #content>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
|
||||
@click="onEdit(c)"
|
||||
>
|
||||
<FontAwesomeIcon :icon="faPenToSquare" class="h-4 w-4 text-gray-600" />
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
|
||||
@click="openObjectsList(c)"
|
||||
>
|
||||
<FontAwesomeIcon :icon="faCircleInfo" class="h-4 w-4 text-gray-600" />
|
||||
<span>Predmeti</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
|
||||
@click="openObjectDialog(c)"
|
||||
>
|
||||
<FontAwesomeIcon :icon="faPlus" class="h-4 w-4 text-gray-600" />
|
||||
<span>Premet</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-3 py-2 text-left text-sm text-red-700 hover:bg-red-50 flex items-center gap-2"
|
||||
@click="onDelete(c)"
|
||||
>
|
||||
<FontAwesomeIcon :icon="faTrash" class="h-4 w-4 text-red-600" />
|
||||
<span>Briši</span>
|
||||
</button>
|
||||
<div class="my-1 border-t border-gray-100" />
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
|
||||
@click="onAddActivity(c)"
|
||||
>
|
||||
<FontAwesomeIcon :icon="faListCheck" class="h-4 w-4 text-gray-600" />
|
||||
<span>Aktivnost</span>
|
||||
</button>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</FwbTableCell>
|
||||
</FwbTableRow>
|
||||
</template>
|
||||
</FwbTableBody>
|
||||
</FwbTable>
|
||||
<div v-if="!contracts || contracts.length === 0" class="p-6 text-center text-sm text-gray-500">No contracts.</div>
|
||||
</div>
|
||||
<CaseObjectCreateDialog
|
||||
:show="showObjectDialog"
|
||||
@close="closeObjectDialog"
|
||||
:client_case="client_case"
|
||||
:contract="selectedContract"
|
||||
/>
|
||||
<CaseObjectsDialog
|
||||
:show="showObjectsList"
|
||||
@close="closeObjectsList"
|
||||
:client_case="client_case"
|
||||
:contract="selectedContract"
|
||||
/>
|
||||
</template>
|
||||
@@ -15,6 +15,7 @@ import { classifyDocument } from "@/Services/documents";
|
||||
import { router } from '@inertiajs/vue3';
|
||||
import { AngleDownIcon, AngleUpIcon } from "@/Utilities/Icons";
|
||||
import Pagination from "@/Components/Pagination.vue";
|
||||
import ConfirmDialog from "@/Components/ConfirmDialog.vue";
|
||||
|
||||
const props = defineProps({
|
||||
client: Object,
|
||||
@@ -49,22 +50,42 @@ const openViewer = (doc) => {
|
||||
};
|
||||
const closeViewer = () => { viewer.value.open = false; viewer.value.src = ''; };
|
||||
|
||||
const clientDetails = ref(true);
|
||||
const clientDetails = ref(false);
|
||||
|
||||
//Drawer add new contract
|
||||
// Contract drawer (create/edit)
|
||||
const drawerCreateContract = ref(false);
|
||||
|
||||
const contractEditing = ref(null);
|
||||
const openDrawerCreateContract = () => {
|
||||
contractEditing.value = null;
|
||||
drawerCreateContract.value = true;
|
||||
};
|
||||
const openDrawerEditContract = (c) => {
|
||||
contractEditing.value = c;
|
||||
drawerCreateContract.value = true;
|
||||
};
|
||||
|
||||
//Drawer add new activity
|
||||
const drawerAddActivity = ref(false);
|
||||
const activityContractUuid = ref(null);
|
||||
|
||||
const openDrawerAddActivity = () => {
|
||||
const openDrawerAddActivity = (c = null) => {
|
||||
activityContractUuid.value = c?.uuid ?? null;
|
||||
drawerAddActivity.value = true;
|
||||
};
|
||||
|
||||
// delete confirmation
|
||||
const confirmDelete = ref({ show: false, contract: null })
|
||||
const requestDeleteContract = (c) => { confirmDelete.value = { show: true, contract: c } }
|
||||
const closeConfirmDelete = () => { confirmDelete.value.show = false; confirmDelete.value.contract = null }
|
||||
const doDeleteContract = () => {
|
||||
const c = confirmDelete.value.contract
|
||||
if (!c) return closeConfirmDelete()
|
||||
router.delete(route('clientCase.contract.delete', { client_case: props.client_case.uuid, uuid: c.uuid }), {
|
||||
preserveScroll: true,
|
||||
onFinish: () => closeConfirmDelete(),
|
||||
})
|
||||
}
|
||||
|
||||
//Close drawer (all)
|
||||
const closeDrawer = () => {
|
||||
drawerCreateContract.value = false;
|
||||
@@ -154,34 +175,15 @@ const hideClietnDetails = () => {
|
||||
:client_case="client_case"
|
||||
:contracts="contracts"
|
||||
:contract_types="contract_types"
|
||||
@edit="openDrawerEditContract"
|
||||
@delete="requestDeleteContract"
|
||||
@add-activity="openDrawerAddActivity"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Documents section -->
|
||||
<div class="pt-12">
|
||||
<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">
|
||||
<div class="mx-auto max-w-4x1">
|
||||
<div class="flex justify-between p-4">
|
||||
<SectionTitle>
|
||||
<template #title>Dokumenti</template>
|
||||
</SectionTitle>
|
||||
<FwbButton @click="openUpload">Dodaj</FwbButton>
|
||||
</div>
|
||||
<DocumentsTable :documents="documents" @view="openViewer" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DocumentUploadDialog
|
||||
:show="showUpload"
|
||||
@close="closeUpload"
|
||||
@uploaded="onUploaded"
|
||||
:post-url="route('clientCase.document.store', client_case)"
|
||||
/>
|
||||
<DocumentViewerDialog :show="viewer.open" :src="viewer.src" :title="viewer.title" @close="closeViewer" />
|
||||
|
||||
<div class="pt-12 pb-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">
|
||||
@@ -199,17 +201,55 @@ const hideClietnDetails = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Documents section -->
|
||||
<div class="pt-12">
|
||||
<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">
|
||||
<div class="mx-auto max-w-4x1">
|
||||
<div class="flex justify-between p-4">
|
||||
<SectionTitle>
|
||||
<template #title>Dokumenti</template>
|
||||
</SectionTitle>
|
||||
<FwbButton @click="openUpload">Dodaj</FwbButton>
|
||||
</div>
|
||||
<DocumentsTable
|
||||
:documents="documents"
|
||||
@view="openViewer"
|
||||
:download-url-builder="doc => route('clientCase.document.download', { client_case: client_case.uuid, document: doc.uuid })"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DocumentUploadDialog
|
||||
:show="showUpload"
|
||||
@close="closeUpload"
|
||||
@uploaded="onUploaded"
|
||||
:post-url="route('clientCase.document.store', client_case)"
|
||||
/>
|
||||
<DocumentViewerDialog :show="viewer.open" :src="viewer.src" :title="viewer.title" @close="closeViewer" />
|
||||
</AppLayout>
|
||||
<ContractDrawer
|
||||
:show="drawerCreateContract"
|
||||
@close="closeDrawer"
|
||||
:types="contract_types"
|
||||
:client_case="client_case"
|
||||
:contract="contractEditing"
|
||||
/>
|
||||
<ActivityDrawer
|
||||
:show="drawerAddActivity"
|
||||
@close="closeDrawer"
|
||||
:client_case="client_case"
|
||||
:actions="actions"
|
||||
:contract-uuid="activityContractUuid"
|
||||
/>
|
||||
<ConfirmDialog
|
||||
:show="confirmDelete.show"
|
||||
title="Izbriši pogodbo"
|
||||
message="Ali ste prepričani, da želite izbrisati pogodbo?"
|
||||
confirm-text="Izbriši"
|
||||
:danger="true"
|
||||
@close="closeConfirmDelete"
|
||||
@confirm="doDeleteContract"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -133,7 +133,7 @@ async function uploadAndPreview() {
|
||||
processResult.value = null;
|
||||
const fd = new window.FormData();
|
||||
fd.append('file', form.file);
|
||||
if (Number.isFinite(form.import_template_id)) {
|
||||
if (form.import_template_id !== null && form.import_template_id !== undefined && String(form.import_template_id).trim() !== '') {
|
||||
fd.append('import_template_id', String(form.import_template_id));
|
||||
}
|
||||
if (form.client_uuid) {
|
||||
|
||||
@@ -8,6 +8,7 @@ const props = defineProps({
|
||||
import: Object,
|
||||
templates: Array,
|
||||
clients: Array,
|
||||
client: Object
|
||||
});
|
||||
|
||||
const importId = ref(props.import?.id || null);
|
||||
@@ -227,13 +228,13 @@ const fieldOptionsByEntity = {
|
||||
|
||||
// Local state for selects
|
||||
const form = ref({
|
||||
client_uuid: null,
|
||||
client_uuid: props.client.uuid,
|
||||
import_template_id: props.import?.import_template_id || null,
|
||||
});
|
||||
|
||||
// Initialize client_uuid from numeric client_id using provided clients list
|
||||
if (props.import?.client_id) {
|
||||
const found = (props.clients || []).find(c => c.id === props.import.client_id);
|
||||
// Initialize client_uuid from import.client_uuid (preferred) using provided clients list
|
||||
if (props.import?.client_uuid) {
|
||||
const found = (props.clients || []).find(c => c.uuid === props.import.client_uuid);
|
||||
form.value.client_uuid = found ? found.uuid : null;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ function statusBadge(status) {
|
||||
<td class="p-2 whitespace-nowrap">{{ new Date(imp.created_at).toLocaleString() }}</td>
|
||||
<td class="p-2">{{ imp.original_name }}</td>
|
||||
<td class="p-2"><span :class="['px-2 py-0.5 rounded text-xs', statusBadge(imp.status)]">{{ imp.status }}</span></td>
|
||||
<td class="p-2">{{ imp.client?.uuid ?? '—' }}</td>
|
||||
<td class="p-2">{{ imp.client?.person?.full_name ?? '—' }}</td>
|
||||
<td class="p-2">{{ imp.template?.name ?? '—' }}</td>
|
||||
<td class="p-2 space-x-2">
|
||||
<Link :href="route('imports.continue', { import: imp.uuid })" class="px-2 py-1 rounded bg-gray-200 text-gray-800 text-xs">Poglej</Link>
|
||||
|
||||
@@ -181,6 +181,7 @@ const store = () => {
|
||||
:searchable="true"
|
||||
:taggable="false"
|
||||
placeholder="Izberi segment"
|
||||
:append-to-body="true"
|
||||
:custom-label="(opt) => (segmentOptions.find(s=>s.id===opt)?.name || '')"
|
||||
/>
|
||||
</div>
|
||||
@@ -196,6 +197,7 @@ const store = () => {
|
||||
track-by="id"
|
||||
:taggable="true"
|
||||
placeholder="Dodaj odločitev"
|
||||
:append-to-body="true"
|
||||
label="name"
|
||||
/>
|
||||
</div>
|
||||
@@ -256,6 +258,7 @@ const store = () => {
|
||||
:searchable="true"
|
||||
:taggable="false"
|
||||
placeholder="Izberi segment"
|
||||
:append-to-body="true"
|
||||
:custom-label="(opt) => (segmentOptions.find(s=>s.id===opt)?.name || '')"
|
||||
/>
|
||||
</div>
|
||||
@@ -270,6 +273,7 @@ const store = () => {
|
||||
track-by="id"
|
||||
:taggable="true"
|
||||
placeholder="Dodaj odločitev"
|
||||
:append-to-body="true"
|
||||
label="name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -159,6 +159,7 @@ const store = () => {
|
||||
track-by="id"
|
||||
:taggable="true"
|
||||
placeholder="Dodaj akcijo"
|
||||
:append-to-body="true"
|
||||
label="name"
|
||||
/>
|
||||
</div>
|
||||
@@ -219,6 +220,7 @@ const store = () => {
|
||||
track-by="id"
|
||||
:taggable="true"
|
||||
placeholder="Dodaj akcijo"
|
||||
:append-to-body="true"
|
||||
label="name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user