complete rewrite of contracts create

shift-build-2464
Nadim Salloum 2021-06-19 13:43:08 +03:00
parent 920b056ee5
commit 88655172b2
15 changed files with 1201 additions and 1948 deletions

View File

@ -340,7 +340,7 @@ class CarController extends Controller
{ {
return [ return [
'stammnummer' => ['required', 'unique:cars', 'string', 'size:11', 'regex:/[0-9]{3}[.][0-9]{3}[.][0-9]{3}/i'], 'stammnummer' => ['required', 'unique:cars', 'string', 'size:11', 'regex:/[0-9]{3}[.][0-9]{3}[.][0-9]{3}/i'],
'vin' => ['required', 'unique:cars', 'string', 'size:17'], 'vin' => ['required', 'unique:cars', 'string', 'size:17', 'min:17', 'max:17'],
'initial_date' => ['required', 'date'], 'initial_date' => ['required', 'date'],
'last_check_date' => ['required', 'date'], 'last_check_date' => ['required', 'date'],
'colour' => ['nullable', 'max:75'], 'colour' => ['nullable', 'max:75'],
@ -426,7 +426,7 @@ class CarController extends Controller
$car->update( $car->update(
$request->validate([ $request->validate([
'stammnummer' => ['required', 'unique:cars,stammnummer,' . $car->id, 'string', 'size:11', 'regex:/[0-9]{3}[.][0-9]{3}[.][0-9]{3}/i'], 'stammnummer' => ['required', 'unique:cars,stammnummer,' . $car->id, 'string', 'size:11', 'regex:/[0-9]{3}[.][0-9]{3}[.][0-9]{3}/i'],
'vin' => ['required', 'unique:cars,vin,' . $car->id, 'string', 'size:17'], 'vin' => ['required', 'unique:cars,vin,' . $car->id, 'string', 'size:17', 'min:17', 'max:17'],
'initial_date' => ['required', 'date'], 'initial_date' => ['required', 'date'],
'last_check_date' => ['required', 'date'], 'last_check_date' => ['required', 'date'],
'colour' => ['nullable', 'max:75'], 'colour' => ['nullable', 'max:75'],

View File

@ -53,53 +53,28 @@ class ContractController extends Controller
]); ]);
} }
public function create(Request $request, string $type, Car $car, Contact $contact) public function create(Request $request)
{ {
$type = (string)($request->get('type') ?: '1');
$car = Car::find($request->get('car'));
$contact = Contact::find($request->get('contact'));
return Inertia::render('Contracts/Create', [ return Inertia::render('Contracts/Create', [
'car' => $this->getCarFields($car), 'car' => $this->getCarFields($car),
'contact' => $this->getContactFields($contact), 'contact' => $this->getContactFields($contact),
'is_sell_contract' => ContractType::coerce($type) == ContractType::SellContract,
'type' => $type, 'type' => $type,
'car_first' => (bool)$request->query('carFirst'),
'insurance_types' => InsuranceType::asSelectArray(), 'insurance_types' => InsuranceType::asSelectArray(),
]);
}
public function createFromCar(Request $request, string $type, Car $car)
{
return Inertia::render('Contracts/CreateFromCar', [
'car' => $this->getCarFields($car),
'is_sell_contract' => ContractType::coerce($type) == ContractType::SellContract,
'type' => $type,
'contacts' => Contact::all()->map(function ($contact) { 'contacts' => Contact::all()->map(function ($contact) {
return $this->getContactFields($contact); return $this->getContactFields($contact);
}), }),
]); 'cars' => Car::all()->map(function ($car) {
}
public function createFromContact(Request $request, string $type, Contact $contact)
{
$contractType = ContractType::coerce($type);
$cars = $contractType->value == ContractType::SellContract ? Car::unsoldOnly() : Car::soldOnly();
return Inertia::render('Contracts/CreateFromContact', [
'contact' => $this->getContactFields($contact),
'is_sell_contract' => $contractType == ContractType::SellContract,
'type' => $type,
'cars' => $cars->get()->map(function ($car) {
return $this->getCarFields($car); return $this->getCarFields($car);
}), }),
'brands' => Brand::all()->map(function ($brand) { 'brands' => Brand::all()->map(function ($brand) {
return [ return [
'id' => $brand->id, 'id' => $brand->id,
'name' => $brand->name, 'name' => $brand->name,
'models' => $brand->carModels()->get() 'models' => $brand->carModels()->get(['id', 'name']),
->map(function ($carModel) {
return [
'id' => $carModel->id,
'name' => $carModel->name,
];
}),
]; ];
}), }),
]); ]);
@ -107,11 +82,20 @@ class ContractController extends Controller
private function getCarFields(?Car $car) { private function getCarFields(?Car $car) {
if (!$car) { if (!$car) {
return null; return [
'name' => null,
'id' => null,
'stammnummer' => null,
'vin' => null,
'name' => null,
'colour' => null,
'initial_date' => null,
];
} }
return [ return [
'name' => $car->name,
'id' => $car->id, 'id' => $car->id,
'stammnummer' => $car->stammnummer, 'stammnummer' => $car->stammnummer,
'vin' => $car->vin, 'vin' => $car->vin,
'name' => $car->name, 'name' => $car->name,
'colour' => $car->colour, 'colour' => $car->colour,
@ -121,7 +105,20 @@ class ContractController extends Controller
private function getContactFields(?Contact $contact) { private function getContactFields(?Contact $contact) {
if (!$contact) { if (!$contact) {
return null; return [
'id' => null,
'title' => null,
'name' => null,
'firstname' => null,
'lastname' => null,
'phone' => null,
'address' => null,
'zip' => null,
'city' => null,
'country' => null,
'company' => null,
'email' => null,
];
} }
return [ return [
'id' => $contact->id, 'id' => $contact->id,

View File

@ -27,6 +27,10 @@ class Car extends Model
public function getNameAttribute() public function getNameAttribute()
{ {
if (!$this->carModel) {
return '';
}
$out = $this->brand->name . ' ' . $this->carModel->name; $out = $this->brand->name . ' ' . $this->carModel->name;
return $out; return $out;

2192
public/js/app.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -22,13 +22,14 @@ export default {
return this.type === 1 ? 'Verkaufsvertrag' : 'Ankaufsvertrag'; return this.type === 1 ? 'Verkaufsvertrag' : 'Ankaufsvertrag';
}, },
link() { link() {
// return route('contracts.create') + '?carId=1&contactId=1&type=' + String(this.type);
if (this.contactId && this.carId) { if (this.contactId && this.carId) {
return route('contracts.create', [this.type, this.carId, this.contactId]); return route('contracts.create', {type: this.type, car: this.carId, contact: this.contactId});
} }
if (this.contactId) { if (this.contactId) {
return route('contracts.create_from_contact', [this.type, this.contactId]); return route('contracts.create', {type: this.type, contact: this.contactId});
} }
return route('contracts.create_from_car', [this.type, this.carId]); return route('contracts.create', {type: this.type, car: this.carId, contact: this.contactId});
}, },
}, },
}; };

View File

@ -0,0 +1,150 @@
<template>
<jet-form-section :emptyBg="existing_car.id !== null">
<template #title>
Auto
</template>
<template #description>
{{ existing_car.id ? 'Ausgewähltes Auto' : 'Auto auswählen oder neu erfassen' }}
</template>
<template v-if="existing_car.id" #form>
<car-card v-if="car.id" class="col-span-3" :car="car" hideEmpty="true" />
</template>
<template v-else #form>
<div class="col-span-3">
<jet-label for="car" value="Auto" />
<multiselect @select="onCarChange" :disabled="createCar" v-model="car" label="name" track-by="id" :options="carsChoice" class="mt-1 block w-full" placeholder="Auto auswählen" />
</div>
<div class="col-span-6">
<car-card v-if="car.id" class="mt-3 col-span-3" :car="car" hideEmpty="true" />
</div>
<div class="col-span-6">
oder
</div>
<div v-if="!createCar" class="col-span-6">
<button @click="openCarForm" class="bg-indigo-800 hover:bg-indigo-700 active:bg-indigo-900 focus:border-indigo-900 focus:ring-indigo-300 justify-center inline-flex items-center px-4 py-2 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest focus:outline-none focus:ring disabled:opacity-25 transition">
Neu erfassen
</button>
</div>
<div v-else class="col-span-6">
<p class="w-full mb-1 font-bold">Neues Auto erfassen:</p>
<form @submit="submitCreateCarForm">
<div class="grid grid-cols-6 gap-6">
<car-form-fields :form="car" :car_model="car_model" :brand="brand" :brands="brands" />
<div class="col-span-6 sm:col-span-4 flex items-center justify-end text-right">
<jet-button>
Auto speichern
</jet-button>
</div>
</div>
</form>
</div>
</template>
</jet-form-section>
</template>
<script>
import CarFormFields from '@/Pages/Cars/Components/CarFormFields.vue';
import CarCard from '@/Components/CarCard.vue';
import JetFormSection from '@/Jetstream/FormSection';
import Multiselect from 'vue-multiselect';
import JetLabel from '@/Jetstream/Label.vue';
import JetButton from '@/Jetstream/Button';
export default {
components: {
CarCard,
CarFormFields,
JetFormSection,
Multiselect,
JetLabel,
JetButton,
},
props: {
existing_car: Object,
cars: Object,
brands: Object,
type: String,
},
data() {
return {
carsChoice: this.cars,
car: {
id: this.existing_car?.id ?? null,
name: this.existing_car?.name ?? null,
stammnummer: this.existing_car?.stammnummer ?? null,
vin: this.existing_car?.vin ?? null,
colour: this.existing_car?.colour ?? null,
car_model_id: this.existing_car?.car_model_id ?? null,
initial_date: this.existing_car?.initial_date ?? null,
last_check_date: this.existing_car?.last_check_date ?? null,
kilometers: this.existing_car?.kilometers ?? null,
known_damage: this.existing_car?.known_damage ?? null,
notes: this.existing_car?.notes ?? null,
errors: {},
},
brand: { id: null, name: null },
car_model: { id: null, name: null },
createCar: false,
};
},
computed: {
contractType() {
return this.isSellContract ? 'Verkaufsvertrag' : 'Ankaufsvertrag';
},
contactType() {
return this.isSellContract ? 'Käufer' : 'Verkäufer';
},
isSellContract() {
return this.type == '1';
},
emptyCar() {
return {
id: null,
stammnummer: null,
vin: null,
colour: null,
car_model_id: null,
initial_date: null,
last_check_date: null,
kilometers: null,
known_damage: null,
notes: null,
errors: {},
};
},
},
methods: {
openCarForm() {
this.createCar = true;
this.car = this.emptyCar;
},
onCarChange(car, val) {
this.triggerChange(car.id);
},
triggerChange(val) {
this.$emit('car-id-change', val);
},
submitCreateCarForm(e) {
e.preventDefault();
axios.post(this.route('cars.store_for_contract'), this.car)
.then((res) => {
this.carsChoice.push(res.data);
this.car = res.data;
this.createCar = false;
this.triggerChange(this.car.id);
}).catch((err) => {
if (err.response) {
const { errors } = err.response.data;
Object.keys(errors).map((key, index) => {
errors[key] = errors[key].join(' ');
});
this.car.errors = errors;
}
});
},
},
};
</script>
<style src="vue-multiselect/dist/vue-multiselect.css"></style>

View File

@ -0,0 +1,146 @@
<template>
<jet-form-section :emptyBg="existing_contact.id !== null">
<template #title>
{{ contactType }}
</template>
<template #description>
{{ contactType }} auswählen oder neu erfassen
</template>
<template v-if="existing_contact.id" #form>
<contact-card v-if="existing_contact.id" class="col-span-3" :contact="contact" />
</template>
<template v-else #form>
<div class="col-span-3">
<jet-label for="contact" :value="contactType" />
<multiselect @select="onContactChange" @remove="contact = emptyContact" :disabled="createContact" v-model="contact" label="title" track-by="id" :options="contactsChoice" class="mt-1 block w-full" placeholder="Vertragspartner auswählen" />
</div>
<div class="col-span-6">
<contact-card v-if="contact.id" class="mt-3 col-span-3" :contact="contact" />
</div>
<div class="col-span-6">
oder
</div>
<div v-if="!createContact" class="col-span-6">
<button @click="openContactForm" class="bg-indigo-800 hover:bg-indigo-700 active:bg-indigo-900 focus:border-indigo-900 focus:ring-indigo-300 justify-center inline-flex items-center px-4 py-2 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest focus:outline-none focus:ring disabled:opacity-25 transition">
Neu erfassen
</button>
</div>
<div v-else class="col-span-6">
<p class="w-full mb-1 font-bold">Neuen Kontakt erfassen:</p>
<form @submit="submitCreateContactForm">
<div class="grid grid-cols-6 gap-6">
<contact-form-fields :form="contact" />
<div class="col-span-6 sm:col-span-4 flex items-center justify-end text-right">
<jet-button>
Kontakt speichern
</jet-button>
</div>
</div>
</form>
</div>
</template>
</jet-form-section>
</template>
<script>
import ContactFormFields from '@/Pages/Contacts/Components/ContactFormFields.vue';
import ContactCard from '@/Components/ContactCard.vue';
import JetFormSection from '@/Jetstream/FormSection';
import Multiselect from 'vue-multiselect';
import JetLabel from '@/Jetstream/Label.vue';
import JetButton from '@/Jetstream/Button';
export default {
components: {
ContactCard,
JetFormSection,
ContactFormFields,
Multiselect,
JetLabel,
JetButton,
},
props: {
existing_contact: Object,
contacts: Object,
type: String,
},
data() {
return {
contactsChoice: this.contacts,
createContact: false,
contact: {
id: this.existing_contact?.id ?? null,
firstname: this.existing_contact?.firstname ?? null,
name: this.existing_contact?.name ?? null,
title: this.existing_contact?.title ?? null,
lastname: this.existing_contact?.lastname ?? null,
company: this.existing_contact?.company ?? null,
email: this.existing_contact?.email ?? null,
phone: this.existing_contact?.phone ?? null,
address: this.existing_contact?.address ?? null,
zip: this.existing_contact?.zip ?? null,
city: this.existing_contact?.city ?? null,
country: this.existing_contact?.country ?? 'CH',
notes: this.existing_contact?.notes ?? null,
errors: {},
},
};
},
computed: {
contactType() {
return this.isSellContract ? 'Käufer' : 'Verkäufer';
},
isSellContract() {
return this.type === '1';
},
emptyContact() {
return {
id: null,
firstname: null,
lastname: null,
company: null,
email: null,
phone: null,
address: null,
zip: null,
city: null,
country: 'CH',
notes: null,
errors: {},
};
},
},
methods: {
openContactForm() {
this.createContact = true;
this.contact = this.emptyContact;
},
onContactChange(contact, val) {
this.triggerChange(contact.id);
},
triggerChange(val) {
this.$emit('contact-id-change', val);
},
submitCreateContactForm(e) {
e.preventDefault();
axios.post(this.route('contacts.store_for_contract'), this.contact)
.then((res) => {
this.contactsChoice.push(res.data);
this.contact = res.data;
this.createContact = false;
this.triggerChange(this.contact.id);
}).catch((err) => {
if (err.response) {
const { errors } = err.response.data;
Object.keys(errors).map((key, index) => {
errors[key] = errors[key].join(' ');
});
this.contact.errors = errors;
}
});
},
},
};
</script>
<style src="vue-multiselect/dist/vue-multiselect.css"></style>

View File

@ -16,6 +16,20 @@
</jet-nav-link> </jet-nav-link>
</div> </div>
<div class="mb-4 px-4">
<p class="text-sm font-semibold mb-1 text-indigo-100 flex items-center">
Verträge
</p>
<jet-nav-link :href="route('contracts.create', {type: 0})" :active="onBasicContractCreate && type == '0'">
<unicon fill="currentColor" class="mr-2" height="22" width="22" name="plus-circle"></unicon>
Neuer Einkauf
</jet-nav-link>
<jet-nav-link :href="route('contracts.create', {type: 1})" :active="onBasicContractCreate && type == '1'">
<unicon fill="currentColor" class="mr-2" height="22" width="22" name="plus-circle"></unicon>
Neuer Verkauf
</jet-nav-link>
</div>
<div class="mb-4 px-4"> <div class="mb-4 px-4">
<p class="text-sm font-semibold mb-1 text-indigo-100 flex items-center"> <p class="text-sm font-semibold mb-1 text-indigo-100 flex items-center">
Autos Autos
@ -72,6 +86,18 @@ export default {
}, },
computed: { computed: {
...mapState(['sideBarOpen']), ...mapState(['sideBarOpen']),
type() {
let params = new URLSearchParams(window.location.search);
return params.get('type');
},
onBasicContractCreate() {
if (!route().current('contracts.create')) {
return false;
}
let params = new URLSearchParams(window.location.search);
return !(params.get('car') || params.get('contact'));
},
}, },
}; };
</script> </script>

View File

@ -44,8 +44,6 @@
import ShowPage from '@/Components/ShowPage.vue'; import ShowPage from '@/Components/ShowPage.vue';
import BreadCrumb from '@/Components/BreadCrumb.vue'; import BreadCrumb from '@/Components/BreadCrumb.vue';
import CarCard from '@/Components/CarCard.vue'; import CarCard from '@/Components/CarCard.vue';
import BuyContractCard from '@/Components/BuyContractCard.vue';
import SellContractCard from '@/Components/SellContractCard.vue';
import EditButton from '@/Components/Buttons/EditButton.vue'; import EditButton from '@/Components/Buttons/EditButton.vue';
import DeleteButton from '@/Components/Buttons/DeleteButton.vue'; import DeleteButton from '@/Components/Buttons/DeleteButton.vue';
import RestoreButton from '@/Components/Buttons/RestoreButton.vue'; import RestoreButton from '@/Components/Buttons/RestoreButton.vue';
@ -56,8 +54,6 @@ export default {
ShowPage, ShowPage,
BreadCrumb, BreadCrumb,
CarCard, CarCard,
BuyContractCard,
SellContractCard,
EditButton, EditButton,
DeleteButton, DeleteButton,
RestoreButton, RestoreButton,

View File

@ -47,8 +47,6 @@
import ShowPage from '@/Components/ShowPage.vue'; import ShowPage from '@/Components/ShowPage.vue';
import BreadCrumb from '@/Components/BreadCrumb.vue'; import BreadCrumb from '@/Components/BreadCrumb.vue';
import ContactCard from '@/Components/ContactCard.vue'; import ContactCard from '@/Components/ContactCard.vue';
import BuyContractCard from '@/Components/BuyContractCard.vue';
import SellContractCard from '@/Components/SellContractCard.vue';
import EditButton from '@/Components/Buttons/EditButton.vue'; import EditButton from '@/Components/Buttons/EditButton.vue';
import DeleteButton from '@/Components/Buttons/DeleteButton.vue'; import DeleteButton from '@/Components/Buttons/DeleteButton.vue';
import RestoreButton from '@/Components/Buttons/RestoreButton.vue'; import RestoreButton from '@/Components/Buttons/RestoreButton.vue';
@ -59,8 +57,6 @@ export default {
ShowPage, ShowPage,
BreadCrumb, BreadCrumb,
ContactCard, ContactCard,
BuyContractCard,
SellContractCard,
EditButton, EditButton,
DeleteButton, DeleteButton,
RestoreButton, RestoreButton,

View File

@ -1,60 +1,58 @@
<template> <template>
<div class="max-w-7xl"> <jet-form-section @submitted="submitForm">
<jet-form-section @submitted="submitForm"> <template #title>
<template #title> <slot name="title"></slot>
<slot name="title"></slot> </template>
</template>
<template #description> <template #description>
<slot name="description"></slot> <slot name="description"></slot>
</template> </template>
<template #form> <template #form>
<div class="col-span-3 grid grid-cols-6 gap-3"> <div class="col-span-3 grid grid-cols-6 gap-3">
<div class="col-span-6 sm:col-span-4"> <div class="col-span-6 sm:col-span-4">
<jet-label for="date" value="Datum" /> <jet-label for="date" value="Datum" />
<datepicker id="date" ref="date" v-model="form.date" inputFormat="dd.MM.yyyy" class="border-gray-300 rounded-md shadow-sm mt-1 block w-full" /> <datepicker id="date" ref="date" v-model="form.date" inputFormat="dd.MM.yyyy" class="border-gray-300 rounded-md shadow-sm mt-1 block w-full" />
<jet-input-error :message="form.errors.date" class="mt-2" /> <jet-input-error :message="form.errors.date" class="mt-2" />
</div> </div>
<div class="col-span-6 sm:col-span-4"> <div class="col-span-6 sm:col-span-4">
<jet-label for="delivery_date" value="Lieferdatum" /> <jet-label for="delivery_date" value="Lieferdatum" />
<datepicker id="delivery_date" ref="delivery_date" v-model="form.delivery_date" inputFormat="dd.MM.yyyy" class="border-gray-300 rounded-md shadow-sm mt-1 block w-full" /> <datepicker id="delivery_date" ref="delivery_date" v-model="form.delivery_date" inputFormat="dd.MM.yyyy" class="border-gray-300 rounded-md shadow-sm mt-1 block w-full" />
<jet-input-error :message="form.errors.delivery_date" class="mt-2" /> <jet-input-error :message="form.errors.delivery_date" class="mt-2" />
</div>
<div class="col-span-6 sm:col-span-4">
<jet-label for="price" value="Betrag" />
<currency-input v-model="form.price" :options="currencyOptions" id="price" class="w-full mt-1 block border-gray-300 rounded-md shadow-sm" ref="price"/>
<jet-input-error :message="form.errors.price" class="mt-2" />
</div>
<div v-if="form.is_sell_contract" class="col-span-6 sm:col-span-4">
<jet-label for="insurance_type" value="Versicherung" />
<select v-model="form.insurance_type" class="mt-1 block w-full border-gray-300 rounded-md shadow-sm">
<option v-for="(insurance, index) in insurance_types" :value="index" v-bind:key="index" :selected="form.insurance_type == index">{{ insurance }}</option>
</select>
<jet-input-error :message="form.errors.insurance_type" class="mt-2" />
</div>
<div class="col-span-6 sm:col-span-4">
<jet-label for="notes" value="Bemerkungen" />
<textarea class="mt-1 block w-full border-gray-300 rounded-md shadow-sm" v-model="form.notes" ref="input">
</textarea>
<jet-input-error :message="form.errors.notes" class="mt-2" />
</div>
</div> </div>
</template>
<template #actions> <div class="col-span-6 sm:col-span-4">
<jet-action-message :on="form.recentlySuccessful" class="mr-3"> <jet-label for="price" value="Betrag" />
{{ meta.on_success }} <currency-input v-model="form.price" :options="currencyOptions" id="price" class="w-full mt-1 block border-gray-300 rounded-md shadow-sm" ref="price"/>
</jet-action-message> <jet-input-error :message="form.errors.price" class="mt-2" />
</div>
<jet-button :class="{ 'opacity-25': form.processing }" :disabled="form.processing"> <div v-if="form.is_sell_contract" class="col-span-6 sm:col-span-4">
{{ meta.button_text }} <jet-label for="insurance_type" value="Versicherung" />
</jet-button> <select v-model="form.insurance_type" class="mt-1 block w-full border-gray-300 rounded-md shadow-sm">
</template> <option v-for="(insurance, index) in insurance_types" :value="index" v-bind:key="index" :selected="form.insurance_type == index">{{ insurance }}</option>
</jet-form-section> </select>
</div> <jet-input-error :message="form.errors.insurance_type" class="mt-2" />
</div>
<div class="col-span-6 sm:col-span-4">
<jet-label for="notes" value="Bemerkungen" />
<textarea class="mt-1 block w-full border-gray-300 rounded-md shadow-sm" v-model="form.notes" ref="input">
</textarea>
<jet-input-error :message="form.errors.notes" class="mt-2" />
</div>
</div>
</template>
<template #actions>
<jet-action-message :on="form.recentlySuccessful" class="mr-3">
{{ meta.on_success }}
</jet-action-message>
<jet-button :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
{{ meta.button_text }}
</jet-button>
</template>
</jet-form-section>
</template> </template>
<script> <script>
@ -95,6 +93,8 @@ export default {
}, },
methods: { methods: {
submitForm() { submitForm() {
this.form.car_id = this.data.car_id;
this.form.contact_id = this.data.contact_id;
this.form.submit(this.meta.method, this.meta.route); this.form.submit(this.meta.method, this.meta.route);
}, },
}, },

View File

@ -2,49 +2,19 @@
<layout> <layout>
<template #header> <template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight"> <h2 class="font-semibold text-xl text-gray-800 leading-tight">
<bread-crumb v-if="car_first" text="Autos" :href="route('cars')" /> <bread-crumb v-if="!car.id && !contact.id" text="Verträge" :href="route('dashboard')" />
<bread-crumb v-else text="Kontakte" :href="route('contacts')" /> <bread-crumb v-if="car.id && !contact.id" text="Autos" :href="route('cars')" />
<bread-crumb v-if="car_first" :text="car.name" :href="route('cars.show', car.id)" /> <bread-crumb v-if="car.id && !contact.id" :text="car.name" :href="route('cars.show', car.id)" />
<bread-crumb :text="contact.name" :href="route('contacts.show', contact.id)" /> <bread-crumb v-if="!car.id && contact.id" text="Kontakte" :href="route('contacts')" />
<bread-crumb v-if="!car_first" :text="car.name" :href="route('cars.show', car.id)" /> <bread-crumb v-if="!car.id && contact.id" :text="contact.name" :href="route('contacts.show', contact.id)" />
Neuen {{ contractType }} erstellen Neuen {{ contractType }} erstellen
</h2> </h2>
</template> </template>
<div> <div>
<jet-form-section v-if="!car_first" :emptyBg="true" class="max-w-7xl mb-5"> <contact-create-or-select v-if="contact.id" class="mb-5" @contact-id-change="updateContactId" :existing_contact="contact" :contacts="contacts" :type="data.type" />
<template #title> <car-create-or-select @car-id-change="updateCarId" :existing_car="car" :cars="cars" :brands="brands" :type="data.type" />
{{ contactType }} <contact-create-or-select v-if="!contact.id" class="mt-5" @contact-id-change="updateContactId" :existing_contact="contact" :contacts="contacts" :type="data.type" />
</template>
<template #description>
Ausgewählter {{ contactType }} für diesen {{ contractType }}
</template>
<template #form>
<contact-card class="col-span-12" :contact="contact" />
</template>
</jet-form-section>
<jet-form-section class="max-w-7xl" :emptyBg="true">
<template #title>
Auto
</template>
<template #description>
Ausgewähltes Auto für diesen {{ contractType }}
</template>
<template #form>
<car-card class="col-span-12" :car="car" />
</template>
</jet-form-section>
<jet-form-section v-if="car_first" :emptyBg="true" class="max-w-7xl mt-5">
<template #title>
{{ contactType }}
</template>
<template #description>
Ausgewählter {{ contactType }} für diesen {{ contractType }}
</template>
<template #form>
<contact-card class="col-span-12" :contact="contact" />
</template>
</jet-form-section>
<contract-form class="mt-5" :data="data" :meta="meta" :insurance_types="insurance_types"> <contract-form class="mt-5" :data="data" :meta="meta" :insurance_types="insurance_types">
<template #title>Vertragsinformationen erfassen</template> <template #title>Vertragsinformationen erfassen</template>
<template #description>Der Vertrag kann anschliessend gespeichert werden.</template> <template #description>Der Vertrag kann anschliessend gespeichert werden.</template>
@ -56,9 +26,8 @@
<script> <script>
import Layout from '@/Layouts/Layout'; import Layout from '@/Layouts/Layout';
import BreadCrumb from '@/Components/BreadCrumb.vue'; import BreadCrumb from '@/Components/BreadCrumb.vue';
import CarCard from '@/Components/CarCard.vue'; import ContactCreateOrSelect from '@/Components/Contacts/CreateOrSelect.vue';
import ContactCard from '@/Components/ContactCard.vue'; import CarCreateOrSelect from '@/Components/Cars/CreateOrSelect.vue';
import JetFormSection from '@/Jetstream/FormSection';
import { ref } from 'vue'; import { ref } from 'vue';
import ContractForm from './Components/ContractForm.vue'; import ContractForm from './Components/ContractForm.vue';
@ -67,16 +36,17 @@ export default {
Layout, Layout,
BreadCrumb, BreadCrumb,
ContractForm, ContractForm,
CarCard, ContactCreateOrSelect,
ContactCard, CarCreateOrSelect,
JetFormSection,
}, },
props: { props: {
car: Object, car: Object,
contact: Object, contact: Object,
type: String, type: String,
car_first: Boolean,
insurance_types: Array, insurance_types: Array,
contacts: Array,
cars: Array,
brands: Array,
}, },
data() { data() {
return { return {
@ -95,22 +65,24 @@ export default {
notes: null, notes: null,
type: this.type, type: this.type,
insurance_type: '0', insurance_type: '0',
car_id: this.car.id, car_id: this.car?.id ?? null,
contact_id: this.contact.id, contact_id: this.contact?.id ?? null,
is_sell_contract: this.type == '0', is_sell_contract: this.type === '0',
}, },
}; };
}, },
computed: { computed: {
contractType() { contractType() {
return this.isSellContract ? 'Verkaufsvertrag' : 'Ankaufsvertrag'; return this.data.type === '1' ? 'Verkaufsvertrag' : 'Ankaufsvertrag';
}, },
contactType() { },
return this.isSellContract ? 'Käufer' : 'Verkäufer'; methods: {
updateContactId(val) {
this.data.contact_id = parseInt(val);
}, },
isSellContract() { updateCarId(val) {
return this.type === '1'; this.data.car_id = parseInt(val);
}, },
}, },
}; };
</script> </script>

View File

@ -1,177 +0,0 @@
<template>
<layout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
<bread-crumb text="Autos" :href="route('cars')" />
<bread-crumb :text="car.name" :href="route('cars.show', car.id)" />
Neuen {{ contractType }} erstellen
</h2>
</template>
<div>
<jet-form-section class="max-w-7xl" :emptyBg="true">
<template #title>
Auto
</template>
<template #description>
Ausgewähltes Auto für diesen {{ contractType }}
</template>
<template #form>
<car-card class="col-span-12" :car="car" />
</template>
</jet-form-section>
<jet-form-section class="max-w-7xl mt-5">
<template #title>
{{ contactType }}
</template>
<template #description>
{{ contactType }} auswählen oder neu erfassen
</template>
<template #form>
<div class="col-span-3">
<jet-label for="contact" :value="contactType" />
<multiselect :disabled="createContact" v-model="contact" label="title" track-by="id" :options="contactsChoice" class="mt-1 block w-full" placeholder="Vertragspartner auswählen" />
</div>
<div class="col-span-6">
<contact-card v-if="contact.id" class="mt-3 col-span-4" :contact="contact" />
</div>
<div class="col-span-6">
oder
</div>
<div v-if="!createContact" class="col-span-6">
<button @click="openContactForm" class="bg-indigo-800 hover:bg-indigo-700 active:bg-indigo-900 focus:border-indigo-900 focus:ring-indigo-300 justify-center inline-flex items-center px-4 py-2 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest focus:outline-none focus:ring disabled:opacity-25 transition">
Neu erfassen
</button>
</div>
<div v-else class="col-span-6">
<p class="w-full mb-1 font-bold">Neuen Kontakt erfassen:</p>
<form @submit="submitCreateContactForm">
<div class="grid grid-cols-6 gap-6">
<contact-form-fields :form="contact" />
<div class="col-span-6 sm:col-span-4 flex items-center justify-end text-right">
<jet-button>
Kontakt speichern
</jet-button>
</div>
</div>
</form>
</div>
</template>
<template #actions>
<jet-button :disabled="!contact.id" @click="nextPage">
Nächster Schritt
</jet-button>
</template>
</jet-form-section>
</div>
</layout>
</template>
<script>
import Layout from '@/Layouts/Layout';
import BreadCrumb from '@/Components/BreadCrumb.vue';
import ContactFormFields from '@/Pages/Contacts/Components/ContactFormFields.vue';
import CarCard from '@/Components/CarCard.vue';
import ContactCard from '@/Components/ContactCard.vue';
import JetFormSection from '@/Jetstream/FormSection';
import Multiselect from 'vue-multiselect';
import JetLabel from '@/Jetstream/Label.vue';
import JetButton from '@/Jetstream/Button';
export default {
components: {
Layout,
BreadCrumb,
CarCard,
ContactCard,
JetFormSection,
ContactFormFields,
Multiselect,
JetLabel,
JetButton,
},
props: {
car: Object,
contacts: Object,
type: String,
},
data() {
return {
contactsChoice: this.contacts,
contact: {
id: null,
firstname: null,
lastname: null,
company: null,
email: null,
phone: null,
address: null,
zip: null,
city: null,
country: 'CH',
notes: null,
errors: {},
},
createContact: false,
};
},
computed: {
contractType() {
return this.isSellContract ? 'Verkaufsvertrag' : 'Ankaufsvertrag';
},
contactType() {
return this.isSellContract ? 'Käufer' : 'Verkäufer';
},
isSellContract() {
return this.type == '1';
},
emptyContact() {
return {
id: null,
firstname: null,
lastname: null,
company: null,
email: null,
phone: null,
address: null,
zip: null,
city: 'CH',
country: null,
notes: null,
errors: {},
};
},
},
methods: {
nextPage() {
this.$inertia.get(route('contracts.create', {
type: this.isSellContract ? 1 : 0,
car: this.car.id,
contact: this.contact.id,
}), { preserveScroll: true, carFirst: true });
},
openContactForm() {
this.createContact = true;
this.contact = this.emptyContact;
},
submitCreateContactForm(e) {
e.preventDefault();
axios.post(this.route('contacts.store_for_contract'), this.contact)
.then((res) => {
this.contactsChoice.push(res.data);
this.contact = res.data;
this.createContact = false;
}).catch((err) => {
if (err.response) {
const { errors } = err.response.data;
Object.keys(errors).map((key, index) => {
errors[key] = errors[key].join(' ');
});
this.contact.errors = errors;
}
});
},
},
};
</script>
<style src="vue-multiselect/dist/vue-multiselect.css"></style>

View File

@ -1,179 +0,0 @@
<template>
<layout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
<bread-crumb text="Kontakte" :href="route('contacts')" />
<bread-crumb :text="contact.name" :href="route('contacts.show', contact.id)" />
Neuen {{ contractType }} erstellen
</h2>
</template>
<div>
<jet-form-section class="max-w-7xl" :emptyBg="true">
<template #title>
{{ contactType }}
</template>
<template #description>
Ausgewählter {{ contactType }} für diesen {{ contractType }}
</template>
<template #form>
<contact-card class="col-span-12" :contact="contact" />
</template>
</jet-form-section>
<jet-form-section class="max-w-7xl mt-5">
<template #title>
Auto
</template>
<template #description>
Auto auswählen oder neu erfassen
</template>
<template #form>
<div class="col-span-3">
<jet-label for="car" value="Auto" />
<multiselect :disabled="createCar" v-model="car" label="name" track-by="id" :options="carsChoice" class="mt-1 block w-full" placeholder="Auto auswählen" />
</div>
<div class="col-span-6">
<car-card v-if="car.id" class="mt-3 col-span-6" :car="car" />
</div>
<div class="col-span-6">
oder
</div>
<div v-if="!createCar" class="col-span-6">
<button @click="openCarForm" class="bg-indigo-800 hover:bg-indigo-700 active:bg-indigo-900 focus:border-indigo-900 focus:ring-indigo-300 justify-center inline-flex items-center px-4 py-2 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest focus:outline-none focus:ring disabled:opacity-25 transition">
Neu erfassen
</button>
</div>
<div v-else class="col-span-6">
<p class="w-full mb-1 font-bold">Neues Auto erfassen:</p>
<form @submit="submitCreateCarForm">
<div class="grid grid-cols-6 gap-6">
<car-form-fields :form="car" :car_model="car_model" :brand="brand" :brands="brands" />
<div class="col-span-6 sm:col-span-4 flex items-center justify-end text-right">
<jet-button>
Auto speichern
</jet-button>
</div>
</div>
</form>
</div>
</template>
<template #actions>
<jet-button :disabled="!car.id" @click="nextPage">
Nächster Schritt
</jet-button>
</template>
</jet-form-section>
</div>
</layout>
</template>
<script>
import Layout from '@/Layouts/Layout';
import BreadCrumb from '@/Components/BreadCrumb.vue';
import CarFormFields from '@/Pages/Cars/Components/CarFormFields.vue';
import CarCard from '@/Components/CarCard.vue';
import ContactCard from '@/Components/ContactCard.vue';
import JetFormSection from '@/Jetstream/FormSection';
import Multiselect from 'vue-multiselect';
import JetLabel from '@/Jetstream/Label.vue';
import JetButton from '@/Jetstream/Button';
export default {
components: {
Layout,
BreadCrumb,
CarCard,
ContactCard,
CarFormFields,
JetFormSection,
Multiselect,
JetLabel,
JetButton,
},
props: {
contact: Object,
cars: Object,
brands: Object,
type: String,
},
data() {
return {
carsChoice: this.cars,
car: {
id: null,
stammnummer: null,
vin: null,
colour: null,
car_model_id: null,
initial_date: null,
last_check_date: null,
kilometers: null,
known_damage: null,
notes: null,
errors: {},
},
brand: { id: null, name: null },
car_model: { id: null, name: null },
createCar: false,
};
},
computed: {
contractType() {
return this.isSellContract ? 'Verkaufsvertrag' : 'Ankaufsvertrag';
},
contactType() {
return this.isSellContract ? 'Käufer' : 'Verkäufer';
},
isSellContract() {
return this.type == '1';
},
emptyCar() {
return {
id: null,
stammnummer: null,
vin: null,
colour: null,
car_model_id: null,
initial_date: null,
last_check_date: null,
kilometers: null,
known_damage: null,
notes: null,
errors: {},
};
},
},
methods: {
nextPage() {
this.$inertia.get(route('contracts.create', {
type: this.isSellContract ? 1 : 0,
car: this.car.id,
contact: this.contact.id,
}), { preserveScroll: true });
},
openCarForm() {
this.createCar = true;
this.car = this.emptyCar;
},
submitCreateCarForm(e) {
e.preventDefault();
axios.post(this.route('cars.store_for_contract'), this.car)
.then((res) => {
this.carsChoice.push(res.data);
this.car = res.data;
this.createCar = false;
}).catch((err) => {
if (err.response) {
const { errors } = err.response.data;
Object.keys(errors).map((key, index) => {
errors[key] = errors[key].join(' ');
});
this.car.errors = errors;
}
});
},
},
};
</script>
<style src="vue-multiselect/dist/vue-multiselect.css"></style>

View File

@ -59,12 +59,7 @@ Route::middleware(['auth:sanctum', 'verified'])->group(function () {
Route::prefix('contracts')->group(function () { Route::prefix('contracts')->group(function () {
Route::post('/', [ContractController::class, 'store'])->name('contracts.store'); Route::post('/', [ContractController::class, 'store'])->name('contracts.store');
Route::get('create', [ContractController::class, 'create'])->name('contracts.create');
Route::prefix('create/{type}')->group(function () {
Route::get('car/{car}/contact/{contact}', [ContractController::class, 'create'])->where('type', '0|1')->name('contracts.create');
Route::get('car/{car}', [ContractController::class, 'createFromCar'])->where('type', '0|1')->name('contracts.create_from_car');
Route::get('contact/{contact}', [ContractController::class, 'createFromContact'])->where('type', '0|1')->name('contracts.create_from_contact');
});
Route::prefix('{contract}')->group(function () { Route::prefix('{contract}')->group(function () {
Route::get('/', [ContractController::class, 'show'])->name('contracts.show'); Route::get('/', [ContractController::class, 'show'])->name('contracts.show');