overhauled recruiter tools

This commit is contained in:
2025-12-03 15:20:37 -05:00
parent b8f18c060e
commit b79e78c2a6
3 changed files with 106 additions and 42 deletions

View File

@@ -121,7 +121,7 @@ async function handleDeny(id) {
</script> </script>
<template> <template>
<div v-if="!loading" class="max-w-3xl mx-auto"> <div v-if="!loading" class="w-full">
<div v-if="!newApp" class="flex flex-row justify-between items-center py-2 mb-8"> <div v-if="!newApp" class="flex flex-row justify-between items-center py-2 mb-8">
<!-- Application header --> <!-- Application header -->
<div> <div>
@@ -170,5 +170,5 @@ async function handleDeny(id) {
</div> </div>
</div> </div>
<!-- TODO: Implement some kinda loading screen --> <!-- TODO: Implement some kinda loading screen -->
<div v-else>Loading</div> <div v-else class="flex items-center justify-center h-full -mt-40">Loading</div>
</template> </template>

View File

@@ -10,9 +10,10 @@ import {
TableRow, TableRow,
} from '@/components/ui/table' } from '@/components/ui/table'
import Button from '@/components/ui/button/Button.vue'; import Button from '@/components/ui/button/Button.vue';
import { onMounted, ref } from 'vue'; import { onMounted, ref, watch } from 'vue';
import { useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { CheckIcon, XIcon } from 'lucide-vue-next'; import { CheckIcon, XIcon } from 'lucide-vue-next';
import Application from './Application.vue';
const appList = ref([]); const appList = ref([]);
const now = Date.now(); const now = Date.now();
@@ -61,48 +62,111 @@ async function handleDeny(id) {
const router = useRouter(); const router = useRouter();
function openApplication(id) { function openApplication(id) {
router.push(`./application/${id}`) router.push(`/administration/applications/${id}`)
openPanel.value = true;
} }
function closeApplication() {
router.push('/administration/applications')
openPanel.value = false;
}
const route = useRoute();
watch(() => route.params.id, (newId) => {
if (newId === undefined) {
openPanel.value = false;
}
})
const openPanel = ref(false);
onMounted(async () => { onMounted(async () => {
appList.value = await getAllApplications(); appList.value = await getAllApplications();
//preload application
if (route.params.id != undefined) {
openApplication(route.params.id)
}
}) })
</script> </script>
<template> <template>
<div class="mx-auto w-full max-w-5xl"> <div class="px-20 mx-auto max-w-[100rem] w-full flex mt-5">
<h1 class="my-4">Manage Applications</h1> <!-- application list -->
<Table> <div :class="openPanel == false ? 'w-full' : 'w-2/5'" class="pr-9">
<!-- <TableCaption>A list of your recent invoices.</TableCaption> --> <h1 class="scroll-m-20 text-2xl font-semibold tracking-tight">Manage Applications</h1>
<TableHeader> <Table>
<TableRow> <TableHeader>
<TableHead>User</TableHead> <TableRow>
<TableHead>Date Submitted</TableHead> <TableHead>User</TableHead>
<TableHead class="text-right">Status</TableHead> <TableHead>Date Submitted</TableHead>
</TableRow> <TableHead class="text-right">Status</TableHead>
</TableHeader> </TableRow>
<TableBody> </TableHeader>
<TableRow v-for="app in appList" :key="app.id" class="cursor-pointer" <TableBody>
:onClick="() => { openApplication(app.id) }"> <TableRow v-for="app in appList" :key="app.id" class="cursor-pointer"
<TableCell class="font-medium">{{ app.member_name }}</TableCell> :onClick="() => { openApplication(app.id) }">
<TableCell :title="formatExact(app.submitted_at)"> <TableCell class="font-medium">{{ app.member_name }}</TableCell>
{{ formatAgo(app.submitted_at) }} <TableCell :title="formatExact(app.submitted_at)">
</TableCell> {{ formatAgo(app.submitted_at) }}
<TableCell v-if="app.app_status === ApplicationStatus.Pending" class="inline-flex items-end gap-2"> </TableCell>
<Button variant="success" @click.stop="() => { handleApprove(app.id) }"> <TableCell v-if="app.app_status === ApplicationStatus.Pending"
<CheckIcon></CheckIcon> class="inline-flex items-end gap-2">
</Button> <Button variant="success" @click.stop="() => { handleApprove(app.id) }">
<Button variant="destructive" @click.stop="() => { handleDeny(app.id) }"> <CheckIcon></CheckIcon>
<XIcon></XIcon> </Button>
</Button> <Button variant="destructive" @click.stop="() => { handleDeny(app.id) }">
</TableCell> <XIcon></XIcon>
<TableCell class="text-right font-semibold" :class="[ </Button>
, </TableCell>
app.app_status === ApplicationStatus.Pending && 'text-yellow-500', <TableCell class="text-right font-semibold" :class="[
app.app_status === ApplicationStatus.Accepted && 'text-green-500', ,
app.app_status === ApplicationStatus.Denied && 'text-destructive' app.app_status === ApplicationStatus.Pending && 'text-yellow-500',
]">{{ app.app_status }}</TableCell> app.app_status === ApplicationStatus.Accepted && 'text-green-500',
</TableRow> app.app_status === ApplicationStatus.Denied && 'text-destructive'
</TableBody> ]">{{ app.app_status }}</TableCell>
</Table> </TableRow>
</TableBody>
</Table>
</div>
<div v-if="openPanel" class="pl-9 border-l w-3/5" :key="$route.params.id">
<div class="mb-5 flex justify-between">
<p class="scroll-m-20 text-2xl font-semibold tracking-tight"> Application</p>
<button @click="closeApplication()" class="cursor-pointer">
<XIcon></XIcon>
</button>
</div>
<Application :mode="'view-recruiter'"></Application>
</div>
</div> </div>
</template> </template>
<style scoped>
/* Firefox */
.scrollbar-themed {
scrollbar-width: thin;
scrollbar-color: #555 #1f1f1f;
padding-right: 6px;
}
/* Chrome, Edge, Safari */
.scrollbar-themed::-webkit-scrollbar {
width: 10px;
/* slightly wider to allow padding look */
}
.scrollbar-themed::-webkit-scrollbar-track {
background: #1f1f1f;
margin-left: 6px;
/* ❗ adds space between content + scrollbar */
}
.scrollbar-themed::-webkit-scrollbar-thumb {
background: #555;
border-radius: 9999px;
}
.scrollbar-themed::-webkit-scrollbar-thumb:hover {
background: #777;
}
</style>

View File

@@ -29,7 +29,7 @@ const router = createRouter({
meta: { requiresAuth: true, memberOnly: true, roles: ['staff', 'admin'] }, meta: { requiresAuth: true, memberOnly: true, roles: ['staff', 'admin'] },
children: [ children: [
{ path: 'applications', component: () => import('@/pages/ManageApplications.vue') }, { path: 'applications', component: () => import('@/pages/ManageApplications.vue') },
{ path: 'application/:id', component: () => import('@/pages/Application.vue') }, { path: 'applications/:id', component: () => import('@/pages/ManageApplications.vue') },
{ path: 'rankChange', component: () => import('@/pages/RankChange.vue') }, { path: 'rankChange', component: () => import('@/pages/RankChange.vue') },
{ path: 'applications/:id', component: () => import('@/pages/Application.vue') }, { path: 'applications/:id', component: () => import('@/pages/Application.vue') },
{ path: 'transfer', component: () => import('@/pages/ManageTransfers.vue') }, { path: 'transfer', component: () => import('@/pages/ManageTransfers.vue') },