feat(listing): update sorting and filtering

This commit is contained in:
Daniel Klein 2024-09-17 08:12:43 +02:00
parent 161ba35ff6
commit 14291d18e6
4 changed files with 76 additions and 91 deletions

View file

@ -4,22 +4,32 @@ import type { ProjectShallow } from '~/types'
const props = defineProps<{ const props = defineProps<{
projects: { title: string, projects: ProjectShallow[] }[] projects: { title: string, projects: ProjectShallow[] }[]
}>() }>()
const { switcher } = storeToRefs(useData()) const { switcher, filter } = storeToRefs(useData())
const displayCount = ref(100) const totalProjectsCount = props.projects.map(g => g.projects.length).reduce((a, b) => a + b, 0)
const displayedProjects = computed(() => props.projects.slice(0, displayCount.value))
function showMoreProjects() { function onChangeSort(sortKey: string) {
displayCount.value += 50 if (filter.value.sortby === sortKey) {
if (filter.value.sortDirection === 'desc' && filter.value.sortby !== 'score') {
filter.value.sortby = 'score'
filter.value.sortDirection = 'desc'
return
}
filter.value.sortDirection = filter.value.sortDirection === 'asc' ? 'desc' : 'asc'
return
}
filter.value.sortby = sortKey
filter.value.sortDirection = sortKey === 'score' ? 'desc' : 'asc'
} }
const cardTitles = ref< { label: string, togglable?: boolean, toggled?: boolean }[]>([ const cardTitles = ref< { label: string, sortKey: string, togglable?: boolean }[]>([
{ label: 'Usecase' }, { label: 'Usecase', sortKey: 'usecase' },
{ label: 'Openess', togglable: true }, { label: 'Openess', sortKey: 'openess', togglable: true },
{ label: 'Technology', togglable: true }, { label: 'Technology', sortKey: 'technology', togglable: true },
{ label: 'Privacy', togglable: true }, { label: 'Privacy', sortKey: 'privacy', togglable: true },
{ label: 'Ecosystem' }, { label: 'Ecosystem', sortKey: 'ecosystem' },
{ label: 'Links' }, { label: 'Links', sortKey: 'links' },
{ label: 'W3PN Score', togglable: true }, { label: 'W3PN Score', sortKey: 'score', togglable: true },
]) ])
</script> </script>
@ -71,10 +81,12 @@ const cardTitles = ref< { label: string, togglable?: boolean, toggled?: boolean
flex flex
items-center items-center
gap-4px gap-4px
col-span="1 lg:3" col-span="1 lg:2"
:class="['title' === filter.sortby ? 'text-app-white' : 'text-app-text-grey', 'cursor-pointer']"
@click="onChangeSort('title')"
> >
<p <p
text="12px lg:14px app-text-grey" text="12px lg:14px"
leading="16px lg:24px" leading="16px lg:24px"
whitespace-nowrap whitespace-nowrap
> >
@ -82,8 +94,10 @@ const cardTitles = ref< { label: string, togglable?: boolean, toggled?: boolean
</p> </p>
<button <button
type="button" type="button"
i-ic-baseline-arrow-drop-down :class="['title' === filter.sortby ? filter.sortDirection === 'desc' ? 'i-ic-baseline-arrow-drop-up'
text="app-text-grey 20px" : 'i-ic-baseline-arrow-drop-down'
: 'i-ic-baseline-arrow-drop-down']"
text="20px"
/> />
</div> </div>
<div <div
@ -113,7 +127,7 @@ const cardTitles = ref< { label: string, togglable?: boolean, toggled?: boolean
/> />
</div> </div>
<div <div
v-for="title in cardTitles" v-for="(title, index) in cardTitles"
:key="title.label" :key="title.label"
lg:flex lg:flex
items-center items-center
@ -121,9 +135,11 @@ const cardTitles = ref< { label: string, togglable?: boolean, toggled?: boolean
last:justify-end last:justify-end
gap-4px gap-4px
hidden hidden
:class="[title.sortKey === filter.sortby ? 'text-app-white' : 'text-app-text-grey', { 'cursor-pointer': title.togglable, 'col-span-1 lg:col-span-2': index === 0 }]"
@click="onChangeSort(title.sortKey)"
> >
<p <p
text="12px lg:14px app-text-grey" text="12px lg:14px "
leading="16px lg:24px" leading="16px lg:24px"
whitespace-nowrap whitespace-nowrap
> >
@ -132,16 +148,14 @@ const cardTitles = ref< { label: string, togglable?: boolean, toggled?: boolean
<button <button
v-if="title.togglable" v-if="title.togglable"
type="button" type="button"
:class="[title.toggled :class="[title.sortKey === filter.sortby ? filter.sortDirection === 'desc' ? 'i-ic-baseline-arrow-drop-up'
? 'i-ic-baseline-arrow-drop-up' : 'i-ic-baseline-arrow-drop-down'
: 'i-ic-baseline-arrow-drop-down']" : 'i-ic-baseline-arrow-drop-down']"
text="app-text-grey 20px" text=" 20px"
@click="title.toggled = !title.toggled"
/> />
</div> </div>
</div> </div>
<div <div
v-if="displayedProjects.length"
grid grid
:class="switcher ? 'grid-cols-1 lg:grid-cols-1' : 'xl:grid-cols-3 lg:grid-cols-3 sm:grid-cols-2 grid-cols-1'" :class="switcher ? 'grid-cols-1 lg:grid-cols-1' : 'xl:grid-cols-3 lg:grid-cols-3 sm:grid-cols-2 grid-cols-1'"
gap-16px gap-16px
@ -154,23 +168,9 @@ const cardTitles = ref< { label: string, togglable?: boolean, toggled?: boolean
:project="project" :project="project"
/> />
</div> </div>
<div v-else> </template>
<div v-if="totalProjectsCount === 0">
<h3>No Projects found...</h3> <h3>No Projects found...</h3>
</div> </div>
<button
v-if="displayedProjects.length < projects.length"
mt-29px
text="14px"
leading-24px
font-700
px-12px
py-4px
border-2px
border-app-white
@click="showMoreProjects"
>
Load more projects
</button>
</template>
</div> </div>
</template> </template>

View file

@ -1,26 +1,22 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { InputOption } from '~/types' import type { InputOption } from '~/types'
const { categories, usecases, ecosystems, assets, features, filteredProjectsCount, selectedCategoryId } = storeToRefs(useData()) const { categories, usecases, ecosystems, assets, features, filteredProjectsCount, selectedCategoryId, selectedUsecaseId, selectedEcosystemId, selectedAssetsUsedId, selectedFeaturesId } = storeToRefs(useData())
const selectedUsecaseId = ref('all') const selectedCategory = computed(() => {
const selectedEcosystemId = ref('all') return categories.value.find(c => c.id === selectedCategoryId.value)
const selectedAssetsUsedId = ref('all') })
const selectedFeaturesId = ref('all') const availableUsecases = computed(() => {
if (selectedCategoryId.value === 'all')
return usecases.value
return usecases.value.filter(u => selectedCategory.value?.usecases?.includes(u.id))
})
const categoryOptions = ref<InputOption[]>(categories.value ? [{ label: 'Category', value: 'all' }, ...categories.value.map(c => ({ label: c.name, value: c.id, count: c.projectsCount }))] : []) const categoryOptions = ref<InputOption[]>(categories.value ? [{ label: 'Category', value: 'all' }, ...categories.value.map(c => ({ label: c.name, value: c.id, count: c.projectsCount }))] : [])
const usecaseOptions = ref<InputOption[]>(usecases.value ? [{ label: 'Usecase', value: 'all' }, ...usecases.value.map(u => ({ label: u.name, value: u.id }))] : []) const usecaseOptions = computed<InputOption[]>(() => availableUsecases.value.length ? [{ label: 'Usecase', value: 'all' }, ...availableUsecases.value.map(u => ({ label: u.name, value: u.id }))] : [])
const ecosystemOptions = ref<InputOption[]>(ecosystems.value ? [{ label: 'Ecosystem', value: 'all' }, ...ecosystems.value.map(e => ({ label: e.name, value: e.id }))] : []) const ecosystemOptions = ref<InputOption[]>(ecosystems.value ? [{ label: 'Ecosystem', value: 'all' }, ...ecosystems.value.map(e => ({ label: e.name, value: e.id }))] : [])
const assetOptions = ref<InputOption[]>(assets.value ? [{ label: 'Asset used', value: 'all' }, ...assets.value.map(a => ({ label: a.name, value: a.id }))] : []) const assetOptions = ref<InputOption[]>(assets.value ? [{ label: 'Asset used', value: 'all' }, ...assets.value.map(a => ({ label: `${a.id.toUpperCase()} (${a.name})`, value: a.id }))] : [])
const featureOptions = ref<InputOption[]>(features.value ? [{ label: 'Feature', value: 'all' }, ...features.value.map(f => ({ label: f.name, value: f.id }))] : []) const featureOptions = ref<InputOption[]>(features.value ? [{ label: 'Feature', value: 'all' }, ...features.value.map(f => ({ label: f.name, value: f.id }))] : [])
// const selectedCategory = computed(() => {
// return categories.value.find(c => c.id === selectedCategoryId.value)
// })
// const sortedFilteredCategories = computed(() => ([
// categories.value.find(c => c.id === 'defi')!,
// ...[...categories.value].sort((a, b) => a.name.localeCompare(b.name)).filter(c => c.id !== 'defi'),
// ]))
const { showBar } = storeToRefs(useNavigaiton()) const { showBar } = storeToRefs(useNavigaiton())
const swipeEl = ref() const swipeEl = ref()
@ -94,28 +90,28 @@ watch([scrollY, top, y], (newValues, oldValues) => {
@selected="selectedCategoryId === 'all' ? navigateTo(`/`) : navigateTo(`/category/${selectedCategoryId}`)" @selected="selectedCategoryId === 'all' ? navigateTo(`/`) : navigateTo(`/category/${selectedCategoryId}`)"
/> />
<CategorySelectBox <CategorySelectBox
v-if="usecases.length" v-if="usecases?.length"
v-model="selectedUsecaseId" v-model="selectedUsecaseId"
name="usecaseSelect" name="usecaseSelect"
:options="usecaseOptions" :options="usecaseOptions"
w-full w-full
/> />
<CategorySelectBox <CategorySelectBox
v-if="ecosystems.length" v-if="ecosystems?.length"
v-model="selectedEcosystemId" v-model="selectedEcosystemId"
name="ecosystemSelect" name="ecosystemSelect"
:options="ecosystemOptions" :options="ecosystemOptions"
w-full w-full
/> />
<CategorySelectBox <CategorySelectBox
v-if="assets.length" v-if="assets?.length"
v-model="selectedAssetsUsedId" v-model="selectedAssetsUsedId"
name="assetsUsedSelect" name="assetsUsedSelect"
:options="assetOptions" :options="assetOptions"
w-full w-full
/> />
<CategorySelectBox <CategorySelectBox
v-if="features.length" v-if="features?.length"
v-model="selectedFeaturesId" v-model="selectedFeaturesId"
name="featuresSelect" name="featuresSelect"
:options="featureOptions" :options="featureOptions"

View file

@ -1,5 +1,5 @@
export interface Ecosystem { export interface Ecosystem {
id: string id: string
name: string name: string
icon: string icon?: string
} }

View file

@ -7,11 +7,13 @@ export interface Project {
id: string id: string
name: string name: string
categories: string[] categories: string[]
ecosystem?: string usecases?: string[]
ecosystem?: string[]
product_readiness?: string product_readiness?: string
security?: string security?: string
have_token?: boolean have_token?: boolean
token_link?: string token_link?: string
assets_used?: string[]
tokens?: { tokens?: {
name?: string name?: string
symbol: string symbol: string
@ -122,11 +124,7 @@ export interface Project {
url?: string url?: string
[k: string]: unknown [k: string]: unknown
}[] }[]
ratings: { ratings?: ProjectRating[]
openess: OpenessRating
technology: TechnologyRating
privacy: PrivacyRating
}
} }
export interface ProjectShallow { export interface ProjectShallow {
@ -136,6 +134,9 @@ export interface ProjectShallow {
description: string description: string
percentage: number percentage: number
categories: string[] categories: string[]
usecases?: string[]
ecosystem?: string[]
assets_used?: string []
forum?: string | undefined forum?: string | undefined
github?: string | undefined github?: string | undefined
website?: string | undefined website?: string | undefined
@ -149,36 +150,24 @@ export interface ProjectShallow {
audits?: Audit[] | undefined audits?: Audit[] | undefined
support?: number | undefined support?: number | undefined
anonymity?: boolean | undefined anonymity?: boolean | undefined
ratings: { ratings?: ProjectRating[]
openess: OpenessRating
technology: TechnologyRating
privacy: PrivacyRating
}
} }
export interface ProjectIndexable extends Project { export interface ProjectIndexable extends Project {
[key: string]: unknown [key: string]: unknown
} }
export interface OpenessRating { export interface ProjectRating {
documentation: string type: string
github: string name: string
socials: string items: ProjectRatingItem[]
whitepaper: string points: number
team: number
funding: number
}
export interface TechnologyRating {
mainnet: boolean
opensource: boolean
assets: boolean
no_pgradability: boolean
audits: number
} }
export interface PrivacyRating { export interface ProjectRatingItem {
policy: string isValid: boolean
no_kyc: boolean label: string
no_compliance: boolean positive: string
default_privacy: boolean negative: string
value: any
} }