From 282ef63cf2e8b8568b5156c647d7a18dca1f1593 Mon Sep 17 00:00:00 2001 From: PovilasKorop Date: Tue, 10 Sep 2024 11:26:04 +0300 Subject: [PATCH] Clients CRUD --- app/Enums/ProjectStatus.php | 12 +++ app/Http/Controllers/ClientController.php | 50 +++++++++ app/Http/Controllers/ProjectController.php | 58 ++++++++++ app/Http/Requests/StoreClientRequest.php | 36 +++++++ app/Http/Requests/StoreProjectRequest.php | 35 ++++++ app/Http/Requests/UpdateClientRequest.php | 36 +++++++ app/Http/Requests/UpdateProjectRequest.php | 35 ++++++ app/Models/Client.php | 23 ++++ app/Models/Project.php | 40 +++++++ database/factories/ClientFactory.php | 30 ++++++ database/factories/ProjectFactory.php | 34 ++++++ ...2024_09_09_122543_create_clients_table.php | 36 +++++++ ...024_09_10_061234_create_projects_table.php | 34 ++++++ database/seeders/ClientSeeder.php | 18 ++++ database/seeders/DatabaseSeeder.php | 17 ++- database/seeders/ProjectSeeder.php | 18 ++++ database/seeders/UserSeeder.php | 25 +++++ resources/views/clients/create.blade.php | 100 ++++++++++++++++++ resources/views/clients/edit.blade.php | 92 ++++++++++++++++ resources/views/clients/index.blade.php | 67 ++++++++++++ resources/views/layouts/navigation.blade.php | 12 +++ resources/views/projects/create.blade.php | 81 ++++++++++++++ resources/views/projects/edit.blade.php | 82 ++++++++++++++ resources/views/projects/index.blade.php | 79 ++++++++++++++ routes/web.php | 4 + 25 files changed, 1043 insertions(+), 11 deletions(-) create mode 100644 app/Enums/ProjectStatus.php create mode 100644 app/Http/Controllers/ClientController.php create mode 100644 app/Http/Controllers/ProjectController.php create mode 100644 app/Http/Requests/StoreClientRequest.php create mode 100644 app/Http/Requests/StoreProjectRequest.php create mode 100644 app/Http/Requests/UpdateClientRequest.php create mode 100644 app/Http/Requests/UpdateProjectRequest.php create mode 100644 app/Models/Client.php create mode 100644 app/Models/Project.php create mode 100644 database/factories/ClientFactory.php create mode 100644 database/factories/ProjectFactory.php create mode 100644 database/migrations/2024_09_09_122543_create_clients_table.php create mode 100644 database/migrations/2024_09_10_061234_create_projects_table.php create mode 100644 database/seeders/ClientSeeder.php create mode 100644 database/seeders/ProjectSeeder.php create mode 100644 database/seeders/UserSeeder.php create mode 100644 resources/views/clients/create.blade.php create mode 100644 resources/views/clients/edit.blade.php create mode 100644 resources/views/clients/index.blade.php create mode 100644 resources/views/projects/create.blade.php create mode 100644 resources/views/projects/edit.blade.php create mode 100644 resources/views/projects/index.blade.php diff --git a/app/Enums/ProjectStatus.php b/app/Enums/ProjectStatus.php new file mode 100644 index 0000000..e08efdc --- /dev/null +++ b/app/Enums/ProjectStatus.php @@ -0,0 +1,12 @@ +validated()); + + return redirect()->route('clients.index'); + } + + public function edit(Client $client): View + { + return view('clients.edit', compact('client')); + } + + public function update(UpdateClientRequest $request, Client $client): RedirectResponse + { + $client->update($request->validated()); + + return redirect()->route('clients.index'); + } + + public function destroy(Client $client): RedirectResponse + { + $client->delete(); + + return redirect()->route('clients.index'); + } +} diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php new file mode 100644 index 0000000..84b1e0d --- /dev/null +++ b/app/Http/Controllers/ProjectController.php @@ -0,0 +1,58 @@ +paginate(10); + + return view('projects.index', compact('projects')); + } + + public function create(): View + { + $users = User::select(['id', 'first_name', 'last_name'])->get(); + $clients = Client::select(['id', 'company_name'])->get(); + + return view('projects.create', compact('users', 'clients')); + } + + public function store(StoreProjectRequest $request): RedirectResponse + { + Project::create($request->validated()); + + return redirect()->route('projects.index'); + } + + public function edit(Project $project): View + { + $users = User::select(['id', 'first_name', 'last_name'])->get(); + $clients = Client::select(['id', 'company_name'])->get(); + + return view('projects.edit', compact('project', 'users', 'clients')); + } + + public function update(UpdateProjectRequest $request, Project $project): RedirectResponse + { + $project->update($request->validated()); + + return redirect()->route('projects.index'); + } + + public function destroy(Project $project): RedirectResponse + { + $project->delete(); + + return redirect()->route('projects.index'); + } +} diff --git a/app/Http/Requests/StoreClientRequest.php b/app/Http/Requests/StoreClientRequest.php new file mode 100644 index 0000000..48bdadf --- /dev/null +++ b/app/Http/Requests/StoreClientRequest.php @@ -0,0 +1,36 @@ +|string> + */ + public function rules(): array + { + return [ + 'contact_name' => ['required', 'string', 'max:255'], + 'contact_email' => ['required', 'string', 'email', 'max:255', Rule::unique('clients')], + 'contact_phone_number' => ['required'], + 'company_name' => ['required'], + 'company_address' => ['required'], + 'company_city' => ['required', 'string'], + 'company_zip' => ['required', 'integer'], + 'company_vat' => ['required', 'numeric'], + ]; + } +} diff --git a/app/Http/Requests/StoreProjectRequest.php b/app/Http/Requests/StoreProjectRequest.php new file mode 100644 index 0000000..e67f9ca --- /dev/null +++ b/app/Http/Requests/StoreProjectRequest.php @@ -0,0 +1,35 @@ +|string> + */ + public function rules(): array + { + return [ + 'title' => ['required'], + 'description' => ['required'], + 'user_id' => ['required', Rule::exists('users', 'id')], + 'client_id' => ['required', Rule::exists('clients', 'id')], + 'deadline_at' => ['required', 'date'], + 'status' => ['required', Rule::enum(ProjectStatus::class)], + ]; + } +} diff --git a/app/Http/Requests/UpdateClientRequest.php b/app/Http/Requests/UpdateClientRequest.php new file mode 100644 index 0000000..73d2d0b --- /dev/null +++ b/app/Http/Requests/UpdateClientRequest.php @@ -0,0 +1,36 @@ +|string> + */ + public function rules(): array + { + return [ + 'contact_name' => ['required', 'string', 'max:255'], + 'contact_email' => ['required', 'string', 'email', 'max:255', Rule::unique('clients')->ignore($this->client)], + 'contact_phone_number' => ['required'], + 'company_name' => ['required'], + 'company_address' => ['required'], + 'company_city' => ['required', 'string'], + 'company_zip' => ['required', 'integer'], + 'company_vat' => ['required', 'numeric'], + ]; + } +} diff --git a/app/Http/Requests/UpdateProjectRequest.php b/app/Http/Requests/UpdateProjectRequest.php new file mode 100644 index 0000000..c078170 --- /dev/null +++ b/app/Http/Requests/UpdateProjectRequest.php @@ -0,0 +1,35 @@ +|string> + */ + public function rules(): array + { + return [ + 'title' => ['required'], + 'description' => ['required'], + 'user_id' => ['required', Rule::exists('users', 'id')], + 'client_id' => ['required', Rule::exists('clients', 'id')], + 'deadline_at' => ['required', 'date'], + 'status' => ['required', Rule::enum(ProjectStatus::class)], + ]; + } +} diff --git a/app/Models/Client.php b/app/Models/Client.php new file mode 100644 index 0000000..bbbc97a --- /dev/null +++ b/app/Models/Client.php @@ -0,0 +1,23 @@ +belongsTo(User::class); + } + + public function client(): BelongsTo + { + return $this->belongsTo(Client::class); + } + + protected function casts(): array + { + return [ + 'status' => ProjectStatus::class, + ]; + } +} diff --git a/database/factories/ClientFactory.php b/database/factories/ClientFactory.php new file mode 100644 index 0000000..e831813 --- /dev/null +++ b/database/factories/ClientFactory.php @@ -0,0 +1,30 @@ + + */ +class ClientFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'contact_name' => fake()->name(), + 'contact_email' => fake()->unique()->safeEmail(), + 'contact_phone_number' => fake()->phoneNumber(), + 'company_name' => fake()->company(), + 'company_address' => fake()->address(), + 'company_city' => fake()->city(), + 'company_zip' => fake()->postcode(), + 'company_vat' => fake()->unique()->numerify(), + ]; + } +} diff --git a/database/factories/ProjectFactory.php b/database/factories/ProjectFactory.php new file mode 100644 index 0000000..f07bbfb --- /dev/null +++ b/database/factories/ProjectFactory.php @@ -0,0 +1,34 @@ + + */ +class ProjectFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + $users = User::pluck('id'); + $clients = Client::pluck('id'); + + return [ + 'title' => fake()->sentence(3), + 'description' => fake()->paragraph(), + 'deadline_at' => now()->addDays(rand(1, 30))->format('Y-m-d'), + 'status' => fake()->randomElement(ProjectStatus::cases())->value, + 'user_id' => $users->random(), + 'client_id' => $clients->random(), + ]; + } +} diff --git a/database/migrations/2024_09_09_122543_create_clients_table.php b/database/migrations/2024_09_09_122543_create_clients_table.php new file mode 100644 index 0000000..d68dd36 --- /dev/null +++ b/database/migrations/2024_09_09_122543_create_clients_table.php @@ -0,0 +1,36 @@ +id(); + $table->string('contact_name'); + $table->string('contact_email')->unique(); + $table->string('contact_phone_number'); + $table->string('company_name'); + $table->string('company_address'); + $table->string('company_city'); + $table->string('company_zip'); + $table->integer('company_vat'); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('clients'); + } +}; diff --git a/database/migrations/2024_09_10_061234_create_projects_table.php b/database/migrations/2024_09_10_061234_create_projects_table.php new file mode 100644 index 0000000..95d3193 --- /dev/null +++ b/database/migrations/2024_09_10_061234_create_projects_table.php @@ -0,0 +1,34 @@ +id(); + $table->string('title'); + $table->text('description'); + $table->foreignId('user_id')->constrained(); + $table->foreignId('client_id')->constrained(); + $table->date('deadline_at'); + $table->string('status'); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('projects'); + } +}; diff --git a/database/seeders/ClientSeeder.php b/database/seeders/ClientSeeder.php new file mode 100644 index 0000000..ab7717d --- /dev/null +++ b/database/seeders/ClientSeeder.php @@ -0,0 +1,18 @@ +create(); + } +} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index bfee64a..4a67273 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -2,9 +2,7 @@ namespace Database\Seeders; -use App\Models\User; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; -use App\RoleEnum; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder @@ -14,14 +12,11 @@ class DatabaseSeeder extends Seeder */ public function run(): void { - $this->call(RoleSeeder::class); - - User::factory(10)->create(); - User::factory()->create([ - 'first_name' => 'Admin', - 'last_name' => 'Admin', - 'email' => 'admin@admin.com', - 'password' => 'secret', - ])->syncRoles([RoleEnum::ADMIN]); + $this->call([ + RoleSeeder::class, + UserSeeder::class, + ClientSeeder::class, + ProjectSeeder::class, + ]); } } diff --git a/database/seeders/ProjectSeeder.php b/database/seeders/ProjectSeeder.php new file mode 100644 index 0000000..46422ff --- /dev/null +++ b/database/seeders/ProjectSeeder.php @@ -0,0 +1,18 @@ +create(); + } +} diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php new file mode 100644 index 0000000..d56e63c --- /dev/null +++ b/database/seeders/UserSeeder.php @@ -0,0 +1,25 @@ +create(); + User::factory()->create([ + 'first_name' => 'Admin', + 'last_name' => 'Admin', + 'email' => 'admin@admin.com', + 'password' => 'secret', + ])->syncRoles([RoleEnum::ADMIN]); + } +} diff --git a/resources/views/clients/create.blade.php b/resources/views/clients/create.blade.php new file mode 100644 index 0000000..c1e139f --- /dev/null +++ b/resources/views/clients/create.blade.php @@ -0,0 +1,100 @@ + + +

+ {{ __('Create Client') }} +

+
+ +
+
+
+
+ +
+ @csrf + +
+
+

Contact information

+ +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+
+ +
+

Company information

+ +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+
+ +
+ + {{ __('Save') }} + +
+
+
+ +
+
+
+
+
diff --git a/resources/views/clients/edit.blade.php b/resources/views/clients/edit.blade.php new file mode 100644 index 0000000..dc5613a --- /dev/null +++ b/resources/views/clients/edit.blade.php @@ -0,0 +1,92 @@ + + +

+ {{ __('Edit Client') }} +

+
+ +
+
+
+
+ +
+ @method('PUT') + @csrf + +
+
+

Contact information

+ +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+
+ +
+

Company information

+ +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+
+ +
+ + {{ __('Save') }} + +
+
+
+ +
+
+
+
+
diff --git a/resources/views/clients/index.blade.php b/resources/views/clients/index.blade.php new file mode 100644 index 0000000..cdda2e0 --- /dev/null +++ b/resources/views/clients/index.blade.php @@ -0,0 +1,67 @@ + + +

+ {{ __('Clients') }} +

+
+ +
+
+
+
+ Add new client + + + + + + + + + + + + + @foreach($clients as $client) + + + + + + + @endforeach + +
+ Company + + VAT + + Address + +
+ {{ $client->company_name }} + + {{ $client->company_vat }} + + {{ $client->company_address }} + + Edit + | +
+ @method('DELETE') + @csrf + +
+
+ +
+ {{ $clients->links() }} +
+
+
+
+
+
diff --git a/resources/views/layouts/navigation.blade.php b/resources/views/layouts/navigation.blade.php index 0c50250..0d1d8d6 100644 --- a/resources/views/layouts/navigation.blade.php +++ b/resources/views/layouts/navigation.blade.php @@ -20,6 +20,12 @@ {{ __('Users') }} @endrole + + {{ __('Clients') }} + + + {{ __('Projects') }} + @@ -80,6 +86,12 @@ {{ __('Users') }} @endrole + + {{ __('Clients') }} + + + {{ __('Projects') }} + diff --git a/resources/views/projects/create.blade.php b/resources/views/projects/create.blade.php new file mode 100644 index 0000000..0004738 --- /dev/null +++ b/resources/views/projects/create.blade.php @@ -0,0 +1,81 @@ + + +

+ {{ __('Create Project') }} +

+
+ +
+
+
+
+ +
+ @csrf + + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + + {{ __('Save') }} + +
+ +
+
+
+
+
diff --git a/resources/views/projects/edit.blade.php b/resources/views/projects/edit.blade.php new file mode 100644 index 0000000..765f666 --- /dev/null +++ b/resources/views/projects/edit.blade.php @@ -0,0 +1,82 @@ + + +

+ {{ __('Edit Project') }} +

+
+ +
+
+
+
+ +
+ @csrf + @method('PUT') + + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + + {{ __('Save') }} + +
+ +
+
+
+
+
diff --git a/resources/views/projects/index.blade.php b/resources/views/projects/index.blade.php new file mode 100644 index 0000000..0f86142 --- /dev/null +++ b/resources/views/projects/index.blade.php @@ -0,0 +1,79 @@ + + +

+ {{ __('Projects') }} +

+
+ +
+
+
+
+ Add new project + + + + + + + + + + + + + + + @foreach($projects as $project) + + + + + + + + + @endforeach + +
+ Title + + Assigned To + + Client + + Deadline + + Status + +
+ {{ $project->title }} + + {{ $project->user->first_name }} {{ $project->user->last_name }} + + {{ $project->client->company_name }} + + {{ $project->deadline_at }} + + {{ $project->status }} + + Edit + | +
+ @method('DELETE') + @csrf + +
+
+ +
+ {{ $projects->links() }} +
+
+
+
+
+
diff --git a/routes/web.php b/routes/web.php index 40277a1..8d0e88f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,8 @@ group(function () { Route::resource('users', UserController::class) ->middleware(['role:' . RoleEnum::ADMIN->value]); + Route::resource('clients', ClientController::class); + Route::resource('projects', ProjectController::class); Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');