diff --git a/backend/prisma/migrations/20260623120651_featured_field_for_doctor/migration.sql b/backend/prisma/migrations/20260623120651_featured_field_for_doctor/migration.sql new file mode 100644 index 0000000..1851719 --- /dev/null +++ b/backend/prisma/migrations/20260623120651_featured_field_for_doctor/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Doctor" ADD COLUMN "isFeatured" BOOLEAN NOT NULL DEFAULT false; diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 993825d..2833572 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -27,6 +27,7 @@ model Doctor { workingStatus String? qualification String? isActive Boolean @default(true) + isFeatured Boolean @default(false) globalSortOrder Int @default(1000) specializations DoctorSpecialization[] professionalSummary String? @db.Text diff --git a/backend/src/controllers/doctor.controller.js b/backend/src/controllers/doctor.controller.js index 6692e2a..0dfa4f6 100644 --- a/backend/src/controllers/doctor.controller.js +++ b/backend/src/controllers/doctor.controller.js @@ -34,6 +34,7 @@ export const getAllDoctors = async (req, res) => { workingStatus: doc.workingStatus, qualification: doc.qualification, isActive: doc.isActive, + isFeatured: doc.isFeatured, experience: doc.experience, professionalSummary: doc.professionalSummary, globalSortOrder: doc.globalSortOrder, @@ -129,6 +130,7 @@ export const getDoctorByDoctorId = async (req, res) => { experience: doctor.experience, professionalSummary: doctor.professionalSummary, isActive: doctor.isActive, + isFeatured: doctor.isFeatured, seo: { seoTitle: doctor.seo?.seoTitle ?? '', metaDescription: doctor.seo?.metaDescription ?? '', @@ -240,6 +242,7 @@ export const createDoctor = async (req, res) => { workingStatus, qualification, isActive, + isFeatured, globalSortOrder, departments, experience, @@ -297,6 +300,7 @@ export const createDoctor = async (req, res) => { professionalSummary, seoId: seo.id, isActive: isActive !== undefined ? isActive : true, + isFeatured: isFeatured !== undefined ? isFeatured : false, globalSortOrder: globalSortOrder !== undefined ? Number(globalSortOrder) : 0, }, }); @@ -361,6 +365,7 @@ export const updateDoctor = async (req, res) => { workingStatus, qualification, isActive, + isFeatured, globalSortOrder, departments, experience, @@ -397,6 +402,19 @@ export const updateDoctor = async (req, res) => { message: `Doctor has been ${doctor.isActive ? 'deactivated' : 'activated'} successfully`, }); } + if (action === 'toggleFeatured') { + await prisma.doctor.update({ + where: { id: doctor.id }, + data: { + isFeatured: !doctor.isFeatured, + }, + }); + + return res.status(200).json({ + success: true, + message: `Doctor has been ${doctor.isFeatured ? 'removed from featured' : 'marked as featured'} successfully`, + }); + } const messages = []; if (!doctorId) messages.push('Doctor ID is required'); @@ -423,7 +441,8 @@ export const updateDoctor = async (req, res) => { image, workingStatus, qualification, - isActive, + isActive: isActive !== undefined ? isActive : undefined, + isFeatured: isFeatured !== undefined ? isFeatured : undefined, experience: experience ? Number(experience) : null, professionalSummary, globalSortOrder: globalSortOrder !== undefined ? Number(globalSortOrder) : undefined, @@ -700,3 +719,52 @@ export const getDoctorTimingById = async (req, res) => { }); } }; + +export const getFeaturedDoctors = async (req, res) => { + try { + const doctors = await prisma.doctor.findMany({ + where: { + isActive: true, + isFeatured: true, + }, + include: { + seo: { + select: { + slug: true, + }, + }, + departments: { + include: { + department: true, + }, + }, + }, + orderBy: [{ globalSortOrder: 'asc' }, { name: 'asc' }], + }); + + const data = doctors.map((doc) => ({ + doctorId: doc.doctorId, + name: doc.name, + image: doc.image ?? '', + designation: doc.designation, + qualification: doc.qualification, + experience: doc.experience, + slug: doc.seo?.slug ?? '', + departments: doc.departments.map((d) => ({ + departmentId: d.department.departmentId, + departmentName: d.department.name, + })), + })); + + res.status(200).json({ + success: true, + data, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: 'Failed to fetch featured doctors', + }); + } +}; diff --git a/backend/src/controllers/healthCheck.controller.js b/backend/src/controllers/healthCheck.controller.js index 8b05595..eb0361a 100644 --- a/backend/src/controllers/healthCheck.controller.js +++ b/backend/src/controllers/healthCheck.controller.js @@ -486,3 +486,33 @@ export const getAllInquiries = async (req, res) => { return res.status(500).json({ success: false, message: 'Failed to fetch inquiries' }); } }; + +export const getFeaturedPackages = async (req, res) => { + try { + const packages = await prisma.healthPackage.findMany({ + where: { + isActive: true, + isFeatured: true, + category: { + isActive: true, + }, + }, + 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 featured packages', + }); + } +}; diff --git a/backend/src/routes/doctor.routes.js b/backend/src/routes/doctor.routes.js index 3a4bcf1..9d0a5b7 100644 --- a/backend/src/routes/doctor.routes.js +++ b/backend/src/routes/doctor.routes.js @@ -8,6 +8,7 @@ import { getDoctorTimingById, getDoctorByDoctorId, getDoctorsByDepartmentId, + getFeaturedDoctors, } from '../controllers/doctor.controller.js'; import jwtAuthMiddleware from '../middleware/auth.js'; @@ -19,6 +20,7 @@ router.get('/search', getDoctorsByDepartmentId); router.get('/getTimings', getDoctorTimings); router.get('/getTimings/:doctorId', getDoctorTimingById); router.get('/:doctorId', getDoctorByDoctorId); +router.get('/featured', getFeaturedDoctors); router.post('/', jwtAuthMiddleware, createDoctor); router.patch('/:doctorId/:action', jwtAuthMiddleware, updateDoctor); diff --git a/backend/src/routes/healthCheck.route.js b/backend/src/routes/healthCheck.route.js index fcc7db2..3b7503a 100644 --- a/backend/src/routes/healthCheck.route.js +++ b/backend/src/routes/healthCheck.route.js @@ -12,6 +12,7 @@ import { createPackage, updatePackage, deletePackage, + getFeaturedPackages, // Inquiries createPackageInquiry, @@ -26,6 +27,7 @@ router.get('/packages', getAllPackages); router.get('/packages/:slug', getPackageBySlug); router.get('/categories', getAllCategories); router.post('/inquiry', createPackageInquiry); +router.get('/featured', getFeaturedPackages); router.get('/inquiries', jwtAuthMiddleware, getAllInquiries); router.post('/', jwtAuthMiddleware, createPackage); diff --git a/frontend/src/components/HealthPackageModal/HealthPackageModal.tsx b/frontend/src/components/HealthPackageModal/HealthPackageModal.tsx index b76638b..7dcc199 100644 --- a/frontend/src/components/HealthPackageModal/HealthPackageModal.tsx +++ b/frontend/src/components/HealthPackageModal/HealthPackageModal.tsx @@ -120,22 +120,40 @@ export default function HealthPackageModal({ /> -
-
-

Active Visibility

+
+
+
+

Active

+

Show publicly

+
-

Show this package publicly

+ + setPkgForm({ + ...pkgForm, + isActive: val, + }) + } + />
- - setPkgForm({ - ...pkgForm, - isActive: val, - }) - } - /> +
+
+

Featured

+

Show on homepage

+
+ + + setPkgForm({ + ...pkgForm, + isFeatured: val, + }) + } + /> +
diff --git a/frontend/src/pages/HealthPackagePage.tsx b/frontend/src/pages/HealthPackagePage.tsx index c17fe03..8083065 100644 --- a/frontend/src/pages/HealthPackagePage.tsx +++ b/frontend/src/pages/HealthPackagePage.tsx @@ -62,6 +62,7 @@ export default function HealthPackagePage() { discountedPrice: undefined, categoryId: 0, isActive: true, + isFeatured: false, sortOrder: 1000, seo: { seoTitle: '', @@ -167,6 +168,7 @@ export default function HealthPackagePage() { discountedPrice: undefined, categoryId: categories[0]?.id || 0, isActive: true, + isFeatured: false, sortOrder: 1000, seo: { seoTitle: '', @@ -366,6 +368,7 @@ export default function HealthPackagePage() { Category Pricing Status + Featured Actions @@ -418,6 +421,17 @@ export default function HealthPackagePage() {
+ + { + await updateHealthPackageApi(pkg.id!, { + isFeatured: !pkg.isFeatured, + }); + fetchData(); + }} + /> +