contract view

shift-build-2464
Nadim Salloum 2021-05-27 23:09:42 +03:00
parent 3691cb613a
commit bddb7b9fe5
27 changed files with 3068 additions and 264 deletions

View File

@ -68,10 +68,11 @@ class CarController extends Controller
$contact = $contract->contact; $contact = $contract->contact;
return [ return [
'id' => $contract->id, 'id' => $contract->id,
'date' => $contract->date, 'date' => $contract->date_formatted,
'price' => $contract->price->format(), 'price' => $contract->price->format(),
'type' => $contract->type, 'type' => $contract->type,
'insurance_type' => InsuranceType::fromValue((int)$contract->insurance_type)->key, 'is_sell_contract' => $contract->isSellContract(),
'insurance_type' => $contract->insurance_type ? InsuranceType::fromValue((int)$contract->insurance_type)->key : null,
'contact' => [ 'contact' => [
'id' => $contact->id, 'id' => $contact->id,
'name' => $contact->name, 'name' => $contact->name,
@ -180,12 +181,6 @@ class CarController extends Controller
]); ]);
} }
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request) public function store(Request $request)
{ {
$car = Car::create( $car = Car::create(
@ -206,12 +201,6 @@ class CarController extends Controller
} }
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Car $car
* @return \Illuminate\Http\Response
*/
public function show(Car $car) public function show(Car $car)
{ {
return Inertia::render('Cars/Show', [ return Inertia::render('Cars/Show', [
@ -241,12 +230,6 @@ class CarController extends Controller
]); ]);
} }
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Car $car
* @return \Illuminate\Http\Response
*/
public function edit(Car $car) public function edit(Car $car)
{ {
return Inertia::render('Cars/Edit', [ return Inertia::render('Cars/Edit', [
@ -281,13 +264,6 @@ class CarController extends Controller
]); ]);
} }
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Car $car
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Car $car) public function update(Request $request, Car $car)
{ {
$car->update( $car->update(
@ -307,12 +283,6 @@ class CarController extends Controller
return Redirect::route('cars.show', $car)->with('success', 'Auto geändert.'); return Redirect::route('cars.show', $car)->with('success', 'Auto geändert.');
} }
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Car $car
* @return \Illuminate\Http\Response
*/
public function destroy(Car $car) public function destroy(Car $car)
{ {
$car->delete(); $car->delete();

View File

@ -2,9 +2,14 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Enums\ContractType;
use App\Models\Car;
use Inertia\Inertia; use Inertia\Inertia;
use App\Models\Contact;
use App\Models\Contract; use App\Models\Contract;
use App\Enums\InsuranceType;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
class ContractController extends Controller class ContractController extends Controller
{ {
@ -38,13 +43,184 @@ class ContractController extends Controller
return []; return [];
} }
public function create(Request $request, Car $car, Contact $contact)
{
return Inertia::render('Contracts/Create', [
'car' => $this->getCarFields($car),
'contact' => $this->getContactFields($contact),
'type' => (string)ContractType::BuyContract,
]);
}
public function createFromCar(Request $request, Car $car)
{
return Inertia::render('Contracts/CreateFromCar', [
'car' => $this->getCarFields($car),
'contacts' => Contact::all()->map(function ($contact) {
return [
'id' => $contact->id,
'name' => $contact->name,
];
}),
]);
}
public function createFromContact(Request $request, Contact $contact)
{
return Inertia::render('Contracts/CreateFromContact', [
'contact' => $this->getContactFields($contact),
'cars' => Car::all()->map(function ($car) {
return [
'id' => $car->id,
'name' => $car->name,
'stammnummer' => $car->stammnummer,
];
}),
]);
}
private function getCarFields(?Car $car) {
if (!$car) {
return null;
}
return [
'id' => $car->id,
'stammnummer' => $car->stammnummer,
'vin' => $car->vin,
'name' => $car->name,
'colour' => $car->colour,
'last_check_date' => $car->last_check_date_formatted,
'kilometers' => $car->kilometers,
'initial_date' => $car->initial_date_formatted,
];
}
private function getContactFields(?Contact $contact) {
if (!$contact) {
return null;
}
return [
'id' => $contact->id,
'name' => $contact->name,
'firstname' => $contact->firstname,
'lastname' => $contact->lastname,
'phone' => $contact->phone,
'address' => $contact->address,
'zip' => $contact->zip,
'city' => $contact->city,
'country' => $contact->country,
'company' => $contact->company,
'email' => $contact->email,
];
}
public function store(Request $request)
{
$contract = Contract::create(
$request->validate([
'date' => ['required', 'date'],
'price' => ['required', 'integer'],
'car_id' => ['required', 'exists:App\Models\Car,id'],
'contact_id' => ['required', 'exists:App\Models\Contact,id'],
'insurance_type' => ['nullable', 'max:75'],
])
);
return Redirect::route('contracts.show', $contract);
}
public function edit(Contract $contract)
{
return Inertia::render('Contracts/Edit', [
'contract' => [
'id' => $contract->id,
'date' => $contract->date,
'is_sell_contract' => $contract->isSellContract(),
'price' => $contract->price->getAmount(),
'insurance_type' => (string)$contract->insurance_type,
'car' => [
'id' => $contract->car->id,
'name' => $contract->car->name,
],
],
'insurance_types' => InsuranceType::asArray(),
]);
}
public function update(Request $request, Contract $contract)
{
$contract->update(
$request->validate([
'date' => ['required', 'date'],
'price' => ['required', 'integer'],
'insurance_type' => ['nullable', 'max:75'],
])
);
return Redirect::route('contracts.show', $contract)->with('success', 'Vertrag angepasst.');
}
public function show(Contract $contract) public function show(Contract $contract)
{ {
return []; return Inertia::render('Contracts/Show', [
'contract' => [
'id' => $contract->id,
'date' => $contract->date_formatted,
'price' => $contract->price->format(),
'type' => $contract->type,
'is_sell_contract' => $contract->isSellContract(),
'insurance_type' => $contract->insurance_type ? InsuranceType::fromValue((int)$contract->insurance_type)->key : null,
'deleted_at' => $contract->deleted_at,
'contact' => [
'id' => $contract->contact->id,
'name' => $contract->contact->name,
'firstname' => $contract->contact->firstname,
'lastname' => $contract->contact->lastname,
'phone' => $contract->contact->phone,
'address' => $contract->contact->address,
'zip' => $contract->contact->zip,
'city' => $contract->contact->city,
'country' => $contract->contact->country,
'company' => $contract->contact->company,
'email' => $contract->contact->email,
'link' => route('contacts.edit', $contract->contact),
],
'car' => [
'id' => $contract->car->id,
'stammnummer' => $contract->car->stammnummer,
'vin' => $contract->car->vin,
'car_model' => $contract->car->carModel->only('id', 'name'),
'brand' => $contract->car->brand,
'name' => $contract->car->name,
'initial_date' => $contract->car->initial_date_formatted,
'colour' => $contract->car->colour,
'last_check_date' => $contract->car->last_check_date_formatted,
'kilometers' => $contract->car->kilometers,
'known_damage' => $contract->car->known_damage,
'notes' => $contract->car->notes,
'deleted_at' => $contract->car->deleted_at,
'link' => route('cars.show', $contract->car),
],
],
]);
} }
public function print(Contract $contract) public function print(Contract $contract)
{ {
return []; return [];
} }
public function destroy(Contract $contract)
{
$contract->delete();
return Redirect::route('contracts.show', $contract)->with('success', 'Vertrag gelöscht.');
}
public function restore(Contract $contract)
{
$contract->restore();
return Redirect::route('contracts.show', $contract)->with('success', 'Vertrag wiederhergestellt.');
}
} }

View File

@ -22,9 +22,9 @@ class Contract extends Model
'insurance_type', 'insurance_type',
]; ];
public function getDateAttribute($date) public function getDateFormattedAttribute()
{ {
return Carbon::parse($date)->format('d.m.Y'); return Carbon::parse($this->date)->format('d.m.Y');
} }
public function getPriceAttribute($price) public function getPriceAttribute($price)
@ -32,6 +32,25 @@ class Contract extends Model
return Money::CHF($price); return Money::CHF($price);
} }
public function getDeletedAtAttribute($deleted_at)
{
if ($deleted_at) {
return Carbon::parse($deleted_at)->format('d.m.Y');
}
return null;
}
public function isBuyContract()
{
return $this->type === (string)ContractType::BuyContract;
}
public function isSellContract()
{
return $this->type === (string)ContractType::SellContract;
}
public function documents() public function documents()
{ {
return $this->morphMany(Document::class, 'documentable'); return $this->morphMany(Document::class, 'documentable');

View File

@ -3,6 +3,7 @@
namespace App\Providers; namespace App\Providers;
use App\Models\Car; use App\Models\Car;
use App\Models\Contract;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Cache\RateLimiting\Limit;
@ -54,6 +55,13 @@ class RouteServiceProvider extends ServiceProvider
} }
return Car::find($value); return Car::find($value);
}); });
Route::bind('contract', function ($value) {
if (in_array(Route::currentRouteName(), ['contracts.show', 'contracts.restore'])) {
return Contract::withTrashed()->find($value);
}
return Contract::find($value);
});
}); });
} }

2383
public/js/app.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
<template>
<standard-button :href="href" :class="this.class + ' mb-3'" colour="red">
<unicon fill="white" class="mr-1" height="22" width="22" name="trash-alt"></unicon>
löschen
</standard-button>
</template>
<script>
import StandardButton from './StandardButton.vue';
export default {
components: { StandardButton },
props: {
class: String,
href: String,
},
}
</script>

View File

@ -0,0 +1,18 @@
<template>
<standard-button :href="href" :class="this.class + ' mb-3'" colour="gray">
<unicon fill="white" class="mr-1" height="22" width="22" name="pen"></unicon>
bearbeiten
</standard-button>
</template>
<script>
import StandardButton from './StandardButton.vue';
export default {
components: { StandardButton },
props: {
class: String,
href: String,
},
}
</script>

View File

@ -0,0 +1,18 @@
<template>
<standard-button :href="href" :class="this.class" colour="gray">
<unicon fill="white" class="mr-1" height="22" width="22" name="file-download"></unicon>
drucken
</standard-button>
</template>
<script>
import StandardButton from './StandardButton.vue';
export default {
components: { StandardButton },
props: {
class: String,
href: String,
},
}
</script>

View File

@ -0,0 +1,18 @@
<template>
<standard-button :href="href" :class="this.class + ' mb-3'" colour="green">
<unicon fill="white" class="mr-1" height="22" width="22" name="trash-alt"></unicon>
wiederherstellen
</standard-button>
</template>
<script>
import StandardButton from './StandardButton.vue';
export default {
components: { StandardButton },
props: {
class: String,
href: String,
},
}
</script>

View File

@ -0,0 +1,24 @@
<template>
<inertia-link :href="href" :class="allClasses">
<slot></slot>
</inertia-link>
</template>
<script>
export default {
props: {
class: String,
href: String,
colour: String
},
computed: {
allClasses: function () {
let classes = "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"
classes += ` bg-${this.colour}-800 hover:bg-${this.colour}-700 active:bg-${this.colour}-900 focus:border-${this.colour}-900 focus:ring-${this.colour}-300`;
return classes + " " + this.class;
}
},
}
</script>

View File

@ -7,13 +7,13 @@
<div class="col-span-1 xs:col-span-2"> <div class="col-span-1 xs:col-span-2">
Stammnummer Stammnummer
</div> </div>
<div v-if="car.stammnummer" class="col-span-3 xs:col-span-2"> <div class="col-span-3 xs:col-span-2">
{{ car.stammnummer ? car.stammnummer : '-' }} {{ car.stammnummer ? car.stammnummer : '-' }}
</div> </div>
<div v-if="car.vin" class="col-span-1 xs:col-span-2"> <div class="col-span-1 xs:col-span-2">
Chassisnummer Chassisnummer
</div> </div>
<div v-if="car.vin" class="col-span-3 xs:col-span-2"> <div class="col-span-3 xs:col-span-2">
{{ car.vin ? car.vin : '-'}} {{ car.vin ? car.vin : '-'}}
</div> </div>
<div class="col-span-1 xs:col-span-2"> <div class="col-span-1 xs:col-span-2">

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="mt-3 p-5 bg-white shadow rounded-md font-medium"> <div class="p-5 bg-white shadow rounded-md font-medium">
<div v-if="contact.company" class="font-bold"> <div v-if="contact.company" class="font-bold">
{{ contact.company }} {{ contact.company }}
</div> </div>

View File

@ -2,11 +2,11 @@
<div class="py-3 grid grid-cols-12 gap-3 w-full"> <div class="py-3 grid grid-cols-12 gap-3 w-full">
<div v-if="contract.contact" class="col-span-6 xs:col-span-12"> <div v-if="contract.contact" class="col-span-6 xs:col-span-12">
<h3>{{ meta.contact }}</h3> <h3 class="mb-3">{{ meta.contact }}</h3>
<contact-card :contact="contract.contact" /> <contact-card :contact="contract.contact" />
</div> </div>
<div v-if="contract.car" class="col-span-6 xs:col-span-12"> <div v-if="contract.car" class="col-span-6 xs:col-span-12">
<h3>{{ meta.car }}</h3> <h3 class="mb-3">Auto</h3>
<car-card :car="contract.car" /> <car-card :car="contract.car" />
</div> </div>
<div class="col-span-6 xs:col-span-12 h-full relative"> <div class="col-span-6 xs:col-span-12 h-full relative">
@ -15,7 +15,7 @@
<div v-if="contract.date"> <div v-if="contract.date">
Datum: {{ contract.date }} Datum: {{ contract.date }}
</div> </div>
<div v-if="contract.insurance_type"> <div v-if="contract.is_sell_contract && contract.insurance_type">
Versicherung: {{ contract.insurance_type }} Versicherung: {{ contract.insurance_type }}
</div> </div>
<div v-if="contract.price" class="pt-8 font-bold text-2xl"> <div v-if="contract.price" class="pt-8 font-bold text-2xl">
@ -30,13 +30,9 @@
</div> </div>
<div class="absolute left-0 right-0 bottom-0"> <div class="absolute left-0 right-0 bottom-0">
<div class="w-full flex flex-col"> <div class="w-full flex flex-col">
<a :href="route('contracts.print', contract.id)" class="justify-center inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring focus:ring-gray-300 disabled:opacity-25 transition" > <print-button class="mb-0" :href="route('contracts.print', contract.id)" />
<unicon fill="white" class="mr-1" height="22" width="22" name="file-download"></unicon>
drucken
</a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
@ -45,12 +41,14 @@
import SimpleTable from '@/Components/SimpleTable.vue' import SimpleTable from '@/Components/SimpleTable.vue'
import ContactCard from '@/Components/ContactCard.vue' import ContactCard from '@/Components/ContactCard.vue'
import CarCard from '@/Components/CarCard.vue' import CarCard from '@/Components/CarCard.vue'
import PrintButton from './Buttons/PrintButton.vue'
export default { export default {
components: { components: {
SimpleTable, SimpleTable,
ContactCard, ContactCard,
CarCard, CarCard,
PrintButton,
}, },
props: { props: {
contract: Object, contract: Object,

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="sticky top-0 z-40"> <div class="sticky top-0 z-40">
<div class="w-full h-20 px-6 bg-gray-100 border-b flex items-center justify-between"> <div class="w-full h-20 px-6 bg-white-100 border-b flex items-center justify-between">
<!-- left navbar --> <!-- left navbar -->
<div class="flex"> <div class="flex">

View File

@ -0,0 +1,30 @@
<template>
<layout>
<template #header>
<slot name="header"></slot>
</template>
<div class="py-6 grid grid-cols-12 gap-12 max-w-7xl sm:px-6 lg:px-8">
<div class="col-span-6 xs:col-span-12">
<slot name="info"></slot>
</div>
<div class="col-span-3 xs:col-span-12">
<div class="w-full flex flex-col">
<slot name="actions"></slot>
</div>
</div>
</div>
<div class="py-6 grid grid-cols-12 gap-8 w-full">
<slot name="more"></slot>
</div>
</layout>
</template>
<script>
import Layout from '@/Layouts/Layout'
export default {
components: {
Layout,
},
}
</script>

View File

@ -1,7 +1,7 @@
<template> <template>
<!-- give the sidebar z-50 class so its higher than the navbar if you want to see the logo --> <!-- give the sidebar z-50 class so its higher than the navbar if you want to see the logo -->
<!-- you will need to add a little "X" button next to the logo in order to close it though --> <!-- you will need to add a little "X" button next to the logo in order to close it though -->
<div class="w-1/2 md:w-1/3 lg:w-64 fixed md:top-0 md:left-0 h-screen lg:block bg-gray-100 border-r z-30" :class="sideBarOpen ? '' : 'hidden'" id="main-nav"> <div class="w-1/2 md:w-1/3 lg:w-64 fixed md:top-0 md:left-0 h-screen lg:block bg-white-100 border-r z-30" :class="sideBarOpen ? '' : 'hidden'" id="main-nav">
<div class="w-full h-20 border-b flex px-4 items-center mb-8"> <div class="w-full h-20 border-b flex px-4 items-center mb-8">
<p class="font-semibold text-2xl text-blue-400 pl-4">Your SwissCar</p> <p class="font-semibold text-2xl text-blue-400 pl-4">Your SwissCar</p>

View File

@ -2,9 +2,11 @@
<div class="leading-normal tracking-normal" id="main-body"> <div class="leading-normal tracking-normal" id="main-body">
<div class="flex flex-wrap"> <div class="flex flex-wrap">
<Sidebar /> <Sidebar />
<div class="w-full bg-gray-100 pl-0 lg:pl-64 min-h-screen" :class="sideBarOpen ? 'overlay' : ''" id="main-content"> <div class="w-full bg-white-100 pl-0 lg:pl-64 min-h-screen" :class="sideBarOpen ? 'overlay' : ''" id="main-content">
<Navbar> <Navbar>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
<slot name="header"></slot> <slot name="header"></slot>
</h2>
</Navbar> </Navbar>
<div class="p-6 bg-gray-100 mb-20"> <div class="p-6 bg-gray-100 mb-20">
<main> <main>

View File

@ -6,7 +6,6 @@
{{ name }} {{ name }}
</h2> </h2>
</template> </template>
<div> <div>
<car-form :data="data" :meta="meta" :car_model="car_model" :brand="brand" :brands="brands"> <car-form :data="data" :meta="meta" :car_model="car_model" :brand="brand" :brands="brands">
<template #title>Autoangaben</template> <template #title>Autoangaben</template>

View File

@ -1,38 +1,24 @@
<template> <template>
<layout> <show-page>
<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 text="Autos" :href="route('cars')" /> <bread-crumb text="Autos" :href="route('cars')" />
{{ car.name }} {{ car.name }}
</h2> </h2>
</template> </template>
<div class="py-6 grid grid-cols-12 gap-12 max-w-7xl sm:px-6 lg:px-8"> <template #info>
<div class="col-span-6 xs:col-span-12">
<car-card :car="car" /> <car-card :car="car" />
</div> </template>
<div class="col-span-3 xs:col-span-12"> <template #actions>
<div class="w-full flex flex-col"> <edit-button v-if="!car.deleted_at" :href="route('cars.destroy', car.id)" />
<inertia-link v-if="!car.deleted_at" :href="route('cars.edit', car.id)" class="justify-center mb-5 inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring focus:ring-gray-300 disabled:opacity-25 transition" > <delete-button v-if="!car.deleted_at" :href="route('cars.destroy', car.id)" />
<unicon fill="white" class="mr-1" height="22" width="22" name="pen"></unicon> <restore-button v-if="car.deleted_at" :href="route('cars.restore', car.id)" />
bearbeiten
</inertia-link>
<inertia-link v-if="!car.deleted_at" :href="route('cars.destroy', car.id)" class="justify-center mb-5 inline-flex items-center px-4 py-2 bg-red-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-700 active:bg-red-900 focus:outline-none focus:border-red-900 focus:ring focus:ring-red-300 disabled:opacity-25 transition" >
<unicon fill="white" class="mr-1" height="22" width="22" name="trash-alt"></unicon>
löschen
</inertia-link>
<inertia-link v-if="car.deleted_at" :href="route('cars.restore', car.id)" class="justify-center mb-5 inline-flex items-center px-4 py-2 bg-green-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-green-700 active:bg-green-900 focus:outline-none focus:border-red-900 focus:ring focus:ring-green-300 disabled:opacity-25 transition" >
<unicon fill="white" class="mr-1" height="22" width="22" name="trash-alt"></unicon>
wiederherstellen
</inertia-link>
<div v-if="car.deleted_at"> <div v-if="car.deleted_at">
gelöscht: {{ car.deleted_at }} gelöscht: {{ car.deleted_at }}
</div> </div>
</div> </template>
<template #more>
</div> <div class="sm:px-6 lg:px-8 col-span-6 xs:col-span-12">
</div>
<div class="py-12 grid grid-cols-12 gap-12 w-full">
<div class="w-full sm:px-6 lg:px-8 col-span-6 xs:col-span-12">
<div class="whitespace-nowrap"> <div class="whitespace-nowrap">
<h1 class="font-bold text-3xl">{{ car.buy_contracts.total > 1 ? car.buy_contracts.total + ' Ankaufsverträge' : 'Ankaufsvertrag' }}</h1> <h1 class="font-bold text-3xl">{{ car.buy_contracts.total > 1 ? car.buy_contracts.total + ' Ankaufsverträge' : 'Ankaufsvertrag' }}</h1>
</div> </div>
@ -40,13 +26,13 @@
<buy-contract-card :contract="contract"/> <buy-contract-card :contract="contract"/>
</div> </div>
<div v-if="!car.deleted_at && car.buy_contracts.total <= car.sell_contracts.total"> <div v-if="!car.deleted_at && car.buy_contracts.total <= car.sell_contracts.total">
<inertia-link :href="route('cars.edit', car.id)" class="py-6 w-full mt-12 inline-flex items-center px-4 bg-green-800 border border-transparent rounded-md font-semibold justify-center text-md text-white uppercase tracking-widest hover:bg-green-700 focus:outline-none focus:border-green-900 focus:ring focus:ring-green-300 disabled:opacity-25 transition" > <inertia-link :href="route('contracts.create_from_car', car.id)" class="w-full py-6 mt-12 inline-flex items-center px-4 bg-green-800 border border-transparent rounded-md font-semibold justify-center text-md text-white uppercase tracking-widest hover:bg-green-700 focus:outline-none focus:border-green-900 focus:ring focus:ring-green-300 disabled:opacity-25 transition" >
<unicon fill="white" class="mr-1" height="22" width="22" name="plus-circle"></unicon> <unicon fill="white" class="mr-1" height="22" width="22" name="plus-circle"></unicon>
Neuer Ankaufsvertrag Neuer Ankaufsvertrag
</inertia-link> </inertia-link>
</div> </div>
</div> </div>
<div class="w-full sm:px-6 lg:px-8 col-span-6 xs:col-span-12"> <div class="sm:px-6 lg:px-8 col-span-6 xs:col-span-12">
<div class="whitespace-nowrap"> <div class="whitespace-nowrap">
<h1 class="font-bold text-3xl">{{ car.sell_contracts.total > 1 ? car.sell_contracts.total + ' Verkaufsverträge' : 'Verkaufsvertrag' }}</h1> <h1 class="font-bold text-3xl">{{ car.sell_contracts.total > 1 ? car.sell_contracts.total + ' Verkaufsverträge' : 'Verkaufsvertrag' }}</h1>
</div> </div>
@ -54,38 +40,41 @@
<sell-contract-card :contract="contract"/> <sell-contract-card :contract="contract"/>
</div> </div>
<div v-if="!car.deleted_at && car.buy_contracts.total > car.sell_contracts.total"> <div v-if="!car.deleted_at && car.buy_contracts.total > car.sell_contracts.total">
<inertia-link :href="route('cars.edit', car.id)" class="py-6 w-full mt-12 inline-flex items-center px-4 bg-green-800 border border-transparent rounded-md font-semibold justify-center text-md text-white uppercase tracking-widest hover:bg-green-700 focus:outline-none focus:border-green-900 focus:ring focus:ring-green-300 disabled:opacity-25 transition" > <inertia-link :href="route('contracts.create_from_car', car.id)" class="py-6 w-full mt-12 inline-flex items-center px-4 bg-green-800 border border-transparent rounded-md font-semibold justify-center text-md text-white uppercase tracking-widest hover:bg-green-700 focus:outline-none focus:border-green-900 focus:ring focus:ring-green-300 disabled:opacity-25 transition" >
<unicon fill="white" class="mr-1" height="22" width="22" name="plus-circle"></unicon> <unicon fill="white" class="mr-1" height="22" width="22" name="plus-circle"></unicon>
Neuer Verkaufssvertrag Neuer Verkaufssvertrag
</inertia-link> </inertia-link>
</div> </div>
</div> </div>
</div> </template>
</layout> </show-page>
</template> </template>
<script> <script>
import Layout from '@/Layouts/Layout' import ShowPage from '@/Components/ShowPage.vue'
import BreadCrumb from '@/Components/BreadCrumb.vue' import BreadCrumb from '@/Components/BreadCrumb.vue'
import SimpleTable from '@/Components/SimpleTable.vue' import SimpleTable from '@/Components/SimpleTable.vue'
import CarCard from '@/Components/CarCard.vue' import CarCard from '@/Components/CarCard.vue'
import BuyContractCard from '@/Components/BuyContractCard.vue' import BuyContractCard from '@/Components/BuyContractCard.vue'
import SellContractCard from '@/Components/SellContractCard.vue' import SellContractCard from '@/Components/SellContractCard.vue'
import EditButton from '@/Components/Buttons/EditButton.vue'
import DeleteButton from '@/Components/Buttons/DeleteButton.vue'
import RestoreButton from '@/Components/Buttons/RestoreButton.vue'
export default { export default {
components: { components: {
ShowPage,
BreadCrumb, BreadCrumb,
Layout,
SimpleTable, SimpleTable,
CarCard, CarCard,
BuyContractCard, BuyContractCard,
SellContractCard, SellContractCard,
EditButton,
DeleteButton,
RestoreButton,
}, },
props: { props: {
car: Object, car: Object,
},
computed: {
}, },
data() { data() {
return { return {

View File

@ -0,0 +1,100 @@
<template>
<div class="max-w-7xl py-10 sm:px-6 lg:px-8">
<jet-form-section @submitted="submitForm">
<template #title>
<slot name="title"></slot>
</template>
<template #description>
<slot name="description"></slot>
</template>
<template #form>
<div class="col-span-6 sm:col-span-4">
<jet-label for="date" value="Datum" />
<jet-input id="date" type="text" class="mt-1 block w-full" v-model="form.date" ref="date" autocomplete="date" />
<jet-input-error :message="form.errors.date" class="mt-2" />
</div>
<div class="col-span-6 sm:col-span-4">
<jet-label for="price" value="Betrag" />
<jet-input id="price" type="text" class="mt-1 block w-full" v-model="form.price" ref="price" autocomplete="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" />
<multiselect class="mt-1 block w-full" @select="updateInsuranceSelection" v-model="insuranceSelection" deselect-label="Kann nicht entfernt werden" track-by="key" label="label" placeholder="Versicherung auswählen" :options="insurances" :searchable="false" :allow-empty="false" />
<jet-input-error :message="form.errors.insurance_type" class="mt-2" />
</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>
</div>
</template>
<script>
import JetButton from '@/Jetstream/Button'
import JetLabel from '@/Jetstream/Label.vue'
import JetInput from '@/Jetstream/Input.vue'
import JetActionMessage from '@/Jetstream/ActionMessage'
import JetInputError from '@/Jetstream/InputError'
import JetFormSection from '@/Jetstream/FormSection'
import Multiselect from 'vue-multiselect'
import { useForm } from '@inertiajs/inertia-vue3'
export default {
components: {
JetButton,
JetFormSection,
JetLabel,
JetInput,
JetInputError,
JetActionMessage,
Multiselect,
},
props: {
data: Object,
meta: Object,
insurance_types: Object,
},
data() {
return {
form: useForm(this.meta.form_name, this.data),
insuranceSelection: {key: this.data.insurance_type, label: 'asd'},
}
},
computed: {
insurances: function() {
let insurances = [];
for (const label in this.insurance_types) {
insurances.push({key: this.insurance_types[label], label: label});
}
return insurances;
},
},
methods: {
submitForm() {
this.form.submit(this.meta.method, this.meta.route);
},
updateInsuranceSelection(selection) {
this.form.insurance_type = (selection.key).toString();
},
},
mounted: function () {
this.$nextTick(function () {
this.insuranceSelection = this.insurances.find(x => x.key === parseInt(this.data.insurance_type));
})
},
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.css"></style>

View File

@ -0,0 +1,87 @@
<template>
<layout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
<bread-crumb text="Autos" :href="route('cars')" />
Neuen Vertrag erstellen
</h2>
</template>
<div>
<jet-form-section class="max-w-7xl pb-5 sm:px-6 lg:px-8">
<template #title>
Auto
</template>
<template #description>
Ausgewähltes Auto für diesen Vertrag
</template>
<template #form>
<car-card class="col-span-12" :car="car" />
</template>
</jet-form-section>
<jet-form-section class="max-w-7xl py-5 sm:px-6 lg:px-8">
<template #title>
Kontakt
</template>
<template #description>
Ausgewählter Kontakt für diesen Vertrag
</template>
<template #form>
<contact-card class="col-span-4" :contact="contact" />
</template>
</jet-form-section>
<contract-form :data="data" :meta="meta">
<template #title>Vertragsinformationen erfassen</template>
<template #description>Der Vertrag kann anschliessend gespeichert werden.</template>
</contract-form>
</div>
</layout>
</template>
<script>
import Layout from '@/Layouts/Layout'
import BreadCrumb from '@/Components/BreadCrumb.vue'
import ContractForm from './Components/ContractForm.vue'
import CarCard from '@/Components/CarCard.vue'
import ContactCard from '@/Components/ContactCard.vue'
import JetFormSection from '@/Jetstream/FormSection'
export default {
components: {
Layout,
BreadCrumb,
ContractForm,
CarCard,
ContactCard,
JetFormSection,
},
props: {
car: Object,
contact: Object,
type: String,
},
data() {
return {
meta: {
form_name: 'CreateContract',
route: this.route('contracts.store'),
method: 'post',
button_text: 'Vertrag speichern',
on_success: 'Vertrag gespeichert',
},
data: {
id: null,
date: null,
price: null,
type: this.type,
insurance_type: '0',
car_id: this.car.id,
contact_id: this.contact.id,
},
}
},
}
</script>

View File

@ -0,0 +1,101 @@
<template>
<layout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
<bread-crumb text="Autos" :href="route('cars')" />
Neuen Vertrag erstellen
</h2>
</template>
<div>
<jet-form-section class="max-w-7xl pb-5 sm:px-6 lg:px-8">
<template #title>
Auto
</template>
<template #description>
Ausgewähltes Auto für diesen Vertrag
</template>
<template #form>
<car-card class="col-span-12" :car="car" />
</template>
</jet-form-section>
<jet-form-section class="max-w-7xl py-5 sm:px-6 lg:px-8">
<template #title>
Kontakt
</template>
<template #description>
Kontakt auswählen oder neu erfassen
<contact-card v-if="contact.firstname || contact.company" class="col-span-4" :contact="contact" />
</template>
<template #form>
<div class="col-span-3">
<jet-label for="contact" value="Marke" />
<jet-input id="contact" type="text" class="mt-1 block w-full" v-model="contact.id" ref="contact" autocomplete="contact" />
<!-- <jet-input-error :message="form.errors.contact" class="mt-2" /> -->
</div>
<div class="col-span-6">
oder
</div>
<div class="col-span-6">
<standard-button class="" href="" colour="gray">Neu erfassen</standard-button>
</div>
</template>
</jet-form-section>
</div>
</layout>
</template>
<script>
import Layout from '@/Layouts/Layout'
import BreadCrumb from '@/Components/BreadCrumb.vue'
import ContactForm from '@/Pages/Contacts/Components/ContactForm.vue'
import CarCard from '@/Components/CarCard.vue'
import ContactCard from '@/Components/ContactCard.vue'
import JetFormSection from '@/Jetstream/FormSection'
import StandardButton from '@/Components/Buttons/StandardButton.vue'
export default {
components: {
Layout,
BreadCrumb,
CarCard,
ContactCard,
JetFormSection,
ContactForm,
StandardButton,
},
props: {
car: Object,
contacts: Object,
type: String,
},
data() {
return {
meta: {
link: 'contacts.store',
button_text: 'Kontakt speichern',
on_success: 'Kontakt gespeichert',
},
contact: {
id: null,
firstname: null,
lastname: null,
company: null,
email: null,
phone: null,
address: null,
zip: null,
city: null,
country: null,
notes: null,
},
meta: {
form_name: 'CreateContractFromCar',
// route: this.route('contracts.create', this.car.id, this.contact.id),
method: 'post',
button_text: 'Vertrag speichern',
on_success: 'Vertrag gespeichert',
},
}
},
}
</script>

View File

@ -0,0 +1,51 @@
<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="contract.car.name" :href="route('cars.show', contract.car.id)" />
<bread-crumb :text="'Vertrag vom ' + contract.date" :href="route('contracts.show', contract.id)" />
bearbeiten
</h2>
</template>
<div>
<contract-form :data="contract" :insurance_types="insurance_types" :meta="meta">
<template #title>Vertragsangaben</template>
<template #description>
Vertrag &amp; anpassen.
</template>
</contract-form>
</div>
</layout>
</template>
<script>
import Layout from '@/Layouts/Layout'
import BreadCrumb from '@/Components/BreadCrumb.vue'
import ContractForm from './Components/ContractForm.vue'
export default {
components: {
BreadCrumb,
Layout,
ContractForm,
},
props: {
contract: Object,
insurance_types: Object,
},
data() {
return {
currentRoute: 'contracts.edit',
meta: {
form_name: 'EditContract' + this.contract.id,
route: this.route('contracts.update', this.contract.id),
method: 'put',
button_text: 'Änderungen speichern',
on_success: 'Änderungen gespeichert',
},
}
},
}
</script>

View File

@ -0,0 +1,109 @@
<template>
<show-page>
<template #header>
<bread-crumb text="Autos" :href="route('cars')" />
<bread-crumb :text="contract.car.name" :href="route('cars.show', contract.car.id)" />
{{ contractType}} vom {{ contract.date }}
</template>
<template #info>
<div class="p-5 bg-white shadow rounded-md font-medium">
<div class="font-bold pb-1 mb-1 text-2xl border-b">
{{ contractType}} vom {{ contract.date }}
</div>
<div class="grid grid-cols-4 gap-2 w-full">
<div class="col-span-1 xs:col-span-2">
Datum
</div>
<div class="col-span-3 xs:col-span-2">
{{ contract.date }}
</div>
<div v-if="contract.is_sell_contract && contract.insurance_type" class="col-span-1 xs:col-span-2">
Versicherung
</div>
<div v-if="contract.is_sell_contract && contract.insurance_type" class="col-span-3 xs:col-span-2">
{{ contract.insurance_type }}
</div>
<div class="col-span-1 xs:col-span-2">
Betrag
</div>
<div class="col-span-3 xs:col-span-2 font-bold">
{{ contract.price }}
</div>
</div>
</div>
</template>
<template #actions>
<edit-button :href="route('contracts.edit', contract.id)" />
<print-button class="mb-3" :href="route('contracts.print', contract.id)" />
<delete-button v-if="!contract.deleted_at" :href="route('contracts.destroy', contract.id)" />
<restore-button v-if="contract.deleted_at" :href="route('contracts.restore', contract.id)" />
<div v-if="contract.deleted_at">
gelöscht: {{ contract.deleted_at }}
</div>
</template>
<template #more>
<div class="sm:px-6 lg:px-8 col-span-6 xs:col-span-12">
<h3 class="mb-3">Auto</h3>
<car-card :car="contract.car" />
</div>
<div class="sm:px-6 lg:px-8 col-span-4 xs:col-span-12">
<h3 class="mb-3">{{ contactTitle }}</h3>
<contact-card :contact="contract.contact" />
</div>
</template>
</show-page>
</template>
<script>
import ShowPage from '@/Components/ShowPage.vue'
import BreadCrumb from '@/Components/BreadCrumb.vue'
import SimpleTable from '@/Components/SimpleTable.vue'
import DeleteButton from '@/Components/Buttons/DeleteButton.vue'
import RestoreButton from '@/Components/Buttons/RestoreButton.vue'
import CarCard from '@/Components/CarCard.vue'
import PrintButton from '@/Components/Buttons/PrintButton.vue'
import ContactCard from '@/Components/ContactCard.vue'
import EditButton from '../../Components/Buttons/EditButton.vue'
export default {
components: {
BreadCrumb,
ShowPage,
SimpleTable,
CarCard,
DeleteButton,
RestoreButton,
PrintButton,
ContactCard,
EditButton,
CarCard,
},
props: {
contract: Object,
},
computed: {
contractType: function() {
return this.contract.type == 0 ? 'Ankaufsvertrag' : 'Verkaufsvertrag';
},
contactTitle: function() {
return this.contract.type == 0 ? 'Verkäufer' : 'Käufer';
}
},
data() {
return {
currentRoute: 'contracts.show',
buyContractsColumns: [
{key: 'contact', value: 'Verkäufer'},
{key: 'date', value: 'Kaufdatum'},
{key: 'price', value: 'Kaufpreis'},
],
sellContractsColumns: [
{key: 'contact', value: 'Käufer'},
{key: 'date', value: 'Verkaufsdatum'},
{key: 'price', value: 'Verkaufspreis'},
{key: 'insurance_type', value: 'Versicherungstyp'},
],
}
},
}
</script>

View File

@ -117,10 +117,6 @@ Route::post('models', [CarModelController::class, 'store'])
->name('models.store') ->name('models.store')
->middleware(['auth:sanctum', 'verified']); ->middleware(['auth:sanctum', 'verified']);
Route::get('contracts', [ContractController::class, 'index'])
->name('contracts')
->middleware(['auth:sanctum', 'verified']);
Route::get('contracts/{contract}', [ContractController::class, 'show']) Route::get('contracts/{contract}', [ContractController::class, 'show'])
->name('contracts.show') ->name('contracts.show')
->middleware(['auth:sanctum', 'verified']); ->middleware(['auth:sanctum', 'verified']);
@ -129,10 +125,30 @@ Route::get('contracts/{contract}/print', [ContractController::class, 'print'])
->name('contracts.print') ->name('contracts.print')
->middleware(['auth:sanctum', 'verified']); ->middleware(['auth:sanctum', 'verified']);
Route::get('contracts/ankaufvertraege', [ContractController::class, 'buyContracts']) Route::get('contracts/create/car/{car}/contact/{contact}', [ContractController::class, 'create'])
->name('contracts.buy_contracts') ->name('contracts.create')
->middleware(['auth:sanctum', 'verified']); ->middleware(['auth:sanctum', 'verified']);
Route::get('contracts/kaufvertraege', [ContractController::class, 'sellContracts']) Route::get('contracts/create/car/{car}', [ContractController::class, 'createFromCar'])
->name('contracts.sell_contracts') ->name('contracts.create_from_car')
->middleware(['auth:sanctum', 'verified']);
Route::post('contracts', [ContractController::class, 'store'])
->name('contracts.store')
->middleware(['auth:sanctum', 'verified']);
Route::get('contracts/{contract}/edit', [ContractController::class, 'edit'])
->name('contracts.edit')
->middleware(['auth:sanctum', 'verified']);
Route::put('contracts/{contract}', [ContractController::class, 'update'])
->name('contracts.update')
->middleware(['auth:sanctum', 'verified']);
Route::get('contracts/{contract}/delete', [ContractController::class, 'destroy'])
->name('contracts.destroy')
->middleware(['auth:sanctum', 'verified']);
Route::get('contracts/{contract}/restore', [ContractController::class, 'restore'])
->name('contracts.restore')
->middleware(['auth:sanctum', 'verified']); ->middleware(['auth:sanctum', 'verified']);