From 8c81b6988849e170472b64f859d5751ae641c218 Mon Sep 17 00:00:00 2001 From: Esteban Date: Mon, 20 Jul 2026 18:48:17 -0500 Subject: [PATCH] add bible study filter system with responsive FiltersContainer and global useFilters composable --- .../searchPanel/FiltersContainer.vue | 106 +++++++ app/components/searchPanel/SearchPanel.vue | 197 ++++++------ app/composables/useFilters.ts | 73 +++++ app/utils/changelog.ts | 280 +++++++++--------- lang/en.json | 1 + lang/es.json | 1 + lang/fr.json | 1 + lang/pt.json | 1 + 8 files changed, 434 insertions(+), 226 deletions(-) create mode 100644 app/components/searchPanel/FiltersContainer.vue create mode 100644 app/composables/useFilters.ts diff --git a/app/components/searchPanel/FiltersContainer.vue b/app/components/searchPanel/FiltersContainer.vue new file mode 100644 index 0000000..8b68d01 --- /dev/null +++ b/app/components/searchPanel/FiltersContainer.vue @@ -0,0 +1,106 @@ + + + diff --git a/app/components/searchPanel/SearchPanel.vue b/app/components/searchPanel/SearchPanel.vue index 32a6b7c..0317795 100644 --- a/app/components/searchPanel/SearchPanel.vue +++ b/app/components/searchPanel/SearchPanel.vue @@ -2,6 +2,7 @@ import { computed, ref, watch, onMounted, onBeforeUnmount } from 'vue' import { breakpointsTailwind, useDebounce } from '@vueuse/core' import PublicationDetail from '~/components/PublicationDetail.vue' +import FiltersContainer from '~/components/searchPanel/FiltersContainer.vue' import { useSettingsStore } from '~/stores/settings' interface Props { @@ -156,64 +157,15 @@ const sortMode = ref<'relevance' | 'date'>('relevance') // ---- Filtro bible_study multi-chip (solo para Estudios) -------------------- -interface BibleStudyChip { - id: number - title: string -} - -const bibleStudyInput = ref(null) -const activeBibleStudies = ref([]) -const isValidating = ref(false) - -async function applyBibleStudyFilter() { - const val = bibleStudyInput.value - if (val === null || val <= 0) return - if (activeBibleStudies.value.some(bs => bs.id === val)) { - toast.add({ title: 'Estudio ya agregado', description: `El estudio #${val} ya está en el filtro`, color: 'info' }) - bibleStudyInput.value = null - return - } - - isValidating.value = true - try { - const res = await documentsApi.multiSearch({ - multiSearchParameters: {}, - multiSearchSearchesParameter: { - searches: [{ - collection: props.mainCollection, - q: '*', - queryBy: 'title', - filterBy: `bible_study:=${val}`, - perPage: 1, - includeFields: 'bible_study,title' - }] - } - }) - - const hit = (res?.results?.[0] as { hits?: Array<{ document: { title?: string } }> })?.hits?.[0] - if (hit) { - activeBibleStudies.value = [...activeBibleStudies.value, { id: val, title: hit.document.title || '' }] - bibleStudyInput.value = null - refetchResults() - } else { - toast.add({ title: 'Estudio no encontrado', description: `No existe el estudio #${val}`, color: 'warning' }) - } - } catch (err) { - console.error('Error validando bible_study', err) - } finally { - isValidating.value = false - } -} - -function removeBibleStudyFilter(id: number) { - activeBibleStudies.value = activeBibleStudies.value.filter(bs => bs.id !== id) - refetchResults() -} - -function clearAllBibleStudyFilters() { - activeBibleStudies.value = [] - refetchResults() -} +const { + activeBibleStudies, + activeCount, + bibleStudyInput, + isValidating, + applyBibleStudyFilter, + removeBibleStudyFilter, + clearAllBibleStudyFilters, +} = useFilters() function refetchResults() { if (!debouncedQuery.value.trim()) { @@ -226,6 +178,10 @@ function refetchResults() { } } +watch(activeBibleStudies, () => { + refetchResults() +}) + // ---------------------------------------------------------------------------- const groupedHits = ref([]) @@ -690,6 +646,7 @@ function metaLocation(meta: DocMeta | undefined): string { {{ author }} +
{{ t('search.phrase') }}
-
- -
-
-
- - - + {{ $t('search.filter') }} - -
-
+ + +
+
+ +
+

Busqueda:

+ +
+ + + + + ('bible-study-chips', () => []) + +export function useFilters() { + const { documentsApi } = useTypesenseApi() + const toast = useToast() + + const bibleStudyInput = ref(null) + const isValidating = ref(false) + + const activeCount = computed(() => activeBibleStudies.value.length) + + async function applyBibleStudyFilter(mainCollection: string) { + const val = bibleStudyInput.value + if (val === null || val <= 0) return + if (activeBibleStudies.value.some(bs => bs.id === val)) { + toast.add({ title: 'Estudio ya agregado', description: `El estudio #${val} ya está en el filtro`, color: 'info' }) + bibleStudyInput.value = null + return + } + + isValidating.value = true + try { + const res = await documentsApi.multiSearch({ + multiSearchParameters: {}, + multiSearchSearchesParameter: { + searches: [{ + collection: mainCollection, + q: '*', + queryBy: 'title', + filterBy: `bible_study:=${val}`, + perPage: 1, + includeFields: 'bible_study,title' + }] + } + }) + + const hit = (res?.results?.[0] as { hits?: Array<{ document: { title?: string } }> })?.hits?.[0] + if (hit) { + activeBibleStudies.value = [...activeBibleStudies.value, { id: val, title: hit.document.title || '' }] + bibleStudyInput.value = null + } else { + toast.add({ title: 'Estudio no encontrado', description: `No existe el estudio #${val}`, color: 'warning' }) + } + } catch (err) { + console.error('Error validando bible_study', err) + } finally { + isValidating.value = false + } + } + + function removeBibleStudyFilter(id: number) { + activeBibleStudies.value = activeBibleStudies.value.filter(bs => bs.id !== id) + } + + function clearAllBibleStudyFilters() { + activeBibleStudies.value = [] + } + + return { + activeBibleStudies, + activeCount, + bibleStudyInput, + isValidating, + applyBibleStudyFilter, + removeBibleStudyFilter, + clearAllBibleStudyFilters + } +} diff --git a/app/utils/changelog.ts b/app/utils/changelog.ts index 45313f8..17e981b 100644 --- a/app/utils/changelog.ts +++ b/app/utils/changelog.ts @@ -6,141 +6,153 @@ export const typeConfig: Record