diff --git a/frontend/composables/use-route-params.ts b/frontend/composables/use-route-params.ts index c06d2db..6708c38 100644 --- a/frontend/composables/use-route-params.ts +++ b/frontend/composables/use-route-params.ts @@ -1,3 +1,5 @@ +import { useRouteQuery as useRouteQueryBase } from "@vueuse/router"; + /* eslint no-redeclare: 0 */ import { WritableComputedRef } from "vue"; @@ -10,12 +12,24 @@ export function useRouteQuery(q: string, def: any): WritableComputedRef { const route = useRoute(); const router = useRouter(); + const v = useRouteQueryBase(q, def); + + const first = computed(() => { + const qv = route.query[q]; + if (Array.isArray(qv)) { + return qv[0]?.toString() || def; + } + return qv?.toString() || def; + }); + + onMounted(() => { + if (route.query[q] === undefined) { + v.value = def; + } + }); + switch (typeof def) { case "string": - if (route.query[q] === undefined) { - router.push({ query: { ...route.query, [q]: def } }); - } - return computed({ get: () => { const qv = route.query[q]; @@ -46,11 +60,7 @@ export function useRouteQuery(q: string, def: any): WritableComputedRef { case "boolean": return computed({ get: () => { - const qv = route.query[q]; - if (Array.isArray(qv)) { - return qv[0] === "true"; - } - return qv === "true"; + return first.value === "true"; }, set: v => { const query = { ...route.query, [q]: `${v}` }; @@ -59,16 +69,9 @@ export function useRouteQuery(q: string, def: any): WritableComputedRef { }); case "number": return computed({ - get: () => { - const qv = route.query[q]; - if (Array.isArray(qv) && qv.length > 0) { - return parseInt(qv[0] as string, 10); - } - return parseInt(qv as string, 10) || def; - }, - set: v => { - const query = { ...route.query, [q]: `${v}` }; - router.push({ query }); + get: () => parseInt(first.value, 10), + set: nv => { + v.value = nv.toString(); }, }); } diff --git a/frontend/package.json b/frontend/package.json index 93ca2a2..03a3535 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -35,6 +35,7 @@ "@tailwindcss/forms": "^0.5.2", "@tailwindcss/typography": "^0.5.4", "@vueuse/nuxt": "^9.1.1", + "@vueuse/router": "^9.9.0", "autoprefixer": "^10.4.8", "chart.js": "^4.0.1", "daisyui": "^2.24.0", @@ -44,6 +45,7 @@ "postcss": "^8.4.16", "tailwindcss": "^3.1.8", "vue": "^3.2.38", - "vue-chartjs": "^4.1.2" + "vue-chartjs": "^4.1.2", + "vue-router": "4" } } \ No newline at end of file diff --git a/frontend/pages/items.vue b/frontend/pages/items.vue index ce58ff9..0c16cf4 100644 --- a/frontend/pages/items.vue +++ b/frontend/pages/items.vue @@ -13,6 +13,8 @@ }); const searchLocked = ref(false); + const queryParamsInitialized = ref(false); + const initialSearch = ref(true); const api = useUserApi(); const loading = useMinLoader(2000); @@ -20,21 +22,21 @@ const total = ref(0); const page = useRouteQuery("page", 1); - const perPage = useRouteQuery("perPage", 24); + const pageSize = useRouteQuery("pageSize", 21); const query = useRouteQuery("q", ""); const advanced = useRouteQuery("advanced", false); const includeArchived = useRouteQuery("archived", false); const hasNext = computed(() => { - return page.value * perPage.value < total.value; + return page.value * pageSize.value < total.value; }); const totalPages = computed(() => { - return Math.ceil(total.value / perPage.value); + return Math.ceil(total.value / pageSize.value); }); function next() { - page.value = Math.min(Math.ceil(total.value / perPage.value), page.value + 1); + page.value = Math.min(Math.ceil(total.value / pageSize.value), page.value + 1); } const hasPrev = computed(() => { @@ -46,7 +48,14 @@ } async function resetPageSearch() { - page.value = 1; + if (searchLocked.value) { + return; + } + + if (!initialSearch.value) { + page.value = 1; + } + items.value = []; await search(); } @@ -67,29 +76,30 @@ labels, includeArchived: includeArchived.value, page: page.value, - pageSize: perPage.value, + pageSize: pageSize.value, }); if (error) { - page.value--; + page.value = Math.max(1, page.value - 1); loading.value = false; return; } if (!data.items || data.items.length === 0) { - page.value--; + page.value = Math.max(1, page.value - 1); + loading.value = false; + return; } total.value = data.total; items.value = data.items; loading.value = false; + initialSearch.value = false; } const route = useRoute(); const router = useRouter(); - const queryParamsInitialized = ref(false); - onMounted(async () => { loading.value = true; // Wait until locations and labels are loaded @@ -166,8 +176,10 @@ } }); - watchDebounced([selectedLocations, selectedLabels, query, page, perPage], search, { debounce: 250, maxWait: 1000 }); - watch(includeArchived, search); + watchDebounced([selectedLocations, selectedLabels, query], resetPageSearch, { debounce: 250, maxWait: 1000 }); + watch(includeArchived, resetPageSearch); + + watchDebounced([page, pageSize], search, { debounce: 250, maxWait: 1000 });