323 lines
10 KiB
Vue
323 lines
10 KiB
Vue
<template>
|
|
<div class="news-list container">
|
|
<div class="row pt-5">
|
|
<div class="col-12 col-lg-6">
|
|
<div class="ranges-title w-100 w-lg-fit">
|
|
<span class="divide d-block w-100 mt-xl-3 my-2" />
|
|
<h1 class="f-sheldon text-uppercase text-lg-start my-3 text-center">
|
|
{{ __('nos actualités') }}
|
|
</h1>
|
|
<span class="divide d-block w-100 mt-xl-3 my-2" />
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-6">
|
|
<div class="d-flex align-items-center h-100 w-100 justify-content-end">
|
|
<fieldset class="select-form position-relative my-3 my-lg-0">
|
|
<select
|
|
class="form-control highlight yellow"
|
|
role="button"
|
|
@change="updateNews($event)"
|
|
:disabled="intLoading"
|
|
>
|
|
<option
|
|
value="0"
|
|
selected
|
|
>
|
|
{{ __('Toutes catégories') }}
|
|
</option>
|
|
<option
|
|
v-for="(categoryEnum, categoryEnumIndex) in categoryEnums"
|
|
:key="categoryEnumIndex"
|
|
:value="categoryEnum.value"
|
|
>
|
|
{{ categoryEnum.label }}
|
|
</option>
|
|
</select>
|
|
</fieldset>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="news-slider d-block d-lg-none bg-agis-white pb-5">
|
|
<div class="swiper swiper-news overflow-hidden">
|
|
<div class="swiper-wrapper">
|
|
<div
|
|
class="swiper-slide swiper-slide-content"
|
|
v-for="(news, id) in newsModels"
|
|
:key="id"
|
|
>
|
|
<div class="swiper-slide-news-container">
|
|
<div class="mx-auto p-4">
|
|
<img
|
|
class="card-img-top"
|
|
:src="(news.picture_thumbnail as string)"
|
|
:alt="(news.picture_thumbnail_alt as string)"
|
|
:title="(news.picture_thumbnail_title as string)"
|
|
>
|
|
<div class="card-body">
|
|
<p class="card-text m-0">
|
|
{{ new Date(news.publish_at as string).toLocaleDateString() }}
|
|
</p>
|
|
<p class="card-text m-0">
|
|
{{ news.title }}
|
|
</p>
|
|
<a
|
|
class="card-text m-0"
|
|
:href="showNews(news.slug as string)"
|
|
>
|
|
{{ __('> Lire la suite') }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex justify-content-around">
|
|
<div class="swiper-button-prev mx-3">
|
|
<img
|
|
src="@/images/global/btn-arrow.png"
|
|
title="{{ __('Navigation du slider (gauche)') }}"
|
|
alt="{{ __('flèche vers la gauche') }}"
|
|
>
|
|
</div>
|
|
<div class="swiper-button-next mx-3">
|
|
<img
|
|
src="@/images/global/btn-arrow.png"
|
|
title="{{ __('Navigation du slider (droite)') }}"
|
|
alt="{{ __('flèche vers la droite') }}"
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="d-none d-lg-flex flex-wrap justify-content-between my-4">
|
|
<div
|
|
v-for="(news, id) in newsModels"
|
|
:key="id"
|
|
class="cards-container"
|
|
>
|
|
<div class="bg-agis-white d-flex flex-column p-4">
|
|
<div class="text-center">
|
|
<img
|
|
class="img-fluid w-auto"
|
|
:src="(news.picture_thumbnail as string)"
|
|
:alt="(news.picture_thumbnail_alt as string)"
|
|
:title="(news.picture_thumbnail_title as string)"
|
|
>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="card-text m-0">
|
|
{{ new Date(news.publish_at as string).toLocaleDateString() }}
|
|
</p>
|
|
<p class="card-text m-0">
|
|
{{ news.title }}
|
|
</p>
|
|
<a
|
|
class="card-text m-0"
|
|
:href="showNews(news.slug as string)"
|
|
>
|
|
{{ __('> Lire la suite') }}
|
|
</a>
|
|
<template
|
|
v-for="(categoryEnum, categoryEnumIndex) in categoryEnums"
|
|
:key="categoryEnumIndex"
|
|
>
|
|
<p
|
|
class="card-text m-0"
|
|
v-if="news.category === categoryEnum.value"
|
|
>
|
|
{{ categoryEnum.label }}
|
|
</p>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import Swiper from "swiper";
|
|
import { Navigation } from "swiper/modules";
|
|
import { defineComponent } from "vue";
|
|
import parseLaravelErrorMessages from "./../../modules/parseLaravelErrorMessages";
|
|
import route from "./../../modules/route";
|
|
import trans from "./../../modules/trans";
|
|
|
|
export default defineComponent({
|
|
name: "NewsListComponent",
|
|
mixins: [trans, route, parseLaravelErrorMessages],
|
|
data(): {
|
|
newsModels: LaravelModelList;
|
|
newsFilteredRoute: string;
|
|
categoryEnums: {
|
|
label: string,
|
|
name: string,
|
|
value: number,
|
|
}[];
|
|
selectedValue: number,
|
|
swiper: Swiper|null,
|
|
intLoading: boolean,
|
|
} {
|
|
return {
|
|
newsModels: [],
|
|
newsFilteredRoute: "",
|
|
categoryEnums: [],
|
|
selectedValue: 0,
|
|
swiper: null,
|
|
intLoading: false,
|
|
};
|
|
},
|
|
computed: {},
|
|
mounted() {
|
|
const json = String(this.$attrs.json ?? "{}"),
|
|
data = JSON.parse(json);
|
|
this.newsModels = 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>;
|
|
document.body.addEventListener("click", function (e: MouseEvent) {
|
|
const target = e.target as HTMLElement | null;
|
|
contactFormSelectList.forEach(select => {
|
|
if (select.contains(target)) {
|
|
select.parentElement!.classList.toggle("active");
|
|
} else {
|
|
select.parentElement!.classList.remove("active");
|
|
}
|
|
});
|
|
});
|
|
},
|
|
/** Init news slider */
|
|
initSwiper() {
|
|
this.swiper = new Swiper(".swiper-news", {
|
|
modules: [Navigation],
|
|
grabCursor: true,
|
|
slidesPerView: 1,
|
|
spaceBetween: 20,
|
|
navigation: {
|
|
nextEl: ".swiper-button-next",
|
|
prevEl: ".swiper-button-prev",
|
|
},
|
|
});
|
|
},
|
|
/** Get selected value */
|
|
getSelectedValue(e: Event): number {
|
|
let target = e.target as HTMLSelectElement | null;
|
|
return Number(target?.value);
|
|
},
|
|
/** Get filtered news route */
|
|
getFilteredNewsRoute(): string {
|
|
const route = this.route("fo.news.filtered");
|
|
if (!route) {
|
|
throw new Error("Undefined route fo.news.filtered");
|
|
}
|
|
return route + "?category=" + this.selectedValue;
|
|
},
|
|
/** Get filtered news */
|
|
getFilteredNews() {
|
|
window.axios
|
|
.get(this.newsFilteredRoute)
|
|
.then((reponse) => {
|
|
this.newsModels = reponse.data;
|
|
this.$nextTick(() => {
|
|
this.swiper?.update();
|
|
this.intLoading = false;
|
|
});
|
|
})
|
|
.catch(this.ajaxErrorHandler);
|
|
},
|
|
/** Return error message after the ajax call */
|
|
ajaxErrorHandler(e: any) {
|
|
this.intLoading = false;
|
|
let message = this.__("Une erreur est survenue");
|
|
if (e.response.status === 422) {
|
|
message =
|
|
this.parseValidationErrors(
|
|
e.response?.data?.errors ?? {}
|
|
) || message;
|
|
if (window.vueDebug) {
|
|
console.warn(e.response.data.errors);
|
|
}
|
|
} else if (e.response.status === 419) {
|
|
// * CSRF TOKEN MISMATCH (On ne pourrais plus faire d'appels AJAX sans recharger la page)
|
|
console.log(this.__("Votre session a expiré, la page va être rechargée"));
|
|
setTimeout(() => window.location.reload(), 2000);
|
|
} else if (e.response.status === 403) {
|
|
// * Access denied
|
|
console.log(this.__("Vous n'êtes pas autorisé a effectuer cette action"));
|
|
setTimeout(() => window.location.reload(), 2000);
|
|
} else {
|
|
if (window.vueDebug) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
console.log(message);
|
|
},
|
|
/** Update news with filtered news */
|
|
updateNews(e: Event) {
|
|
this.intLoading = true;
|
|
this.selectedValue = this.getSelectedValue(e);
|
|
this.newsFilteredRoute = this.getFilteredNewsRoute();
|
|
this.getFilteredNews();
|
|
},
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import "./../../../../node_modules/bootstrap/scss/functions";
|
|
@import "./../../../../node_modules/bootstrap/scss/variables";
|
|
@import "./../../../../node_modules/bootstrap/scss/mixins";
|
|
.news-list {
|
|
.select-form {
|
|
width: 100%;
|
|
@include media-breakpoint-up(lg) {
|
|
width: 20rem;
|
|
}
|
|
&::after {
|
|
position: absolute;
|
|
right: 1rem;
|
|
bottom: 0.3rem;
|
|
display: block;
|
|
width: 2rem;
|
|
height: 2rem;
|
|
background: url("@/images/global/btn-arrow.png");
|
|
background-position: center;
|
|
background-repeat: no-repeat;
|
|
background-size: 100% 100%;
|
|
content: "";
|
|
pointer-events: none;
|
|
transform: rotate(180deg);
|
|
transition: 0.1s ease-in-out;
|
|
}
|
|
|
|
&.active::after {
|
|
background: url("@/images/global/btn-arrow-dark.png");
|
|
background-position: center;
|
|
background-repeat: no-repeat;
|
|
background-size: 100% 100%;
|
|
transform: rotate(270deg);
|
|
}
|
|
}
|
|
.cards-container{
|
|
max-width: 314px;
|
|
}
|
|
}
|
|
</style>
|