2026-05-26 15:48:01 +05:30
|
|
|
import prisma from '../prisma/client.js';
|
2026-03-13 16:26:06 +05:30
|
|
|
|
2026-05-26 15:48:01 +05:30
|
|
|
import { sendEmail } from '../utils/sendEmail.js';
|
|
|
|
|
import { getEmailsByType } from '../utils/getEmailByTypes.js';
|
2026-03-25 10:10:15 +05:30
|
|
|
|
2026-03-13 16:26:06 +05:30
|
|
|
// CREATE CANDIDATE
|
|
|
|
|
|
|
|
|
|
export const createCandidate = async (req, res) => {
|
|
|
|
|
try {
|
2026-05-26 15:48:01 +05:30
|
|
|
const { fullName, mobile, email, subject, coverLetter, careerId } = req.body;
|
2026-03-13 16:26:06 +05:30
|
|
|
|
|
|
|
|
if (!fullName || !mobile || !email || !careerId) {
|
|
|
|
|
return res.status(400).json({
|
|
|
|
|
success: false,
|
2026-05-26 15:48:01 +05:30
|
|
|
message: 'Required fields missing',
|
2026-03-13 16:26:06 +05:30
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const candidate = await prisma.candidate.create({
|
|
|
|
|
data: {
|
|
|
|
|
fullName,
|
|
|
|
|
mobile,
|
|
|
|
|
email,
|
|
|
|
|
subject,
|
|
|
|
|
coverLetter,
|
|
|
|
|
careerId: Number(careerId),
|
|
|
|
|
},
|
2026-03-25 10:10:15 +05:30
|
|
|
include: {
|
|
|
|
|
career: true,
|
|
|
|
|
},
|
2026-03-13 16:26:06 +05:30
|
|
|
});
|
|
|
|
|
|
2026-03-25 10:10:15 +05:30
|
|
|
try {
|
2026-05-26 15:48:01 +05:30
|
|
|
const emailList = await getEmailsByType('CANDIDATE');
|
2026-03-25 10:10:15 +05:30
|
|
|
|
|
|
|
|
if (emailList && emailList.length > 0) {
|
|
|
|
|
await sendEmail({
|
|
|
|
|
to: emailList,
|
2026-05-26 15:48:01 +05:30
|
|
|
subject: 'New Job Application Received',
|
2026-03-25 10:10:15 +05:30
|
|
|
html: `
|
2026-04-27 17:29:33 +05:30
|
|
|
<div style="font-family: Arial, sans-serif; background-color: #f4f6f8; padding: 20px;">
|
|
|
|
|
|
|
|
|
|
<div style="max-width: 600px; margin: auto; background: #ffffff; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 10px rgba(0,0,0,0.05);">
|
|
|
|
|
|
|
|
|
|
<!-- Header -->
|
|
|
|
|
<div style="background-color: #0d6efd; color: #ffffff; padding: 20px;">
|
|
|
|
|
<h2 style="margin: 0;">GG Hospital</h2>
|
|
|
|
|
<p style="margin: 5px 0 0; font-size: 14px;">
|
|
|
|
|
New Job Application Received
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
2026-03-25 10:10:15 +05:30
|
|
|
|
2026-04-27 17:29:33 +05:30
|
|
|
<!-- Body -->
|
|
|
|
|
<div style="padding: 20px; color: #333;">
|
|
|
|
|
|
|
|
|
|
<h3 style="margin-top: 0;">Candidate Details</h3>
|
|
|
|
|
|
|
|
|
|
<table style="width: 100%; border-collapse: collapse;">
|
|
|
|
|
<tr>
|
|
|
|
|
<td style="padding: 8px 0;"><b>Name:</b></td>
|
|
|
|
|
<td style="padding: 8px 0;">${fullName}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td style="padding: 8px 0;"><b>Phone:</b></td>
|
|
|
|
|
<td style="padding: 8px 0;">${mobile}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td style="padding: 8px 0;"><b>Email:</b></td>
|
|
|
|
|
<td style="padding: 8px 0;">${email}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
2026-03-25 10:10:15 +05:30
|
|
|
|
2026-04-27 17:29:33 +05:30
|
|
|
<h3 style="margin-top: 20px;">Application Details</h3>
|
2026-03-25 10:10:15 +05:30
|
|
|
|
2026-04-27 17:29:33 +05:30
|
|
|
<table style="width: 100%; border-collapse: collapse;">
|
|
|
|
|
<tr>
|
|
|
|
|
<td style="padding: 8px 0;"><b>Applied For:</b></td>
|
2026-05-26 15:48:01 +05:30
|
|
|
<td style="padding: 8px 0;">${candidate.career?.post || '-'}</td>
|
2026-04-27 17:29:33 +05:30
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td style="padding: 8px 0;"><b>Designation:</b></td>
|
2026-05-26 15:48:01 +05:30
|
|
|
<td style="padding: 8px 0;">${candidate.career?.designation || '-'}</td>
|
2026-04-27 17:29:33 +05:30
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td style="padding: 8px 0;"><b>Subject:</b></td>
|
2026-05-26 15:48:01 +05:30
|
|
|
<td style="padding: 8px 0;">${subject || '-'}</td>
|
2026-04-27 17:29:33 +05:30
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
<!-- Cover Letter -->
|
|
|
|
|
<div style="margin-top: 20px;">
|
|
|
|
|
<h3>Cover Letter</h3>
|
|
|
|
|
<div style="
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
padding: 15px;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
overflow-wrap: anywhere;
|
|
|
|
|
">
|
2026-05-26 15:48:01 +05:30
|
|
|
${coverLetter ? coverLetter.replace(/\n/g, '<br/>') : '-'}
|
2026-04-27 17:29:33 +05:30
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Footer -->
|
|
|
|
|
<div style="background: #f1f1f1; padding: 15px; text-align: center; font-size: 12px; color: #666;">
|
|
|
|
|
This application was submitted via the GG Hospital careers page.
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
`,
|
2026-03-25 10:10:15 +05:30
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
2026-05-26 15:48:01 +05:30
|
|
|
console.error('Candidate email failed:', err);
|
2026-03-25 10:10:15 +05:30
|
|
|
}
|
|
|
|
|
|
2026-03-13 16:26:06 +05:30
|
|
|
res.status(201).json({
|
|
|
|
|
success: true,
|
2026-05-26 15:48:01 +05:30
|
|
|
message: 'Application submitted successfully',
|
2026-03-13 16:26:06 +05:30
|
|
|
data: candidate,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
success: false,
|
2026-05-26 15:48:01 +05:30
|
|
|
message: 'Failed to create candidate',
|
2026-03-13 16:26:06 +05:30
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// GET ALL CANDIDATES
|
|
|
|
|
|
|
|
|
|
export const getCandidates = async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const candidates = await prisma.candidate.findMany({
|
|
|
|
|
include: {
|
|
|
|
|
career: true,
|
|
|
|
|
},
|
|
|
|
|
orderBy: {
|
2026-05-26 15:48:01 +05:30
|
|
|
createdAt: 'desc',
|
2026-03-13 16:26:06 +05:30
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
res.status(200).json({
|
|
|
|
|
success: true,
|
|
|
|
|
data: candidates,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
success: false,
|
2026-05-26 15:48:01 +05:30
|
|
|
message: 'Failed to fetch candidates',
|
2026-03-13 16:26:06 +05:30
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// GET SINGLE CANDIDATE
|
|
|
|
|
|
|
|
|
|
export const getCandidate = async (req, res) => {
|
|
|
|
|
try {
|
2026-03-25 10:10:15 +05:30
|
|
|
const { id } = req.params;
|
2026-03-13 16:26:06 +05:30
|
|
|
|
|
|
|
|
const candidate = await prisma.candidate.findUnique({
|
|
|
|
|
where: {
|
|
|
|
|
id: Number(id),
|
|
|
|
|
},
|
|
|
|
|
include: {
|
|
|
|
|
career: true,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!candidate) {
|
|
|
|
|
return res.status(404).json({
|
|
|
|
|
success: false,
|
2026-05-26 15:48:01 +05:30
|
|
|
message: 'Candidate not found',
|
2026-03-13 16:26:06 +05:30
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res.status(200).json({
|
|
|
|
|
success: true,
|
|
|
|
|
data: candidate,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
success: false,
|
2026-05-26 15:48:01 +05:30
|
|
|
message: 'Failed to fetch candidate',
|
2026-03-13 16:26:06 +05:30
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// GET CANDIDATES BY CAREER
|
|
|
|
|
|
|
|
|
|
export const getCandidatesByCareer = async (req, res) => {
|
|
|
|
|
try {
|
2026-03-25 10:10:15 +05:30
|
|
|
const { careerId } = req.params;
|
2026-03-13 16:26:06 +05:30
|
|
|
|
|
|
|
|
const candidates = await prisma.candidate.findMany({
|
|
|
|
|
where: {
|
|
|
|
|
careerId: Number(careerId),
|
|
|
|
|
},
|
|
|
|
|
include: {
|
|
|
|
|
career: true,
|
|
|
|
|
},
|
|
|
|
|
orderBy: {
|
2026-05-26 15:48:01 +05:30
|
|
|
createdAt: 'desc',
|
2026-03-13 16:26:06 +05:30
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
res.status(200).json({
|
|
|
|
|
success: true,
|
|
|
|
|
data: candidates,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
success: false,
|
2026-05-26 15:48:01 +05:30
|
|
|
message: 'Failed to fetch candidates',
|
2026-03-13 16:26:06 +05:30
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// UPDATE CANDIDATE
|
|
|
|
|
|
|
|
|
|
export const updateCandidate = async (req, res) => {
|
|
|
|
|
try {
|
2026-03-25 10:10:15 +05:30
|
|
|
const { id } = req.params;
|
2026-03-13 16:26:06 +05:30
|
|
|
|
|
|
|
|
const candidate = await prisma.candidate.update({
|
|
|
|
|
where: {
|
|
|
|
|
id: Number(id),
|
|
|
|
|
},
|
|
|
|
|
data: req.body,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
res.status(200).json({
|
|
|
|
|
success: true,
|
2026-05-26 15:48:01 +05:30
|
|
|
message: 'Candidate updated successfully',
|
2026-03-13 16:26:06 +05:30
|
|
|
data: candidate,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
success: false,
|
2026-05-26 15:48:01 +05:30
|
|
|
message: 'Failed to update candidate',
|
2026-03-13 16:26:06 +05:30
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// DELETE CANDIDATE
|
|
|
|
|
|
|
|
|
|
export const deleteCandidate = async (req, res) => {
|
|
|
|
|
try {
|
2026-03-25 10:10:15 +05:30
|
|
|
const { id } = req.params;
|
2026-03-13 16:26:06 +05:30
|
|
|
|
|
|
|
|
await prisma.candidate.delete({
|
|
|
|
|
where: {
|
|
|
|
|
id: Number(id),
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
res.status(200).json({
|
|
|
|
|
success: true,
|
2026-05-26 15:48:01 +05:30
|
|
|
message: 'Candidate deleted successfully',
|
2026-03-13 16:26:06 +05:30
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
res.status(500).json({
|
|
|
|
|
success: false,
|
2026-05-26 15:48:01 +05:30
|
|
|
message: 'Failed to delete candidate',
|
2026-03-13 16:26:06 +05:30
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|