@recruitnepal/shared-packages 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +0 -0
  2. package/dist/api-client.d.ts +22 -0
  3. package/dist/api-client.d.ts.map +1 -1
  4. package/dist/api-client.js +22 -0
  5. package/dist/components/JobDescriptionClient.d.ts +59 -0
  6. package/dist/components/JobDescriptionClient.d.ts.map +1 -0
  7. package/dist/components/JobDescriptionClient.js +260 -0
  8. package/dist/hooks/useApplicantList.d.ts +3 -0
  9. package/dist/hooks/useApplicantList.d.ts.map +1 -0
  10. package/dist/hooks/useApplicantList.js +35 -0
  11. package/dist/hooks/useApplicantMutations.d.ts +18 -0
  12. package/dist/hooks/useApplicantMutations.d.ts.map +1 -0
  13. package/dist/hooks/useApplicantMutations.js +100 -0
  14. package/dist/hooks/useEasyApplyFormHook.d.ts +180 -0
  15. package/dist/hooks/useEasyApplyFormHook.d.ts.map +1 -0
  16. package/dist/hooks/useEasyApplyFormHook.js +174 -0
  17. package/dist/hooks/useEasyApplyMutations.d.ts +13 -0
  18. package/dist/hooks/useEasyApplyMutations.d.ts.map +1 -0
  19. package/dist/hooks/useEasyApplyMutations.js +59 -0
  20. package/dist/hooks/useSavedVacancyList.d.ts +3 -0
  21. package/dist/hooks/useSavedVacancyList.d.ts.map +1 -0
  22. package/dist/hooks/useSavedVacancyList.js +35 -0
  23. package/dist/hooks/useSavedVacancyMutations.d.ts +9 -0
  24. package/dist/hooks/useSavedVacancyMutations.d.ts.map +1 -0
  25. package/dist/hooks/useSavedVacancyMutations.js +49 -0
  26. package/dist/hooks/useShareJob.d.ts +14 -0
  27. package/dist/hooks/useShareJob.d.ts.map +1 -0
  28. package/dist/hooks/useShareJob.js +44 -0
  29. package/dist/hooks/useVacancyQuestions.d.ts +5 -0
  30. package/dist/hooks/useVacancyQuestions.d.ts.map +1 -0
  31. package/dist/hooks/useVacancyQuestions.js +26 -0
  32. package/dist/index.d.ts +16 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +9 -0
  35. package/dist/types/applicant.d.ts +37 -0
  36. package/dist/types/applicant.d.ts.map +1 -0
  37. package/dist/types/applicant.js +1 -0
  38. package/dist/types/easy-apply.d.ts +36 -0
  39. package/dist/types/easy-apply.d.ts.map +1 -0
  40. package/dist/types/easy-apply.js +1 -0
  41. package/dist/types/questions.d.ts +28 -0
  42. package/dist/types/questions.d.ts.map +1 -0
  43. package/dist/types/questions.js +1 -0
  44. package/dist/types/saved-vacancy.d.ts +22 -0
  45. package/dist/types/saved-vacancy.d.ts.map +1 -0
  46. package/dist/types/saved-vacancy.js +1 -0
  47. package/dist/types.d.ts +2 -0
  48. package/dist/types.d.ts.map +1 -1
  49. package/package.json +37 -39
package/README.md CHANGED
Binary file
@@ -8,5 +8,27 @@ export declare const API: {
8
8
  byId: (id: string) => string;
9
9
  intern: () => string;
10
10
  };
11
+ savedVacancies: {
12
+ list: (page?: number) => string;
13
+ create: () => string;
14
+ delete: (id: string) => string;
15
+ };
16
+ applicants: {
17
+ list: (page?: number) => string;
18
+ create: () => string;
19
+ update: (id: string) => string;
20
+ delete: (id: string) => string;
21
+ batch: () => string;
22
+ };
23
+ questions: {
24
+ byVacancyId: (vacancyId: string) => string;
25
+ };
26
+ easyApplicants: {
27
+ create: () => string;
28
+ };
29
+ applicantAnswers: {
30
+ byApplicantId: (applicantId: string) => string;
31
+ byPersonalDetailId: (personalDetailId: string) => string;
32
+ };
11
33
  };
12
34
  //# sourceMappingURL=api-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAEA,wBAAgB,OAAO,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,QAElD;AAED,wBAAgB,UAAU,WAKzB;AAED,eAAO,MAAM,GAAG;;;mBAGD,MAAM;;;CAGpB,CAAC"}
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAEA,wBAAgB,OAAO,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,QAElD;AAED,wBAAgB,UAAU,WAKzB;AAED,eAAO,MAAM,GAAG;;;mBAGD,MAAM;;;;sBAIH,MAAM;;qBAEP,MAAM;;;sBAGL,MAAM;;qBAEP,MAAM;qBACN,MAAM;;;;iCAIM,MAAM;;;;;;qCAMF,MAAM;+CACI,MAAM;;CAEhD,CAAC"}
@@ -14,4 +14,26 @@ export const API = {
14
14
  byId: (id) => `${getBaseURL()}/vacancies/${id}`,
15
15
  intern: () => `${getBaseURL()}/intern/vacancies`,
16
16
  },
17
+ savedVacancies: {
18
+ list: (page) => `${getBaseURL()}/saved/vacancies${page ? `?page=${page}` : ''}`,
19
+ create: () => `${getBaseURL()}/saved/vacancies`,
20
+ delete: (id) => `${getBaseURL()}/saved/vacancies/${id}`,
21
+ },
22
+ applicants: {
23
+ list: (page) => `${getBaseURL()}/applicants${page ? `?page=${page}` : ''}`,
24
+ create: () => `${getBaseURL()}/applicants/`,
25
+ update: (id) => `${getBaseURL()}/applicants/${id}`,
26
+ delete: (id) => `${getBaseURL()}/applicants/${id}`,
27
+ batch: () => `${getBaseURL()}/applicants/multiple`,
28
+ },
29
+ questions: {
30
+ byVacancyId: (vacancyId) => `${getBaseURL()}/application/questions/?vacancyId=${vacancyId}`,
31
+ },
32
+ easyApplicants: {
33
+ create: () => `${getBaseURL()}/easy/applicants`,
34
+ },
35
+ applicantAnswers: {
36
+ byApplicantId: (applicantId) => `${getBaseURL()}/applicant/answers?applicantId=${applicantId}`,
37
+ byPersonalDetailId: (personalDetailId) => `${getBaseURL()}/applicant/answers?personalDetailId=${personalDetailId}`,
38
+ },
17
39
  };
@@ -0,0 +1,59 @@
1
+ import type { SingleVacancyRes } from '../types';
2
+ export type JobDescriptionClientProps = {
3
+ job: SingleVacancyRes;
4
+ slug: string;
5
+ token?: string;
6
+ sessionStatus?: 'authenticated' | 'unauthenticated' | 'loading';
7
+ personalDetailId?: string;
8
+ imageBaseURL?: string;
9
+ /** Custom components - if not provided, will use basic HTML */
10
+ Button?: React.ComponentType<any>;
11
+ Badge?: React.ComponentType<any>;
12
+ Dialog?: React.ComponentType<any>;
13
+ DialogContent?: React.ComponentType<any>;
14
+ DialogHeader?: React.ComponentType<any>;
15
+ DialogTitle?: React.ComponentType<any>;
16
+ DialogDescription?: React.ComponentType<any>;
17
+ DialogFooter?: React.ComponentType<any>;
18
+ Sheet?: React.ComponentType<any>;
19
+ SheetContent?: React.ComponentType<any>;
20
+ SheetDescription?: React.ComponentType<any>;
21
+ Image?: React.ComponentType<any>;
22
+ Link?: React.ComponentType<any>;
23
+ LoadingButton?: React.ComponentType<any>;
24
+ /** Custom panels/components */
25
+ ScreeningQuestionsPanel?: React.ComponentType<any>;
26
+ CVSelectionPanel?: React.ComponentType<any>;
27
+ EasyApply?: React.ComponentType<any>;
28
+ /** Additional content */
29
+ randomTraining?: {
30
+ id: string;
31
+ title: string;
32
+ description: string;
33
+ imageUrl: string;
34
+ } | null;
35
+ randomDlyticaCourse?: {
36
+ id: string;
37
+ title: string;
38
+ description: string;
39
+ imageUrl: string;
40
+ };
41
+ /** Callbacks */
42
+ onApply?: () => void;
43
+ onSave?: () => void;
44
+ onShare?: (method: 'copy' | 'whatsapp' | 'viber' | 'messenger') => void;
45
+ onLoginRequired?: (action: 'apply' | 'save') => void;
46
+ /** Utility functions */
47
+ capitalize?: (str: string) => string;
48
+ sanitizeHTML?: (html: string) => string;
49
+ extractPlainText?: (html: string) => string;
50
+ /** Share icons */
51
+ shareIcons?: {
52
+ whatsapp: string;
53
+ viber: string;
54
+ messenger: string;
55
+ chain: string;
56
+ };
57
+ };
58
+ export default function JobDescriptionClient({ job, slug, token, sessionStatus, personalDetailId, imageBaseURL, Button, Badge, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, Sheet, SheetContent, SheetDescription, Image, Link, LoadingButton, ScreeningQuestionsPanel, CVSelectionPanel, EasyApply, randomTraining, randomDlyticaCourse, onApply, onSave, onShare, onLoginRequired, capitalize, sanitizeHTML, extractPlainText, shareIcons, }: JobDescriptionClientProps): import("react/jsx-runtime").JSX.Element;
59
+ //# sourceMappingURL=JobDescriptionClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JobDescriptionClient.d.ts","sourceRoot":"","sources":["../../src/components/JobDescriptionClient.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAQjD,MAAM,MAAM,yBAAyB,GAAG;IACtC,GAAG,EAAE,gBAAgB,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,eAAe,GAAG,iBAAiB,GAAG,SAAS,CAAC;IAChE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IAClC,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACzC,YAAY,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACxC,WAAW,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACvC,iBAAiB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IAC7C,YAAY,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACxC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACxC,gBAAgB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IAC5C,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IAChC,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACzC,+BAA+B;IAC/B,uBAAuB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACnD,gBAAgB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACrC,yBAAyB;IACzB,cAAc,CAAC,EAAE;QACf,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI,CAAC;IACT,mBAAmB,CAAC,EAAE;QACpB,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,WAAW,KAAK,IAAI,CAAC;IACxE,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IACrD,wBAAwB;IACxB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,kBAAkB;IAClB,UAAU,CAAC,EAAE;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH,CAAC;AAsBF,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAC3C,GAAG,EACH,IAAI,EACJ,KAAK,EACL,aAAiC,EACjC,gBAAgB,EAChB,YAAiB,EACjB,MAAM,EACN,KAAK,EACL,MAAM,EACN,aAAa,EACb,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,KAAK,EACL,YAAY,EACZ,gBAAgB,EAChB,KAAK,EACL,IAAI,EACJ,aAAa,EACb,uBAAuB,EACvB,gBAAgB,EAChB,SAAS,EACT,cAAc,EACd,mBAAmB,EACnB,OAAO,EACP,MAAM,EACN,OAAO,EACP,eAAe,EACf,UAAiC,EACjC,YAAqC,EACrC,gBAAiE,EACjE,UAAU,GACX,EAAE,yBAAyB,2CAgd3B"}
@@ -0,0 +1,260 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState, useEffect } from 'react';
4
+ import { useRouter, useSearchParams } from 'next/navigation';
5
+ import { useSavedVacancyMutations } from '../hooks/useSavedVacancyMutations';
6
+ import { useSavedVacancyList } from '../hooks/useSavedVacancyList';
7
+ import { useApplicantMutations } from '../hooks/useApplicantMutations';
8
+ import { useApplicantList } from '../hooks/useApplicantList';
9
+ import { useVacancyQuestions } from '../hooks/useVacancyQuestions';
10
+ import { useShareJob } from '../hooks/useShareJob';
11
+ // Helper functions
12
+ const formatJobTypes = (types) => Array.isArray(types) && types.length
13
+ ? types
14
+ .map((t) => t.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()))
15
+ .join(', ')
16
+ : 'Not Specified';
17
+ const getSalaryText = (job) => {
18
+ if (job.offered_salary && typeof job.offered_salary.min === 'number') {
19
+ const { min, max } = job.offered_salary;
20
+ return `NPR ${min.toLocaleString()} - ${max.toLocaleString()}${job.salary_type ? ` (${job.salary_type})` : ''}`;
21
+ }
22
+ return job.salary_type || 'Not Disclosed';
23
+ };
24
+ export default function JobDescriptionClient({ job, slug, token, sessionStatus = 'unauthenticated', personalDetailId, imageBaseURL = '', Button, Badge, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, Sheet, SheetContent, SheetDescription, Image, Link, LoadingButton, ScreeningQuestionsPanel, CVSelectionPanel, EasyApply, randomTraining, randomDlyticaCourse, onApply, onSave, onShare, onLoginRequired, capitalize = (str) => str, sanitizeHTML = (html) => html, extractPlainText = (html) => html.replace(/<[^>]*>/g, ''), shareIcons, }) {
25
+ const router = useRouter();
26
+ const searchParams = useSearchParams();
27
+ const [openJobOverview, setOpenJobOverview] = useState(false);
28
+ const [openQuestions, setOpenQuestions] = useState(false);
29
+ const [isEasyApplyOpen, setIsEasyApplyOpen] = useState(false);
30
+ const [authDialogOpen, setAuthDialogOpen] = useState(false);
31
+ const [pendingAction, setPendingAction] = useState(null);
32
+ const [showCVSelection, setShowCVSelection] = useState(false);
33
+ const [selectedCV, setSelectedCV] = useState(null);
34
+ const { data: savedVacancy } = useSavedVacancyList(token);
35
+ const { createSavedVacancy, deleteSavedVacancy } = useSavedVacancyMutations(token);
36
+ const { applyToVacancy } = useApplicantMutations(token);
37
+ const { data: applicantList } = useApplicantList(token, sessionStatus === 'authenticated');
38
+ const { data: screeningQuestions } = useVacancyQuestions(job.id, token);
39
+ const { handleCopy, handleWhatsAppShare, handleViberShare, handleMessengerShare, } = useShareJob(`/jobs/${slug}`);
40
+ useEffect(() => {
41
+ const qp = searchParams?.get('open');
42
+ const hash = typeof window !== 'undefined' ? window.location.hash : '';
43
+ const shouldOpenEasy = qp === 'apply' || hash.toLowerCase() === '#apply';
44
+ if (shouldOpenEasy) {
45
+ if (sessionStatus !== 'authenticated') {
46
+ setIsEasyApplyOpen(true);
47
+ }
48
+ else {
49
+ setIsEasyApplyOpen(false);
50
+ setOpenQuestions(true);
51
+ }
52
+ }
53
+ }, [searchParams, sessionStatus]);
54
+ const canApplyToVacancy = () => {
55
+ if (!job)
56
+ return { canApply: false, reason: 'Invalid vacancy' };
57
+ if (job.deadline_date) {
58
+ const deadline = new Date(job.deadline_date);
59
+ const today = new Date();
60
+ today.setHours(0, 0, 0, 0);
61
+ deadline.setHours(0, 0, 0, 0);
62
+ if (deadline < today) {
63
+ return {
64
+ canApply: false,
65
+ reason: 'Application deadline has passed. You cannot apply to this vacancy anymore.',
66
+ };
67
+ }
68
+ }
69
+ return { canApply: true };
70
+ };
71
+ const isSaved = !!job &&
72
+ savedVacancy?.some((v) => String(v.vacancy_id) === String(job.id));
73
+ const isApplied = !!job &&
74
+ applicantList?.some((a) => a.vacancy_id === job.id && a.personal_detail_id === personalDetailId);
75
+ const applyValidation = canApplyToVacancy();
76
+ const canApply = applyValidation.canApply;
77
+ const handleBookmarkClick = () => {
78
+ if (!job)
79
+ return;
80
+ if (sessionStatus !== 'authenticated') {
81
+ setPendingAction('save');
82
+ setAuthDialogOpen(true);
83
+ onLoginRequired?.('save');
84
+ return;
85
+ }
86
+ const matched = savedVacancy?.find((v) => String(v.vacancy_id) === String(job.id));
87
+ if (matched) {
88
+ deleteSavedVacancy.mutate({ vacancy_id: matched.id });
89
+ }
90
+ else {
91
+ createSavedVacancy.mutate({ vacancy_id: job.id });
92
+ }
93
+ onSave?.();
94
+ };
95
+ const onApplyJobClick = () => {
96
+ if (!job)
97
+ return;
98
+ const validation = canApplyToVacancy();
99
+ if (!validation.canApply) {
100
+ return;
101
+ }
102
+ if (sessionStatus !== 'authenticated') {
103
+ setPendingAction('apply');
104
+ setAuthDialogOpen(true);
105
+ onLoginRequired?.('apply');
106
+ return;
107
+ }
108
+ if (!personalDetailId) {
109
+ return;
110
+ }
111
+ const hasQuestions = screeningQuestions?.data &&
112
+ Array.isArray(screeningQuestions.data) &&
113
+ screeningQuestions.data.length > 0;
114
+ if (hasQuestions) {
115
+ setOpenQuestions(true);
116
+ return;
117
+ }
118
+ setShowCVSelection(true);
119
+ };
120
+ const handleApplyWithCV = () => {
121
+ if (!job)
122
+ return;
123
+ const validation = canApplyToVacancy();
124
+ if (!validation.canApply) {
125
+ setShowCVSelection(false);
126
+ setSelectedCV(null);
127
+ return;
128
+ }
129
+ if (!personalDetailId) {
130
+ return;
131
+ }
132
+ const payload = {
133
+ personal_detail_id: personalDetailId,
134
+ vacancy_id: job.id,
135
+ };
136
+ if (selectedCV) {
137
+ payload.cv_id = selectedCV.id;
138
+ payload.cv_type = selectedCV.type;
139
+ }
140
+ applyToVacancy.mutate(payload);
141
+ setShowCVSelection(false);
142
+ setSelectedCV(null);
143
+ onApply?.();
144
+ };
145
+ const handleIsEasyApplyOpen = () => setIsEasyApplyOpen((s) => !s);
146
+ const handleAuthRequiredAction = (t) => {
147
+ setPendingAction(t);
148
+ setAuthDialogOpen(true);
149
+ onLoginRequired?.(t);
150
+ };
151
+ const confirmLoginRedirect = () => {
152
+ if (pendingAction) {
153
+ setAuthDialogOpen(false);
154
+ router.push('/login');
155
+ }
156
+ };
157
+ const companyLogoSrc = job.company_logo
158
+ ? job.company_logo.startsWith('http')
159
+ ? job.company_logo
160
+ : `${imageBaseURL}/${job.company_logo}`
161
+ : null;
162
+ // Bridge object for ScreeningQuestionsPanel
163
+ const selectedVacancyForQuestions = {
164
+ companies: {
165
+ id: job.company_id,
166
+ organization_name: job.organization_name,
167
+ company_logo: job.company_logo,
168
+ },
169
+ vacancies: {
170
+ id: job.id,
171
+ slug: job.slug,
172
+ job_title: job.title,
173
+ location: job.location,
174
+ employment_type: job.employment_type,
175
+ expected_salary: job.offered_salary
176
+ ? [
177
+ { value: job.offered_salary.min, inclusive: true },
178
+ { value: job.offered_salary.max, inclusive: true },
179
+ ]
180
+ : [],
181
+ skills_required: job.skills_required,
182
+ description: job.description,
183
+ key_responsibilities: job.key_responsibilities,
184
+ education_level: job.education_level,
185
+ },
186
+ };
187
+ // Use provided share handlers or default ones
188
+ const shareHandlers = {
189
+ copy: () => {
190
+ handleCopy();
191
+ onShare?.('copy');
192
+ },
193
+ whatsapp: () => {
194
+ handleWhatsAppShare();
195
+ onShare?.('whatsapp');
196
+ },
197
+ viber: () => {
198
+ handleViberShare();
199
+ onShare?.('viber');
200
+ },
201
+ messenger: () => {
202
+ handleMessengerShare();
203
+ onShare?.('messenger');
204
+ },
205
+ };
206
+ // Basic fallback components
207
+ const ButtonComponent = (Button || 'button');
208
+ const BadgeComponent = (Badge || 'span');
209
+ const ImageComponent = (Image || 'img');
210
+ const LinkComponent = (Link || 'a');
211
+ // Helper to create button props conditionally
212
+ const getButtonProps = (props) => {
213
+ if (Button) {
214
+ return props;
215
+ }
216
+ // For native button, remove custom props
217
+ const { size, variant, ...nativeProps } = props;
218
+ return nativeProps;
219
+ };
220
+ return (_jsxs("div", { className: "container", children: [_jsx("div", { className: "md:flex justify-between items-center rounded-2xl p-8 bg-primary/5 mb-8 relative", children: _jsxs("div", { className: "flex justify-between w-full", children: [_jsx("div", { children: _jsxs("div", { className: "flex gap-4 mb-2", children: [companyLogoSrc && ImageComponent && (_jsx(ImageComponent, { src: job.hide_company_name
221
+ ? '/assets/leading-company.png'
222
+ : companyLogoSrc, alt: "company-logo", width: 64, height: 64, className: "object-contain h-16 w-16 p-0.5 rounded-xl" })), _jsxs("div", { children: [_jsx("h2", { className: "text-base md:text-2xl", children: job.title }), _jsx("p", { className: "text-sm text-text-secondary", children: job.organization_name })] })] }) }), _jsx("div", { className: "flex flex-col items-end gap-2", children: _jsx("div", { className: "flex gap-3", children: isApplied ? (_jsx(ButtonComponent, { ...getButtonProps({
223
+ size: 'sm',
224
+ variant: 'secondary',
225
+ disabled: true,
226
+ className: 'hidden md:flex px-10',
227
+ }), children: "Applied" })) : sessionStatus === 'authenticated' ? (LoadingButton ? (_jsx(LoadingButton, { size: "sm", loading: applyToVacancy.isPending, onClick: onApplyJobClick, className: "hidden md:flex px-10", disabled: !canApply, children: "Apply Now" })) : (_jsx(ButtonComponent, { ...getButtonProps({
228
+ size: 'sm',
229
+ onClick: onApplyJobClick,
230
+ className: 'hidden md:flex px-10',
231
+ disabled: !canApply || applyToVacancy.isPending,
232
+ }), children: applyToVacancy.isPending ? 'Applying...' : 'Apply Now' }))) : (_jsxs(_Fragment, { children: [_jsx(ButtonComponent, { ...getButtonProps({
233
+ size: 'sm',
234
+ variant: 'secondary',
235
+ className: 'hidden md:flex px-10',
236
+ onClick: handleIsEasyApplyOpen,
237
+ disabled: !canApply,
238
+ }), children: "Easy Apply" }), _jsx(ButtonComponent, { ...getButtonProps({
239
+ size: 'sm',
240
+ className: 'hidden md:flex px-10',
241
+ onClick: () => handleAuthRequiredAction('apply'),
242
+ disabled: !canApply,
243
+ }), children: "Apply Now" })] })) }) })] }) }), _jsx("div", { className: "grid md:grid-cols-3 gap-4", children: _jsxs("div", { className: "md:col-span-2 space-y-8", children: [_jsxs("section", { children: [_jsx("p", { className: "text-lg font-medium", children: "Job Description" }), extractPlainText(job.description) ? (_jsx("div", { className: "text-text-secondary prose prose-sm max-w-none", dangerouslySetInnerHTML: {
244
+ __html: sanitizeHTML(job.description),
245
+ } })) : (_jsx("p", { className: "text-text-secondary", children: "No description provided." }))] }), _jsxs("section", { children: [_jsx("p", { className: "text-lg font-medium", children: "Key Responsibilities" }), extractPlainText(job.key_responsibilities) ? (_jsx("div", { className: "text-text-secondary prose prose-sm max-w-none", dangerouslySetInnerHTML: {
246
+ __html: sanitizeHTML(job.key_responsibilities),
247
+ } })) : (_jsx("p", { className: "text-text-secondary", children: "No responsibilities listed." }))] }), _jsxs("section", { children: [_jsx("p", { className: "text-lg font-medium", children: "Skills" }), _jsx("ul", { className: "list-disc list-inside space-y-2 text-text-secondary", children: job.skills_required?.map((skill, index) => (_jsx("li", { children: skill }, index))) })] })] }) }), ScreeningQuestionsPanel && (_jsx(ScreeningQuestionsPanel, { isOpen: openQuestions, setOpen: setOpenQuestions, selectedVacancy: selectedVacancyForQuestions, questions: screeningQuestions?.data || [] })), EasyApply && (_jsx(EasyApply, { isSheetOpen: isEasyApplyOpen, setSheetOpen: setIsEasyApplyOpen, selectedVacancy: job, mode: "advanced" })), Dialog &&
248
+ DialogContent &&
249
+ DialogHeader &&
250
+ DialogTitle &&
251
+ DialogDescription &&
252
+ DialogFooter &&
253
+ CVSelectionPanel && (_jsx(Dialog, { open: showCVSelection, onOpenChange: setShowCVSelection, children: _jsxs(DialogContent, { className: "max-w-2xl max-h-[90vh] overflow-hidden flex flex-col", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Select CV for Application" }), _jsxs(DialogDescription, { children: ["Choose which CV to send with your application for", ' ', _jsx("span", { className: "font-semibold", children: job?.title })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto pr-4", children: _jsx(CVSelectionPanel, { selectedCV: selectedCV, onCVSelect: setSelectedCV }) }), _jsxs(DialogFooter, { children: [_jsx(ButtonComponent, { ...getButtonProps({
254
+ variant: 'outline',
255
+ onClick: () => {
256
+ setShowCVSelection(false);
257
+ setSelectedCV(null);
258
+ },
259
+ }), children: "Cancel" }), LoadingButton ? (_jsx(LoadingButton, { loading: applyToVacancy.isPending, onClick: handleApplyWithCV, children: "Apply Now" })) : (_jsx(ButtonComponent, { onClick: handleApplyWithCV, disabled: applyToVacancy.isPending, children: applyToVacancy.isPending ? 'Applying...' : 'Apply Now' }))] })] }) }))] }));
260
+ }
@@ -0,0 +1,3 @@
1
+ import type { ApplicantItem } from '../types/applicant';
2
+ export declare const useApplicantList: (token?: string, enabled?: boolean) => import("@tanstack/react-query").UseQueryResult<ApplicantItem[], Error>;
3
+ //# sourceMappingURL=useApplicantList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useApplicantList.d.ts","sourceRoot":"","sources":["../../src/hooks/useApplicantList.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAwB,MAAM,oBAAoB,CAAC;AAG9E,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,EAAE,UAAS,OAAc,2EAoCvE,CAAC"}
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+ import { useQuery } from '@tanstack/react-query';
3
+ import { apiRequest } from '../utils/api-request';
4
+ import { API } from '../api-client';
5
+ export const useApplicantList = (token, enabled = true) => {
6
+ return useQuery({
7
+ queryKey: ['applicants', token],
8
+ queryFn: async () => {
9
+ if (!token)
10
+ return [];
11
+ let page = 1;
12
+ let hasNextPage = true;
13
+ const allApplicants = [];
14
+ while (hasNextPage) {
15
+ const response = await apiRequest({
16
+ endpoint: API.applicants.list(page),
17
+ token,
18
+ });
19
+ if (!response.success || !response.data) {
20
+ break;
21
+ }
22
+ const pageData = response.data?.data;
23
+ if (!Array.isArray(pageData) || pageData.length === 0) {
24
+ break;
25
+ }
26
+ allApplicants.push(...pageData);
27
+ hasNextPage = !!response.data?.pagination?.nextPage;
28
+ page++;
29
+ }
30
+ return allApplicants;
31
+ },
32
+ enabled: enabled && !!token,
33
+ staleTime: 1000 * 60 * 2, // 2 minutes
34
+ });
35
+ };
@@ -0,0 +1,18 @@
1
+ import type { ApplicantPayload } from '../types/applicant';
2
+ export declare const useApplicantMutations: (token?: string) => {
3
+ applyToVacancy: import("@tanstack/react-query").UseMutationResult<unknown, Error, ApplicantPayload, unknown>;
4
+ updateApplication: import("@tanstack/react-query").UseMutationResult<unknown, Error, {
5
+ id: string;
6
+ data: ApplicantPayload;
7
+ }, unknown>;
8
+ deleteApplication: import("@tanstack/react-query").UseMutationResult<unknown, Error, string, unknown>;
9
+ applyToVacancyBatch: import("@tanstack/react-query").UseMutationResult<unknown, Error, {
10
+ personal_detail_id: string;
11
+ vacancy_id: string;
12
+ }[], unknown>;
13
+ statusUpdate: import("@tanstack/react-query").UseMutationResult<unknown, Error, {
14
+ status: string;
15
+ id: string;
16
+ }, unknown>;
17
+ };
18
+ //# sourceMappingURL=useApplicantMutations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useApplicantMutations.d.ts","sourceRoot":"","sources":["../../src/hooks/useApplicantMutations.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG3D,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM;;;YA4B1C,MAAM;cACJ,gBAAgB;;;;4BA0CA,MAAM;oBACd,MAAM;;;gBAsBuB,MAAM;YAAM,MAAM;;CA4BlE,CAAC"}
@@ -0,0 +1,100 @@
1
+ 'use client';
2
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
3
+ import { apiRequest } from '../utils/api-request';
4
+ import { API } from '../api-client';
5
+ export const useApplicantMutations = (token) => {
6
+ const queryClient = useQueryClient();
7
+ const applyToVacancy = useMutation({
8
+ mutationFn: async (data) => {
9
+ const res = await apiRequest({
10
+ endpoint: API.applicants.create(),
11
+ method: 'POST',
12
+ data,
13
+ token: token,
14
+ });
15
+ if (!res.success) {
16
+ throw new Error(res.errors?.general?.[0] || 'Application failed');
17
+ }
18
+ return res.data;
19
+ },
20
+ onSuccess: () => {
21
+ queryClient.invalidateQueries({ queryKey: ['applicants'] });
22
+ },
23
+ });
24
+ const updateApplication = useMutation({
25
+ mutationFn: async ({ id, data, }) => {
26
+ const res = await apiRequest({
27
+ endpoint: API.applicants.update(id),
28
+ method: 'PUT',
29
+ data,
30
+ token: token,
31
+ });
32
+ if (!res.success) {
33
+ throw new Error(res.errors?.general?.[0] || 'Update failed');
34
+ }
35
+ return res.data;
36
+ },
37
+ onSuccess: () => {
38
+ queryClient.invalidateQueries({ queryKey: ['applicants'] });
39
+ },
40
+ });
41
+ const deleteApplication = useMutation({
42
+ mutationFn: async (id) => {
43
+ const res = await apiRequest({
44
+ endpoint: API.applicants.delete(id),
45
+ method: 'DELETE',
46
+ token: token,
47
+ });
48
+ if (!res.success) {
49
+ throw new Error(res.errors?.general?.[0] || 'Delete failed');
50
+ }
51
+ return res.data;
52
+ },
53
+ onSuccess: () => {
54
+ queryClient.invalidateQueries({ queryKey: ['applicants'] });
55
+ },
56
+ });
57
+ const applyToVacancyBatch = useMutation({
58
+ mutationFn: async (payload) => {
59
+ const res = await apiRequest({
60
+ endpoint: API.applicants.batch(),
61
+ method: 'POST',
62
+ data: payload,
63
+ token: token,
64
+ });
65
+ if (!res.success) {
66
+ throw new Error(res.errors?.general?.[0] || 'Failed to assign application');
67
+ }
68
+ return res.data;
69
+ },
70
+ onSuccess: () => {
71
+ queryClient.invalidateQueries({ queryKey: ['applicants'] });
72
+ },
73
+ });
74
+ const statusUpdate = useMutation({
75
+ mutationFn: async ({ status, id }) => {
76
+ const res = await apiRequest({
77
+ endpoint: API.applicants.update(id),
78
+ method: 'PATCH',
79
+ token: token,
80
+ data: {
81
+ status: status,
82
+ },
83
+ });
84
+ if (!res.success) {
85
+ throw new Error(res.errors?.general?.[0] || 'Failed to Update status');
86
+ }
87
+ return res.data;
88
+ },
89
+ onSuccess: () => {
90
+ queryClient.invalidateQueries({ queryKey: ['applicants'] });
91
+ },
92
+ });
93
+ return {
94
+ applyToVacancy,
95
+ updateApplication,
96
+ deleteApplication,
97
+ applyToVacancyBatch,
98
+ statusUpdate,
99
+ };
100
+ };