feat: health checkup CRUD apis
This commit is contained in:
@@ -15,6 +15,7 @@ import academicsResearchRoutes from "./routes/academicsResearch.routes.js";
|
||||
import emailConfigRoutes from "./routes/emailConfig.routes.js";
|
||||
import newsMediaRoutes from "./routes/newsMedia.routes.js";
|
||||
import importRoutes from "./routes/importRoutes.js";
|
||||
import healthCheckRoutes from "./routes/healthCheck.route.js";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
@@ -55,6 +56,7 @@ app.use("/api/academics", academicsResearchRoutes);
|
||||
app.use("/api/email", emailConfigRoutes);
|
||||
app.use("/api/newsMedia", newsMediaRoutes);
|
||||
app.use("/api/import", importRoutes);
|
||||
app.use("/api/health-check", healthCheckRoutes);
|
||||
|
||||
const PORT = process.env.PORT || 5008;
|
||||
app.listen(PORT, () => {
|
||||
|
||||
@@ -0,0 +1,436 @@
|
||||
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,
|
||||
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);
|
||||
|
||||
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 },
|
||||
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,
|
||||
discountedPrice,
|
||||
inclusions,
|
||||
categoryId,
|
||||
isActive,
|
||||
isFeatured,
|
||||
sortOrder,
|
||||
} = req.body;
|
||||
|
||||
const healthPackage = await prisma.healthPackage.create({
|
||||
data: {
|
||||
name,
|
||||
slug,
|
||||
description,
|
||||
price,
|
||||
discountedPrice,
|
||||
inclusions,
|
||||
categoryId: Number(categoryId),
|
||||
isActive: isActive ?? true,
|
||||
isFeatured: isFeatured ?? false,
|
||||
sortOrder: sortOrder ? Number(sortOrder) : 1000,
|
||||
},
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
if (data.categoryId) data.categoryId = Number(data.categoryId);
|
||||
if (data.sortOrder) data.sortOrder = Number(data.sortOrder);
|
||||
|
||||
const updated = await prisma.healthPackage.update({
|
||||
where: { id: Number(id) },
|
||||
data,
|
||||
});
|
||||
|
||||
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 },
|
||||
});
|
||||
|
||||
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" });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import express from "express";
|
||||
import {
|
||||
// Categories
|
||||
getAllCategories,
|
||||
getPackageBySlug,
|
||||
createCategory,
|
||||
updateCategory,
|
||||
deleteCategory,
|
||||
|
||||
// Packages
|
||||
getAllPackages,
|
||||
createPackage,
|
||||
updatePackage,
|
||||
deletePackage,
|
||||
|
||||
// Inquiries
|
||||
createPackageInquiry,
|
||||
getAllInquiries,
|
||||
} from "../controllers/healthCheck.controller.js";
|
||||
|
||||
import jwtAuthMiddleware from "../middleware/auth.js";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/packages", getAllPackages);
|
||||
router.get("/packages/:slug", getPackageBySlug);
|
||||
router.get("/categories", getAllCategories);
|
||||
router.post("/inquiry", createPackageInquiry);
|
||||
|
||||
router.get("/inquiries", jwtAuthMiddleware, getAllInquiries);
|
||||
router.post("/", jwtAuthMiddleware, createPackage);
|
||||
router.patch("/:id", jwtAuthMiddleware, updatePackage);
|
||||
router.delete("/:id", jwtAuthMiddleware, deletePackage);
|
||||
|
||||
router.post("/categories", jwtAuthMiddleware, createCategory);
|
||||
router.patch("/categories/:id", jwtAuthMiddleware, updateCategory);
|
||||
router.delete("/categories/:id", jwtAuthMiddleware, deleteCategory);
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user