diff --git a/src/components/project/ProjectList.vue b/src/components/project/ProjectList.vue index 471e733..1759bdd 100644 --- a/src/components/project/ProjectList.vue +++ b/src/components/project/ProjectList.vue @@ -40,7 +40,7 @@ -
+
@@ -51,7 +51,7 @@
-
-

Showing {{ filteredProjects.length }} of {{ projects.length }} project{{ projects.length !== 1 ? 's' : '' }}

-
+
+
+ + +
+ + + + + + + + +
+ + + +
+
@@ -132,23 +164,90 @@ }>(); const projectStore = useProjectStore(); - const { projects, isLoading: loading } = toRefs(projectStore); - const { createProject, loadProjectData } = useProjectManager(); - + const { createProject } = useProjectManager(); const searchQuery = ref(''); - const filteredProjects = computed(() => { - if (!searchQuery.value) return projects.value; - const query = searchQuery.value.toLowerCase(); - return projects.value.filter(p => p.name.toLowerCase().includes(query)); + const { projects, isLoading: loading, page, perPage, totalItems, totalPages, fetchProjects } = toRefs(projectStore); + + let searchTimeout: any = null; + + watch(searchQuery, newVal => { + if (searchTimeout) clearTimeout(searchTimeout); + searchTimeout = setTimeout(() => { + fetchProjects.value(1, perPage.value, newVal); + }, 300); + }); + + const nextPage = () => { + if (page.value < totalPages.value) { + fetchProjects.value(page.value + 1, perPage.value, searchQuery.value); + } + }; + + const prevPage = () => { + if (page.value > 1) { + fetchProjects.value(page.value - 1, perPage.value, searchQuery.value); + } + }; + + const goToPage = (p: number) => { + if (p !== page.value && p >= 1 && p <= totalPages.value) { + fetchProjects.value(p, perPage.value, searchQuery.value); + } + }; + + const jumpToPage = (event: any) => { + const val = parseInt(event.target.value); + if (!isNaN(val) && val >= 1 && val <= totalPages.value) { + goToPage(val); + // Clear input after jump if desired, or keep it. Keeping it is fine. + } else { + // Reset to current page if invalid + event.target.value = ''; + } + }; + + const visiblePages = computed(() => { + const total = totalPages.value; + const current = page.value; + const delta = 2; // How many pages to show around current + const range: (number | string)[] = []; + const left = current - delta; + const right = current + delta + 1; + + for (let i = 1; i <= total; i++) { + if (i === 1 || i === total || (i >= left && i < right)) { + range.push(i); + } + } + + // Add dots + const finalRange: (number | string)[] = []; + let l: number | null = null; + + for (const i of range) { + if (typeof i === 'number') { + if (l) { + if (i - l === 2) { + finalRange.push(l + 1); + } else if (i - l !== 1) { + finalRange.push('...'); + } + } + finalRange.push(i); + l = i; + } + } + return finalRange; }); watch( () => props.isOpen, isOpen => { if (isOpen) { - projectStore.fetchProjects(); searchQuery.value = ''; // Reset search on open + // Reset to page 1 is handled by fetchProjects default or we can explicitly call it + projectStore.fetchProjects(1, perPage.value, ''); } } ); diff --git a/src/stores/useProjectStore.ts b/src/stores/useProjectStore.ts index bcfa13f..431051e 100644 --- a/src/stores/useProjectStore.ts +++ b/src/stores/useProjectStore.ts @@ -15,13 +15,19 @@ export const useProjectStore = defineStore('project', () => { const projects = ref([]); const currentProject = ref(null); const isLoading = ref(false); + const page = ref(1); + const perPage = ref(12); + const totalItems = ref(0); + const totalPages = ref(0); - async function fetchProjects() { + async function fetchProjects(pageVal = 1, perPageVal = 12, searchVal = '') { if (!authStore.user) return; isLoading.value = true; try { - const records = await authStore.pb.collection('projects').getList(1, 50, { + const filter = searchVal ? `name ~ "${searchVal}"` : ''; + const records = await authStore.pb.collection('projects').getList(pageVal, perPageVal, { sort: '-updated', + filter, }); projects.value = records.items.map((r: any) => ({ id: r.id, @@ -30,6 +36,10 @@ export const useProjectStore = defineStore('project', () => { created: r.created, updated: r.updated, })); + page.value = records.page; + perPage.value = records.perPage; + totalItems.value = records.totalItems; + totalPages.value = records.totalPages; } catch (error) { console.error('Failed to fetch projects:', error); } finally { @@ -53,7 +63,8 @@ export const useProjectStore = defineStore('project', () => { created: record.created, updated: record.updated, }; - await fetchProjects(); + // Refresh current page + await fetchProjects(page.value, perPage.value); } catch (error) { console.error('Failed to create project:', error); throw error; @@ -76,7 +87,9 @@ export const useProjectStore = defineStore('project', () => { data: record.data, updated: record.updated, }; - await fetchProjects(); + // No need to fetch all, just update locals or re-fetch current page if desired. + // For now, let's re-fetch to keep consistency. + await fetchProjects(page.value, perPage.value); } catch (error) { console.error('Failed to update project:', error); throw error; @@ -115,7 +128,9 @@ export const useProjectStore = defineStore('project', () => { if (currentProject.value?.id === id) { currentProject.value = null; } - await fetchProjects(); + // If we delete the last item on a page, we might want to go back a page. + // For simplicity, just refetch current page. + await fetchProjects(page.value, perPage.value); } catch (error) { console.error('Failed to delete project:', error); } finally { @@ -127,6 +142,10 @@ export const useProjectStore = defineStore('project', () => { projects, currentProject, isLoading, + page, + perPage, + totalItems, + totalPages, fetchProjects, createProject, updateProject,