refactor(links): extract sort menu and fix sort logic

- Extracts dropdown sort menu into separate DashboardLinksSort component
- Fixes incorrect sort order for newest/oldest links
- Adds internationalization support for sort options
- Updates QR code download button size for consistency
- Adds helpful tooltip about sort limitation for loaded links

The sort logic was reversed where newest/oldest were concerned, this fixes
that while making the sorting feature more maintainable through component
extraction.
This commit is contained in:
ccbikai 2025-03-16 11:52:29 +08:00
parent 71c726ab9b
commit 5118201911
5 changed files with 75 additions and 40 deletions

View file

@ -1,27 +1,22 @@
<script setup> <script setup>
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '#components'
import { useInfiniteScroll } from '@vueuse/core' import { useInfiniteScroll } from '@vueuse/core'
import { ArrowUpDown, Loader } from 'lucide-vue-next' import { Loader } from 'lucide-vue-next'
const links = ref([]) const links = ref([])
const limit = 24 const limit = 24
let cursor = '' let cursor = ''
let listComplete = false let listComplete = false
let listError = false let listError = false
const sortBy = ref('newest')
const sortBy = ref('az')
const displayedLinks = computed(() => { const displayedLinks = computed(() => {
const sorted = [...links.value] const sorted = [...links.value]
switch (sortBy.value) { switch (sortBy.value) {
case 'newest': case 'newest':
return sorted.sort((a, b) => b.createdAt - a.createdAt)
case 'oldest':
return sorted.sort((a, b) => a.createdAt - b.createdAt) return sorted.sort((a, b) => a.createdAt - b.createdAt)
case 'oldest':
return sorted.sort((a, b) => b.createdAt - a.createdAt)
case 'az': case 'az':
return sorted.sort((a, b) => a.slug.localeCompare(b.slug)) return sorted.sort((a, b) => a.slug.localeCompare(b.slug))
case 'za': case 'za':
@ -83,33 +78,7 @@ function updateLinkList(link, type) {
<DashboardNav class="flex-1"> <DashboardNav class="flex-1">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<DashboardLinksEditor @update:link="updateLinkList" /> <DashboardLinksEditor @update:link="updateLinkList" />
<DropdownMenu> <DashboardLinksSort v-model:sort-by="sortBy" />
<DropdownMenuTrigger as-child>
<Button variant="outline" size="sm">
<ArrowUpDown class="mr-2 h-4 w-4" />
Sort by: {{
sortBy === 'newest' ? 'Newest First'
: sortBy === 'oldest' ? 'Oldest First'
: sortBy === 'az' ? 'Slug A-Z'
: 'Slug Z-A'
}}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem @click="sortBy = 'newest'">
Newest First
</DropdownMenuItem>
<DropdownMenuItem @click="sortBy = 'oldest'">
Oldest First
</DropdownMenuItem>
<DropdownMenuItem @click="sortBy = 'az'">
A to Z
</DropdownMenuItem>
<DropdownMenuItem @click="sortBy = 'za'">
Z to A
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div> </div>
</DashboardNav> </DashboardNav>
<LazyDashboardLinksSearch /> <LazyDashboardLinksSearch />

View file

@ -118,7 +118,11 @@ onMounted(() => {
> >
</div> </div>
</div> </div>
<Button variant="outline" @click="downloadQRCode"> <Button
variant="outline"
size="sm"
@click="downloadQRCode"
>
<Download class="w-4 h-4 mr-2" /> <Download class="w-4 h-4 mr-2" />
{{ $t('links.download_qr_code') }} {{ $t('links.download_qr_code') }}
</Button> </Button>

View file

@ -0,0 +1,48 @@
<script setup>
import { ArrowUpDown } from 'lucide-vue-next'
defineProps({
sortBy: {
type: String,
default: 'az',
},
})
const emit = defineEmits(['update:sortBy'])
</script>
<template>
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button variant="outline">
<TooltipProvider>
<Tooltip>
<TooltipTrigger class="flex items-center">
<ArrowUpDown class="h-4 w-4 sm:mr-2" />
<span class="hidden sm:inline">
{{ $t('links.sort.newest') }}
</span>
</TooltipTrigger>
<TooltipContent>
<p>{{ $t('links.sort.tip') }}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem @click="emit('update:sortBy', 'newest')">
{{ $t('links.sort.newest') }}
</DropdownMenuItem>
<DropdownMenuItem @click="emit('update:sortBy', 'oldest')">
{{ $t('links.sort.oldest') }}
</DropdownMenuItem>
<DropdownMenuItem @click="emit('update:sortBy', 'az')">
{{ $t('links.sort.az') }}
</DropdownMenuItem>
<DropdownMenuItem @click="emit('update:sortBy', 'za')">
{{ $t('links.sort.za') }}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</template>

View file

@ -157,6 +157,13 @@
"expires_at": "Expires At", "expires_at": "Expires At",
"no_more": "No more links", "no_more": "No more links",
"load_failed": "Loading links failed,", "load_failed": "Loading links failed,",
"download_qr_code": "Download QR Code" "download_qr_code": "Download QR Code",
"sort": {
"newest": "Newest First",
"oldest": "Oldest First",
"az": "Slug A-Z",
"za": "Slug Z-A",
"tip": "Experience: Only for loaded links"
}
} }
} }

View file

@ -157,6 +157,13 @@
"expires_at": "过期时间", "expires_at": "过期时间",
"no_more": "没有更多链接", "no_more": "没有更多链接",
"load_failed": "加载链接失败,", "load_failed": "加载链接失败,",
"download_qr_code": "下载二维码" "download_qr_code": "下载二维码",
"sort": {
"newest": "最新",
"oldest": "最旧",
"az": "字母(A-Z)",
"za": "字母(Z-A)",
"tip": "体验功能: 仅对已经加载的链接有效"
}
} }
} }