@hjortur17 Well what’s the problem? Given that is how you’d redirect.
Redirect back from PUT
I am struggling to redirect to some route from PUT request or send some data back. I am basically trying to redirect back to the route and with flash message.
This is how I am trying to redirect back
return redirect('/edit/' . $booking->id)->with('success', 'Booking updated!');
Any idea how I can redirect back to the edit page after editing the booking (after PUT) or do this ->with('success', 'Booking updated!'); another way?
You can do
return redirect()->back()->with('success', 'Booking updated!');
//or
return back()->with('success', 'Booking updated!');
The issue I get when trying to redirect like this is this:
"The PUT method is not supported for this route. Supported methods: GET, HEAD."
But my routes are like this:
// web.php
// The route which displays the form
Route::prefix('dashboard')->group(function () {
Route::prefix('reservations')->group(function () {
Route::get('/edit/{booking:id}', [ReservationController::class, 'show'])->name('edit.booking');
...
...
});
});
// api.php
// The route which handles the form and updates the booking
Route::controller(BookingController::class)->group(function () {
Route::patch('/booking/edit', 'edit');
...
...
...
});
@hjortur17 setup a get route to redirect to and display the message there.
@hjortur17 did you try the code I provided or?
@Sinnbeck - Yes, still the same error
@jlrdw - I would just prefer to use the route that I have already created
Hi @hjortur17
look at this: https://laravel.com/docs/9.x/blade#method-field
I don't know.
Did you try to clear the cache?
php artisan optimize:clear
Still getting 405 - The PUT method is not supported for this route. Supported methods: GET, HEAD.
So if I check the network tab, First I get 302 code, and then 405 - The PUT method is not supported for this route. Supported methods: GET, HEAD.
Any idea why it isn't allowing me to redirect after updating the booking?
If it helps, I use axios to send the put request. But all my frontend is Inertia and the backend Laravel
@hjortur17 you cannot redirect in an axios request. Redirects are for regular requests
@hjortur17 with Ajax you haven't left the page so just show the message there and include a link for a page to go to after user sees the update was successful.
Do you guys have any solution? I was trying to use this.$inertia.put('/api/booking/update', this.updatedBooking);. Like the do in their demo, but no luck. Still same error, does the URL have to be the same to be able to use redirect?
@hjortur17 oh its inertia. Didn't notice any mention of this earlier. Inertia is an exception. That should work. Show the routes
@Sinnbeck - Here are my routes:
// api.php
Route::controller(BookingController::class)->group(function () {
Route::post('/book/process', 'store');
Route::put('/booking/update', 'update');
Route::patch('/booking/edit', 'edit');
Route::patch('/booking/cancel', 'destroy');
});
// web.php
Route::middleware(['auth', 'can:admin'])->group(function () {
Route::prefix('dashboard')->group(function () {
Route::prefix('reservations')->group(function () {
Route::get('/pickup', [ReservationController::class, 'pickup']);
Route::get('/dropoff', [ReservationController::class, 'dropoff']);
Route::get('/edit/{booking:id}', [ReservationController::class, 'show'])->name('edit.booking');
});
});
});
So I have the route that handles the form stored in api.php, but the route that displays it is stored in web.php
@hjortur17 Is it a multi-step form?
@Snapey - Nope, just a single page (form) including all information about the booking. And in that form the admin can update some details and press a button to submit thoose changes and that is the PUT request
Any ideas on how to solve this with Inertia? @sinnbeck
@hjortur17 I cannot recreate the problem. Show the form in vue/react please. And the full controller method
@Sinnbeck - Okay,
Here is the controller:
public function update(Request $request)
{
$booking = Booking::where('uuid', $request->uuid)->firstOrFail();
$booking->pick_up_date = Carbon::parse($request->pick_up_date)->format('d/m/Y');
$booking->pick_up_time = $request->pick_up_time;
$booking->drop_off_date = Carbon::parse($request->drop_off_date)->format('d/m/Y');
$booking->drop_off_time = $request->drop_off_time;
// Updating additional information
$booking->additional_pick_up_time = $request->additional_pick_up_time;
$booking->additional_drop_off_time = $request->additional_drop_off_time;
$booking->additional_flight_no = $request->additional_flight_no;
$booking->additional_nr_of_pax = $request->additional_nr_of_pax;
$booking->customer()->update($request->customer);
$booking->carClass()->sync([CarClass::where('uuid', $request->carClass['uuid'])->firstOrFail()->id]);
$booking->car()->sync([Car::where('uuid', $request->car['uuid'])->firstOrFail()->id]);
foreach ($request->drivers as $driver) {
$booking->drivers()->update($driver);
}
// Update Packages
$arrayToBySynced = [];
foreach ($request->packages as $package) {
$arrayToBySynced[] = $package['id'];
}
$booking->packages()->sync($arrayToBySynced);
// Update Insurance
$arrayToBySynced = [];
foreach ($request->insurances as $insurance) {
$arrayToBySynced[] = $insurance['id'];
}
$booking->insurances()->sync($arrayToBySynced);
// Update Extras
$arrayToBySynced = [];
foreach ($request->extras as $exrra) {
$arrayToBySynced[] = $exrra['id'];
}
$booking->extras()->sync($arrayToBySynced);
$booking->save();
return Redirect::back()->with('success', 'Booking updated.');
// return Redirect::route('edit.booking', [$booking])->with('success', 'Sucess message');
}
And here is the form:
<template>
<Head :title="'Booking #' + booking.id" />
<div class="mb-6 flex w-full items-center justify-between">
<div class="flex items-center space-x-6">
<h2>Booking #{{ booking.id }}</h2>
<p
class="text-sm text-neutral-400"
v-if="updatedBooking.status !== 'Created'"
>
This booking has been
<span v-if="updatedBooking.status === 'Rented'">rented.</span>
<span v-if="updatedBooking.status === 'Returned'">closed.</span>
<span v-if="updatedBooking.status === 'Cancelled'">cancelled.</span>
</p>
</div>
<div class="flex space-x-4">
<button
class="mx-auto h-10 w-10 cursor-pointer rounded-full bg-red-200 p-2.5 text-red-900 duration-150 ease-in-out hover:bg-red-400 hover:text-red-50"
@click="cancelBooking($event)"
v-if="
updatedBooking.status !== 'Rented' ||
updatedBooking.status !== 'Returned' ||
updatedBooking.status !== 'Cancelled'
"
title="Cancel booking"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
class="h-full w-full fill-current"
>
<!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) -->
<path
d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z"
/>
</svg>
</button>
<button
class="mx-auto h-10 w-10 cursor-pointer rounded-full bg-indigo-200 p-2.5 text-indigo-900 duration-150 ease-in-out hover:bg-indigo-400 hover:text-indigo-50"
@click="editBooking($event, 'end')"
v-if="updatedBooking.status === 'Rented'"
title="End rental"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 384 512"
class="h-full w-full fill-current"
>
<!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) -->
<path
d="M349.5 475.5l-211.1-211c-4.7-4.7-4.7-12.3 0-17l211.1-211c4.7-4.7 12.3-4.7 17 0l7.1 7.1c4.7 4.7 4.7 12.3 0 17L178.1 256l195.5 195.5c4.7 4.7 4.7 12.3 0 17l-7.1 7.1c-4.7 4.6-12.3 4.6-17-.1zm-111 0l7.1-7.1c4.7-4.7 4.7-12.3 0-17L50.1 256 245.5 60.5c4.7-4.7 4.7-12.3 0-17l-7.1-7.1c-4.7-4.7-12.3-4.7-17 0l-211.1 211c-4.7 4.7-4.7 12.3 0 17l211.1 211c4.8 4.8 12.4 4.8 17.1.1z"
/>
</svg>
</button>
<button
class="mx-auto h-10 w-10 cursor-pointer rounded-full bg-green-200 p-2.5 text-green-900 duration-150 ease-in-out hover:bg-green-400 hover:text-green-50"
@click="editBooking($event, 'start')"
v-if="updatedBooking.status === 'Created'"
title="Start rental"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 384 512"
class="h-full w-full fill-current"
>
<!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) -->
<path
d="M34.5 36.5l211.1 211c4.7 4.7 4.7 12.3 0 17l-211.1 211c-4.7 4.7-12.3 4.7-17 0l-7.1-7.1c-4.7-4.7-4.7-12.3 0-17L205.9 256 10.5 60.5c-4.7-4.7-4.7-12.3 0-17l7.1-7.1c4.6-4.6 12.2-4.6 16.9.1zm111 0l-7.1 7.1c-4.7 4.7-4.7 12.3 0 17L333.9 256 138.5 451.5c-4.7 4.7-4.7 12.3 0 17l7.1 7.1c4.7 4.7 12.3 4.7 17 0l211.1-211c4.7-4.7 4.7-12.3 0-17l-211.1-211c-4.8-4.8-12.4-4.8-17.1-.1z"
/>
</svg>
</button>
<button
class="mx-auto h-10 w-10 cursor-pointer rounded-full bg-sky-200 p-2.5 text-sky-900 duration-150 ease-in-out hover:bg-sky-400 hover:text-sky-50"
@click="updateBooking($event)"
title="Save booking"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512"
class="h-full w-full fill-current"
>
<!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) -->
<path
d="M433.941 129.941l-83.882-83.882A48 48 0 0 0 316.118 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V163.882a48 48 0 0 0-14.059-33.941zM288 64v96H96V64h192zm128 368c0 8.822-7.178 16-16 16H48c-8.822 0-16-7.178-16-16V80c0-8.822 7.178-16 16-16h16v104c0 13.255 10.745 24 24 24h208c13.255 0 24-10.745 24-24V64.491a15.888 15.888 0 0 1 7.432 4.195l83.882 83.882A15.895 15.895 0 0 1 416 163.882V432zM224 232c-48.523 0-88 39.477-88 88s39.477 88 88 88 88-39.477 88-88-39.477-88-88-88zm0 144c-30.879 0-56-25.121-56-56s25.121-56 56-56 56 25.121 56 56-25.121 56-56 56z"
/>
</svg>
</button>
<button
class="mx-auto h-10 w-10 cursor-pointer rounded-full bg-pink-200 p-2.5 text-pink-900 duration-150 ease-in-out hover:bg-pink-400 hover:text-pink-50"
@click="sendRentalAgreement()"
title="Send Rental Agreement"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
class="h-full w-full fill-current"
>
<!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) -->
<path
d="M149.106 512c-33.076 0-66.153-12.59-91.333-37.771-50.364-50.361-50.364-132.305-.002-182.665L319.842 29.498c39.331-39.331 103.328-39.331 142.66 0 39.331 39.332 39.331 103.327 0 142.657l-222.63 222.626c-28.297 28.301-74.347 28.303-102.65 0-28.3-28.301-28.3-74.349 0-102.649l170.301-170.298c4.686-4.686 12.284-4.686 16.97 0l5.661 5.661c4.686 4.686 4.686 12.284 0 16.971l-170.3 170.297c-15.821 15.821-15.821 41.563.001 57.385 15.821 15.82 41.564 15.82 57.385 0l222.63-222.626c26.851-26.851 26.851-70.541 0-97.394-26.855-26.851-70.544-26.849-97.395 0L80.404 314.196c-37.882 37.882-37.882 99.519 0 137.401 37.884 37.881 99.523 37.882 137.404.001l217.743-217.739c4.686-4.686 12.284-4.686 16.97 0l5.661 5.661c4.686 4.686 4.686 12.284 0 16.971L240.44 474.229C215.26 499.41 182.183 512 149.106 512z"
/>
</svg>
</button>
</div>
</div>
<div class="space-y-8">
<div class="rounded-lg border-2 border-neutral-100 bg-white">
<div
class="w-full border-b-2 border-neutral-100 bg-neutral-100 px-8 py-3"
>
<h5 class="text-neutral-800">Order information</h5>
</div>
<div class="flex w-full items-end space-x-4 p-8">
<div class="flex w-1/3 flex-col">
<label
for="vehicle_class"
class="mb-2 block text-sm font-black uppercase text-gray-900 dark:text-gray-400"
>Class</label
>
<select
id="vehicle_class"
class="min-w-full rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.carClass"
>
<option>Choose a class</option>
<option v-for="vc in vehicleClasses" :value="vc">
{{ vc.title }}
</option>
</select>
</div>
<div class="flex w-1/3 flex-col">
<label
for="vehicle_class"
class="mb-2 block text-sm font-black uppercase text-gray-900 dark:text-gray-400"
>Unit</label
>
<select
id="vehicle_class"
class="min-w-full rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.car"
>
<option value="Choose a unit">Choose a unit</option>
<option v-for="car in units" :value="car">
{{ car.license_plate }} - {{ car.make }}
{{ car.model }}
</option>
</select>
</div>
<div class="flex w-auto flex-col">
<label
for="booking_type"
class="mb-2 block text-sm font-black uppercase text-gray-900 dark:text-gray-400"
>Booking Type</label
>
<select
id="booking_type"
class="min-w-full rounded-lg border bg-white p-2 focus:border-blue-400"
>
<option
v-for="type in booking_type"
:selected="type === booking.booking_type"
>
{{ type }}
</option>
</select>
</div>
<div class="flex w-52 flex-col">
<label
for="ref_id"
class="mb-2 block text-sm font-black uppercase text-gray-900 dark:text-gray-400"
>Reference Id</label
>
<input
type="text"
id="ref_id"
class="min-w-full rounded-lg border bg-white p-2 outline-none focus:border-blue-400"
:value="booking.random_key"
/>
</div>
<div class="flex w-24 flex-col">
<label
for="acriss"
class="mb-2 block text-sm font-black uppercase text-gray-900 dark:text-gray-400"
>ACRISS</label
>
<input
type="text"
id="acriss"
class="w-full rounded-lg border bg-white p-2 outline-none focus:border-blue-400"
value=""
/>
</div>
</div>
</div>
<div class="rounded-lg border-2 border-neutral-100 bg-white">
<div
class="w-full border-b-2 border-neutral-100 bg-neutral-100 px-8 py-3"
>
<h5 class="text-neutral-800">Date information</h5>
</div>
<div class="flex w-full flex-col space-y-4 p-8">
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Pick Up
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-auto">
<datepicker
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.pick_up_date"
:lowerLimit="new Date()"
inputFormat="dd/MM/yyyy"
/>
</div>
<div class="w-32">
<TimeDropdown
class="min-w-full rounded-lg border bg-white p-2 focus:border-blue-400"
name="pick_up_time"
label="pick-up-time"
v-model="updatedBooking.pick_up_time"
/>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Drop Off
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-auto">
<datepicker
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.drop_off_date"
:lowerLimit="minDay"
inputFormat="dd/MM/yyyy"
/>
</div>
<div class="w-32">
<TimeDropdown
class="min-w-full rounded-lg border bg-white p-2 focus:border-blue-400"
name="drop_off_time"
label="drop-off-time"
v-model="updatedBooking.drop_off_time"
/>
</div>
</div>
</div>
</div>
</div>
<div class="rounded-lg border-2 border-neutral-100 bg-white">
<div
class="w-full border-b-2 border-neutral-100 bg-neutral-100 px-8 py-3"
>
<h5 class="text-neutral-800">Insurances and Extras</h5>
</div>
<div class="flex w-full flex-col space-y-4 p-8">
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Packages
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-auto" v-for="pack in packages">
<label class="block">
<input
class="mr-2 leading-tight"
type="checkbox"
:aria-label="pack.title"
:value="{ id: pack.id, title: pack.title }"
v-model="updatedBooking.packages"
@change="
recalculatePrice(
pack.title,
pack.price_per_day
)
"
/>
<span>{{ pack.title }}</span>
</label>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Insurances
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-auto" v-for="insurance in insurances">
<label class="block">
<input
class="mr-2 leading-tight"
type="checkbox"
:aria-label="insurance.title"
:value="{
id: insurance.id,
title: insurance.title,
}"
v-model="updatedBooking.insurances"
@change="
recalculatePrice(
insurance.title,
insurance.price_per_day
)
"
/>
<span>{{ insurance.title }}</span>
</label>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Extras
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-auto" v-for="extra in extras">
<label class="block">
<input
class="mr-2 leading-tight"
type="checkbox"
:aria-label="extra.title"
:value="{
id: extra.id,
title: extra.title,
}"
v-model="updatedBooking.extras"
@change="
recalculatePrice(
extra.title,
extra.price_per_day
)
"
/>
<span>{{ extra.title }}</span>
</label>
</div>
</div>
</div>
</div>
</div>
<div class="rounded-lg border-2 border-neutral-100 bg-white">
<div
class="w-full border-b-2 border-neutral-100 bg-neutral-100 px-8 py-3"
>
<h5 class="text-neutral-800">Prices</h5>
</div>
<div class="flex w-full flex-col space-y-4 p-8">
<table class="w-full table-auto">
<thead
class="border-b border-neutral-200 bg-transparent text-xs font-semibold uppercase text-neutral-900"
>
<tr>
<th></th>
<th class="p-2">
<div class="text-right">Price</div>
</th>
<th class="p-2">
<div class="text-right font-semibold">
Discount
</div>
</th>
<th class="p-2">
<div class="text-right font-semibold">
Price Per Day
</div>
</th>
<th class="p-2">
<div class="text-right font-semibold">
Total
</div>
</th>
</tr>
</thead>
<tbody class="border border-neutral-200 text-sm">
<tr
class="border-b border-neutral-200 odd:bg-neutral-50"
v-for="(data, index) in JSON.parse(
updatedBooking.price.data
)"
>
<td class="p-2">
<div class="font-bold text-gray-800">
{{ data.title }}
</div>
</td>
<td class="p-2">
<div class="text-right">
{{ parseFloat(data.price) }} kr
</div>
</td>
<td class="p-2">
<div class="text-right font-medium">
<input
type="number"
:value="data.discount"
:disabled="!changePrice"
min="0"
max="100"
@change="
manuallyChangePrice(
data.title,
$event.target.value,
data.price_per_day
)
"
class="w-12 border border-blue-400 bg-white text-right outline-none disabled:border-0 disabled:bg-transparent"
/>%
</div>
</td>
<td class="p-2">
<div class="text-right font-medium">
<input
type="number"
:value="parseFloat(data.price_per_day)"
:disabled="!changePrice"
@change="
manuallyChangePrice(
data.title,
data.discount,
$event.target.value
)
"
class="w-20 border border-blue-400 bg-white text-right outline-none disabled:border-0 disabled:bg-transparent"
/>
kr
</div>
</td>
<td class="p-2">
<div class="text-right font-medium">
{{
data.discount
? parseFloat(
data.price -
data.price *
(data.discount / 100)
)
: data.price
}}
kr
</div>
</td>
</tr>
</tbody>
</table>
<div class="flex items-start justify-between">
<div class="flex flex-1">
<label class="block">
<input
class="mr-2 leading-tight"
type="checkbox"
v-model="changePrice"
aria-label="Manually change price"
/>
<span> Manually change price </span>
</label>
</div>
<div class="flex max-w-max flex-col">
<div class="flex w-64 items-center justify-end">
<div class="w-1/2 text-right text-sm">
<strong class="uppercase">Total:</strong>
</div>
<div class="w-5/12 text-right">
<p>
{{
parseFloat(
updatedBooking.price.total_price
)
}}
kr
</p>
</div>
</div>
<div class="flex w-64 items-center justify-end">
<div class="w-1/2 text-right text-sm">
<strong class="uppercase">Total paid:</strong>
</div>
<div class="w-5/12 text-right">
<p>
{{
parseFloat(
updatedBooking.price.total_paid
)
}}
kr
</p>
</div>
</div>
<div class="flex w-64 items-center justify-end">
<div class="w-1/2 text-right text-sm">
<strong class="uppercase"
>Total balance:</strong
>
</div>
<div class="w-5/12 text-right">
<p>
{{
parseFloat(
updatedBooking.price.total_balance
)
}}
kr
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="rounded-lg border-2 border-neutral-100 bg-white">
<div
class="w-full border-b-2 border-neutral-100 bg-neutral-100 px-8 py-3"
>
<h5 class="text-neutral-800">Customer Information</h5>
</div>
<div class="flex w-full flex-col space-y-4 p-8">
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Full Name
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<input
type="text"
name="full_name"
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.customer.full_name"
/>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Email
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<input
type="email"
name="email"
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.customer.email_address"
/>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Phone
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<input
type="tel"
name="phone"
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.customer.phone_number"
/>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Street Address
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<input
type="text"
name="streeet_address"
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.customer.street_address"
/>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Town
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<input
type="text"
name="town"
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.customer.town"
/>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
ZIP Code
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<input
type="text"
name="zip_code"
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.customer.zip_code"
/>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Country
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<input
type="text"
name="country"
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.customer.country"
/>
</div>
</div>
</div>
</div>
</div>
<div class="rounded-lg border-2 border-neutral-100 bg-white">
<div
class="w-full border-b-2 border-neutral-100 bg-neutral-100 px-8 py-3"
>
<h5 class="text-neutral-800">Drivers Information</h5>
</div>
<div
class="flex w-full flex-col space-y-4 p-8"
v-for="(driver, index) in booking.drivers"
>
<span v-if="booking.drivers.length > 1"
>Driver #{{ index + 1 }}</span
>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Driver's name
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<input
type="text"
name="drivers_full_name"
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="
updatedBooking.drivers[index].full_name
"
/>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
License number
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<input
type="text"
name="drivers_license_number"
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="
updatedBooking.drivers[index].license_no
"
/>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Date of Birth
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<datepicker
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="
updatedBooking.drivers[index].date_of_birth
"
inputFormat="dd/MM/yyyy"
/>
</div>
</div>
</div>
</div>
</div>
<div class="rounded-lg border-2 border-neutral-100 bg-white">
<div
class="w-full border-b-2 border-neutral-100 bg-neutral-100 px-8 py-3"
>
<h5 class="text-neutral-800">Additional Information</h5>
</div>
<div class="flex w-full flex-col space-y-4 p-8">
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Flight number
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<input
type="text"
name="flight_number"
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.additional_flight_no"
/>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Pick Up
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<input
type="text"
name="additional_pick_up_time"
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.additional_pick_up_time"
/>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Drop Off
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<input
type="text"
name="additional_drop_off_time"
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="
updatedBooking.additional_drop_off_time
"
/>
</div>
</div>
</div>
<div class="flex w-full items-center space-x-6">
<div class="w-48 text-right">
<h5
class="text-sm font-medium uppercase text-neutral-600"
>
Nr of Passengers
</h5>
</div>
<div class="flex w-full space-x-4">
<div class="w-1/2">
<input
type="text"
name="additional_nr_of_pax"
class="w-auto rounded-lg border bg-white p-2 focus:border-blue-400"
v-model="updatedBooking.additional_nr_of_pax"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Datepicker from "vue3-datepicker";
import TimeDropdown from "../../../Shared/TimeDropdown";
import dayjs from "dayjs";
import axios from "axios";
import { Inertia } from "@inertiajs/inertia";
export default {
layout: "Dashboard",
components: { Datepicker, TimeDropdown },
props: ["booking", "vehicleClasses", "insurances", "packages", "extras"],
data() {
return {
updatedBooking: JSON.parse(
JSON.stringify({
...this.booking,
car: this.booking.car[0],
carClass: this.booking.carClass[0],
packages: this.booking.packages.map((x) =>
this._selectFewerProps(x)
),
insurances: this.booking.insurances.map((x) =>
this._selectFewerProps(x)
),
extras: this.booking.extras.map((x) =>
this._selectFewerProps(x)
),
})
),
booking_type: [
"Online",
"By email",
"By phone",
"Walk in",
"Invoice",
"Broker",
],
minDay: new Date(dayjs(new Date()).add(2, "day")),
units: [],
changed: false,
changePrice: false,
};
},
methods: {
recalculatePrice(title, price_per_day) {
const days = dayjs(this.updatedBooking.drop_off_date).diff(
this.updatedBooking.pick_up_date,
"day"
);
const data = JSON.parse(this.updatedBooking.price.data);
let totalPrice = this.updatedBooking.price.total_price;
let totalBalance = this.updatedBooking.price.total_balance;
const price = price_per_day * days;
const looking = data.find((d) => d.title === title);
if (looking !== undefined) {
totalPrice = Math.round(
parseFloat(totalPrice) - parseFloat(looking.total_price)
);
totalBalance = Math.round(
parseFloat(totalBalance) - parseFloat(looking.total_price)
);
data.splice(
data.findIndex((d) => d.title === title),
1
);
} else {
totalPrice = Math.round(parseFloat(totalPrice) + price);
totalBalance = Math.round(parseFloat(totalBalance) + price);
data.push({
title,
price,
discount: 0,
price_per_day,
total_price: price,
});
}
this.updatedBooking.price.total_price = totalPrice;
this.updatedBooking.price.total_balance = totalBalance;
this.updatedBooking.price.data = JSON.stringify(data);
},
manuallyChangePrice(title, discount, price_per_day) {
const days = dayjs(this.updatedBooking.drop_off_date).diff(
this.updatedBooking.pick_up_date,
"day"
);
const data = JSON.parse(this.updatedBooking.price.data);
let totalPrice = this.updatedBooking.price.total_price;
let totalBalance = this.updatedBooking.price.total_balance;
let total_price;
const price = price_per_day * days;
if (discount === undefined || discount === 0) {
total_price = price;
} else {
total_price = price - price * (discount / 100);
}
const looking = data.find((d) => d.title === title);
const updatedPricePerDay = total_price / days;
totalPrice = total_price;
totalBalance = total_price - this.updatedBooking.price.total_paid;
looking.discount = discount;
looking.price_per_day = updatedPricePerDay;
looking.total_price = total_price;
// noinspection EqualityComparisonWithCoercionJS
if (looking && discount == 0) {
totalPrice = this.booking.price.total_price;
totalBalance =
totalPrice - this.updatedBooking.price.total_paid;
const oldVal = JSON.parse(this.booking.price.data);
looking.discount = discount;
looking.price_per_day =
oldVal[
data.findIndex((d) => d.title === title)
].price_per_day;
looking.total_price =
oldVal[
data.findIndex((d) => d.title === title)
].total_price;
}
// FIXME: Not override price, add to
this.updatedBooking.price.total_price = totalPrice;
this.updatedBooking.price.total_balance = totalBalance;
this.updatedBooking.price.data = JSON.stringify(data);
},
updateBooking() {
// TODO: Figure out a way to recalculate the prices
if (confirm("Are you sure you want to update this booking?")) {
this.$inertia.put("/api/booking/update", this.updatedBooking);
}
},
editBooking(e, status) {
e.preventDefault();
// TODO: Add loader
if (confirm("Are you sure you want to update this booking?")) {
axios
.patch("/api/booking/edit", {
uuid: this.updatedBooking.uuid,
carClass: this.booking.carClass[0],
status,
})
.then((resp) => this.updatedBooking.status = resp.data);
}
},
cancelBooking(e) {
e.preventDefault();
// TODO: Add loader
// TODO: Figure out a way to recalculate the prices
if (
confirm(
"Are you sure you want to cancel booking: #" +
this.updatedBooking.id
)
) {
axios
.patch("/api/booking/cancel", {
uuid: this.booking.uuid,
carClass: this.booking.carClass[0],
})
.then(function (data) {})
.finally(() => Inertia.visit("/dashboard"));
}
},
async sendRentalAgreement() {
if (confirm("Are you sure you want to update this booking?")) {
await axios
.post("/api/mail/rental-agreement", {
uuid: this.booking.uuid,
})
.then(function (data) {
console.log(data);
});
}
},
async _collectUnits(newVal, oldVal) {
// TODO: Add spam protection
await this._getCars();
if (newVal === undefined) {
this.units.push(this.booking.car[0]);
}
},
_getCars: function () {
return axios
.get(
window.location.origin +
"/api/cars/vehicleClass/" +
this.updatedBooking.carClass.slug,
{
params: {
pick_up_date: this.updatedBooking.pick_up_date,
drop_off_date: this.updatedBooking.drop_off_date,
},
}
)
.then((resp) => {
this.units = resp.data;
});
},
_selectFewerProps(item) {
const { id, title } = item;
return { id, title };
},
_updatePickUpDate() {
const [day, month, year] = this.booking.pick_up_date.split("/");
this.updatedBooking.pick_up_date = new Date(
+year,
+month - 1,
+day
);
},
_updateDropOffDate() {
const [day, month, year] = this.booking.drop_off_date.split("/");
this.updatedBooking.drop_off_date = new Date(
+year,
+month - 1,
+day
);
},
_updateDriversDOB() {
this.booking.drivers.forEach((driver, index) => {
this.updatedBooking.drivers[index].date_of_birth = new Date(
driver.date_of_birth
);
});
},
},
mounted: function () {
this._collectUnits();
this._updatePickUpDate();
this._updateDropOffDate();
this._updateDriversDOB();
},
watch: {
"updatedBooking.carClass"(newVal, oldVal) {
this._collectUnits(newVal, oldVal);
},
"updatedBooking.pick_up_date"(val) {
if (
dayjs(this.updatedBooking.drop_off_date).diff(
this.updatedBooking.pick_up_date,
"day"
) < 2
) {
const newDate = dayjs(val).add(2, "day");
this.minDay = newDate.$d;
this.updatedBooking.drop_off_date = newDate.$d;
}
},
updatedBooking() {
this.changed = true;
},
},
};
</script>
<style>
.dropdown:hover .dropdown-menu {
display: block;
}
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type="number"] {
-moz-appearance: textfield;
}
</style>
@hjortur17 Ah I just noticed you put routes inside api.php. All routes goes in web.php. Inertia isnt an api
@Sinnbeck - So their is no way to make Inertia talk to /api? So I should move all my routes to web.php
@hjortur17 it's because inertia uses sessions and api is stateless. Also I'm unsure if inertia binds to the api middleware at all. You can try setting api.php to use the web middleware
Why would you want them in api.php anyways? An api would never redirect back
@Sinnbeck - So most of my code base so it's no longer trying to redirect from the api.php. But the issue I am getting now is that when I try to edit my rental I get 21 302 requests and if I refresh the page I get the flash message. Any idea what can causing this?
Here is the routes (web.php):
Route::middleware(['auth', 'can:admin'])->prefix('dashboard')->group(function () {
Route::prefix('reservations')->group(function () {
Route::get('pickup', [ReservationController::class, 'pickup']);
Route::get('dropoff', [ReservationController::class, 'dropoff']);
Route::get('edit/{rental}', [ReservationController::class, 'edit'])->name('edit.rental');
Route::patch('edit', [RentalController::class, 'edit']);
Route::put('update', [RentalController::class, 'update']);
Route::patch('cancel', [RentalController::class, 'destroy']);
});
});
Here is the edit method:
public function edit(Rental $rental, Request $request)
{
if ($request->status === 'start') {
$rental->status = BookingStatusEnum::RENTED;
} else if ($request->status === 'end') {
$carClass = CarClass::where('uuid', $request->carClass['uuid'])->firstOrFail();
$carClass->available = $carClass->available + 1;
$carClass->save();
$rental->status = BookingStatusEnum::RETURNED;
}
$rental->save();
return Redirect::route('edit.rental', $rental)->with('success', 'Rental ' . $rental->status->value . '.');
}
It looks like it trying to redirect and then update the rental. Because now I'm getting 405 error because It's trying to send put request to dashboard/reservations/edit/18.
Any idea why that is happening?
@sinnbeck - Any idea what can be causing this? Thanks in advance
This happens because you are doing back() on a PUT route, which causes it to be redirected to the previous route but passing the same HTTP method, in this case PUT.
To solve this, you return a JSON from the backend and on the frontend validate or change it to the route in addition to GET receiving PUT as well.
I believe that this should not actually exist, because if you receive a PUT and do redirect()->back(), logically it should automatically convert the request to HTTP, but for some reason when it is a PUT it does not do this.
Please or to participate in this conversation.