Skip to content

Commit

Permalink
Merge pull request #27 from laravelcm/user-dashboard
Browse files Browse the repository at this point in the history
User dashboard
  • Loading branch information
mckenziearts authored Dec 1, 2021
2 parents 3a3da41 + 1444614 commit f7f2216
Show file tree
Hide file tree
Showing 29 changed files with 600 additions and 41 deletions.
40 changes: 40 additions & 0 deletions app/Http/Controllers/User/DashboardController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace App\Http\Controllers\User;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;

class DashboardController extends Controller
{
public function dashboard()
{
return view('user.dashboard', [
'user' => $user = Auth::user(),
'articles' => $user->articles()
->orderByDesc('submitted_at')
->orderByDesc('created_at')
->paginate(5),
]);
}

public function threads()
{
return view('user.threads', [
'user' => $user = Auth::user(),
'threads' => $user->threads()
->recent()
->paginate(5),
]);
}

public function discussions()
{
return view('user.discussions', [
'user' => $user = Auth::user(),
'discussions' => $user->discussions()
->orderByDesc('created_at')
->paginate(5),
]);
}
}
1 change: 1 addition & 0 deletions app/Http/Controllers/User/SettingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public function update(UpdateProfileRequest $request)
'bio' => trim(strip_tags($request->bio)),
'twitter_profile' => $request->twitter_profile,
'github_profile' => $request->github_profile,
'linkedin_profile' => $request->linkedin_profile,
'phone_number' => $request->phone_number,
'location' => $request->location,
'website' => $request->website,
Expand Down
4 changes: 2 additions & 2 deletions app/Http/Livewire/Articles/Create.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ public function store()

if ($this->submitted) {
// Envoi du mail a l'admin pour la validation de l'article
session()->flash('success', 'Merci d\'avoir soumis votre article. Vous aurez des nouvelles que lorsque nous accepterons votre article.');
session()->flash('status', 'Merci d\'avoir soumis votre article. Vous aurez des nouvelles que lorsque nous accepterons votre article.');
}

$user->hasRole('user') ?
$this->redirect('/articles/me') :
$this->redirectRoute('dashboard') :
$this->redirectRoute('articles.show', $article);
}

Expand Down
4 changes: 2 additions & 2 deletions app/Models/Discussion.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,12 @@ public function excerpt(int $limit = 110): string

public function isPinned(): bool
{
return (bool) $this->is_pinned;
return $this->is_pinned;
}

public function isLocked(): bool
{
return (bool) $this->locked;
return $this->locked;
}

public function getCountAllRepliesWithChildAttribute(): int
Expand Down
38 changes: 33 additions & 5 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\Permission\Traits\HasRoles;
Expand Down Expand Up @@ -42,6 +43,7 @@ class User extends Authenticatable implements MustVerifyEmail, HasMedia
'phone_number',
'github_profile',
'twitter_profile',
'linkedin_profile',
'website',
'last_login_at',
'last_login_ip',
Expand Down Expand Up @@ -219,6 +221,16 @@ public function twitter(): ?string
return $this->twitter_profile;
}

public function hasTwitterAccount(): bool
{
return ! empty($this->twitter());
}

public function linkedin(): ?string
{
return $this->linkedin_profile;
}

public function scopeModerators(Builder $query): Builder
{
return $query->whereHas('roles', function ($query) {
Expand Down Expand Up @@ -286,19 +298,34 @@ public function routeNotificationForSlack($notification): string
return env('SLACK_WEBHOOK_URL', '');
}

public function countReplies(): int
public function replies(): Collection
{
return $this->replyAble()->count();
return $this->replyAble;
}

public function replies(): Collection
public function countReplies(): int
{
return $this->replyAble;
return Cache::remember('replies_count', now()->addHours(2), fn () => $this->replyAble()->count());
}

public function countSolutions(): int
{
return $this->replyAble()->isSolution()->count();
return Cache::remember('solutions_count', now()->addHours(2), fn () => $this->replyAble()->isSolution()->count());
}

public function countArticles(): int
{
return Cache::remember('articles_count', now()->addHours(2), fn () => $this->articles()->approved()->count());
}

public function countDiscussions(): int
{
return Cache::remember('discussions_count', now()->addHours(2), fn () => $this->discussions()->count());
}

public function countThreads(): int
{
return Cache::remember('threads_count', now()->addHours(2), fn () => $this->threads()->count());
}

public function scopeMostSolutions(Builder $query, int $inLastDays = null)
Expand Down Expand Up @@ -339,6 +366,7 @@ public function scopeMostSubmissionsInLastDays(Builder $query, int $days)
public function scopeWithCounts(Builder $query)
{
return $query->withCount([
'articles as articles_count',
'threads as threads_count',
'replyAble as replies_count',
'replyAble as solutions_count' => function (Builder $query) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddLinkedinProfileColumnToUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('linkedin_profile')
->after('twitter_profile')
->nullable();
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('linkedin_profile');
});
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"prettier-standard": "^16.4.1"
},
"dependencies": {
"@alpinejs/intersect": "^3.6.1",
"@headlessui/react": "^1.4.2",
"@heroicons/react": "^1.0.5",
"@tailwindcss/aspect-ratio": "^0.2.0",
Expand Down
2 changes: 1 addition & 1 deletion public/css/app.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/js/app.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions public/mix-manifest.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"/js/app.js": "/js/app.js?id=6e9d959a77c0ed56a844",
"/css/app.css": "/css/app.css?id=a4b94cf149d9afd5d3e5"
"/js/app.js": "/js/app.js?id=7d557689434ab2f122d8",
"/css/app.css": "/css/app.css?id=fe258fa62dce97e1cd59"
}
2 changes: 2 additions & 0 deletions resources/js/app.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Alpine from 'alpinejs'
import intersect from '@alpinejs/intersect'

import internationalNumber from './plugins/internationalNumber'
import './elements'
Expand All @@ -10,5 +11,6 @@ import './scrollspy'
window.Alpine = Alpine;

Alpine.data('internationalNumber', internationalNumber)
Alpine.plugin(intersect)

Alpine.start();
47 changes: 47 additions & 0 deletions resources/views/components/discussions/summary.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@props(['discussion'])

<div class="py-6 border-b border-skin-base">
<div class="mt-2 lg:flex lg:items-center lg:justify-between">
<div class="flex-1">
<h2 class="text-xl font-semibold font-sans text-skin-inverted leading-7">
<a href="{{ route('discussions.show', $discussion) }}" class="hover:text-skin-primary">{{ $discussion->title }}</a>
</h2>
</div>
<div class="mt-2 lg:mt-0 flex-shrink-0 self-start">
@if (count($tags = $discussion->tags))
<div class="flex flex-wrap gap-2 lg:gap-x-3">
@foreach ($tags as $tag)
<x-tag :tag="$tag" />
@endforeach
</div>
@endif
</div>
</div>
<p class="mt-1 text-sm font-normal text-skin-base leading-5">
{!! $discussion->excerpt(175) !!}
</p>
<div class="mt-3 flex justify-between">
<div class="flex items-center text-sm font-sans text-skin-muted">
<a class="flex-shrink-0" href="{{ route('profile', $discussion->author->username) }}">
<img class="h-6 w-6 rounded-full" src="{{ $discussion->author->profile_photo_url }}" alt="{{ $discussion->author->name }}">
</a>
<span class="ml-2 pr-1">Posté par</span>
<div class="flex items-center space-x-1">
<a href="{{ route('profile', $discussion->author->username) }}" class="text-skin-inverted hover:underline">{{ $discussion->author->name }}</a>
<span aria-hidden="true">&middot;</span>
<time-ago time="{{ $discussion->created_at->getTimestamp() }}"/>
</div>
</div>
<div class="flex items-center text-sm space-x-4">
<p class="inline-flex space-x-2 text-skin-base">
<x-heroicon-o-chat-alt-2 class="h-5 w-5" />
<span class="font-normal text-skin-inverted-muted">{{ $discussion->count_all_replies_with_child }}</span>
<span class="sr-only">réponses</span>
</p>
<a href="{{ route('discussions.edit', $discussion->slug()) }}" class="inline-flex items-center font-normal text-skin-inverted-muted hover:text-skin-base hover:underline">
<x-heroicon-o-pencil class="h-4 w-4 mr-1.5" />
Éditer
</a>
</div>
</div>
</div>
67 changes: 67 additions & 0 deletions resources/views/components/forum/thread-summary.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
@props(['thread'])

<div class="py-6 border-b border-skin-base">
<article aria-labelledby="question-title-{{ $thread->id }}">
<div>
<div class="lg:flex lg:space-x-3">
<div class="flex-1 flex items-center space-x-3">
<div class="flex-shrink-0">
<img class="h-8 w-8 rounded-full" src="{{ $thread->author->profile_photo_url }}" alt="">
</div>
<div class="min-w-0 flex-1 flex items-center space-x-1 font-sans">
<p class="text-sm font-medium text-skin-inverted">
<a href="{{ route('profile', $thread->author->username) }}" class="block group">
<span class="group-hover:underline">{{ $thread->author->name }}</span>
<span class="text-skin-muted">{{ '@'. $thread->author->username }}</span>
</a>
</p>
<span aria-hidden="true">&middot;</span>
<p class="text-sm text-skin-base">
<time-ago time="{{ $thread->created_at->getTimestamp() }}"/>
</p>
</div>
</div>
<div class="mt-2 lg:mt-0 flex-shrink-0 self-center">
@if (count($channels = $thread->channels->load('parent')))
<div class="flex flex-wrap gap-2 mt-2 lg:mt-0 lg:gap-x-3">
@foreach ($channels as $channel)
<a href="{{ route('forum.channels', $channel) }}" class="flex gap-2">
<x-forum.channel :channel="$channel" />
</a>
@endforeach
</div>
@endif
</div>
</div>
<h2 id="question-title-{{ $thread->id }}" class="mt-4 text-base font-medium text-skin-inverted font-sans">
<a href="{{ route('forum.show', $thread) }}" class="hover:underline">{{ $thread->subject() }}</a>
</h2>
</div>
<div class="mt-2 text-sm text-skin-inverted-muted font-normal">
<a href="{{ route('forum.show', $thread) }}">{!! $thread->excerpt() !!}</a>
</div>
<div class="mt-6 flex justify-between space-x-8">
<div class="flex items-center space-x-4">
<div class="flex items-center text-sm text-skin-inverted-muted font-normal">
<span class="text-base mr-2">👏</span>
{{ count($thread->reactions) }}
</div>
<p class="inline-flex text-sm space-x-2 text-skin-base">
<x-heroicon-o-chat-alt class="h-5 w-5" />
<span class="font-normal text-skin-inverted-muted">{{ count($thread->replies) }}</span>
<span class="sr-only">réponses</span>
</p>
</div>
<div class="flex text-sm">
<p class="inline-flex items-center space-x-3 text-sm">
@if ($thread->isSolved())
<a href="{{ route('forum.show', $thread->slug) }}#{{ $thread->solution_reply_id }}" class="flex items-center gap-x-2 font-medium text-green-500">
<x-heroicon-s-badge-check class="w-5 h-5" />
<span class="hover:underline">Résolu</span>
</a>
@endif
</p>
</div>
</div>
</article>
</div>
12 changes: 12 additions & 0 deletions resources/views/components/skeleton.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div class="p-4 w-full mx-auto bg-skin-card border border-skin-base shadow-sm rounded-md">
<div class="animate-pulse flex space-x-4">
<div class="flex-1 space-y-4 py-1">
<div class="h-4 bg-skin-card-muted rounded w-3/4"></div>
<div class="space-y-2">
<div class="h-4 bg-skin-card-muted rounded"></div>
<div class="h-4 bg-skin-card-muted rounded w-5/6"></div>
<div class="h-4 bg-skin-card-muted rounded w-5/6"></div>
</div>
</div>
</div>
</div>
23 changes: 23 additions & 0 deletions resources/views/components/user/breadcrumb.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@props(['section'])

<nav class="flex mb-3" aria-label="Breadcrumb">
<ol role="list" class="flex items-center space-x-4">
<li>
<div>
<a href="{{ route('dashboard') }}" class="text-skin-muted hover:text-skin-base">
<x-heroicon-s-home class="flex-shrink-0 h-5 w-5" />
<span class="sr-only">Accueil</span>
</a>
</div>
</li>

<li>
<div class="flex items-center">
<svg class="flex-shrink-0 h-5 w-5 text-gray-300" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<path d="M5.555 17.776l8-16 .894.448-8 16-.894-.448z" />
</svg>
<span href="#" class="ml-4 text-sm font-medium text-skin-base" aria-current="page">{{ $section }}</span>
</div>
</li>
</ol>
</nav>
14 changes: 14 additions & 0 deletions resources/views/components/user/page-heading.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@props(['title', 'button', 'url'])

<div class="md:flex md:items-center md:justify-between">
<div class="flex-1 min-w-0">
<h2 class="text-lg font-bold leading-7 text-skin-inverted sm:text-xl sm:truncate font-sans">
{{ $title }}
</h2>
</div>
<div class="mt-4 flex md:mt-0 md:ml-4">
<x-button :link="$url" class="ml-3">
{{ $button }}
</x-button>
</div>
</div>
Loading

0 comments on commit f7f2216

Please sign in to comment.