diff --git a/app/Entities/Projects/Job.php b/app/Entities/Projects/Job.php index cb87fb5..e658f17 100755 --- a/app/Entities/Projects/Job.php +++ b/app/Entities/Projects/Job.php @@ -17,6 +17,17 @@ class Job extends Model use PresentableTrait; /** + * The event map for the model. + * + * @var array + */ + protected $dispatchesEvents = [ + 'created' => 'App\Events\Jobs\Created', + 'updated' => 'App\Events\Jobs\Updated', + 'deleted' => 'App\Events\Jobs\Deleted', + ]; + + /** * @var \App\Entities\Projects\JobPresenter */ protected $presenter = JobPresenter::class; diff --git a/app/Entities/Projects/Project.php b/app/Entities/Projects/Project.php index 9834a57..47b56e8 100755 --- a/app/Entities/Projects/Project.php +++ b/app/Entities/Projects/Project.php @@ -18,6 +18,15 @@ use Laracasts\Presenter\PresentableTrait; class Project extends Model { use PresentableTrait; + /** + * The event map for the model. + * + * @var array + */ + protected $dispatchesEvents = [ + 'created' => 'App\Events\Projects\Created', + 'updated' => 'App\Events\Projects\Updated', + ]; /** * @var \App\Entities\Projects\ProjectPresenter diff --git a/app/Entities/Projects/Task.php b/app/Entities/Projects/Task.php index cb5e78a..c9167bf 100755 --- a/app/Entities/Projects/Task.php +++ b/app/Entities/Projects/Task.php @@ -6,6 +6,17 @@ use Illuminate\Database\Eloquent\Model; class Task extends Model { + /** + * The event map for the model. + * + * @var array + */ + protected $dispatchesEvents = [ + 'created' => 'App\Events\Tasks\Created', + 'updated' => 'App\Events\Tasks\Updated', + 'deleted' => 'App\Events\Tasks\Deleted', + ]; + protected $guarded = ['id', 'created_at', 'updated_at']; protected $touches = ['job']; diff --git a/app/Entities/Users/Activity.php b/app/Entities/Users/Activity.php new file mode 100644 index 0000000..bc0b04d --- /dev/null +++ b/app/Entities/Users/Activity.php @@ -0,0 +1,24 @@ + 'array']; + + public function user() + { + return $this->belongsTo(User::class)->withDefault(['name' => 'n/a']); + } + + public function object() + { + return $this->morphTo(); + } +} 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/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/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/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 new file mode 100644 index 0000000..d634086 --- /dev/null +++ b/app/Events/Projects/Updated.php @@ -0,0 +1,15 @@ +project = $project; + } +} 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/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/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/Http/Controllers/Projects/ActivityController.php b/app/Http/Controllers/Projects/ActivityController.php new file mode 100644 index 0000000..8996fc6 --- /dev/null +++ b/app/Http/Controllers/Projects/ActivityController.php @@ -0,0 +1,34 @@ +where(function ($query) use ($project) { + $query->where('object_id', $project->id); + $query->where('object_type', 'projects'); + }); + + $activityQuery->orWhere(function ($query) use ($project) { + $query->whereIn('object_id', $project->jobs->pluck('id')); + $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/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/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/Listeners/Jobs/LogJobUpdateActivity.php b/app/Listeners/Jobs/LogJobUpdateActivity.php new file mode 100644 index 0000000..4bb0c2d --- /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' => [ + '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/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/LogProjectJobDeletionActivity.php b/app/Listeners/Projects/LogProjectJobDeletionActivity.php new file mode 100644 index 0000000..7a07102 --- /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' => [ + '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 new file mode 100644 index 0000000..e597b33 --- /dev/null +++ b/app/Listeners/Projects/LogProjectUpdateActivity.php @@ -0,0 +1,52 @@ +project; + $originalProject = $project->getOriginal(); + $attributeChanges = $project->getChanges(); + $attributeKeys = array_keys($project->getChanges()); + + $activityEntry = [ + 'type' => 'project_updated', + 'parent_id' => null, + 'user_id' => auth()->id(), + 'object_id' => $project->id, + 'object_type' => 'projects', + 'data' => [ + '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/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/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/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/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 907cff8..7f0ce06 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -13,8 +13,29 @@ class EventServiceProvider extends ServiceProvider * @var array */ protected $listen = [ - 'App\Events\SomeEvent' => [ - 'App\Listeners\EventListener', + 'App\Events\Projects\Created' => [ + 'App\Listeners\Projects\LogProjectCreationActivity', + ], + 'App\Events\Projects\Updated' => [ + 'App\Listeners\Projects\LogProjectUpdateActivity', + ], + 'App\Events\Jobs\Created' => [ + 'App\Listeners\Jobs\LogJobCreationActivity', + ], + 'App\Events\Jobs\Updated' => [ + 'App\Listeners\Jobs\LogJobUpdateActivity', + ], + 'App\Events\Jobs\Deleted' => [ + 'App\Listeners\Projects\LogProjectJobDeletionActivity', + ], + 'App\Events\Tasks\Created' => [ + 'App\Listeners\Tasks\LogTaskCreationActivity', + ], + 'App\Events\Tasks\Updated' => [ + 'App\Listeners\Tasks\LogTaskUpdateActivity', + ], + 'App\Events\Tasks\Deleted' => [ + 'App\Listeners\Jobs\LogJobTaskDeletionActivity', ], ]; 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..9d040de --- /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('user_activities')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('user_activities'); + } +} diff --git a/resources/lang/de/activity.php b/resources/lang/de/activity.php new file mode 100644 index 0000000..6d4ab47 --- /dev/null +++ b/resources/lang/de/activity.php @@ -0,0 +1,18 @@ + [ + 'project_created' => 'Project created: :name new :user.', + '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.', + ], + '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 feb078a..a331a2b 100644 --- a/resources/lang/de/job.php +++ b/resources/lang/de/job.php @@ -47,6 +47,8 @@ return [ 'target_end_date' => 'Target End Date', '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/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..6d4ab47 --- /dev/null +++ b/resources/lang/en/activity.php @@ -0,0 +1,18 @@ + [ + 'project_created' => 'Project created: :name new :user.', + '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.', + ], + '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 3415cab..ce51fd6 100644 --- a/resources/lang/en/job.php +++ b/resources/lang/en/job.php @@ -49,6 +49,8 @@ return [ 'target_end_date' => 'Target End Date', '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/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..aeff609 --- /dev/null +++ b/resources/lang/id/activity.php @@ -0,0 +1,18 @@ + [ + 'project_created' => 'Input project baru: :name oleh :user.', + '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.', + ], + '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 4835805..f6f9e2a 100644 --- a/resources/lang/id/job.php +++ b/resources/lang/id/job.php @@ -49,6 +49,8 @@ return [ 'target_end_date' => 'Target Tgl Selesai', '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/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') + +
+ {!! __('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 ++ {!! __('activity.'.$activity->object_type.'.'.$activity->type, [ + 'user' => $activity->user->name, + ]) !!} +
++ {!! __('activity.'.$activity->object_type.'.'.$activity->type, [ + 'user' => $activity->user->name, + ]) !!} +
++ {!! __('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 ++ {!! __('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 +