Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion app/Http/Controllers/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Helpers\ChartHelper;
use App\Models\User;
use App\Models\Chapter;
use App\Services\RatingCalculator;
use Illuminate\View\View;

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

$user->load('chapterMembers', 'exerciseMembers');
$chart = ChartHelper::getChart($user->id);

$chapters = Chapter::with('children', 'exercises')->get();
$allChapters = $chapters->where('parent_id', null)->sortBy('path');
$chapterMembers = $user->chapterMembers->keyBy('chapter_id');
$exerciseMembers = $user->exerciseMembers->keyBy('exercise_id');

return view('user.show', compact(
'user',
'position',
'points',
'chart'
'chart',
'chapters',
'allChapters',
'chapterMembers',
'exerciseMembers'
));
}
}
1 change: 1 addition & 0 deletions resources/lang/ru/user.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
'created_at' => 'В сообществе с',
'comments' => '{0} комментариев|{1} комментарий|[2,4] комментария|[5,*] комментариев',
'statistics' => 'Статистика',
'chapters_progress' => 'Прогресс по главам',
'contribution_to_community' => 'Вклад в сообщество',
'edit_profile' => 'Редактировать профиль',
],
Expand Down
132 changes: 132 additions & 0 deletions resources/views/partials/user_chapter_partial.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
@php
/**
* @var Chapter $chapter
* @var Collection $chapterMembers
* @var Collection $exerciseMembers
* @var Collection $chapters
* @var int $level
*/

use App\Models\Chapter;
use Illuminate\Support\Collection;
use App\Helpers\ChapterHelper;

$hasChildren = $chapter->children->isNotEmpty();
$isCompleted = $chapterMembers->has($chapter->id) && $chapterMembers[$chapter->id]->isFinished();

if ($hasChildren) {
$completedChildren = 0;
$totalChildren = 0;

foreach($chapter->children as $child) {
$fullChild = $chapters->find($child->id);
if($fullChild && $fullChild->children->count() > 0) {
foreach($fullChild->children as $grandChild) {
$totalChildren++;
if($chapterMembers->has($grandChild->id) && $chapterMembers[$grandChild->id]->isFinished()) {
$completedChildren++;
}
}
} else {
$totalChildren++;
if($chapterMembers->has($child->id) && $chapterMembers[$child->id]->isFinished()) {
$completedChildren++;
}
}
}

$badgeClass = match (true) {
$completedChildren === $totalChildren => 'bg-success',
$completedChildren > 0 => 'bg-warning',
default => 'bg-secondary'
};
}

$paddingClass = match($level) {
1,2 => 'ps-4',
default => ''
};

$badgeSize = $level > 0 ? 'small' : '';
@endphp

<div class="chapter-item mb-3 {{ $paddingClass }}">
@if($hasChildren)
<div class="d-flex align-items-center justify-content-between p-2 border rounded"
data-bs-toggle="collapse"
data-bs-target="#children-{{ $chapter->id }}"
role="button"
aria-expanded="false">
<div class="d-flex align-items-center">
<i class="bi bi-chevron-right me-2"></i>
<a href="{{ route('chapters.show', $chapter) }}"
class="text-decoration-none {{ $level === 0 ? 'text-dark fw-bold' : ($isCompleted ? 'text-success' : 'text-muted') }}"
>
{{ ChapterHelper::fullChapterName($chapter->path) }}. {{ $chapter->title }}
</a>
</div>
<span class="badge {{ $badgeClass }} {{ $badgeSize }}">
{{ $completedChildren }}/{{ $totalChildren }}
</span>
</div>
<div class="collapse" id="children-{{ $chapter->id }}">
<div class="chapter-children mt-2">
@foreach($chapter->children->sortBy('path') as $child)
@include(
'partials.user_chapter_partial',
[
'chapters' => $chapters,
'chapter' => $chapters->find($child->id),
'chapterMembers' => $chapterMembers,
'exerciseMembers' => $exerciseMembers,
'level' => $level + 1
]
)
@endforeach
</div>
</div>
@else
<div class="{{ $level === 0 ? 'p-2 border rounded' : '' }}">
<div class="d-flex align-items-center py-1">
<span class="me-2">
@if($isCompleted)
<i class="bi bi-check-circle-fill text-success"></i>
@else
<i class="bi bi-circle text-muted"></i>
@endif
</span>
<a href="{{ route('chapters.show', $chapter) }}"
class="text-decoration-none {{ $level === 0 ? 'text-dark fw-bold' : ($isCompleted ? 'text-success' : 'text-muted') }}">
{{ ChapterHelper::fullChapterName($chapter->path) }}. {{ $chapter->title }}
</a>
</div>

@if($chapter->exercises->isNotEmpty())
<div class="ms-4 mt-1">
@foreach($chapter->exercises as $exercise)
@php
$isExerciseCompleted = $exerciseMembers->has($exercise->id) && $exerciseMembers[$exercise->id]->isFinished();
$isExerciseStarted = $exerciseMembers->has($exercise->id) && $exerciseMembers[$exercise->id]->isStarted();
@endphp
<div class="d-flex align-items-center py-1 small">
<span class="me-2">
@if($isExerciseCompleted)
<i class="bi bi-check-circle-fill text-success"></i>
@elseif($isExerciseStarted)
<i class="bi bi-clock-history text-warning"></i>
@else
<i class="bi bi-circle text-muted"></i>
@endif
</span>
<a href="{{ route('exercises.show', $exercise) }}"
class="text-decoration-none link-secondary"
>
{{ $exercise->getFullTitle() }}
</a>
</div>
@endforeach
</div>
@endif
</div>
@endif
</div>
195 changes: 109 additions & 86 deletions resources/views/user/show.blade.php
Original file line number Diff line number Diff line change
@@ -1,108 +1,131 @@
@extends('layouts.app')

@php
/**
* @var \App\Models\Chapter $chapter
* @var string $userRatingPosition
* @var int $points
* @var \App\Models\User $user
*/
/**
* @var Chapter $chapter
* @var string $userRatingPosition
* @var int $points
* @var User $user
*/

use App\Models\Chapter;
use App\Models\User;
@endphp
@section('description', $user->name)
@push('styles')
@vite('resources/sass/_activity_chart.scss')
@vite('resources/sass/_activity_chart.scss')
@endpush
@section('content')
<div class="row my-4">
<div class="col-12 col-md-3">
<div class="sticky-top pt-4 mb-2 x-z-index-0">
<img class="w-100 img-fluid" src="{{ $user->present()->getProfileImageLink() }}" alt="User avatar">
<h1 class="h4 text-break my-2">{{ $user->name }}</h1>
<div class="h5">
<span class="bi bi-trophy-fill"></span>
<span>{{ $position }}</span>
<a class="h6" href="{{ route('top.index') }}">
{{ __('user.show.statistics.rating') }}
</a>
</div>
<div class="h5">
<span class="bi bi-award"></span>
<span>{{ $points }}</span>
<span class="h6 text-secondary"> {{ trans_choice('user.show.statistics.points', $points) }}</span>
</div>
<div class="text-secondary">
{{ __('user.show.statistics.created_at') }}
@if (App::isLocale('ru'))
{{ $user->created_at->isoFormat('DD MMMM YYYY') }}
@else
{{ $user->created_at->isoFormat('MMMM Do YYYY') }}
@endif
</div>
<div class="mt-3">
@if ($user->github_name)
<span>
<div class="row my-4">
<div class="col-12 col-md-3">
<div class="sticky-top pt-4 mb-2 x-z-index-0">
<img class="w-100 img-fluid" src="{{ $user->present()->getProfileImageLink() }}" alt="User avatar">
<h1 class="h4 text-break my-2">{{ $user->name }}</h1>
<div class="h5">
<span class="bi bi-trophy-fill"></span>
<span>{{ $position }}</span>
<a class="h6" href="{{ route('top.index') }}">
{{ __('user.show.statistics.rating') }}
</a>
</div>
<div class="h5">
<span class="bi bi-award"></span>
<span>{{ $points }}</span>
<span class="h6 text-secondary"> {{ trans_choice('user.show.statistics.points', $points) }}</span>
</div>
<div class="text-secondary">
{{ __('user.show.statistics.created_at') }}
@if (App::isLocale('ru'))
{{ $user->created_at->isoFormat('DD MMMM YYYY') }}
@else
{{ $user->created_at->isoFormat('MMMM Do YYYY') }}
@endif
</div>
<div class="mt-3">
@if ($user->github_name)
<span>
<a class="x-link-without-decoration mr-3 mb-2 text-dark" target="_blank" rel="noopener noreferrer"
href="https://github.com/{{ $user->github_name }}">
href="https://github.com/{{ $user->github_name }}">
<i class="bi bi-github icon-2x"></i>
</a>
</span>
@endif
@if ($user->hexlet_nickname)
<span>
@endif
@if ($user->hexlet_nickname)
<span>
<a class="x-link-without-decoration mr-2 text-dark" target="_blank" rel="noopener noreferrer"
href="https://ru.hexlet.io/u/{{ $user->hexlet_nickname }}">
href="https://ru.hexlet.io/u/{{ $user->hexlet_nickname }}">
<img class="mb-3" src={{ Vite::asset('resources/assets/images/hexlet_logo.png') }} width="20"
height="30" alt="Hexlet logo">
height="30" alt="Hexlet logo">
</a>
</span>
@endif
</div>
@can('update', $user)
<div class="small mt-4">
<a class="text-muted" href="{{ route('settings.profile.index') }}">
{{ __('user.show.statistics.edit_profile') }}
</a>
</div>
@endcan
</div>
</div>
<div class="col-12 col-md-9 my-4">
<div class="shadow rounded p-3 mb-5">
<div class="h2 text-center text-secondary mb-2">
{{ __('user.show.statistics.statistics') }}
</div>
<div class="row no-gutters my-2">
<div class="col-4 col-md text-center my-2">
<div class="h2 text-info">
{{ $user->chapterMembers->count() }}
</div>
<div class="text-secondary">
{{ trans_choice('user.show.statistics.chapter_members', $user->chapterMembers->count()) }}
@endif
</div>
@can('update', $user)
<div class="small mt-4">
<a class="text-muted" href="{{ route('settings.profile.index') }}">
{{ __('user.show.statistics.edit_profile') }}
</a>
</div>
@endcan
</div>
</div>
<div class="col-4 col-md text-center my-2">
<div class="h2 text-info">
{{ $user->exerciseMembers()->finished()->count() }}
</div>
<div class="col-12 col-md-9 my-4">
<div class="shadow rounded p-3 mb-5">
<div class="h2 text-center text-secondary mb-2">
{{ __('user.show.statistics.statistics') }}
</div>
<div class="row no-gutters my-2">
<div class="col-4 col-md text-center my-2">
<div class="h2 text-info">
{{ $user->chapterMembers->count() }}
</div>
<div class="text-secondary">
{{ trans_choice('user.show.statistics.chapter_members', $user->chapterMembers->count()) }}
</div>
</div>
<div class="col-4 col-md text-center my-2">
<div class="h2 text-info">
{{ $user->exerciseMembers()->finished()->count() }}
</div>
<div class="text-secondary">
{{ trans_choice('user.show.statistics.exercise_members', $user->exerciseMembers->count()) }}
</div>
</div>
<div class="col-4 col-md text-center my-2">
<a class="text-decoration-none" href="{{ route('users.comments.index', [$user]) }}">
<div class="h2 text-info">
{{ $user->comments->count() }}
</div>
<div class="text-secondary">
{{ trans_choice('user.show.statistics.comments', $user->comments->count()) }}
</div>
</a>
</div>
</div>
<div class="col-md-12">
@include('components.activity_chart')
</div>
</div>
<div class="text-secondary">
{{ trans_choice('user.show.statistics.exercise_members', $user->exerciseMembers->count()) }}

<div class="shadow rounded p-3 mb-5">
<div class="h3 text-center text-secondary mb-3">
{{ __('user.show.statistics.chapters_progress') }}
</div>
<div class="chapter-progress">
@foreach($allChapters as $chapter)
@include(
'partials.user_chapter_partial',
[
'chapters' => $chapters,
'chapter' => $chapter,
'chapterMembers' => $chapterMembers,
'exerciseMembers' => $exerciseMembers,
'level' => 0
]
)
@endforeach
</div>
</div>
</div>
<div class="col-4 col-md text-center my-2">
<a class="text-decoration-none" href="{{ route('users.comments.index', [$user]) }}">
<div class="h2 text-info">
{{ $user->comments->count() }}
</div>
<div class="text-secondary">
{{ trans_choice('user.show.statistics.comments', $user->comments->count()) }}
</div>
</a>
</div>
</div>
<div class="col-md-12">
@include('components.activity_chart')
</div>
</div>
</div>
</div>
@endsection