From be33c3b2138db8d1c388a054f32d95fcf8b37821 Mon Sep 17 00:00:00 2001 From: Thomas Citharel <tcit@tcit.fr> Date: Mon, 31 Oct 2022 18:07:14 +0100 Subject: [PATCH] Search improvements Signed-off-by: Thomas Citharel <tcit@tcit.fr> --- .../Event/SkeletonEventResultList.vue | 20 +++ .../Group/SkeletonGroupResultList.vue | 16 +++ js/src/i18n/en_US.json | 12 +- js/src/i18n/fr_FR.json | 12 +- js/src/views/SearchView.vue | 127 +++++++++++++++--- 5 files changed, 163 insertions(+), 24 deletions(-) create mode 100644 js/src/components/Event/SkeletonEventResultList.vue create mode 100644 js/src/components/Group/SkeletonGroupResultList.vue diff --git a/js/src/components/Event/SkeletonEventResultList.vue b/js/src/components/Event/SkeletonEventResultList.vue new file mode 100644 index 000000000..ce795a14a --- /dev/null +++ b/js/src/components/Event/SkeletonEventResultList.vue @@ -0,0 +1,20 @@ +<template> + <div class="bg-white dark:bg-slate-800 shadow rounded-md w-full mx-auto"> + <div class="animate-pulse flex flex-col sm:flex-row space-3-4 items-center"> + <div class="object-cover h-40 w-72 bg-slate-700 m-2 md:m-4 shrink-0" /> + + <div + class="flex gap-3 flex self-start flex-col justify-between m-2 md:m-4 w-full px-2 md:px-4" + > + <div class="h-3 bg-slate-700 w-52 hidden sm:block"></div> + <div class="h-5 bg-slate-700 w-72 lg:w-96"></div> + <div class="flex items-center"> + <div + class="rounded-full object-cover h-6 w-6 bg-slate-700 mx-2 shrink-0" + /> + <div class="h-3 bg-slate-700 w-52"></div> + </div> + </div> + </div> + </div> +</template> diff --git a/js/src/components/Group/SkeletonGroupResultList.vue b/js/src/components/Group/SkeletonGroupResultList.vue new file mode 100644 index 000000000..4749ff3aa --- /dev/null +++ b/js/src/components/Group/SkeletonGroupResultList.vue @@ -0,0 +1,16 @@ +<template> + <div class="bg-white dark:bg-slate-800 shadow rounded-md w-full mx-auto"> + <div class="animate-pulse flex flex-col sm:flex-row space-3-4 items-center"> + <div + class="object-cover h-40 w-40 rounded-full bg-slate-700 m-2 md:m-4 shrink-0" + /> + + <div + class="flex gap-3 flex self-start flex-col justify-between m-2 md:m-4 self-center w-full px-2 md:px-4" + > + <div class="h-5 bg-slate-700 w-64"></div> + <div class="h-3 bg-slate-700 w-52"></div> + </div> + </div> + </div> +</template> diff --git a/js/src/i18n/en_US.json b/js/src/i18n/en_US.json index 5bdd4c5ba..e8671ce43 100644 --- a/js/src/i18n/en_US.json +++ b/js/src/i18n/en_US.json @@ -1425,5 +1425,15 @@ "Message body": "Message body", "Describe your event": "Describe your event", "A few lines about your group": "A few lines about your group", - "Write your post": "Write your post" + "Write your post": "Write your post", + "Suggestions:": "Suggestions:", + "Make sure that all words are spelled correctly.": "Make sure that all words are spelled correctly.", + "Try different keywords.": "Try different keywords.", + "Try more general keywords.": "Try more general keywords.", + "Try fewer keywords.": "Try fewer keywords.", + "Change the filters.": "Change the filters.", + "No results found for {search}": "No results found for {search}", + "No events found for {search}": "No events found for {search}", + "No groups found for {search}": "No groups found for {search}", + "No event found at this address": "No event found at this address" } \ No newline at end of file diff --git a/js/src/i18n/fr_FR.json b/js/src/i18n/fr_FR.json index e0baf2e3e..02f50b558 100644 --- a/js/src/i18n/fr_FR.json +++ b/js/src/i18n/fr_FR.json @@ -1423,5 +1423,15 @@ "Message body": "Corps du message", "Describe your event": "Décrivez votre événement", "A few lines about your group": "Quelques lignes à propos de votre groupe", - "Write your post": "Écrivez votre billet" + "Write your post": "Écrivez votre billet", + "Suggestions:": "Suggestions :", + "Make sure that all words are spelled correctly.": "Vérifiez l’orthographe des termes de recherche.", + "Try different keywords.": "Essayez d'autres mots.", + "Try more general keywords.": "Utilisez des mots clés plus généraux.", + "Try fewer keywords.": "Spécifiez un moins grand nombre de mots-clés.", + "Change the filters.": "Changez les filtres.", + "No results found for {search}": "Aucun résultat trouvé pour {search}", + "No events found for {search}": "Aucun événement trouvé pour {search}", + "No groups found for {search}": "Aucun groupe trouvé pour {search}", + "No event found at this address": "Aucun événement trouvé à cette addresse" } diff --git a/js/src/views/SearchView.vue b/js/src/views/SearchView.vue index 52808e904..61354e8df 100644 --- a/js/src/views/SearchView.vue +++ b/js/src/views/SearchView.vue @@ -500,11 +500,16 @@ </div> <div v-if="mode === ViewMode.LIST"> <template v-if="contentType === ContentType.ALL"> + <template v-if="searchLoading"> + <SkeletonGroupResultList v-for="i in 2" :key="i" /> + <SkeletonEventResultList v-for="i in 4" :key="i" /> + </template> <o-notification v-if="features && !features.groups" variant="danger"> {{ t("Groups are not enabled on this instance.") }} </o-notification> <div v-else-if="searchGroups && searchGroups?.total > 0"> <GroupCard + class="my-2" v-for="group in searchGroups?.elements" :group="group" :key="group.id" @@ -513,9 +518,6 @@ mode="row" /> </div> - <o-notification v-else-if="searchLoading === false" variant="danger"> - {{ t("No groups found") }} - </o-notification> <div v-if="searchEvents && searchEvents.total > 0"> <event-card mode="row" @@ -529,16 +531,40 @@ class="my-4" /> </div> - <o-notification v-else-if="searchLoading === false" variant="info"> - <p>{{ t("No events found") }}</p> - <p v-if="searchIsUrl && !currentUser?.id"> + <EmptyContent v-else-if="searchLoading === false" icon="magnify"> + <span v-if="searchIsUrl"> + {{ t("No event found at this address") }} + </span> + <span v-else-if="!search"> + {{ t("No results found") }} + </span> + <i18n-t keypath="No results found for {search}" tag="span" v-else> + <template #search> + <b class="">{{ search }}</b> + </template> + </i18n-t> + <template #desc v-if="searchIsUrl && !currentUser?.id"> {{ t( "Only registered users may fetch remote events from their URL." ) }} - </p> - </o-notification> + </template> + <template #desc v-else> + <p class="my-2 text-start"> + {{ t("Suggestions:") }} + </p> + <ul class="list-disc list-inside text-start"> + <li> + {{ t("Make sure that all words are spelled correctly.") }} + </li> + <li>{{ t("Try different keywords.") }}</li> + <li>{{ t("Try more general keywords.") }}</li> + <li>{{ t("Try fewer keywords.") }}</li> + <li>{{ t("Change the filters.") }}</li> + </ul> + </template> + </EmptyContent> <o-pagination v-if=" (searchEvents && searchEvents?.total > EVENT_PAGE_LIMIT) || @@ -556,6 +582,9 @@ /> </template> <template v-else-if="contentType === ContentType.EVENTS"> + <template v-if="searchLoading"> + <SkeletonEventResultList v-for="i in 8" :key="i" /> + </template> <template v-if="searchEvents && searchEvents.total > 0"> <event-card mode="row" @@ -580,24 +609,51 @@ > </o-pagination> </template> - <o-notification v-else-if="searchLoading === false" variant="info"> - <p>{{ t("No events found") }}</p> - <p v-if="searchIsUrl && !currentUser?.id"> + <EmptyContent v-else-if="searchLoading === false" icon="calendar"> + <span v-if="searchIsUrl"> + {{ t("No event found at this address") }} + </span> + <span v-else-if="!search"> + {{ t("No events found") }} + </span> + <i18n-t keypath="No events found for {search}" tag="span" v-else> + <template #search> + <b>{{ search }}</b> + </template> + </i18n-t> + <template #desc v-if="searchIsUrl && !currentUser?.id"> {{ t( "Only registered users may fetch remote events from their URL." ) }} - </p> - </o-notification> + </template> + <template #desc v-else> + <p class="my-2 text-start"> + {{ t("Suggestions:") }} + </p> + <ul class="list-disc list-inside text-start"> + <li> + {{ t("Make sure that all words are spelled correctly.") }} + </li> + <li>{{ t("Try different keywords.") }}</li> + <li>{{ t("Try more general keywords.") }}</li> + <li>{{ t("Try fewer keywords.") }}</li> + <li>{{ t("Change the filters.") }}</li> + </ul> + </template> + </EmptyContent> </template> <template v-else-if="contentType === ContentType.GROUPS"> <o-notification v-if="features && !features.groups" variant="danger"> {{ t("Groups are not enabled on this instance.") }} </o-notification> - + <template v-else-if="searchLoading"> + <SkeletonGroupResultList v-for="i in 6" :key="i" /> + </template> <template v-else-if="searchGroups && searchGroups?.total > 0"> <GroupCard + class="my-2" v-for="group in searchGroups?.elements" :group="group" :key="group.id" @@ -617,9 +673,33 @@ > </o-pagination> </template> - <o-notification v-else-if="searchLoading === false" variant="danger"> - {{ t("No groups found") }} - </o-notification> + <EmptyContent + v-else-if="searchLoading === false" + icon="account-multiple" + > + <span v-if="!search"> + {{ t("No events found") }} + </span> + <i18n-t keypath="No groups found for {search}" tag="span" v-else> + <template #search> + <b>{{ search }}</b> + </template> + </i18n-t> + <template #desc> + <p class="my-2 text-start"> + {{ t("Suggestions:") }} + </p> + <ul class="list-disc list-inside text-start"> + <li> + {{ t("Make sure that all words are spelled correctly.") }} + </li> + <li>{{ t("Try different keywords.") }}</li> + <li>{{ t("Try more general keywords.") }}</li> + <li>{{ t("Try fewer keywords.") }}</li> + <li>{{ t("Change the filters.") }}</li> + </ul> + </template> + </EmptyContent> </template> </div> <event-marker-map @@ -693,6 +773,9 @@ import { IConfig } from "@/types/config.model"; import { TypeNamed } from "@/types/apollo"; import { LatLngBounds } from "leaflet"; import lodashSortBy from "lodash/sortBy"; +import EmptyContent from "@/components/Utils/EmptyContent.vue"; +import SkeletonGroupResultList from "@/components/Group/SkeletonGroupResultList.vue"; +import SkeletonEventResultList from "@/components/Event/SkeletonEventResultList.vue"; const EventMarkerMap = defineAsyncComponent( () => import("@/components/Search/EventMarkerMap.vue") @@ -764,6 +847,10 @@ const arrayTransformer: RouteQueryTransformer<string[]> = { }, }; +const props = defineProps<{ + tag?: string; +}>(); + const page = useRouteQuery("page", 1, integerTransformer); const eventPage = useRouteQuery("eventPage", 1, integerTransformer); const groupPage = useRouteQuery("groupPage", 1, integerTransformer); @@ -775,7 +862,7 @@ const distance = useRouteQuery("distance", "10_km"); const when = useRouteQuery("when", "any"); const contentType = useRouteQuery( "contentType", - ContentType.ALL, + props.tag ? ContentType.EVENTS : ContentType.ALL, enumTransformer(ContentType) ); @@ -819,10 +906,6 @@ const EVENT_PAGE_LIMIT = 16; const GROUP_PAGE_LIMIT = 16; -const props = defineProps<{ - tag?: string; -}>(); - const { features } = useFeatures(); const { eventCategories } = useEventCategories();