Created
September 12, 2025 19:14
-
-
Save dedemenezes/9f6df4c0b172fd4176537fd1b3792208 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/app/frontend/components/features/filters/ResponsiveFilterMenu.vue b/app/frontend/components/features/filters/ResponsiveFilterMenu.vue | |
| index a80bacc..93ef2cc 100644 | |
| --- a/app/frontend/components/features/filters/ResponsiveFilterMenu.vue | |
| +++ b/app/frontend/components/features/filters/ResponsiveFilterMenu.vue | |
| @@ -1,3 +1,27 @@ | |
| +<!-- | |
| +// WE REMOVED EVERYHTING BELOW | |
| +// AND NOW THE WHOLE DATA FLOW | |
| +// INSIDE THIS COMPONENT RELIES | |
| +// ON THE MODELVALUE PROPS PASSED FROM THE PARENT | |
| +// IT WILL FORWARD THE MODELVALUE | |
| +// TO THE SEARCHFILTER COMPONENT | |
| +// AS PROP :modelValue AND RECEIVE | |
| +// IT BACK IN THE TEMPLATE THROUGH | |
| +// MODELVALUE :modelValue | |
| + | |
| +// WHEN SEARCHFILTER UPDATES MODELVALUE | |
| +// IT WILL FORWARD THE EVENT UPDATE:MODELVALUE | |
| +// WE RECEIVE HERE, UPDATE THE KEY FILTER | |
| +// INSIDE MODELVALUE WITH THE USER SELECTED OPTION | |
| +// AND FORWARD AN UPDATE:MODELVALUE EVENT | |
| + | |
| +// THIS LAST EMIT IS NOT RECEIVED HERE BUT IN THE PARENT | |
| +// AND THEN WE SUBMIT THE FILTER IN THE PARENT | |
| + | |
| +// A HUGE CHAIN THAT FOR SURE CAN BE SIMPLIFIED | |
| +// BECAUSE THE SEARCHFILTER ALSO HAS AN UPDATEFIELD FUNCTION | |
| +// THIS SHOULD BE ONLY IN ONE PLACE. | |
| + --> | |
| <script setup> | |
| import { ref, watch, useTemplateRef } from "vue"; | |
| import { IconClose } from "@/components/common/icons"; | |
| @@ -6,23 +30,10 @@ import TwContainer from "@/components/layout/TwContainer.vue"; | |
| const props = defineProps({ | |
| isOpen: { type: Boolean, required: true }, | |
| - initialFilters: { type: Object, required: true }, | |
| + // initialFilters: { type: Object, required: true }, | |
| modelValue: { type: Object, required: true }, | |
| }); | |
| -// Use a single internal state for both desktop and mobile | |
| -const internalFilters = ref({ ...props.modelValue }); | |
| - | |
| -// Watch for changes in modelValue from parent | |
| -watch(() => props.modelValue, (newValue) => { | |
| - internalFilters.value = { ...newValue }; | |
| -}, { deep: true, immediate: true }); | |
| - | |
| -// Watch for changes in initialFilters (after successful submission) | |
| -watch(() => props.initialFilters, (newFilters) => { | |
| - internalFilters.value = { ...newFilters }; | |
| -}, { deep: true }); | |
| - | |
| const emit = defineEmits([ | |
| "update:modelValue", | |
| "filtersApplied", | |
| @@ -35,31 +46,27 @@ const closeBtn = useTemplateRef('close-btn'); | |
| // Unified update function that emits changes back to parent | |
| const updateField = (field, value) => { | |
| console.log(`ResponsiveFilterMenu updating ${field}:`, value); | |
| - | |
| - // Update internal state | |
| - internalFilters.value[field] = value; | |
| - | |
| // Emit the change back to parent | |
| - const updatedFilters = { ...internalFilters.value }; | |
| + const updatedFilters = { ...props.modelValue, [field]: value }; | |
| emit('update:modelValue', updatedFilters); | |
| }; | |
| // Handle filter application | |
| const handleFiltersApplied = () => { | |
| - console.log('Filters applied:', internalFilters.value); | |
| - emit('filtersApplied', internalFilters.value); | |
| + console.log('Filters applied:', props.modelValue); | |
| + emit('filtersApplied', props.modelValue); | |
| }; | |
| // Handle filter clearing | |
| const handleFiltersCleared = () => { | |
| - console.log('Filters cleared'); | |
| - // Reset internal filters | |
| - const clearedFilters = Object.keys(internalFilters.value).reduce((acc, key) => { | |
| + console.log('Clearing Filters...'); | |
| + // Reset internal filters = RESET MODELVALUE | |
| + const clearedFilters = Object.keys(props.modelValue).reduce((acc, key) => { | |
| acc[key] = null; | |
| return acc; | |
| }, {}); | |
| + console.log('Filters cleared', clearedFilters); | |
| - internalFilters.value = clearedFilters; | |
| emit('update:modelValue', clearedFilters); | |
| emit('filtersCleared'); | |
| }; | |
| @@ -68,11 +75,11 @@ const handleFiltersCleared = () => { | |
| <template> | |
| <!-- Debug info --> | |
| <!-- <div class="bg-blue-50 p-2 mb-4 text-xs"> | |
| - <p><strong>ResponsiveFilterMenu internalFilters:</strong> {{ internalFilters }}</p> | |
| <p><strong>ResponsiveFilterMenu modelValue:</strong> {{ modelValue }}</p> | |
| </div> --> | |
| <!-- Desktop Layout: Sticky sidebar (always visible) --> | |
| + <!-- add js to isDesktop --> | |
| <div | |
| style="margin-top: 0" | |
| class="desktop-style bg-white hidden md:flex md:flex-col sticky top-0" | |
| @@ -90,8 +97,8 @@ const handleFiltersCleared = () => { | |
| <!-- Desktop Filter Content --> | |
| <SearchFilter | |
| - v-model="internalFilters" | |
| - @update:modelValue="(val) => emit('update:modelValue', val)" | |
| + :modelValue="props.modelValue" | |
| + @update:modelValue="emit('update:modelValue', $event)" | |
| @filtersApplied="handleFiltersApplied" | |
| @filtersCleared="handleFiltersCleared" | |
| @close-filter-menu="emit('close-filter-menu')" | |
| @@ -99,7 +106,7 @@ const handleFiltersCleared = () => { | |
| <template #filters="slotProps"> | |
| <slot | |
| name="filters" | |
| - :modelValue="internalFilters" | |
| + :modelValue="modelValue" | |
| :updateField="updateField" | |
| /> | |
| </template> | |
| @@ -144,7 +151,7 @@ const handleFiltersCleared = () => { | |
| <!-- Mobile Filter Content --> | |
| <SearchFilter | |
| - v-model="internalFilters" | |
| + :modelValue="modelValue" | |
| @update:modelValue="(val) => emit('update:modelValue', val)" | |
| @filtersApplied="handleFiltersApplied" | |
| @filtersCleared="handleFiltersCleared" | |
| @@ -153,7 +160,7 @@ const handleFiltersCleared = () => { | |
| <template #filters="slotProps"> | |
| <slot | |
| name="filters" | |
| - :modelValue="internalFilters" | |
| + :modelValue="modelValue" | |
| :updateField="updateField" | |
| /> | |
| </template> | |
| diff --git a/app/frontend/pages/ProgramPage.vue b/app/frontend/pages/ProgramPage.vue | |
| index eba40b5..eb05572 100644 | |
| --- a/app/frontend/pages/ProgramPage.vue | |
| +++ b/app/frontend/pages/ProgramPage.vue | |
| @@ -7,6 +7,15 @@ | |
| // TODO: Add icon for menu tabs scroll | |
| // TODO: Fix image urls | |
| +// FOLLOWING THE COMMENTS INSIDE RESPONSIVE MENU | |
| +// IN HERE WE RECEIVE ALL THE FILTER OPTIONS AS PROPS | |
| +// WE RECEIVE THE CURRENT_FILTERS FROM THE CONTROLLER | |
| +// A HASH CONTAINING THE SELECTED OPTION | |
| +// WITH STRUCTURE PREDEFINED | |
| +// {filter_display: '', filter_value: '', filter_label: ''} | |
| +// but maybe this is not mandatory | |
| + | |
| + | |
| import { ref, watch } from "vue"; | |
| import { router } from "@inertiajs/vue3" | |
| @@ -58,46 +67,48 @@ const initializeFilters = () => ({ | |
| pais: null, | |
| direcao: null, | |
| elenco: null, | |
| - ...props.current_filters // Override with actual values | |
| }) | |
| +// Override with reactive filter with | |
| +// current filtered values in case | |
| +// there are any coming from controller | |
| +const overrideFiltersValues = () => { | |
| + const emptyFilters = initializeFilters() | |
| + return {emptyFilters, ...props.current_filters} | |
| +} | |
| -const localFilters = ref(initializeFilters()) | |
| -const filters = ref(initializeFilters()) | |
| +const filters = ref(overrideFiltersValues()) | |
| // Watch for prop changes and update our local state | |
| +// DONT THINK WE NEED AS THE PROP COMES FROM THE CONTROLLER | |
| +// WHEN WE CHANGE A FILTER WE WILL AND SHOULD | |
| +// ALWAYS UPDATE FILTERS REACTIVE AND NOT PROPS | |
| +// PROPS DONT CHANGE VALUE | |
| watch(() => props.current_filters, (newFilters) => { | |
| - localFilters.value = initializeFilters() | |
| - filters.value = initializeFilters() | |
| + filters.value = overrideFiltersValues() | |
| }, { immediate: true, deep: true }) | |
| -// This can be removed right? | |
| -// We are not using the props anymore | |
| -// here are the removed props from MenuContext below | |
| -// :items="props.items" | |
| -// :icon-mapping="iconMapping" | |
| -//const iconMapping = { | |
| -// "program": IconProgram, | |
| -// "user": IconNewUser, | |
| -// "change": IconChange, | |
| -// "clock": IconClock | |
| -//}; | |
| - | |
| // Update field function to pass to the form | |
| +// if we are defining and passin we dont need it there | |
| +// but in my mind it should be inside searchfilter | |
| +// i said smth related to this | |
| +// inside responsivefiltermenu comments | |
| const updateField = (fieldName, value) => { | |
| console.log(`Updating ${fieldName}:`, value) | |
| filters.value[fieldName] = value | |
| - localFilters.value[fieldName] = value | |
| } | |
| // Called when user clears search bar | |
| -// const handleClear = () => { | |
| -// filters.value.query = null; | |
| -// submit(props.tabBaseUrl); | |
| -// }; | |
| +const handleClear = () => { | |
| + debugger | |
| + filters.value.query = null; | |
| + submit(props.tabBaseUrl); | |
| +}; | |
| -// Called when filters applied in MobileFilterMenu | |
| +// Called when filters applied through button inside filter | |
| const filterSearch = (filtersFromChild) => { | |
| + // this function extract actually | |
| + // build the query params ignoring null filters | |
| const cleanedFilters = extractFilterValues(filtersFromChild || filters.value) | |
| router.get(props.tabBaseUrl, cleanedFilters, { | |
| preserveScroll: true, | |
| @@ -109,44 +120,39 @@ const removeQuery = (what) => { | |
| const newParams = new URLSearchParams() | |
| // Clear the specific filter | |
| + // TODO: REFAC TIP ADD FILTER_KEY FROM CONTROLLER | |
| + // IT SHOULD MAKE AGNOSTIC | |
| if (["Time", "Sessão"].includes(what.filter_label)) { | |
| - localFilters.value['sessao'] = null | |
| filters.value['sessao'] = null | |
| } | |
| if (["Showcase", "Mostra"].includes(what.filter_label)) { | |
| - localFilters.value['mostra'] = null | |
| filters.value['mostra'] = null | |
| } | |
| // Add other filter types as needed | |
| if (["Cinema"].includes(what.filter_label)) { | |
| - localFilters.value['cinema'] = null | |
| filters.value['cinema'] = null | |
| } | |
| if (["Genre", "Genero"].includes(what.filter_label)) { | |
| - localFilters.value['genero'] = null | |
| filters.value['genero'] = null | |
| } | |
| if (["Country", "Pais"].includes(what.filter_label)) { | |
| - localFilters.value['pais'] = null | |
| filters.value['pais'] = null | |
| } | |
| if (["Director", "Direção"].includes(what.filter_label)) { | |
| - localFilters.value['direcao'] = null | |
| filters.value['direcao'] = null | |
| } | |
| if (["Cast", "Elenco"].includes(what.filter_label)) { | |
| - localFilters.value['elenco'] = null | |
| filters.value['elenco'] = null | |
| } | |
| - Object.entries(localFilters.value).forEach(([key, value]) => { | |
| + Object.entries(filters.value).forEach(([key, value]) => { | |
| if (value !== null && value !== undefined && value !== "" && value?.filter_value) { | |
| newParams.set(key, value.filter_value); | |
| } | |
| @@ -158,11 +164,12 @@ const removeQuery = (what) => { | |
| }) | |
| } | |
| -// Called when filters cleared from MobileFilterMenu | |
| +// Called when filters cleared from Limpar Todos btn | |
| const clearSearchQuery = () => { | |
| const clearedFilters = initializeFilters() | |
| - localFilters.value = clearedFilters | |
| filters.value = clearedFilters | |
| + debugger | |
| + // MUST ACTIVATE IT | |
| // router.get(props.tabBaseUrl, {}, { | |
| // preserveScroll: true, | |
| // only: ['elements', 'pagy', 'current_filters', 'has_active_filters', 'menuTabs'] | |
| @@ -174,9 +181,9 @@ const { sentinel, isSticky } = useStickyMenuTabs() | |
| </script> | |
| <template> | |
| - <!-- <p>current_filters: {{ props.current_filters }}</p> | |
| - <p>localFilters: {{ localFilters }}</p> | |
| - <p>filters: {{ filters }}</p> --> | |
| + <p class="text-neutrals-900">current_filters: {{ props.current_filters }}</p> | |
| + <!-- <p class="text-neutrals-700">localFilters: {{ localFilters }}</p> --> | |
| + <p class="text-neutrals-500">filters: {{ filters }}</p> | |
| <TwContainer> | |
| <Breadcrumb :crumbs="props.crumbs" /> | |
| @@ -226,7 +233,8 @@ const { sentinel, isSticky } = useStickyMenuTabs() | |
| :text="value.filter_display" | |
| @remove-filter="removeQuery" | |
| /> | |
| - </div> <!-- CONTENT --> | |
| + </div> | |
| + <!-- CONTENT --> | |
| <InfiniteScrollLayout #content="{ allElements }" | |
| :elements="props.elements" | |
| :pagy="props.pagy" | |
| @@ -242,7 +250,6 @@ const { sentinel, isSticky } = useStickyMenuTabs() | |
| <ResponsiveFilterMenu | |
| v-model="filters" | |
| :is-open="isFilterMenuOpen" | |
| - :initialFilters="localFilters" | |
| @filtersApplied="filterSearch" | |
| @filtersCleared="clearSearchQuery" | |
| @close-filter-menu="closeMenu" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment