import React, { startTransition, useState } from 'react' import { FormProvider } from 'react-hook-form' import { Progress, Card, Button, Stepper, Box, Flex, Text, Paper, Container, Portal } from "@pikku/mantine/core" import { BasicInfo } from "@/components/jobs/form/BasicInfo" import { TrainingCertificates } from "@/components/jobs/form/TrainingCertificates" import { License } from "@/components/jobs/form/License" import { GermanLanguage } from "@/components/jobs/form/GermanLanguage" import { ProfessionalExperience } from "@/components/jobs/form/ProfessionalExperience" import { ApplicationCandidateData } from '@heygermany/sdk' import { useCandidateForm } from '@/hooks/candidate/useCandidateForm' import { useGetCandidate } from '@/hooks/candidate/useGetCandidate' import { pikku } from '@/pikku/http' import { useQueryClient } from '@tanstack/react-query' import { Done } from '@/components/jobs/form/Done' import { Invalid } from '@/components/jobs/form/Invalid' import { useI18n } from '@/context/i18n-provider' import { ApplicantStatuses } from '@heygermany/sdk' import { CandidateResult } from '@/components/jobs/CandidateResult' import { useGetCandidateResults } from '@/hooks/useGetCandidateResult' import { IconCheck } from '@tabler/icons-react' import { HelpAffix } from '@/components/jobs/form/HelpAffix' import { useLocation } from '@tanstack/react-router' import { asI18n } from '@pikku/react' const Form: React.FunctionComponent<{ candidate: ApplicationCandidateData }> = ({ candidate }) => { const t = useI18n() const steps = [ { title: t('jobs.apply.steps.basicinfo'), component: BasicInfo }, { title: t('jobs.apply.steps.educationdocuments' as any), component: TrainingCertificates }, { title: t('jobs.apply.steps.license'), component: License }, { title: t('jobs.apply.steps.germanlanguage'), component: GermanLanguage }, { title: t('jobs.apply.steps.professionalexperience'), component: ProfessionalExperience }, ] const [currentStep, setCurrentStep] = useState(0) // Single form instance for the entire application const form = useCandidateForm(candidate) const progressPercentage = (currentStep / steps.length) * 100 const queryClient = useQueryClient() // Return a Date (not an ISO string): the pikku fetch client serializes Date // values over the wire, and the /candidate input type expects Date fields. const serializeDate = (value: Date | string | null | undefined) => { if (!value) return null const date = value instanceof Date ? value : new Date(value) return Number.isNaN(date.getTime()) ? null : date } const handleNext = async () => { // Validate current form state before proceeding const isValid = await form.trigger() if (isValid) { // Perform partial update - save current form data const { education, licenses, languageCertificates, workExperience, ...formData } = form.getValues() const currentData = { ...formData, candidateId: candidate.candidateId, dateOfBirth: serializeDate(formData.dateOfBirth), } if (currentStep < steps.length - 1) { startTransition(() => { setCurrentStep((step) => step + 1) }) void pikku().patch('/candidate/:candidateId', currentData).catch((error: unknown) => { console.error('Error saving application data:', error) }) return } try { // Final submission ;(window as any).fbq?.('track', 'Lead', { content_name: 'HeyGermany application', content_category: 'Application' }) await pikku().patch('/candidate/:candidateId', { ...currentData, submittedAt: new Date(), }) } catch (error) { console.error('Error saving application data:', error) } queryClient.invalidateQueries({ queryKey: ['candidate'] }) } } const handleBack = () => { if (currentStep > 0) { setCurrentStep(currentStep - 1) } } const renderStepContent = () => { const currentStepConfig = steps[currentStep] if (!currentStepConfig) return null const StepComponent = currentStepConfig.component return } return ( {/* Mobile Progress Bar */} {t('jobs.apply.stepprogress', { current: currentStep + 1, total: steps.length })} {/* Desktop Stepper */} {steps.map((step, index) => } />)} {/* Render the current step once to avoid duplicate form controls across breakpoints */} {renderStepContent()} {/* Navigation Buttons */} ) } const Result: React.FunctionComponent<{ candidateId: string }> = ({ candidateId }) => { const { data: candidateResults, isLoading: resultLoading, error } = useGetCandidateResults(candidateId) if (resultLoading) { return null } if (error) { return ( {asI18n('Error Loading Qualification Results')} {asI18n('We encountered an error while checking your qualifications. Please try again later or contact support if the issue persists.')} ) } if (!candidateResults) { return null } return } const ApplicationPage: React.FunctionComponent = () => { const searchParams = useLocation({ select: (l) => new URLSearchParams(l.searchStr) }) const candidateIdFromParams = searchParams.get('candidateId') const { data: candidate, isLoading: candidateLoading } = useGetCandidate(candidateIdFromParams || undefined) if (candidateLoading || !candidate) { return null } if (candidate.status === ApplicantStatuses.PENDING) { return } if (candidate.status === ApplicantStatuses.INVALID) { return } if (candidate.status === ApplicantStatuses.COMPLETE) { return } return
} export default ApplicationPage