perf(Project Loading):

Added Lazy load for project

Restructured some files
This commit is contained in:
devoalda 2023-08-08 22:18:21 +08:00
parent 59ffb4bc32
commit 99d37aafb6
10 changed files with 179 additions and 126 deletions

View File

@ -44,7 +44,7 @@ class DashboardController extends Controller
$todo_completed_count = $this->all_completed_todos()['completed_count']; $todo_completed_count = $this->all_completed_todos()['completed_count'];
return view('dashboard', compact( return view('dashboard.index', compact(
'todos', 'todos',
'incomplete_count', 'incomplete_count',
'project_count', 'project_count',

View File

@ -8,9 +8,12 @@ use App\Models\Project;
use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Models\User; use App\Models\User;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Response;
class ProjectController extends Controller class ProjectController extends Controller
@ -18,15 +21,23 @@ class ProjectController extends Controller
/** /**
* Display Listing of all Projects. * Display Listing of all Projects.
*/ */
public function index(): Application|Factory|View public function index(Request $request): Application|Factory|View|JsonResponse
{ {
$user = User::find(auth()->user()->id); $user = User::find(auth()->user()->id);
$projects = $user->projects; $projects = $user->projects()->paginate(4);
// Aggregate all todos for all projects // Aggregate all todos for all projects
$todos = $projects->map(function ($project) { $todos = $projects->map(function ($project) {
return $project->todos; return $project->todos;
})->flatten(); })->flatten();
if ($request->ajax()){
$view = view('project.load-projects', compact('projects'))->render();
return Response::json([
'view' => $view,
'nextPageUrl' => $projects->nextPageUrl(),
]);
}
return view('project.index', [ return view('project.index', [
'projects' => $projects, 'projects' => $projects,
'todos' => $todos->whereNull('completed_at')->values(), 'todos' => $todos->whereNull('completed_at')->values(),

View File

@ -12,6 +12,14 @@ use App\Models\{
class Pomos extends Component class Pomos extends Component
{ {
public $perPage = 10;
public function loadMore()
{
$this->perPage += 10;
}
public function render() public function render()
{ {
$user = User::find(auth()->id()); $user = User::find(auth()->id());

View File

@ -105,16 +105,16 @@
</p> </p>
</div> </div>
<livewire:dashboard.pomo-count /> <livewire:dashboard.pomo-count/>
<livewire:dashboard.pomo-time /> <livewire:dashboard.pomo-time/>
</div> </div>
</div> </div>
<!-- List out Todos and their details (time left/ago) + checkbox in form to toggle completed --> <!-- List out Todos and their details (time left/ago) + checkbox in form to toggle completed -->
<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"> <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 py-3">
<h2 class="text-2xl font-semibold mb-4 text-green-500 dark:text-green-400"> <h2 class="text-2xl font-semibold mb-4 text-green-500 dark:text-green-400">
Today's Todos Today's Todos
({{ $incomplete_count }}) ({{ $incomplete_count }})
@ -122,42 +122,41 @@
<div class="space-y-4" id="todo-container"> <div class="space-y-4" id="todo-container">
@include('dashboard.load-todo') @include('dashboard.load-todo')
</div> </div>
</div> <!-- Pagination with CSS -->
<div class="invisible">
<!-- Pagination with CSS --> {{ $todos->links() }}
<div class="invisible"> </div>
{{ $todos->links() }}
</div> </div>
<script> <script>
$(document).ready(function () { $(document).ready(function () {
let nextPageUrl = '{{ $todos->nextPageUrl() }}'; let nextPageUrl = '{{ $todos->nextPageUrl() }}';
$(window).scroll(function () { $(window).scroll(function () {
if ($(window).scrollTop() + $(window).height() >= $(document).height() - 100) { if ($(window).scrollTop() + $(window).height() >= $(document).height() - 100) {
if (nextPageUrl) { if (nextPageUrl) {
loadMorePosts(); loadMoreTodos();
} }
} }
}); });
function loadMorePosts() { function loadMoreTodos() {
$.ajax({ $.ajax({
url: nextPageUrl, url: nextPageUrl,
type: 'get', type: 'get',
beforeSend: function () { beforeSend: function () {
nextPageUrl = ''; nextPageUrl = '';
}, },
success: function (data) { success: function (data) {
nextPageUrl = data.nextPageUrl; nextPageUrl = data.nextPageUrl;
$('#todo-container').append(data.view); $('#todo-container').append(data.view);
}, },
error: function (xhr, status, error) { error: function (xhr, status, error) {
console.error("Error loading more posts:", error); console.error("Error loading more posts:", error);
}
});
} }
}); });
}
});
</script> </script>
</x-app-layout> </x-app-layout>

View File

@ -1,6 +1,6 @@
<div class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6"> <div class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6">
<h3 class="text-xl font-semibold mb-4 text-blue-500 dark:text-blue-400"> <h3 class="text-xl font-semibold mb-4 text-blue-500 dark:text-blue-400">
<!-- SVG for Pomo Average Cound --> <!-- SVG for Pomo Average Count -->
<svg class="inline-block h-6 w-6 text-blue-500 dark:text-blue-400" <svg class="inline-block h-6 w-6 text-blue-500 dark:text-blue-400"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"

View File

@ -0,0 +1,46 @@
@foreach ($pomos as $pomo)
<tr class="hover:bg-blue-100 dark:hover:bg-gray-700">
<td class="border px-4 py-2 text-blue-900 dark:text-gray-100">
<a href="{{ route('project.todo.edit', ['project' => $pomo->todo->project->id, 'todo' => $pomo->todo->id]) }}">
{{ $pomo->todo->title }}
</a>
</td>
<!-- Pomo Start and Pomo End -->
<td class="border px-4 py-2 text-blue-900 dark:text-gray-100">
{{ \Carbon\Carbon::createFromTimestamp($pomo->pomo_start)->format('Y-m-d H:i:s') }}
</td>
<td class="border px-4 py-2 text-blue-900 dark:text-gray-100">
{{ \Carbon\Carbon::createFromTimestamp($pomo->pomo_end)->format('Y-m-d H:i:s') }}
</td>
<!-- Duration -->
<td class="border px-4 py-2 text-blue-900 dark:text-gray-100">
{{
\Carbon\Carbon::createFromTimestamp($pomo->pomo_start)->diffInMinutes(\Carbon\Carbon::createFromTimestamp($pomo->pomo_end))
}}
</td>
<td class="border px-4 py-2 text-blue-900 dark:text-gray-100">
<!-- Truncate notes to 32 characters -->
<div class="max-w-sm truncate">
{{ $pomo->notes }}
</div>
</td>
<td class="border px-4 py-2 text-blue-900 dark:text-gray-100">
<!-- Edit and Delete form button groups -->
<div class="flex flex-row">
<div class="flex flex-col">
<a href="{{ route('pomo.edit', $pomo->id) }}"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Edit</a>
</div>
<div class="flex flex-col">
<form action="{{ route('pomo.destroy', $pomo->id) }}" method="POST">
@csrf
@method('DELETE')
<button type="submit"
class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">Delete
</button>
</form>
</div>
</div>
</td>
</tr>
@endforeach

View File

@ -10,53 +10,8 @@
<th class="px-4 py-2">Actions</th> <th class="px-4 py-2">Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody id="pomo-container">
@foreach ($pomos as $pomo) @include('livewire.pomo.load-pomo')
<tr class="hover:bg-blue-100 dark:hover:bg-gray-700">
<td class="border px-4 py-2 text-blue-900 dark:text-gray-100">
<a href="{{ route('project.todo.edit', ['project' => $pomo->todo->project->id, 'todo' => $pomo->todo->id]) }}">
{{ $pomo->todo->title }}
</a>
</td>
<!-- Pomo Start and Pomo End -->
<td class="border px-4 py-2 text-blue-900 dark:text-gray-100">
{{ \Carbon\Carbon::createFromTimestamp($pomo->pomo_start)->format('Y-m-d H:i:s') }}
</td>
<td class="border px-4 py-2 text-blue-900 dark:text-gray-100">
{{ \Carbon\Carbon::createFromTimestamp($pomo->pomo_end)->format('Y-m-d H:i:s') }}
</td>
<!-- Duration -->
<td class="border px-4 py-2 text-blue-900 dark:text-gray-100">
{{
\Carbon\Carbon::createFromTimestamp($pomo->pomo_start)->diffInMinutes(\Carbon\Carbon::createFromTimestamp($pomo->pomo_end))
}}
</td>
<td class="border px-4 py-2 text-blue-900 dark:text-gray-100">
<!-- Truncate notes to 32 characters -->
<div class="max-w-sm truncate">
{{ $pomo->notes }}
</div>
</td>
<td class="border px-4 py-2 text-blue-900 dark:text-gray-100">
<!-- Edit and Delete form button groups -->
<div class="flex flex-row">
<div class="flex flex-col">
<a href="{{ route('pomo.edit', $pomo->id) }}"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Edit</a>
</div>
<div class="flex flex-col">
<form action="{{ route('pomo.destroy', $pomo->id) }}" method="POST">
@csrf
@method('DELETE')
<button type="submit"
class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">Delete
</button>
</form>
</div>
</div>
</td>
</tr>
@endforeach
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -11,53 +11,16 @@
<div class="py-4"> <div class="py-4">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4" id="project-container">
@foreach($projects as $project) @include('project.load-projects')
<div class="relative">
<a href="{{ route('project.todo.index', $project) }}" class="card-link">
<div
class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 hover:shadow-md transition duration-300 ease-in-out transform hover:-translate-y-1">
<div class="text-gray-800 dark:text-gray-100">
<div class="mb-4">
<h3 class="font-semibold text-lg mb-2">{{ $project->name }}</h3>
<p class="text-gray-600 dark:text-gray-400">{{ $project->description }}</p>
</div>
</div>
</div>
</a>
<form action="{{ route('project.destroy', $project) }}" method="POST"
class="delete-project-form absolute top-1 right-1">
@csrf
@method('DELETE')
<button type="button"
class="delete-button text-red-600 hover:text-red-800 transition duration-300 ease-in-out">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
<div class="modal hidden">
<!-- Small Popover, with a background that is visible when modal is open -->
<div class="popover popover-sm bg-white dark:bg-gray-800 shadow-lg rounded-lg p-6">
<p class="mb-4">Are you sure you want to delete this project?</p>
<button type="submit"
class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded mr-2">
Delete
</button>
<button type="button"
class="cancel-button bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Cancel
</button>
</div>
</div>
</form>
</div>
@endforeach
</div> </div>
</div> </div>
</div> </div>
<div class="invisible">
{{ $projects->links() }}
</div>
<script> <script>
const deleteButtons = document.querySelectorAll('.delete-button'); const deleteButtons = document.querySelectorAll('.delete-button');
const cancelButtons = document.querySelectorAll('.cancel-button'); const cancelButtons = document.querySelectorAll('.cancel-button');
@ -72,6 +35,35 @@
modals[index].classList.add('hidden'); modals[index].classList.add('hidden');
}); });
}); });
$(document).ready(function () {
let nextPageUrl = '{{ $projects->nextPageUrl() }}';
$(window).scroll(function () {
if ($(window).scrollTop() + $(window).height() >= $(document).height() - 2000) {
if (nextPageUrl) {
loadMoreProjects();
}
}
});
function loadMoreProjects() {
$.ajax({
url: nextPageUrl,
type: 'get',
beforeSend: function () {
nextPageUrl = '';
},
success: function (data) {
nextPageUrl = data.nextPageUrl;
$('#project-container').append(data.view);
},
error: function (xhr, status, error) {
console.error("Error loading more posts:", error);
}
});
}
});
</script> </script>
@include('todo.todo_list') @include('todo.todo_list')

View File

@ -0,0 +1,42 @@
@foreach($projects as $project)
<div class="relative">
<a href="{{ route('project.todo.index', $project) }}" class="card-link">
<div
class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 hover:shadow-md transition duration-300 ease-in-out transform hover:-translate-y-1">
<div class="text-gray-800 dark:text-gray-100">
<div class="mb-4">
<h3 class="font-semibold text-lg mb-2">{{ $project->name }}</h3>
<p class="text-gray-600 dark:text-gray-400">{{ $project->description }}</p>
</div>
</div>
</div>
</a>
<form action="{{ route('project.destroy', $project) }}" method="POST"
class="delete-project-form absolute top-1 right-1">
@csrf
@method('DELETE')
<button type="button"
class="delete-button text-red-600 hover:text-red-800 transition duration-300 ease-in-out">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
<div class="modal hidden">
<!-- Small Popover, with a background that is visible when modal is open -->
<div class="popover popover-sm bg-white dark:bg-gray-800 shadow-lg rounded-lg p-6">
<p class="mb-4">Are you sure you want to delete this project?</p>
<button type="submit"
class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded mr-2">
Delete
</button>
<button type="button"
class="cancel-button bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Cancel
</button>
</div>
</div>
</form>
</div>
@endforeach

View File

@ -19,7 +19,7 @@ use Illuminate\Support\Facades\Route;
*/ */
Route::get('/', function () { Route::get('/', function () {
return redirect(route('project.index')); return redirect(route('dashboard'));
}); });
Route::get('/dashboard', [DashboardController::class, 'index']) Route::get('/dashboard', [DashboardController::class, 'index'])