200 lines
5.8 KiB
Vue
200 lines
5.8 KiB
Vue
<template>
|
|
<div class="search-component row">
|
|
<!-- INPUT -->
|
|
<div class="col-12 col-md-6 mb-5 mx-auto text-center">
|
|
<div class="input-group mx-auto">
|
|
<div class="form-floating">
|
|
<input
|
|
id="search-input"
|
|
ref="searchInput"
|
|
class="form-control fs-5 border-dark border-1 border-end-0 f-bourton-base shadow-none rounded-start-2"
|
|
type="text"
|
|
name="search"
|
|
:value="searchValue"
|
|
@keydown="executeSearchFromKey($event)"
|
|
:placeholder="__('Rechercher...')"
|
|
>
|
|
<label for="search-input">{{ __('Rechercher...') }}</label>
|
|
</div>
|
|
<button
|
|
class="btn btn-outline-dark shadow-none border-end-0"
|
|
id="btn-search-empty"
|
|
type="button"
|
|
@click="resetSearch()"
|
|
:title="__('Effacer la recherche')"
|
|
:aria-label="__('Effacer le contenu la barre de recherche')"
|
|
>
|
|
<i class="fa-solid fa-xmark" />
|
|
</button>
|
|
<button
|
|
class="btn btn-outline-dark shadow-none rounded-end-2"
|
|
type="button"
|
|
@click="setSearch()"
|
|
:title="__('Lancer la recherche')"
|
|
:aria-label="__('Lancer la recherche')"
|
|
>
|
|
<i class="fa-solid fa-magnifying-glass" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- RESULTS -->
|
|
<template v-if="elements.length">
|
|
<template
|
|
v-for="(element, elementKey) in elements"
|
|
:key="elementKey"
|
|
>
|
|
<div class="col-12 mb-5">
|
|
<MeilisearchPaginateViewsComponent
|
|
:identifier="elementKey"
|
|
:search="searchInput?.value"
|
|
:route-url="routeUrl"
|
|
:trans-name="element.name"
|
|
:class-name="element.class"
|
|
:default-models="element.models"
|
|
@reset-search="resetSearch"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { __ } from "@modules/trans";
|
|
import type { SwiperContainer } from "swiper/element";
|
|
import { computed, nextTick, onMounted, ref, useAttrs } from "vue";
|
|
import axios from "~/axios";
|
|
import parseLaravelErrorMessages from "../../modules/parseLaravelErrorMessages";
|
|
import MeilisearchPaginateViewsComponent from "./MeilisearchPaginateViewsComponent.vue";
|
|
|
|
defineOptions({
|
|
name: "MeilisearchModelsComponent"
|
|
});
|
|
|
|
// * ATTRIBUTES
|
|
const attrs = useAttrs();
|
|
|
|
// * REFS
|
|
const searchInput = ref<HTMLInputElement|null>(null);
|
|
|
|
// * DATA
|
|
const elements = ref<Array<{name: string, class: string, models: LaravelPaginator}>>([]);
|
|
const searchValue = ref<string|null>(null);
|
|
const routeUrl = ref<string>("");
|
|
const loading = ref<boolean>(false);
|
|
|
|
// * COMPUTED
|
|
const isUsedWithProps = computed<boolean>(() => attrs.json === undefined);
|
|
|
|
// * LIFECYCLE
|
|
onMounted(() => {
|
|
const json = String(attrs.json ?? "{}"),
|
|
data = JSON.parse(json);
|
|
if (!isUsedWithProps.value) {
|
|
elements.value = data.elements;
|
|
searchValue.value = data.searchValue;
|
|
routeUrl.value = data.routeUrl;
|
|
}
|
|
});
|
|
|
|
// * METHODS
|
|
|
|
/**
|
|
* Reset all filters activated.
|
|
* @return void
|
|
*/
|
|
function resetSearch(): void {
|
|
searchInput.value!.value = "";
|
|
searchValue.value = "";
|
|
setSearch();
|
|
history.replaceState("Object", "", "/recherche");
|
|
}
|
|
|
|
/**
|
|
* Execute the search when the enter key is down.
|
|
* @return void
|
|
*/
|
|
function executeSearchFromKey(event: KeyboardEvent): void {
|
|
if (event.key === "Enter") {
|
|
setSearch();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get models by activated filters.
|
|
* @return void
|
|
*/
|
|
function setSearch(): void {
|
|
if (!routeUrl.value) {
|
|
throw new Error(`Route ${routeUrl.value} doesn't exist`);
|
|
}
|
|
searchValue.value = String(searchInput.value?.value);
|
|
loading.value = true;
|
|
window.axios
|
|
.post(routeUrl.value, {
|
|
"texte": searchValue.value,
|
|
"models": elements.value.map((element) => { return element.class; }),
|
|
"page": 1,
|
|
})
|
|
.then((response) => {
|
|
elements.value.forEach((element, index) => {
|
|
// Erase existing results by the new.
|
|
element.models = response.data[index];
|
|
// Update sliders in children components.
|
|
const swiperContainer = document.querySelector(`.swiper-search-${index}`) as SwiperContainer|null;
|
|
swiperContainer?.swiper.slideTo(0);
|
|
nextTick(() => {
|
|
swiperContainer?.swiper.update();
|
|
});
|
|
});
|
|
history.replaceState("Object", "", "/recherche" + ((searchValue.value?.length) ? "?texte=" + searchValue.value : ""));
|
|
}).then(() => {
|
|
loading.value = false;
|
|
document.getElementById("search-input")?.scrollIntoView();
|
|
})
|
|
.catch(ajaxErrorHandler);
|
|
}
|
|
|
|
/**
|
|
* Return error message after the ajax call.
|
|
* @param e any
|
|
* @return void
|
|
*/
|
|
function ajaxErrorHandler(e: unknown): void {
|
|
let message = __("Une erreur est survenue");
|
|
|
|
if (axios.isAxiosError<LaravelValidationErrorResponse>(e)) {
|
|
if (e.response) {
|
|
if (e.response.status === 422) {
|
|
message = parseLaravelErrorMessages.methods.parseValidationErrors(
|
|
e.response?.data?.errors ?? {}
|
|
) || message;
|
|
if (window.vueDebug) {
|
|
console.warn(e.response.data.errors);
|
|
}
|
|
} else if (e.response.status === 419) {
|
|
console.log(__("Votre session a expiré, la page va être rechargée"));
|
|
setTimeout(() => window.location.reload(), 2000);
|
|
} else if (e.response.status === 403) {
|
|
console.log(__("Vous n'êtes pas autorisé a effectuer cette action"));
|
|
setTimeout(() => window.location.reload(), 2000);
|
|
} else {
|
|
if (window.vueDebug) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
} else if (e.request) {
|
|
console.error("Aucune réponse reçue :", e.request);
|
|
} else {
|
|
console.error("Erreur de configuration de la requête :", e.message);
|
|
}
|
|
}
|
|
console.log(message);
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
#search-input {
|
|
scroll-margin-top: 8rem;
|
|
}
|
|
</style>
|