Compare commits
2 Commits
d8f52cea61
...
8fe8f1e122
Author | SHA1 | Date | |
---|---|---|---|
8fe8f1e122 | |||
db9c7ee523 |
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']
|
CurrentlyBuilding: typeof import('./src/components/CurrentlyBuilding.vue')['default']
|
||||||
MainNav: typeof import('./src/components/MainNav.vue')['default']
|
MainNav: typeof import('./src/components/MainNav.vue')['default']
|
||||||
Packages: typeof import('./src/components/Packages.vue')['default']
|
Packages: typeof import('./src/components/Packages.vue')['default']
|
||||||
|
QueuedPackagesList: typeof import('./src/components/QueuedPackagesList.vue')['default']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
variant="elevated">
|
variant="elevated">
|
||||||
<v-card-title>
|
<v-card-title>
|
||||||
<v-row align="center" class="w-100" style="margin-top: 1px">
|
<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">
|
<v-row :class="mobile ? 'mt-1' : ''" class="flex-nowrap ms-1 d-flex align-items-center">
|
||||||
<div
|
<div
|
||||||
:class="
|
:class="
|
||||||
@@ -19,8 +19,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
<v-col v-if="packageCount.building > 0" class="v-col-12 v-col-lg-8 mb-3">
|
||||||
<v-col v-if="packageCount.building > 0" class="v-col-12 v-col-lg-8">
|
|
||||||
<v-progress-linear
|
<v-progress-linear
|
||||||
:max="packageCount.built + packageCount.building + packageCount.queued"
|
:max="packageCount.built + packageCount.building + packageCount.queued"
|
||||||
:model-value="packageCount.built"
|
:model-value="packageCount.built"
|
||||||
@@ -29,10 +28,9 @@
|
|||||||
rounded
|
rounded
|
||||||
striped></v-progress-linear>
|
striped></v-progress-linear>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col
|
<v-col
|
||||||
:class="mobile ? 'mt-n3' : 'text-end ms-auto'"
|
: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"
|
cols="auto"
|
||||||
style="font-size: 13px">
|
style="font-size: 13px">
|
||||||
Last updated
|
Last updated
|
||||||
@@ -44,7 +42,6 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
|
|
||||||
<v-card-text
|
<v-card-text
|
||||||
v-if="packageCount.building > 0 || packageCount.queued > 0"
|
v-if="packageCount.building > 0 || packageCount.queued > 0"
|
||||||
class="d-flex flex-column">
|
class="d-flex flex-column">
|
||||||
@@ -55,88 +52,31 @@
|
|||||||
<div class="pulsating-circle-amber me-4" />
|
<div class="pulsating-circle-amber me-4" />
|
||||||
</template>
|
</template>
|
||||||
<v-list-item-title>
|
<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-title>
|
||||||
<v-list-item-subtitle>{{ pkg.arch_version }}</v-list-item-subtitle>
|
<v-list-item-subtitle>{{ pkg.arch_version }}</v-list-item-subtitle>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
|
<v-sheet class="ps-4 mt-4" color="transparent" rounded width="100%">
|
||||||
<v-list width="100%">
|
<h4 class="mb-2 font-weight-light text-grey">Queued</h4>
|
||||||
<v-list-subheader>Queued</v-list-subheader>
|
<QueuedPackagesList :packages="packages.queued" />
|
||||||
|
</v-sheet>
|
||||||
<!-- 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-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// built + building + queued = total
|
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import type { Packages } from '@/types/Packages'
|
import type { Packages } from '@/types/Packages'
|
||||||
import { Package } from '@/types/Package'
|
import { Package } from '@/types/Package'
|
||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
|
import QueuedPackagesList from './QueuedPackagesList.vue'
|
||||||
|
|
||||||
// This variable sets the update-interval
|
const BASE_URL = 'https://api.alhp.dev/packages'
|
||||||
const updateIntervalInMinutes = 5
|
const UPDATE_INTERVAL_MINUTES = 5
|
||||||
|
|
||||||
const { mobile } = useDisplay()
|
const { mobile } = useDisplay()
|
||||||
|
|
||||||
const lastUpdatedTime = ref(0)
|
const lastUpdatedTime = ref(0)
|
||||||
const lastUpdatedSeconds = ref(0)
|
const lastUpdatedSeconds = ref(0)
|
||||||
const rtf = new Intl.RelativeTimeFormat('en', {
|
const rtf = new Intl.RelativeTimeFormat('en', {
|
||||||
@@ -151,6 +91,7 @@ const packageCount = ref({
|
|||||||
built: 0,
|
built: 0,
|
||||||
queued: 0
|
queued: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
const packages = ref<{
|
const packages = ref<{
|
||||||
built: Array<Package>
|
built: Array<Package>
|
||||||
building: Array<Package>
|
building: Array<Package>
|
||||||
@@ -161,71 +102,54 @@ const packages = ref<{
|
|||||||
queued: []
|
queued: []
|
||||||
})
|
})
|
||||||
|
|
||||||
const getTotalPackages = () => {
|
const fetchPackages = async (endpoint: string, status?: string) => {
|
||||||
fetch('https://api.alhp.dev/packages?limit=1&offset=0', {
|
let url = `${BASE_URL}?limit=0&offset=0`
|
||||||
method: 'GET'
|
if (status) {
|
||||||
})
|
url += `&status=${status}`
|
||||||
.then((response) => {
|
}
|
||||||
if (response.ok) return response.json()
|
|
||||||
})
|
try {
|
||||||
.then((json: Packages) => {
|
const response = await fetch(url, { method: 'GET' })
|
||||||
if (!json) return
|
if (!response.ok) {
|
||||||
packageCount.value.total = json.total
|
throw new Error(`HTTP error! status: ${response.status}`)
|
||||||
})
|
}
|
||||||
.catch((error) => {
|
const json: Packages = await response.json()
|
||||||
console.error(error)
|
return json
|
||||||
})
|
} catch (error) {
|
||||||
|
console.error('Error fetching packages:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getBuiltPackages = () => {
|
const getTotalPackages = async () => {
|
||||||
fetch('https://api.alhp.dev/packages?limit=1&offset=0&status=built', {
|
const json = await fetchPackages(`${BASE_URL}?limit=1&offset=0`)
|
||||||
method: 'GET'
|
if (json) {
|
||||||
})
|
packageCount.value.total = json.total
|
||||||
.then((response) => {
|
}
|
||||||
if (response.ok) return response.json()
|
}
|
||||||
})
|
|
||||||
.then((json: Packages) => {
|
const getBuiltPackages = async () => {
|
||||||
if (!json) return
|
const json = await fetchPackages(`${BASE_URL}?limit=0&offset=0`, 'built')
|
||||||
|
if (json) {
|
||||||
packageCount.value.built = json.total
|
packageCount.value.built = json.total
|
||||||
packages.value.built = json.packages
|
packages.value.built = json.packages
|
||||||
})
|
}
|
||||||
.catch((error) => {
|
|
||||||
console.error(error)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getBuildingPackages = () => {
|
const getBuildingPackages = async () => {
|
||||||
fetch('https://api.alhp.dev/packages?limit=0&offset=0&status=building', {
|
const json = await fetchPackages(`${BASE_URL}?limit=0&offset=0`, 'building')
|
||||||
method: 'GET'
|
if (json) {
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
if (response.ok) return response.json()
|
|
||||||
})
|
|
||||||
.then((json: Packages) => {
|
|
||||||
if (!json) return
|
|
||||||
packageCount.value.building = json.total
|
packageCount.value.building = json.total
|
||||||
packages.value.building = json.packages
|
packages.value.building = json.packages
|
||||||
})
|
}
|
||||||
.catch((error) => {
|
|
||||||
console.error(error)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getQueuedPackages = () => {
|
const getQueuedPackages = async () => {
|
||||||
fetch('https://api.alhp.dev/packages?limit=0&offset=0&status=queued', {
|
const json = await fetchPackages(`${BASE_URL}?limit=0&offset=0`, 'queued')
|
||||||
method: 'GET'
|
if (json) {
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
if (response.ok) return response.json()
|
|
||||||
})
|
|
||||||
.then((json: Packages) => {
|
|
||||||
if (!json) return
|
|
||||||
packageCount.value.queued = json.total
|
packageCount.value.queued = json.total
|
||||||
packages.value.queued = json.packages
|
packages.value.queued = json.packages
|
||||||
})
|
}
|
||||||
.catch((error) => {
|
|
||||||
console.error(error)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -233,14 +157,15 @@ onMounted(() => {
|
|||||||
getBuiltPackages()
|
getBuiltPackages()
|
||||||
getBuildingPackages()
|
getBuildingPackages()
|
||||||
getQueuedPackages()
|
getQueuedPackages()
|
||||||
|
|
||||||
lastUpdatedTime.value = Date.now()
|
lastUpdatedTime.value = Date.now()
|
||||||
lastUpdatedSeconds.value = Math.floor((Date.now() - lastUpdatedTime.value) / 1000)
|
lastUpdatedSeconds.value = Math.floor((Date.now() - lastUpdatedTime.value) / 1000)
|
||||||
|
|
||||||
const lastUpdatedInterval = setInterval(() => {
|
setInterval(() => {
|
||||||
lastUpdatedSeconds.value = Math.floor((Date.now() - lastUpdatedTime.value) / 1000)
|
lastUpdatedSeconds.value = Math.floor((Date.now() - lastUpdatedTime.value) / 1000)
|
||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
const interval = setInterval(
|
setInterval(
|
||||||
() => {
|
() => {
|
||||||
getTotalPackages()
|
getTotalPackages()
|
||||||
getBuiltPackages()
|
getBuiltPackages()
|
||||||
@@ -249,7 +174,7 @@ onMounted(() => {
|
|||||||
lastUpdatedTime.value = Date.now()
|
lastUpdatedTime.value = Date.now()
|
||||||
lastUpdatedSeconds.value = Math.floor((Date.now() - lastUpdatedTime.value) / 1000)
|
lastUpdatedSeconds.value = Math.floor((Date.now() - lastUpdatedTime.value) / 1000)
|
||||||
},
|
},
|
||||||
updateIntervalInMinutes * 60 * 1000
|
UPDATE_INTERVAL_MINUTES * 60 * 1000
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -313,6 +238,7 @@ onMounted(() => {
|
|||||||
0% {
|
0% {
|
||||||
transform: scale(0.33);
|
transform: scale(0.33);
|
||||||
}
|
}
|
||||||
|
|
||||||
80%,
|
80%,
|
||||||
100% {
|
100% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -323,6 +249,7 @@ onMounted(() => {
|
|||||||
0% {
|
0% {
|
||||||
transform: scale(0.33);
|
transform: scale(0.33);
|
||||||
}
|
}
|
||||||
|
|
||||||
80%,
|
80%,
|
||||||
100% {
|
100% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -333,9 +260,11 @@ onMounted(() => {
|
|||||||
0% {
|
0% {
|
||||||
transform: scale(0.8);
|
transform: scale(0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: scale(0.8);
|
transform: scale(0.8);
|
||||||
}
|
}
|
||||||
@@ -345,9 +274,11 @@ onMounted(() => {
|
|||||||
0% {
|
0% {
|
||||||
transform: scale(0.8);
|
transform: scale(0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: scale(0.8);
|
transform: scale(0.8);
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
<template>
|
<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>
|
<h5 class="text-h5 mb-4">Packages</h5>
|
||||||
|
<v-row :style="mobile ? '' : `height: ${ROW_HEIGHT}px`" width="100%">
|
||||||
<v-row :style="mobile ? '' : 'height: 60px'" width="100%">
|
|
||||||
<v-col class="v-col-12 v-col-sm-2 v-col-lg-2">
|
<v-col class="v-col-12 v-col-sm-2 v-col-lg-2">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="inputPkgBase"
|
v-model="inputPkgBase"
|
||||||
@@ -11,10 +10,9 @@
|
|||||||
placeholder="Search Pkgbase"
|
placeholder="Search Pkgbase"
|
||||||
variant="outlined"></v-text-field>
|
variant="outlined"></v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col class="v-col-12 v-col-sm-2 v-col-lg-2 mt-n6 mt-sm-0">
|
<v-col class="v-col-12 v-col-sm-2 v-col-lg-2 mt-n6 mt-sm-0">
|
||||||
<v-select
|
<v-select
|
||||||
v-model="selectRepo"
|
v-model="selectedRepo"
|
||||||
:items="selectRepoItems"
|
:items="selectRepoItems"
|
||||||
clearable
|
clearable
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -25,10 +23,9 @@
|
|||||||
single-line
|
single-line
|
||||||
variant="outlined"></v-select>
|
variant="outlined"></v-select>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col class="v-col-12 v-col-sm-2 v-col-lg-3 mt-n6 mt-sm-0">
|
<v-col class="v-col-12 v-col-sm-2 v-col-lg-3 mt-n6 mt-sm-0">
|
||||||
<v-select
|
<v-select
|
||||||
v-model="selectStatus"
|
v-model="selectedStatuses"
|
||||||
:items="selectStatusItems"
|
:items="selectStatusItems"
|
||||||
chips
|
chips
|
||||||
closable-chips
|
closable-chips
|
||||||
@@ -41,11 +38,9 @@
|
|||||||
return-object
|
return-object
|
||||||
variant="outlined"></v-select>
|
variant="outlined"></v-select>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col class="v-col-12 v-col-sm-2 v-col-lg-2 mt-n6 mt-sm-0">
|
<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-switch v-model="enableExact" color="primary" label="Exact search"></v-switch>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col :class="mobile ? 'mt-n6' : ''" :cols="mobile ? 12 : 'auto'" class="ms-auto">
|
<v-col :class="mobile ? 'mt-n6' : ''" :cols="mobile ? 12 : 'auto'" class="ms-auto">
|
||||||
<v-pagination
|
<v-pagination
|
||||||
v-model="page"
|
v-model="page"
|
||||||
@@ -57,7 +52,6 @@
|
|||||||
variant="text"></v-pagination>
|
variant="text"></v-pagination>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<v-table class="mt-2 mt-sm-6" style="width: 100%; background: transparent; font-size: 1rem">
|
<v-table class="mt-2 mt-sm-6" style="width: 100%; background: transparent; font-size: 1rem">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -84,7 +78,6 @@
|
|||||||
<th class="text-end" scope="col" style="width: 30px">Info</th>
|
<th class="text-end" scope="col" style="width: 30px">Info</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody id="main-tbody">
|
<tbody id="main-tbody">
|
||||||
<tr v-if="noPackagesFound">
|
<tr v-if="noPackagesFound">
|
||||||
No Packages found
|
No Packages found
|
||||||
@@ -93,16 +86,16 @@
|
|||||||
<tr
|
<tr
|
||||||
v-for="(pkg, index) in packages"
|
v-for="(pkg, index) in packages"
|
||||||
:key="index"
|
:key="index"
|
||||||
:style="`background-color: ${getStatus(pkg.status)};`">
|
:style="`background-color: ${getStatusColor(pkg.status)};`">
|
||||||
<td class="font-weight-bold text-no-wrap">
|
<td class="font-weight-bold text-no-wrap">
|
||||||
{{ pkg.repo.split('-')[0] }}
|
{{ repoName(pkg.repo) }}
|
||||||
<v-chip
|
<v-chip
|
||||||
:color="getVersionColor(pkg.repo.split('-')[pkg.repo.split('-').length - 1])"
|
:color="getVersionColor(repoVersion(pkg.repo))"
|
||||||
class="ms-2"
|
class="ms-2"
|
||||||
density="comfortable"
|
density="comfortable"
|
||||||
label
|
label
|
||||||
variant="flat">
|
variant="flat">
|
||||||
{{ pkg.repo.split('-')[pkg.repo.split('-').length - 1] }}
|
{{ repoVersion(pkg.repo) }}
|
||||||
</v-chip>
|
</v-chip>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-no-wrap">{{ pkg.pkgbase }}</td>
|
<td class="text-no-wrap">{{ pkg.pkgbase }}</td>
|
||||||
@@ -165,23 +158,31 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 Package } from '@/types/Package'
|
||||||
import { type Packages } from '@/types/Packages'
|
import { type Packages } from '@/types/Packages'
|
||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
|
|
||||||
|
// Constants
|
||||||
const OFFSET = 50
|
const OFFSET = 50
|
||||||
|
const ROW_HEIGHT = 60
|
||||||
|
const TRANSPARENT_COLOR = 'transparent'
|
||||||
|
|
||||||
|
// Refs
|
||||||
const page = ref(0)
|
const page = ref(0)
|
||||||
const pagesMax = ref(1)
|
const pagesMax = ref(1)
|
||||||
const packages = ref<Array<Package>>([])
|
const packages = ref<Array<Package>>([])
|
||||||
const noPackagesFound = ref(false)
|
const noPackagesFound = ref(false)
|
||||||
const init = 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 { mobile } = useDisplay()
|
||||||
|
|
||||||
const inputPkgBase = ref('')
|
// Select Items
|
||||||
const selectRepo = ref()
|
|
||||||
const selectRepoItems = [
|
const selectRepoItems = [
|
||||||
{ title: 'core-x86-64-v2', value: 'core-x86-64-v2' },
|
{ title: 'core-x86-64-v2', value: 'core-x86-64-v2' },
|
||||||
{ title: 'extra-x86-64-v2', value: 'extra-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: 'extra-x86-64-v4', value: 'extra-x86-64-v4' },
|
||||||
{ title: 'multilib-x86-64-v4', value: 'multilib-x86-64-v4' }
|
{ title: 'multilib-x86-64-v4', value: 'multilib-x86-64-v4' }
|
||||||
]
|
]
|
||||||
const selectStatus = ref<Array<{ title: string; value: string }>>([])
|
|
||||||
const selectStatusItems = [
|
const selectStatusItems = [
|
||||||
{ title: 'Latest', value: 'latest' },
|
{ title: 'Latest', value: 'latest' },
|
||||||
{ title: 'Built', value: 'built' },
|
{ title: 'Built', value: 'built' },
|
||||||
@@ -205,63 +206,142 @@ const selectStatusItems = [
|
|||||||
{ title: 'Signing', value: 'signing' },
|
{ title: 'Signing', value: 'signing' },
|
||||||
{ title: 'Unknown', value: 'unknown' }
|
{ title: 'Unknown', value: 'unknown' }
|
||||||
]
|
]
|
||||||
const enableExact = ref(false)
|
|
||||||
|
|
||||||
const searchPackages = () => {
|
// Computed properties
|
||||||
const offset = OFFSET * (page.value > 0 ? page.value - 1 : page.value)
|
const repoName = (repo: string) => repo.split('-')[0]
|
||||||
noPackagesFound.value = false
|
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()
|
const params = new URLSearchParams()
|
||||||
|
if (page.value > 0) {
|
||||||
// set params
|
params.set('offset', (page.value * OFFSET).toString())
|
||||||
if (offset > 0) {
|
|
||||||
url.searchParams.set('offset', offset.toString())
|
|
||||||
} else {
|
|
||||||
url.searchParams.delete('offset')
|
|
||||||
}
|
}
|
||||||
if (inputPkgBase.value) {
|
if (inputPkgBase.value) {
|
||||||
params.set('pkgbase', inputPkgBase.value.toLowerCase())
|
params.set('pkgbase', inputPkgBase.value.toLowerCase())
|
||||||
url.searchParams.set('pkgbase', inputPkgBase.value.toLowerCase())
|
|
||||||
} else {
|
|
||||||
params.delete('pkgbase')
|
|
||||||
url.searchParams.delete('pkgbase')
|
|
||||||
}
|
}
|
||||||
if (selectRepo.value) {
|
if (selectedRepo.value) {
|
||||||
params.set('repo', selectRepo.value.value)
|
params.set('repo', selectedRepo.value.value)
|
||||||
url.searchParams.set('repo', selectRepo.value.value)
|
|
||||||
} else {
|
|
||||||
params.delete('repo')
|
|
||||||
url.searchParams.delete('repo')
|
|
||||||
}
|
}
|
||||||
if (selectStatus.value.length > 0) {
|
if (selectedStatuses.value.length > 0) {
|
||||||
selectStatus.value.forEach((status) => {
|
selectedStatuses.value.forEach((status) => {
|
||||||
if (!params.has('status', status.value)) {
|
|
||||||
params.append('status', status.value)
|
params.append('status', status.value)
|
||||||
}
|
|
||||||
if (!url.searchParams.has('status', status.value)) {
|
|
||||||
url.searchParams.append('status', status.value)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
params.delete('status')
|
|
||||||
url.searchParams.delete('status')
|
|
||||||
}
|
}
|
||||||
if (enableExact.value) {
|
if (enableExact.value) {
|
||||||
params.set('exact', '')
|
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
|
const searchPackages = () => {
|
||||||
window.history.pushState(null, '', url.toString())
|
const offset = OFFSET * page.value
|
||||||
|
noPackagesFound.value = false
|
||||||
// add limit to query params
|
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('limit', OFFSET.toString())
|
||||||
params.set('offset', offset.toString())
|
params.set('offset', offset.toString())
|
||||||
|
|
||||||
fetch('https://api.alhp.dev/packages?' + params, {
|
fetch('https://api.alhp.dev/packages?' + params, {
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
})
|
})
|
||||||
@@ -276,174 +356,43 @@ const searchPackages = () => {
|
|||||||
})
|
})
|
||||||
.then((json: Packages) => {
|
.then((json: Packages) => {
|
||||||
if (!json) return
|
if (!json) return
|
||||||
|
|
||||||
if (pagesMax.value !== json.total) {
|
|
||||||
pagesMax.value = json.total / OFFSET + 1
|
pagesMax.value = json.total / OFFSET + 1
|
||||||
}
|
|
||||||
|
|
||||||
packages.value = json.packages
|
packages.value = json.packages
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// Properly set the initial values from URL params
|
||||||
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()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
onMounted(() => {
|
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()
|
searchPackages()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Watchers
|
||||||
|
watch(
|
||||||
|
[page, inputPkgBase, selectedRepo, selectedStatuses, enableExact],
|
||||||
|
() => {
|
||||||
|
if (init.value) return
|
||||||
|
updateUrlParams()
|
||||||
|
searchPackages()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
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