489 lines
13 KiB
JavaScript
489 lines
13 KiB
JavaScript
import prisma from '../prisma/client.js';
|
|
import { sendEmail } from '../utils/sendEmail.js';
|
|
import { getEmailsByType } from '../utils/getEmailByTypes.js';
|
|
|
|
export const getAllCategories = async (req, res) => {
|
|
try {
|
|
const { admin } = req.query;
|
|
|
|
const categories = await prisma.healthCheckCategory.findMany({
|
|
where: admin === 'true' ? {} : { isActive: true },
|
|
orderBy: { sortOrder: 'asc' },
|
|
include: {
|
|
_count: { select: { packages: true } },
|
|
},
|
|
});
|
|
return res.status(200).json({ success: true, data: categories });
|
|
} catch (error) {
|
|
return res.status(500).json({ success: false, message: 'Failed to fetch categories' });
|
|
}
|
|
};
|
|
|
|
export const createCategory = async (req, res) => {
|
|
try {
|
|
const { name, slug, description, isActive, sortOrder } = req.body;
|
|
|
|
const category = await prisma.healthCheckCategory.create({
|
|
data: {
|
|
name,
|
|
slug: slug || null,
|
|
description,
|
|
isActive: isActive ?? true,
|
|
sortOrder: sortOrder ? Number(sortOrder) : 1000,
|
|
},
|
|
});
|
|
|
|
return res.status(201).json({ success: true, message: 'Category created', data: category });
|
|
} catch (error) {
|
|
console.error(error);
|
|
return res.status(500).json({ success: false, message: 'Failed to create category' });
|
|
}
|
|
};
|
|
|
|
export const updateCategory = async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const data = { ...req.body };
|
|
|
|
delete data.id;
|
|
delete data._count;
|
|
delete data.createdAt;
|
|
delete data.updatedAt;
|
|
|
|
if (data.sortOrder !== undefined) data.sortOrder = Number(data.sortOrder);
|
|
|
|
if (data.slug === '') data.slug = null;
|
|
|
|
const updatedCategory = await prisma.$transaction(async (tx) => {
|
|
const category = await tx.healthCheckCategory.update({
|
|
where: { id: Number(id) },
|
|
data,
|
|
});
|
|
|
|
if (data.isActive === false) {
|
|
await tx.healthPackage.updateMany({
|
|
where: { categoryId: Number(id) },
|
|
data: { isActive: false },
|
|
});
|
|
}
|
|
|
|
return category;
|
|
});
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
message: 'Category updated',
|
|
data: updatedCategory,
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
return res.status(500).json({ success: false, message: 'Failed to update category' });
|
|
}
|
|
};
|
|
|
|
export const deleteCategory = async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
await prisma.healthCheckCategory.delete({
|
|
where: { id: Number(id) },
|
|
});
|
|
|
|
return res.status(200).json({ success: true, message: 'Category deleted successfully' });
|
|
} catch (error) {
|
|
console.error(error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
message: 'Failed to delete category. Ensure no packages are linked to it.',
|
|
});
|
|
}
|
|
};
|
|
|
|
export const getAllPackages = async (req, res) => {
|
|
try {
|
|
const { admin, categorySlug } = req.query;
|
|
|
|
const packages = await prisma.healthPackage.findMany({
|
|
where: {
|
|
AND: [admin === 'true' ? {} : { isActive: true }, categorySlug ? { category: { slug: categorySlug } } : {}],
|
|
},
|
|
include: {
|
|
category: true,
|
|
seo: true,
|
|
},
|
|
orderBy: [{ sortOrder: 'asc' }, { createdAt: 'desc' }],
|
|
});
|
|
|
|
return res.status(200).json({ success: true, data: packages });
|
|
} catch (error) {
|
|
console.error(error);
|
|
return res.status(500).json({ success: false, message: 'Failed to fetch packages' });
|
|
}
|
|
};
|
|
|
|
export const createPackage = async (req, res) => {
|
|
try {
|
|
const {
|
|
name,
|
|
slug,
|
|
description,
|
|
price,
|
|
image,
|
|
discountedPrice,
|
|
inclusions,
|
|
categoryId,
|
|
isActive,
|
|
isFeatured,
|
|
sortOrder,
|
|
seo,
|
|
} = req.body;
|
|
|
|
const healthPackage = await prisma.healthPackage.create({
|
|
data: {
|
|
name,
|
|
slug,
|
|
description,
|
|
price,
|
|
image,
|
|
discountedPrice,
|
|
inclusions,
|
|
categoryId: Number(categoryId),
|
|
isActive: isActive ?? true,
|
|
isFeatured: isFeatured ?? false,
|
|
sortOrder: sortOrder ? Number(sortOrder) : 1000,
|
|
|
|
...(seo && {
|
|
seo: {
|
|
create: {
|
|
seoTitle: seo.seoTitle,
|
|
metaDescription: seo.metaDescription,
|
|
focusKeyphrase: seo.focusKeyphrase,
|
|
slug: slug,
|
|
tags: seo.tags || [],
|
|
ogTitle: seo.ogTitle,
|
|
ogDescription: seo.ogDescription,
|
|
ogImage: seo.ogImage,
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
include: {
|
|
category: true,
|
|
seo: true,
|
|
},
|
|
});
|
|
|
|
return res.status(201).json({ success: true, message: 'Package created', data: healthPackage });
|
|
} catch (error) {
|
|
console.error(error);
|
|
return res.status(500).json({ success: false, message: 'Failed to create package' });
|
|
}
|
|
};
|
|
|
|
export const updatePackage = async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const data = { ...req.body };
|
|
delete data.id;
|
|
delete data.category;
|
|
delete data.createdAt;
|
|
delete data.updatedAt;
|
|
delete data.seoId;
|
|
|
|
if (data.categoryId) data.categoryId = Number(data.categoryId);
|
|
if (data.sortOrder) data.sortOrder = Number(data.sortOrder);
|
|
|
|
const existingPackage = await prisma.healthPackage.findUnique({
|
|
where: { id: Number(id) },
|
|
select: { slug: true },
|
|
});
|
|
const seoSlug = data.slug || existingPackage.slug;
|
|
|
|
const updated = await prisma.healthPackage.update({
|
|
where: { id: Number(id) },
|
|
|
|
data: {
|
|
...data,
|
|
|
|
seo: data.seo
|
|
? {
|
|
upsert: {
|
|
create: {
|
|
seoTitle: data.seo.seoTitle,
|
|
metaDescription: data.seo.metaDescription,
|
|
focusKeyphrase: data.seo.focusKeyphrase,
|
|
slug: seoSlug,
|
|
tags: data.seo.tags || [],
|
|
ogTitle: data.seo.ogTitle,
|
|
ogDescription: data.seo.ogDescription,
|
|
ogImage: data.seo.ogImage,
|
|
},
|
|
|
|
update: {
|
|
seoTitle: data.seo.seoTitle,
|
|
metaDescription: data.seo.metaDescription,
|
|
focusKeyphrase: data.seo.focusKeyphrase,
|
|
slug: seoSlug,
|
|
tags: data.seo.tags || [],
|
|
ogTitle: data.seo.ogTitle,
|
|
ogDescription: data.seo.ogDescription,
|
|
ogImage: data.seo.ogImage,
|
|
},
|
|
},
|
|
}
|
|
: undefined,
|
|
},
|
|
|
|
include: {
|
|
category: true,
|
|
seo: true,
|
|
},
|
|
});
|
|
|
|
return res.status(200).json({ success: true, message: 'Package updated', data: updated });
|
|
} catch (error) {
|
|
console.error(error);
|
|
return res.status(500).json({ success: false, message: 'Update failed' });
|
|
}
|
|
};
|
|
|
|
export const deletePackage = async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
await prisma.healthPackage.delete({
|
|
where: { id: Number(id) },
|
|
});
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
message: 'Package deleted',
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
message: 'Delete failed',
|
|
});
|
|
}
|
|
};
|
|
|
|
export const createPackageInquiry = async (req, res) => {
|
|
try {
|
|
const { fullName, mobileNumber, email, age, gender, preferredDate, packageId, message } = req.body;
|
|
|
|
const inquiry = await prisma.healthPackageInquiry.create({
|
|
data: {
|
|
fullName,
|
|
mobileNumber,
|
|
email,
|
|
age: age ? Number(age) : null,
|
|
gender,
|
|
preferredDate: preferredDate ? new Date(preferredDate) : null,
|
|
message,
|
|
packageId: Number(packageId),
|
|
},
|
|
include: {
|
|
healthPackage: true,
|
|
},
|
|
});
|
|
|
|
try {
|
|
const emailList = await getEmailsByType('HCINQUIRY');
|
|
|
|
if (emailList) {
|
|
await sendEmail({
|
|
to: emailList,
|
|
subject: 'New Health Checkup Package Inquiry',
|
|
html: `
|
|
<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 Health Checkup Package Inquiry
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Body -->
|
|
<div style="padding: 20px; color: #333;">
|
|
|
|
<h3 style="margin-top: 0;">Inquirer Details</h3>
|
|
|
|
<table style="width: 100%; border-collapse: collapse;">
|
|
<tr>
|
|
<td style="padding: 8px 0; width: 35%;"><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;">${mobileNumber}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 8px 0;"><b>Email:</b></td>
|
|
<td style="padding: 8px 0;">${email || '-'}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 8px 0;"><b>Age:</b></td>
|
|
<td style="padding: 8px 0;">${age || '-'}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 8px 0;"><b>Gender:</b></td>
|
|
<td style="padding: 8px 0;">${gender || '-'}</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h3 style="margin-top: 20px;">Package Details</h3>
|
|
|
|
<table style="width: 100%; border-collapse: collapse;">
|
|
<tr>
|
|
<td style="padding: 8px 0; width: 35%;"><b>Package Name:</b></td>
|
|
<td style="padding: 8px 0;">${inquiry.healthPackage?.name || 'Unknown Package'}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 8px 0;"><b>Preferred Date:</b></td>
|
|
<td style="padding: 8px 0;">
|
|
${
|
|
preferredDate
|
|
? new Date(preferredDate).toLocaleDateString('en-GB', {
|
|
day: '2-digit',
|
|
month: 'long',
|
|
year: 'numeric',
|
|
})
|
|
: 'Not specified'
|
|
}
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<!-- Message Box -->
|
|
<div style="margin-top: 20px;">
|
|
<h3>Message</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;
|
|
">
|
|
${message ? message.replace(/\n/g, '<br/>') : '-'}
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<div style="background: #f1f1f1; padding: 15px; text-align: center; font-size: 12px; color: #666;">
|
|
This inquiry was submitted via the GG Hospital website.
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
`,
|
|
});
|
|
}
|
|
} catch (err) {
|
|
console.error('Email failed:', err);
|
|
}
|
|
|
|
return res.status(201).json({
|
|
success: true,
|
|
message: 'Booking inquiry sent successfully',
|
|
data: inquiry,
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
return res.status(500).json({ success: false, message: 'Failed to submit inquiry' });
|
|
}
|
|
};
|
|
|
|
export const getPackageBySlug = async (req, res) => {
|
|
try {
|
|
const { slug } = req.params;
|
|
const healthPackage = await prisma.healthPackage.findFirst({
|
|
where: { slug, isActive: true },
|
|
|
|
include: {
|
|
category: true,
|
|
seo: true,
|
|
},
|
|
});
|
|
|
|
if (!healthPackage) {
|
|
return res.status(404).json({ success: false, message: 'Package not found' });
|
|
}
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
data: healthPackage,
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
return res.status(500).json({ success: false, message: 'Failed to fetch package' });
|
|
}
|
|
};
|
|
|
|
export const getAllInquiries = async (req, res) => {
|
|
try {
|
|
const { page = 1, limit = 10, filterDate, startDate, endDate } = req.query;
|
|
|
|
const queryPage = parseInt(page);
|
|
const queryLimit = parseInt(limit);
|
|
const skip = (queryPage - 1) * queryLimit;
|
|
|
|
let where = {};
|
|
|
|
if (filterDate) {
|
|
where.preferredDate = {
|
|
gte: new Date(`${filterDate}T00:00:00.000Z`),
|
|
lte: new Date(`${filterDate}T23:59:59.999Z`),
|
|
};
|
|
} else if (startDate || endDate) {
|
|
where.preferredDate = {};
|
|
if (startDate) {
|
|
where.preferredDate.gte = new Date(`${startDate}T00:00:00.000Z`);
|
|
}
|
|
if (endDate) {
|
|
where.preferredDate.lte = new Date(`${endDate}T23:59:59.999Z`);
|
|
}
|
|
}
|
|
|
|
const [total, inquiries] = await prisma.$transaction([
|
|
prisma.healthPackageInquiry.count({ where }),
|
|
prisma.healthPackageInquiry.findMany({
|
|
where,
|
|
skip,
|
|
take: queryLimit,
|
|
include: {
|
|
healthPackage: {
|
|
include: {
|
|
category: true,
|
|
},
|
|
},
|
|
},
|
|
orderBy: { createdAt: 'desc' },
|
|
}),
|
|
]);
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
data: inquiries,
|
|
pagination: {
|
|
total,
|
|
page: queryPage,
|
|
limit: queryLimit,
|
|
totalPages: Math.ceil(total / queryLimit),
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
return res.status(500).json({ success: false, message: 'Failed to fetch inquiries' });
|
|
}
|
|
};
|