Refactor Packages component for better readability and maintainability

Simplified logic by extracting helper functions and constants, and improved state management with more descriptive variable names. Enhanced URL parameter handling and adjusted watchers to dynamically update based on changes, ensuring cleaner and more modular code.
This commit is contained in:
2025-04-07 20:51:32 +02:00
parent d8f52cea61
commit db9c7ee523

View File

@@ -1,8 +1,7 @@
<template>
<v-sheet class="mt-6" color="transparent" width="100%">
<v-sheet :color="TRANSPARENT_COLOR" class="mt-6" width="100%">
<h5 class="text-h5 mb-4">Packages</h5>
<v-row :style="mobile ? '' : 'height: 60px'" width="100%">
<v-row :style="mobile ? '' : `height: ${ROW_HEIGHT}px`" width="100%">
<v-col class="v-col-12 v-col-sm-2 v-col-lg-2">
<v-text-field
v-model="inputPkgBase"
@@ -11,10 +10,9 @@
placeholder="Search Pkgbase"
variant="outlined"></v-text-field>
</v-col>
<v-col class="v-col-12 v-col-sm-2 v-col-lg-2 mt-n6 mt-sm-0">
<v-select
v-model="selectRepo"
v-model="selectedRepo"
:items="selectRepoItems"
clearable
color="primary"
@@ -25,10 +23,9 @@
single-line
variant="outlined"></v-select>
</v-col>
<v-col class="v-col-12 v-col-sm-2 v-col-lg-3 mt-n6 mt-sm-0">
<v-select
v-model="selectStatus"
v-model="selectedStatuses"
:items="selectStatusItems"
chips
closable-chips
@@ -41,11 +38,9 @@
return-object
variant="outlined"></v-select>
</v-col>
<v-col class="v-col-12 v-col-sm-2 v-col-lg-2 mt-n6 mt-sm-0">
<v-switch v-model="enableExact" color="primary" label="Exact search"></v-switch>
</v-col>
<v-col :class="mobile ? 'mt-n6' : ''" :cols="mobile ? 12 : 'auto'" class="ms-auto">
<v-pagination
v-model="page"
@@ -57,7 +52,6 @@
variant="text"></v-pagination>
</v-col>
</v-row>
<v-table class="mt-2 mt-sm-6" style="width: 100%; background: transparent; font-size: 1rem">
<thead>
<tr>
@@ -84,7 +78,6 @@
<th class="text-end" scope="col" style="width: 30px">Info</th>
</tr>
</thead>
<tbody id="main-tbody">
<tr v-if="noPackagesFound">
No Packages found
@@ -93,16 +86,16 @@
<tr
v-for="(pkg, index) in packages"
:key="index"
:style="`background-color: ${getStatus(pkg.status)};`">
:style="`background-color: ${getStatusColor(pkg.status)};`">
<td class="font-weight-bold text-no-wrap">
{{ pkg.repo.split('-')[0] }}
{{ repoName(pkg.repo) }}
<v-chip
:color="getVersionColor(pkg.repo.split('-')[pkg.repo.split('-').length - 1])"
:color="getVersionColor(repoVersion(pkg.repo))"
class="ms-2"
density="comfortable"
label
variant="flat">
{{ pkg.repo.split('-')[pkg.repo.split('-').length - 1] }}
{{ repoVersion(pkg.repo) }}
</v-chip>
</td>
<td class="text-no-wrap">{{ pkg.pkgbase }}</td>
@@ -165,23 +158,31 @@
</template>
<script lang="ts" setup>
import { onMounted, ref, UnwrapRef, watch } from 'vue'
import { onMounted, ref, watch } from 'vue'
import { type Package } from '@/types/Package'
import { type Packages } from '@/types/Packages'
import { useDisplay } from 'vuetify'
// Constants
const OFFSET = 50
const ROW_HEIGHT = 60
const TRANSPARENT_COLOR = 'transparent'
// Refs
const page = ref(0)
const pagesMax = ref(1)
const packages = ref<Array<Package>>([])
const noPackagesFound = ref(false)
const init = ref(false)
const inputPkgBase = ref('')
const selectedRepo = ref<{ title: string; value: string } | undefined>(undefined)
const selectedStatuses = ref<Array<{ title: string; value: string }>>([])
const enableExact = ref(false)
// Display
const { mobile } = useDisplay()
const inputPkgBase = ref('')
const selectRepo = ref()
// Select Items
const selectRepoItems = [
{ title: 'core-x86-64-v2', value: 'core-x86-64-v2' },
{ title: 'extra-x86-64-v2', value: 'extra-x86-64-v2' },
@@ -193,7 +194,7 @@ const selectRepoItems = [
{ title: 'extra-x86-64-v4', value: 'extra-x86-64-v4' },
{ title: 'multilib-x86-64-v4', value: 'multilib-x86-64-v4' }
]
const selectStatus = ref<Array<{ title: string; value: string }>>([])
const selectStatusItems = [
{ title: 'Latest', value: 'latest' },
{ title: 'Built', value: 'built' },
@@ -205,63 +206,142 @@ const selectStatusItems = [
{ title: 'Signing', value: 'signing' },
{ title: 'Unknown', value: 'unknown' }
]
const enableExact = ref(false)
const searchPackages = () => {
const offset = OFFSET * (page.value > 0 ? page.value - 1 : page.value)
noPackagesFound.value = false
// Computed properties
const repoName = (repo: string) => repo.split('-')[0]
const repoVersion = (repo: string) => repo.split('-')[repo.split('-').length - 1]
// init params
// Helper function for getting the color associated with a version
const getVersionColor = (version: string) => {
switch (version) {
case 'v2':
return '#3498db'
case 'v3':
return '#f39c12'
case 'v4':
return '#2ecc71'
default:
return 'grey'
}
}
// Helper function for getting the status color
const getStatusColor = (status: Package['status']) => {
switch (status) {
case 'skipped':
return '#373737'
case 'queued':
return '#5d2f03'
case 'latest':
return ''
case 'failed':
return '#4f140f'
case 'signing':
return '#093372'
case 'building':
return '#084f46'
case 'unknown':
return '#191919'
default:
return ''
}
}
const getLto = (lto: Package['lto']) => {
switch (lto) {
case 'enabled':
return {
title: 'built with LTO',
class: 'fa fa-check fa-lg text-success'
}
case 'unknown':
return {
title: 'not built with LTO yet',
class: 'fa fa-hourglass-o fa-lg text-grey'
}
case 'disabled':
return {
title: 'LTO explicitly disabled',
class: 'fa fa-times fa-lg text-red'
}
case 'auto_disabled':
return {
title: 'LTO automatically disabled',
class: 'fa fa-times-circle-o fa-lg text-amber'
}
default:
return { title: '', class: '' }
}
}
const getDs = (ds: Package['debug_symbols']) => {
switch (ds) {
case 'available':
return {
title: 'Debug symbols available',
class: 'fa fa-check fa-lg text-success'
}
case 'unknown':
return {
title: 'Not built yet',
class: 'fa fa-hourglass-o fa-lg text-grey'
}
case 'not_available':
return {
title: 'Not built with debug symbols',
class: 'fa fa-times fa-lg text-red'
}
default:
return { title: '', class: '' }
}
}
// Helper function for updating URL params dynamically
const updateUrlParams = () => {
const params = new URLSearchParams()
// set params
if (offset > 0) {
url.searchParams.set('offset', offset.toString())
} else {
url.searchParams.delete('offset')
if (page.value > 0) {
params.set('offset', (page.value * OFFSET).toString())
}
if (inputPkgBase.value) {
params.set('pkgbase', inputPkgBase.value.toLowerCase())
url.searchParams.set('pkgbase', inputPkgBase.value.toLowerCase())
} else {
params.delete('pkgbase')
url.searchParams.delete('pkgbase')
}
if (selectRepo.value) {
params.set('repo', selectRepo.value.value)
url.searchParams.set('repo', selectRepo.value.value)
} else {
params.delete('repo')
url.searchParams.delete('repo')
if (selectedRepo.value) {
params.set('repo', selectedRepo.value.value)
}
if (selectStatus.value.length > 0) {
selectStatus.value.forEach((status) => {
if (!params.has('status', status.value)) {
params.append('status', status.value)
}
if (!url.searchParams.has('status', status.value)) {
url.searchParams.append('status', status.value)
}
if (selectedStatuses.value.length > 0) {
selectedStatuses.value.forEach((status) => {
params.append('status', status.value)
})
} else {
params.delete('status')
url.searchParams.delete('status')
}
if (enableExact.value) {
params.set('exact', '')
url.searchParams.set('exact', '')
} else {
params.delete('exact')
url.searchParams.delete('exact')
}
window.history.pushState(null, '', `${window.location.pathname}?${params}`)
}
// push params to url
window.history.pushState(null, '', url.toString())
// add limit to query params
const searchPackages = () => {
const offset = OFFSET * page.value
noPackagesFound.value = false
const params = new URLSearchParams()
if (page.value > 0) {
params.set('offset', offset.toString())
}
if (inputPkgBase.value) {
params.set('pkgbase', inputPkgBase.value.toLowerCase())
}
if (selectedRepo.value) {
params.set('repo', selectedRepo.value.value)
}
if (selectedStatuses.value.length > 0) {
selectedStatuses.value.forEach((status) => {
params.append('status', status.value)
})
}
if (enableExact.value) {
params.set('exact', '')
}
params.set('limit', OFFSET.toString())
params.set('offset', offset.toString())
fetch('https://api.alhp.dev/packages?' + params, {
method: 'GET'
})
@@ -276,174 +356,43 @@ const searchPackages = () => {
})
.then((json: Packages) => {
if (!json) return
if (pagesMax.value !== json.total) {
pagesMax.value = json.total / OFFSET + 1
}
pagesMax.value = json.total / OFFSET + 1
packages.value = json.packages
})
.catch((error) => {
console.error(error)
})
}
const getVersionColor = (version: string) => {
let color = 'grey'
switch (version) {
case 'v2':
color = '#3498db'
break
case 'v3':
color = '#f39c12'
break
case 'v4':
color = '#2ecc71'
break
}
return color
}
const getStatus = (status: UnwrapRef<Package['status']>) => {
let color = ''
switch (status) {
case 'skipped':
color = '#373737'
break
case 'queued':
color = '#5d2f03'
break
case 'latest':
color = ''
break
case 'failed':
color = '#4f140f'
break
case 'signing':
color = '#093372'
break
case 'building':
color = '#084f46'
break
case 'unknown':
color = '#191919'
break
}
return color
}
const getLto = (lto: UnwrapRef<Package['lto']>) => {
let icon = {
title: '',
class: ''
}
switch (lto) {
case 'enabled':
icon = {
title: 'built with LTO',
class: 'fa fa-check fa-lg text-success'
}
break
case 'unknown':
icon = {
title: 'not built with LTO yet',
class: 'fa fa-hourglass-o fa-lg text-grey'
}
break
case 'disabled':
icon = {
title: 'LTO explicitly disabled',
class: 'fa fa-times fa-lg text-red'
}
break
case 'auto_disabled':
icon = {
title: 'LTO automatically disabled',
class: 'fa fa-times-circle-o fa-lg text-amber'
}
break
}
return icon
}
const getDs = (ds: UnwrapRef<Package['debug_symbols']>) => {
let icon = {
title: '',
class: ''
}
switch (ds) {
case 'available':
icon = {
title: 'Debug symbols available',
class: 'fa fa-check fa-lg text-success'
}
break
case 'unknown':
icon = {
title: 'Not built yet',
class: 'fa fa-hourglass-o fa-lg text-grey'
}
break
case 'not_available':
icon = {
title: 'Not built with debug symbols',
class: 'fa fa-times fa-lg text-red'
}
break
}
return icon
}
const url = new URL(window.location.href)
window.addEventListener('load', () => {
init.value = true
url.searchParams.has('offset')
? (page.value = parseInt(url.searchParams.get('offset') || '0'))
: (page.value = 0)
inputPkgBase.value = url.searchParams.has('pkgbase')
? (url.searchParams.get('pkgbase') || '').toLowerCase()
: ''
url.searchParams.has('repo')
? (selectRepo.value = {
value: url.searchParams.get('repo'),
title: url.searchParams.get('repo')
})
: undefined
url.searchParams.has('status')
? url.searchParams.getAll('status').forEach((status) => {
selectStatus.value.push({ value: status, title: status.toLocaleUpperCase() })
})
: undefined
enableExact.value = url.searchParams.has('exact')
searchPackages()
init.value = false
})
watch(
[
() => enableExact.value,
() => inputPkgBase.value,
() => selectRepo.value,
() => selectStatus.value
],
() => {
page.value = 0
searchPackages()
}
)
watch(
() => page.value,
() => {
searchPackages()
}
)
// Properly set the initial values from URL params
onMounted(() => {
const urlParams = new URLSearchParams(window.location.search)
page.value = urlParams.has('offset')
? Math.floor(parseInt(urlParams.get('offset') || '0') / OFFSET)
: 0
inputPkgBase.value = urlParams.get('pkgbase') || ''
const repoValue = urlParams.get('repo')
if (repoValue) {
selectedRepo.value = { title: repoValue, value: repoValue }
} else {
selectedRepo.value = undefined
}
const statuses = urlParams.getAll('status')
selectedStatuses.value = statuses.map((status) => ({
title: status.toUpperCase(),
value: status
}))
enableExact.value = urlParams.has('exact')
searchPackages()
})
// Watchers
watch(
[page, inputPkgBase, selectedRepo, selectedStatuses, enableExact],
() => {
if (init.value) return
updateUrlParams()
searchPackages()
},
{ deep: true }
)
</script>