Make customer addresses editable.
This commit is contained in:
18
app/Http/Controllers/AddressController.php
Normal file
18
app/Http/Controllers/AddressController.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Address;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
class AddressController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(int $address_id): View
|
||||
{
|
||||
$address = Address::find($address_id);
|
||||
return view('address.edit', ['address' => $address]);
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ class AddressController extends Controller
|
||||
}
|
||||
|
||||
$address = $customer->addresses()->create($addressData);
|
||||
|
||||
return response()->json($address);
|
||||
}
|
||||
|
||||
@@ -54,6 +55,29 @@ class AddressController extends Controller
|
||||
return response()->json($address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Address $address): JsonResponse
|
||||
{
|
||||
$customer = $address->customer;
|
||||
$addressData = $request->validate([
|
||||
'is_address' => 'boolean',
|
||||
'is_delivery' => 'boolean',
|
||||
]);
|
||||
|
||||
if (isset($addressData['is_address']) && $addressData['is_address'] == 1) {
|
||||
$customer->addresses()->where('is_address', true)->update(['is_address' => false]);
|
||||
}
|
||||
|
||||
if (isset($addressData['is_delivery']) && $addressData['is_delivery'] == 1) {
|
||||
$customer->addresses()->where('is_delivery', true)->update(['is_delivery' => false]);
|
||||
}
|
||||
$address->update($addressData);
|
||||
|
||||
return response()->json($address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Address extends Model
|
||||
@@ -26,4 +27,12 @@ class Address extends Model
|
||||
'country',
|
||||
'zip',
|
||||
];
|
||||
|
||||
/**
|
||||
* Return the customer having the address.
|
||||
*/
|
||||
public function customer(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Customer::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ return [
|
||||
'Customer' => 'Kunde',
|
||||
'Add new customer' => 'Neuer Kunde',
|
||||
'Edit existing customer' => 'Bestehenden Kunden bearbeiten',
|
||||
'Edit existing address' => 'Bestehende Adresse bearbeiten',
|
||||
'Edit existing addresses' => 'Bestehende Adresse bearbeiten',
|
||||
'Edit customer' => 'Kunde bearbeiten',
|
||||
'Add new customer by clicking add' => 'Durch Klick auf "Anlegen" neuen Kunden erstellen',
|
||||
'Existing customers' => 'Bestehende Kunden',
|
||||
@@ -31,5 +33,6 @@ return [
|
||||
'Are you sure you want to delete the address?' => 'Sicher, dass die Adresse gelöscht werden soll?',
|
||||
'Once the address is deleted, all the ressources and data will be permanently deleted.' => 'Sobald die Adresse gelöscht wird, werden alle Ressourcen und Daten dauerhaft gelöscht.',
|
||||
'Enter your customer\'s address.' => 'Gib die Adresse des Kunden ein.',
|
||||
'Hint edit address.' => 'Bei bestehenden Adressen kann nur gewählt werden, ob es sich um eine Rechnungs- oder Versandadresse handelt. Die Bearbeitung der anderen Felder ist unterbunden, da diese Daten Einfluss auf bereits bestehende Rechnungen haben.'
|
||||
|
||||
];
|
||||
|
||||
@@ -19,6 +19,8 @@ return [
|
||||
'Existing invoices' => 'Bestehende Rechnungen',
|
||||
'Create new invoice' => 'Neue Rechnung anlegen',
|
||||
'Select customer' => 'Kunde auswählen',
|
||||
'Select address' => 'Adresse auswählen',
|
||||
'Search customer' => 'Kunden suchen',
|
||||
'Select your customer and address' => 'Wähle einen Kunden und seine Adresse aus',
|
||||
'Invoice item' => 'Rechnungsposition',
|
||||
'Invoice items' => 'Rechnungspositionen',
|
||||
|
||||
129
resources/views/address/edit.blade.php
Normal file
129
resources/views/address/edit.blade.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('customer.Edit existing address') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12" x-data="addressForm()">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg" x-bind="address">
|
||||
|
||||
<div class="max-w-xl">
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('customer.Edit address') }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ __("customer.Hint edit address.") }}
|
||||
</p>
|
||||
</header>
|
||||
<form class="mt-6 space-y-6" @submit.prevent="">
|
||||
|
||||
<p class="text-red-600 font-bold" x-text="message" x-show="error"></p>
|
||||
<p x-show="success" x-transition
|
||||
class="text-sm text-green-600 dark:text-green-400">{{ __('form.Saved') }}</p>
|
||||
|
||||
<div>
|
||||
<x-input-label for="name" :value="__('common.Name')"/>
|
||||
<x-text-input id="name" name="name" type="text"
|
||||
class="mt-1 block w-full" :value="old('name')" disabled autofocus
|
||||
autocomplete="name" x-model="address.name"/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="email" :value="__('common.Email')"/>
|
||||
<x-text-input id="email" name="email" type="email"
|
||||
class="mt-1 block w-full" :value="old('email')" disabled
|
||||
autocomplete="username" x-model="address.email"/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="phone" :value="__('common.Phone')"/>
|
||||
<x-text-input id="phone" name="phone" type="text"
|
||||
class="mt-1 block w-full" :value="old('phone')" disabled
|
||||
autocomplete="phone"
|
||||
x-model="address.phone"/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-input-label for="address" :value="__('common.Address')"/>
|
||||
<x-text-input id="address" name="address" type="text"
|
||||
class="mt-1 block w-full" :value="old('address')" disabled
|
||||
autocomplete="address"
|
||||
x-model="address.address"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row space-x-8">
|
||||
<div class="w-1/4">
|
||||
<x-input-label for="zip" :value="__('common.Zip Code')"/>
|
||||
<x-text-input id="zip" name="zip" type="text"
|
||||
class="mt-1 block w-full" :value="old('zip')" disabled
|
||||
autocomplete="zip"
|
||||
x-model="address.zip"/>
|
||||
</div>
|
||||
<div class="w-3/4">
|
||||
<x-input-label for="city" :value="__('common.City')"/>
|
||||
<x-text-input id="city" name="city" type="text"
|
||||
class="mt-1 block w-full" :value="old('city')" disabled
|
||||
autocomplete="city"
|
||||
x-model="address.city"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row space-x-8">
|
||||
<x-input-label class="w-1/4" for="is_address"
|
||||
:value="__('customer.Invoice Address')"/>
|
||||
<input type="hidden" name="is_address" value="0"/>
|
||||
<x-text-input id="is_address" name="is_address" type="checkbox"
|
||||
class="mt-1" :value="old('is_address')"
|
||||
x-model="address.is_address"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row space-x-8">
|
||||
<x-input-label class="w-1/4" for="is_delivery"
|
||||
:value="__('customer.Delivery Address')"/>
|
||||
<input type="hidden" name="is_delivery" value="0"/>
|
||||
<x-text-input id="is_delivery" name="is_delivery" type="checkbox"
|
||||
class="mt-1" :value="old('is_delivery')"
|
||||
x-model="address.is_delivery"/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<x-primary-button @click="submit">{{ __('form.Save') }}</x-primary-button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
||||
<script>
|
||||
function addressForm() {
|
||||
return {
|
||||
address: {!! $address !!},
|
||||
error: false,
|
||||
message: '',
|
||||
success: false,
|
||||
|
||||
submit() {
|
||||
let vm = this;
|
||||
axios.put('/address/' + this.address.id, this.address)
|
||||
.then(function (response) {
|
||||
vm.success = true;
|
||||
vm.error = false;
|
||||
})
|
||||
.catch(function (error) {
|
||||
vm.error = true;
|
||||
vm.message = error.response.data;
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
3
resources/views/components/edit-icon.blade.php
Normal file
3
resources/views/components/edit-icon.blade.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" {{ $attributes->merge(['class' => 'size-8 p-1']) }}>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 401 B |
3
resources/views/components/trash-icon.blade.php
Normal file
3
resources/views/components/trash-icon.blade.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" {{ $attributes->merge(['class' => 'size-8 p-1']) }}>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 651 B |
@@ -7,6 +7,7 @@
|
||||
|
||||
<div class="py-12" x-data="customerForm()">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
|
||||
<!-- Customer data -->
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w-xl">
|
||||
@@ -49,7 +50,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Existing addresses -->
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg" x-bind="addresses">
|
||||
<div class="max-w">
|
||||
@@ -58,19 +58,22 @@
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('customer.Existing addresses') }}
|
||||
</h2>
|
||||
|
||||
</header>
|
||||
<div class="grid grid-cols-3">
|
||||
<div class="grid grid-cols-3 space-x-4 space-y-4">
|
||||
<template x-for="address in addresses" :key="address.id">
|
||||
<div>
|
||||
<div class="flex flex-col p-8 pb-0">
|
||||
<div class="border border-gray-500 pb-8">
|
||||
<div class="flex flex-col p-8">
|
||||
<x-address-card/>
|
||||
</div>
|
||||
<x-danger-button
|
||||
x-show="!address.is_delivery && !address.is_address"
|
||||
class="ml-8"
|
||||
x-data=""
|
||||
x-on:click.prevent="delete_id = address.id;$dispatch('open-modal', 'confirm-user-deletion')"
|
||||
>{{ __('customer.Delete Address') }}</x-danger-button>
|
||||
<div class="flex flex-row">
|
||||
<a class="ml-8 inline-block" x-bind:href="'/address/' + address.id + '/edit'"><x-primary-button class="pl-2"><x-edit-icon class="h-6 mr-2"/>{{ __('form.Edit') }}</x-primary-button></a>
|
||||
<x-danger-button
|
||||
class="ml-2 pl-2"
|
||||
x-data=""
|
||||
x-on:click.prevent="delete_id = address.id;$dispatch('open-modal', 'confirm-user-deletion')"
|
||||
><x-trash-icon class="h-6 mr-2"/>{{ __('form.Delete') }}</x-danger-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@@ -189,7 +192,6 @@
|
||||
</form>
|
||||
</x-modal>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -73,8 +73,10 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a class="ml-8 mb-8 inline-block" x-bind:href="'/customer/' + customer.id + '/edit'"><x-primary-button>{{ __('form.Edit') }}</x-primary-button></a>
|
||||
<a class="ml-8 mb-8 inline-block" x-bind:href="'/customer/' + customer.id + '/edit'"><x-primary-button class="pl-2+
|
||||
"><x-edit-icon class="h-6 mr-2"/>{{ __('form.Edit') }}</x-primary-button></a>
|
||||
</div>
|
||||
|
||||
</details>
|
||||
</template>
|
||||
</section>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
{{ __('common.Dashboard') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('customer.index')"
|
||||
:active="\Illuminate\Support\Str::startsWith(request()->route()->getName(), 'customer.')">
|
||||
:active="\Illuminate\Support\Str::startsWith(request()->route()->getName(), 'customer.') || \Illuminate\Support\Str::startsWith(request()->route()->getName(), 'address.')">
|
||||
{{ __('customer.Customers') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('invoice.index')"
|
||||
|
||||
@@ -5,6 +5,7 @@ use App\Http\Controllers\Api\AuthController;
|
||||
use App\Http\Controllers\Api\CustomerController;
|
||||
use App\Http\Controllers\Api\InvoiceController;
|
||||
use App\Http\Controllers\Api\InvoiceitemController;
|
||||
use App\Http\Controllers\Api\MailController;
|
||||
use App\Http\Controllers\Api\OptionController;
|
||||
use App\Http\Controllers\Api\TaxrateController;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -23,13 +24,13 @@ Route::group(['as' => 'api.'], function () {
|
||||
});
|
||||
|
||||
Route::apiResource('/customer', CustomerController::class);
|
||||
Route::apiResource('/customer.address', AddressController::class)->shallow()->except(['update']);
|
||||
Route::apiResource('/customer.address', AddressController::class)->shallow();
|
||||
Route::apiResource('/taxrate', TaxRateController::class)->except(['show']);
|
||||
Route::get('/invoice/{start?}/{end?}', [InvoiceController::class, 'index'])->name('invoice.index');
|
||||
Route::apiResource('/invoice.item', InvoiceItemController::class)->shallow();
|
||||
Route::get('/option', [OptionController::class, 'index'])->name('option.index');
|
||||
Route::post('/option', [OptionController::class, 'store'])->name('option.store');
|
||||
Route::post('/sendInvoice', [\App\Http\Controllers\Api\MailController::class, 'sendInvoice'])->name('sendInvoice');
|
||||
Route::post('/sendInvoice', [MailController::class, 'sendInvoice'])->name('sendInvoice');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\AddressController;
|
||||
use App\Http\Controllers\CustomerController;
|
||||
use App\Http\Controllers\EController;
|
||||
use App\Http\Controllers\InvoiceController;
|
||||
@@ -23,6 +24,7 @@ Route::middleware('auth')->group(function () {
|
||||
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
|
||||
|
||||
Route::resource('/customer', CustomerController::class)->only(['index', 'create', 'edit']);
|
||||
Route::get('/address/{id}/edit', [AddressController::class, 'edit'])->name('address.edit');
|
||||
Route::resource('/taxrate', TaxrateController::class)->only(['index', 'create', 'edit']);
|
||||
Route::resource('/invoice', InvoiceController::class)->only(['index', 'create', 'show', 'edit']);
|
||||
Route::get('/option', [OptionController::class, 'index'])->name('option.index');
|
||||
|
||||
Reference in New Issue
Block a user