Browse Source

Merge branch 'project-issue-comments' into project-issue

pull/37/head
Nafies Luthfi 7 years ago
parent
commit
0a57772049
  1. 11
      app/Entities/Projects/Issue.php
  2. 83
      app/Http/Controllers/Issues/CommentController.php
  3. 16
      app/Http/Controllers/Projects/IssueController.php
  4. 12
      app/Policies/Projects/IssuePolicy.php
  5. 1
      app/Providers/AppServiceProvider.php
  6. 1
      resources/lang/de/app.php
  7. 1
      resources/lang/en/app.php
  8. 1
      resources/lang/id/app.php
  9. 2
      resources/lang/id/comment.php
  10. 6
      resources/views/projects/issues/index.blade.php
  11. 54
      resources/views/projects/issues/partials/comment-section.blade.php
  12. 13
      resources/views/projects/issues/show.blade.php
  13. 7
      routes/web/projects.php
  14. 106
      tests/Feature/Projects/IssueCommentsTest.php
  15. 15
      tests/Unit/Models/IssueTest.php
  16. 9
      tests/Unit/Policies/IssuePolicyTest.php

11
app/Entities/Projects/Issue.php

@ -3,6 +3,7 @@
namespace App\Entities\Projects;
use App\Entities\Users\User;
use App\Entities\Projects\Comment;
use App\Entities\Projects\Project;
use Illuminate\Database\Eloquent\Model;
@ -48,4 +49,14 @@ class Issue extends Model
{
return '<span class="badge">'.$this->status.'</span>';
}
/**
* Issue has many comments relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}

83
app/Http/Controllers/Issues/CommentController.php

@ -0,0 +1,83 @@
<?php
namespace App\Http\Controllers\Issues;
use Illuminate\Http\Request;
use App\Entities\Projects\Issue;
use App\Entities\Projects\Comment;
use App\Http\Controllers\Controller;
class CommentController extends Controller
{
/**
* Store a new comment in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Entities\Projects\Issue $issue
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request, Issue $issue)
{
$this->authorize('comment-on', $issue);
$newComment = $request->validate([
'body' => 'required|string|max:255',
]);
$issue->comments()->create([
'body' => $newComment['body'],
'creator_id' => auth()->id(),
]);
$issue->touch();
flash(__('comment.created'), 'success');
return back();
}
/**
* Update the specified comment.
*
* @param \Illuminate\Http\Request $request
* @param \App\Entities\Projects\Issue $issue
* @param \App\Entities\Projects\Comment $comment
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Issue $issue, Comment $comment)
{
$this->authorize('update', $comment);
$commentData = $request->validate([
'body' => 'required|string|max:255',
]);
$comment->update($commentData);
flash(__('comment.updated'), 'success');
return redirect()->route('projects.issues.show', [$issue->project, $issue]);
}
/**
* Remove the specified comment.
*
* @param \App\Entities\Projects\Issue $issue
* @param \\App\Entities\Projects\Comment $comment
* @return \Illuminate\Routing\Redirector
*/
public function destroy(Issue $issue, Comment $comment)
{
$this->authorize('delete', $comment);
request()->validate([
'comment_id' => 'required|exists:comments,id',
]);
if (request('comment_id') == $comment->id && $comment->delete()) {
flash(__('comment.deleted'), 'warning');
return redirect()->route('projects.issues.show', [$issue->project, $issue]);
}
flash(__('comment.undeleted'), 'error');
return back();
}
}

16
app/Http/Controllers/Projects/IssueController.php

@ -5,6 +5,7 @@ namespace App\Http\Controllers\Projects;
use App\Entities\Users\User;
use Illuminate\Http\Request;
use App\Entities\Projects\Issue;
use App\Entities\Projects\Comment;
use App\Entities\Projects\Project;
use App\Entities\Projects\Priority;
use App\Http\Controllers\Controller;
@ -14,7 +15,11 @@ class IssueController extends Controller
{
public function index(Project $project)
{
$issues = $project->issues()->with(['pic', 'creator'])->get();
$issues = $project->issues()
->orderBy('updated_at', 'desc')
->with(['pic', 'creator'])
->withCount(['comments'])
->get();
return view('projects.issues.index', compact('project', 'issues'));
}
@ -50,12 +55,19 @@ class IssueController extends Controller
public function show(Project $project, Issue $issue)
{
$editableComment = null;
$priorities = Priority::toArray();
$statuses = IssueStatus::toArray();
$users = User::pluck('name', 'id');
$comments = $issue->comments()->with('creator')->get();
if (request('action') == 'comment-edit' && request('comment_id') != null) {
$editableComment = Comment::find(request('comment_id'));
}
return view('projects.issues.show', compact(
'project', 'issue', 'users', 'statuses', 'priorities'
'project', 'issue', 'users', 'statuses', 'priorities', 'comments',
'editableComment'
));
}

12
app/Policies/Projects/IssuePolicy.php

@ -14,4 +14,16 @@ class IssuePolicy
{
return true;
}
/**
* Determine whether the user can add comment to an issue.
*
* @param \App\Entities\Users\User $user
* @param \App\Entities\Projects\Issue $issue
* @return bool
*/
public function commentOn(User $user, Issue $issue)
{
return true;
}
}

1
app/Providers/AppServiceProvider.php

@ -22,6 +22,7 @@ class AppServiceProvider extends ServiceProvider
Relation::morphMap([
'projects' => 'App\Entities\Projects\Project',
'issues' => 'App\Entities\Projects\Issue',
'jobs' => 'App\Entities\Projects\Job',
]);
}

1
resources/lang/de/app.php

@ -17,6 +17,7 @@ return [
'total' => 'gesamt',
'count' => 'Summe',
'remark' => 'Remark',
'last_update' => 'Last Update',
// Action
'add' => 'Hinzufügen',

1
resources/lang/en/app.php

@ -17,6 +17,7 @@ return [
'total' => 'Total',
'count' => 'Count',
'remark' => 'Remark',
'last_update' => 'Last Update',
// Action
'add' => 'Add',

1
resources/lang/id/app.php

@ -17,6 +17,7 @@ return [
'total' => 'Total',
'count' => 'Jumlah',
'remark' => 'Keterangan',
'last_update' => 'Update',
// Action
'add' => 'Tambah',

2
resources/lang/id/comment.php

@ -12,7 +12,7 @@ return [
'created' => 'Input Komentar berhasil.',
'edit' => 'Edit Komentar',
'update' => 'Update Komentar',
'updated' => 'Update data Komentar telah berhasil.',
'updated' => 'Update Komentar berhasil.',
'delete' => 'Hapus Komentar',
'delete_confirm' => 'Anda yakin akan menghapus Komentar ini?',
'deleted' => 'Komentar berhasil dihapus.',

6
resources/views/projects/issues/index.blade.php

@ -19,8 +19,10 @@
<th>{{ __('issue.title') }}</th>
<th>{{ __('issue.priority') }}</th>
<th>{{ __('app.status') }}</th>
<th class="text-center">{{ __('comment.comment') }}</th>
<th>{{ __('issue.pic') }}</th>
<th>{{ __('issue.creator') }}</th>
<th>{{ __('app.last_update') }}</th>
<th class="text-center">{{ __('app.action') }}</th>
</thead>
<tbody>
@ -33,8 +35,10 @@
<td>{{ $issue->title }}</td>
<td>{!! $issue->priority_label !!}</td>
<td>{!! $issue->status_label !!}</td>
<td class="text-center">{{ $issue->comments_count }}</td>
<td>{{ $issue->pic->name }}</td>
<td>{{ $issue->creator->name }}</td>
<td>{{ $issue->updated_at->diffForHumans() }}</td>
<td class="text-center">
{{ link_to_route(
'projects.issues.show',
@ -45,7 +49,7 @@
</td>
</tr>
@empty
<tr><td colspan="6">{{ __('issue.empty') }}</td></tr>
<tr><td colspan="7">{{ __('issue.empty') }}</td></tr>
@endforelse
</tbody>
</table>

54
resources/views/projects/issues/partials/comment-section.blade.php

@ -0,0 +1,54 @@
@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('projects.issues.show', __('app.edit'), [$project, $issue, 'action' => 'comment-edit', 'comment_id' => $comment->id], ['id' => 'edit-comment-'.$comment->id, 'class' => 'small', 'title' => __('comment.edit')]) }}
@endcan
@can('delete', $comment)
{!! FormField::delete(
['route' => ['issues.comments.destroy', $issue, $comment], 'class' => ''],
'&times;',
['class' => 'btn-link', 'id' => 'delete-comment-'.$comment->id],
['comment_id' => $comment->id]
) !!}
@endcan
</div>
{!! nl2br($comment->body) !!}
</div>
@endforeach
@can('comment-on', $issue)
{{ Form::open(['route' => ['issues.comments.store', $issue]]) }}
{!! FormField::textarea('body', ['required' => true, 'label' => false, 'placeholder' => __('comment.create_text')]) !!}
{{ Form::submit(__('comment.create'), ['class' => 'btn btn-success pull-right']) }}
{{ Form::close() }}
<div class="clearfix"></div><br>
@endcan
@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('projects.issues.show', '&times;', [$issue->project, $issue], ['class' => 'close']) }}
<h4 class="modal-title">{{ __('comment.edit') }}</h4>
</div>
{!! Form::model($editableComment, ['route' => ['issues.comments.update', $issue, $editableComment],'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('projects.issues.show', __('app.cancel'), [$project->issue, $issue] + request(['page']), ['class' => 'btn btn-default']) }}
</div>
{!! Form::close() !!}
</div>
</div>
</div>
@endif

13
resources/views/projects/issues/show.blade.php

@ -32,6 +32,8 @@
{{ link_to_route('projects.issues.index', __('issue.back_to_index'), [$project], ['class' => 'btn btn-default pull-right']) }}
</div>
</div>
<hr>
@include('projects.issues.partials.comment-section')
</div>
<div class="col-md-6">
{{ Form::model($issue, ['route' => ['issues.options.update', $issue], 'method' => 'patch']) }}
@ -50,3 +52,14 @@
</div>
</div>
@endsection
@section('script')
<script>
(function () {
$('#commentModal').modal({
show: true,
backdrop: 'static',
});
})();
</script>
@endsection

7
routes/web/projects.php

@ -105,3 +105,10 @@ Route::group(['middleware' => ['auth']], function () {
* Issue Options Routes
*/
Route::patch('issues/{issue}/options', 'Issues\OptionController@update')->name('issues.options.update');
/**
* Issue Comments Routes
*/
Route::post('issues/{issue}/comments', 'Issues\CommentController@store')->name('issues.comments.store');
Route::patch('issues/{issue}/comments/{comment}', 'Issues\CommentController@update')->name('issues.comments.update');
Route::delete('issues/{issue}/comments/{comment}', 'Issues\CommentController@destroy')->name('issues.comments.destroy');

106
tests/Feature/Projects/IssueCommentsTest.php

@ -0,0 +1,106 @@
<?php
namespace Tests\Feature\Projects;
use Tests\TestCase;
use App\Entities\Projects\Issue;
use App\Entities\Projects\Comment;
use Illuminate\Foundation\Testing\RefreshDatabase;
class IssueCommentsTest extends TestCase
{
use RefreshDatabase;
/** @test */
public function user_can_view_issue_comments()
{
$this->adminUserSigningIn();
$issue = factory(Issue::class)->create();
$comment = factory(Comment::class)->create([
'commentable_type' => 'issues',
'commentable_id' => $issue->id,
'body' => 'This is issue comment.',
]);
$this->visitRoute('projects.issues.show', [$issue->project, $issue]);
$this->seeText('This is issue comment.');
}
/** @test */
public function admin_can_add_comment_to_an_issue()
{
$admin = $this->adminUserSigningIn();
$issue = factory(Issue::class)->create();
$this->visitRoute('projects.issues.show', [$issue->project, $issue]);
$this->submitForm(__('comment.create'), [
'body' => 'First comment.',
]);
$this->seePageIs(route('projects.issues.show', [$issue->project, $issue]));
$this->see(__('comment.created'));
$this->seeInDatabase('comments', [
'commentable_type' => 'issues',
'commentable_id' => $issue->id,
'body' => 'First comment.',
'creator_id' => $admin->id,
]);
}
/** @test */
public function user_can_edit_an_issue_comment()
{
$this->adminUserSigningIn();
$issue = factory(Issue::class)->create();
$comment = factory(Comment::class)->create([
'commentable_type' => 'issues',
'commentable_id' => $issue->id,
'body' => 'This is issue comment.',
]);
$this->visitRoute('projects.issues.show', [$issue->project, $issue]);
$this->seeElement('a', ['id' => 'edit-comment-'.$comment->id]);
$this->click('edit-comment-'.$comment->id);
$this->seeRouteIs('projects.issues.show', [$issue->project, $issue, 'action' => 'comment-edit', 'comment_id' => $comment->id]);
$this->submitForm(__('comment.update'), [
'body' => 'Edited comment.',
]);
$this->seePageIs(route('projects.issues.show', [$issue->project, $issue]));
$this->see(__('comment.updated'));
$this->seeInDatabase('comments', [
'id' => $comment->id,
'commentable_type' => 'issues',
'commentable_id' => $issue->id,
'body' => 'Edited comment.',
]);
}
/** @test */
public function user_can_delete_an_issue_comment()
{
$this->adminUserSigningIn();
$issue = factory(Issue::class)->create();
$comment = factory(Comment::class)->create([
'commentable_type' => 'issues',
'commentable_id' => $issue->id,
'body' => 'This is issue comment.',
]);
$this->visitRoute('projects.issues.show', [$issue->project, $issue]);
$this->seeElement('button', ['id' => 'delete-comment-'.$comment->id]);
$this->press('delete-comment-'.$comment->id);
$this->seePageIs(route('projects.issues.show', [$issue->project, $issue]));
$this->see(__('comment.deleted'));
$this->dontSeeInDatabase('comments', [
'id' => $comment->id,
]);
}
}

15
tests/Unit/Models/IssueTest.php

@ -5,7 +5,9 @@ namespace Tests\Unit\Models;
use Tests\TestCase;
use App\Entities\Users\User;
use App\Entities\Projects\Issue;
use App\Entities\Projects\Comment;
use App\Entities\Projects\Project;
use Illuminate\Support\Collection;
use App\Entities\Projects\Priority;
use Illuminate\Foundation\Testing\RefreshDatabase;
@ -81,4 +83,17 @@ class IssueTest extends TestCase
$this->assertEquals('<span class="label label-'.$colorClass.'">'.$issue->priority.'</span>', $issue->priority_label);
}
/** @test */
public function an_issue_has_many_comments_relation()
{
$issue = factory(Issue::class)->create();
$comment = factory(Comment::class)->create([
'commentable_type' => 'issues',
'commentable_id' => $issue->id,
]);
$this->assertInstanceOf(Collection::class, $issue->comments);
$this->assertInstanceOf(Comment::class, $issue->comments->first());
}
}

9
tests/Unit/Policies/IssuePolicyTest.php

@ -22,4 +22,13 @@ class IssuePolicyTest extends TestCase
$this->assertTrue($admin->can('create', new Issue()));
}
/** @test */
public function admin_can_add_comment_to_an_issue()
{
$admin = $this->createUser('admin');
$issue = factory(Issue::class)->create();
$this->assertTrue($admin->can('comment-on', $issue));
}
}
Loading…
Cancel
Save