From 67afce712f1b10cec108b993345809dabd3887b2 Mon Sep 17 00:00:00 2001 From: Niels Mokkenstorm Date: Mon, 17 Nov 2025 23:55:59 +0100 Subject: [PATCH] feat(client): add application flow and management features --- .../components/ApplicationFlow.tsx | 182 ++++++++++ .../components/ApplicationsOverview.tsx | 222 ++++++++++++ .../components/CVTemplateSelector.tsx | 208 +++++++++++ .../components/MatchIndicator.tsx | 148 ++++++++ .../applications/components/MatchTooltip.tsx | 77 +++++ .../applications/components/VacancyCard.tsx | 126 +++++++ .../components/VacancyFilterPanel.tsx | 325 ++++++++++++++++++ .../components/VacancySelector.tsx | 160 +++++++++ .../components/useVacancyFilters.ts | 59 ++++ .../mutations/create-application.graphql | 36 ++ .../queries/my-applications.graphql | 41 +++ apps/client/src/pages/ApplicationFlowPage.tsx | 5 + apps/client/src/pages/ApplicationsPage.tsx | 5 + 13 files changed, 1594 insertions(+) create mode 100644 apps/client/src/features/applications/components/ApplicationFlow.tsx create mode 100644 apps/client/src/features/applications/components/ApplicationsOverview.tsx create mode 100644 apps/client/src/features/applications/components/CVTemplateSelector.tsx create mode 100644 apps/client/src/features/applications/components/MatchIndicator.tsx create mode 100644 apps/client/src/features/applications/components/MatchTooltip.tsx create mode 100644 apps/client/src/features/applications/components/VacancyCard.tsx create mode 100644 apps/client/src/features/applications/components/VacancyFilterPanel.tsx create mode 100644 apps/client/src/features/applications/components/VacancySelector.tsx create mode 100644 apps/client/src/features/applications/components/useVacancyFilters.ts create mode 100644 apps/client/src/features/applications/mutations/create-application.graphql create mode 100644 apps/client/src/features/applications/queries/my-applications.graphql create mode 100644 apps/client/src/pages/ApplicationFlowPage.tsx create mode 100644 apps/client/src/pages/ApplicationsPage.tsx diff --git a/apps/client/src/features/applications/components/ApplicationFlow.tsx b/apps/client/src/features/applications/components/ApplicationFlow.tsx new file mode 100644 index 0000000..115c767 --- /dev/null +++ b/apps/client/src/features/applications/components/ApplicationFlow.tsx @@ -0,0 +1,182 @@ +import { useQueryClient } from "@tanstack/react-query"; +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { Progress, useProgressContext } from "@/components/Progress"; +import { useCreateApplicationMutation, useCvQuery } from "@/generated/graphql"; +import { CVTemplateSelector } from "./CVTemplateSelector"; +import { VacancySelector } from "./VacancySelector"; + +export const ApplicationFlow = () => { + const navigate = useNavigate(); + const queryClient = useQueryClient(); + const [selectedVacancyId, setSelectedVacancyId] = useState( + null, + ); + const [selectedCVId, setSelectedCVId] = useState(null); + const [coverLetter, setCoverLetter] = useState(""); + + const createApplicationMutation = useCreateApplicationMutation(); + + const handleCancel = () => { + navigate("/dashboard"); + }; + + const handleSubmitApplication = async () => { + if (!(selectedVacancyId && selectedCVId)) { + return; + } + + try { + await createApplicationMutation.mutateAsync({ + input: { + vacancyId: selectedVacancyId, + cvId: selectedCVId, + coverLetter: coverLetter || null, + }, + }); + + // Invalidate and refetch application queries + await queryClient.invalidateQueries({ + queryKey: ["MyApplications"], + }); + + navigate("/applications", { + state: { message: "Application submitted successfully!" }, + }); + } catch (error) { + console.error("Failed to submit application:", error); + // Handle error (show toast, etc.) + } + }; + + return ( +
+
+ + + + + + + + + + + +
+
+ ); +}; + +interface ApplicationReviewProps { + vacancyId: string | null; + cvId: string | null; + coverLetter: string; + onCoverLetterChange: (value: string) => void; + onCancel: () => void; + onSubmit: () => void; + isSubmitting: boolean; +} + +const ApplicationReview = ({ + vacancyId, + cvId, + coverLetter, + onCoverLetterChange, + onCancel, + onSubmit, + isSubmitting, +}: ApplicationReviewProps) => { + const { back } = useProgressContext(); + const { data: cvData, isLoading: cvLoading } = useCvQuery( + { id: cvId ?? "" }, + { enabled: !!cvId }, + ); + + return ( +
+
+

Review Application

+

+ Review your application details before submitting +

+
+ +
+
+

Selected Vacancy

+

Vacancy ID: {vacancyId}

+
+ +
+

Selected CV

+ {cvLoading ? ( +

Loading...

+ ) : cvData?.cv ? ( +

{cvData.cv.title}

+ ) : ( +

CV ID: {cvId}

+ )} +
+ +
+ +