From c99301797c73ca6db106fdb818d07a83b1a2911f Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Fri, 5 Mar 2021 22:41:50 +0800 Subject: [PATCH 01/16] Add user activites on a project update --- app/Entities/Projects/Project.php | 8 +++ app/Entities/Users/Activity.php | 12 +++++ app/Events/Projects/Updated.php | 20 +++++++ .../Projects/LogProjectUpdateActivity.php | 61 ++++++++++++++++++++++ app/Providers/EventServiceProvider.php | 4 +- ...1_03_05_221708_create_user_activities_table.php | 40 ++++++++++++++ tests/Unit/Models/ActivityTest.php | 39 ++++++++++++++ 7 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 app/Entities/Users/Activity.php create mode 100644 app/Events/Projects/Updated.php create mode 100644 app/Listeners/Projects/LogProjectUpdateActivity.php create mode 100644 database/migrations/2021_03_05_221708_create_user_activities_table.php create mode 100644 tests/Unit/Models/ActivityTest.php diff --git a/app/Entities/Projects/Project.php b/app/Entities/Projects/Project.php index 9834a57..fee1dd3 100755 --- a/app/Entities/Projects/Project.php +++ b/app/Entities/Projects/Project.php @@ -18,6 +18,14 @@ use Laracasts\Presenter\PresentableTrait; class Project extends Model { use PresentableTrait; + /** + * The event map for the model. + * + * @var array + */ + protected $dispatchesEvents = [ + 'updated' => 'App\Events\Projects\Updated', + ]; /** * @var \App\Entities\Projects\ProjectPresenter diff --git a/app/Entities/Users/Activity.php b/app/Entities/Users/Activity.php new file mode 100644 index 0000000..15b92db --- /dev/null +++ b/app/Entities/Users/Activity.php @@ -0,0 +1,12 @@ +project = $project; + } +} diff --git a/app/Listeners/Projects/LogProjectUpdateActivity.php b/app/Listeners/Projects/LogProjectUpdateActivity.php new file mode 100644 index 0000000..6c5e681 --- /dev/null +++ b/app/Listeners/Projects/LogProjectUpdateActivity.php @@ -0,0 +1,61 @@ +project->fresh()->toArray(), $event->project->getOriginal(), $event->project->getDirty()); + $project = $event->project; + // dd($project->getOriginal(), $project->getChanges()); + $originalProject = $project->getOriginal(); + $attributeChanges = $project->getChanges(); + $attributeKeys = array_keys($project->getChanges()); + // dd($attributeChanges, $attributeKeys); + + $activityEntry = [ + 'type' => 'project_updated', + 'parent_id' => null, + 'user_id' => auth()->id(), + 'object_id' => $project->id, + 'object_type' => 'projects', + 'data' => json_encode([ + 'before' => $this->getBeforeValues($originalProject, $attributeKeys), + 'after' => $this->getAfterValues($project->toArray(), $attributeKeys), + 'notes' => null, + ]), + ]; + + Activity::create($activityEntry); + } + + private function getBeforeValues(array $originalProject, array $attributeKeys) + { + $beforeValues = []; + foreach ($attributeKeys as $attributeKey) { + $beforeValues[$attributeKey] = $originalProject[$attributeKey]; + } + + return $beforeValues; + } + + private function getAfterValues(array $project, array $attributeKeys) + { + $afterValues = []; + foreach ($attributeKeys as $attributeKey) { + $afterValues[$attributeKey] = $project[$attributeKey]; + } + + return $afterValues; + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 907cff8..71878ea 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -13,8 +13,8 @@ class EventServiceProvider extends ServiceProvider * @var array */ protected $listen = [ - 'App\Events\SomeEvent' => [ - 'App\Listeners\EventListener', + 'App\Events\Projects\Updated' => [ + 'App\Listeners\Projects\LogProjectUpdateActivity', ], ]; diff --git a/database/migrations/2021_03_05_221708_create_user_activities_table.php b/database/migrations/2021_03_05_221708_create_user_activities_table.php new file mode 100644 index 0000000..a7750aa --- /dev/null +++ b/database/migrations/2021_03_05_221708_create_user_activities_table.php @@ -0,0 +1,40 @@ +increments('id'); + $table->string('type'); + $table->unsignedInteger('parent_id')->nullable()->comment('Parent Activity ID'); + $table->unsignedInteger('user_id')->nullable(); + $table->unsignedInteger('object_id'); + $table->string('object_type', 60); + $table->text('data')->nullable(); + $table->timestamps(); + + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->foreign('parent_id')->references('id')->on('activities')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('user_activities'); + } +} diff --git a/tests/Unit/Models/ActivityTest.php b/tests/Unit/Models/ActivityTest.php new file mode 100644 index 0000000..ed98b98 --- /dev/null +++ b/tests/Unit/Models/ActivityTest.php @@ -0,0 +1,39 @@ +adminUserSigningIn(); + $project = factory(Project::class)->create(['name' => 'New Project']); + + $project->name = 'Updated project'; + $project->save(); + + $this->seeInDatabase('user_activities', [ + 'type' => 'project_updated', + 'parent_id' => null, + 'user_id' => $admin->id, + 'object_id' => $project->id, + 'object_type' => 'projects', + 'data' => json_encode([ + 'before' => [ + 'name' => 'New Project', + ], + 'after' => [ + 'name' => 'Updated project', + ], + 'notes' => null, + ]), + ]); + } +} From 0eb87d3ccc6069aff23cedfcb687eac8f6bc13ae Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Sat, 6 Mar 2021 21:51:04 +0800 Subject: [PATCH 02/16] Add project creation activity log --- app/Entities/Projects/Project.php | 1 + app/Events/Projects/Created.php | 15 +++++++++++++ app/Events/Projects/Updated.php | 5 ----- .../Projects/LogProjectCreationActivity.php | 24 ++++++++++++++++++++ .../Projects/LogProjectUpdateActivity.php | 3 --- app/Providers/EventServiceProvider.php | 3 +++ tests/Unit/Models/ActivityTest.php | 26 ++++++++++++++++------ 7 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 app/Events/Projects/Created.php create mode 100644 app/Listeners/Projects/LogProjectCreationActivity.php diff --git a/app/Entities/Projects/Project.php b/app/Entities/Projects/Project.php index fee1dd3..47b56e8 100755 --- a/app/Entities/Projects/Project.php +++ b/app/Entities/Projects/Project.php @@ -24,6 +24,7 @@ class Project extends Model * @var array */ protected $dispatchesEvents = [ + 'created' => 'App\Events\Projects\Created', 'updated' => 'App\Events\Projects\Updated', ]; diff --git a/app/Events/Projects/Created.php b/app/Events/Projects/Created.php new file mode 100644 index 0000000..aa78dd6 --- /dev/null +++ b/app/Events/Projects/Created.php @@ -0,0 +1,15 @@ +project = $project; + } +} diff --git a/app/Events/Projects/Updated.php b/app/Events/Projects/Updated.php index c344f38..d634086 100644 --- a/app/Events/Projects/Updated.php +++ b/app/Events/Projects/Updated.php @@ -8,11 +8,6 @@ class Updated { public $project; - /** - * Create a new event instance. - * - * @return void - */ public function __construct(Project $project) { $this->project = $project; diff --git a/app/Listeners/Projects/LogProjectCreationActivity.php b/app/Listeners/Projects/LogProjectCreationActivity.php new file mode 100644 index 0000000..a7d8455 --- /dev/null +++ b/app/Listeners/Projects/LogProjectCreationActivity.php @@ -0,0 +1,24 @@ +project; + + $activityEntry = [ + 'type' => 'project_created', + 'parent_id' => null, + 'user_id' => auth()->id(), + 'object_id' => $project->id, + 'object_type' => 'projects', + ]; + + Activity::create($activityEntry); + } +} diff --git a/app/Listeners/Projects/LogProjectUpdateActivity.php b/app/Listeners/Projects/LogProjectUpdateActivity.php index 6c5e681..af5b61a 100644 --- a/app/Listeners/Projects/LogProjectUpdateActivity.php +++ b/app/Listeners/Projects/LogProjectUpdateActivity.php @@ -15,13 +15,10 @@ class LogProjectUpdateActivity */ public function handle(Updated $event) { - // dd($event->project->fresh()->toArray(), $event->project->getOriginal(), $event->project->getDirty()); $project = $event->project; - // dd($project->getOriginal(), $project->getChanges()); $originalProject = $project->getOriginal(); $attributeChanges = $project->getChanges(); $attributeKeys = array_keys($project->getChanges()); - // dd($attributeChanges, $attributeKeys); $activityEntry = [ 'type' => 'project_updated', diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 71878ea..3d3a65a 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -13,6 +13,9 @@ class EventServiceProvider extends ServiceProvider * @var array */ protected $listen = [ + 'App\Events\Projects\Created' => [ + 'App\Listeners\Projects\LogProjectCreationActivity', + ], 'App\Events\Projects\Updated' => [ 'App\Listeners\Projects\LogProjectUpdateActivity', ], diff --git a/tests/Unit/Models/ActivityTest.php b/tests/Unit/Models/ActivityTest.php index ed98b98..a2254a4 100644 --- a/tests/Unit/Models/ActivityTest.php +++ b/tests/Unit/Models/ActivityTest.php @@ -11,7 +11,23 @@ class ActivityTest extends TestCase use RefreshDatabase; /** @test */ - public function it_records_project_data_update() + public function it_creates_project_creation_activities() + { + $admin = $this->adminUserSigningIn(); + $project = factory(Project::class)->create(['name' => 'New Project']); + + $this->seeInDatabase('user_activities', [ + 'type' => 'project_created', + 'parent_id' => null, + 'user_id' => $admin->id, + 'object_id' => $project->id, + 'object_type' => 'projects', + 'data' => null, + ]); + } + + /** @test */ + public function it_records_project_data_update_activities() { $admin = $this->adminUserSigningIn(); $project = factory(Project::class)->create(['name' => 'New Project']); @@ -26,12 +42,8 @@ class ActivityTest extends TestCase 'object_id' => $project->id, 'object_type' => 'projects', 'data' => json_encode([ - 'before' => [ - 'name' => 'New Project', - ], - 'after' => [ - 'name' => 'Updated project', - ], + 'before' => ['name' => 'New Project'], + 'after' => ['name' => 'Updated project'], 'notes' => null, ]), ]); From 02352cb07d4daefc5912a877190746539975fe3d Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Sat, 6 Mar 2021 21:57:54 +0800 Subject: [PATCH 03/16] Add job creation activity log --- app/Entities/Projects/Job.php | 9 +++++++++ app/Events/Jobs/Created.php | 15 +++++++++++++++ app/Listeners/Jobs/LogJobCreationActivity.php | 25 +++++++++++++++++++++++++ app/Providers/EventServiceProvider.php | 3 +++ tests/Unit/Models/ActivityTest.php | 20 +++++++++++++++++++- 5 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 app/Events/Jobs/Created.php create mode 100644 app/Listeners/Jobs/LogJobCreationActivity.php diff --git a/app/Entities/Projects/Job.php b/app/Entities/Projects/Job.php index cb87fb5..57086e3 100755 --- a/app/Entities/Projects/Job.php +++ b/app/Entities/Projects/Job.php @@ -17,6 +17,15 @@ class Job extends Model use PresentableTrait; /** + * The event map for the model. + * + * @var array + */ + protected $dispatchesEvents = [ + 'created' => 'App\Events\Jobs\Created', + ]; + + /** * @var \App\Entities\Projects\JobPresenter */ protected $presenter = JobPresenter::class; diff --git a/app/Events/Jobs/Created.php b/app/Events/Jobs/Created.php new file mode 100644 index 0000000..624967a --- /dev/null +++ b/app/Events/Jobs/Created.php @@ -0,0 +1,15 @@ +job = $job; + } +} diff --git a/app/Listeners/Jobs/LogJobCreationActivity.php b/app/Listeners/Jobs/LogJobCreationActivity.php new file mode 100644 index 0000000..1741e8b --- /dev/null +++ b/app/Listeners/Jobs/LogJobCreationActivity.php @@ -0,0 +1,25 @@ +job; + + $activityEntry = [ + 'type' => 'job_created', + 'parent_id' => null, + 'user_id' => auth()->id(), + 'object_id' => $job->id, + 'object_type' => 'jobs', + 'data' => null, + ]; + + Activity::create($activityEntry); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 3d3a65a..d6d3121 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -19,6 +19,9 @@ class EventServiceProvider extends ServiceProvider 'App\Events\Projects\Updated' => [ 'App\Listeners\Projects\LogProjectUpdateActivity', ], + 'App\Events\Jobs\Created' => [ + 'App\Listeners\Jobs\LogJobCreationActivity', + ], ]; /** diff --git a/tests/Unit/Models/ActivityTest.php b/tests/Unit/Models/ActivityTest.php index a2254a4..8f51c88 100644 --- a/tests/Unit/Models/ActivityTest.php +++ b/tests/Unit/Models/ActivityTest.php @@ -2,6 +2,7 @@ namespace Tests\Unit\Models; +use App\Entities\Projects\Job; use App\Entities\Projects\Project; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; @@ -14,7 +15,7 @@ class ActivityTest extends TestCase public function it_creates_project_creation_activities() { $admin = $this->adminUserSigningIn(); - $project = factory(Project::class)->create(['name' => 'New Project']); + $project = factory(Project::class)->create(); $this->seeInDatabase('user_activities', [ 'type' => 'project_created', @@ -48,4 +49,21 @@ class ActivityTest extends TestCase ]), ]); } + + /** @test */ + public function it_records_job_creation_activities() + { + $admin = $this->adminUserSigningIn(); + $project = factory(Project::class)->create(); + $job = factory(Job::class)->create(['project_id' => $project->id]); + + $this->seeInDatabase('user_activities', [ + 'type' => 'job_created', + 'parent_id' => null, + 'user_id' => $admin->id, + 'object_id' => $job->id, + 'object_type' => 'jobs', + 'data' => null, + ]); + } } From 0872b6ef1c6d41370a2df37699eb65e54feae174 Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Sat, 6 Mar 2021 22:01:56 +0800 Subject: [PATCH 04/16] Add job update activity log --- app/Entities/Projects/Job.php | 1 + app/Events/Jobs/Updated.php | 15 +++++++++ app/Listeners/Jobs/LogJobUpdateActivity.php | 52 +++++++++++++++++++++++++++++ app/Providers/EventServiceProvider.php | 3 ++ tests/Unit/Models/ActivityTest.php | 27 +++++++++++++++ 5 files changed, 98 insertions(+) create mode 100644 app/Events/Jobs/Updated.php create mode 100644 app/Listeners/Jobs/LogJobUpdateActivity.php diff --git a/app/Entities/Projects/Job.php b/app/Entities/Projects/Job.php index 57086e3..3b3a491 100755 --- a/app/Entities/Projects/Job.php +++ b/app/Entities/Projects/Job.php @@ -23,6 +23,7 @@ class Job extends Model */ protected $dispatchesEvents = [ 'created' => 'App\Events\Jobs\Created', + 'updated' => 'App\Events\Jobs\Updated', ]; /** diff --git a/app/Events/Jobs/Updated.php b/app/Events/Jobs/Updated.php new file mode 100644 index 0000000..e6ed3f1 --- /dev/null +++ b/app/Events/Jobs/Updated.php @@ -0,0 +1,15 @@ +job = $job; + } +} diff --git a/app/Listeners/Jobs/LogJobUpdateActivity.php b/app/Listeners/Jobs/LogJobUpdateActivity.php new file mode 100644 index 0000000..7567f88 --- /dev/null +++ b/app/Listeners/Jobs/LogJobUpdateActivity.php @@ -0,0 +1,52 @@ +job; + $originalJob = $job->getOriginal(); + $attributeChanges = $job->getChanges(); + $attributeKeys = array_keys($job->getChanges()); + + $activityEntry = [ + 'type' => 'job_updated', + 'parent_id' => null, + 'user_id' => auth()->id(), + 'object_id' => $job->id, + 'object_type' => 'jobs', + 'data' => json_encode([ + 'before' => $this->getBeforeValues($originalJob, $attributeKeys), + 'after' => $this->getAfterValues($job->toArray(), $attributeKeys), + 'notes' => null, + ]), + ]; + + Activity::create($activityEntry); + } + + private function getBeforeValues(array $originalJob, array $attributeKeys) + { + $beforeValues = []; + foreach ($attributeKeys as $attributeKey) { + $beforeValues[$attributeKey] = $originalJob[$attributeKey]; + } + + return $beforeValues; + } + + private function getAfterValues(array $job, array $attributeKeys) + { + $afterValues = []; + foreach ($attributeKeys as $attributeKey) { + $afterValues[$attributeKey] = $job[$attributeKey]; + } + + return $afterValues; + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index d6d3121..8bd3a50 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -22,6 +22,9 @@ class EventServiceProvider extends ServiceProvider 'App\Events\Jobs\Created' => [ 'App\Listeners\Jobs\LogJobCreationActivity', ], + 'App\Events\Jobs\Updated' => [ + 'App\Listeners\Jobs\LogJobUpdateActivity', + ], ]; /** diff --git a/tests/Unit/Models/ActivityTest.php b/tests/Unit/Models/ActivityTest.php index 8f51c88..a13a33b 100644 --- a/tests/Unit/Models/ActivityTest.php +++ b/tests/Unit/Models/ActivityTest.php @@ -66,4 +66,31 @@ class ActivityTest extends TestCase 'data' => null, ]); } + + /** @test */ + public function it_records_job_data_update_activities() + { + $admin = $this->adminUserSigningIn(); + $project = factory(Project::class)->create(); + $job = factory(Job::class)->create([ + 'name' => 'New Job', + 'project_id' => $project->id, + ]); + + $job->name = 'Updated job'; + $job->save(); + + $this->seeInDatabase('user_activities', [ + 'type' => 'job_updated', + 'parent_id' => null, + 'user_id' => $admin->id, + 'object_id' => $job->id, + 'object_type' => 'jobs', + 'data' => json_encode([ + 'before' => ['name' => 'New Job'], + 'after' => ['name' => 'Updated job'], + 'notes' => null, + ]), + ]); + } } From 88ec3bc34cbd0eda70d05ccf1441e7a3bfc35ed4 Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Sat, 6 Mar 2021 22:08:44 +0800 Subject: [PATCH 05/16] Add job deletion activity log --- app/Entities/Projects/Job.php | 1 + app/Events/Jobs/Deleted.php | 15 +++++++++++ .../Projects/LogProjectJobDeletionActivity.php | 30 ++++++++++++++++++++++ app/Providers/EventServiceProvider.php | 3 +++ tests/Unit/Models/ActivityTest.php | 22 ++++++++++++++++ 5 files changed, 71 insertions(+) create mode 100644 app/Events/Jobs/Deleted.php create mode 100644 app/Listeners/Projects/LogProjectJobDeletionActivity.php diff --git a/app/Entities/Projects/Job.php b/app/Entities/Projects/Job.php index 3b3a491..e658f17 100755 --- a/app/Entities/Projects/Job.php +++ b/app/Entities/Projects/Job.php @@ -24,6 +24,7 @@ class Job extends Model protected $dispatchesEvents = [ 'created' => 'App\Events\Jobs\Created', 'updated' => 'App\Events\Jobs\Updated', + 'deleted' => 'App\Events\Jobs\Deleted', ]; /** diff --git a/app/Events/Jobs/Deleted.php b/app/Events/Jobs/Deleted.php new file mode 100644 index 0000000..b438550 --- /dev/null +++ b/app/Events/Jobs/Deleted.php @@ -0,0 +1,15 @@ +job = $job; + } +} diff --git a/app/Listeners/Projects/LogProjectJobDeletionActivity.php b/app/Listeners/Projects/LogProjectJobDeletionActivity.php new file mode 100644 index 0000000..8d5a0a3 --- /dev/null +++ b/app/Listeners/Projects/LogProjectJobDeletionActivity.php @@ -0,0 +1,30 @@ +job; + $projectId = $job->project_id; + + $activityEntry = [ + 'type' => 'job_deleted', + 'parent_id' => null, + 'user_id' => auth()->id(), + 'object_id' => $projectId, + 'object_type' => 'projects', + 'data' => json_encode([ + 'name' => $job->name, + 'description' => $job->description, + 'price' => $job->price, + ]), + ]; + + Activity::create($activityEntry); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 8bd3a50..f5bab95 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -25,6 +25,9 @@ class EventServiceProvider extends ServiceProvider 'App\Events\Jobs\Updated' => [ 'App\Listeners\Jobs\LogJobUpdateActivity', ], + 'App\Events\Jobs\Deleted' => [ + 'App\Listeners\Projects\LogProjectJobDeletionActivity', + ], ]; /** diff --git a/tests/Unit/Models/ActivityTest.php b/tests/Unit/Models/ActivityTest.php index a13a33b..1bd2b25 100644 --- a/tests/Unit/Models/ActivityTest.php +++ b/tests/Unit/Models/ActivityTest.php @@ -93,4 +93,26 @@ class ActivityTest extends TestCase ]), ]); } + + /** @test */ + public function it_records_job_deletion_activities() + { + $admin = $this->adminUserSigningIn(); + $project = factory(Project::class)->create(); + $job = factory(Job::class)->create(['project_id' => $project->id]); + $job->delete(); + + $this->seeInDatabase('user_activities', [ + 'type' => 'job_deleted', + 'parent_id' => null, + 'user_id' => $admin->id, + 'object_id' => $project->id, + 'object_type' => 'projects', + 'data' => json_encode([ + 'name' => $job->name, + 'description' => $job->description, + 'price' => $job->price, + ]), + ]); + } } From 81cec84ff07eac610feca33abd0711835634d0ce Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Sat, 6 Mar 2021 22:10:25 +0800 Subject: [PATCH 06/16] Use attribute casting on the activity data --- app/Entities/Users/Activity.php | 4 ++++ app/Listeners/Jobs/LogJobUpdateActivity.php | 4 ++-- app/Listeners/Projects/LogProjectJobDeletionActivity.php | 4 ++-- app/Listeners/Projects/LogProjectUpdateActivity.php | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/Entities/Users/Activity.php b/app/Entities/Users/Activity.php index 15b92db..89f8845 100644 --- a/app/Entities/Users/Activity.php +++ b/app/Entities/Users/Activity.php @@ -9,4 +9,8 @@ class Activity extends Model protected $table = 'user_activities'; protected $fillable = ['type', 'parent_id', 'user_id', 'object_id', 'object_type', 'data']; + + protected $casts = [ + 'data' => 'array', + ]; } diff --git a/app/Listeners/Jobs/LogJobUpdateActivity.php b/app/Listeners/Jobs/LogJobUpdateActivity.php index 7567f88..4bb0c2d 100644 --- a/app/Listeners/Jobs/LogJobUpdateActivity.php +++ b/app/Listeners/Jobs/LogJobUpdateActivity.php @@ -20,11 +20,11 @@ class LogJobUpdateActivity 'user_id' => auth()->id(), 'object_id' => $job->id, 'object_type' => 'jobs', - 'data' => json_encode([ + 'data' => [ 'before' => $this->getBeforeValues($originalJob, $attributeKeys), 'after' => $this->getAfterValues($job->toArray(), $attributeKeys), 'notes' => null, - ]), + ], ]; Activity::create($activityEntry); diff --git a/app/Listeners/Projects/LogProjectJobDeletionActivity.php b/app/Listeners/Projects/LogProjectJobDeletionActivity.php index 8d5a0a3..7a07102 100644 --- a/app/Listeners/Projects/LogProjectJobDeletionActivity.php +++ b/app/Listeners/Projects/LogProjectJobDeletionActivity.php @@ -18,11 +18,11 @@ class LogProjectJobDeletionActivity 'user_id' => auth()->id(), 'object_id' => $projectId, 'object_type' => 'projects', - 'data' => json_encode([ + 'data' => [ 'name' => $job->name, 'description' => $job->description, 'price' => $job->price, - ]), + ], ]; Activity::create($activityEntry); diff --git a/app/Listeners/Projects/LogProjectUpdateActivity.php b/app/Listeners/Projects/LogProjectUpdateActivity.php index af5b61a..751585a 100644 --- a/app/Listeners/Projects/LogProjectUpdateActivity.php +++ b/app/Listeners/Projects/LogProjectUpdateActivity.php @@ -26,11 +26,11 @@ class LogProjectUpdateActivity 'user_id' => auth()->id(), 'object_id' => $project->id, 'object_type' => 'projects', - 'data' => json_encode([ + 'data' => [ 'before' => $this->getBeforeValues($originalProject, $attributeKeys), 'after' => $this->getAfterValues($project->toArray(), $attributeKeys), 'notes' => null, - ]), + ], ]; Activity::create($activityEntry); From 3ca8e7df69e37127eb23f90e7beaa3c4392ae006 Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Sat, 6 Mar 2021 22:17:06 +0800 Subject: [PATCH 07/16] Code cleanup --- app/Entities/Users/Activity.php | 4 +--- app/Listeners/Projects/LogProjectUpdateActivity.php | 6 ------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/app/Entities/Users/Activity.php b/app/Entities/Users/Activity.php index 89f8845..2f63cbe 100644 --- a/app/Entities/Users/Activity.php +++ b/app/Entities/Users/Activity.php @@ -10,7 +10,5 @@ class Activity extends Model protected $fillable = ['type', 'parent_id', 'user_id', 'object_id', 'object_type', 'data']; - protected $casts = [ - 'data' => 'array', - ]; + protected $casts = ['data' => 'array']; } diff --git a/app/Listeners/Projects/LogProjectUpdateActivity.php b/app/Listeners/Projects/LogProjectUpdateActivity.php index 751585a..e597b33 100644 --- a/app/Listeners/Projects/LogProjectUpdateActivity.php +++ b/app/Listeners/Projects/LogProjectUpdateActivity.php @@ -7,12 +7,6 @@ use App\Events\Projects\Updated; class LogProjectUpdateActivity { - /** - * Handle the event. - * - * @param Updated $event - * @return void - */ public function handle(Updated $event) { $project = $event->project; From 933e935583ecd99655c3f3d43ddea27e4593061b Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Sun, 7 Mar 2021 21:17:13 +0800 Subject: [PATCH 08/16] Add task creation activity log --- app/Entities/Projects/Task.php | 9 +++++++++ app/Events/Tasks/Created.php | 15 +++++++++++++++ app/Listeners/Tasks/LogTaskCreationActivity.php | 25 +++++++++++++++++++++++++ app/Providers/EventServiceProvider.php | 3 +++ tests/Unit/Models/ActivityTest.php | 19 +++++++++++++++++++ 5 files changed, 71 insertions(+) create mode 100644 app/Events/Tasks/Created.php create mode 100644 app/Listeners/Tasks/LogTaskCreationActivity.php diff --git a/app/Entities/Projects/Task.php b/app/Entities/Projects/Task.php index cb5e78a..fde2605 100755 --- a/app/Entities/Projects/Task.php +++ b/app/Entities/Projects/Task.php @@ -6,6 +6,15 @@ use Illuminate\Database\Eloquent\Model; class Task extends Model { + /** + * The event map for the model. + * + * @var array + */ + protected $dispatchesEvents = [ + 'created' => 'App\Events\Tasks\Created', + ]; + protected $guarded = ['id', 'created_at', 'updated_at']; protected $touches = ['job']; diff --git a/app/Events/Tasks/Created.php b/app/Events/Tasks/Created.php new file mode 100644 index 0000000..fa0ef0c --- /dev/null +++ b/app/Events/Tasks/Created.php @@ -0,0 +1,15 @@ +task = $task; + } +} diff --git a/app/Listeners/Tasks/LogTaskCreationActivity.php b/app/Listeners/Tasks/LogTaskCreationActivity.php new file mode 100644 index 0000000..4c1523d --- /dev/null +++ b/app/Listeners/Tasks/LogTaskCreationActivity.php @@ -0,0 +1,25 @@ +task; + + $activityEntry = [ + 'type' => 'task_created', + 'parent_id' => null, + 'user_id' => auth()->id(), + 'object_id' => $task->id, + 'object_type' => 'tasks', + 'data' => null, + ]; + + Activity::create($activityEntry); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index f5bab95..66fd7e8 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -28,6 +28,9 @@ class EventServiceProvider extends ServiceProvider 'App\Events\Jobs\Deleted' => [ 'App\Listeners\Projects\LogProjectJobDeletionActivity', ], + 'App\Events\Tasks\Created' => [ + 'App\Listeners\Tasks\LogTaskCreationActivity', + ], ]; /** diff --git a/tests/Unit/Models/ActivityTest.php b/tests/Unit/Models/ActivityTest.php index 1bd2b25..7977fb1 100644 --- a/tests/Unit/Models/ActivityTest.php +++ b/tests/Unit/Models/ActivityTest.php @@ -4,6 +4,7 @@ namespace Tests\Unit\Models; use App\Entities\Projects\Job; use App\Entities\Projects\Project; +use App\Entities\Projects\Task; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; @@ -115,4 +116,22 @@ class ActivityTest extends TestCase ]), ]); } + + /** @test */ + public function it_records_task_creation_activities() + { + $admin = $this->adminUserSigningIn(); + $project = factory(Project::class)->create(); + $job = factory(Job::class)->create(['project_id' => $project->id]); + $task = factory(Task::class)->create(['job_id' => $job->id]); + + $this->seeInDatabase('user_activities', [ + 'type' => 'task_created', + 'parent_id' => null, + 'user_id' => $admin->id, + 'object_id' => $task->id, + 'object_type' => 'tasks', + 'data' => null, + ]); + } } From 27fc9a2b299b47c180fc0ff3510b2275a3d67ac9 Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Sun, 7 Mar 2021 21:20:44 +0800 Subject: [PATCH 09/16] Add task update activity log --- app/Entities/Projects/Task.php | 1 + app/Events/Tasks/Updated.php | 15 ++++++++ app/Listeners/Tasks/LogTaskUpdateActivity.php | 52 +++++++++++++++++++++++++++ app/Providers/EventServiceProvider.php | 3 ++ tests/Unit/Models/ActivityTest.php | 28 +++++++++++++++ 5 files changed, 99 insertions(+) create mode 100644 app/Events/Tasks/Updated.php create mode 100644 app/Listeners/Tasks/LogTaskUpdateActivity.php diff --git a/app/Entities/Projects/Task.php b/app/Entities/Projects/Task.php index fde2605..4b209cd 100755 --- a/app/Entities/Projects/Task.php +++ b/app/Entities/Projects/Task.php @@ -13,6 +13,7 @@ class Task extends Model */ protected $dispatchesEvents = [ 'created' => 'App\Events\Tasks\Created', + 'updated' => 'App\Events\Tasks\Updated', ]; protected $guarded = ['id', 'created_at', 'updated_at']; diff --git a/app/Events/Tasks/Updated.php b/app/Events/Tasks/Updated.php new file mode 100644 index 0000000..3b6da9d --- /dev/null +++ b/app/Events/Tasks/Updated.php @@ -0,0 +1,15 @@ +task = $task; + } +} diff --git a/app/Listeners/Tasks/LogTaskUpdateActivity.php b/app/Listeners/Tasks/LogTaskUpdateActivity.php new file mode 100644 index 0000000..f9b3bb2 --- /dev/null +++ b/app/Listeners/Tasks/LogTaskUpdateActivity.php @@ -0,0 +1,52 @@ +task; + $originalTask = $task->getOriginal(); + $attributeChanges = $task->getChanges(); + $attributeKeys = array_keys($task->getChanges()); + + $activityEntry = [ + 'type' => 'task_updated', + 'parent_id' => null, + 'user_id' => auth()->id(), + 'object_id' => $task->id, + 'object_type' => 'tasks', + 'data' => [ + 'before' => $this->getBeforeValues($originalTask, $attributeKeys), + 'after' => $this->getAfterValues($task->toArray(), $attributeKeys), + 'notes' => null, + ], + ]; + + Activity::create($activityEntry); + } + + private function getBeforeValues(array $originalTask, array $attributeKeys) + { + $beforeValues = []; + foreach ($attributeKeys as $attributeKey) { + $beforeValues[$attributeKey] = $originalTask[$attributeKey]; + } + + return $beforeValues; + } + + private function getAfterValues(array $task, array $attributeKeys) + { + $afterValues = []; + foreach ($attributeKeys as $attributeKey) { + $afterValues[$attributeKey] = $task[$attributeKey]; + } + + return $afterValues; + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 66fd7e8..0888522 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -31,6 +31,9 @@ class EventServiceProvider extends ServiceProvider 'App\Events\Tasks\Created' => [ 'App\Listeners\Tasks\LogTaskCreationActivity', ], + 'App\Events\Tasks\Updated' => [ + 'App\Listeners\Tasks\LogTaskUpdateActivity', + ], ]; /** diff --git a/tests/Unit/Models/ActivityTest.php b/tests/Unit/Models/ActivityTest.php index 7977fb1..ed0afc6 100644 --- a/tests/Unit/Models/ActivityTest.php +++ b/tests/Unit/Models/ActivityTest.php @@ -134,4 +134,32 @@ class ActivityTest extends TestCase 'data' => null, ]); } + + /** @test */ + public function it_records_task_data_update_activities() + { + $admin = $this->adminUserSigningIn(); + $project = factory(Project::class)->create(); + $job = factory(Job::class)->create(['project_id' => $project->id]); + $task = factory(Task::class)->create([ + 'name' => 'New Task', + 'job_id' => $job->id, + ]); + + $task->name = 'Updated task'; + $task->save(); + + $this->seeInDatabase('user_activities', [ + 'type' => 'task_updated', + 'parent_id' => null, + 'user_id' => $admin->id, + 'object_id' => $task->id, + 'object_type' => 'tasks', + 'data' => json_encode([ + 'before' => ['name' => 'New Task'], + 'after' => ['name' => 'Updated task'], + 'notes' => null, + ]), + ]); + } } From 1141d6e42c6c7795bce5154d33fc43df005c588b Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Sun, 7 Mar 2021 21:37:04 +0800 Subject: [PATCH 10/16] Add task deletion activity log --- app/Entities/Projects/Task.php | 1 + app/Events/Tasks/Deleted.php | 15 ++++++++++++ app/Listeners/Jobs/LogJobTaskDeletionActivity.php | 30 +++++++++++++++++++++++ app/Providers/EventServiceProvider.php | 3 +++ tests/Unit/Models/ActivityTest.php | 23 +++++++++++++++++ 5 files changed, 72 insertions(+) create mode 100644 app/Events/Tasks/Deleted.php create mode 100644 app/Listeners/Jobs/LogJobTaskDeletionActivity.php diff --git a/app/Entities/Projects/Task.php b/app/Entities/Projects/Task.php index 4b209cd..c9167bf 100755 --- a/app/Entities/Projects/Task.php +++ b/app/Entities/Projects/Task.php @@ -14,6 +14,7 @@ class Task extends Model protected $dispatchesEvents = [ 'created' => 'App\Events\Tasks\Created', 'updated' => 'App\Events\Tasks\Updated', + 'deleted' => 'App\Events\Tasks\Deleted', ]; protected $guarded = ['id', 'created_at', 'updated_at']; diff --git a/app/Events/Tasks/Deleted.php b/app/Events/Tasks/Deleted.php new file mode 100644 index 0000000..a7b613e --- /dev/null +++ b/app/Events/Tasks/Deleted.php @@ -0,0 +1,15 @@ +task = $task; + } +} diff --git a/app/Listeners/Jobs/LogJobTaskDeletionActivity.php b/app/Listeners/Jobs/LogJobTaskDeletionActivity.php new file mode 100644 index 0000000..d1684bf --- /dev/null +++ b/app/Listeners/Jobs/LogJobTaskDeletionActivity.php @@ -0,0 +1,30 @@ +task; + $jobId = $task->job_id; + + $activityEntry = [ + 'type' => 'task_deleted', + 'parent_id' => null, + 'user_id' => auth()->id(), + 'object_id' => $jobId, + 'object_type' => 'jobs', + 'data' => [ + 'name' => $task->name, + 'description' => $task->description, + 'progress' => $task->progress, + ], + ]; + + Activity::create($activityEntry); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 0888522..7f0ce06 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -34,6 +34,9 @@ class EventServiceProvider extends ServiceProvider 'App\Events\Tasks\Updated' => [ 'App\Listeners\Tasks\LogTaskUpdateActivity', ], + 'App\Events\Tasks\Deleted' => [ + 'App\Listeners\Jobs\LogJobTaskDeletionActivity', + ], ]; /** diff --git a/tests/Unit/Models/ActivityTest.php b/tests/Unit/Models/ActivityTest.php index ed0afc6..7ca2b56 100644 --- a/tests/Unit/Models/ActivityTest.php +++ b/tests/Unit/Models/ActivityTest.php @@ -162,4 +162,27 @@ class ActivityTest extends TestCase ]), ]); } + + /** @test */ + public function it_records_task_deletion_activities() + { + $admin = $this->adminUserSigningIn(); + $project = factory(Project::class)->create(); + $job = factory(Job::class)->create(['project_id' => $project->id]); + $task = factory(Task::class)->create(['job_id' => $job->id]); + $task->delete(); + + $this->seeInDatabase('user_activities', [ + 'type' => 'task_deleted', + 'parent_id' => null, + 'user_id' => $admin->id, + 'object_id' => $job->id, + 'object_type' => 'jobs', + 'data' => json_encode([ + 'name' => $task->name, + 'description' => $task->description, + 'progress' => $task->progress, + ]), + ]); + } } From 0e9520415749f3aa7f503c8234efbdf4dd1e6f1b Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Sun, 7 Mar 2021 21:42:13 +0800 Subject: [PATCH 11/16] Add test to make sure the task progress update gets logged --- tests/Unit/Models/ActivityTest.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Unit/Models/ActivityTest.php b/tests/Unit/Models/ActivityTest.php index 7ca2b56..96bd2c8 100644 --- a/tests/Unit/Models/ActivityTest.php +++ b/tests/Unit/Models/ActivityTest.php @@ -164,6 +164,34 @@ class ActivityTest extends TestCase } /** @test */ + public function it_records_task_progress_update_activities() + { + $admin = $this->adminUserSigningIn(); + $project = factory(Project::class)->create(); + $job = factory(Job::class)->create(['project_id' => $project->id]); + $task = factory(Task::class)->create([ + 'progress' => 20, + 'job_id' => $job->id, + ]); + + $task->progress = 40; + $task->save(); + + $this->seeInDatabase('user_activities', [ + 'type' => 'task_updated', + 'parent_id' => null, + 'user_id' => $admin->id, + 'object_id' => $task->id, + 'object_type' => 'tasks', + 'data' => json_encode([ + 'before' => ['progress' => 20], + 'after' => ['progress' => 40], + 'notes' => null, + ]), + ]); + } + + /** @test */ public function it_records_task_deletion_activities() { $admin = $this->adminUserSigningIn(); From ee02d5926d1baef2aa9ce1b16135a6ba222bea5c Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Mon, 8 Mar 2021 10:39:14 +0800 Subject: [PATCH 12/16] Fix invalid constraint table --- database/migrations/2021_03_05_221708_create_user_activities_table.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/migrations/2021_03_05_221708_create_user_activities_table.php b/database/migrations/2021_03_05_221708_create_user_activities_table.php index a7750aa..9d040de 100644 --- a/database/migrations/2021_03_05_221708_create_user_activities_table.php +++ b/database/migrations/2021_03_05_221708_create_user_activities_table.php @@ -24,7 +24,7 @@ class CreateUserActivitiesTable extends Migration $table->timestamps(); $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - $table->foreign('parent_id')->references('id')->on('activities')->onDelete('cascade'); + $table->foreign('parent_id')->references('id')->on('user_activities')->onDelete('cascade'); }); } From aed240710d3444d967c6682269cdd72046b549df Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Mon, 8 Mar 2021 22:16:28 +0800 Subject: [PATCH 13/16] Add user activities tab on the project detail page --- app/Entities/Users/Activity.php | 11 +++++++ .../Controllers/Projects/ActivityController.php | 24 +++++++++++++++ app/Providers/AppServiceProvider.php | 1 + resources/lang/de/activity.php | 9 ++++++ resources/lang/de/project.php | 2 ++ resources/lang/en/activity.php | 9 ++++++ resources/lang/en/project.php | 2 ++ resources/lang/id/activity.php | 9 ++++++ resources/lang/id/project.php | 2 ++ .../views/projects/activities/index.blade.php | 22 ++++++++++++++ .../views/projects/partials/nav-tabs.blade.php | 3 ++ .../users/activities/activity_list_item.blade.php | 4 +++ .../activities/projects/job_deleted.blade.php | 15 ++++++++++ .../activities/projects/project_created.blade.php | 11 +++++++ .../activities/projects/project_updated.blade.php | 22 ++++++++++++++ routes/web/projects.php | 5 ++++ tests/Unit/Models/ActivityTest.php | 34 ++++++++++++++++++++++ 17 files changed, 185 insertions(+) create mode 100644 app/Http/Controllers/Projects/ActivityController.php create mode 100644 resources/lang/de/activity.php create mode 100644 resources/lang/en/activity.php create mode 100644 resources/lang/id/activity.php create mode 100755 resources/views/projects/activities/index.blade.php create mode 100644 resources/views/users/activities/activity_list_item.blade.php create mode 100644 resources/views/users/activities/projects/job_deleted.blade.php create mode 100644 resources/views/users/activities/projects/project_created.blade.php create mode 100644 resources/views/users/activities/projects/project_updated.blade.php diff --git a/app/Entities/Users/Activity.php b/app/Entities/Users/Activity.php index 2f63cbe..9d50807 100644 --- a/app/Entities/Users/Activity.php +++ b/app/Entities/Users/Activity.php @@ -2,6 +2,7 @@ namespace App\Entities\Users; +use App\Entities\Users\User; use Illuminate\Database\Eloquent\Model; class Activity extends Model @@ -11,4 +12,14 @@ class Activity extends Model protected $fillable = ['type', 'parent_id', 'user_id', 'object_id', 'object_type', 'data']; protected $casts = ['data' => 'array']; + + public function user() + { + return $this->belongsTo(User::class)->withDefault(['name' => 'n/a']); + } + + public function object() + { + return $this->morphTo(); + } } diff --git a/app/Http/Controllers/Projects/ActivityController.php b/app/Http/Controllers/Projects/ActivityController.php new file mode 100644 index 0000000..b39416e --- /dev/null +++ b/app/Http/Controllers/Projects/ActivityController.php @@ -0,0 +1,24 @@ +where(function ($query) use ($project) { + $query->where('object_id', $project->id); + $query->where('object_type', 'projects'); + }); + + $activities = $activityQuery->latest()->paginate(); + + return view('projects.activities.index', compact('project', 'activities')); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index e62e20c..5fe772b 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -25,6 +25,7 @@ class AppServiceProvider extends ServiceProvider 'projects' => 'App\Entities\Projects\Project', 'issues' => 'App\Entities\Projects\Issue', 'jobs' => 'App\Entities\Projects\Job', + 'tasks' => 'App\Entities\Projects\Task', ]); Paginator::useBootstrap(); } diff --git a/resources/lang/de/activity.php b/resources/lang/de/activity.php new file mode 100644 index 0000000..a483ba5 --- /dev/null +++ b/resources/lang/de/activity.php @@ -0,0 +1,9 @@ + [ + 'project_created' => 'Project created: :name new :user.', + 'project_updated' => 'Project data :name updated by :user.', + 'job_deleted' => 'Job deleted by :user.', + ], +]; diff --git a/resources/lang/de/project.php b/resources/lang/de/project.php index 07898c5..28ab2d7 100644 --- a/resources/lang/de/project.php +++ b/resources/lang/de/project.php @@ -59,6 +59,7 @@ return [ 'proposal_date' => 'Datum des Angebotes', 'project_value' => 'Projektwert', 'proposal_value' => 'Angebotswert', + 'updated_at' => 'Last Update', // Relations 'files' => 'Dokumentenliste', @@ -72,6 +73,7 @@ return [ 'status' => 'Projektstatus', 'payments' => 'Zahlungen', 'issues' => 'Issues', + 'activities' => 'Activities', // Statuses 'planned' => 'geplant', diff --git a/resources/lang/en/activity.php b/resources/lang/en/activity.php new file mode 100644 index 0000000..a483ba5 --- /dev/null +++ b/resources/lang/en/activity.php @@ -0,0 +1,9 @@ + [ + 'project_created' => 'Project created: :name new :user.', + 'project_updated' => 'Project data :name updated by :user.', + 'job_deleted' => 'Job deleted by :user.', + ], +]; diff --git a/resources/lang/en/project.php b/resources/lang/en/project.php index c3a5c40..8b104f6 100644 --- a/resources/lang/en/project.php +++ b/resources/lang/en/project.php @@ -59,6 +59,7 @@ return [ 'proposal_date' => 'Proposal Date', 'project_value' => 'Project Value', 'proposal_value' => 'Proposal Value', + 'updated_at' => 'Last Update', // Relations 'files' => 'Document List', @@ -72,6 +73,7 @@ return [ 'status' => 'Project Status', 'payments' => 'Payments', 'issues' => 'Issues', + 'activities' => 'Activities', // Statuses 'planned' => 'Planned', diff --git a/resources/lang/id/activity.php b/resources/lang/id/activity.php new file mode 100644 index 0000000..19cb1ea --- /dev/null +++ b/resources/lang/id/activity.php @@ -0,0 +1,9 @@ + [ + 'project_created' => 'Input project baru: :name oleh :user.', + 'project_updated' => 'Data project :name diubah oleh :user.', + 'job_deleted' => 'Job dihapus oleh :user.', + ], +]; diff --git a/resources/lang/id/project.php b/resources/lang/id/project.php index 2ea3b04..fcea474 100644 --- a/resources/lang/id/project.php +++ b/resources/lang/id/project.php @@ -59,6 +59,7 @@ return [ 'proposal_date' => 'Tanggal Proposal', 'project_value' => 'Nilai Project', 'proposal_value' => 'Nilai Proposal', + 'updated_at' => 'Waktu Update', // Relations 'files' => 'List Dokumen', @@ -72,6 +73,7 @@ return [ 'status' => 'Status Project', 'payments' => 'Pembayaran', 'issues' => 'Issue', + 'activities' => 'Aktifitas', // Statuses 'planned' => 'Rencana', diff --git a/resources/views/projects/activities/index.blade.php b/resources/views/projects/activities/index.blade.php new file mode 100755 index 0000000..ade6096 --- /dev/null +++ b/resources/views/projects/activities/index.blade.php @@ -0,0 +1,22 @@ +@extends('layouts.project') + +@section('subtitle', __('project.activities')) + +@section('content-project') + +
+
+ {{ $activities->links() }} +
    + @foreach($activities as $activity) + @includeWhen( + view()->exists('users.activities.'.$activity->object_type.'.'.$activity->type), + 'users.activities.'.$activity->object_type.'.'.$activity->type + ) + @endforeach +
+ {{ $activities->links() }} +
+
+ +@endsection diff --git a/resources/views/projects/partials/nav-tabs.blade.php b/resources/views/projects/partials/nav-tabs.blade.php index ed2ee19..47df5b1 100644 --- a/resources/views/projects/partials/nav-tabs.blade.php +++ b/resources/views/projects/partials/nav-tabs.blade.php @@ -3,6 +3,9 @@
  • {!! link_to_route('projects.show', __('project.detail'), $project) !!}
  • +
  • + {!! link_to_route('projects.activities.index', __('project.activities'), $project) !!} +
  • @can('view-jobs', $project)
  • {!! link_to_route('projects.jobs.index', __('project.jobs').' ('.$project->jobs->count().')', $project) !!} diff --git a/resources/views/users/activities/activity_list_item.blade.php b/resources/views/users/activities/activity_list_item.blade.php new file mode 100644 index 0000000..ad295f8 --- /dev/null +++ b/resources/views/users/activities/activity_list_item.blade.php @@ -0,0 +1,4 @@ +
  • + {{ $time }} + {{ $body }} +
  • diff --git a/resources/views/users/activities/projects/job_deleted.blade.php b/resources/views/users/activities/projects/job_deleted.blade.php new file mode 100644 index 0000000..57a1fa9 --- /dev/null +++ b/resources/views/users/activities/projects/job_deleted.blade.php @@ -0,0 +1,15 @@ +@component('users.activities.activity_list_item') +@slot('time') + {{ $activity->created_at }} +@endslot +@slot('body') +

    + {!! __('activity.'.$activity->object_type.'.'.$activity->type, [ + 'user' => $activity->user->name, + ]) !!} +

    +
    {{ __('job.name') }}: {{ $activity->data['name'] }}
    +
    {{ __('job.description') }}: {{ $activity->data['description'] }}
    +
    {{ __('job.price') }}: {{ format_money($activity->data['price']) }}
    +@endslot +@endcomponent diff --git a/resources/views/users/activities/projects/project_created.blade.php b/resources/views/users/activities/projects/project_created.blade.php new file mode 100644 index 0000000..421f0c6 --- /dev/null +++ b/resources/views/users/activities/projects/project_created.blade.php @@ -0,0 +1,11 @@ +@component('users.activities.activity_list_item') +@slot('time') + {{ $activity->created_at }} +@endslot +@slot('body') + {!! __('activity.'.$activity->object_type.'.'.$activity->type, [ + 'user' => $activity->user->name, + 'name' => $activity->object->name, + ]) !!} +@endslot +@endcomponent diff --git a/resources/views/users/activities/projects/project_updated.blade.php b/resources/views/users/activities/projects/project_updated.blade.php new file mode 100644 index 0000000..0a2d36b --- /dev/null +++ b/resources/views/users/activities/projects/project_updated.blade.php @@ -0,0 +1,22 @@ +@component('users.activities.activity_list_item') +@slot('time') + {{ $activity->created_at }} +@endslot +@slot('body') +

    + {!! __('activity.'.$activity->object_type.'.'.$activity->type, [ + 'user' => $activity->user->name, + 'name' => $activity->object->name, + ]) !!} +

    + @php + $data = $activity->data; + @endphp + @foreach ($data['before'] as $key => $value) + @php + $afterValue = $data['after'][$key] ?? null; + @endphp +
    {{ __('project.'.$key) }}: {{ $value }} => {{ $afterValue }}
    + @endforeach +@endslot +@endcomponent diff --git a/routes/web/projects.php b/routes/web/projects.php index 45aefba..bc92298 100644 --- a/routes/web/projects.php +++ b/routes/web/projects.php @@ -26,6 +26,11 @@ Route::group(['middleware' => ['auth'], 'namespace' => 'Projects'], function () Route::get('projects/{project}/invoices', ['as' => 'projects.invoices', 'uses' => 'InvoicesController@index']); /* + * Project Activities Routes + */ + Route::get('projects/{project}/activities', ['as' => 'projects.activities.index', 'uses' => 'ActivityController@index']); + + /* * Project Jobs Routes */ Route::get('projects/{project}/jobs-export/{type?}', ['as' => 'projects.jobs-export', 'uses' => 'JobsController@jobsExport']); diff --git a/tests/Unit/Models/ActivityTest.php b/tests/Unit/Models/ActivityTest.php index 96bd2c8..5187594 100644 --- a/tests/Unit/Models/ActivityTest.php +++ b/tests/Unit/Models/ActivityTest.php @@ -5,6 +5,8 @@ namespace Tests\Unit\Models; use App\Entities\Projects\Job; use App\Entities\Projects\Project; use App\Entities\Projects\Task; +use App\Entities\Users\Activity; +use App\Entities\Users\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; @@ -213,4 +215,36 @@ class ActivityTest extends TestCase ]), ]); } + + /** @test */ + public function an_activity_has_belongs_to_user_relation() + { + $project = factory(Project::class)->create(); + $activity = Activity::where('object_type', 'projects') + ->where('object_id', $project->id) + ->first(); + + $this->assertInstanceOf(User::class, $activity->user); + $this->assertEquals($activity->user_id, $activity->user->id); + } + + /** @test */ + public function an_activity_has_belongs_to_object_relation() + { + $project = factory(Project::class)->create(); + $job = factory(Job::class)->create(['project_id' => $project->id]); + $task = factory(Task::class)->create(['job_id' => $job->id]); + + $projectActivity = Activity::where('object_type', 'projects')->first(); + $this->assertInstanceOf(Project::class, $projectActivity->object); + $this->assertEquals($projectActivity->object_id, $projectActivity->object->id); + + $jobActivity = Activity::where('object_type', 'jobs')->first(); + $this->assertInstanceOf(Job::class, $jobActivity->object); + $this->assertEquals($jobActivity->object_id, $jobActivity->object->id); + + $taskActivity = Activity::where('object_type', 'tasks')->first(); + $this->assertInstanceOf(Task::class, $taskActivity->object); + $this->assertEquals($taskActivity->object_id, $taskActivity->object->id); + } } From e441f12e0d65987a4d03af1b6e401c16f89aea1d Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Mon, 8 Mar 2021 22:29:20 +0800 Subject: [PATCH 14/16] Add job related activities to the project activities tab --- .../Controllers/Projects/ActivityController.php | 7 +++++- resources/lang/de/activity.php | 5 ++++ resources/lang/de/job.php | 1 + resources/lang/en/activity.php | 5 ++++ resources/lang/en/job.php | 1 + resources/lang/id/activity.php | 5 ++++ resources/lang/id/job.php | 1 + .../users/activities/jobs/job_created.blade.php | 11 +++++++++ .../users/activities/jobs/job_updated.blade.php | 28 ++++++++++++++++++++++ .../users/activities/jobs/task_deleted.blade.php | 15 ++++++++++++ 10 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 resources/views/users/activities/jobs/job_created.blade.php create mode 100644 resources/views/users/activities/jobs/job_updated.blade.php create mode 100644 resources/views/users/activities/jobs/task_deleted.blade.php diff --git a/app/Http/Controllers/Projects/ActivityController.php b/app/Http/Controllers/Projects/ActivityController.php index b39416e..1317984 100644 --- a/app/Http/Controllers/Projects/ActivityController.php +++ b/app/Http/Controllers/Projects/ActivityController.php @@ -17,7 +17,12 @@ class ActivityController extends Controller $query->where('object_type', 'projects'); }); - $activities = $activityQuery->latest()->paginate(); + $activityQuery->orWhere(function ($query) use ($project) { + $query->whereIn('object_id', $project->jobs->pluck('id')); + $query->where('object_type', 'jobs'); + }); + + $activities = $activityQuery->latest()->paginate(50); return view('projects.activities.index', compact('project', 'activities')); } diff --git a/resources/lang/de/activity.php b/resources/lang/de/activity.php index a483ba5..645cbcd 100644 --- a/resources/lang/de/activity.php +++ b/resources/lang/de/activity.php @@ -6,4 +6,9 @@ return [ 'project_updated' => 'Project data :name updated by :user.', 'job_deleted' => 'Job deleted by :user.', ], + 'jobs' => [ + 'job_created' => 'Job created: :name new :user.', + 'job_updated' => 'Job data :name updated by :user.', + 'task_deleted' => 'Task deleted by :user.', + ], ]; diff --git a/resources/lang/de/job.php b/resources/lang/de/job.php index feb078a..1de5f16 100644 --- a/resources/lang/de/job.php +++ b/resources/lang/de/job.php @@ -47,6 +47,7 @@ return [ 'target_end_date' => 'Target End Date', 'actual_start_date' => 'Actual Start Date', 'actual_end_date' => 'Actual End Date', + 'updated_at' => 'Last Update', // Types 'main' => 'Haupt', diff --git a/resources/lang/en/activity.php b/resources/lang/en/activity.php index a483ba5..645cbcd 100644 --- a/resources/lang/en/activity.php +++ b/resources/lang/en/activity.php @@ -6,4 +6,9 @@ return [ 'project_updated' => 'Project data :name updated by :user.', 'job_deleted' => 'Job deleted by :user.', ], + 'jobs' => [ + 'job_created' => 'Job created: :name new :user.', + 'job_updated' => 'Job data :name updated by :user.', + 'task_deleted' => 'Task deleted by :user.', + ], ]; diff --git a/resources/lang/en/job.php b/resources/lang/en/job.php index 3415cab..df8c6ff 100644 --- a/resources/lang/en/job.php +++ b/resources/lang/en/job.php @@ -49,6 +49,7 @@ return [ 'target_end_date' => 'Target End Date', 'actual_start_date' => 'Actual Start Date', 'actual_end_date' => 'Actual End Date', + 'updated_at' => 'Last Update', // Types 'main' => 'Main', diff --git a/resources/lang/id/activity.php b/resources/lang/id/activity.php index 19cb1ea..8f996c9 100644 --- a/resources/lang/id/activity.php +++ b/resources/lang/id/activity.php @@ -6,4 +6,9 @@ return [ 'project_updated' => 'Data project :name diubah oleh :user.', 'job_deleted' => 'Job dihapus oleh :user.', ], + 'jobs' => [ + 'job_created' => 'Input job baru: :name oleh :user.', + 'job_updated' => 'Data job :name diubah oleh :user.', + 'task_deleted' => 'Task dihapus oleh :user.', + ], ]; diff --git a/resources/lang/id/job.php b/resources/lang/id/job.php index 4835805..053fb84 100644 --- a/resources/lang/id/job.php +++ b/resources/lang/id/job.php @@ -49,6 +49,7 @@ return [ 'target_end_date' => 'Target Tgl Selesai', 'actual_start_date' => 'Tgl Mulai Aktual', 'actual_end_date' => 'Tgl Selesai Aktual', + 'updated_at' => 'Waktu Update', // Types 'main' => 'Utama', diff --git a/resources/views/users/activities/jobs/job_created.blade.php b/resources/views/users/activities/jobs/job_created.blade.php new file mode 100644 index 0000000..421f0c6 --- /dev/null +++ b/resources/views/users/activities/jobs/job_created.blade.php @@ -0,0 +1,11 @@ +@component('users.activities.activity_list_item') +@slot('time') + {{ $activity->created_at }} +@endslot +@slot('body') + {!! __('activity.'.$activity->object_type.'.'.$activity->type, [ + 'user' => $activity->user->name, + 'name' => $activity->object->name, + ]) !!} +@endslot +@endcomponent diff --git a/resources/views/users/activities/jobs/job_updated.blade.php b/resources/views/users/activities/jobs/job_updated.blade.php new file mode 100644 index 0000000..59dbe2e --- /dev/null +++ b/resources/views/users/activities/jobs/job_updated.blade.php @@ -0,0 +1,28 @@ +@component('users.activities.activity_list_item') +@slot('time') + {{ $activity->created_at }} +@endslot +@slot('body') +

    + {!! __('activity.'.$activity->object_type.'.'.$activity->type, [ + 'user' => $activity->user->name, + 'name' => $activity->object->name, + ]) !!} +

    + @php + $data = $activity->data; + @endphp + @foreach ($data['before'] as $key => $value) + @php + if (in_array($key, ['price']) && !is_null($value)) { + $value = format_money($value); + } + $afterValue = $data['after'][$key] ?? null; + if (in_array($key, ['price']) && !is_null($afterValue)) { + $afterValue = format_money($afterValue); + } + @endphp +
    {{ __('job.'.$key) }}: {{ $value }} => {{ $afterValue }}
    + @endforeach +@endslot +@endcomponent diff --git a/resources/views/users/activities/jobs/task_deleted.blade.php b/resources/views/users/activities/jobs/task_deleted.blade.php new file mode 100644 index 0000000..b81aca5 --- /dev/null +++ b/resources/views/users/activities/jobs/task_deleted.blade.php @@ -0,0 +1,15 @@ +@component('users.activities.activity_list_item') +@slot('time') + {{ $activity->created_at }} +@endslot +@slot('body') +

    + {!! __('activity.'.$activity->object_type.'.'.$activity->type, [ + 'user' => $activity->user->name, + ]) !!} +

    +
    {{ __('task.name') }}: {{ $activity->data['name'] }}
    +
    {{ __('task.description') }}: {{ $activity->data['description'] }}
    +
    {{ __('task.progress') }}: {{ $activity->data['progress'] }} %
    +@endslot +@endcomponent From bd69cbb19281673bc923325ac392865ecfc3cb51 Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Fri, 12 Mar 2021 08:25:31 +0800 Subject: [PATCH 15/16] Remove unused use import --- app/Entities/Users/Activity.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Entities/Users/Activity.php b/app/Entities/Users/Activity.php index 9d50807..bc0b04d 100644 --- a/app/Entities/Users/Activity.php +++ b/app/Entities/Users/Activity.php @@ -2,7 +2,6 @@ namespace App\Entities\Users; -use App\Entities\Users\User; use Illuminate\Database\Eloquent\Model; class Activity extends Model From 6818d428e6ba158a4f094baf8987d3461f5efcab Mon Sep 17 00:00:00 2001 From: Nafies Luthfi Date: Sun, 14 Mar 2021 23:23:55 +0800 Subject: [PATCH 16/16] Add task activity logs on the project activity tab --- .../Controllers/Projects/ActivityController.php | 5 +++++ resources/lang/de/activity.php | 4 ++++ resources/lang/de/job.php | 1 + resources/lang/en/activity.php | 4 ++++ resources/lang/en/job.php | 1 + resources/lang/id/activity.php | 4 ++++ resources/lang/id/job.php | 1 + .../users/activities/tasks/task_created.blade.php | 11 +++++++++++ .../users/activities/tasks/task_updated.blade.php | 22 ++++++++++++++++++++++ 9 files changed, 53 insertions(+) create mode 100644 resources/views/users/activities/tasks/task_created.blade.php create mode 100644 resources/views/users/activities/tasks/task_updated.blade.php diff --git a/app/Http/Controllers/Projects/ActivityController.php b/app/Http/Controllers/Projects/ActivityController.php index 1317984..8996fc6 100644 --- a/app/Http/Controllers/Projects/ActivityController.php +++ b/app/Http/Controllers/Projects/ActivityController.php @@ -22,6 +22,11 @@ class ActivityController extends Controller $query->where('object_type', 'jobs'); }); + $activityQuery->orWhere(function ($query) use ($project) { + $query->whereIn('object_id', $project->tasks->pluck('id')); + $query->where('object_type', 'tasks'); + }); + $activities = $activityQuery->latest()->paginate(50); return view('projects.activities.index', compact('project', 'activities')); diff --git a/resources/lang/de/activity.php b/resources/lang/de/activity.php index 645cbcd..6d4ab47 100644 --- a/resources/lang/de/activity.php +++ b/resources/lang/de/activity.php @@ -11,4 +11,8 @@ return [ 'job_updated' => 'Job data :name updated by :user.', 'task_deleted' => 'Task deleted by :user.', ], + 'tasks' => [ + 'task_created' => 'Task created: :name new :user.', + 'task_updated' => 'Task data :name updated by :user.', + ], ]; diff --git a/resources/lang/de/job.php b/resources/lang/de/job.php index 1de5f16..a331a2b 100644 --- a/resources/lang/de/job.php +++ b/resources/lang/de/job.php @@ -48,6 +48,7 @@ return [ 'actual_start_date' => 'Actual Start Date', 'actual_end_date' => 'Actual End Date', 'updated_at' => 'Last Update', + 'position' => 'Priority', // Types 'main' => 'Haupt', diff --git a/resources/lang/en/activity.php b/resources/lang/en/activity.php index 645cbcd..6d4ab47 100644 --- a/resources/lang/en/activity.php +++ b/resources/lang/en/activity.php @@ -11,4 +11,8 @@ return [ 'job_updated' => 'Job data :name updated by :user.', 'task_deleted' => 'Task deleted by :user.', ], + 'tasks' => [ + 'task_created' => 'Task created: :name new :user.', + 'task_updated' => 'Task data :name updated by :user.', + ], ]; diff --git a/resources/lang/en/job.php b/resources/lang/en/job.php index df8c6ff..ce51fd6 100644 --- a/resources/lang/en/job.php +++ b/resources/lang/en/job.php @@ -50,6 +50,7 @@ return [ 'actual_start_date' => 'Actual Start Date', 'actual_end_date' => 'Actual End Date', 'updated_at' => 'Last Update', + 'position' => 'Priority', // Types 'main' => 'Main', diff --git a/resources/lang/id/activity.php b/resources/lang/id/activity.php index 8f996c9..aeff609 100644 --- a/resources/lang/id/activity.php +++ b/resources/lang/id/activity.php @@ -11,4 +11,8 @@ return [ 'job_updated' => 'Data job :name diubah oleh :user.', 'task_deleted' => 'Task dihapus oleh :user.', ], + 'tasks' => [ + 'task_created' => 'Input task baru: :name oleh :user.', + 'task_updated' => 'Data task :name diubah oleh :user.', + ], ]; diff --git a/resources/lang/id/job.php b/resources/lang/id/job.php index 053fb84..f6f9e2a 100644 --- a/resources/lang/id/job.php +++ b/resources/lang/id/job.php @@ -50,6 +50,7 @@ return [ 'actual_start_date' => 'Tgl Mulai Aktual', 'actual_end_date' => 'Tgl Selesai Aktual', 'updated_at' => 'Waktu Update', + 'position' => 'Prioritas', // Types 'main' => 'Utama', diff --git a/resources/views/users/activities/tasks/task_created.blade.php b/resources/views/users/activities/tasks/task_created.blade.php new file mode 100644 index 0000000..421f0c6 --- /dev/null +++ b/resources/views/users/activities/tasks/task_created.blade.php @@ -0,0 +1,11 @@ +@component('users.activities.activity_list_item') +@slot('time') + {{ $activity->created_at }} +@endslot +@slot('body') + {!! __('activity.'.$activity->object_type.'.'.$activity->type, [ + 'user' => $activity->user->name, + 'name' => $activity->object->name, + ]) !!} +@endslot +@endcomponent diff --git a/resources/views/users/activities/tasks/task_updated.blade.php b/resources/views/users/activities/tasks/task_updated.blade.php new file mode 100644 index 0000000..63b03db --- /dev/null +++ b/resources/views/users/activities/tasks/task_updated.blade.php @@ -0,0 +1,22 @@ +@component('users.activities.activity_list_item') +@slot('time') + {{ $activity->created_at }} +@endslot +@slot('body') +

    + {!! __('activity.'.$activity->object_type.'.'.$activity->type, [ + 'user' => $activity->user->name, + 'name' => $activity->object->name, + ]) !!} +

    + @php + $data = $activity->data; + @endphp + @foreach ($data['before'] as $key => $value) + @php + $afterValue = $data['after'][$key] ?? null; + @endphp +
    {{ __('job.'.$key) }}: {{ $value }} => {{ $afterValue }}
    + @endforeach +@endslot +@endcomponent