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 19e789e..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 = 20): 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 = 20): 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 { @@ -73,7 +78,8 @@ export async function createNewLOA(data: LOARequest) { export async function closeLOA(id: number, closer: number) { const sql = `UPDATE leave_of_absences SET closed = 1, - closed_by = ? + closed_by = ?, + ended_at = NOW() WHERE leave_of_absences.id = ?`; let out = await pool.query(sql, [closer, id]); console.log(out); diff --git a/shared/types/member.ts b/shared/types/member.ts index 20e0469..c0fbf95 100644 --- a/shared/types/member.ts +++ b/shared/types/member.ts @@ -14,6 +14,7 @@ export enum MemberState { export type Member = { member_id: number; member_name: string; + displayName?: string; rank: string | null; rank_date: string | null; unit: string | null; 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/loaForm.vue b/ui/src/components/loa/loaForm.vue index a7362b8..2bb5d20 100644 --- a/ui/src/components/loa/loaForm.vue +++ b/ui/src/components/loa/loaForm.vue @@ -2,7 +2,8 @@ import { Check, Search } from "lucide-vue-next" import { ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList } from "@/components/ui/combobox" import { computed, onMounted, ref, watch } from "vue"; -import { Member, getMembers } from "@/api/member"; +import { getMembers } from "@/api/member"; +import { Member } from "@shared/types/member"; import Button from "@/components/ui/button/Button.vue"; import { CalendarDate, @@ -70,6 +71,8 @@ const { handleSubmit, values, resetForm } = useForm({ validationSchema: toTypedSchema(loaSchema), }) +const formSubmitted = ref(false); + const onSubmit = handleSubmit(async (values) => { console.log(values); const out: LOARequest = { @@ -85,6 +88,7 @@ const onSubmit = handleSubmit(async (values) => { await submitLOA(out); userStore.loadUser(); } + formSubmitted.value = true; }) onMounted(async () => { @@ -137,7 +141,7 @@ const maxEndDate = computed(() => {
-
+
@@ -272,6 +276,24 @@ const maxEndDate = computed(() => {
+
+

+ LOA Request Submitted +

+ +

+ Your Leave of Absence request has been submitted successfully. + It will take effect on your selected start date. +

+ + +
+ +
+
+
\ No newline at end of file diff --git a/ui/src/components/loa/loaList.vue b/ui/src/components/loa/loaList.vue index 386f392..585d74d 100644 --- a/ui/src/components/loa/loaList.vue +++ b/ui/src/components/loa/loaList.vue @@ -15,7 +15,7 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" -import { Ellipsis } from "lucide-vue-next"; +import { ChevronDown, ChevronUp, Ellipsis, X } from "lucide-vue-next"; import { cancelLOA, extendLOA, getAllLOAs, getMyLOAs } from "@/api/loa"; import { onMounted, ref, computed } from "vue"; import { LOARequest } from "@shared/types/loa"; @@ -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(); @@ -104,6 +109,26 @@ async function commitExtend() { isExtending.value = false; await loadLOAs(); } + +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(); +} 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