Skip to content

Commit

Permalink
feat(saved posts): add search and filtering
Browse files Browse the repository at this point in the history
  • Loading branch information
AlejandroAkbal committed Jul 22, 2024
1 parent a8644b6 commit e2279c3
Show file tree
Hide file tree
Showing 4 changed files with 668 additions and 542 deletions.
124 changes: 43 additions & 81 deletions components/pages/posts/navigation/search/SearchMenu.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
<script lang="ts" setup>
import { CheckIcon, ChevronUpDownIcon, MagnifyingGlassIcon, NoSymbolIcon, PlusIcon } from '@heroicons/vue/20/solid'
import { Bars3BottomRightIcon, EyeIcon, StarIcon, TagIcon } from '@heroicons/vue/24/outline'
import { TagIcon } from '@heroicons/vue/24/outline'
import { watchDebounced } from '@vueuse/core'
import { abbreviateNumber } from 'js-abbreviation-number'
import { cloneDeep, unionWith } from 'lodash-es'
import Tag from '~/assets/js/tag.dto'
import SearchSelect from './SearchSelect.vue'
const props = defineProps<{
initialSelectedTags: Tag[]
initialSelectedFilters: {
sort: string
rating: string
score: string
initialSelectedFilters: Record<string, any>
filterConfig: {
[key: string]: {
type: 'select'
label: string
options?: { label: string; value: any }[]
icon?: any
}
}
tagResults: Tag[]
}>()
Expand All @@ -24,11 +27,7 @@
submit: [
payload: {
tags: Tag[]
filters: {
sort: string
rating: string
score: string
}
filters: Record<string, any>
}
]
}>()
Expand Down Expand Up @@ -135,60 +134,30 @@
function onSubmitted() {
emit('submit', {
tags: selectedTags.value,
filters: {
sort: selectedSortingOption.value.value,
rating: selectedRatingOption.value.value,
score: selectedScoreOption.value.value
}
filters: Object.fromEntries(
//
Object.entries(selectedFilters.value).map(([key, filter]) => [key, filter.value])
)
})
}
// Sorting
const sortingOptions = [
{ title: 'Sort', value: undefined },
{ title: 'Score', value: 'score' },
{ title: 'Created', value: 'id' }
]
const selectedSortingOption = shallowRef(
sortingOptions.find((option) => option.value === props.initialSelectedFilters.sort)
)
// Rating
const ratingOptions = [
{ title: 'Rating', value: undefined },
{ title: 'Safe', value: 'safe' },
{ title: 'General', value: 'general' },
{ title: 'Sensitive', value: 'sensitive' },
{ title: 'Questionable', value: 'questionable' },
{ title: 'Explicit', value: 'explicit' }
]
const selectedRatingOption = shallowRef(
ratingOptions.find((option) => option.value === props.initialSelectedFilters.rating)
const selectedFilters = ref(
Object.fromEntries(
Object.entries(props.initialSelectedFilters).map(([initialFilterKey, initialFilterValue], index) => [
initialFilterKey,
props.filterConfig[initialFilterKey].options?.find((option) => option.value === initialFilterValue)
])
)
)
// Score
const scoreOptions = [
{ title: 'Score', value: undefined },
{ title: '>= 0', value: '>=0' },
{ title: '>= 5', value: '>=5' },
{ title: '>= 10', value: '>=10' },
{ title: '>= 25', value: '>=25' },
{ title: '>= 50', value: '>=50' },
{ title: '>= 100', value: '>=100' },
{ title: '>= 250', value: '>=250' },
{ title: '>= 500', value: '>=500' },
{ title: '>= 750', value: '>=750' },
{ title: '>= 1000', value: '>=1000' }
]
const selectedScoreOption = shallowRef(
scoreOptions.find((option) => option.value === props.initialSelectedFilters.score)
)
function getFilterComponent(type: string) {
switch (type) {
case 'select':
return SearchSelect
default:
throw new Error(`Unknown filter type: ${type}`)
}
}
</script>

<template>
Expand Down Expand Up @@ -316,26 +285,19 @@
</BottomSheetWrapper>
</div>

<!-- Sort Filter -->
<SearchSelect
v-model="selectedSortingOption"
:icon="Bars3BottomRightIcon"
:options="sortingOptions"
/>

<!-- Rating filter -->
<SearchSelect
v-model="selectedRatingOption"
:icon="EyeIcon"
:options="ratingOptions"
/>

<!-- Score filter -->
<SearchSelect
v-model="selectedScoreOption"
:icon="StarIcon"
:options="scoreOptions"
/>
<!-- Dynamic Filters -->
<template
v-for="(filter, key, index) in props.filterConfig"
:key="key"
>
<component
:is="getFilterComponent(filter.type)"
v-model="selectedFilters[key]"
:label="filter.label"
:options="filter.options"
:icon="filter.icon"
/>
</template>
</section>
</section>

Expand Down
13 changes: 9 additions & 4 deletions components/pages/posts/navigation/search/SearchSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
required: true
},
label: {
type: String,
required: true
},
options: {
type: Array,
required: true
Expand Down Expand Up @@ -47,14 +52,14 @@
class="-ml-0.5 h-5 w-5"
/>

<span class="whitespace-nowrap text-sm font-medium">{{ modelValue.title }}</span>
<span class="whitespace-nowrap text-sm font-medium">{{ modelValue.label }}</span>
</div>

<!-- Right side -->
<HeadlessListboxButton
class="focus-visible:focus-outline-util hover:hover-text-util hover:hover-bg-util inline-flex items-center rounded-l-none rounded-r-md px-2 py-1"
>
<span class="sr-only">Change published status</span>
<span class="sr-only">Change {{ label }}</span>

<ChevronDownIcon class="-ml-0.5 h-5 w-5" />
</HeadlessListboxButton>
Expand All @@ -65,7 +70,7 @@
>
<HeadlessListboxOption
v-for="option in options"
:key="option.title"
:key="option.label"
v-slot="{ active, selected }"
:value="option"
>
Expand All @@ -75,7 +80,7 @@
>
<div class="flex flex-col">
<div class="flex justify-between">
<p :class="selected ? 'font-medium' : 'font-normal'">{{ option.title }}</p>
<p :class="selected ? 'font-medium' : 'font-normal'">{{ option.label }}</p>

<!-- Check icon -->
<span
Expand Down
Loading

0 comments on commit e2279c3

Please sign in to comment.