Refactor and modularize queued packages handling
Extract queued packages logic into a dedicated `QueuedPackagesList` component for better code modularity and maintainability. Streamline API fetch functions with reusable `fetchPackages` utility and improve code readability by adopting async/await syntax.
This commit is contained in:
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@@ -13,5 +13,6 @@ declare module 'vue' {
|
||||
CurrentlyBuilding: typeof import('./src/components/CurrentlyBuilding.vue')['default']
|
||||
MainNav: typeof import('./src/components/MainNav.vue')['default']
|
||||
Packages: typeof import('./src/components/Packages.vue')['default']
|
||||
QueuedPackagesList: typeof import('./src/components/QueuedPackagesList.vue')['default']
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@
|
||||
variant="elevated">
|
||||
<v-card-title>
|
||||
<v-row align="center" class="w-100" style="margin-top: 1px">
|
||||
<v-col class="v-col-12 v-col-lg-1">
|
||||
<v-col class="v-col-12 v-col-lg-1 mb-3">
|
||||
<v-row :class="mobile ? 'mt-1' : ''" class="flex-nowrap ms-1 d-flex align-items-center">
|
||||
<div
|
||||
:class="
|
||||
@@ -19,8 +19,7 @@
|
||||
</span>
|
||||
</v-row>
|
||||
</v-col>
|
||||
|
||||
<v-col v-if="packageCount.building > 0" class="v-col-12 v-col-lg-8">
|
||||
<v-col v-if="packageCount.building > 0" class="v-col-12 v-col-lg-8 mb-3">
|
||||
<v-progress-linear
|
||||
:max="packageCount.built + packageCount.building + packageCount.queued"
|
||||
:model-value="packageCount.built"
|
||||
@@ -29,10 +28,9 @@
|
||||
rounded
|
||||
striped></v-progress-linear>
|
||||
</v-col>
|
||||
|
||||
<v-col
|
||||
:class="mobile ? 'mt-n3' : 'text-end ms-auto'"
|
||||
class="text-grey v-col-12 v-col-lg-2"
|
||||
class="text-grey v-col-12 v-col-lg-2 mb-3"
|
||||
cols="auto"
|
||||
style="font-size: 13px">
|
||||
Last updated
|
||||
@@ -44,7 +42,6 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text
|
||||
v-if="packageCount.building > 0 || packageCount.queued > 0"
|
||||
class="d-flex flex-column">
|
||||
@@ -55,88 +52,31 @@
|
||||
<div class="pulsating-circle-amber me-4" />
|
||||
</template>
|
||||
<v-list-item-title>
|
||||
{{ pkg.pkgbase }} <span class="text-grey">({{ pkg.repo }})</span>
|
||||
{{ pkg.pkgbase }}
|
||||
<span class="text-grey">({{ pkg.repo }})</span>
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ pkg.arch_version }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
|
||||
<v-list width="100%">
|
||||
<v-list-subheader>Queued</v-list-subheader>
|
||||
|
||||
<!-- SHOW MESSAGE IF NO QUEUED PACKAGES -->
|
||||
<template v-if="packages.queued.length === 0">
|
||||
<v-list-item>
|
||||
<v-list-item-title class="text-grey"> No packages queued.</v-list-item-title>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
<!-- ELSE SHOW SUMMARY VIEW AND EXPANSION PANEL -->
|
||||
<template v-else>
|
||||
<!-- SUMMARY VIEW: First few queued items -->
|
||||
<v-list-item
|
||||
v-for="(pkg, index) in packages.queued.slice(0, packageCount.building)"
|
||||
:key="index"
|
||||
prepend-icon="mdi-chevron-right">
|
||||
<v-list-item-title>
|
||||
{{ pkg.pkgbase }} <span class="text-grey">({{ pkg.repo }})</span>
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ pkg.arch_version }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
|
||||
<!-- SUMMARY VIEW: Remaining count -->
|
||||
<v-list-item
|
||||
v-if="packageCount.queued > packageCount.building"
|
||||
prepend-icon="mdi-dots-horizontal">
|
||||
<v-list-item-title>
|
||||
<span v-if="packageCount.building > 0">+</span>
|
||||
{{ packageCount.queued - packageCount.building }}
|
||||
<span v-if="packageCount.building > 0">more</span><span v-else>in queue</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<!-- FULL LIST EXPANSION PANEL -->
|
||||
<v-expansion-panels>
|
||||
<v-expansion-panel>
|
||||
<v-expansion-panel-title>
|
||||
Show all queued packages
|
||||
<span class="text-grey">({{ packages.queued.length }})</span>
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-list outlined rounded>
|
||||
<v-list-item
|
||||
v-for="(pkg, index) in packages.queued"
|
||||
:key="index"
|
||||
class="d-flex justify-space-between">
|
||||
<v-list-item-title>
|
||||
{{ pkg.pkgbase }} <span class="text-grey">({{ pkg.repo }})</span>
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{ pkg.arch_version }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-expansion-panel-text>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</template>
|
||||
</v-list>
|
||||
<v-sheet class="ps-4 mt-4" color="transparent" rounded width="100%">
|
||||
<h4 class="mb-2 font-weight-light text-grey">Queued</h4>
|
||||
<QueuedPackagesList :packages="packages.queued" />
|
||||
</v-sheet>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// built + building + queued = total
|
||||
import { onMounted, ref } from 'vue'
|
||||
import type { Packages } from '@/types/Packages'
|
||||
import { Package } from '@/types/Package'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import QueuedPackagesList from './QueuedPackagesList.vue'
|
||||
|
||||
// This variable sets the update-interval
|
||||
const updateIntervalInMinutes = 5
|
||||
const BASE_URL = 'https://api.alhp.dev/packages'
|
||||
const UPDATE_INTERVAL_MINUTES = 5
|
||||
|
||||
const { mobile } = useDisplay()
|
||||
|
||||
const lastUpdatedTime = ref(0)
|
||||
const lastUpdatedSeconds = ref(0)
|
||||
const rtf = new Intl.RelativeTimeFormat('en', {
|
||||
@@ -151,6 +91,7 @@ const packageCount = ref({
|
||||
built: 0,
|
||||
queued: 0
|
||||
})
|
||||
|
||||
const packages = ref<{
|
||||
built: Array<Package>
|
||||
building: Array<Package>
|
||||
@@ -161,71 +102,54 @@ const packages = ref<{
|
||||
queued: []
|
||||
})
|
||||
|
||||
const getTotalPackages = () => {
|
||||
fetch('https://api.alhp.dev/packages?limit=1&offset=0', {
|
||||
method: 'GET'
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.ok) return response.json()
|
||||
})
|
||||
.then((json: Packages) => {
|
||||
if (!json) return
|
||||
packageCount.value.total = json.total
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
})
|
||||
const fetchPackages = async (endpoint: string, status?: string) => {
|
||||
let url = `${BASE_URL}?limit=0&offset=0`
|
||||
if (status) {
|
||||
url += `&status=${status}`
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(url, { method: 'GET' })
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
const json: Packages = await response.json()
|
||||
return json
|
||||
} catch (error) {
|
||||
console.error('Error fetching packages:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const getBuiltPackages = () => {
|
||||
fetch('https://api.alhp.dev/packages?limit=1&offset=0&status=built', {
|
||||
method: 'GET'
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.ok) return response.json()
|
||||
})
|
||||
.then((json: Packages) => {
|
||||
if (!json) return
|
||||
packageCount.value.built = json.total
|
||||
packages.value.built = json.packages
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
})
|
||||
const getTotalPackages = async () => {
|
||||
const json = await fetchPackages(`${BASE_URL}?limit=1&offset=0`)
|
||||
if (json) {
|
||||
packageCount.value.total = json.total
|
||||
}
|
||||
}
|
||||
|
||||
const getBuildingPackages = () => {
|
||||
fetch('https://api.alhp.dev/packages?limit=0&offset=0&status=building', {
|
||||
method: 'GET'
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.ok) return response.json()
|
||||
})
|
||||
.then((json: Packages) => {
|
||||
if (!json) return
|
||||
packageCount.value.building = json.total
|
||||
packages.value.building = json.packages
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
})
|
||||
const getBuiltPackages = async () => {
|
||||
const json = await fetchPackages(`${BASE_URL}?limit=0&offset=0`, 'built')
|
||||
if (json) {
|
||||
packageCount.value.built = json.total
|
||||
packages.value.built = json.packages
|
||||
}
|
||||
}
|
||||
|
||||
const getQueuedPackages = () => {
|
||||
fetch('https://api.alhp.dev/packages?limit=0&offset=0&status=queued', {
|
||||
method: 'GET'
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.ok) return response.json()
|
||||
})
|
||||
.then((json: Packages) => {
|
||||
if (!json) return
|
||||
packageCount.value.queued = json.total
|
||||
packages.value.queued = json.packages
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
})
|
||||
const getBuildingPackages = async () => {
|
||||
const json = await fetchPackages(`${BASE_URL}?limit=0&offset=0`, 'building')
|
||||
if (json) {
|
||||
packageCount.value.building = json.total
|
||||
packages.value.building = json.packages
|
||||
}
|
||||
}
|
||||
|
||||
const getQueuedPackages = async () => {
|
||||
const json = await fetchPackages(`${BASE_URL}?limit=0&offset=0`, 'queued')
|
||||
if (json) {
|
||||
packageCount.value.queued = json.total
|
||||
packages.value.queued = json.packages
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@@ -233,14 +157,15 @@ onMounted(() => {
|
||||
getBuiltPackages()
|
||||
getBuildingPackages()
|
||||
getQueuedPackages()
|
||||
|
||||
lastUpdatedTime.value = Date.now()
|
||||
lastUpdatedSeconds.value = Math.floor((Date.now() - lastUpdatedTime.value) / 1000)
|
||||
|
||||
const lastUpdatedInterval = setInterval(() => {
|
||||
setInterval(() => {
|
||||
lastUpdatedSeconds.value = Math.floor((Date.now() - lastUpdatedTime.value) / 1000)
|
||||
}, 1000)
|
||||
|
||||
const interval = setInterval(
|
||||
setInterval(
|
||||
() => {
|
||||
getTotalPackages()
|
||||
getBuiltPackages()
|
||||
@@ -249,7 +174,7 @@ onMounted(() => {
|
||||
lastUpdatedTime.value = Date.now()
|
||||
lastUpdatedSeconds.value = Math.floor((Date.now() - lastUpdatedTime.value) / 1000)
|
||||
},
|
||||
updateIntervalInMinutes * 60 * 1000
|
||||
UPDATE_INTERVAL_MINUTES * 60 * 1000
|
||||
)
|
||||
})
|
||||
</script>
|
||||
@@ -313,6 +238,7 @@ onMounted(() => {
|
||||
0% {
|
||||
transform: scale(0.33);
|
||||
}
|
||||
|
||||
80%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
@@ -323,6 +249,7 @@ onMounted(() => {
|
||||
0% {
|
||||
transform: scale(0.33);
|
||||
}
|
||||
|
||||
80%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
@@ -333,9 +260,11 @@ onMounted(() => {
|
||||
0% {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
@@ -345,9 +274,11 @@ onMounted(() => {
|
||||
0% {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
51
frontend/src/components/QueuedPackagesList.vue
Normal file
51
frontend/src/components/QueuedPackagesList.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<v-sheet color="transparent" rounded width="100%">
|
||||
<!-- SHOW MESSAGE IF NO QUEUED PACKAGES -->
|
||||
<template v-if="packages.length === 0">
|
||||
<span class="text-grey"> No packages queued.</span>
|
||||
</template>
|
||||
<!-- ELSE SHOW EXPANSION PANEL -->
|
||||
<template v-else>
|
||||
<!-- FULL LIST EXPANSION PANEL -->
|
||||
<v-expansion-panels>
|
||||
<v-expansion-panel bg-color="#303030" color="primary" elevation="0">
|
||||
<v-expansion-panel-title>
|
||||
Show all queued packages
|
||||
<span class="ms-1 text-disabled text-high-emphasis">({{ packages.length }})</span>
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-list bg-color="transparent" outlined rounded>
|
||||
<v-list-item v-for="(pkg, index) in packages" :key="index">
|
||||
<template #prepend>
|
||||
<v-icon
|
||||
icon="mdi-chevron-right"
|
||||
style="margin-left: -20px; margin-right: -20px" />
|
||||
</template>
|
||||
<v-list-item-title>
|
||||
{{ pkg.pkgbase }}
|
||||
<span class="text-grey">({{ pkg.repo }})</span>
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>
|
||||
{{ pkg.arch_version }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-expansion-panel-text>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</template>
|
||||
</v-sheet>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineProps } from 'vue'
|
||||
import type { Package } from '@/types/Package'
|
||||
|
||||
defineProps({
|
||||
packages: {
|
||||
type: Array<Package>,
|
||||
required: true,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
</script>
|
Reference in New Issue
Block a user