From b8f18c060e0214fac9bcbf1534a549d095e0e69e Mon Sep 17 00:00:00 2001 From: ajdj100 Date: Wed, 3 Dec 2025 13:37:03 -0500 Subject: [PATCH 1/8] Mega recruitment pipeline overhaul --- api/src/routes/applications.ts | 23 ++- ui/src/api/application.ts | 2 +- ui/src/components/ui/stepper/Stepper.vue | 31 ++++ .../ui/stepper/StepperDescription.vue | 25 +++ .../ui/stepper/StepperIndicator.vue | 36 +++++ ui/src/components/ui/stepper/StepperItem.vue | 33 ++++ .../ui/stepper/StepperSeparator.vue | 33 ++++ ui/src/components/ui/stepper/StepperTitle.vue | 24 +++ .../components/ui/stepper/StepperTrigger.vue | 29 ++++ ui/src/components/ui/stepper/index.js | 7 + ui/src/pages/Application.vue | 105 ++++++++---- ui/src/pages/Join.vue | 150 +++++++++++++++--- ui/src/router/index.js | 2 +- 13 files changed, 445 insertions(+), 55 deletions(-) create mode 100644 ui/src/components/ui/stepper/Stepper.vue create mode 100644 ui/src/components/ui/stepper/StepperDescription.vue create mode 100644 ui/src/components/ui/stepper/StepperIndicator.vue create mode 100644 ui/src/components/ui/stepper/StepperItem.vue create mode 100644 ui/src/components/ui/stepper/StepperSeparator.vue create mode 100644 ui/src/components/ui/stepper/StepperTitle.vue create mode 100644 ui/src/components/ui/stepper/StepperTrigger.vue create mode 100644 ui/src/components/ui/stepper/index.js diff --git a/api/src/routes/applications.ts b/api/src/routes/applications.ts index 8b2f61d..76c0eca 100644 --- a/api/src/routes/applications.ts +++ b/api/src/routes/applications.ts @@ -38,12 +38,27 @@ router.get('/all', async (req, res) => { }); router.get('/me', async (req, res) => { + let userID = req.user.id; - console.log("application/me") + try { + let application = await getMemberApplication(userID); - let app = getMemberApplication(userID); - console.log(app); + if (application === undefined) + res.sendStatus(204); + + const comments: CommentRow[] = await getApplicationComments(application.id); + + const output: ApplicationFull = { + application, + comments, + } + + return res.status(200).json(output); + } catch (error) { + console.error('Failed to load application:', error); + return res.status(500).json(error); + } }) // GET /application/:id @@ -54,7 +69,7 @@ router.get('/:id', async (req, res) => { const application = await getApplicationByID(appID); if (application === undefined) return res.sendStatus(204); - + const comments: CommentRow[] = await getApplicationComments(appID); const output: ApplicationFull = { diff --git a/ui/src/api/application.ts b/ui/src/api/application.ts index 7ef6086..85a3dc5 100644 --- a/ui/src/api/application.ts +++ b/ui/src/api/application.ts @@ -74,7 +74,7 @@ export enum ApplicationStatus { const addr = import.meta.env.VITE_APIHOST; export async function loadApplication(id: number | string): Promise { - const res = await fetch(`${addr}/application/${id}`) + const res = await fetch(`${addr}/application/${id}`, { credentials: 'include' }) if (res.status === 204) return null if (!res.ok) throw new Error('Failed to load application') const json = await res.json() diff --git a/ui/src/components/ui/stepper/Stepper.vue b/ui/src/components/ui/stepper/Stepper.vue new file mode 100644 index 0000000..2232560 --- /dev/null +++ b/ui/src/components/ui/stepper/Stepper.vue @@ -0,0 +1,31 @@ + + + diff --git a/ui/src/components/ui/stepper/StepperDescription.vue b/ui/src/components/ui/stepper/StepperDescription.vue new file mode 100644 index 0000000..7445252 --- /dev/null +++ b/ui/src/components/ui/stepper/StepperDescription.vue @@ -0,0 +1,25 @@ + + + diff --git a/ui/src/components/ui/stepper/StepperIndicator.vue b/ui/src/components/ui/stepper/StepperIndicator.vue new file mode 100644 index 0000000..64be9d7 --- /dev/null +++ b/ui/src/components/ui/stepper/StepperIndicator.vue @@ -0,0 +1,36 @@ + + + diff --git a/ui/src/components/ui/stepper/StepperItem.vue b/ui/src/components/ui/stepper/StepperItem.vue new file mode 100644 index 0000000..6c5a9f2 --- /dev/null +++ b/ui/src/components/ui/stepper/StepperItem.vue @@ -0,0 +1,33 @@ + + + diff --git a/ui/src/components/ui/stepper/StepperSeparator.vue b/ui/src/components/ui/stepper/StepperSeparator.vue new file mode 100644 index 0000000..8ed502d --- /dev/null +++ b/ui/src/components/ui/stepper/StepperSeparator.vue @@ -0,0 +1,33 @@ + + + diff --git a/ui/src/components/ui/stepper/StepperTitle.vue b/ui/src/components/ui/stepper/StepperTitle.vue new file mode 100644 index 0000000..d8a81ac --- /dev/null +++ b/ui/src/components/ui/stepper/StepperTitle.vue @@ -0,0 +1,24 @@ + + + diff --git a/ui/src/components/ui/stepper/StepperTrigger.vue b/ui/src/components/ui/stepper/StepperTrigger.vue new file mode 100644 index 0000000..f252535 --- /dev/null +++ b/ui/src/components/ui/stepper/StepperTrigger.vue @@ -0,0 +1,29 @@ + + + diff --git a/ui/src/components/ui/stepper/index.js b/ui/src/components/ui/stepper/index.js new file mode 100644 index 0000000..62f0e03 --- /dev/null +++ b/ui/src/components/ui/stepper/index.js @@ -0,0 +1,7 @@ +export { default as Stepper } from "./Stepper.vue"; +export { default as StepperDescription } from "./StepperDescription.vue"; +export { default as StepperIndicator } from "./StepperIndicator.vue"; +export { default as StepperItem } from "./StepperItem.vue"; +export { default as StepperSeparator } from "./StepperSeparator.vue"; +export { default as StepperTitle } from "./StepperTitle.vue"; +export { default as StepperTrigger } from "./StepperTrigger.vue"; diff --git a/ui/src/pages/Application.vue b/ui/src/pages/Application.vue index 2558340..3670654 100644 --- a/ui/src/pages/Application.vue +++ b/ui/src/pages/Application.vue @@ -17,48 +17,95 @@ const decisionDate = ref(null); const submitDate = ref(null); const loading = ref(true); const member_name = ref(); + +const props = defineProps<{ + mode?: "create" | "view-self" | "view-recruiter" +}>() + +const finalMode = ref<"create" | "view-self" | "view-recruiter">("create"); + +async function loadByID(id: number | string) { + const raw = await loadApplication(id); + + const data = raw.application; + + appID.value = data.id; + appData.value = data.app_data; + chatData.value = raw.comments; + status.value = data.app_status; + decisionDate.value = new Date(data.decision_at); + submitDate.value = data.submitted_at ? new Date(data.submitted_at) : null; + member_name.value = data.member_name; + newApp.value = false; + readOnly.value = true; +} + +const router = useRoute(); + onMounted(async () => { - try { - //get app ID from URL param - const router = useRoute(); - const appIDRaw = router.params.id; - if (appIDRaw === undefined) { - //new app - appData.value = null - readOnly.value = false; - newApp.value = true; - } else { - //load app - const raw = await loadApplication(appIDRaw.toString()); - const data = raw.application; - - appID.value = data.id; - appData.value = data.app_data; - chatData.value = raw.comments; - status.value = data.app_status; - decisionDate.value = new Date(data.decision_at); - submitDate.value = data.submitted_at ? new Date(data.submitted_at) : null; - member_name.value = data.member_name; - newApp.value = false; - readOnly.value = true; - } - } catch (e) { - console.error(e); + //recruiter mode + if (props.mode === 'view-recruiter') { + finalMode.value = 'view-recruiter'; + await loadByID(Number(router.params.id)); } + + //viewer mode + if (props.mode === 'view-self') { + finalMode.value = 'view-self'; + await loadByID('me'); + } + + //creator mode + if (props.mode === 'create') { + finalMode.value = 'create'; + appData.value = null + readOnly.value = false; + newApp.value = true; + } + loading.value = false; + + // try { + // //get app ID from URL param + // if (appIDRaw === undefined) { + // //new app + // appData.value = null + // readOnly.value = false; + // newApp.value = true; + // } else { + // //load app + // const raw = await loadApplication(appIDRaw.toString()); + + // const data = raw.application; + + // appID.value = data.id; + // appData.value = data.app_data; + // chatData.value = raw.comments; + // status.value = data.app_status; + // decisionDate.value = new Date(data.decision_at); + // submitDate.value = data.submitted_at ? new Date(data.submitted_at) : null; + // member_name.value = data.member_name; + // newApp.value = false; + // readOnly.value = true; + // } + // } catch (e) { + // console.error(e); + // } }) async function postComment(comment) { chatData.value.push(await postChatMessage(comment, appID.value)); } +const emit = defineEmits(['submit']); + async function postApp(appData) { - console.log("test") const res = await postApplication(appData); if (res.ok) { readOnly.value = true; newApp.value = false; + emit('submit'); } // TODO: Handle fail to post } @@ -74,7 +121,7 @@ async function handleDeny(id) {