Create the projects part.

This commit is contained in:
2025-01-22 16:37:24 +01:00
parent 0e712a3412
commit 83cea92630
13 changed files with 829 additions and 0 deletions

View File

@@ -19,6 +19,10 @@
: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('project.index')"
:active="\Illuminate\Support\Str::startsWith(request()->route()->getName(), 'project.')">
{{ __('project.Projects') }}
</x-nav-link>
<x-nav-link :href="route('invoice.index')"
:active="\Illuminate\Support\Str::startsWith(request()->route()->getName(), 'invoice.')">
{{ __('invoice.Invoices') }}

View File

@@ -0,0 +1,203 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('project.Create new project') }}
</h2>
</x-slot>
<div class="py-12" x-data="projectForm()">
<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" x-show="project.customer_id == 0">
<div class="max-w">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('project.Select customer') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("project.Select your customer") }}
</p>
</header>
<div class="my-8">
<x-input-label for="search" :value="__('common.Search')"/>
<x-text-input id="search" name="search" type="search" class="mt-1 block w-full"
x-ref="searchInput"
autofocus
placeholder="{{ __('invoice.Search customer') }}"
x-on:keydown.window.prevent.slash="$refs.searchInput.focus()"
x-model="search"/>
</div>
<div>
<template x-for="(customer, index) in getFilteredCustomer()">
<div class="cursor-pointer grid grid-cols-4 even:bg-gray-100 odd:bg-white"
:class="customer.id == project.customer_id ? 'font-bold' : ''"
x-on:click="project.customer_id = customer.id; selected_customer = customer">
<div x-text="customer.name"></div>
<div x-text="customer.email"></div>
<div x-text="(customer.address) ? customer.address.address : ''"></div>
<div x-text="(customer.address) ? customer.address.city : ''"></div>
</div>
</template>
</div>
</section>
</div>
</div>
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg" x-show="project.customer_id != 0">
<div class="max-w">
<div>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('customer.Customer') }}
</h2>
<div class="mt-4">
<div x-text="selected_customer.name"></div>
<div x-text="selected_customer.email"></div>
</div>
</div>
</div>
</div>
<!-- Project data -->
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg" x-show="project.customer_id != 0">
<div class="max-w">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('project.Project data') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("project.Enter your project data. Klick Save to store the project.") }}
</p>
</header>
</section>
<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="project.name"/>
</div>
<div>
<x-input-label for="project_number" :value="__('project.Project Number')"/>
<x-text-input id="project_number" name="project_number" type="text"
class="mt-1 block w-full"
:value="old('project_number')" required autofocus
autocomplete="project_number"
x-model="project.project_number"/>
</div>
<div>
<x-input-label for="description" :value="__('invoice.Description')"/>
<textarea placeholder="{{ __('invoice.Description') }}"
name="description" x-model="project.description"
x-text="project.description"
class="border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm mt-1 block w-full"></textarea>
</div>
<div>
<x-input-label for="start_date" :value="__('project.Start date')"/>
<x-text-input type="date" id="start_date" name="start_date" x-model="project.start_date"/>
</div>
<div>
<x-input-label for="end_date" :value="__('project.End date')"/>
<x-text-input type="date" id="end_date" name="end_date" x-model="project.end_date"/>
</div>
<div class="flex items-center gap-4">
<x-primary-button @click="submit">{{ __('form.Save') }}</x-primary-button>
</div>
</form>
</div>
</div>
</div>
</div>
</x-app-layout>
<script>
function projectForm() {
return {
selected_customer: {},
customers: [],
project: {
customer_id: 0,
name: '',
project_number: '',
description: '',
start_date: "{{ \Illuminate\Support\Facades\Date::now()->format('Y-m-d') }}",
end_date: "{{ \Illuminate\Support\Facades\Date::now()->format('Y-m-d') }}",
},
error: false,
message: '',
search: '',
init() {
this.getCustomers();
},
getCustomers() {
let vm = this;
axios.get('/customer')
.then(function (response) {
vm.customers = response.data;
})
.catch(function (error) {
vm.error = true;
vm.message = error.response.data.message;
})
},
getFilteredCustomer() {
if (this.search === '') {
return this.customers;
}
return this.customers.filter((customer) => {
return customer.name
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
||
customer.email
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
||
customer.street
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
||
customer.city
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
})
},
submit() {
axios.post('/project', this.project)
.then(function (response) {
window.location.href = '/project';
})
.catch(function (error) {
console.log(error);
})
},
}
}
</script>

View File

@@ -0,0 +1,197 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('project.Edit existing project') }}
</h2>
</x-slot>
<div class="py-12" x-data="projectForm()">
<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">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('project.Select customer') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("project.Select your customer") }}
</p>
</header>
<div class="my-8">
<x-input-label for="search" :value="__('common.Search')"/>
<x-text-input id="search" name="search" type="search" class="mt-1 block w-full"
x-ref="searchInput"
autofocus
placeholder="{{ __('invoice.Search customer') }}"
x-on:keydown.window.prevent.slash="$refs.searchInput.focus()"
x-model="search"/>
</div>
<div>
<template x-for="(customer, index) in getFilteredCustomer()">
<div class="cursor-pointer grid grid-cols-4 even:bg-gray-100 odd:bg-white"
:class="customer.id == project.customer_id ? 'font-bold' : ''"
x-on:click="project.customer_id = customer.id; selected_customer = customer">
<div x-text="customer.name"></div>
<div x-text="customer.email"></div>
<div x-text="(customer.address) ? customer.address.address : ''"></div>
<div x-text="(customer.address) ? customer.address.city : ''"></div>
</div>
</template>
</div>
</section>
</div>
</div>
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg" x-show="project.customer_id != 0">
<div class="max-w">
<div>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('customer.Customer') }}
</h2>
<div class="mt-4">
<div x-text="selected_customer.name"></div>
<div x-text="selected_customer.email"></div>
</div>
</div>
</div>
</div>
<!-- Project data -->
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="max-w">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('project.Project data') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("project.Enter your project data. Klick Save to store the project.") }}
</p>
</header>
</section>
<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="project.name"/>
</div>
<div>
<x-input-label for="project_number" :value="__('project.Project Number')"/>
<x-text-input id="project_number" name="project_number" type="text"
class="mt-1 block w-full"
:value="old('project_number')" required autofocus
autocomplete="project_number"
x-model="project.project_number"/>
</div>
<div>
<x-input-label for="description" :value="__('invoice.Description')"/>
<textarea placeholder="{{ __('invoice.Description') }}"
name="description" x-model="project.description"
x-text="project.description"
class="border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm mt-1 block w-full"></textarea>
</div>
<div>
<x-input-label for="start_date" :value="__('project.Start date')"/>
<x-text-input type="date" id="start_date" name="start_date" x-model="project.start_date"/>
</div>
<div>
<x-input-label for="end_date" :value="__('project.End date')"/>
<x-text-input type="date" id="end_date" name="end_date" x-model="project.end_date"/>
</div>
<div class="flex items-center gap-4">
<x-primary-button @click="submit">{{ __('form.Save') }}</x-primary-button>
</div>
</form>
</div>
</div>
</div>
</div>
</x-app-layout>
<script>
function projectForm() {
return {
selected_customer: {!! $project->customer !!},
customers: [],
project: {!! $project !!},
error: false,
message: '',
search: '',
init() {
this.getCustomers();
},
getCustomers() {
let vm = this;
axios.get('/customer')
.then(function (response) {
vm.customers = response.data;
})
.catch(function (error) {
vm.error = true;
vm.message = error.response.data.message;
})
},
getFilteredCustomer() {
if (this.search === '') {
return this.customers;
}
return this.customers.filter((customer) => {
return customer.name
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
||
customer.email
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
||
customer.street
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
||
customer.city
.replace(/ /g, '')
.toLowerCase()
.includes(this.search.replace(/ /g, '').toLowerCase())
})
},
submit() {
axios.put('/project/' + this.project.id, this.project)
.then(function (response) {
window.location.href = '/project';
})
.catch(function (error) {
console.log(error);
})
},
}
}
</script>

View File

@@ -0,0 +1,84 @@
<x-app-layout>
<x-slot name="header">
<div class="flex flex-row w-full">
<h2 class="grow font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('project.Projects') }}
</h2>
</div>
</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">
{{ __('project.Add new project') }}
</h2>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
{{ __("project.Add new project by clicking add") }}
</p>
</header>
<a class="mt-6 inline-block" href="{{ route('project.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="projectForm">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('project.Existing projects') }}
</h2>
</header>
<summary class="cursor-pointer flex flex-row w-full mt-4">
<div class="w-1/6 font-bold border-b-2">{{ __('project.Project Number') }}</div>
<div class="w-1/3 font-bold border-b-2">{{ __('customer.Customer') }}</div>
<div class="w-1/3 font-bold border-b-2">{{ __('common.Name') }}</div>
<div class="w-1/12 font-bold border-b-2 text-right">{{ __('project.Start date') }}</div>
<div class="w-1/12 font-bold border-b-2 text-right">{{ __('project.End date') }}</div>
</summary>
<template x-for="project in projects">
<details class="even:bg-gray-100 odd:bg-white">
<summary class="cursor-pointer flex flex-row w-full" @click="window.location.href='/project/' + project.id;">
<div class="w-1/6" x-text="project.project_number"></div>
<div class="w-1/3" x-text="project.customer.name"></div>
<div class="w-1/3" x-text="project.name"></div>
<div class="w-1/12 text-right" x-text="project.start"></div>
<div class="w-1/12 text-right" x-text="project.end"></div>
</summary>
</details>
</template>
</section>
</div>
</div>
</div>
</div>
</x-app-layout>
<script>
function projectForm() {
return {
projects: [],
init() {
this.getProjects();
},
getProjects() {
let vm = this;
axios.get('/project')
.then(function(response) {
vm.projects = response.data;
})
}
}
}
</script>

View File

@@ -0,0 +1,69 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('project.Project') }} {{ $project->name }} ({{ $project->project_number }})
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
<!-- Customer -->
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="max-w">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('customer.Customer') }}
</h2>
<div class="mt-4">
<div>{{ $project->customer->name }}</div>
<div>{{ $project->customer->email }}</div>
</div>
</header>
</section>
</div>
</div>
<!-- Project data -->
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="max-w">
<section>
<header>
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('project.Project data') }}
</h2>
<div class="mt-4">
<div class="grid grid-cols-[20%_80%] gap-2">
<x-input-label for="name" :value="__('common.Name')"/>
<div>{{ $project->name }}</div>
<x-input-label for="project_number" :value="__('project.Project Number')"/>
<div>{{ $project->project_number }}</div>
<x-input-label for="description" :value="__('invoice.Description')"/>
<div>{{ $project->description }}</div>
<x-input-label for="start_date" :value="__('project.Start date')"/>
<div>{{ $project->start }}</div>
<x-input-label for="end_date" :value="__('project.End date')"/>
<div>{{ $project->end }}</div>
</div>
</div>
</header>
</section>
</div>
</div>
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
<div class="max-w">
<div class="flex flex-row items-end gap-2 w-full">
<x-primary-button onclick="window.location.href = '{{ route('project.edit', $project->id) }}'"><x-edit-icon class="mr-4"/>{{ __('form.Edit') }}</x-primary-button>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>