Showing
2 changed files
with
43 additions
and
11 deletions
@@ -6,7 +6,7 @@ import { useTMDB } from "~/composables/tMDB"; | @@ -6,7 +6,7 @@ import { useTMDB } from "~/composables/tMDB"; | ||
6 | import { Movie } from "~/models/movie"; | 6 | import { Movie } from "~/models/movie"; |
7 | import { FilmIcon } from "lucide-vue-next"; | 7 | import { FilmIcon } from "lucide-vue-next"; |
8 | import type { MovieInterface } from "~/interfaces/movie"; | 8 | import type { MovieInterface } from "~/interfaces/movie"; |
9 | -import { useDateFormat } from '@vueuse/core' | 9 | +import { useDateFormat } from "@vueuse/core"; |
10 | //#endregion | 10 | //#endregion |
11 | 11 | ||
12 | //#region --Declaration--. | 12 | //#region --Declaration--. |
@@ -26,7 +26,7 @@ const observer = ref<IntersectionObserver | null>(null); | @@ -26,7 +26,7 @@ const observer = ref<IntersectionObserver | null>(null); | ||
26 | 26 | ||
27 | //#region --Computed--. | 27 | //#region --Computed--. |
28 | const movies = computed(() => { | 28 | const movies = computed(() => { |
29 | - return useRepo(Movie).query().orderBy("popularity", 'desc').get() as unknown as MovieInterface[]; | 29 | + return useRepo(Movie).query().orderBy("popularity", "desc").get() as unknown as MovieInterface[]; |
30 | }); | 30 | }); |
31 | //#endregion | 31 | //#endregion |
32 | 32 | ||
@@ -40,7 +40,11 @@ const fetchMovies = async (page: number) => { | @@ -40,7 +40,11 @@ const fetchMovies = async (page: number) => { | ||
40 | isLoadingMore.value = true; | 40 | isLoadingMore.value = true; |
41 | const data = await fetchPopularMovies(page); | 41 | const data = await fetchPopularMovies(page); |
42 | // Save in Movie model. | 42 | // Save in Movie model. |
43 | - isInitialLoading.value ? useRepo(Movie).fresh(data.results) : useRepo(Movie).save(data.results); | 43 | + if (isInitialLoading.value) { |
44 | + useRepo(Movie).fresh(data.results); | ||
45 | + } else { | ||
46 | + useRepo(Movie).save(data.results); | ||
47 | + } | ||
44 | 48 | ||
45 | totalPages.value = data.total_pages; | 49 | totalPages.value = data.total_pages; |
46 | currentPage.value = page; | 50 | currentPage.value = page; |
@@ -63,6 +67,15 @@ function createIntersectionObserver() { | @@ -63,6 +67,15 @@ function createIntersectionObserver() { | ||
63 | { threshold: 1.0 }, | 67 | { threshold: 1.0 }, |
64 | ); | 68 | ); |
65 | } | 69 | } |
70 | + | ||
71 | +function handleSearchEvent(event: string) { | ||
72 | + console.log(event); | ||
73 | +} | ||
74 | + | ||
75 | +function handleClearSearchEvent() { | ||
76 | + console.log("clear event"); | ||
77 | +} | ||
78 | + | ||
66 | //#endregion | 79 | //#endregion |
67 | 80 | ||
68 | //#region --Global event--. | 81 | //#region --Global event--. |
@@ -94,7 +107,11 @@ onBeforeUnmount(() => { | @@ -94,7 +107,11 @@ onBeforeUnmount(() => { | ||
94 | <section> | 107 | <section> |
95 | <h1 class="text-4xl font-bold mb-8 text-center">Découvrez les films populaires</h1> | 108 | <h1 class="text-4xl font-bold mb-8 text-center">Découvrez les films populaires</h1> |
96 | <!-- Barre de recherche --> | 109 | <!-- Barre de recherche --> |
97 | - <search-bar placeholder="Rechercher un film..." /> | 110 | + <search-bar |
111 | + placeholder="Rechercher un film..." | ||
112 | + @event:search="handleSearchEvent" | ||
113 | + @event:clear_search="handleClearSearchEvent" | ||
114 | + /> | ||
98 | <!-- Loading Skeleton --> | 115 | <!-- Loading Skeleton --> |
99 | <skeleton-movies-loader v-if="isInitialLoading" :is-initial-loading="isInitialLoading" :skeleton-number="20" /> | 116 | <skeleton-movies-loader v-if="isInitialLoading" :is-initial-loading="isInitialLoading" :skeleton-number="20" /> |
100 | <!-- Liste des films --> | 117 | <!-- Liste des films --> |
@@ -108,10 +125,10 @@ onBeforeUnmount(() => { | @@ -108,10 +125,10 @@ onBeforeUnmount(() => { | ||
108 | <div class="relative pb-[150%]"> | 125 | <div class="relative pb-[150%]"> |
109 | <img | 126 | <img |
110 | v-if="movie.poster_path" | 127 | v-if="movie.poster_path" |
111 | - :src="`https://image.tmdb.org/t/p/w500${movie.poster_path}`" | ||
112 | :alt="movie.title" | 128 | :alt="movie.title" |
129 | + :src="`https://image.tmdb.org/t/p/w500${movie.poster_path}`" | ||
113 | class="absolute inset-0 w-full h-full object-cover" | 130 | class="absolute inset-0 w-full h-full object-cover" |
114 | - > | 131 | + /> |
115 | <div v-else class="absolute inset-0 w-full h-full bg-gray-700 flex items-center justify-center"> | 132 | <div v-else class="absolute inset-0 w-full h-full bg-gray-700 flex items-center justify-center"> |
116 | <FilmIcon :size="48" class="text-gray-500" /> | 133 | <FilmIcon :size="48" class="text-gray-500" /> |
117 | </div> | 134 | </div> |
@@ -123,13 +140,13 @@ onBeforeUnmount(() => { | @@ -123,13 +140,13 @@ onBeforeUnmount(() => { | ||
123 | </div> | 140 | </div> |
124 | <div class="p-4"> | 141 | <div class="p-4"> |
125 | <h2 class="text-lg font-bold mb-1 line-clamp-1">{{ movie.title }}</h2> | 142 | <h2 class="text-lg font-bold mb-1 line-clamp-1">{{ movie.title }}</h2> |
126 | - <p class="text-sm text-gray-400">{{ useDateFormat(movie.release_date, 'DD-MM-YYYY') }}</p> | 143 | + <p class="text-sm text-gray-400">{{ useDateFormat(movie.release_date, "DD-MM-YYYY") }}</p> |
127 | </div> | 144 | </div> |
128 | </div> | 145 | </div> |
129 | </div> | 146 | </div> |
130 | 147 | ||
131 | <!-- Élément observé pour le défilement infini --> | 148 | <!-- Élément observé pour le défilement infini --> |
132 | - <div ref="loadMoreTrigger" class="h-10 mt-4"/> | 149 | + <div ref="loadMoreTrigger" class="h-10 mt-4" /> |
133 | </section> | 150 | </section> |
134 | </template> | 151 | </template> |
135 | 152 |
@@ -2,10 +2,11 @@ | @@ -2,10 +2,11 @@ | ||
2 | //#region --import--. | 2 | //#region --import--. |
3 | import { SearchIcon, XIcon } from "lucide-vue-next"; | 3 | import { SearchIcon, XIcon } from "lucide-vue-next"; |
4 | import { ref } from "vue"; | 4 | import { ref } from "vue"; |
5 | +import { useDebounceFn } from "@vueuse/core"; | ||
5 | //#endregion | 6 | //#endregion |
6 | 7 | ||
7 | //#region --Emits--. | 8 | //#region --Emits--. |
8 | -// const emit = defineEmits([]); | 9 | +const emit = defineEmits(['event:search', 'event:clear_search']); |
9 | //#endregion | 10 | //#endregion |
10 | 11 | ||
11 | //#region --Props--. | 12 | //#region --Props--. |
@@ -22,6 +23,20 @@ defineProps({ | @@ -22,6 +23,20 @@ defineProps({ | ||
22 | //#region --Data/refs--. | 23 | //#region --Data/refs--. |
23 | const searchQuery = ref(""); | 24 | const searchQuery = ref(""); |
24 | //#endregion | 25 | //#endregion |
26 | + | ||
27 | +//#region --Function--. | ||
28 | +/** | ||
29 | + * Debounced function | ||
30 | + */ | ||
31 | +const handleSearchEvent = useDebounceFn(() => { | ||
32 | + emit('event:search', searchQuery.value); | ||
33 | +}, 500); | ||
34 | + | ||
35 | +function handleClearSearchEvent() { | ||
36 | + searchQuery.value = ''; | ||
37 | + emit('event:clear_search') | ||
38 | +} | ||
39 | +//#endregion | ||
25 | </script> | 40 | </script> |
26 | 41 | ||
27 | <template> | 42 | <template> |
@@ -33,12 +48,12 @@ const searchQuery = ref(""); | @@ -33,12 +48,12 @@ const searchQuery = ref(""); | ||
33 | :placeholder="placeholder" | 48 | :placeholder="placeholder" |
34 | class="w-full px-4 py-3 bg-gray-800 rounded-full text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary" | 49 | class="w-full px-4 py-3 bg-gray-800 rounded-full text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary" |
35 | type="text" | 50 | type="text" |
36 | - @input="console.log('debouncedSearch à prévoir')" | 51 | + @input="handleSearchEvent" |
37 | /> | 52 | /> |
38 | <button | 53 | <button |
39 | v-if="searchQuery" | 54 | v-if="searchQuery" |
40 | class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-white" | 55 | class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-white" |
41 | - @click="console.log('clearSearch à prévoir')" | 56 | + @click="handleClearSearchEvent" |
42 | > | 57 | > |
43 | <XIcon :size="20" /> | 58 | <XIcon :size="20" /> |
44 | </button> | 59 | </button> |
-
Please register or login to post a comment