Browse Source

Merge pull request #13 from nafiesl/job-layout

Add Job Comment and Update Job View Layout
pull/15/head
Nafies Luthfi 7 years ago
committed by GitHub
parent
commit
80b3eec3e6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/Http/Controllers/Api/ProjectsController.php
  2. 102
      app/Http/Controllers/Jobs/CommentsController.php
  3. 9
      app/Http/Controllers/JobsController.php
  4. 27
      app/Policies/Projects/JobPolicy.php
  5. 6
      resources/lang/id/comment.php
  6. 54
      resources/views/jobs/comments.blade.php
  7. 32
      resources/views/jobs/partials/comment-section.blade.php
  8. 42
      resources/views/jobs/partials/job-tasks-operation.blade.php
  9. 25
      resources/views/jobs/partials/job-tasks.blade.php
  10. 12
      resources/views/jobs/partials/nav-tabs.blade.php
  11. 37
      resources/views/jobs/show.blade.php
  12. 2
      resources/views/jobs/unfinished.blade.php
  13. 22
      resources/views/layouts/job.blade.php
  14. 18
      resources/views/projects/jobs/add-from-other-project.blade.php
  15. 15
      resources/views/projects/jobs/create.blade.php
  16. 52
      resources/views/projects/jobs/index.blade.php
  17. 11
      routes/web/projects.php
  18. 104
      tests/Feature/Projects/JobCommentsTest.php
  19. 30
      tests/Unit/Policies/JobPolicyTest.php

1
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) {

102
app/Http/Controllers/Jobs/CommentsController.php

@ -0,0 +1,102 @@
<?php
namespace App\Http\Controllers\Jobs;
use Illuminate\Http\Request;
use App\Entities\Projects\Job;
use App\Entities\Projects\Comment;
use App\Http\Controllers\Controller;
class CommentsController extends Controller
{
/**
* Display a listing of the job comments.
*
* @param \App\Entities\Projects\Job $job
* @return \Illuminate\View\View
*/
public function index(Job $job)
{
$this->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();
}
}

9
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'));
}
/**

27
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);
}
}

6
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',

54
resources/views/jobs/comments.blade.php

@ -0,0 +1,54 @@
@extends('layouts.job')
@section('subtitle', __('comment.list'))
@section('content-job')
<div class="row">
<div class="col-md-8 col-md-offset-1">
{{ $comments->links() }}
@include('jobs.partials.comment-section')
{{ $comments->links() }}
</div>
</div>
@if (Request::get('action') == 'comment-edit' && $editableComment)
<div id="commentModal" class="modal" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
{{ link_to_route('jobs.comments.index', '&times;', [$job] + request(['page']), ['class' => 'close']) }}
<h4 class="modal-title">{{ __('comment.edit') }}</h4>
</div>
{!! Form::model($editableComment, ['route' => ['jobs.comments.update', $job, $editableComment->id],'method' => 'patch']) !!}
<div class="modal-body">
{!! FormField::textarea('body', ['label' => __('comment.body')]) !!}
{{ Form::hidden('page', request('page')) }}
</div>
<div class="modal-footer">
{!! Form::submit(__('comment.update'), ['class' => 'btn btn-success']) !!}
{{ link_to_route('jobs.comments.index', __('app.cancel'), [$job] + request(['page']), ['class' => 'btn btn-default']) }}
</div>
{!! Form::close() !!}
</div>
</div>
</div>
@endif
@endsection
@section('ext_css')
<style>
ul.pagination { margin-top: 0px }
</style>
@endsection
@section('script')
<script>
(function () {
$('#commentModal').modal({
show: true,
backdrop: 'static',
});
})();
</script>
@endsection

32
resources/views/jobs/partials/comment-section.blade.php

@ -0,0 +1,32 @@
@can('comment-on', $job)
{{ Form::open(['route' => ['jobs.comments.store', $job]]) }}
<div class="row">
<div class="col-md-9">{!! FormField::textarea('body', ['required' => true, 'label' => false, 'placeholder' => __('comment.create_text')]) !!}</div>
<div class="col-md-3">
{{ Form::submit(__('comment.create'), ['class' => 'btn btn-success btn-block']) }}<br>
</div>
</div>
{{ Form::close() }}
@endcan
@foreach($comments as $comment)
<div class="alert alert-warning">
<legend style="font-size: 14px;margin-bottom: 10px;">
<span class="label label-default pull-right">{{ $comment->time_display }}</span>
<strong>{{ $comment->creator->name }}</strong>
</legend>
<div class="pull-right">
@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' => ''],
'&times;',
['class' => 'btn-link', 'id' => 'delete-comment-'.$comment->id],
['comment_id' => $comment->id, 'page' => request('page')]
) !!}
@endcan
</div>
{!! nl2br($comment->body) !!}
</div>
@endforeach

42
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]])!!}
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">{{ __('task.create') }}</h3></div>
<div class="panel-body">
<div class="row">
<div class="col-sm-6">{!! FormField::text('name', ['label' => __('task.name')]) !!}</div>
<div class="col-md-4">
{!! Form::label('progress', __('task.progress'), ['class' => 'control-label']) !!}
{!! Form::input('range', 'progress', 0, [
'min' => '0', 'max' => '100', 'step' => '10',
]) !!}
</div>
<div class="col-md-2" style="font-size: 28px; margin-top: 15px;">
<strong id="ap_weight">0</strong>%
</div>
</div>
{!! FormField::textarea('description', ['label' => __('task.description')]) !!}
{!! Form::submit(__('task.create'), ['class' => 'btn btn-primary']) !!}
{!! Form::close() !!}
</div>
</div>
@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']) }}
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">{{ __('task.edit') }}</h3></div>
<div class="panel-body">
<div class="row">
<div class="col-sm-6">{!! FormField::text('name') !!}</div>
<div class="col-md-4">
{!! 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',
]) !!}
]) }}
</div>
<div class="col-md-2" style="font-size: 28px; margin-top: 15px;">
<strong id="ap_weight">{{ $editableTask->progress }}</strong>%
@ -50,10 +24,12 @@
<div class="col-md-6">
{!! FormField::select('job_id', $job->project->jobs->pluck('name', 'id'), ['label' => __('task.move_to_other_job')]) !!}
</div>
<div class="col-md-6 text-right"><br>
{{ Form::submit(__('task.update'), ['class' => 'btn btn-warning']) }}
{{ link_to_route('jobs.show', __('app.cancel'), [$job], ['class' => 'btn btn-default']) }}
</div>
</div>
{!! Form::submit(__('task.update'), ['class' => 'btn btn-warning']) !!}
{{ link_to_route('jobs.show', __('app.cancel'), [$job], ['class' => 'btn btn-default']) }}
{!! Form::close() !!}
{{ Form::close() }}
</div>
</div>
@endcan

25
resources/views/jobs/partials/job-tasks.blade.php

@ -80,6 +80,31 @@
@endif
</div>
@if (Request::has('action') == false)
@can('create', new App\Entities\Projects\Task)
{{ Form::open(['route' => ['tasks.store', $job->id]]) }}
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">{{ __('task.create') }}</h3></div>
<div class="panel-body">
<div class="row">
<div class="col-sm-6">{!! FormField::text('name', ['label' => __('task.name')]) !!}</div>
<div class="col-md-4">
{{ Form::label('progress', __('task.progress'), ['class' => 'control-label']) }}
{{ Form::input('range', 'progress', 0, [
'min' => '0', 'max' => '100', 'step' => '10',
]) }}
</div>
<div class="col-md-2" style="font-size: 28px; margin-top: 15px;">
<strong id="ap_weight">0</strong>%
</div>
</div>
{!! FormField::textarea('description', ['label' => __('task.description')]) !!}
{{ Form::submit(__('task.create'), ['class' => 'btn btn-primary']) }}
{{ Form::close() }}
</div>
</div>
@endcan
@endif
@if (request('action') == 'sort_tasks')
@section('ext_js')

12
resources/views/jobs/partials/nav-tabs.blade.php

@ -0,0 +1,12 @@
<!-- Nav tabs -->
<ul class="nav nav-tabs">
<li class="{{ Request::segment(3) == null ? 'active' : '' }}">
{!! link_to_route('jobs.show', __('job.detail'), $job) !!}
</li>
@can('view-comments', $job)
<li class="{{ Request::segment(3) == 'comments' ? 'active' : '' }}">
{!! link_to_route('jobs.comments.index', __('comment.list').' ('.$job->comments->count().')', $job) !!}
</li>
@endcan
</ul>
<br>

37
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')
<h1 class="page-header">
<div class="pull-right">
@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']) }}
</div>
{{ $job->name }} <small>{{ __('job.detail') }}</small>
</h1>
<div class="row">
<div class="col-md-5">
@include('jobs.partials.job-show')
@include('jobs.partials.job-dates')
</div>
<div class="col-sm-7">
@include('jobs.partials.job-dates')
@include('jobs.partials.job-tasks-operation')
@include('jobs.partials.job-tasks')
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-2">
@include('jobs.partials.job-tasks')
</div>
</div>
@endsection
@ -50,6 +45,7 @@
width: 8px;
height: 8px;
}
ul.pagination { margin-top: 0px }
</style>
@endsection
@ -66,6 +62,11 @@
var ap_weight = e.currentTarget.value;
$('#ap_weight').text(ap_weight);
});
$('#commentModal').modal({
show: true,
backdrop: 'static',
});
})();
</script>
@endsection

2
resources/views/jobs/unfinished.blade.php

@ -6,7 +6,7 @@
<ul class="breadcrumb hidden-print"><li>{{ __('job.on_progress') }}</li></ul>
<div class="panel panel-default">
<div class="panel panel-default table-responsive">
<div class="panel-heading">
{{ Form::open(['method' => 'get', 'class' => 'form-inline']) }}
{!! FormField::select('project_id', $projects, ['label' => __('project.select'), 'placeholder' => __('project.all')]) !!}

22
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')
<h1 class="page-header">
<div class="pull-right">
@yield('action-buttons')
{{ link_to_route('projects.jobs.index', __('job.back_to_index'), [$job->project_id, '#' . $job->id], ['class' => 'btn btn-default']) }}
</div>
{{ $job->name }} <small>@yield('subtitle', __('job.detail'))</small>
</h1>
@include('jobs.partials.nav-tabs')
@yield('content-job')
@endsection

18
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]]) }}
<ul class="list-unstyled">
@forelse($selectedProject->jobs as $key => $job)
<li>
<label for="job_id_{{ $job->id }}">
{!! Form::checkbox('job_ids['.$job->id.']', $job->id, null, ['id' => 'job_id_'.$job->id]) !!}
{{ Form::checkbox('job_ids['.$job->id.']', $job->id, null, ['id' => 'job_id_'.$job->id]) }}
{{ $job->name }}</label>
<ul style="list-style-type:none">
@foreach($job->tasks as $task)
<li>
<label for="{{ $job->id }}_task_id_{{ $task->id }}" style="font-weight:normal">
{!! Form::checkbox($job->id.'_task_ids['.$task->id.']', $task->id, null, ['id' => $job->id.'_task_id_'.$task->id]) !!}
{{ Form::checkbox($job->id.'_task_ids['.$task->id.']', $task->id, null, ['id' => $job->id.'_task_id_'.$task->id]) }}
{{ $task->name }}</label>
</li>
@endforeach
@ -45,8 +45,8 @@
@else
<div class="alert alert-info">{{ __('job.select_project') }}</div>
@endif
{!! Form::submit(__('job.add'), ['class' => 'btn btn-primary']) !!}
{!! Form::close() !!}
{{ Form::submit(__('job.add'), ['class' => 'btn btn-primary']) }}
{{ Form::close() }}
</div>
<div class="panel-footer">
@ -58,7 +58,7 @@
@endsection
@section('ext_css')
{!! Html::style(url('assets/css/plugins/select2.min.css')) !!}
{{ Html::style(url('assets/css/plugins/select2.min.css')) }}
<style>
.select2-selection.select2-selection--single {
border-radius: 0;
@ -68,7 +68,7 @@
@endsection
@section('script')
{!! Html::script(url('assets/js/plugins/select2.min.js')) !!}
{{ Html::script(url('assets/js/plugins/select2.min.js')) }}
<script>
(function() {
$('select[name=project_id]').select2();

15
resources/views/projects/jobs/create.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], ['class' => 'btn btn-success', 'icon' => 'plus']) !!}
{!! html_link_to_route('projects.jobs.add-from-other-project', __('job.add_from_other_project'), [$project], ['class' => 'btn btn-default', 'icon' => 'plus']) !!}
@endcan
@endsection
@ -13,7 +13,7 @@
<div class="row">
<div class="col-sm-6 col-sm-offset-2">
{!! Form::open(['route' => ['projects.jobs.store', $project->id]]) !!}
{{ Form::open(['route' => ['projects.jobs.store', $project]]) }}
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">{{ __('job.create') }}</h3></div>
<div class="panel-body">
@ -41,11 +41,11 @@
</div>
<div class="panel-footer">
{!! Form::submit(__('job.create'), ['class' => 'btn btn-primary']) !!}
{{ Form::submit(__('job.create'), ['class' => 'btn btn-primary']) }}
{{ link_to_route('projects.jobs.index', __('app.cancel'), [$project], ['class' => 'btn btn-default']) }}
</div>
</div>
{!! Form::close() !!}
{{ Form::close() }}
</div>
</div>
@endsection
@ -54,11 +54,8 @@
{!! Html::style(url('assets/css/plugins/jquery.datetimepicker.css')) !!}
@endsection
@section('ext_js')
{!! Html::script(url('assets/js/plugins/jquery.datetimepicker.js')) !!}
@endsection
@section('script')
{!! Html::script(url('assets/js/plugins/jquery.datetimepicker.js')) !!}
<script>
(function() {
$('.date-select').datetimepicker({

52
resources/views/projects/jobs/index.blade.php

@ -1,19 +1,19 @@
@extends('layouts.project')
@section('subtitle', trans('project.jobs'))
@section('subtitle', __('project.jobs'))
@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], ['class' => 'btn btn-success','icon' => 'plus']) !!}
{!! html_link_to_route('projects.jobs.add-from-other-project', __('job.add_from_other_project'), [$project], ['class' => 'btn btn-default','icon' => 'plus']) !!}
@endcan
@endsection
@section('content-project')
@if ($jobs->isEmpty())
<p>{{ trans('project.no_jobs') }},
{{ link_to_route('projects.jobs.create', trans('job.create'), [$project->id]) }}.
<p>{{ __('project.no_jobs') }},
{{ link_to_route('projects.jobs.create', __('job.create'), [$project]) }}.
</p>
@else
@ -24,20 +24,20 @@
<div class="pull-right">
@can('update', $project)
@if (request('action') == 'sort_jobs')
{{ link_to_route('projects.jobs.index', trans('app.done'), [$project->id], ['class' => 'btn btn-default btn-xs pull-right', 'style' => 'margin-top: -2px; margin-left: 6px; margin-right: -8px']) }}
{{ link_to_route('projects.jobs.index', __('app.done'), [$project], ['class' => 'btn btn-default btn-xs pull-right', 'style' => 'margin-top: -2px; margin-left: 6px; margin-right: -8px']) }}
@else
{{ link_to_route('projects.jobs.index', trans('project.sort_jobs'), [$project->id, 'action' => 'sort_jobs', '#project-jobs'], ['class' => 'btn btn-default btn-xs pull-right', 'style' => 'margin-top: -2px; margin-left: 6px; margin-right: -8px']) }}
{{ link_to_route('projects.jobs.index', __('project.sort_jobs'), [$project, 'action' => 'sort_jobs', '#project-jobs'], ['class' => 'btn btn-default btn-xs pull-right', 'style' => 'margin-top: -2px; margin-left: 6px; margin-right: -8px']) }}
@can('see-pricings', $project)
{!! link_to_route('projects.jobs-export', trans('project.jobs_list_export_html'), [$project->id, 'html', 'job_type' => $key], ['class' => '','target' => '_blank']) !!} |
{!! link_to_route('projects.job-progress-export', trans('project.jobs_progress_export_html'), [$project->id, 'html', 'job_type' => $key], ['class' => '','target' => '_blank']) !!}
{!! link_to_route('projects.jobs-export', __('project.jobs_list_export_html'), [$project, 'html', 'job_type' => $key], ['class' => '','target' => '_blank']) !!} |
{!! link_to_route('projects.job-progress-export', __('project.jobs_progress_export_html'), [$project, 'html', 'job_type' => $key], ['class' => '','target' => '_blank']) !!}
@endcan
@endif
@endcan
</div>
<h3 class="panel-title">
{{ $key == 1 ? trans('project.jobs') : trans('project.additional_jobs') }}
{{ $key == 1 ? __('project.jobs') : __('project.additional_jobs') }}
@if (request('action') == 'sort_jobs')
<em>: {{ trans('project.sort_jobs') }}</em>
<em>: {{ __('project.sort_jobs') }}</em>
@endif
</h3>
</div>
@ -52,16 +52,16 @@
@else
<table class="table table-condensed table-striped">
<thead>
<th>{{ trans('app.table_no') }}</th>
<th>{{ trans('job.name') }}</th>
<th class="text-center">{{ trans('job.tasks_count') }}</th>
<th class="text-center">{{ trans('job.progress') }}</th>
<th>{{ __('app.table_no') }}</th>
<th>{{ __('job.name') }}</th>
<th class="text-center">{{ __('job.tasks_count') }}</th>
<th class="text-center">{{ __('job.progress') }}</th>
@can('see-pricings', new App\Entities\Projects\Job)
<th class="text-right">{{ trans('job.price') }}</th>
<th class="text-right">{{ __('job.price') }}</th>
@endcan
{{-- <th>{{ trans('job.worker') }}</th> --}}
<th class="text-center">{{ trans('time.updated_at') }}</th>
<th class="text-center">{{ trans('app.action') }}</th>
{{-- <th>{{ __('job.worker') }}</th> --}}
<th class="text-center">{{ __('time.updated_at') }}</th>
<th class="text-center">{{ __('app.action') }}</th>
</thead>
<tbody>
@forelse($groupedJobs as $key => $job)
@ -86,19 +86,21 @@
@can('see-pricings', $job)
<td class="text-right">{{ formatRp($job->price) }}</td>
@endcan
{{-- <td>{{ $job->worker->name }}</td> --}}
<td class="text-center">{{ $job->updated_at->diffForHumans() }}</td>
<td class="text-center">
{{ $job->updated_at->diffForHumans() }} <br>
{{ __('job.worker') }} : {{ $job->worker->name }}
</td>
<td class="text-center">
@can('view', $job)
{!! html_link_to_route('jobs.show', '',[$job->id],['icon' => 'search', 'title' => __('job.show'), 'class' => 'btn btn-info btn-xs','id' => 'show-job-' . $job->id]) !!}
@endcan
@can('edit', $job)
{!! html_link_to_route('jobs.edit', '',[$job->id],['icon' => 'edit', 'title' => trans('job.edit'), 'class' => 'btn btn-warning btn-xs']) !!}
{!! html_link_to_route('jobs.edit', '',[$job->id],['icon' => 'edit', 'title' => __('job.edit'), 'class' => 'btn btn-warning btn-xs']) !!}
@endcan
</td>
</tr>
@empty
<tr><td colspan="7">{{ trans('job.empty') }}</td></tr>
<tr><td colspan="7">{{ __('job.empty') }}</td></tr>
@endforelse
</tbody>
<tfoot>
@ -115,9 +117,9 @@
<th colspan="2">
@can('update', $project)
@if (request('action') == 'sort_jobs')
{{ link_to_route('projects.jobs.index', trans('app.done'), [$project->id], ['class' => 'btn btn-default btn-xs pull-right']) }}
{{ link_to_route('projects.jobs.index', __('app.done'), [$project->id], ['class' => 'btn btn-default btn-xs pull-right']) }}
@else
{{ link_to_route('projects.jobs.index', trans('project.sort_jobs'), [$project->id, 'action' => 'sort_jobs', '#project-jobs'], ['class' => 'btn btn-default btn-xs pull-right']) }}
{{ link_to_route('projects.jobs.index', __('project.sort_jobs'), [$project->id, 'action' => 'sort_jobs', '#project-jobs'], ['class' => 'btn btn-default btn-xs pull-right']) }}
@endif
@endcan
</th>

11
routes/web/projects.php

@ -69,9 +69,6 @@ Route::group(['middleware' => ['auth']], function () {
*/
Route::get('jobs', ['as' => 'jobs.index', 'uses' => 'JobsController@index']);
Route::get('jobs/{job}', ['as' => 'jobs.show', 'uses' => 'JobsController@show']);
});
Route::group(['middleware' => ['auth']], function () {
/*
* Job Actions Routes
@ -81,4 +78,12 @@ Route::group(['middleware' => ['auth']], function () {
Route::get('jobs/{job}/delete', ['as' => 'jobs.delete', 'uses' => 'JobsController@delete']);
Route::delete('jobs/{job}', ['as' => 'jobs.destroy', 'uses' => 'JobsController@destroy']);
Route::post('jobs/{id}/tasks-reorder', ['as' => 'jobs.tasks-reorder', 'uses' => 'JobsController@tasksReorder']);
/*
* Project Comments Routes
*/
Route::get('jobs/{job}/comments', 'Jobs\CommentsController@index')->name('jobs.comments.index');
Route::post('jobs/{job}/comments', 'Jobs\CommentsController@store')->name('jobs.comments.store');
Route::patch('jobs/{job}/comments/{comment}', 'Jobs\CommentsController@update')->name('jobs.comments.update');
Route::delete('jobs/{job}/comments/{comment}', 'Jobs\CommentsController@destroy')->name('jobs.comments.destroy');
});

104
tests/Feature/Projects/JobCommentsTest.php

@ -0,0 +1,104 @@
<?php
namespace Tests\Feature\Projects;
use Tests\TestCase;
use App\Entities\Projects\Job;
use App\Entities\Projects\Comment;
class JobCommentsTest extends TestCase
{
/** @test */
public function user_can_view_job_comments()
{
$this->adminUserSigningIn();
$job = factory(Job::class)->create();
$comment = factory(Comment::class)->create([
'commentable_type' => 'jobs',
'commentable_id' => $job->id,
'body' => 'This is job comment.',
]);
$this->visitRoute('jobs.comments.index', $job);
$this->seeRouteIs('jobs.comments.index', $job);
$this->seeText('This is job comment.');
}
/** @test */
public function admin_can_add_comment_to_a_job()
{
$admin = $this->adminUserSigningIn();
$job = factory(Job::class)->create();
$this->visitRoute('jobs.comments.index', $job);
$this->submitForm(__('comment.create'), [
'body' => 'Komentar pertama.',
]);
$this->seePageIs(route('jobs.comments.index', $job));
$this->see(__('comment.created'));
$this->seeInDatabase('comments', [
'commentable_type' => 'jobs',
'commentable_id' => $job->id,
'body' => 'Komentar pertama.',
'creator_id' => $admin->id,
]);
}
/** @test */
public function user_can_edit_comment()
{
$this->adminUserSigningIn();
$job = factory(Job::class)->create();
$comment = factory(Comment::class)->create([
'commentable_type' => 'jobs',
'commentable_id' => $job->id,
'body' => 'This is job comment.',
]);
$this->visitRoute('jobs.comments.index', $job);
$this->seeElement('a', ['id' => 'edit-comment-'.$comment->id]);
$this->click('edit-comment-'.$comment->id);
$this->seeRouteIs('jobs.comments.index', [$job, 'action' => 'comment-edit', 'comment_id' => $comment->id]);
$this->submitForm(__('comment.update'), [
'body' => 'Komentar pertama.',
]);
$this->seePageIs(route('jobs.comments.index', $job));
$this->see(__('comment.updated'));
$this->seeInDatabase('comments', [
'id' => $comment->id,
'commentable_type' => 'jobs',
'commentable_id' => $job->id,
'body' => 'Komentar pertama.',
]);
}
/** @test */
public function user_can_delete_comment()
{
$this->adminUserSigningIn();
$job = factory(Job::class)->create();
$comment = factory(Comment::class)->create([
'commentable_type' => 'jobs',
'commentable_id' => $job->id,
'body' => 'This is job comment.',
]);
$this->visitRoute('jobs.comments.index', $job);
$this->seeElement('button', ['id' => 'delete-comment-'.$comment->id]);
$this->press('delete-comment-'.$comment->id);
$this->seePageIs(route('jobs.comments.index', $job));
$this->see(__('comment.deleted'));
$this->dontSeeInDatabase('comments', [
'id' => $comment->id,
]);
}
}

30
tests/Unit/Policies/JobPolicyTest.php

@ -2,8 +2,8 @@
namespace Tests\Unit\Policies;
use Tests\TestCase;
use App\Entities\Projects\Job;
use Tests\TestCase as TestCase;
class JobPolicyTest extends TestCase
{
@ -81,4 +81,32 @@ class JobPolicyTest extends TestCase
$this->assertTrue($admin->can('see-pricings', $job));
$this->assertFalse($worker->can('see-pricings', $job));
}
/** @test */
public function admin_and_worker_view_job_comment_list()
{
$admin = $this->createUser('admin');
$worker = $this->createUser('worker');
$job = factory(Job::class)->create([
'worker_id' => $worker->id,
]);
$this->assertTrue($admin->can('view-comments', $job));
$this->assertTrue($worker->can('view-comments', $job));
}
/** @test */
public function admin_and_job_workers_can_add_comment_to_job()
{
$admin = $this->createUser('admin');
$worker = $this->createUser('worker');
$job = factory(Job::class)->create([
'worker_id' => $worker->id,
]);
$this->assertTrue($admin->can('comment-on', $job));
$this->assertTrue($worker->can('comment-on', $job));
}
}
Loading…
Cancel
Save