From 1eef9145a485f8a555845574fcc17f84cffc4631 Mon Sep 17 00:00:00 2001 From: ajdj100 Date: Mon, 15 Dec 2025 20:28:14 -0500 Subject: [PATCH] implemented pagination, header polish, and new display fixes --- api/src/routes/loa.ts | 6 +- api/src/services/loaService.ts | 11 +++- shared/types/pagination.ts | 11 ++++ shared/utils/time.ts | 2 +- ui/src/api/loa.ts | 15 ++++- ui/src/components/loa/loaList.vue | 64 +++++++++++++++++-- .../components/ui/pagination/Pagination.vue | 33 ++++++++++ .../ui/pagination/PaginationContent.vue | 24 +++++++ .../ui/pagination/PaginationEllipsis.vue | 27 ++++++++ .../ui/pagination/PaginationFirst.vue | 36 +++++++++++ .../ui/pagination/PaginationItem.vue | 35 ++++++++++ .../ui/pagination/PaginationLast.vue | 36 +++++++++++ .../ui/pagination/PaginationNext.vue | 36 +++++++++++ .../ui/pagination/PaginationPrevious.vue | 36 +++++++++++ ui/src/components/ui/pagination/index.js | 8 +++ ui/src/pages/ManageLOA.vue | 8 ++- 16 files changed, 370 insertions(+), 18 deletions(-) create mode 100644 shared/types/pagination.ts create mode 100644 ui/src/components/ui/pagination/Pagination.vue create mode 100644 ui/src/components/ui/pagination/PaginationContent.vue create mode 100644 ui/src/components/ui/pagination/PaginationEllipsis.vue create mode 100644 ui/src/components/ui/pagination/PaginationFirst.vue create mode 100644 ui/src/components/ui/pagination/PaginationItem.vue create mode 100644 ui/src/components/ui/pagination/PaginationLast.vue create mode 100644 ui/src/components/ui/pagination/PaginationNext.vue create mode 100644 ui/src/components/ui/pagination/PaginationPrevious.vue create mode 100644 ui/src/components/ui/pagination/index.js diff --git a/api/src/routes/loa.ts b/api/src/routes/loa.ts index f2de3b1..17f198a 100644 --- a/api/src/routes/loa.ts +++ b/api/src/routes/loa.ts @@ -66,9 +66,11 @@ router.get("/history", async (req: Request, res: Response) => { } }) -router.get('/all', [requireRole("17th Administrator")], async (req, res) => { +router.get('/all', [requireRole("17th Administrator")], async (req: Request, res: Response) => { try { - const result = await getAllLOA(); + const page = Number(req.query.page) || undefined; + const pageSize = Number(req.query.pageSize) || undefined; + const result = await getAllLOA(page, pageSize); res.status(200).json(result) } catch (error) { console.error(error); diff --git a/api/src/services/loaService.ts b/api/src/services/loaService.ts index 7792074..fa8cd52 100644 --- a/api/src/services/loaService.ts +++ b/api/src/services/loaService.ts @@ -1,12 +1,13 @@ import { toDateTime } from "@app/shared/utils/time"; import pool from "../db"; import { LOARequest, LOAType } from '@app/shared/types/loa' +import { PagedData } from '@app/shared/types/pagination' export async function getLoaTypes(): Promise { return await pool.query('SELECT * FROM leave_of_absences_types;'); } -export async function getAllLOA(page = 1, pageSize = 10): Promise { +export async function getAllLOA(page = 1, pageSize = 10): Promise> { const offset = (page - 1) * pageSize; const sql = ` @@ -26,9 +27,13 @@ export async function getAllLOA(page = 1, pageSize = 10): Promise loa.start_date DESC LIMIT ? OFFSET ?; `; + let loaList: LOARequest[] = await pool.query(sql, [pageSize, offset]) as LOARequest[]; - let res: LOARequest[] = await pool.query(sql, [pageSize, offset]) as LOARequest[]; - return res; + let loaCount = Number((await pool.query(`SELECT COUNT(*) as count FROM leave_of_absences;`))[0].count); + let pageCount = loaCount / pageSize; + + let output: PagedData = { data: loaList, pagination: { page: page, pageSize: pageSize, total: loaCount, totalPages: pageCount } } + return output; } export async function getUserLOA(userId: number): Promise { diff --git a/shared/types/pagination.ts b/shared/types/pagination.ts new file mode 100644 index 0000000..4e6b563 --- /dev/null +++ b/shared/types/pagination.ts @@ -0,0 +1,11 @@ +export interface PagedData { + data: T[] + pagination: pagination +} + +export interface pagination { + page: number + pageSize: number + total: number + totalPages: number +} \ No newline at end of file diff --git a/shared/utils/time.ts b/shared/utils/time.ts index 322b015..9c5d03c 100644 --- a/shared/utils/time.ts +++ b/shared/utils/time.ts @@ -12,4 +12,4 @@ export function toDateTime(date: Date): string { const second = date.getSeconds().toString().padStart(2, "0"); return `${year}-${month}-${day} ${hour}:${minute}:${second}`; -} \ No newline at end of file +} diff --git a/ui/src/api/loa.ts b/ui/src/api/loa.ts index 5a356a0..bd8802f 100644 --- a/ui/src/api/loa.ts +++ b/ui/src/api/loa.ts @@ -1,4 +1,5 @@ import { LOARequest, LOAType } from '@shared/types/loa' +import { PagedData } from '@shared/types/pagination' // @ts-ignore const addr = import.meta.env.VITE_APIHOST; @@ -58,8 +59,18 @@ export async function getMyLOA(): Promise { } } -export function getAllLOAs(): Promise { - return fetch(`${addr}/loa/all`, { +export async function getAllLOAs(page?: number, pageSize?: number): Promise> { + const params = new URLSearchParams(); + + if (page !== undefined) { + params.set("page", page.toString()); + } + + if (pageSize !== undefined) { + params.set("pageSize", pageSize.toString()); + } + + return fetch(`${addr}/loa/all?${params}`, { method: "GET", headers: { "Content-Type": "application/json", diff --git a/ui/src/components/loa/loaList.vue b/ui/src/components/loa/loaList.vue index edc28c1..585d74d 100644 --- a/ui/src/components/loa/loaList.vue +++ b/ui/src/components/loa/loaList.vue @@ -33,6 +33,15 @@ import { } from "@internationalized/date" import { el } from "@fullcalendar/core/internal-common"; import MemberCard from "../members/MemberCard.vue"; +import { + Pagination, + PaginationContent, + PaginationEllipsis, + PaginationItem, + PaginationNext, + PaginationPrevious, +} from '@/components/ui/pagination' +import { pagination } from "@shared/types/pagination"; const props = defineProps<{ adminMode?: boolean @@ -46,7 +55,9 @@ onMounted(async () => { async function loadLOAs() { if (props.adminMode) { - LOAList.value = await getAllLOAs(); + let result = await getAllLOAs(pageNum.value, pageSize.value); + LOAList.value = result.data; + pageData.value = result.pagination; } else { LOAList.value = await getMyLOAs(); } @@ -76,12 +87,6 @@ function loaStatus(loa: LOARequest): "Upcoming" | "Active" | "Overdue" | "Closed return "Overdue"; // fallback } -function sortByStartDate(loas: LOARequest[]): LOARequest[] { - return [...loas].sort( - (a, b) => new Date(b.start_date).getTime() - new Date(a.start_date).getTime() - ); -} - async function cancelAndReload(id: number) { await cancelLOA(id, props.adminMode); await loadLOAs(); @@ -107,6 +112,23 @@ async function commitExtend() { const expanded = ref(null); const hoverID = ref(null); + +const pageNum = ref(1); +const pageData = ref(); + +const pageSize = ref(15) +const pageSizeOptions = [10, 15, 30] + +function setPageSize(size: number) { + pageSize.value = size + pageNum.value = 1; + loadLOAs(); +} + +function setPage(pagenum: number) { + pageNum.value = pagenum; + loadLOAs(); +} +
+
+ + + + + + + + +
+

Per page:

+ + +
+
diff --git a/ui/src/components/ui/pagination/Pagination.vue b/ui/src/components/ui/pagination/Pagination.vue new file mode 100644 index 0000000..7ac2a36 --- /dev/null +++ b/ui/src/components/ui/pagination/Pagination.vue @@ -0,0 +1,33 @@ + + + diff --git a/ui/src/components/ui/pagination/PaginationContent.vue b/ui/src/components/ui/pagination/PaginationContent.vue new file mode 100644 index 0000000..61f7809 --- /dev/null +++ b/ui/src/components/ui/pagination/PaginationContent.vue @@ -0,0 +1,24 @@ + + + diff --git a/ui/src/components/ui/pagination/PaginationEllipsis.vue b/ui/src/components/ui/pagination/PaginationEllipsis.vue new file mode 100644 index 0000000..48e04a6 --- /dev/null +++ b/ui/src/components/ui/pagination/PaginationEllipsis.vue @@ -0,0 +1,27 @@ + + + diff --git a/ui/src/components/ui/pagination/PaginationFirst.vue b/ui/src/components/ui/pagination/PaginationFirst.vue new file mode 100644 index 0000000..8fe158b --- /dev/null +++ b/ui/src/components/ui/pagination/PaginationFirst.vue @@ -0,0 +1,36 @@ + + + diff --git a/ui/src/components/ui/pagination/PaginationItem.vue b/ui/src/components/ui/pagination/PaginationItem.vue new file mode 100644 index 0000000..7a6b6ef --- /dev/null +++ b/ui/src/components/ui/pagination/PaginationItem.vue @@ -0,0 +1,35 @@ + + + diff --git a/ui/src/components/ui/pagination/PaginationLast.vue b/ui/src/components/ui/pagination/PaginationLast.vue new file mode 100644 index 0000000..7c67efa --- /dev/null +++ b/ui/src/components/ui/pagination/PaginationLast.vue @@ -0,0 +1,36 @@ + + + diff --git a/ui/src/components/ui/pagination/PaginationNext.vue b/ui/src/components/ui/pagination/PaginationNext.vue new file mode 100644 index 0000000..40c1ee6 --- /dev/null +++ b/ui/src/components/ui/pagination/PaginationNext.vue @@ -0,0 +1,36 @@ + + + diff --git a/ui/src/components/ui/pagination/PaginationPrevious.vue b/ui/src/components/ui/pagination/PaginationPrevious.vue new file mode 100644 index 0000000..b682259 --- /dev/null +++ b/ui/src/components/ui/pagination/PaginationPrevious.vue @@ -0,0 +1,36 @@ + + + diff --git a/ui/src/components/ui/pagination/index.js b/ui/src/components/ui/pagination/index.js new file mode 100644 index 0000000..349edff --- /dev/null +++ b/ui/src/components/ui/pagination/index.js @@ -0,0 +1,8 @@ +export { default as Pagination } from "./Pagination.vue"; +export { default as PaginationContent } from "./PaginationContent.vue"; +export { default as PaginationEllipsis } from "./PaginationEllipsis.vue"; +export { default as PaginationFirst } from "./PaginationFirst.vue"; +export { default as PaginationItem } from "./PaginationItem.vue"; +export { default as PaginationLast } from "./PaginationLast.vue"; +export { default as PaginationNext } from "./PaginationNext.vue"; +export { default as PaginationPrevious } from "./PaginationPrevious.vue"; diff --git a/ui/src/pages/ManageLOA.vue b/ui/src/pages/ManageLOA.vue index 1d9298f..37512ac 100644 --- a/ui/src/pages/ManageLOA.vue +++ b/ui/src/pages/ManageLOA.vue @@ -30,10 +30,12 @@ const showLOADialog = ref(false);
-
- +
+

LOA Log

+
+ +
-

LOA Log