Create the projects part.
This commit is contained in:
66
app/Http/Controllers/Api/ProjectController.php
Normal file
66
app/Http/Controllers/Api/ProjectController.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Project;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class ProjectController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
return response()->json(Project::with(['customer'])->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
$projectData = $request->validate([
|
||||
'customer_id' => 'required|exists:customers,id',
|
||||
'name' => 'required|string',
|
||||
'project_number' => 'required|string',
|
||||
'description' => 'nullable|string',
|
||||
'start_date' => 'required|date',
|
||||
'end_date' => 'required|date',
|
||||
]);
|
||||
|
||||
$project = new Project($projectData);
|
||||
$project->save();
|
||||
|
||||
return response()->json($project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Project $project): JsonResponse
|
||||
{
|
||||
$projectData = $request->validate([
|
||||
'customer_id' => 'required|exists:customers,id',
|
||||
'name' => 'required|string',
|
||||
'project_number' => 'required|string',
|
||||
'description' => 'nullable|string',
|
||||
'start_date' => 'required|date',
|
||||
'end_date' => 'required|date',
|
||||
]);
|
||||
|
||||
$project->update($projectData);
|
||||
|
||||
return response()->json($project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Project $project)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
41
app/Http/Controllers/ProjectController.php
Normal file
41
app/Http/Controllers/ProjectController.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Project;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
class ProjectController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(): View
|
||||
{
|
||||
return view('projects.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
return view('projects.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Project $project): View
|
||||
{
|
||||
return view('projects.show', ['project' => $project]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Project $project): View
|
||||
{
|
||||
return view('projects.edit', ['project' => $project]);
|
||||
}
|
||||
}
|
||||
61
app/Models/Project.php
Normal file
61
app/Models/Project.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Project extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'customer_id',
|
||||
'name',
|
||||
'project_number',
|
||||
'description',
|
||||
'start_date',
|
||||
'end_date',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are appended with attribute getters.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $appends = [
|
||||
'start',
|
||||
'end',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the end_date attribute in local format.
|
||||
*/
|
||||
public function getStartAttribute(): string
|
||||
{
|
||||
return (is_null($this->start_date)) ? '' : Carbon::createFromFormat('Y-m-d', $this->start_date)->format('d.m.Y');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the end_date attribute in local format.
|
||||
*/
|
||||
public function getEndAttribute(): string
|
||||
{
|
||||
return (is_null($this->end_date)) ? '' : Carbon::createFromFormat('Y-m-d', $this->end_date)->format('d.m.Y');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the customer this project belongs to.
|
||||
*/
|
||||
public function customer(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Customer::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('projects', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('customer_id')->constrained();
|
||||
|
||||
$table->string('name');
|
||||
$table->string('project_number');
|
||||
$table->text('description')->nullable();
|
||||
$table->date('start_date')->nullable();
|
||||
$table->date('end_date')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('projects');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('invoices', function (Blueprint $table) {
|
||||
$table->foreignId('project_id')->nullable()->constrained();
|
||||
$table->integer('project_count')->nullable()->default(0);
|
||||
$table->boolean('is_template')->default(false);
|
||||
$table->string('currency_code')->default('EUR');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('invoices', function (Blueprint $table) {
|
||||
$table->dropColumn('project_id');
|
||||
$table->dropColumn('project_count');
|
||||
$table->dropColumn('is_template');
|
||||
$table->dropColumn('currency_code');
|
||||
});
|
||||
}
|
||||
};
|
||||
30
lang/de/project.php
Normal file
30
lang/de/project.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|------------------------------------------------- -------------------------
|
||||
| Übersetzungen von Projekten
|
||||
|------------------------------------------------- -------------------------
|
||||
|
|
||||
| Die folgenden Sprachzeilen werden für die Lokalisierung von Projekten
|
||||
| genutzt.
|
||||
|
|
||||
*/
|
||||
|
||||
'Project' => 'Projekt',
|
||||
'Projects' => 'Projekte',
|
||||
'Add new project' => 'Neues Projekt erstellen',
|
||||
'Add new project by clicking add' => 'Durch Klick auf "Anlegen" neue Rechnung erstellen',
|
||||
'Existing projects' => 'Bestehende Projekte',
|
||||
'Project Number' => 'Projektnummer',
|
||||
'Start date' => 'Projektstart',
|
||||
'End date' => 'Projektende',
|
||||
'Create new project' => 'Neues Projekt erstellen',
|
||||
'Select customer' => 'Kunde auswählen',
|
||||
'Select your customer' => 'Den Kunden des Projekts auswählen',
|
||||
'Project data' => 'Projektdaten',
|
||||
'Enter your project data. Klick Save to store the project.' => 'Gib die Daten des Projekts ein. Klicke auf "Speichern" um das Projekt zu erstellen.',
|
||||
'Edit existing project' => 'Projekt bearbeitet',
|
||||
|
||||
];
|
||||
@@ -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') }}
|
||||
|
||||
203
resources/views/projects/create.blade.php
Normal file
203
resources/views/projects/create.blade.php
Normal 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>
|
||||
197
resources/views/projects/edit.blade.php
Normal file
197
resources/views/projects/edit.blade.php
Normal 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>
|
||||
84
resources/views/projects/index.blade.php
Normal file
84
resources/views/projects/index.blade.php
Normal 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>
|
||||
69
resources/views/projects/show.blade.php
Normal file
69
resources/views/projects/show.blade.php
Normal 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>
|
||||
@@ -10,6 +10,7 @@ use App\Http\Controllers\Api\InvoiceitemController;
|
||||
use App\Http\Controllers\Api\MailController;
|
||||
use App\Http\Controllers\Api\OptionController;
|
||||
use App\Http\Controllers\Api\PaymentController;
|
||||
use App\Http\Controllers\Api\ProjectController;
|
||||
use App\Http\Controllers\Api\TaxrateController;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
@@ -45,6 +46,7 @@ Route::group(['as' => 'api.'], function () {
|
||||
Route::get('/incoming-filter/{start}/{end}', [IncomingController::class, 'index'])->name('incoming.index');
|
||||
Route::put('/incoming/{incoming}', [IncomingController::class, 'update'])->name('incoming.update');
|
||||
Route::post('/incoming', [IncomingController::class, 'store'])->name('incoming.store');
|
||||
Route::apiResource('/project', ProjectController::class);
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Http\Controllers\OptionController;
|
||||
use App\Http\Controllers\PaymentController;
|
||||
use App\Http\Controllers\PdfController;
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use App\Http\Controllers\ProjectController;
|
||||
use App\Http\Controllers\TaxrateController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
@@ -37,6 +38,7 @@ Route::middleware('auth')->group(function () {
|
||||
Route::get('/excel', function() { return view('excel'); })->name('excel');
|
||||
Route::resource('/incoming', IncomingController::class)->only(['index', 'create', 'edit']);
|
||||
Route::get('/incoming-upload', [IncomingController::class, 'upload'])->name('incoming.upload');
|
||||
Route::resource('/project', ProjectController::class)->only(['index', 'create', 'show', 'edit']);
|
||||
});
|
||||
|
||||
require __DIR__.'/auth.php';
|
||||
|
||||
Reference in New Issue
Block a user