Comment and Reply System

This commit is contained in:
Htet Phone Aung 2023-09-20 21:19:28 +06:30
parent 27cd37bcfe
commit c844ef2ff5
39 changed files with 1001 additions and 51 deletions

View File

@ -49,8 +49,14 @@ class ChapterController extends Controller
} else {
//chapter number
$manga = Manga::where('id', $request->manga_id)->first();
$latestChap = $manga->chapters()->orderBy('chapter_no', 'desc')->first()->chapter_no;
$formData['chapter_no'] = $latestChap + 1;
$totalChap = $manga->chapters()->count();
if($totalChap == 0 ) {
$formData['chapter_no'] = $totalChap + 1;
}else {
$latestChap = $manga->chapters()->orderBy('chapter_no', 'desc')->first()->chapter_no;
$formData['chapter_no'] = $latestChap + 1;
}
}
Chapter::create($formData);

View File

@ -0,0 +1,73 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreCommentRequest;
use App\Http\Requests\UpdateCommentRequest;
use App\Models\Comment;
class CommentController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(StoreCommentRequest $request)
{
// dd($request);
$request->user()->comments()->create($request->only('comment', 'chapter_id'));
return back()->with(['message' => 'Successfully posted a comment']);
}
/**
* Display the specified resource.
*/
public function show(Comment $comment)
{
return abort('403');
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Comment $comment)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(UpdateCommentRequest $request, Comment $comment)
{
// dd($request);
$this->authorize('update', $comment);
$comment->update(['comment' => $request->comment_edit]);
return back()->with(['message' => 'Comment has been updated!']);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Comment $comment)
{
$this->authorize('delete', $comment);
$comment->delete();
return back()->with(['message' => 'Comment has been deleted!!']);
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class GenreController extends Controller
{
//
}

View File

@ -12,26 +12,33 @@ class PageController extends Controller
public function index()
{
// Storage::disk('local')->put('example.txt', 'Contents');
$mangas = Manga::when(request()->has('search'), function($q) {
$mangas = Manga::with(['chapters'])
->when(request()->has('search'), function($q) {
$keyword = request()->search;
$q->where('title', 'like', '%'.$keyword.'%');
})
->latest('id')
->paginate(8)
->withQueryString();
return view('index', ['mangas' => $mangas]);
->paginate(8)->withQueryString();
$hotMangas = Manga::with(['chapters'])
->latest('id')->limit('3')->get();
return view('index', ['mangas' => $mangas, 'hotMangas' => $hotMangas]);
}
public function manga($slug)
public function manga(Manga $manga)
{
$manga = Manga::where('slug', $slug)->first();
$chapters = $manga->chapters()->orderBy('chapter_no', 'desc')->paginate(10);
$firstChapter = $manga->chapters()->orderBy('chapter_no', 'asc')->first();
$lastChapter = $manga->chapters()->latest('chapter_no')->first();
// dd($firstChapter);
$chapters = $manga->chapters()
->latest('chapter_no')
->paginate(10);
$firstChapter = $manga->chapters()
->orderBy('chapter_no', 'asc')->first();
$lastChapter = $manga->chapters()
->latest('chapter_no')
->first();
$hotMangas = Manga::with(['chapters'])
->latest('id')->limit('3')->get();
return view('manga',[
'manga' => $manga,
'hotMangas' => $hotMangas,
'chapters' => $chapters,
'firstChapter' => $firstChapter,
'lastChapter' => $lastChapter
@ -40,11 +47,15 @@ class PageController extends Controller
public function chapter(Manga $manga,Chapter $chapter)
{
$firstChapter = $manga->chapters()->orderBy('chapter_no', 'asc')->first();
$lastChapter = $manga->chapters()->latest('chapter_no')->first();
$firstChapter = $manga->chapters()
->orderBy('chapter_no', 'asc')
->first();
$lastChapter = $manga->chapters()
->latest('chapter_no')
->first();
return view('chapter_page', [
'manga' => $manga,
'chapter' => $chapter,
'manga' => $manga,
'firstChapter' => $firstChapter,
'lastChapter' => $lastChapter
]);
@ -52,7 +63,6 @@ class PageController extends Controller
public function select(Manga $manga, Request $request)
{
// dd($request);
return redirect()->route('page.chapter', [$manga, $request->chapter_no]);
}
}

View File

@ -0,0 +1,73 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreReplyRequest;
use App\Http\Requests\UpdateReplyRequest;
use App\Models\Reply;
class ReplyController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(StoreReplyRequest $request)
{
// dd($request->user()->replies());
$request->user()->replies()->create($request->only('reply', 'comment_id'));
return back()->with(['message' => 'Successfully made a reply!!']);
}
/**
* Display the specified resource.
*/
public function show(Reply $reply)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Reply $reply)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(UpdateReplyRequest $request, Reply $reply)
{
$this->authorize('update', $reply);
$reply->update(['reply' => $request->reply_edit]);
return back()->with(['message' => 'Reply has been updated!!']);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Reply $reply)
{
$this->authorize('delete', $reply);
$reply->delete();
return back()->with(['message' => "Your reply has been deleted"]);
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreCommentRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
*/
public function rules(): array
{
return [
'comment' => 'required|min:1|max:255',
'chapter_id' => 'required|exists:chapters,id'
];
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreReplyRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
*/
public function rules(): array
{
return [
'reply' => 'required|min:1|max:255',
'comment_id' => 'required|exists:comments,id'
];
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateCommentRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
*/
public function rules(): array
{
return [
'comment_edit' => 'required|min:1|max:255',
];
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateReplyRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
*/
public function rules(): array
{
return [
'reply_edit' => 'required|min:1|max:255'
];
}
}

View File

@ -18,4 +18,8 @@ class Chapter extends Model
{
return $this->belongsTo(Manga::class, 'manga_id');
}
public function comments() {
return $this->hasMany(Comment::class, 'chapter_id');
}
}

23
app/Models/Comment.php Normal file
View File

@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
use HasFactory;
protected $fillable = ['comment', 'user_id', 'chapter_id'];
public function user()
{
return $this->belongsTo(User::class);
}
public function replies()
{
return $this->hasMany(Reply::class, 'comment_id');
}
}

11
app/Models/Genre.php Normal file
View File

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Genre extends Model
{
use HasFactory;
}

23
app/Models/Reply.php Normal file
View File

@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Reply extends Model
{
use HasFactory;
protected $fillable = ['reply', 'user_id', 'comment_id'];
public function user()
{
return $this->belongsTo(User::class);
}
public function comment()
{
return $this->belongsTo(Comment::class, 'comment_id');
}
}

View File

@ -47,4 +47,14 @@ class User extends Authenticatable
{
return $this->hasMany(Manga::class, 'author_id');
}
public function comments()
{
return $this->hasMany(Comment::class);
}
public function replies()
{
return $this->hasMany(Reply::class);
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace App\Policies;
use App\Models\Comment;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class CommentPolicy
{
/**
* access all to admin
*/
// public function before(User $user)
// {
// if($user->role === 'admin') {
// return true;
// }
// }
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
//
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, Comment $comment): bool
{
//
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
//
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Comment $comment): bool
{
return $comment->user_id === $user->id;
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Comment $comment): bool
{
return $comment->user_id === $user->id || $user->role === 'admin';
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, Comment $comment): bool
{
//
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, Comment $comment): bool
{
//
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\Reply;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class ReplyPolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
//
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, Reply $reply): bool
{
//
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
//
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Reply $reply): bool
{
return $user->id === $reply->user_id;
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Reply $reply): bool
{
return $user->id === $reply->user_id || $user->role === 'admin';
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, Reply $reply): bool
{
//
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, Reply $reply): bool
{
//
}
}

View File

@ -6,9 +6,12 @@ namespace App\Providers;
use App\Models\User;
use App\Models\Manga;
use App\Models\Reply;
use App\Models\Chapter;
use App\Policies\MangaPolicy;
use App\Policies\ReplyPolicy;
use App\Policies\ChapterPolicy;
use App\Policies\CommentPolicy;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
@ -21,7 +24,9 @@ class AuthServiceProvider extends ServiceProvider
*/
protected $policies = [
Manga::class => MangaPolicy::class,
Chapter::class => ChapterPolicy::class
Chapter::class => ChapterPolicy::class,
Comment::class => CommentPolicy::class,
Reply::class => ReplyPolicy::class
];
/**

View File

@ -0,0 +1,23 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Comment>
*/
class CommentFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
];
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Reply>
*/
class ReplyFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
];
}
}

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->longText('comment');
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('chapter_id')->constrained()->cascadeOnDelete();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('comments');
}
};

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('replies', function (Blueprint $table) {
$table->id();
$table->string('reply');
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('comment_id')->constrained()->cascadeOnDelete();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('replies');
}
};

View File

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('genres', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('genres');
}
};

View File

@ -0,0 +1,17 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class CommentSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
//
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class ReplySeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
//
}
}

View File

@ -4,3 +4,7 @@ import './bootstrap';
// selectChapter.addEventListener('change', function() {
// this.submit();
// });
import "./edit-box";
import "./reply-box";

41
resources/js/edit-box.js Normal file
View File

@ -0,0 +1,41 @@
let editBtn = document.querySelectorAll('.edit-btn');
let editBox =document.querySelectorAll('.edit-box');
editBtn.forEach( btn => {
btn.addEventListener('click', function() {
// console.log('click');
editBox.forEach( box => {
if(btn.id == box.id) {
box.classList.toggle('d-none');
// console.log(box.classList.contains('d-none'));
if(box.classList.contains('d-none')) {
btn.innerHTML = 'Edit';
}else {
btn.innerHTML = 'Cancle';
}
}
})
})
})
//reply edit box
let replyEditBtn = document.querySelectorAll('.reply-edit-btn');
let replyEditBox =document.querySelectorAll('.reply-edit-box');
replyEditBtn.forEach( btn => {
btn.addEventListener('click', function() {
// console.log('click');
replyEditBox.forEach( box => {
if(btn.id == box.id) {
box.classList.toggle('d-none');
// console.log(box.classList.contains('d-none'));
if(box.classList.contains('d-none')) {
btn.innerHTML = "<i class='bi bi-pencil-square'></i>";
}else {
btn.innerHTML = "<i class='bi bi-x-circle'></i>";
}
}
})
})
})

17
resources/js/reply-box.js Normal file
View File

@ -0,0 +1,17 @@
let replyBtn = document.querySelectorAll(".reply-btn") ;
let replyBox = document.querySelectorAll(".reply-box") ;
replyBtn.forEach( btn => {
btn.addEventListener('click', function() {
replyBox.forEach(box => {
if(box.id == btn.id) {
box.classList.toggle('d-none');
if(box.classList.contains('d-none')) {
btn.innerHTML = 'Reply';
}else {
btn.innerHTML = 'Cancle';
}
}
})
})
});

View File

@ -0,0 +1,11 @@
//scroll to top
const sttBtn = document.querySelector('#sttBtn');
window.onscroll = () => {
if(document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
sttBtn.classList.remove('d-none');
console.log("gt20");
}else {
sttBtn.classList.add('d-none');
console.log('lt20');
}
};

View File

@ -24,6 +24,16 @@
@include('partials.chapter-select-box')
@include('partials.chapter-paginate')
</div>
<hr>
<!-- comment and reply here -->
<div class="my-4 mb-5 text-start py-5">
@include('partials.discussion')
</div>
</div>
@include('partials.scroll-to-top')
@vite(['resources/js/scroll-to-top.js'])
@endsection
@section('footer')
<x-footer/>
@endsection

View File

@ -1,3 +1,3 @@
<div class="bg-dark py-4 mt-5">
<div class="bg-dark py-5 mt-5">
<p class="text-center fw-bold text-white">Copyright All Served @MangaDex</p>
</div>

View File

@ -9,7 +9,7 @@
<a title="clear search" href="{{ route('page.index') }}" class="btn btn-danger ms-4 p-0 px-1 border-1 border-dark"> <i class="bi bi-x-lg"></i> </a>
</div>
@endif
<div class="col-12 col-md-9 mb-4">
<div class="col-12 col-lg-9 mb-4">
<div class="row flex-wrap">
@forelse ($mangas as $manga)
<x-outter-manga-frame :manga="$manga" />
@ -24,7 +24,7 @@
</div>
</div>
<hr class="d-block d-md-none w-75 mx-auto text-primary">
<div class="col-12 col-md-3 mb-4">
<div class="col-12 col-lg-3 my-4 my-lg-0">
@include('partials.hot-manga')
</div>
<div class="col-12 mt-5">

View File

@ -0,0 +1,14 @@
<div class="my-3 w-75 d-none edit-box" id="edit{{ $comment->id }}">
<form method="POST" action="{{ route('comments.update', $comment->id) }}">
@csrf
@method('PUT')
<div class="input-group">
<input name="comment_edit" type="text" class="form-control rounded-start-pill px-3 py-1 border border-dark small"
name="content" value="{{ old('comment_edit', $comment->comment) }}">
<button class="btn btn-dark rounded-end-pill"> Save </button>
</div>
@error('comment_edit')
<p class="text-danger small"> {{$message}} </p>
@enderror
</form>
</div>

View File

@ -0,0 +1,80 @@
<h5 class="fw-semibold">Comments From Chapter - {{ $chapter->chapter_no }}</h5>
<h5 class="my-4 mt-5"> <i class="bi bi-star bg-primary text-white px-2 py-1 rounded-1"></i> Discussion </h5>
@guest
<p class="text-black-50">You must <a href="{{ route('register') }}" class="text-decoration-none">Register</a> or <a
href="{{ route('login') }}" class="text-decoration-none">Login</a> to post a Comment. </p>
@endguest
@auth
<div class="d-flex justify-content-between w-75">
<p class="text-black-50"> {{ $chapter->comments->count() }} {{ Str::plural('comment', $chapter->comments->count()) }}
</p>
<p>
<i class="bi bi-chat-fill"></i> {{ auth()->user()->name }}
</p>
</div>
<form action="{{ route('comments.store') }}" method="POST" class="mb-5">
@csrf
<div class="input-group w-75">
<input type="hidden" name="chapter_id" value="{{ $chapter->id }}">
<input type="text" class="form-control rounded-start-pill px-4 py-2 border border-dark" name="comment"
placeholder="Join the discussion ...">
<button type="submit" class="btn btn-dark rounded-end-pill"> Comment </button>
</div>
@error('comment')
<p class="text-danger small"> {{ $message }} </p>
@enderror
</form>
@endauth
<hr>
<!-- comment outputs here -->
<div class="p-2">
@forelse ($chapter->comments()->latest()->get() as $comment)
<div class="mb-4 border-bottom">
<p class="mb-1">
<span class="fw-bold text-primary"> <i class="bi bi-person-fill"></i> {{ $comment->user->name }}</span>
<span class="small text-black-50 fw-light ms-2"> {{ $comment->created_at->diffForHumans() }} </span>
</p>
<p class="small mb-0"> {{ $comment->comment }} </p>
<!-- comment edit box here -->
@include('partials.comment-edit-box')
<!-- reply box here -->
@include('partials.reply-box')
<!-- comment edit | delete | reply -->
<p class="d-flex align-items-center">
@can('update', $comment)
<button class="btn btn-sm text-info edit-btn" id="edit{{ $comment->id }}">
Edit
</button>
<span class="mx-2"> <i class="bi bi-cone-striped text-danger"></i> </span>
@endcan
@can('delete', $comment)
<button form="deleteCommentForm{{ $comment->id }}" class="btn btn-sm text-danger">Delete</button>
<span class="mx-2"> <i class="bi bi-cone-striped text-danger"></i> </span>
@endcan
@auth
<button id="reply{{ $comment->id }}" class="btn btn-sm text-info reply-btn">Reply</button>
@endauth
<!-- replies here -->
@include('partials.replies')
</p>
<form id="deleteCommentForm{{ $comment->id }}" class=""
action="{{ route('comments.destroy', $comment->id) }}" method="POST">
@csrf
@method('DELETE')
</form>
<!-- comment edit | delete | reply -->
</div>
@empty
<p class="text-black-50">There are no comments yet!</p>
@endforelse
</div>

View File

@ -1,28 +1,24 @@
<h5 class="fw-semibold text-primary"> <i class="bi bi-award-fill"></i> Manga Hot</h5>
<div class="row">
@forelse (App\Models\Manga::latest('id')->limit(3)->get() as $hotManga)
<div class="col-12 mt-3">
<div class="row border-0 flex-wrap">
<div class="overflow-hidden col-4">
<a href="{{ route('page.manga', $hotManga->slug) }}">
<img src="{{ asset( 'storage/'.$hotManga->cover) }}"
alt="img" class="cover-img img-fluid">
</a>
</div>
<div class="col-8">
<a href="{{ route('page.manga', $hotManga->slug) }}" class="text-decoration-none">
<p class="fw-semibold mb-1"> {{ $hotManga->title }} </p>
</a>
@forelse ($hotMangas as $hotManga)
<div class="row border-0 flex-wrap my-3 mb-4">
<div class="overflow-hidden col-3 col-lg-4">
<a href="{{ route('page.manga', $hotManga->slug) }}">
<img src="{{ asset('storage/' . $hotManga->cover) }}" alt="img" class="cover-img img-fluid">
</a>
</div>
<div class="col-5 col-lg-8">
<a href="{{ route('page.manga', $hotManga->slug) }}" class="text-decoration-none">
<p class="fw-semibold mb-1"> {{ $hotManga->title }} </p>
</a>
@foreach ($hotManga->chapters()->latest('id')->limit(2)->get() as $hotChap)
<x-latest-chapter :manga="$hotManga" :chapter="$hotChap" />
@endforeach
@foreach ($hotManga->chapters()->latest('id')->limit(2)->get() as $hotChap)
<x-latest-chapter :manga="$hotManga" :chapter="$hotChap" />
@endforeach
</div>
</div>
</div>
@empty
<p class="text-danger small">No Hot Manga Yet</p>
<p class="text-danger small">No Hot Manga Yet</p>
@endforelse
</div>

View File

@ -0,0 +1,52 @@
@if ($comment->replies->count())
<a class="btn btn-sm btn-link" data-bs-toggle="collapse" href="#collapseReplies{{$comment->id}}" role="button" aria-expanded="false"
aria-controls="collapseReplies{{$comment->id}}">
See the other replies ...
</a>
<div class="collapse" id="collapseReplies{{$comment->id}}">
@foreach ($comment->replies()->latest()->get() as $reply)
<div class="ms-5">
<div class="d-flex align-items-center mb-0">
<div>
<span class="text-primary">
{{ $reply->user->name }}
</span>
<i class="bi bi-reply-fill d-inline-block" style="transform: rotate(150deg) skewY(-1deg);"></i>
<span class="text-primary">
{{ $comment->user->name }}
</span>
<span class="small text-black-50 fw-light ms-2">
{{ $reply->created_at->diffForHumans() }}
</span>
</div>
<!-- edit and delete -->
<div class="small d-flex flex-nowrap">
@can('update', $reply)
<button id="editReply{{ $reply->id }}" class="btn btn-sm text-success reply-edit-btn">
<i class="bi bi-pencil-square"></i>
</button>
@endcan
@can('delete', $reply)
<form action="{{ route('replies.destroy', $reply->id) }}" method="POST" class="">
@csrf
@method('DELETE')
<button class="btn btn-sm text-danger">
<i class="bi bi-trash"></i>
</button>
</form>
@endcan
</div>
</div>
<p class="small">
{{ $reply->reply }}
</p>
@include('partials.reply-edit-box')
</div>
@endforeach
</div>
@endif

View File

@ -0,0 +1,18 @@
@auth
<div class="my-3 mb-0 ms-3 w-75 reply-box d-none" id="reply{{ $comment->id}}">
<p class="small text-black-50 mb-1"> <i class="bi bi-reply-all-fill"></i> Relying to <span class="text-primary">{{ $comment->user->name }}</span> </p>
<form method="POST" action="{{route('replies.store')}}">
@csrf
<div class="input-group">
<input type="hidden" name="comment_id" value="{{$comment->id}}">
<input name="reply" type="text"
class="form-control focus-ring focus-ring-light rounded-start-pill px-3 py-1 border border-dark small" name="content"
value="{{ old('reply') }}">
<button class="btn btn-dark rounded-end-pill"> <i class="bi bi-send-fill"></i> </button>
</div>
@error('reply')
<p class="text-danger small"> {{ $message }} </p>
@enderror
</form>
</div>
@endauth

View File

@ -0,0 +1,14 @@
<div class="my-3 w-75 d-none reply-edit-box" id="editReply{{ $reply->id }}">
<form method="POST" action="{{ route('replies.update', $reply->id) }}">
@csrf
@method('PUT')
<div class="input-group">
<input name="reply_edit" type="text" class="form-control rounded-start-pill px-3 py-1 border border-dark small"
name="content" value="{{ old('reply_edit', $reply->reply) }}">
<button class="btn btn-dark rounded-end-pill"> Save </button>
</div>
@error('reply_edit')
<p class="text-danger small"> {{$message}} </p>
@enderror
</form>
</div>

View File

@ -0,0 +1,9 @@
<button
id="sttBtn"
onclick="() =>
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
"
class="btn btn-warning position-fixed d-none z-3" style="bottom:10%;right:10%;">
<i class="bi bi-arrow-up"></i>
</button>

View File

@ -1,13 +1,15 @@
<?php
use App\Http\Controllers\ChapterController;
use App\Http\Controllers\HomeController;
use App\Http\Controllers\MangaController;
use App\Http\Controllers\PageController;
use App\Http\Controllers\UserController;
use App\Models\Chapter;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\HomeController;
use App\Http\Controllers\PageController;
use App\Http\Controllers\UserController;
use App\Http\Controllers\MangaController;
use App\Http\Controllers\ReplyController;
use App\Http\Controllers\ChapterController;
use App\Http\Controllers\CommentController;
/*
|--------------------------------------------------------------------------
@ -22,21 +24,28 @@ use Illuminate\Support\Facades\Route;
Route::controller(PageController::class)->group(function () {
Route::get('/', 'index')->name('page.index');
Route::get('/MangaDex/manga/{slug}', 'manga')->name('page.manga');
Route::get('/manga/{manga:slug}/chapter/{chapter:chapter_no?}', 'chapter')->name('page.chapter');
Route::post('/manga/{manga:slug}/select', 'select')->name('select.chapter');
Route::get('/MangaDex/manga/{manga:slug}', 'manga')->name('page.manga');
Route::get('/manga/{manga:slug}/chapter-{chapter:chapter_no?}', 'chapter')
->name('page.chapter');
Route::post('/{manga:slug}/chapter', 'select')->name('select.chapter');
});
Route::resource('comments', CommentController::class)->middleware('auth');
Route::resource('replies', ReplyController::class)->middleware('auth');
Auth::routes();
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])
->name('home');
Route::middleware(['auth', 'admin.access'])->group(function () {
Route::resource('manga', MangaController::class);
Route::resource('chapter', ChapterController::class);
Route::get('/chapters/manage/{manga:slug}', [ChapterController::class, 'manage'])->name('chapters.manage');
Route::get('/chapters/manage/{manga:slug}', [ChapterController::class, 'manage'])
->name('chapters.manage');
Route::get('/users-list', [UserController::class, 'index'])->name('users.list')->middleware('can:admin-only');
});