import { defineStore } from 'pinia' import { reactive } from 'vue' import { components, getPackages } from '@/api' interface PackageFilters { status?: components['schemas']['Package']['status'][] pkgbase?: components['schemas']['Package']['pkgbase'] exact?: boolean repo?: components['schemas']['Package']['repo'] } export const usePackagesStore = defineStore('packages', () => { const state = reactive({ packages: [] as components['schemas']['Package'][], currentlyBuildingPackages: [] as components['schemas']['Package'][], total: 0, offset: 0, limit: Number(import.meta.env.VITE_LIMIT) || 50, loading: false, loadingCurrentlyBuilding: false, error: null as string | null, errorCurrentlyBuilding: null as string | null, lastUpdated: Date.now(), filters: { status: undefined, pkgbase: undefined, exact: undefined, repo: undefined } as PackageFilters }) // Actions const fetchPackages = (init = false) => { state.loading = true state.error = null state.packages = [] state.total = 0 if (init) { initFromUrl() } const filter: PackageFilters = {} if (state.filters.status && state.filters.status.length > 0) { filter.status = state.filters.status } if (state.filters.pkgbase) { filter.pkgbase = state.filters.pkgbase } if (state.filters.exact === true) { filter.exact = state.filters.exact } if (state.filters.repo) { filter.repo = state.filters.repo } // @ts-ignore getPackages({ limit: state.limit, offset: state.offset, ...filter }) .then((response) => { if (!response) throw new Error('No response from API') state.packages = response.packages || [] state.total = response.total || 0 state.offset = response.offset || 0 state.limit = response.limit || state.limit }) .catch((err) => { if (err.statusCode === 404) { state.packages = [] state.total = 0 state.offset = 0 state.limit = Number(import.meta.env.VITE_LIMIT) || 50 } else { state.error = err instanceof Error ? err.message : 'Failed to fetch packages' console.error('Error fetching packages:', err) } }) .finally(() => { state.loading = false state.lastUpdated = Date.now() }) } const fetchCurrentlyBuilding = () => { state.loadingCurrentlyBuilding = true state.errorCurrentlyBuilding = null state.currentlyBuildingPackages = [] getPackages({ limit: 0, offset: 0, status: ['queued', 'building', 'built'] }) .then((response) => { state.currentlyBuildingPackages = response?.packages || [] }) .catch((err) => { if (err.statusCode === 404) { state.currentlyBuildingPackages = [] } else { state.errorCurrentlyBuilding = err instanceof Error ? err.message : 'Failed to fetch currently building packages' console.error('Error fetching queued packages:', err) } }) .finally(() => { state.loadingCurrentlyBuilding = false }) } const goToPage = (page: number) => { state.offset = (page - 1) * state.limit updateUrlParams() fetchPackages() } const setFilters = (newFilters: PackageFilters, page?: number) => { state.filters = JSON.parse(JSON.stringify(newFilters)) if (state.filters.exact === false) { state.filters.exact = undefined } if (page) { state.offset = (page - 1) * state.limit } updateUrlParams() fetchPackages() } const resetFilters = () => { state.filters = { status: undefined, pkgbase: undefined, exact: undefined, repo: undefined } state.offset = 0 state.limit = Number(import.meta.env.VITE_LIMIT) || 50 updateUrlParams() fetchPackages() } const updateUrlParams = () => { const params = new URLSearchParams() let page = state.offset / state.limit + 1 // Only add a page parameter if it's not the first page if (page > 1) { params.set('page', page.toString()) } if (state.filters.status && state.filters.status.length > 0) { state.filters.status.forEach((status: components['schemas']['Package']['status']) => { if (status) { params.append('status', status) } }) } if (state.filters.pkgbase) { params.set('pkgbase', state.filters.pkgbase.toLowerCase()) } if (state.filters.repo) { params.set('repo', state.filters.repo) } if (state.filters.exact === true) { params.set('exact', '') } else { params.delete('exact') } const paramsString = params.toString() if (paramsString) { window.history.pushState(null, '', `${window.location.pathname}?${paramsString}`) } else { window.history.pushState(null, '', window.location.pathname) } } const initFromUrl = () => { const urlParams = new URLSearchParams(window.location.search) if (urlParams.has('page')) { const pageParam = urlParams.get('page') || '1' const parsedPage = parseInt(pageParam, 10) const page = !isNaN(parsedPage) && parsedPage > 0 ? parsedPage : 1 state.offset = (page - 1) * state.limit } if (urlParams.has('status')) { const status = urlParams.getAll('status') state.filters.status = status as components['schemas']['Package']['status'][] } if (urlParams.has('pkgbase')) { const pkgbase = urlParams.get('pkgbase') if (pkgbase === null) return state.filters.pkgbase = pkgbase as components['schemas']['Package']['pkgbase'] } if (urlParams.has('repo')) { const repo = urlParams.get('repo') if (repo === null) return state.filters.repo = repo as components['schemas']['Package']['repo'] } if (urlParams.has('exact')) { state.filters.exact = true } } return { state, // Actions fetchPackages, fetchCurrentlyBuilding, goToPage, setFilters, resetFilters } })