diff --git a/app/Http/Controllers/Api/ProjectsController.php b/app/Http/Controllers/Api/ProjectsController.php index ed7184b..512d4b8 100644 --- a/app/Http/Controllers/Api/ProjectsController.php +++ b/app/Http/Controllers/Api/ProjectsController.php @@ -33,7 +33,6 @@ class ProjectsController extends Controller public function jobs($id) { $project = $this->repo->requireById($id); - // $project->load('jobs.tasks'); $response = fractal() ->item($project->toArray()) ->transformWith(function ($project) { diff --git a/app/Http/Controllers/Jobs/CommentsController.php b/app/Http/Controllers/Jobs/CommentsController.php new file mode 100644 index 0000000..2fc978b --- /dev/null +++ b/app/Http/Controllers/Jobs/CommentsController.php @@ -0,0 +1,102 @@ +authorize('view-comments', $job); + + $editableComment = null; + $comments = $job->comments()->with('creator')->latest()->paginate(); + + if (request('action') == 'comment-edit' && request('comment_id') != null) { + $editableComment = Comment::find(request('comment_id')); + } + + return view('jobs.comments', compact('job', 'comments', 'editableComment')); + } + + /** + * Store a new comment in storage. + * + * @param \Illuminate\Http\Request $request + * @param \App\Entities\Projects\Job $job + * @return \Illuminate\Http\RedirectResponse + */ + public function store(Request $request, Job $job) + { + $this->authorize('comment-on', $job); + + $newComment = $request->validate([ + 'body' => 'required|string|max:255', + ]); + + $job->comments()->create([ + 'body' => $newComment['body'], + 'creator_id' => auth()->id(), + ]); + + flash(__('comment.created'), 'success'); + + return back(); + } + + /** + * Update the specified comment. + * + * @param \Illuminate\Http\Request $request + * @param \App\Entities\Projects\Job $job + * @param \App\Entities\Jobs\Comment $comment + * @return \Illuminate\Http\Response + */ + public function update(Request $request, Job $job, Comment $comment) + { + $this->authorize('update', $comment); + + $commentData = $request->validate([ + 'body' => 'required|string|max:255', + ]); + $comment->update($commentData); + flash(__('comment.updated'), 'success'); + + return redirect()->route('jobs.comments.index', [$job] + request(['page'])); + } + + /** + * Remove the specified comment. + * + * @param \App\Entities\Jobs\Comment $comment + * @return \Illuminate\Routing\Redirector + */ + public function destroy(Job $job, Comment $comment) + { + $this->authorize('delete', $comment); + + request()->validate([ + 'comment_id' => 'required|exists:comments,id', + ]); + + if (request('comment_id') == $comment->id && $comment->delete()) { + $routeParam = [$job] + request(['page']); + flash(__('comment.deleted'), 'warning'); + + return redirect()->route('jobs.comments.index', $routeParam); + } + flash(__('comment.undeleted'), 'error'); + + return back(); + } +} diff --git a/app/Http/Controllers/JobsController.php b/app/Http/Controllers/JobsController.php index f5dbe36..35fbc23 100755 --- a/app/Http/Controllers/JobsController.php +++ b/app/Http/Controllers/JobsController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Entities\Projects\Job; +use App\Entities\Projects\Comment; use App\Entities\Projects\Project; use App\Entities\Projects\JobsRepository; use App\Http\Requests\Jobs\DeleteRequest; @@ -65,6 +66,8 @@ class JobsController extends Controller $this->authorize('view', $job); $editableTask = null; + $editableComment = null; + $comments = $job->comments()->with('creator')->latest()->paginate(); if ($request->get('action') == 'task_edit' && $request->has('task_id')) { $editableTask = $this->repo->requireTaskById($request->get('task_id')); @@ -74,7 +77,11 @@ class JobsController extends Controller $editableTask = $this->repo->requireTaskById($request->get('task_id')); } - return view('jobs.show', compact('job', 'editableTask')); + if (request('action') == 'comment-edit' && request('comment_id') != null) { + $editableComment = Comment::find(request('comment_id')); + } + + return view('jobs.show', compact('job', 'editableTask', 'comments', 'editableComment')); } /** diff --git a/app/Policies/Projects/JobPolicy.php b/app/Policies/Projects/JobPolicy.php index 3806e78..5e27101 100644 --- a/app/Policies/Projects/JobPolicy.php +++ b/app/Policies/Projects/JobPolicy.php @@ -80,4 +80,31 @@ class JobPolicy { return $user->hasRole('admin'); } + + /** + * Determine whether the user can view job comments. + * + * @param \App\Entities\Users\User $user + * @param \App\Entities\Projects\Job $job + * @return bool + */ + public function viewComments(User $user, Job $job) + { + // Admin and job workers can commenting on their job. + return $user->hasRole('admin') + || ($user->hasRole('worker') && $job->worker_id == $user->id); + } + + /** + * Determine whether the user can add comment to a job. + * + * @param \App\Entities\Users\User $user + * @param \App\Entities\Projects\Job $job + * @return bool + */ + public function commentOn(User $user, Job $job) + { + // Admin and job workers can commenting on their job. + return $this->viewComments($user, $job); + } } diff --git a/resources/lang/id/comment.php b/resources/lang/id/comment.php index b20b834..95a1586 100644 --- a/resources/lang/id/comment.php +++ b/resources/lang/id/comment.php @@ -15,9 +15,9 @@ return [ 'updated' => 'Update data Komentar telah berhasil.', 'delete' => 'Hapus Komentar', 'delete_confirm' => 'Anda yakin akan menghapus Komentar ini?', - 'deleted' => 'Hapus data Komentar telah berhasil.', - 'undeleted' => 'Data Komentar gagal dihapus.', - 'undeleteable' => 'Data Komentar tidak dapat dihapus.', + 'deleted' => 'Komentar berhasil dihapus.', + 'undeleted' => 'Komentar gagal dihapus.', + 'undeleteable' => 'Komentar tidak dapat dihapus.', // Attributes 'body' => 'Komentar', diff --git a/resources/views/jobs/comments.blade.php b/resources/views/jobs/comments.blade.php new file mode 100755 index 0000000..df0c010 --- /dev/null +++ b/resources/views/jobs/comments.blade.php @@ -0,0 +1,54 @@ +@extends('layouts.job') + +@section('subtitle', __('comment.list')) + +@section('content-job') +
+
+ {{ $comments->links() }} + @include('jobs.partials.comment-section') + {{ $comments->links() }} +
+
+ +@if (Request::get('action') == 'comment-edit' && $editableComment) + +@endif +@endsection + +@section('ext_css') + +@endsection + +@section('script') + +@endsection diff --git a/resources/views/jobs/partials/comment-section.blade.php b/resources/views/jobs/partials/comment-section.blade.php new file mode 100644 index 0000000..88809c8 --- /dev/null +++ b/resources/views/jobs/partials/comment-section.blade.php @@ -0,0 +1,32 @@ +@can('comment-on', $job) +{{ Form::open(['route' => ['jobs.comments.store', $job]]) }} +
+
{!! FormField::textarea('body', ['required' => true, 'label' => false, 'placeholder' => __('comment.create_text')]) !!}
+
+ {{ Form::submit(__('comment.create'), ['class' => 'btn btn-success btn-block']) }}
+
+
+{{ Form::close() }} +@endcan +@foreach($comments as $comment) +
+ + {{ $comment->time_display }} + {{ $comment->creator->name }} + +
+ @can('update', $comment) + {{ link_to_route('jobs.comments.index', __('app.edit'), [$job, 'action' => 'comment-edit', 'comment_id' => $comment->id], ['id' => 'edit-comment-'.$comment->id, 'class' => 'small', 'title' => __('comment.edit')]) }} + @endcan + @can('delete', $comment) + {!! FormField::delete( + ['route' => ['jobs.comments.destroy', $job, $comment], 'class' => ''], + '×', + ['class' => 'btn-link', 'id' => 'delete-comment-'.$comment->id], + ['comment_id' => $comment->id, 'page' => request('page')] + ) !!} + @endcan +
+ {!! nl2br($comment->body) !!} +
+@endforeach diff --git a/resources/views/jobs/partials/job-tasks-operation.blade.php b/resources/views/jobs/partials/job-tasks-operation.blade.php index 05304f2..4887762 100644 --- a/resources/views/jobs/partials/job-tasks-operation.blade.php +++ b/resources/views/jobs/partials/job-tasks-operation.blade.php @@ -1,45 +1,19 @@ -@if (Request::has('action') == false) -@can('create', new App\Entities\Projects\Task) -{!! Form::open(['route' => ['tasks.store', $job->id]])!!} -
-

{{ __('task.create') }}

-
-
-
{!! FormField::text('name', ['label' => __('task.name')]) !!}
-
- {!! Form::label('progress', __('task.progress'), ['class' => 'control-label']) !!} - {!! Form::input('range', 'progress', 0, [ - 'min' => '0', 'max' => '100', 'step' => '10', - ]) !!} -
-
- 0% -
-
- {!! FormField::textarea('description', ['label' => __('task.description')]) !!} - {!! Form::submit(__('task.create'), ['class' => 'btn btn-primary']) !!} - {!! Form::close() !!} -
-
-@endcan -@endif - @if (Request::get('action') == 'task_edit' && $editableTask) @can('update', $editableTask) -{!! Form::model($editableTask, ['route' => ['tasks.update', $editableTask], 'method' => 'patch'])!!} +{{ Form::model($editableTask, ['route' => ['tasks.update', $editableTask], 'method' => 'patch']) }}

{{ __('task.edit') }}

{!! FormField::text('name') !!}
- {!! Form::label('progress', __('task.progress'), ['class' => 'control-label']) !!} + {{ Form::label('progress', __('task.progress'), ['class' => 'control-label']) }} - {!! Form::input('range', 'progress', null, [ + {{ Form::input('range', 'progress', null, [ 'min' => '0', 'max' => '100', 'step' => '10', - ]) !!} + ]) }}
{{ $editableTask->progress }}% @@ -50,10 +24,12 @@
{!! FormField::select('job_id', $job->project->jobs->pluck('name', 'id'), ['label' => __('task.move_to_other_job')]) !!}
+

+ {{ Form::submit(__('task.update'), ['class' => 'btn btn-warning']) }} + {{ link_to_route('jobs.show', __('app.cancel'), [$job], ['class' => 'btn btn-default']) }} +
- {!! Form::submit(__('task.update'), ['class' => 'btn btn-warning']) !!} - {{ link_to_route('jobs.show', __('app.cancel'), [$job], ['class' => 'btn btn-default']) }} - {!! Form::close() !!} + {{ Form::close() }}
@endcan diff --git a/resources/views/jobs/partials/job-tasks.blade.php b/resources/views/jobs/partials/job-tasks.blade.php index 222f206..ea906d0 100644 --- a/resources/views/jobs/partials/job-tasks.blade.php +++ b/resources/views/jobs/partials/job-tasks.blade.php @@ -80,6 +80,31 @@ @endif
+@if (Request::has('action') == false) +@can('create', new App\Entities\Projects\Task) +{{ Form::open(['route' => ['tasks.store', $job->id]]) }} +
+

{{ __('task.create') }}

+
+
+
{!! FormField::text('name', ['label' => __('task.name')]) !!}
+
+ {{ Form::label('progress', __('task.progress'), ['class' => 'control-label']) }} + {{ Form::input('range', 'progress', 0, [ + 'min' => '0', 'max' => '100', 'step' => '10', + ]) }} +
+
+ 0% +
+
+ {!! FormField::textarea('description', ['label' => __('task.description')]) !!} + {{ Form::submit(__('task.create'), ['class' => 'btn btn-primary']) }} + {{ Form::close() }} +
+
+@endcan +@endif @if (request('action') == 'sort_tasks') @section('ext_js') diff --git a/resources/views/jobs/partials/nav-tabs.blade.php b/resources/views/jobs/partials/nav-tabs.blade.php new file mode 100644 index 0000000..e1fb60e --- /dev/null +++ b/resources/views/jobs/partials/nav-tabs.blade.php @@ -0,0 +1,12 @@ + + +
diff --git a/resources/views/jobs/show.blade.php b/resources/views/jobs/show.blade.php index 051d4b2..4ff3eda 100755 --- a/resources/views/jobs/show.blade.php +++ b/resources/views/jobs/show.blade.php @@ -1,34 +1,29 @@ -@extends('layouts.app') +@extends('layouts.job') -@section('title', __('job.detail') . ' | ' . $job->name . ' | ' . $job->project->name) +@section('subtitle', __('job.detail')) -@section('content') -@include('jobs.partials.breadcrumb') +@section('action-buttons') +@can('create', new App\Entities\Projects\Job) + {!! html_link_to_route('projects.jobs.create', __('job.create'), [$job->project_id], ['class' => 'btn btn-success','icon' => 'plus']) !!} +@endcan +@can('update', $job) + {{ link_to_route('jobs.edit', __('job.edit'), [$job], ['class' => 'btn btn-warning']) }} +@endcan +@endsection +@section('content-job') -

-
- @can('create', new App\Entities\Projects\Job) - {!! html_link_to_route('projects.jobs.create', __('job.create'), [$job->project_id], ['class' => 'btn btn-success','icon' => 'plus']) !!} - @endcan - @can('update', $job) - {{ link_to_route('jobs.edit', __('job.edit'), [$job], ['class' => 'btn btn-warning']) }} - @endcan - {{ link_to_route('projects.jobs.index', __('job.back_to_index'), [$job->project_id, '#' . $job->id], ['class' => 'btn btn-default']) }} -
- {{ $job->name }} {{ __('job.detail') }} -

@include('jobs.partials.job-show') + @include('jobs.partials.job-dates')
- @include('jobs.partials.job-dates') @include('jobs.partials.job-tasks-operation') + @include('jobs.partials.job-tasks')
- @include('jobs.partials.job-tasks')
@endsection @@ -50,6 +45,7 @@ width: 8px; height: 8px; } + ul.pagination { margin-top: 0px } @endsection @@ -66,6 +62,11 @@ var ap_weight = e.currentTarget.value; $('#ap_weight').text(ap_weight); }); + + $('#commentModal').modal({ + show: true, + backdrop: 'static', + }); })(); @endsection diff --git a/resources/views/jobs/unfinished.blade.php b/resources/views/jobs/unfinished.blade.php index 498ca0a..74dfcae 100755 --- a/resources/views/jobs/unfinished.blade.php +++ b/resources/views/jobs/unfinished.blade.php @@ -6,7 +6,7 @@ -
+
{{ Form::open(['method' => 'get', 'class' => 'form-inline']) }} {!! FormField::select('project_id', $projects, ['label' => __('project.select'), 'placeholder' => __('project.all')]) !!} diff --git a/resources/views/layouts/job.blade.php b/resources/views/layouts/job.blade.php new file mode 100755 index 0000000..302dc99 --- /dev/null +++ b/resources/views/layouts/job.blade.php @@ -0,0 +1,22 @@ +@extends('layouts.app') + +@section('title') +@yield('subtitle', __('job.detail')) - {{ $job->name }} +@endsection + +@section('content') +@include('jobs.partials.breadcrumb') + +

+
+ @yield('action-buttons') + {{ link_to_route('projects.jobs.index', __('job.back_to_index'), [$job->project_id, '#' . $job->id], ['class' => 'btn btn-default']) }} +
+ {{ $job->name }} @yield('subtitle', __('job.detail')) +

+ +@include('jobs.partials.nav-tabs') + +@yield('content-job') + +@endsection diff --git a/resources/views/projects/jobs/add-from-other-project.blade.php b/resources/views/projects/jobs/add-from-other-project.blade.php index 94293a4..63a85b8 100755 --- a/resources/views/projects/jobs/add-from-other-project.blade.php +++ b/resources/views/projects/jobs/add-from-other-project.blade.php @@ -4,8 +4,8 @@ @section('action-buttons') @can('create', new App\Entities\Projects\Job) - {!! html_link_to_route('projects.jobs.create', trans('job.create'), [$project->id], ['class' => 'btn btn-success','icon' => 'plus']) !!} - {!! html_link_to_route('projects.jobs.add-from-other-project', trans('job.add_from_other_project'), [$project->id], ['class' => 'btn btn-default','icon' => 'plus']) !!} + {!! html_link_to_route('projects.jobs.create', __('job.create'), [$project->id], ['class' => 'btn btn-success','icon' => 'plus']) !!} + {!! html_link_to_route('projects.jobs.add-from-other-project', __('job.add_from_other_project'), [$project->id], ['class' => 'btn btn-default','icon' => 'plus']) !!} @endcan @endsection @@ -21,18 +21,18 @@ {{ Form::submit(__('project.show_jobs'), ['class' => 'btn btn-default btn-sm']) }} {{ Form::close() }} @if ($selectedProject) - {!! Form::open(['route' => ['projects.jobs.store-from-other-project', $project->id]]) !!} + {{ Form::open(['route' => ['projects.jobs.store-from-other-project', $project->id]]) }}
    @forelse($selectedProject->jobs as $key => $job)
    • @foreach($job->tasks as $task)
    • @endforeach @@ -45,8 +45,8 @@ @else
      {{ __('job.select_project') }}
      @endif - {!! Form::submit(__('job.add'), ['class' => 'btn btn-primary']) !!} - {!! Form::close() !!} + {{ Form::submit(__('job.add'), ['class' => 'btn btn-primary']) }} + {{ Form::close() }}