Persist list pagination and search in the URL
All checks were successful
CI / backend (push) Successful in 1m25s
CI / naming (push) Successful in 1s
CI / frontend (push) Successful in 7s

List pages kept the current page in local state, so clicking into an
asset and hitting Back remounted the list at page 1. A shared
useListQuery composable now mirrors the page (and search term) into the
URL query via router.replace across all 18 list pages, so Back restores
the page you were on and lists are deep-linkable. Page 1 with no search
stays a bare path; changing a filter resets to page 1; unrelated query
keys are preserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-07-12 14:19:50 -04:00
parent b2e1827e5d
commit f6dcaef4c0
20 changed files with 198 additions and 85 deletions

View File

@@ -103,11 +103,12 @@ import { businessunitsApi } from '../../api'
import PaginationBar from '../../components/PaginationBar.vue'
import { useToast } from '../../composables/toast'
import { apiError } from '../../utils/apiError'
import { useListQuery } from '@/composables/listQuery'
const toast = useToast()
const items = ref([])
const loading = ref(true)
const page = ref(1)
const { page, setPage } = useListQuery({ onChange: loadData })
const totalPages = ref(1)
const perPage = ref(20)
@@ -136,11 +137,11 @@ async function loadData() {
}
}
function goToPage(p) { page.value = p; loadData() }
function goToPage(p) { setPage(p); loadData() }
function changePerPage(newPerPage) {
perPage.value = newPerPage
page.value = 1
setPage(1)
loadData()
}

View File

@@ -213,14 +213,14 @@ import { locationsApi } from '../../api'
import PaginationBar from '../../components/PaginationBar.vue'
import { useToast } from '../../composables/toast'
import { apiError } from '../../utils/apiError'
import { useListQuery } from '@/composables/listQuery'
const toast = useToast()
const locations = ref([])
const locationTypes = ref([])
const allLocations = ref([])
const loading = ref(true)
const search = ref('')
const page = ref(1)
const { page, search, setPage, setSearch } = useListQuery({ onChange: loadLocations })
const totalPages = ref(1)
const perPage = ref(20)
@@ -286,19 +286,19 @@ async function loadLocations() {
function debouncedSearch() {
clearTimeout(searchTimeout)
searchTimeout = setTimeout(() => {
page.value = 1
setSearch(search.value)
loadLocations()
}, 300)
}
function goToPage(p) {
page.value = p
setPage(p)
loadLocations()
}
function changePerPage(newPerPage) {
perPage.value = newPerPage
page.value = 1
setPage(1)
loadLocations()
}

View File

@@ -147,11 +147,12 @@ import { modeltypesApi } from '../../api'
import PaginationBar from '../../components/PaginationBar.vue'
import { useToast } from '../../composables/toast'
import { apiError } from '../../utils/apiError'
import { useListQuery } from '@/composables/listQuery'
const toast = useToast()
const modelTypes = ref([])
const loading = ref(true)
const page = ref(1)
const { page, setPage } = useListQuery({ onChange: loadTypes })
const totalPages = ref(1)
const perPage = ref(20)
@@ -192,13 +193,13 @@ async function loadTypes() {
}
function goToPage(p) {
page.value = p
setPage(p)
loadTypes()
}
function changePerPage(newPerPage) {
perPage.value = newPerPage
page.value = 1
setPage(1)
loadTypes()
}

View File

@@ -209,15 +209,15 @@ import { modelsApi, vendorsApi, modeltypesApi } from '../../api'
import PaginationBar from '../../components/PaginationBar.vue'
import { useToast } from '../../composables/toast'
import { apiError } from '../../utils/apiError'
import { useListQuery } from '@/composables/listQuery'
const toast = useToast()
const models = ref([])
const vendors = ref([])
const modelTypes = ref([])
const loading = ref(true)
const search = ref('')
const { page, search, setPage, setSearch } = useListQuery({ onChange: loadModels })
const vendorFilter = ref('')
const page = ref(1)
const totalPages = ref(1)
const perPage = ref(20)
@@ -290,19 +290,19 @@ async function loadModelTypes() {
function debouncedSearch() {
clearTimeout(searchTimeout)
searchTimeout = setTimeout(() => {
page.value = 1
setSearch(search.value)
loadModels()
}, 300)
}
function goToPage(p) {
page.value = p
setPage(p)
loadModels()
}
function changePerPage(newPerPage) {
perPage.value = newPerPage
page.value = 1
setPage(1)
loadModels()
}

View File

@@ -121,11 +121,12 @@ import { operatingsystemsApi } from '../../api'
import PaginationBar from '../../components/PaginationBar.vue'
import { useToast } from '../../composables/toast'
import { apiError } from '../../utils/apiError'
import { useListQuery } from '@/composables/listQuery'
const toast = useToast()
const items = ref([])
const loading = ref(true)
const page = ref(1)
const { page, setPage } = useListQuery({ onChange: loadData })
const totalPages = ref(1)
const perPage = ref(20)
@@ -154,11 +155,11 @@ async function loadData() {
}
}
function goToPage(p) { page.value = p; loadData() }
function goToPage(p) { setPage(p); loadData() }
function changePerPage(newPerPage) {
perPage.value = newPerPage
page.value = 1
setPage(1)
loadData()
}

View File

@@ -141,11 +141,12 @@ import PaginationBar from '../../components/PaginationBar.vue'
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
import { useToast } from '../../composables/toast'
import { apiError } from '../../utils/apiError'
import { useListQuery } from '@/composables/listQuery'
const toast = useToast()
const statuses = ref([])
const loading = ref(true)
const page = ref(1)
const { page, setPage } = useListQuery({ onChange: loadStatuses })
const totalPages = ref(1)
const perPage = ref(20)
@@ -186,13 +187,13 @@ async function loadStatuses() {
}
function goToPage(p) {
page.value = p
setPage(p)
loadStatuses()
}
function changePerPage(newPerPage) {
perPage.value = newPerPage
page.value = 1
setPage(1)
loadStatuses()
}

View File

@@ -287,6 +287,7 @@ import { networkApi, locationsApi } from '../../api'
import PaginationBar from '../../components/PaginationBar.vue'
import { useToast } from '../../composables/toast'
import { apiError } from '../../utils/apiError'
import { useListQuery } from '@/composables/listQuery'
const toast = useToast()
const route = useRoute()
@@ -295,10 +296,9 @@ const subnets = ref([])
const vlans = ref([])
const locations = ref([])
const loading = ref(true)
const search = ref('')
const { page, search, setPage, setSearch } = useListQuery({ onChange: loadSubnets })
const vlanFilter = ref('')
const typeFilter = ref('')
const page = ref(1)
const totalPages = ref(1)
const perPage = ref(20)
@@ -383,19 +383,19 @@ async function loadSubnets() {
function debouncedSearch() {
clearTimeout(searchTimeout)
searchTimeout = setTimeout(() => {
page.value = 1
setSearch(search.value)
loadSubnets()
}, 300)
}
function goToPage(p) {
page.value = p
setPage(p)
loadSubnets()
}
function changePerPage(newPerPage) {
perPage.value = newPerPage
page.value = 1
setPage(1)
loadSubnets()
}

View File

@@ -189,13 +189,13 @@ import { networkApi } from '../../api'
import PaginationBar from '../../components/PaginationBar.vue'
import { useToast } from '../../composables/toast'
import { apiError } from '../../utils/apiError'
import { useListQuery } from '@/composables/listQuery'
const toast = useToast()
const vlans = ref([])
const loading = ref(true)
const search = ref('')
const { page, search, setPage, setSearch } = useListQuery({ onChange: loadVLANs })
const typeFilter = ref('')
const page = ref(1)
const totalPages = ref(1)
const perPage = ref(20)
@@ -243,19 +243,19 @@ async function loadVLANs() {
function debouncedSearch() {
clearTimeout(searchTimeout)
searchTimeout = setTimeout(() => {
page.value = 1
setSearch(search.value)
loadVLANs()
}, 300)
}
function goToPage(p) {
page.value = p
setPage(p)
loadVLANs()
}
function changePerPage(newPerPage) {
perPage.value = newPerPage
page.value = 1
setPage(1)
loadVLANs()
}