Compare commits

..

8 Commits

Author SHA1 Message Date
rishalkv e74a5b09c2 feat/add blog 2026-04-14 16:04:44 +05:30
rishalkv 86afb86d3c feat/blig edit 2026-04-14 15:25:20 +05:30
Kailasdevdas 0fddd7a656 fix: add missing import 2026-04-14 12:56:47 +05:30
kailasdevdas de53008e2d Merge pull request 'fix/frontend-ui' (#6) from fix/frontend-ui into dev
Reviewed-on: #6
2026-04-14 07:19:58 +00:00
kailasdevdas 0f6b34487e Merge pull request 'fix/backend' (#7) from fix/backend into dev
Reviewed-on: #7
2026-04-14 07:19:45 +00:00
Kailasdevdas fb298cb846 fix: add JWT middleware to private API routes 2026-04-08 16:44:41 +05:30
Kailasdevdas 9c44c66b22 feat: get doctors by department 2026-04-08 16:40:42 +05:30
Kailasdevdas 29d2ed6b96 feat: get department by name 2026-04-08 16:39:29 +05:30
22 changed files with 335 additions and 152 deletions
+11 -2
View File
@@ -22,7 +22,8 @@
"jsonwebtoken": "^9.0.3",
"multer": "^2.1.1",
"postmark": "^4.0.7",
"prisma": "^6.19.2"
"prisma": "^6.19.2",
"slugify": "^1.6.9"
},
"devDependencies": {
"nodemon": "^3.1.11"
@@ -1727,7 +1728,6 @@
"integrity": "sha512-XTKeKxtQElcq3U9/jHyxSPgiRgeYDKxWTPOf6NkXA0dNj5j40MfEsZkMbyNpwDWCUv7YBFUl7I2VK/6ALbmhEg==",
"hasInstallScript": true,
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@prisma/config": "6.19.2",
"@prisma/engines": "6.19.2"
@@ -2064,6 +2064,15 @@
"node": ">=10"
}
},
"node_modules/slugify": {
"version": "1.6.9",
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.9.tgz",
"integrity": "sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==",
"license": "MIT",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+2 -1
View File
@@ -28,7 +28,8 @@
"jsonwebtoken": "^9.0.3",
"multer": "^2.1.1",
"postmark": "^4.0.7",
"prisma": "^6.19.2"
"prisma": "^6.19.2",
"slugify": "^1.6.9"
},
"devDependencies": {
"nodemon": "^3.1.11"
@@ -0,0 +1,8 @@
/*
Warnings:
- Added the required column `slug` to the `Blog` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "Blog" ADD COLUMN "slug" TEXT NOT NULL;
@@ -0,0 +1,8 @@
/*
Warnings:
- A unique constraint covering the columns `[slug]` on the table `Blog` will be added. If there are existing duplicate values, this will fail.
*/
-- CreateIndex
CREATE UNIQUE INDEX "Blog_slug_key" ON "Blog"("slug");
+19 -19
View File
@@ -93,6 +93,7 @@ model Blog {
image String?
content Json
isActive Boolean @default(true)
slug String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -149,20 +150,20 @@ model Appointment {
}
model Inquiry {
id Int @id @default(autoincrement())
id Int @id @default(autoincrement())
fullName String
number String
emailId String?
subject String?
message String?
fullName String
number String
emailId String?
subject String?
message String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model AcademicsResearch {
id Int @id @default(autoincrement())
id Int @id @default(autoincrement())
fullName String
number String
@@ -171,24 +172,23 @@ model AcademicsResearch {
courseName String?
message String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model EmailConfig {
id Int @id @default(autoincrement())
name String
email String
type String
isActive Boolean @default(true)
id Int @id @default(autoincrement())
name String
email String
type String
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model NewsMedia {
id Int @id @default(autoincrement())
id Int @id @default(autoincrement())
headline String
content String?
@@ -197,8 +197,8 @@ model NewsMedia {
author String?
date DateTime?
isActive Boolean @default(true)
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
@@ -1,4 +1,5 @@
import prisma from "../prisma/client.js";
import slugify from "slugify";
/* CREATE BLOG */
@@ -13,6 +14,7 @@ export async function createBlog(req, res) {
image,
content,
isActive,
slug: slugify(title),
},
});
@@ -54,6 +56,26 @@ export async function getAllBlogs(req, res) {
/* GET SINGLE BLOG */
export async function getBlog(req, res) {
try {
const slug = req.params.slug;
const blog = await prisma.blog.findUnique({
where: {slug},
});
if (!blog) {
return res.status(404).json({error: "Blog not found"});
}
res.json(blog);
} catch (error) {
res.status(500).json({error: error.message});
}
}
/* GET SINGLE BLOG (ADMIN)*/
export async function getBlogForAdmin(req, res) {
try {
const id = Number(req.params.id);
@@ -29,6 +29,53 @@ export const getAllDepartments = async (req, res) => {
}
};
export const getDepartmentByName = async (req, res) => {
try {
const {name} = req.query;
if (!name) {
return res.status(400).json({
success: false,
message: "Department name is required",
});
}
const department = await prisma.department.findFirst({
where: {
name: name,
},
});
if (!department) {
return res.status(404).json({
success: false,
message: "Department not found",
});
}
const response = {
departmentId: department.departmentId,
name: department.name,
para1: department.para1 ?? "",
para2: department.para2 ?? "",
para3: department.para3 ?? "",
facilities: department.facilities ?? "",
services: department.services ?? "",
};
return res.status(200).json({
success: true,
data: [response],
});
} catch (error) {
console.error(error);
return res.status(500).json({
success: false,
message: "Failed to fetch department",
});
}
};
export async function createDepartment(req, res) {
try {
const {departmentId, name, para1, para2, para3, facilities, services} =
+103 -91
View File
@@ -16,38 +16,35 @@ export const getAllDoctors = async (req, res) => {
orderBy: {name: "asc"},
});
const formatted = doctors.map((doc, index) => {
return {
SL_NO: String(index + 1),
doctorId: doc.doctorId,
name: doc.name,
designation: doc.designation,
workingStatus: doc.workingStatus,
qualification: doc.qualification,
const formatted = doctors.map((doc, index) => ({
SL_NO: String(index + 1),
doctorId: doc.doctorId,
name: doc.name,
designation: doc.designation,
workingStatus: doc.workingStatus,
qualification: doc.qualification,
departments: doc.departments.map((d) => {
const t = d.timing || {};
departments: doc.departments.map((d) => {
const t = d.timing || {};
const timingArray = [
t.monday && `Monday ${t.monday}`,
t.tuesday && `Tuesday ${t.tuesday}`,
t.wednesday && `Wednesday ${t.wednesday}`,
t.thursday && `Thursday ${t.thursday}`,
t.friday && `Friday ${t.friday}`,
t.saturday && `Saturday ${t.saturday}`,
t.sunday && `Sunday ${t.sunday}`,
t.additional && t.additional,
].filter(Boolean);
const timingArray = [
t.monday && `Monday ${t.monday}`,
t.tuesday && `Tuesday ${t.tuesday}`,
t.wednesday && `Wednesday ${t.wednesday}`,
t.thursday && `Thursday ${t.thursday}`,
t.friday && `Friday ${t.friday}`,
t.saturday && `Saturday ${t.saturday}`,
t.sunday && `Sunday ${t.sunday}`,
t.additional && t.additional,
].filter(Boolean);
return {
departmentId: d.department.departmentId,
departmentName: d.department.name,
timing: timingArray.join(" & "),
};
}),
};
});
return {
departmentId: d.department.departmentId,
departmentName: d.department.name,
timing: timingArray.join(" & "),
};
}),
}));
res.status(200).json({
success: true,
@@ -113,6 +110,54 @@ export const getDoctorByDoctorId = async (req, res) => {
}
};
// get doctors by department
export const getDoctorsByDepartmentId = async (req, res) => {
try {
const {Department_ID} = req.query;
if (!Department_ID) {
return res.status(400).json({
success: false,
message: "Department_ID is required",
});
}
const department = await prisma.department.findUnique({
where: {departmentId: Department_ID},
});
if (!department) {
return res.status(404).json({
success: false,
message: "Department not found",
});
}
const doctors = await prisma.doctorDepartment.findMany({
where: {departmentId: department.id},
include: {
doctor: true,
},
});
const result = doctors.map((d) => ({
GG_ID: d.doctor.doctorId,
Name: d.doctor.name,
}));
res.status(200).json({
success: true,
data: result,
});
} catch (error) {
console.error(error);
res.status(500).json({
success: false,
message: "Failed to fetch doctors",
});
}
};
// add doctors
export const createDoctor = async (req, res) => {
try {
@@ -184,20 +229,14 @@ export const updateDoctor = async (req, res) => {
});
if (!doctor) {
return res.status(404).json({
success: false,
message: "Doctor not found",
});
return res
.status(404)
.json({success: false, message: "Doctor not found"});
}
await prisma.doctor.update({
where: {id: doctor.id},
data: {
name,
designation,
workingStatus,
qualification,
},
data: {name, designation, workingStatus, qualification},
});
const oldRelations = await prisma.doctorDepartment.findMany({
@@ -229,25 +268,24 @@ export const updateDoctor = async (req, res) => {
});
if (dep.timing) {
const {id, doctorDepartmentId, createdAt, updatedAt, ...cleanTiming} =
dep.timing;
await prisma.doctorTiming.create({
data: {
doctorDepartmentId: doctorDepartment.id,
...dep.timing,
...cleanTiming,
},
});
}
}
res.status(200).json({
success: true,
message: "Doctor updated successfully",
});
res
.status(200)
.json({success: true, message: "Doctor updated successfully"});
} catch (error) {
console.error(error);
res.status(500).json({
success: false,
message: "Failed to update doctor",
});
console.error("Update Error:", error);
res.status(500).json({success: false, message: "Failed to update doctor"});
}
};
//delete doctor
@@ -256,13 +294,6 @@ export const deleteDoctor = async (req, res) => {
try {
const {doctorId} = req.params;
if (!doctorId) {
return res.status(400).json({
success: false,
message: "Doctor ID is required",
});
}
const doctor = await prisma.doctor.findUnique({
where: {doctorId},
});
@@ -270,7 +301,7 @@ export const deleteDoctor = async (req, res) => {
if (!doctor) {
return res.status(404).json({
success: false,
message: `Doctor with ID ${doctorId} not found`,
message: "Doctor not found",
});
}
@@ -294,7 +325,7 @@ export const deleteDoctor = async (req, res) => {
res.status(200).json({
success: true,
message: `Doctor ${doctorId} deleted successfully`,
message: "Doctor deleted successfully",
});
} catch (error) {
console.error(error);
@@ -320,23 +351,19 @@ export const getDoctorTimings = async (req, res) => {
});
const result = doctors.map((doc) => {
let timing = {};
if (doc.departments.length > 0) {
timing = doc.departments[0].timing ?? {};
}
const timing = doc.departments[0]?.timing || {};
return {
Doctor_ID: doc.doctorId,
Doctor: doc.name,
Monday: timing?.monday ?? "",
Tuesday: timing?.tuesday ?? "",
Wednesday: timing?.wednesday ?? "",
Thursday: timing?.thursday ?? "",
Friday: timing?.friday ?? "",
Saturday: timing?.saturday ?? "",
Sunday: timing?.sunday ?? "",
Additional: timing?.additional ?? "",
Monday: timing.monday || "",
Tuesday: timing.tuesday || "",
Wednesday: timing.wednesday || "",
Thursday: timing.thursday || "",
Friday: timing.friday || "",
Saturday: timing.saturday || "",
Sunday: timing.sunday || "",
Additional: timing.additional || "",
};
});
@@ -380,26 +407,11 @@ export const getDoctorTimingById = async (req, res) => {
const result = {
doctorId: doctor.doctorId,
doctorName: doctor.name,
departments: doctor.departments.map((d) => {
const t = d.timing || {};
return {
departmentId: d.department.departmentId,
departmentName: d.department.name,
timing: {
monday: t.monday || "",
tuesday: t.tuesday || "",
wednesday: t.wednesday || "",
thursday: t.thursday || "",
friday: t.friday || "",
saturday: t.saturday || "",
sunday: t.sunday || "",
additional: t.additional || "",
},
};
}),
departments: doctor.departments.map((d) => ({
departmentId: d.department.departmentId,
departmentName: d.department.name,
timing: d.timing || {},
})),
};
res.status(200).json({
@@ -11,8 +11,8 @@ import jwtAuthMiddleware from "../middleware/auth.js";
const router = express.Router();
router.post("/", createAcademicsResearch);
router.get("/getAll", getAcademicsResearch);
router.get("/:id", getSingleAcademicsResearch);
router.get("/getAll", jwtAuthMiddleware, getAcademicsResearch);
router.get("/:id", jwtAuthMiddleware, getSingleAcademicsResearch);
router.delete("/:id", jwtAuthMiddleware, deleteAcademicsResearch);
export default router;
+3 -3
View File
@@ -13,11 +13,11 @@ const router = express.Router();
/* PUBLIC */
router.get("/getall", getAppointments);
router.get("/getall", jwtAuthMiddleware, getAppointments);
router.post("/", createAppointment);
router.get("/:id", getAppointment);
router.patch("/:id", updateAppointment);
router.get("/:id", jwtAuthMiddleware, getAppointment);
router.patch("/:id", jwtAuthMiddleware, updateAppointment);
router.delete("/:id", jwtAuthMiddleware, deleteAppointment);
export default router;
+5 -1
View File
@@ -6,6 +6,7 @@ import {
updateBlog,
deleteBlog,
getAllBlogs,
getBlogForAdmin,
} from "../controllers/blog.controller.js";
import jwtAuthMiddleware from "../middleware/auth.js";
@@ -15,11 +16,14 @@ const router = express.Router();
/* PUBLIC */
router.get("/", getBlogs);
router.get("/:id", getBlog);
router.get("/:slug", getBlog);
// Protected
router.get("/admin/all", jwtAuthMiddleware, getAllBlogs);
router.get("/admin/:id", jwtAuthMiddleware, getBlogForAdmin);
router.post("/", jwtAuthMiddleware, createBlog);
router.put("/:id", jwtAuthMiddleware, updateBlog);
router.delete("/:id", jwtAuthMiddleware, deleteBlog);
+6 -6
View File
@@ -13,13 +13,13 @@ import jwtAuthMiddleware from "../middleware/auth.js";
const router = express.Router();
/* PUBLIC */
router.get("/getAll", getCandidates);
router.get("/:id", getCandidate);
router.get("/career/:careerId", getCandidatesByCareer);
router.post("/", createCandidate);
router.patch("/:id", updateCandidate);
router.get("/getAll", jwtAuthMiddleware, getCandidates);
router.get("/:id", jwtAuthMiddleware, getCandidate);
router.get("/career/:careerId", jwtAuthMiddleware, getCandidatesByCareer);
router.patch("/:id", jwtAuthMiddleware, updateCandidate);
router.delete("/:id", jwtAuthMiddleware, deleteCandidate);
export default router;
+5 -3
View File
@@ -6,12 +6,14 @@ import {
deleteCareer,
} from "../controllers/career.controller.js";
import jwtAuthMiddleware from "../middleware/auth.js";
const router = express.Router();
router.get("/getAll", getAllCareers);
router.post("/", createCareer);
router.patch("/:id", updateCareer);
router.delete("/:id", deleteCareer);
router.post("/", jwtAuthMiddleware, createCareer);
router.patch("/:id", jwtAuthMiddleware, updateCareer);
router.delete("/:id", jwtAuthMiddleware, deleteCareer);
export default router;
+2
View File
@@ -1,6 +1,7 @@
import express from "express";
import {
getAllDepartments,
getDepartmentByName,
createDepartment,
updateDepartment,
deleteDepartment,
@@ -11,6 +12,7 @@ const router = express.Router();
// Public
router.get("/getAll", getAllDepartments);
router.get("/search", getDepartmentByName);
// Protected
router.post("/", jwtAuthMiddleware, createDepartment);
+3 -1
View File
@@ -7,6 +7,7 @@ import {
getDoctorTimings,
getDoctorTimingById,
getDoctorByDoctorId,
getDoctorsByDepartmentId,
} from "../controllers/doctor.controller.js";
import jwtAuthMiddleware from "../middleware/auth.js";
@@ -14,9 +15,10 @@ import jwtAuthMiddleware from "../middleware/auth.js";
const router = express.Router();
router.get("/getAll", getAllDoctors);
router.get("/:doctorId", getDoctorByDoctorId);
router.get("/search", getDoctorsByDepartmentId);
router.get("/getTimings", getDoctorTimings);
router.get("/getTimings/:doctorId", getDoctorTimingById);
router.get("/:doctorId", getDoctorByDoctorId);
router.post("/", jwtAuthMiddleware, createDoctor);
router.patch("/:doctorId", jwtAuthMiddleware, updateDoctor);
+2 -2
View File
@@ -12,8 +12,8 @@ const router = express.Router();
router.post("/", createInquiry);
router.get("/getAll", getInquiries);
router.get("/:id", getInquiry);
router.get("/getAll", jwtAuthMiddleware, getInquiries);
router.get("/:id", jwtAuthMiddleware, getInquiry);
router.delete("/:id", jwtAuthMiddleware, deleteInquiry);
export default router;
Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

+1 -18
View File
@@ -115,7 +115,6 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1760,7 +1759,6 @@
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz",
"integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
"license": "MIT",
"peer": true,
"engines": {
"node": "^14.21.3 || >=16"
},
@@ -4093,7 +4091,6 @@
"integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -4104,7 +4101,6 @@
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -4115,7 +4111,6 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@@ -4177,7 +4172,6 @@
"integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.57.0",
"@typescript-eslint/types": "8.57.0",
@@ -4468,7 +4462,6 @@
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -4728,7 +4721,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -5558,7 +5550,6 @@
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -5811,7 +5802,6 @@
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"license": "MIT",
"peer": true,
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.1",
@@ -6466,7 +6456,6 @@
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz",
"integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=16.9.0"
}
@@ -8131,7 +8120,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -8141,7 +8129,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -8905,8 +8892,7 @@
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz",
"integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/tailwindcss-animate": {
"version": "1.0.7",
@@ -9113,7 +9099,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -9314,7 +9299,6 @@
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -9659,7 +9643,6 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
+2
View File
@@ -20,6 +20,7 @@ import CandidatePage from "./pages/candidates";
import InquiryPage from "./pages/inquiry";
import AcademicsPage from "./pages/Academics";
import NewsPage from "./pages/newsMedia";
import BlogDetail from "./pages/BlogDetails";
export default function App() {
return (
@@ -35,6 +36,7 @@ export default function App() {
<Route path="/department" element={<Department />} />
<Route path="/doctor" element={<Doctor />} />
<Route path="/blog" element={<Blog />} />
<Route path="/blog/:id" element={<BlogDetail />} />
<Route path="/blog/create" element={<BlogEditorPage />} />
<Route path="/blog/edit/:id" element={<BlogEditorPage />} />
<Route path="/appointment" element={<Appointment />} />
+1 -1
View File
@@ -14,7 +14,7 @@ export const getAllBlogsApi = async () => {
};
export const getBlogByIdApi = async (id: number) => {
const res = await apiClient.get(`/blogs/${id}`);
const res = await apiClient.get(`/blogs/admin/${id}`);
return res.data;
};
+8 -1
View File
@@ -17,7 +17,7 @@ import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card";
import {Button} from "@/components/ui/button";
import {Input} from "@/components/ui/input";
import {Loader2, RefreshCw, Plus, Pencil, Trash} from "lucide-react";
import {Loader2, RefreshCw, Plus, Pencil, Trash, Eye} from "lucide-react";
interface Blog {
id: number;
@@ -161,6 +161,13 @@ export default function BlogPage() {
<Pencil className="h-4 w-4" />
</Button>
<Button
size="sm"
variant="secondary"
onClick={() => navigate(`/blog/${blog.id}`)}
>
<Eye className="h-4 w-4" />
</Button>
<Button
size="sm"
variant="destructive"
+74
View File
@@ -0,0 +1,74 @@
import React, {useEffect, useState} from "react";
import {useParams, useNavigate} from "react-router-dom";
import axios from "axios";
import {Button} from "@/components/ui/button";
import {Card, CardContent} from "@/components/ui/card";
import {getBlogByIdApi} from "@/api/blog";
const BlogDetail = () => {
const {id} = useParams();
const navigate = useNavigate();
const [blog, setBlog] = useState<any>(null);
const fetchBlogById = async () => {
try {
const response = await getBlogByIdApi(Number(id));
setBlog(response);
} catch (error) {
console.error("Error fetching blog", error);
}
};
useEffect(() => {
fetchBlogById();
}, [id]); // ✅ FIXED dependency
if (!blog) {
return <div className="mt-40 text-center text-gray-500">Loading...</div>;
}
return (
<div className=" mx-auto flex flex-col ">
<Card>
<CardContent className="p-6 space-y-4">
{/* Back */}
<Button
variant="ghost"
className="text-black-600 p-0"
onClick={() => navigate(-1)}
>
Back
</Button>
{/* Title */}
<h1 className="text-2xl md:text-4xl lg:text-5xl font-bold">
{blog.title}
</h1>
{/* Meta */}
<p className="text-gray-500 text-sm">
{blog.writer} {new Date(blog.createdAt).toLocaleDateString()}
</p>
{/* Image */}
<img
src={blog.image}
alt={blog.title}
className="w-full h-[220px] md:h-[400px] object-cover rounded-md"
/>
{/* Content */}
<div className="space-y-3 text-gray-800 leading-relaxed">
{blog.content?.blocks?.map((block: any, index: number) => (
<p key={index}>{block.data?.text}</p>
))}
</div>
</CardContent>
</Card>
</div>
);
};
export default BlogDetail;