Skip to content

Commit 0af4336

Browse files
committed
#1749 progress data added to profile page
1 parent 8767add commit 0af4336

File tree

4 files changed

+254
-87
lines changed

4 files changed

+254
-87
lines changed

app/Http/Controllers/UserController.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use App\Helpers\ChartHelper;
66
use App\Models\User;
7+
use App\Models\Chapter;
78
use App\Services\RatingCalculator;
89
use Illuminate\View\View;
910

@@ -21,11 +22,21 @@ public function show(User $user, RatingCalculator $ratingCalculator): View
2122

2223
$user->load('chapterMembers', 'exerciseMembers');
2324
$chart = ChartHelper::getChart($user->id);
25+
26+
$chapters = Chapter::with('children', 'exercises')->get();
27+
$allChapters = $chapters->where('parent_id', null)->sortBy('path');
28+
$chapterMembers = $user->chapterMembers->keyBy('chapter_id');
29+
$exerciseMembers = $user->exerciseMembers->keyBy('exercise_id');
30+
2431
return view('user.show', compact(
2532
'user',
2633
'position',
2734
'points',
28-
'chart'
35+
'chart',
36+
'chapters',
37+
'allChapters',
38+
'chapterMembers',
39+
'exerciseMembers'
2940
));
3041
}
3142
}

resources/lang/ru/user.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
'created_at' => 'В сообществе с',
1414
'comments' => '{0} комментариев|{1} комментарий|[2,4] комментария|[5,*] комментариев',
1515
'statistics' => 'Статистика',
16+
'chapters_progress' => 'Прогресс по главам',
1617
'contribution_to_community' => 'Вклад в сообщество',
1718
'edit_profile' => 'Редактировать профиль',
1819
],
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
@php
2+
/**
3+
* @var Chapter $chapter
4+
* @var Collection $chapterMembers
5+
* @var Collection $exerciseMembers
6+
* @var Collection $chapters
7+
* @var int $level
8+
*/
9+
10+
use App\Models\Chapter;
11+
use Illuminate\Support\Collection;
12+
use App\Helpers\ChapterHelper;
13+
14+
$hasChildren = $chapter->children->isNotEmpty();
15+
$isCompleted = $chapterMembers->has($chapter->id) && $chapterMembers[$chapter->id]->isFinished();
16+
17+
if ($hasChildren) {
18+
$completedChildren = 0;
19+
$totalChildren = 0;
20+
21+
foreach($chapter->children as $child) {
22+
$fullChild = $chapters->find($child->id);
23+
if($fullChild && $fullChild->children->count() > 0) {
24+
foreach($fullChild->children as $grandChild) {
25+
$totalChildren++;
26+
if($chapterMembers->has($grandChild->id) && $chapterMembers[$grandChild->id]->isFinished()) {
27+
$completedChildren++;
28+
}
29+
}
30+
} else {
31+
$totalChildren++;
32+
if($chapterMembers->has($child->id) && $chapterMembers[$child->id]->isFinished()) {
33+
$completedChildren++;
34+
}
35+
}
36+
}
37+
38+
$badgeClass = match (true) {
39+
$completedChildren === $totalChildren => 'bg-success',
40+
$completedChildren > 0 => 'bg-warning',
41+
default => 'bg-secondary'
42+
};
43+
}
44+
45+
$paddingClass = match($level) {
46+
1,2 => 'ps-4',
47+
default => ''
48+
};
49+
50+
$badgeSize = $level > 0 ? 'small' : '';
51+
@endphp
52+
53+
<div class="chapter-item mb-3 {{ $paddingClass }}">
54+
@if($hasChildren)
55+
<div class="d-flex align-items-center justify-content-between p-2 border rounded"
56+
data-bs-toggle="collapse"
57+
data-bs-target="#children-{{ $chapter->id }}"
58+
role="button"
59+
aria-expanded="false">
60+
<div class="d-flex align-items-center">
61+
<i class="bi bi-chevron-right me-2"></i>
62+
<a href="{{ route('chapters.show', $chapter) }}"
63+
class="text-decoration-none {{ $level === 0 ? 'text-dark fw-bold' : ($isCompleted ? 'text-success' : 'text-muted') }}"
64+
>
65+
{{ ChapterHelper::fullChapterName($chapter->path) }}. {{ $chapter->title }}
66+
</a>
67+
</div>
68+
<span class="badge {{ $badgeClass }} {{ $badgeSize }}">
69+
{{ $completedChildren }}/{{ $totalChildren }}
70+
</span>
71+
</div>
72+
<div class="collapse" id="children-{{ $chapter->id }}">
73+
<div class="chapter-children mt-2">
74+
@foreach($chapter->children->sortBy('path') as $child)
75+
@include(
76+
'partials.user_chapter_partial',
77+
[
78+
'chapters' => $chapters,
79+
'chapter' => $chapters->find($child->id),
80+
'chapterMembers' => $chapterMembers,
81+
'exerciseMembers' => $exerciseMembers,
82+
'level' => $level + 1
83+
]
84+
)
85+
@endforeach
86+
</div>
87+
</div>
88+
@else
89+
<div class="{{ $level === 0 ? 'p-2 border rounded' : '' }}">
90+
<div class="d-flex align-items-center py-1">
91+
<span class="me-2">
92+
@if($isCompleted)
93+
<i class="bi bi-check-circle-fill text-success"></i>
94+
@else
95+
<i class="bi bi-circle text-muted"></i>
96+
@endif
97+
</span>
98+
<a href="{{ route('chapters.show', $chapter) }}"
99+
class="text-decoration-none {{ $level === 0 ? 'text-dark fw-bold' : ($isCompleted ? 'text-success' : 'text-muted') }}">
100+
{{ ChapterHelper::fullChapterName($chapter->path) }}. {{ $chapter->title }}
101+
</a>
102+
</div>
103+
104+
@if($chapter->exercises->isNotEmpty())
105+
<div class="ms-4 mt-1">
106+
@foreach($chapter->exercises as $exercise)
107+
@php
108+
$isExerciseCompleted = $exerciseMembers->has($exercise->id) && $exerciseMembers[$exercise->id]->isFinished();
109+
$isExerciseStarted = $exerciseMembers->has($exercise->id) && $exerciseMembers[$exercise->id]->isStarted();
110+
@endphp
111+
<div class="d-flex align-items-center py-1 small">
112+
<span class="me-2">
113+
@if($isExerciseCompleted)
114+
<i class="bi bi-check-circle-fill text-success"></i>
115+
@elseif($isExerciseStarted)
116+
<i class="bi bi-clock-history text-warning"></i>
117+
@else
118+
<i class="bi bi-circle text-muted"></i>
119+
@endif
120+
</span>
121+
<a href="{{ route('exercises.show', $exercise) }}"
122+
class="text-decoration-none link-secondary"
123+
>
124+
{{ $exercise->getFullTitle() }}
125+
</a>
126+
</div>
127+
@endforeach
128+
</div>
129+
@endif
130+
</div>
131+
@endif
132+
</div>
Lines changed: 109 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,131 @@
11
@extends('layouts.app')
22

33
@php
4-
/**
5-
* @var \App\Models\Chapter $chapter
6-
* @var string $userRatingPosition
7-
* @var int $points
8-
* @var \App\Models\User $user
9-
*/
4+
/**
5+
* @var Chapter $chapter
6+
* @var string $userRatingPosition
7+
* @var int $points
8+
* @var User $user
9+
*/
10+
11+
use App\Models\Chapter;
12+
use App\Models\User;
1013
@endphp
1114
@section('description', $user->name)
1215
@push('styles')
13-
@vite('resources/sass/_activity_chart.scss')
16+
@vite('resources/sass/_activity_chart.scss')
1417
@endpush
1518
@section('content')
16-
<div class="row my-4">
17-
<div class="col-12 col-md-3">
18-
<div class="sticky-top pt-4 mb-2 x-z-index-0">
19-
<img class="w-100 img-fluid" src="{{ $user->present()->getProfileImageLink() }}" alt="User avatar">
20-
<h1 class="h4 text-break my-2">{{ $user->name }}</h1>
21-
<div class="h5">
22-
<span class="bi bi-trophy-fill"></span>
23-
<span>{{ $position }}</span>
24-
<a class="h6" href="{{ route('top.index') }}">
25-
{{ __('user.show.statistics.rating') }}
26-
</a>
27-
</div>
28-
<div class="h5">
29-
<span class="bi bi-award"></span>
30-
<span>{{ $points }}</span>
31-
<span class="h6 text-secondary"> {{ trans_choice('user.show.statistics.points', $points) }}</span>
32-
</div>
33-
<div class="text-secondary">
34-
{{ __('user.show.statistics.created_at') }}
35-
@if (App::isLocale('ru'))
36-
{{ $user->created_at->isoFormat('DD MMMM YYYY') }}
37-
@else
38-
{{ $user->created_at->isoFormat('MMMM Do YYYY') }}
39-
@endif
40-
</div>
41-
<div class="mt-3">
42-
@if ($user->github_name)
43-
<span>
19+
<div class="row my-4">
20+
<div class="col-12 col-md-3">
21+
<div class="sticky-top pt-4 mb-2 x-z-index-0">
22+
<img class="w-100 img-fluid" src="{{ $user->present()->getProfileImageLink() }}" alt="User avatar">
23+
<h1 class="h4 text-break my-2">{{ $user->name }}</h1>
24+
<div class="h5">
25+
<span class="bi bi-trophy-fill"></span>
26+
<span>{{ $position }}</span>
27+
<a class="h6" href="{{ route('top.index') }}">
28+
{{ __('user.show.statistics.rating') }}
29+
</a>
30+
</div>
31+
<div class="h5">
32+
<span class="bi bi-award"></span>
33+
<span>{{ $points }}</span>
34+
<span class="h6 text-secondary"> {{ trans_choice('user.show.statistics.points', $points) }}</span>
35+
</div>
36+
<div class="text-secondary">
37+
{{ __('user.show.statistics.created_at') }}
38+
@if (App::isLocale('ru'))
39+
{{ $user->created_at->isoFormat('DD MMMM YYYY') }}
40+
@else
41+
{{ $user->created_at->isoFormat('MMMM Do YYYY') }}
42+
@endif
43+
</div>
44+
<div class="mt-3">
45+
@if ($user->github_name)
46+
<span>
4447
<a class="x-link-without-decoration mr-3 mb-2 text-dark" target="_blank" rel="noopener noreferrer"
45-
href="https://github.com/{{ $user->github_name }}">
48+
href="https://github.com/{{ $user->github_name }}">
4649
<i class="bi bi-github icon-2x"></i>
4750
</a>
4851
</span>
49-
@endif
50-
@if ($user->hexlet_nickname)
51-
<span>
52+
@endif
53+
@if ($user->hexlet_nickname)
54+
<span>
5255
<a class="x-link-without-decoration mr-2 text-dark" target="_blank" rel="noopener noreferrer"
53-
href="https://ru.hexlet.io/u/{{ $user->hexlet_nickname }}">
56+
href="https://ru.hexlet.io/u/{{ $user->hexlet_nickname }}">
5457
<img class="mb-3" src={{ Vite::asset('resources/assets/images/hexlet_logo.png') }} width="20"
55-
height="30" alt="Hexlet logo">
58+
height="30" alt="Hexlet logo">
5659
</a>
5760
</span>
58-
@endif
59-
</div>
60-
@can('update', $user)
61-
<div class="small mt-4">
62-
<a class="text-muted" href="{{ route('settings.profile.index') }}">
63-
{{ __('user.show.statistics.edit_profile') }}
64-
</a>
65-
</div>
66-
@endcan
67-
</div>
68-
</div>
69-
<div class="col-12 col-md-9 my-4">
70-
<div class="shadow rounded p-3 mb-5">
71-
<div class="h2 text-center text-secondary mb-2">
72-
{{ __('user.show.statistics.statistics') }}
73-
</div>
74-
<div class="row no-gutters my-2">
75-
<div class="col-4 col-md text-center my-2">
76-
<div class="h2 text-info">
77-
{{ $user->chapterMembers->count() }}
78-
</div>
79-
<div class="text-secondary">
80-
{{ trans_choice('user.show.statistics.chapter_members', $user->chapterMembers->count()) }}
61+
@endif
62+
</div>
63+
@can('update', $user)
64+
<div class="small mt-4">
65+
<a class="text-muted" href="{{ route('settings.profile.index') }}">
66+
{{ __('user.show.statistics.edit_profile') }}
67+
</a>
68+
</div>
69+
@endcan
8170
</div>
82-
</div>
83-
<div class="col-4 col-md text-center my-2">
84-
<div class="h2 text-info">
85-
{{ $user->exerciseMembers()->finished()->count() }}
71+
</div>
72+
<div class="col-12 col-md-9 my-4">
73+
<div class="shadow rounded p-3 mb-5">
74+
<div class="h2 text-center text-secondary mb-2">
75+
{{ __('user.show.statistics.statistics') }}
76+
</div>
77+
<div class="row no-gutters my-2">
78+
<div class="col-4 col-md text-center my-2">
79+
<div class="h2 text-info">
80+
{{ $user->chapterMembers->count() }}
81+
</div>
82+
<div class="text-secondary">
83+
{{ trans_choice('user.show.statistics.chapter_members', $user->chapterMembers->count()) }}
84+
</div>
85+
</div>
86+
<div class="col-4 col-md text-center my-2">
87+
<div class="h2 text-info">
88+
{{ $user->exerciseMembers()->finished()->count() }}
89+
</div>
90+
<div class="text-secondary">
91+
{{ trans_choice('user.show.statistics.exercise_members', $user->exerciseMembers->count()) }}
92+
</div>
93+
</div>
94+
<div class="col-4 col-md text-center my-2">
95+
<a class="text-decoration-none" href="{{ route('users.comments.index', [$user]) }}">
96+
<div class="h2 text-info">
97+
{{ $user->comments->count() }}
98+
</div>
99+
<div class="text-secondary">
100+
{{ trans_choice('user.show.statistics.comments', $user->comments->count()) }}
101+
</div>
102+
</a>
103+
</div>
104+
</div>
105+
<div class="col-md-12">
106+
@include('components.activity_chart')
107+
</div>
86108
</div>
87-
<div class="text-secondary">
88-
{{ trans_choice('user.show.statistics.exercise_members', $user->exerciseMembers->count()) }}
109+
110+
<div class="shadow rounded p-3 mb-5">
111+
<div class="h3 text-center text-secondary mb-3">
112+
{{ __('user.show.statistics.chapters_progress') }}
113+
</div>
114+
<div class="chapter-progress">
115+
@foreach($allChapters as $chapter)
116+
@include(
117+
'partials.user_chapter_partial',
118+
[
119+
'chapters' => $chapters,
120+
'chapter' => $chapter,
121+
'chapterMembers' => $chapterMembers,
122+
'exerciseMembers' => $exerciseMembers,
123+
'level' => 0
124+
]
125+
)
126+
@endforeach
127+
</div>
89128
</div>
90-
</div>
91-
<div class="col-4 col-md text-center my-2">
92-
<a class="text-decoration-none" href="{{ route('users.comments.index', [$user]) }}">
93-
<div class="h2 text-info">
94-
{{ $user->comments->count() }}
95-
</div>
96-
<div class="text-secondary">
97-
{{ trans_choice('user.show.statistics.comments', $user->comments->count()) }}
98-
</div>
99-
</a>
100-
</div>
101-
</div>
102-
<div class="col-md-12">
103-
@include('components.activity_chart')
104129
</div>
105-
</div>
106130
</div>
107-
</div>
108131
@endsection

0 commit comments

Comments
 (0)