feature(Infinite Scrolling):

Implemented Infinite Scrolling with AJAX in Dashboard
This commit is contained in:
devoalda 2023-08-08 19:46:57 +08:00
parent 9df9c8d2f2
commit 59ffb4bc32
4 changed files with 120 additions and 67 deletions

View File

@ -4,20 +4,40 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Response;
class DashboardController extends Controller
{
public function index()
public function index(Request $request)
{
$user = auth()->user();
$data = $this->all_incomplete_todos();
$todos = $data['todos'];
$incomplete_count = $data['incomplete_count'];
// Convert all todo to Todo model
$todos = DB::table('todos')
->join('project_todo', 'todos.id', '=', 'project_todo.todo_id')
->join('project_user', 'project_todo.project_id', '=', 'project_user.project_id')
->where('project_user.user_id', '=', $user->id)
->whereDate('due_end', '<=', strtotime('today midnight'))
->whereNull('completed_at')
->orderBy('due_end', 'asc')
->paginate(5);
$todos->transform(function ($todo) {
return \App\Models\Todo::find($todo->id);
});
if ($request->ajax()){
$view = view('dashboard.load-todo', compact('todos'))->render();
return Response::json([
'view' => $view,
'nextPageUrl' => $todos->nextPageUrl(),
]);
}
$incomplete_count = $data['incomplete_count'];
// Convert all todo to Todo model
$projects = $this->projects();
$project_count = $projects['project_count'];
$ave_todo_count = $projects['ave_todo_count'];

View File

@ -119,74 +119,45 @@
Today's Todos
({{ $incomplete_count }})
</h2>
<div class="space-y-4">
@foreach ($todos as $todo)
<a href="{{ route('project.todo.edit', [$todo->project->id, $todo->id]) }}" class="block">
<div
class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 flex items-center justify-between">
<div class="flex items-center">
<form action="{{ route('project.todo.update', [$todo->project->id, $todo->id]) }}"
method="POST"
class="toggle-completed-form">
@csrf
@method('PUT')
<label class="flex items-center cursor-pointer">
<input type="checkbox" name="completed_at" id="completed_at_{{ $todo->id }}"
class="w-4 h-4 text-green-600 bg-gray-100 border-gray-300 rounded focus:ring-green-500 dark:focus:ring-green-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
onchange="this.form.submit()" {{ $todo->completed_at ? 'checked' : ''
}}>
<span class="ml-2 text-sm text-gray-700"></span>
</label>
</form>
<span
class="ml-2 text-2xl font-bold text-gray-800 dark:text-gray-100">
{{ $todo->title }}
</span>
</div>
<div>
@php
if ($todo->due_start && $todo->due_end) {
$due = \Carbon\Carbon::parse($todo->due_end);
$now = now();
$timeRemaining = $due->isFuture() ? $now->diffForHumans($due, true) :
$due->diffForHumans($now,
true);
} elseif ($todo->due_start) {
$due = \Carbon\Carbon::parse($todo->due_start);
$now = now();
$timeRemaining = $due->isFuture() ? $now->diffForHumans($due, true) :
$due->diffForHumans($now,
true);
} elseif ($todo->due_end) {
$due = \Carbon\Carbon::parse($todo->due_end);
$now = now();
$timeRemaining = $due->isFuture() ? $now->diffForHumans($due, true) :
$due->diffForHumans($now,
true);
} else {
// If there is no due_start or due_end, set $timeRemaining to null
$timeRemaining = null;
}
@endphp
@if ($timeRemaining !== null)
@if ($due->isFuture())
<p class="text-sm text-green-600">{{ $timeRemaining }} remaining</p>
@else
<p class="text-sm text-red-600">{{ $timeRemaining }} ago</p>
@endif
@endif
</div>
</div>
</a>
@endforeach
<div class="space-y-4" id="todo-container">
@include('dashboard.load-todo')
</div>
</div>
<!-- Pagination with CSS -->
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 mt-4 pb-4">
<div class="invisible">
{{ $todos->links() }}
</div>
<script>
$(document).ready(function () {
let nextPageUrl = '{{ $todos->nextPageUrl() }}';
$(window).scroll(function () {
if ($(window).scrollTop() + $(window).height() >= $(document).height() - 100) {
if (nextPageUrl) {
loadMorePosts();
}
}
});
function loadMorePosts() {
$.ajax({
url: nextPageUrl,
type: 'get',
beforeSend: function () {
nextPageUrl = '';
},
success: function (data) {
nextPageUrl = data.nextPageUrl;
$('#todo-container').append(data.view);
},
error: function (xhr, status, error) {
console.error("Error loading more posts:", error);
}
});
}
});
</script>
</x-app-layout>

View File

@ -0,0 +1,61 @@
@foreach ($todos as $todo)
<a href="{{ route('project.todo.edit', [$todo->project->id, $todo->id]) }}" class="block">
<div
class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 flex items-center justify-between">
<div class="flex items-center">
<form action="{{ route('project.todo.update', [$todo->project->id, $todo->id]) }}"
method="POST"
class="toggle-completed-form">
@csrf
@method('PUT')
<label class="flex items-center cursor-pointer">
<input type="checkbox" name="completed_at" id="completed_at_{{ $todo->id }}"
class="w-4 h-4 text-green-600 bg-gray-100 border-gray-300 rounded focus:ring-green-500 dark:focus:ring-green-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
onchange="this.form.submit()" {{ $todo->completed_at ? 'checked' : ''
}}>
<span class="ml-2 text-sm text-gray-700"></span>
</label>
</form>
<span
class="ml-2 text-2xl font-bold text-gray-800 dark:text-gray-100">
{{ $todo->title }}
</span>
</div>
<div>
@php
if ($todo->due_start && $todo->due_end) {
$due = \Carbon\Carbon::parse($todo->due_end);
$now = now();
$timeRemaining = $due->isFuture() ? $now->diffForHumans($due, true) :
$due->diffForHumans($now,
true);
} elseif ($todo->due_start) {
$due = \Carbon\Carbon::parse($todo->due_start);
$now = now();
$timeRemaining = $due->isFuture() ? $now->diffForHumans($due, true) :
$due->diffForHumans($now,
true);
} elseif ($todo->due_end) {
$due = \Carbon\Carbon::parse($todo->due_end);
$now = now();
$timeRemaining = $due->isFuture() ? $now->diffForHumans($due, true) :
$due->diffForHumans($now,
true);
} else {
// If there is no due_start or due_end, set $timeRemaining to null
$timeRemaining = null;
}
@endphp
@if ($timeRemaining !== null)
@if ($due->isFuture())
<p class="text-sm text-green-600">{{ $timeRemaining }} remaining</p>
@else
<p class="text-sm text-red-600">{{ $timeRemaining }} ago</p>
@endif
@endif
</div>
</div>
</a>
@endforeach

View File

@ -14,6 +14,7 @@
<!-- Scripts -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
@livewireStyles
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body class="font-sans antialiased">
<div class="min-h-screen bg-gray-100 dark:bg-gray-900">