mirror of
https://github.com/web3privacy/explorer-app.git
synced 2024-10-15 16:46:26 +02:00
feat(listing): update sorting and filtering
This commit is contained in:
parent
161ba35ff6
commit
14291d18e6
4 changed files with 76 additions and 91 deletions
|
@ -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>
|
|
||||||
<h3>No Projects found...</h3>
|
|
||||||
</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>
|
</template>
|
||||||
|
<div v-if="totalProjectsCount === 0">
|
||||||
|
<h3>No Projects found...</h3>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export interface Ecosystem {
|
export interface Ecosystem {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
icon: string
|
icon?: string
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue