Create routes and views for customers and their addresses.

This commit is contained in:
2024-12-10 21:09:12 +01:00
parent 9d2eddbcf1
commit 679a067506
16 changed files with 797 additions and 3 deletions

View File

@@ -0,0 +1,16 @@
<div x-show="address.is_address && address.is_delivery"
class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('customer.Invoice and Delivery Address') }}</div>
<div x-show="address.is_address && !address.is_delivery"
class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('customer.Invoice Address') }}</div>
<div x-show="address.is_delivery && !address.is_address"
class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('customer.Delivery Address') }}</div>
<div x-show="!address.is_delivery && !address.is_address"
class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('customer.Other Address') }}</div>
<div x-text="address.name"></div>
<div x-text="address.address"></div>
<div class="flex flex-row">
<div class="mr-2" x-text="address.zip"></div>
<div x-text="address.city"></div>
</div>
<div x-text="address.phone"></div>
<div x-text="address.email"></div>

View File

@@ -0,0 +1,82 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('customer.Create new customer') }}
</h2>
</x-slot>
<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">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('customer.New customer') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("customer.Enter your customer's information and email 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>
<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')" required autofocus autocomplete="name"
x-model="customer.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')" required autocomplete="username"
x-model="customer.email"/>
</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 customerForm() {
return {
customer: {
name: '',
email: '',
},
error: false,
message: '',
submit() {
let vm = this;
axios.post('/customer', this.customer)
.then(function (response) {
window.location.href = '/customer/' + response.data.id + '/edit';
})
.catch(function (error) {
vm.error = true;
vm.message = error.response.data.message;
})
}
}
}
</script>

View File

@@ -0,0 +1,286 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('customer.Create new customer') }}
</h2>
</x-slot>
<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">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('customer.New customer') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("customer.Enter your customer's information and email address.") }}
</p>
</header>
<form class="mt-6 space-y-6" @submit.prevent="" x-bind="customer">
<p class="text-red-600 font-bold" x-text="customer_message" x-show="customer_error"></p>
<p x-show="customer_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')" required autofocus autocomplete="name"
x-model="customer.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')" required autocomplete="username"
x-model="customer.email"/>
</div>
<div class="flex items-center gap-4">
<x-primary-button @click="updateCustomer">{{ __('form.Save') }}</x-primary-button>
</div>
</form>
</section>
</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">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('customer.Existing addresses') }}
</h2>
</header>
<div class="grid grid-cols-3">
<template x-for="address in addresses" :key="address.id">
<div>
<div class="flex flex-col p-8 pb-0">
<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>
</template>
</div>
</section>
</div>
</div>
<!-- Address data -->
<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.New address') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("customer.Enter your customer's address.") }}
</p>
</header>
<form class="mt-6 space-y-6" @submit.prevent="">
<p class="text-red-600 font-bold" x-text="address_message" x-show="address_error"></p>
<p x-show="address_success" x-transition
class="text-sm text-green-600 dark:text-green-400">{{ __('form.Saved') }}</p>
<div>
<x-input-label for="address[name]" :value="__('common.Name')"/>
<x-text-input id="address[name]" name="address[name]" type="text"
class="mt-1 block w-full" :value="old('name')" required autofocus
autocomplete="name" x-model="address.name"/>
</div>
<div>
<x-input-label for="address[email]" :value="__('common.Email')"/>
<x-text-input id="address[email]" name="address[email]" type="email"
class="mt-1 block w-full" :value="old('email')" required
autocomplete="username" x-model="address.email"/>
</div>
<div>
<x-input-label for="address[phone]" :value="__('common.Phone')"/>
<x-text-input id="address[phone]" name="address[phone]" type="text"
class="mt-1 block w-full" :value="old('phone')" autocomplete="phone"
x-model="address.phone"/>
</div>
<div>
<x-input-label for="address[address]" :value="__('common.Address')"/>
<x-text-input id="address[address]" name="address[address]" type="text"
class="mt-1 block w-full" :value="old('address')" autocomplete="address"
x-model="address.address"/>
</div>
<div class="flex flex-row space-x-8">
<div class="w-1/4">
<x-input-label for="address[zip]" :value="__('common.Zip Code')"/>
<x-text-input id="address[zip]" name="address[zip]" type="text"
class="mt-1 block w-full" :value="old('zip')" autocomplete="zip"
x-model="address.zip"/>
</div>
<div class="w-3/4">
<x-input-label for="address[city]" :value="__('common.City')"/>
<x-text-input id="address[city]" name="address[city]" type="text"
class="mt-1 block w-full" :value="old('city')" autocomplete="city"
x-model="address.city"/>
</div>
</div>
<div class="flex flex-row space-x-8">
<x-input-label class="w-1/4" for="address[is_address]" :value="__('customer.Invoice Address')"/>
<input type="hidden" name="address[is_address]" value="0" />
<x-text-input id="address[is_address]" name="address[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="address[is_delivery]" :value="__('customer.Delivery Address')"/>
<input type="hidden" name="address[is_delivery]" value="0" />
<x-text-input id="address[is_delivery]" name="address[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="createAddress">{{ __('form.Save') }}</x-primary-button>
</div>
</form>
</section>
</div>
</div>
<!-- Modal for address deletion -->
<x-modal name="confirm-user-deletion" :show="$errors->userDeletion->isNotEmpty()" focusable>
<form class="p-6" @submit.prevent="">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('customer.Are you sure you want to delete the address?') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __('customer.Once the address is deleted, all the ressources and data will be permanently deleted.') }}
</p>
<div class="mt-6 flex justify-end">
<x-secondary-button x-on:click="$dispatch('close')">
{{ __('form.Cancel') }}
</x-secondary-button>
<x-danger-button class="ms-3" x-on:click="deleteAddress();$dispatch('close')">
{{ __('customer.Delete Address') }}
</x-danger-button>
</div>
</form>
</x-modal>
</div>
</div>
</x-app-layout>
<script>
function customerForm() {
return {
customer: {},
customer_id: {{ $customer->id }},
addresses: {},
address: {},
delete_id: 0,
customer_error: false,
customer_message: '',
customer_success: false,
address_error: false,
address_message: '',
address_success: false,
init() {
this.getCustomer();
this.getAddresses();
},
updateCustomer() {
let vm = this;
axios.put('/customer/' + this.customer_id, this.customer)
.then(function(response) {
vm.customer_success = true;
}).catch(function (error) {
vm.customer_error = true;
vm.customer_message = error.response.data.message;
})
},
getCustomer() {
let vm = this;
axios.get('/customer/' + this.customer_id)
.then(function (response) {
vm.customer = response.data;
})
.catch(function (error) {
vm.customer_error = true;
vm.customer_message = error.response.data.message;
})
},
getAddresses() {
let vm = this;
axios.get('/customer/' + this.customer_id + '/address')
.then(function (response) {
vm.addresses = response.data;
})
.catch(function (error) {
vm.address_error = true;
vm.address_message = error.response.data.message;
})
},
createAddress() {
let vm = this;
axios.post('/customer/' + this.customer.id + '/address', this.address)
.then(function (response) {
vm.addresses.push(response.data);
vm.address_success = true;
vm.getAddresses();
})
.catch(function (error) {
vm.address_error = true;
vm.address_message = error.response.data.message;
})
},
deleteAddress() {
let vm = this;
axios.delete('/address/' + this.delete_id)
.then(function (response) {
let removeIndex = vm.addresses.map(item => item.id).indexOf(vm.delete_id);
vm.addresses.splice(removeIndex, 1);
})
},
}
}
</script>

View File

@@ -0,0 +1,102 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('customer.Customers') }}
</h2>
</x-slot>
<div class="py-12">
<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">
<div class="max-w-xl">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('customer.Add new customer') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("customer.Add new customer by clicking add") }}
</p>
</header>
<a class="mt-6 inline-block" href="{{ route('customer.create') }}"><x-primary-button>{{ __('form.Add') }}</x-primary-button></a>
</section>
</div>
</div>
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="max-w" x-data="{ customers: []}"
x-init="customers = await fetchCustomers().then((data) => data );">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('customer.Existing customers') }}
</h2>
</header>
<summary class="cursor-pointer grid grid-cols-3 mt-4">
<div class="font-bold border-b-2">Name</div>
<div class="font-bold border-b-2">Email</div>
<div class="font-bold border-b-2">Erstellt am</div>
</summary>
<template x-for="customer in customers">
<details class=" even:bg-gray-100 odd:bg-white">
<summary class="cursor-pointer grid grid-cols-3">
<div x-text="customer.name"></div>
<div x-text="customer.email"></div>
<div x-text="customer.created"></div>
</summary>
<div class="flex flex-row">
<template x-if="customer.address">
<div class="flex flex-col p-8 w-1/3">
<div class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('customer.Invoice Address') }}</div>
<div x-text="customer.address.name"></div>
<div x-text="customer.address.address"></div>
<div x-text="customer.address.zip +' ' + customer.address.city"></div>
<div x-text="customer.address.phone"></div>
<div x-text="customer.address.email"></div>
</div>
</template>
<template x-if="customer.delivery">
<div class="flex flex-col p-8 w-1/3">
<div class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ __('customer.Delivery Address') }}</div>
<div x-text="customer.delivery.name"></div>
<div x-text="customer.delivery.address"></div>
<div x-text="customer.delivery.zip +' ' + customer.delivery.city"></div>
<div x-text="customer.delivery.phone"></div>
<div x-text="customer.delivery.email"></div>
</div>
</template>
</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>
</div>
</details>
</template>
</section>
</div>
</div>
</div>
</div>
</x-app-layout>
<script>
let fetchCustomers = async () => {
return await new Promise((resolve, reject) => {
axios.get('/customer')
.then(function (response) {
resolve(response.data);
})
.catch(function (error) {
console.log(error);
})
});
}
</script>