add pagination on news
This commit is contained in:
parent
bdbbb9ff9a
commit
fc7dad207e
6 changed files with 193 additions and 93 deletions
|
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers\Fo;
|
|||
|
||||
use App\Enums\StaticPageType;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Lib\Helpers\CollectionHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use App\Models\News;
|
||||
use App\Models\StaticPage;
|
||||
|
|
@ -22,7 +23,7 @@ class NewsController extends Controller
|
|||
$staticPageModel = StaticPage::query()->where('type', StaticPageType::news)->firstOrFail();
|
||||
|
||||
/** @var \App\Models\News $newsModels */
|
||||
$newsModels = News::query()->where('published', true)->orderBy('publish_at', 'desc')->get();
|
||||
$newsModels = News::query()->where('published', true)->orderBy('publish_at', 'desc')->paginate(12);
|
||||
return view('fo.pages.news.index', \compact('staticPageModel', 'newsModels'));
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +62,7 @@ class NewsController extends Controller
|
|||
})
|
||||
->orderBy('publish_at', 'desc')
|
||||
->get();
|
||||
$newsModels = CollectionHelper::paginateManually($newsModels, 12, request()->page);
|
||||
return response()->json($newsModels);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,6 @@
|
|||
&-content {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
&-news-container{
|
||||
background-image: url('@/images/global/bg-white-desktop.png');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&-category-arrow{
|
||||
|
|
|
|||
1
resources/sass/fo/vendor/_bootstrap.scss
vendored
1
resources/sass/fo/vendor/_bootstrap.scss
vendored
|
|
@ -38,6 +38,7 @@
|
|||
@import "bootstrap/scss/breadcrumb";
|
||||
@import "bootstrap/scss/offcanvas";
|
||||
@import "bootstrap/scss/card";
|
||||
@import "bootstrap/scss/placeholders";
|
||||
|
||||
// 6. Overload bootstrap classes
|
||||
@import "./../utilities/bootstrap-custom";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<div class="news-list container">
|
||||
<!-- TITLE & SELECT -->
|
||||
<div class="row pt-5">
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="ranges-title w-100 w-lg-fit">
|
||||
|
|
@ -16,7 +17,7 @@
|
|||
<select
|
||||
class="form-control highlight yellow"
|
||||
role="button"
|
||||
@change="updateNews($event)"
|
||||
@change="updateNews(1)"
|
||||
:disabled="intLoading"
|
||||
>
|
||||
<option
|
||||
|
|
@ -37,9 +38,16 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="news-slider d-block d-lg-none bg-agis-white pb-5">
|
||||
<!-- MOBILE: SLIDER -->
|
||||
<div
|
||||
class="news-slider d-block d-lg-none bg-agis-white pb-5"
|
||||
:class="(newsModels.length) ? '' : 'd-none'"
|
||||
>
|
||||
<div class="swiper swiper-news overflow-hidden">
|
||||
<div class="swiper-wrapper">
|
||||
<div
|
||||
class="swiper-wrapper"
|
||||
@touchend="checkActiveSlide()"
|
||||
>
|
||||
<div
|
||||
class="swiper-slide swiper-slide-content"
|
||||
v-for="(news, id) in newsModels"
|
||||
|
|
@ -62,7 +70,7 @@
|
|||
</p>
|
||||
<a
|
||||
class="card-text m-0"
|
||||
:href="showNews(news.slug as string)"
|
||||
:href="getShowNewsRoute(news.slug as string)"
|
||||
>
|
||||
{{ __('> Lire la suite') }}
|
||||
</a>
|
||||
|
|
@ -74,28 +82,75 @@
|
|||
<div class="d-flex justify-content-around">
|
||||
<div class="swiper-button-prev mx-3">
|
||||
<img
|
||||
@click="checkActiveSlide()"
|
||||
src="@/images/global/btn-arrow.png"
|
||||
title="{{ __('Navigation du slider (gauche)') }}"
|
||||
alt="{{ __('flèche vers la gauche') }}"
|
||||
:title="__('Navigation du slider (gauche)')"
|
||||
:alt="__('flèche vers la gauche')"
|
||||
role="button"
|
||||
>
|
||||
</div>
|
||||
<div class="swiper-button-next mx-3">
|
||||
<button
|
||||
v-if="intLoadingMore"
|
||||
class="btn btn-primary"
|
||||
@click="updateNews(page + 1, true)"
|
||||
>
|
||||
{{ __('> Charger plus') }}
|
||||
</button>
|
||||
<div
|
||||
class="swiper-button-next mx-3"
|
||||
:class="(intLoadingMore) ? 'd-none': ''"
|
||||
>
|
||||
<img
|
||||
@click="checkActiveSlide()"
|
||||
src="@/images/global/btn-arrow.png"
|
||||
title="{{ __('Navigation du slider (droite)') }}"
|
||||
alt="{{ __('flèche vers la droite') }}"
|
||||
:title="__('Navigation du slider (droite)')"
|
||||
:alt="__('flèche vers la droite')"
|
||||
role="button"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-lg-flex flex-wrap justify-content-between my-4">
|
||||
<!-- DESKTOP: FLEX WRAP -->
|
||||
<div class="position-relative d-none d-lg-flex row my-4">
|
||||
<template v-if="intLoading">
|
||||
<div class="position-absolute top-0 left-0 bg-secondary w-100 h-100 d-flex justify-content-center align-items-center bg-opacity-25 z-1">
|
||||
<div
|
||||
class="spinner-border"
|
||||
role="status"
|
||||
>
|
||||
<span class="visually-hidden">{{ __('Loading...') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="index in paginator.per_page"
|
||||
:key="index"
|
||||
class="cards-container col-4"
|
||||
>
|
||||
<div
|
||||
class="bg-agis-white d-flex flex-column p-4 my-2"
|
||||
:class="((index % 3 === 0) ? 'ms-auto' : (((index % 3 === 1)) ? 'me-auto' : 'mx-auto'))"
|
||||
>
|
||||
<div class="placeholder-glow">
|
||||
<span class="placeholder placeholder-image" />
|
||||
<span class="placeholder col-12" />
|
||||
<span class="placeholder col-12" />
|
||||
<span class="placeholder col-12" />
|
||||
<span class="placeholder col-12" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
v-else
|
||||
v-for="(news, id) in newsModels"
|
||||
:key="id"
|
||||
class="cards-container"
|
||||
class="cards-container col-4"
|
||||
>
|
||||
<div class="bg-agis-white d-flex flex-column p-4">
|
||||
<div
|
||||
class="bg-agis-white d-flex flex-column p-4 my-2"
|
||||
:class="((id % 3 === 0) ? 'me-auto' : (((id % 3 === 1)) ? 'mx-auto' : 'ms-auto'))"
|
||||
>
|
||||
<div class="text-center">
|
||||
<img
|
||||
class="img-fluid w-auto"
|
||||
|
|
@ -113,7 +168,7 @@
|
|||
</p>
|
||||
<a
|
||||
class="card-text m-0"
|
||||
:href="showNews(news.slug as string)"
|
||||
:href="getShowNewsRoute(news.slug as string)"
|
||||
>
|
||||
{{ __('> Lire la suite') }}
|
||||
</a>
|
||||
|
|
@ -132,6 +187,44 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- PAGINATION -->
|
||||
<div
|
||||
v-if="paginator.total > paginator.per_page"
|
||||
class="d-none d-lg-flex row w-100 mx-auto"
|
||||
>
|
||||
<nav
|
||||
class="py-4"
|
||||
:aria-label="__('Navigation des produits par gamme')"
|
||||
>
|
||||
<ul
|
||||
class="pagination d-flex justify-content-center align-items-center list-unstyled d-flex mb-0"
|
||||
>
|
||||
<template v-for="(link, linkIndex) of paginator.links">
|
||||
<li
|
||||
:key="linkIndex"
|
||||
v-if="link.url"
|
||||
:class="`page-item d-flex ff-poppins-bold fs-md ${
|
||||
link.active ? 'active' : ''
|
||||
}`"
|
||||
>
|
||||
<a
|
||||
@click.prevent="navigateOnPage(String(link.url))"
|
||||
class="page-link d-flex align-items-center"
|
||||
:href="link.url"
|
||||
v-html="String(link.label)"
|
||||
/>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<!-- NO NEWS -->
|
||||
<p
|
||||
v-if="!intLoading"
|
||||
:class="(newsModels.length <= 0) ? '' : 'd-none'"
|
||||
>
|
||||
{{ __('Aucune actualité n\'est disponible') }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -157,6 +250,10 @@ export default defineComponent({
|
|||
selectedValue: number,
|
||||
swiper: Swiper|null,
|
||||
intLoading: boolean,
|
||||
intLoadingMore: boolean,
|
||||
concatNewsFiltered: boolean,
|
||||
page: number;
|
||||
paginator: LaravelPaginator;
|
||||
} {
|
||||
return {
|
||||
newsModels: [],
|
||||
|
|
@ -165,34 +262,39 @@ export default defineComponent({
|
|||
selectedValue: 0,
|
||||
swiper: null,
|
||||
intLoading: false,
|
||||
intLoadingMore: false,
|
||||
concatNewsFiltered: false,
|
||||
page: 1,
|
||||
paginator: {
|
||||
current_page: 1,
|
||||
first_page_url: "",
|
||||
from: 1,
|
||||
last_page: 1,
|
||||
last_page_url: "",
|
||||
links: [],
|
||||
next_page_url: null,
|
||||
path: "",
|
||||
per_page: 12,
|
||||
prev_page_url: null,
|
||||
to: 1,
|
||||
total: 1,
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
mounted() {
|
||||
const json = String(this.$attrs.json ?? "{}"),
|
||||
data = JSON.parse(json);
|
||||
this.newsModels = data.newsModels;
|
||||
this.newsModels = data.newsModels.data;
|
||||
this.paginator = data.newsModels;
|
||||
this.categoryEnums = data.categoryEnums;
|
||||
this.initSelect();
|
||||
this.initSwiper();
|
||||
},
|
||||
methods: {
|
||||
showNews (slug: string): string {
|
||||
const route = this.route("fo.news.show", {
|
||||
SLUG: slug,
|
||||
});
|
||||
if (!route) {
|
||||
throw new Error("Undefined route fo.news.show");
|
||||
}
|
||||
return route;
|
||||
},
|
||||
formatDate (formDate: string | Date) {
|
||||
const date = new Date(formDate);
|
||||
return date.toLocaleDateString;
|
||||
},
|
||||
initSelect() {
|
||||
// * Toggle la dropdown Arrow du select.
|
||||
const contactFormSelectList = document.querySelectorAll(".news-list select") as NodeListOf<HTMLSelectElement>;
|
||||
/** Init select ::after animation */
|
||||
initSelect(): void {
|
||||
const contactFormSelectList = document.querySelectorAll(".news-list .select-form select") as NodeListOf<HTMLSelectElement>;
|
||||
document.body.addEventListener("click", function (e: MouseEvent) {
|
||||
const target = e.target as HTMLElement | null;
|
||||
contactFormSelectList.forEach(select => {
|
||||
|
|
@ -205,7 +307,7 @@ export default defineComponent({
|
|||
});
|
||||
},
|
||||
/** Init news slider */
|
||||
initSwiper() {
|
||||
initSwiper(): void {
|
||||
this.swiper = new Swiper(".swiper-news", {
|
||||
modules: [Navigation],
|
||||
grabCursor: true,
|
||||
|
|
@ -218,8 +320,8 @@ export default defineComponent({
|
|||
});
|
||||
},
|
||||
/** Get selected value */
|
||||
getSelectedValue(e: Event): number {
|
||||
let target = e.target as HTMLSelectElement | null;
|
||||
getSelectedValue(): number {
|
||||
let target = document.querySelector(".news-list .select-form select") as HTMLSelectElement | null;
|
||||
return Number(target?.value);
|
||||
},
|
||||
/** Get filtered news route */
|
||||
|
|
@ -228,23 +330,39 @@ export default defineComponent({
|
|||
if (!route) {
|
||||
throw new Error("Undefined route fo.news.filtered");
|
||||
}
|
||||
return route + "?category=" + this.selectedValue;
|
||||
return route + "?page=" + this.page + "&category=" + this.selectedValue;
|
||||
},
|
||||
/** Get show news route */
|
||||
getShowNewsRoute(slug: string): string {
|
||||
const route = this.route("fo.news.show", {
|
||||
SLUG: slug,
|
||||
});
|
||||
if (!route) {
|
||||
throw new Error("Undefined route fo.news.show");
|
||||
}
|
||||
return route;
|
||||
},
|
||||
/** Get filtered news */
|
||||
getFilteredNews() {
|
||||
getFilteredNews(): void {
|
||||
window.axios
|
||||
.get(this.newsFilteredRoute)
|
||||
.then((reponse) => {
|
||||
this.newsModels = reponse.data;
|
||||
.then((response) => {
|
||||
if (this.intLoadingMore && this.concatNewsFiltered) {
|
||||
this.newsModels = this.newsModels.concat(Object.values(response.data.data));
|
||||
} else {
|
||||
this.newsModels = Object.values(response.data.data);
|
||||
}
|
||||
this.paginator = response.data;
|
||||
this.intLoading = false;
|
||||
this.intLoadingMore = false;
|
||||
this.$nextTick(() => {
|
||||
this.swiper?.update();
|
||||
this.intLoading = false;
|
||||
});
|
||||
})
|
||||
.catch(this.ajaxErrorHandler);
|
||||
},
|
||||
/** Return error message after the ajax call */
|
||||
ajaxErrorHandler(e: any) {
|
||||
ajaxErrorHandler(e: any): void {
|
||||
this.intLoading = false;
|
||||
let message = this.__("Une erreur est survenue");
|
||||
if (e.response.status === 422) {
|
||||
|
|
@ -271,20 +389,40 @@ export default defineComponent({
|
|||
console.log(message);
|
||||
},
|
||||
/** Update news with filtered news */
|
||||
updateNews(e: Event) {
|
||||
updateNews(page: number, concat = false as boolean): void {
|
||||
this.concatNewsFiltered = concat;
|
||||
this.page = page;
|
||||
this.intLoading = true;
|
||||
this.selectedValue = this.getSelectedValue(e);
|
||||
this.selectedValue = this.getSelectedValue();
|
||||
this.newsFilteredRoute = this.getFilteredNewsRoute();
|
||||
this.getFilteredNews();
|
||||
},
|
||||
/** Get all links from the paginator */
|
||||
navigateOnPage(pageUrl: string): void {
|
||||
new URLSearchParams(pageUrl).get("page");
|
||||
this.page = Number.parseInt(
|
||||
String(new URL(pageUrl).searchParams.get("page") ?? 1)
|
||||
);
|
||||
this.updateNews(this.page);
|
||||
},
|
||||
/** Check if it's the last slide is activated */
|
||||
checkActiveSlide(): void {
|
||||
setTimeout(() => {
|
||||
this.intLoadingMore = (
|
||||
this.swiper?.activeIndex !== undefined &&
|
||||
this.paginator.last_page > this.page &&
|
||||
this.paginator.current_page * this.paginator.per_page <= (this.swiper?.activeIndex + 1)
|
||||
) ? true : false;
|
||||
}, 300);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "./../../../../node_modules/bootstrap/scss/functions";
|
||||
@import "./../../../../node_modules/bootstrap/scss/variables";
|
||||
@import "./../../../../node_modules/bootstrap/scss/mixins";
|
||||
@import "./../../../../node_modules/bootstrap/scss/functions";
|
||||
@import "./../../../../node_modules/bootstrap/scss/variables";
|
||||
@import "./../../../../node_modules/bootstrap/scss/mixins";
|
||||
.news-list {
|
||||
.select-form {
|
||||
width: 100%;
|
||||
|
|
@ -316,8 +454,13 @@ export default defineComponent({
|
|||
transform: rotate(270deg);
|
||||
}
|
||||
}
|
||||
.cards-container{
|
||||
.cards-container .bg-agis-white {
|
||||
max-width: 314px;
|
||||
.placeholder-image {
|
||||
max-width: 265px;
|
||||
width: 100%;
|
||||
height: 354px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
<div class="swiper-slide d-md-block d-lg-none">
|
||||
<div class="swiper-slide-news-container mx-auto mb-4">
|
||||
<div class="swiper-slide-content d-flex flex-column justify-content-center align-items-center mx-auto p-4">
|
||||
<img src="{{ asset($newsModel->picture_thumbnail) }}" title="{{ $newsModel->picture_thumbnail_title }}"
|
||||
alt="{{ $newsModel->picture_thumbnail_alt }}">
|
||||
<div
|
||||
class="d-flex flex-column justify-content-start align-items-start w-75 @if ($key % 2) mb-3 @else mt-3 @endif">
|
||||
<p class="f-readex-pro m-0">
|
||||
{{ \Carbon\Carbon::parse($newsModel->publish_at)->locale('fr_FR')->isoFormat('D MMM Y') }}</p>
|
||||
<p class="m-0 text-start">{{ $newsModel->title }}</p>
|
||||
<a href="{{ route('fo.news.show', $newsModel->slug) }}">{{ __('> Lire la suite') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-evenly align-items-center d-lg-none w-100 flex-row pb-3">
|
||||
<div class="swiper-button-prev rounded-circle" role="button">
|
||||
<img src="{{ Vite::asset('resources/assets/images/global/btn-arrow.png') }}"
|
||||
title="{{ __('Navigation du slider (gauche)') }}" alt="{{ __('flèche vers la gauche') }}">
|
||||
</div>
|
||||
<div class="swiper-button-next rounded-circle" role="button">
|
||||
<img src="{{ Vite::asset('resources/assets/images/global/btn-arrow.png') }}"
|
||||
title="{{ __('Navigation du slider (droite)') }}" alt="{{ __('flèche vers la droite') }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3 card-container d-none d-lg-block m-4">
|
||||
<div class="d-flex @if ($key % 2 === 1) flex-column-reverse @else flex-column @endif m-4">
|
||||
<div class="text-center">
|
||||
<img class="img-fluid w-auto" src="{{ $newsModel->picture_thumbnail }}" title="{{ $newsModel->picture_thumbnail_title }}"
|
||||
alt="{{ $newsModel->picture_thumbnail_alt }}" />
|
||||
</div>
|
||||
<div class="">
|
||||
<p>{{ \Carbon\Carbon::parse($newsModel->publish_at)->locale('fr_FR')->isoFormat('D MMM Y') }}</p>
|
||||
<h6>{{ $newsModel->title }}</h6>
|
||||
<a href="{{ route('fo.news.show', $newsModel->slug) }}">{{ __('> Lire la suite') }}</a>
|
||||
<p>{{ $newsModel->category->label() }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
<section class="news position-relative bg-agis-grey pb-5">
|
||||
@php
|
||||
$data = [
|
||||
'newsModels' => $newsModels,
|
||||
'newsModels' => json_decode(json_encode($newsModels)),
|
||||
'categoryEnums' => \App\Enums\News\CategoryEnum::toArray(),
|
||||
];
|
||||
@endphp
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue