Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add view completed exercises list #385

Merged
merged 13 commits into from Jul 7, 2020
10 changes: 9 additions & 1 deletion app/Http/Controllers/MyController.php
Expand Up @@ -4,7 +4,9 @@

use App\Chapter;
use App\User;
use App\Exercise;
use Auth;
use Illuminate\Pagination\LengthAwarePaginator;

class MyController extends Controller
{
Expand All @@ -19,12 +21,18 @@ public function __invoke()
$chapters = Chapter::with('children', 'exercises')->get();
$mainChapters = $chapters->where('parent_id', null);
$completedExercises = $user->completedExercises->keyBy('exercise_id');
$lastSolutions = $user->solutions()
->with('exercise')
->groupBy('exercise_id')
->orderBy('exercise_id')
->paginate(10);

return view('my.index', compact(
'user',
'chapters',
'mainChapters',
'completedExercises'
'completedExercises',
'lastSolutions'
));
}
}
24 changes: 21 additions & 3 deletions app/Http/Controllers/SolutionController.php
Expand Up @@ -18,14 +18,15 @@ public function __construct()
public function store(Request $request, User $user)
{
$validatedData = $request->validate([
'exercise_id' => 'required|integer|min:1',
'content' => 'required|string|min:1'
]);

$solution = new Solution($validatedData);
$solution->user()->associate($user);
$exercise = Exercise::findOrFail($request->get('exercise_id'));

$solution = new Solution($validatedData);
$solution->user()->associate($user)->exercise()->associate($exercise);


if ($solution->save()) {
activity()
->performedOn($solution)
Expand All @@ -42,6 +43,23 @@ public function store(Request $request, User $user)
return back();
}

public function show(User $user, Solution $solution)
{

$currentExercise = $solution->exercise;

$solutionsListForCurrentExercise = $solution->exercise
->solutions()
->where('user_id', $user->id)
->get();

return view('solution.show', compact(
'currentExercise',
'solutionsListForCurrentExercise',
'user'
));
}

public function destroy(User $user, Solution $solution)
{
if ($solution->delete()) {
Expand Down
7 changes: 6 additions & 1 deletion app/Policies/SolutionPolicy.php
Expand Up @@ -10,12 +10,17 @@
class SolutionPolicy
{
use HandlesAuthorization;

public function create(User $user)
{
return Auth::check();
}

public function view(User $user, Solution $solution)
{
return $user->id == $solution->user_id;
}

public function delete(User $user, Solution $solution)
{
return $user->id == $solution->user_id;
Expand Down
1 change: 0 additions & 1 deletion app/Solution.php
Expand Up @@ -7,7 +7,6 @@
class Solution extends Model
{
protected $fillable = [
'exercise_id',
'content'
];

Expand Down
4 changes: 4 additions & 0 deletions resources/sass/app.scss
Expand Up @@ -14,3 +14,7 @@
.x-z-index-0 {
z-index: 0 !important;
}

.nav-tabs .nav-link.active {
AlexeyShobanov marked this conversation as resolved.
Show resolved Hide resolved
background-color: #fff !important;
}
8 changes: 4 additions & 4 deletions resources/views/layouts/app.blade.php
Expand Up @@ -25,20 +25,20 @@
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>

<body class="d-flex flex-column">
<body class="d-flex flex-column min-vh-100">
@if (config('app.env') == 'production')
@include('layouts.deps._gtm_body')
@endif
<div class="d-flex flex-column min-vh-100">
<div class="flex-grow-1">
@include('layouts._nav')
<main class="my-5">
<main class="my-4">
<div class="container mb-3">
@include('flash::message')
@yield('content')
</div>
</main>
@include('layouts._footer')
</div>
@include('layouts._footer')

</body>

Expand Down
56 changes: 14 additions & 42 deletions resources/views/my/index.blade.php
@@ -1,49 +1,21 @@
@extends('layouts.app')

@section('content')
<div class="row">
<div class="col">
<h3>{{ __('layout.nav.my_progress') }}</h3>
</div>
<div class="col text-right">
<h5><a href="{{ route('users.show', $user) }}">{{ $user->name }}</a></h5>
<div class="d-flex flex-wrap justify-content-between mb-4">
<div class="h3 flex-grow-1 flex-shrink-1">{{ __('layout.nav.my_progress') }}</h3></div>
AlexeyShobanov marked this conversation as resolved.
Show resolved Hide resolved
<div class="h5 flex-grow-1 flex-shrink-1">
<a href="{{ route('users.show', $user) }}">{{ $user->name }}</a>
</div>
</div>
<div class="row mt-2">
<div class="col-12 col-md-4 mb-2">
<div class="nav nav-pills flex-column sticky-top x-z-index-0" role="tablist">
@foreach($mainChapters as $mainChapter)
<a class="nav-item nav-link {{ $mainChapter->path === '1' ? 'active' : '' }}"
id="subChapters{{ $mainChapter->id }}-tab"
href="#subChapters{{ $mainChapter->id }}"
data-toggle="tab"
role="tab"
aria-controls="subChapters{{ $mainChapter->id }}"
aria-selected="{{ $mainChapter->path === '1' ? 'true' : 'false' }}">
{{ $mainChapter->path }} {{ getChapterName($mainChapter->path) }}
</a>
@endforeach
</div>
</div>
<div class="col-12 col-md-8">
<div class="card pl-2 pr-3">
{!! Form::open()->route('users.chapters.store', [$user]) !!}
<div class="tab-content">
@foreach($mainChapters as $mainChapter)
<div
class="tab-pane card-body {{ $mainChapter->path === '1' ? 'active' : '' }}"
id="subChapters{{ $mainChapter->id }}"
role="tabpanel"
aria-labelledby="subChapters{{ $mainChapter->id }}-tab">
@include('partials.chapter_form_element', ['chapter' => $mainChapter])
</div>
@endforeach
<div class="form-group text-right">
{!! Form::submit(__('layout.common.save')) !!}
</div>
</div>
{!! Form::close() !!}
</div>
</div>
<nav>
<div class="nav nav-tabs" id="nav-tab" role="tablist">
AlexeyShobanov marked this conversation as resolved.
Show resolved Hide resolved
<a class="nav-item nav-link active" id="nav-chapters-tab" data-toggle="tab" href="#nav-chapters" role="tab" aria-controls="nav-home" aria-selected="true">Chapters</a>
<a class="nav-item nav-link" id="nav-exercises-tab" data-toggle="tab" href="#nav-exercises" role="tab" aria-controls="nav-profile" aria-selected="false">Exercises</a>
</div>
</nav>
<div class="tab-content bg-white" id="nav-tabContent">
<div class="tab-pane fade show active" id="nav-chapters" role="tabpanel">@include('my.progresses._my_chapters')</div>
<div class="tab-pane fade" id="nav-exercises" role="tabpanel">@include('my.progresses._my_exercises')</div>
</div>

@endsection
36 changes: 36 additions & 0 deletions resources/views/my/progresses/_my_chapters.blade.php
@@ -0,0 +1,36 @@

<div class="d-flex border border-top-0 flex-wrap">
<div class="flex-grow-1 flex-lg-grow-0 flex-shrink-0 border-right">
AlexeyShobanov marked this conversation as resolved.
Show resolved Hide resolved
<div class="nav nav-pills flex-column sticky-top x-z-index-0 m-2 pt-2" role="tablist">
@foreach($mainChapters as $mainChapter)
<a class="nav-item nav-link {{ $mainChapter->path === '1' ? 'active' : '' }}"
id="subChapters{{ $mainChapter->id }}-tab"
href="#subChapters{{ $mainChapter->id }}"
data-toggle="tab"
role="tab"
aria-controls="subChapters{{ $mainChapter->id }}"
aria-selected="{{ $mainChapter->path === '1' ? 'true' : 'false' }}">
{{ $mainChapter->path }} {{ getChapterName($mainChapter->path) }}
</a>
@endforeach
</div>
</div>
<div class="flex-grow-1 flex-shrink-1">
AlexeyShobanov marked this conversation as resolved.
Show resolved Hide resolved
{!! Form::open()->route('users.chapters.store', [$user]) !!}
<div class="tab-content m-4">
@foreach($mainChapters as $mainChapter)
<div
class="tab-pane {{ $mainChapter->path === '1' ? 'active' : '' }}"
id="subChapters{{ $mainChapter->id }}"
role="tabpanel"
aria-labelledby="subChapters{{ $mainChapter->id }}-tab">
@include('partials.chapter_form_element', ['chapter' => $mainChapter])
</div>
@endforeach
<div class="text-right">
{!! Form::submit(__('layout.common.save')) !!}
</div>
</div>
{!! Form::close() !!}
</div>
</div>
25 changes: 25 additions & 0 deletions resources/views/my/progresses/_my_exercises.blade.php
@@ -0,0 +1,25 @@
<div class="tab-content">
<div class="table-responsive border border-top-0 p-4">
AlexeyShobanov marked this conversation as resolved.
Show resolved Hide resolved
<table class="table">
<thead>
<tr>
<th class="border-top h5">Exercises</th>
<th class="border-top h5">Solutions</th>
</tr>
@foreach ($lastSolutions as $solution)
<tr>
<td>
<a href="{{ route('exercises.show', $solution['exercise']) }}">
Exercise {{ $solution->exercise->path }} {{ getExerciseTitle($solution->exercise) }} (Chapter {{ $solution->exercise->chapter->path }})
</td>
<td>
<a href="{{ route('users.solutions.show', [$user, $solution]) }}">
See details
</td>
</tr>
@endforeach
</thead>
</table>
</div>
{{ $lastSolutions->withQueryString()->links() }}
</div>
81 changes: 81 additions & 0 deletions resources/views/solution/show.blade.php
@@ -0,0 +1,81 @@
@extends('layouts.app')

@section('content')

<div class="d-flex flex-wrap justify-content-between mb-4">
AlexeyShobanov marked this conversation as resolved.
Show resolved Hide resolved
<div class="h5">
<a href="{{ route('exercises.show', $currentExercise) }}">
Exercise {{ $currentExercise->path }}: {{ getExerciseTitle($currentExercise) }}
</div>
<div class="h5">
<a href="{{ route('users.show', $user) }}">{{ $user->name }}</a>
</div>
</div>
<div class="h1 text-center mb-4">Code Review</div>
AlexeyShobanov marked this conversation as resolved.
Show resolved Hide resolved

<div class="d-flex flex-wrap border-top">
<div class="flex-grow-1 flex-shrink-1 p-4">
AlexeyShobanov marked this conversation as resolved.
Show resolved Hide resolved
<ul class="nav nav-pills mb-3" id="pills-tab" role="tablist">
@foreach ($solutionsListForCurrentExercise as $currentSolution)
@if ($loop->first)
<li class="nav-item" role="presentation">
<a class="nav-link active" id="pills-{{ $currentSolution->id }}-tab" data-toggle="pill" href="#pills-{{ $currentSolution->id }}" role="tab" aria-controls="pills-{{ $currentSolution->id }}" aria-selected="true">v.{{ $loop->iteration }}</a>
</li>
@else
<li class="nav-item" role="presentation">
<a class="nav-link" id="pills-{{ $currentSolution->id }}-tab" data-toggle="pill" href="#pills-{{ $currentSolution->id }}" role="tab" aria-controls="pills-{{ $currentSolution->id }}" aria-selected="true">v.{{ $loop->iteration }}</a>
</li>
@endif
@endforeach
</ul>
<div class="tab-content" id="pills-tabContent">
@foreach ($solutionsListForCurrentExercise as $currentSolution)
@if ($loop->first)
<div class="tab-pane fade show active" id="pills-{{ $currentSolution->id }}" role="tabpanel" aria-labelledby="pills-{{ $currentSolution->id }}-tab">
<pre><code>{{ $currentSolution->content }}</code></pre>
</div>
@else
<div class="tab-pane fade show" id="pills-{{ $currentSolution->id }}" role="tabpanel" aria-labelledby="pills-{{ $currentSolution->id }}-tab">
<pre><code>{{ $currentSolution->content }}</code></pre>
</div>
@endif
@endforeach
</div>
</div>

@if (count($solutionsListForCurrentExercise) > 1)
<div class="flex-grow-1 flex-shrink-1 p-4 border-left">
<ul class="nav nav-pills mb-3" id="pills-tab" role="tablist">
@foreach ($solutionsListForCurrentExercise as $currentSolution)
@if ($loop->first)
<li class="nav-item" role="presentation">
<a class="nav-link active" id="pills-{{ $currentSolution->id }}double-tab" data-toggle="pill" href="#pills-{{ $currentSolution->id }}double" role="tab" aria-controls="pills-{{ $currentSolution->id }}double" aria-selected="true">v.{{ $loop->iteration }}</a>
</li>
@else
<li class="nav-item" role="presentation">
<a class="nav-link" id="pills-{{ $currentSolution->id }}double-tab" data-toggle="pill" href="#pills-{{ $currentSolution->id }}double" role="tab" aria-controls="pills-{{ $currentSolution->id }}double" aria-selected="true">v.{{ $loop->iteration }}</a>
</li>
@endif
@endforeach
</ul>
<div class="tab-content" id="pills-tabContent">
@foreach ($solutionsListForCurrentExercise as $currentSolution)
@if ($loop->first)
<div class="tab-pane fade show active" id="pills-{{ $currentSolution->id }}double" role="tabpanel" aria-labelledby="pills-{{ $currentSolution->id }}double-tab">
<pre><code>{{ $currentSolution->content }}</code></pre>
</div>
@else
<div class="tab-pane fade show" id="pills-{{ $currentSolution->id }}double" role="tabpanel" aria-labelledby="pills-{{ $currentSolution->id }}double-tab">
<pre><code>{{ $currentSolution->content }}</code></pre>
</div>
@endif
@endforeach
</div>
</div>
@endif
</div>




@endsection
2 changes: 1 addition & 1 deletion routes/web.php
Expand Up @@ -31,5 +31,5 @@
Route::resource('exercises', 'ExerciseController')->only('index', 'show');
Route::resource('log', 'ActivitylogController')->only('index');
Route::resource('comments', 'CommentController')->only('store', 'update', 'show', 'destroy');
Route::resource('users.solutions', 'SolutionController')->only('store', 'destroy');
Route::resource('users.solutions', 'SolutionController')->only('store', 'show', 'destroy');
});
16 changes: 15 additions & 1 deletion tests/Feature/Http/Controllers/SolutionControllerTest.php
Expand Up @@ -29,13 +29,27 @@ public function testStore()
])->toArray();
$data = \Arr::only($factoryData, ['exercise_id', 'user_id', 'content']);
$response = $this->post(route('users.solutions.store', $this->user), $data);

$response->assertSessionHasNoErrors();
$response->assertRedirect();

$this->assertDatabaseHas('solutions', $data);
}

public function testShow()
{
$solutions = $this->user->solutions()->saveMany(
factory(Solution::class, 5)
->make()
);

$solution = $solutions->first();

$response = $this->get(route('users.solutions.show', [$this->user, $solution]));

$response->assertOk();
}

public function testDestroy()
{
$solution = factory(Solution::class)->create([
Expand Down