Browse Source
Merge pull request #72 from nafiesl/user_activities
Merge pull request #72 from nafiesl/user_activities
Add User Activity Logs on a Projectmaster
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1048 additions and 2 deletions
-
11app/Entities/Projects/Job.php
-
9app/Entities/Projects/Project.php
-
11app/Entities/Projects/Task.php
-
24app/Entities/Users/Activity.php
-
15app/Events/Jobs/Created.php
-
15app/Events/Jobs/Deleted.php
-
15app/Events/Jobs/Updated.php
-
15app/Events/Projects/Created.php
-
15app/Events/Projects/Updated.php
-
15app/Events/Tasks/Created.php
-
15app/Events/Tasks/Deleted.php
-
15app/Events/Tasks/Updated.php
-
34app/Http/Controllers/Projects/ActivityController.php
-
25app/Listeners/Jobs/LogJobCreationActivity.php
-
30app/Listeners/Jobs/LogJobTaskDeletionActivity.php
-
52app/Listeners/Jobs/LogJobUpdateActivity.php
-
24app/Listeners/Projects/LogProjectCreationActivity.php
-
30app/Listeners/Projects/LogProjectJobDeletionActivity.php
-
52app/Listeners/Projects/LogProjectUpdateActivity.php
-
25app/Listeners/Tasks/LogTaskCreationActivity.php
-
52app/Listeners/Tasks/LogTaskUpdateActivity.php
-
1app/Providers/AppServiceProvider.php
-
25app/Providers/EventServiceProvider.php
-
40database/migrations/2021_03_05_221708_create_user_activities_table.php
-
18resources/lang/de/activity.php
-
2resources/lang/de/job.php
-
2resources/lang/de/project.php
-
18resources/lang/en/activity.php
-
2resources/lang/en/job.php
-
2resources/lang/en/project.php
-
18resources/lang/id/activity.php
-
2resources/lang/id/job.php
-
2resources/lang/id/project.php
-
22resources/views/projects/activities/index.blade.php
-
3resources/views/projects/partials/nav-tabs.blade.php
-
4resources/views/users/activities/activity_list_item.blade.php
-
11resources/views/users/activities/jobs/job_created.blade.php
-
28resources/views/users/activities/jobs/job_updated.blade.php
-
15resources/views/users/activities/jobs/task_deleted.blade.php
-
15resources/views/users/activities/projects/job_deleted.blade.php
-
11resources/views/users/activities/projects/project_created.blade.php
-
22resources/views/users/activities/projects/project_updated.blade.php
-
11resources/views/users/activities/tasks/task_created.blade.php
-
22resources/views/users/activities/tasks/task_updated.blade.php
-
5routes/web/projects.php
-
250tests/Unit/Models/ActivityTest.php
@ -0,0 +1,24 @@ |
|||
<?php |
|||
|
|||
namespace App\Entities\Users; |
|||
|
|||
use Illuminate\Database\Eloquent\Model; |
|||
|
|||
class Activity extends Model |
|||
{ |
|||
protected $table = 'user_activities'; |
|||
|
|||
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(); |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
<?php |
|||
|
|||
namespace App\Events\Jobs; |
|||
|
|||
use App\Entities\Projects\Job; |
|||
|
|||
class Created |
|||
{ |
|||
public $job; |
|||
|
|||
public function __construct(Job $job) |
|||
{ |
|||
$this->job = $job; |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
<?php |
|||
|
|||
namespace App\Events\Jobs; |
|||
|
|||
use App\Entities\Projects\Job; |
|||
|
|||
class Deleted |
|||
{ |
|||
public $job; |
|||
|
|||
public function __construct(Job $job) |
|||
{ |
|||
$this->job = $job; |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
<?php |
|||
|
|||
namespace App\Events\Jobs; |
|||
|
|||
use App\Entities\Projects\Job; |
|||
|
|||
class Updated |
|||
{ |
|||
public $job; |
|||
|
|||
public function __construct(Job $job) |
|||
{ |
|||
$this->job = $job; |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
<?php |
|||
|
|||
namespace App\Events\Projects; |
|||
|
|||
use App\Entities\Projects\Project; |
|||
|
|||
class Created |
|||
{ |
|||
public $project; |
|||
|
|||
public function __construct(Project $project) |
|||
{ |
|||
$this->project = $project; |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
<?php |
|||
|
|||
namespace App\Events\Projects; |
|||
|
|||
use App\Entities\Projects\Project; |
|||
|
|||
class Updated |
|||
{ |
|||
public $project; |
|||
|
|||
public function __construct(Project $project) |
|||
{ |
|||
$this->project = $project; |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
<?php |
|||
|
|||
namespace App\Events\Tasks; |
|||
|
|||
use App\Entities\Projects\Task; |
|||
|
|||
class Created |
|||
{ |
|||
public $task; |
|||
|
|||
public function __construct(Task $task) |
|||
{ |
|||
$this->task = $task; |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
<?php |
|||
|
|||
namespace App\Events\Tasks; |
|||
|
|||
use App\Entities\Projects\Task; |
|||
|
|||
class Deleted |
|||
{ |
|||
public $task; |
|||
|
|||
public function __construct(Task $task) |
|||
{ |
|||
$this->task = $task; |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
<?php |
|||
|
|||
namespace App\Events\Tasks; |
|||
|
|||
use App\Entities\Projects\Task; |
|||
|
|||
class Updated |
|||
{ |
|||
public $task; |
|||
|
|||
public function __construct(Task $task) |
|||
{ |
|||
$this->task = $task; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
<?php |
|||
|
|||
namespace App\Http\Controllers\Projects; |
|||
|
|||
use App\Entities\Projects\Project; |
|||
use App\Entities\Users\Activity; |
|||
use App\Http\Controllers\Controller; |
|||
|
|||
class ActivityController extends Controller |
|||
{ |
|||
public function index(Project $project) |
|||
{ |
|||
$activityQuery = Activity::query(); |
|||
|
|||
$activityQuery->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')); |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
<?php |
|||
|
|||
namespace App\Listeners\Jobs; |
|||
|
|||
use App\Entities\Users\Activity; |
|||
use App\Events\Jobs\Created; |
|||
|
|||
class LogJobCreationActivity |
|||
{ |
|||
public function handle(Created $event) |
|||
{ |
|||
$job = $event->job; |
|||
|
|||
$activityEntry = [ |
|||
'type' => 'job_created', |
|||
'parent_id' => null, |
|||
'user_id' => auth()->id(), |
|||
'object_id' => $job->id, |
|||
'object_type' => 'jobs', |
|||
'data' => null, |
|||
]; |
|||
|
|||
Activity::create($activityEntry); |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
<?php |
|||
|
|||
namespace App\Listeners\Jobs; |
|||
|
|||
use App\Entities\Users\Activity; |
|||
use App\Events\Tasks\Deleted; |
|||
|
|||
class LogJobTaskDeletionActivity |
|||
{ |
|||
public function handle(Deleted $event) |
|||
{ |
|||
$task = $event->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); |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
<?php |
|||
|
|||
namespace App\Listeners\Jobs; |
|||
|
|||
use App\Entities\Users\Activity; |
|||
use App\Events\Jobs\Updated; |
|||
|
|||
class LogJobUpdateActivity |
|||
{ |
|||
public function handle(Updated $event) |
|||
{ |
|||
$job = $event->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; |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
<?php |
|||
|
|||
namespace App\Listeners\Projects; |
|||
|
|||
use App\Entities\Users\Activity; |
|||
use App\Events\Projects\Created; |
|||
|
|||
class LogProjectCreationActivity |
|||
{ |
|||
public function handle(Created $event) |
|||
{ |
|||
$project = $event->project; |
|||
|
|||
$activityEntry = [ |
|||
'type' => 'project_created', |
|||
'parent_id' => null, |
|||
'user_id' => auth()->id(), |
|||
'object_id' => $project->id, |
|||
'object_type' => 'projects', |
|||
]; |
|||
|
|||
Activity::create($activityEntry); |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
<?php |
|||
|
|||
namespace App\Listeners\Projects; |
|||
|
|||
use App\Entities\Users\Activity; |
|||
use App\Events\Jobs\Deleted; |
|||
|
|||
class LogProjectJobDeletionActivity |
|||
{ |
|||
public function handle(Deleted $event) |
|||
{ |
|||
$job = $event->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); |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
<?php |
|||
|
|||
namespace App\Listeners\Projects; |
|||
|
|||
use App\Entities\Users\Activity; |
|||
use App\Events\Projects\Updated; |
|||
|
|||
class LogProjectUpdateActivity |
|||
{ |
|||
public function handle(Updated $event) |
|||
{ |
|||
$project = $event->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; |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
<?php |
|||
|
|||
namespace App\Listeners\Tasks; |
|||
|
|||
use App\Entities\Users\Activity; |
|||
use App\Events\Tasks\Created; |
|||
|
|||
class LogTaskCreationActivity |
|||
{ |
|||
public function handle(Created $event) |
|||
{ |
|||
$task = $event->task; |
|||
|
|||
$activityEntry = [ |
|||
'type' => 'task_created', |
|||
'parent_id' => null, |
|||
'user_id' => auth()->id(), |
|||
'object_id' => $task->id, |
|||
'object_type' => 'tasks', |
|||
'data' => null, |
|||
]; |
|||
|
|||
Activity::create($activityEntry); |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
<?php |
|||
|
|||
namespace App\Listeners\Tasks; |
|||
|
|||
use App\Entities\Users\Activity; |
|||
use App\Events\Tasks\Updated; |
|||
|
|||
class LogTaskUpdateActivity |
|||
{ |
|||
public function handle(Updated $event) |
|||
{ |
|||
$task = $event->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; |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
<?php |
|||
|
|||
use Illuminate\Database\Migrations\Migration; |
|||
use Illuminate\Database\Schema\Blueprint; |
|||
use Illuminate\Support\Facades\Schema; |
|||
|
|||
class CreateUserActivitiesTable extends Migration |
|||
{ |
|||
/** |
|||
* Run the migrations. |
|||
* |
|||
* @return void |
|||
*/ |
|||
public function up() |
|||
{ |
|||
Schema::create('user_activities', function (Blueprint $table) { |
|||
$table->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'); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
<?php |
|||
|
|||
return [ |
|||
'projects' => [ |
|||
'project_created' => 'Project created: <strong>:name</strong> new <strong>:user</strong>.', |
|||
'project_updated' => 'Project data <strong>:name</strong> updated by <strong>:user</strong>.', |
|||
'job_deleted' => 'Job deleted by <strong>:user</strong>.', |
|||
], |
|||
'jobs' => [ |
|||
'job_created' => 'Job created: <strong>:name</strong> new <strong>:user</strong>.', |
|||
'job_updated' => 'Job data <strong>:name</strong> updated by <strong>:user</strong>.', |
|||
'task_deleted' => 'Task deleted by <strong>:user</strong>.', |
|||
], |
|||
'tasks' => [ |
|||
'task_created' => 'Task created: <strong>:name</strong> new <strong>:user</strong>.', |
|||
'task_updated' => 'Task data <strong>:name</strong> updated by <strong>:user</strong>.', |
|||
], |
|||
]; |
|||
@ -0,0 +1,18 @@ |
|||
<?php |
|||
|
|||
return [ |
|||
'projects' => [ |
|||
'project_created' => 'Project created: <strong>:name</strong> new <strong>:user</strong>.', |
|||
'project_updated' => 'Project data <strong>:name</strong> updated by <strong>:user</strong>.', |
|||
'job_deleted' => 'Job deleted by <strong>:user</strong>.', |
|||
], |
|||
'jobs' => [ |
|||
'job_created' => 'Job created: <strong>:name</strong> new <strong>:user</strong>.', |
|||
'job_updated' => 'Job data <strong>:name</strong> updated by <strong>:user</strong>.', |
|||
'task_deleted' => 'Task deleted by <strong>:user</strong>.', |
|||
], |
|||
'tasks' => [ |
|||
'task_created' => 'Task created: <strong>:name</strong> new <strong>:user</strong>.', |
|||
'task_updated' => 'Task data <strong>:name</strong> updated by <strong>:user</strong>.', |
|||
], |
|||
]; |
|||
@ -0,0 +1,18 @@ |
|||
<?php |
|||
|
|||
return [ |
|||
'projects' => [ |
|||
'project_created' => 'Input project baru: <strong>:name</strong> oleh <strong>:user</strong>.', |
|||
'project_updated' => 'Data project <strong>:name</strong> diubah oleh <strong>:user</strong>.', |
|||
'job_deleted' => 'Job dihapus oleh <strong>:user</strong>.', |
|||
], |
|||
'jobs' => [ |
|||
'job_created' => 'Input job baru: <strong>:name</strong> oleh <strong>:user</strong>.', |
|||
'job_updated' => 'Data job <strong>:name</strong> diubah oleh <strong>:user</strong>.', |
|||
'task_deleted' => 'Task dihapus oleh <strong>:user</strong>.', |
|||
], |
|||
'tasks' => [ |
|||
'task_created' => 'Input task baru: <strong>:name</strong> oleh <strong>:user</strong>.', |
|||
'task_updated' => 'Data task <strong>:name</strong> diubah oleh <strong>:user</strong>.', |
|||
], |
|||
]; |
|||
@ -0,0 +1,22 @@ |
|||
@extends('layouts.project') |
|||
|
|||
@section('subtitle', __('project.activities')) |
|||
|
|||
@section('content-project') |
|||
|
|||
<div class="row"> |
|||
<div class="col-md-8 col-md-offset-2"> |
|||
{{ $activities->links() }} |
|||
<ul class="list-group"> |
|||
@foreach($activities as $activity) |
|||
@includeWhen( |
|||
view()->exists('users.activities.'.$activity->object_type.'.'.$activity->type), |
|||
'users.activities.'.$activity->object_type.'.'.$activity->type |
|||
) |
|||
@endforeach |
|||
</ul> |
|||
{{ $activities->links() }} |
|||
</div> |
|||
</div> |
|||
|
|||
@endsection |
|||
@ -0,0 +1,4 @@ |
|||
<li class="list-group-item"> |
|||
<span class="label label-info pull-right">{{ $time }}</span> |
|||
{{ $body }} |
|||
</li> |
|||
@ -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 |
|||
@ -0,0 +1,28 @@ |
|||
@component('users.activities.activity_list_item') |
|||
@slot('time') |
|||
{{ $activity->created_at }} |
|||
@endslot |
|||
@slot('body') |
|||
<p> |
|||
{!! __('activity.'.$activity->object_type.'.'.$activity->type, [ |
|||
'user' => $activity->user->name, |
|||
'name' => $activity->object->name, |
|||
]) !!} |
|||
</p> |
|||
@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 |
|||
<div>{{ __('job.'.$key) }}: {{ $value }} => {{ $afterValue }}</div> |
|||
@endforeach |
|||
@endslot |
|||
@endcomponent |
|||
@ -0,0 +1,15 @@ |
|||
@component('users.activities.activity_list_item') |
|||
@slot('time') |
|||
{{ $activity->created_at }} |
|||
@endslot |
|||
@slot('body') |
|||
<p> |
|||
{!! __('activity.'.$activity->object_type.'.'.$activity->type, [ |
|||
'user' => $activity->user->name, |
|||
]) !!} |
|||
</p> |
|||
<div>{{ __('task.name') }}: {{ $activity->data['name'] }}</div> |
|||
<div>{{ __('task.description') }}: {{ $activity->data['description'] }}</div> |
|||
<div>{{ __('task.progress') }}: {{ $activity->data['progress'] }} %</div> |
|||
@endslot |
|||
@endcomponent |
|||
@ -0,0 +1,15 @@ |
|||
@component('users.activities.activity_list_item') |
|||
@slot('time') |
|||
{{ $activity->created_at }} |
|||
@endslot |
|||
@slot('body') |
|||
<p> |
|||
{!! __('activity.'.$activity->object_type.'.'.$activity->type, [ |
|||
'user' => $activity->user->name, |
|||
]) !!} |
|||
</p> |
|||
<div>{{ __('job.name') }}: {{ $activity->data['name'] }}</div> |
|||
<div>{{ __('job.description') }}: {{ $activity->data['description'] }}</div> |
|||
<div>{{ __('job.price') }}: {{ format_money($activity->data['price']) }}</div> |
|||
@endslot |
|||
@endcomponent |
|||
@ -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 |
|||
@ -0,0 +1,22 @@ |
|||
@component('users.activities.activity_list_item') |
|||
@slot('time') |
|||
{{ $activity->created_at }} |
|||
@endslot |
|||
@slot('body') |
|||
<p> |
|||
{!! __('activity.'.$activity->object_type.'.'.$activity->type, [ |
|||
'user' => $activity->user->name, |
|||
'name' => $activity->object->name, |
|||
]) !!} |
|||
</p> |
|||
@php |
|||
$data = $activity->data; |
|||
@endphp |
|||
@foreach ($data['before'] as $key => $value) |
|||
@php |
|||
$afterValue = $data['after'][$key] ?? null; |
|||
@endphp |
|||
<div>{{ __('project.'.$key) }}: {{ $value }} => {{ $afterValue }}</div> |
|||
@endforeach |
|||
@endslot |
|||
@endcomponent |
|||
@ -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 |
|||
@ -0,0 +1,22 @@ |
|||
@component('users.activities.activity_list_item') |
|||
@slot('time') |
|||
{{ $activity->created_at }} |
|||
@endslot |
|||
@slot('body') |
|||
<p> |
|||
{!! __('activity.'.$activity->object_type.'.'.$activity->type, [ |
|||
'user' => $activity->user->name, |
|||
'name' => $activity->object->name, |
|||
]) !!} |
|||
</p> |
|||
@php |
|||
$data = $activity->data; |
|||
@endphp |
|||
@foreach ($data['before'] as $key => $value) |
|||
@php |
|||
$afterValue = $data['after'][$key] ?? null; |
|||
@endphp |
|||
<div>{{ __('job.'.$key) }}: {{ $value }} => {{ $afterValue }}</div> |
|||
@endforeach |
|||
@endslot |
|||
@endcomponent |
|||
@ -0,0 +1,250 @@ |
|||
<?php |
|||
|
|||
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; |
|||
|
|||
class ActivityTest extends TestCase |
|||
{ |
|||
use RefreshDatabase; |
|||
|
|||
/** @test */ |
|||
public function it_creates_project_creation_activities() |
|||
{ |
|||
$admin = $this->adminUserSigningIn(); |
|||
$project = factory(Project::class)->create(); |
|||
|
|||
$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']); |
|||
|
|||
$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, |
|||
]), |
|||
]); |
|||
} |
|||
|
|||
/** @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, |
|||
]); |
|||
} |
|||
|
|||
/** @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, |
|||
]), |
|||
]); |
|||
} |
|||
|
|||
/** @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, |
|||
]), |
|||
]); |
|||
} |
|||
|
|||
/** @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, |
|||
]); |
|||
} |
|||
|
|||
/** @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, |
|||
]), |
|||
]); |
|||
} |
|||
|
|||
/** @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(); |
|||
$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, |
|||
]), |
|||
]); |
|||
} |
|||
|
|||
/** @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); |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue