328 lines
8.6 KiB
JavaScript
328 lines
8.6 KiB
JavaScript
const BASE_URL = "http://localhost:8080/api";
|
|
const XLSX = require("xlsx-js-style");
|
|
|
|
// Helper function for handling fetch requests
|
|
const fetchData = async (url, options) => {
|
|
try {
|
|
const response = await fetch(url, options);
|
|
|
|
// Check if response is OK (status 200-299)
|
|
if (!response.ok) {
|
|
let errorDetails = {};
|
|
try {
|
|
errorDetails = await response.json(); // Attempt to parse error response
|
|
} catch (err) {
|
|
console.warn("Failed to parse error details:", err);
|
|
}
|
|
throw new Error(
|
|
`Error: ${response.statusText} (${response.status}) - ${
|
|
errorDetails.message || "No details available"
|
|
}`
|
|
);
|
|
}
|
|
|
|
// Return JSON response if successful
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error(`Request failed for ${url}:`, error.message);
|
|
throw error; // Re-throw for the caller to handle
|
|
}
|
|
};
|
|
|
|
// Fetch courses with optional filters
|
|
export const fetchCourses = async (filterData = {}) => {
|
|
try {
|
|
const queryString = new URLSearchParams(filterData).toString();
|
|
const url = `${BASE_URL}/courses?${queryString}`;
|
|
return fetchData(url, {
|
|
method: "GET",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error("Error fetching courses:", error.message);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
// Fetch list of faculties
|
|
export const fetchFaculties = async () => {
|
|
try {
|
|
const url = `${BASE_URL}/faculty`;
|
|
return fetchData(url, {
|
|
method: "GET",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error("Error fetching faculties:", error.message);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
// Fetch available options for form dropdowns
|
|
export const fetchOptions = async () => {
|
|
try {
|
|
const url = `${BASE_URL}/options`;
|
|
return fetchData(url, {
|
|
method: "GET",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error("Error fetching options:", error.message);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export const fetchAppointments = async (academicYear) => {
|
|
try {
|
|
const response = await fetch(`${BASE_URL}/appointments?academicYear=${academicYear}`);
|
|
if (!response.ok) {
|
|
throw new Error("Failed to fetch appointments");
|
|
}
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error(error);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
// Save multiple appointments to MongoDB
|
|
export const saveAppointment = async (appointmentsData) => {
|
|
console.log("Saving appointments with payload:", appointmentsData);
|
|
|
|
// Validate input format
|
|
if (!Array.isArray(appointmentsData) || appointmentsData.length === 0) {
|
|
const errorMessage =
|
|
"Invalid or missing appointment data: expected a non-empty array";
|
|
console.error(errorMessage);
|
|
throw new Error(errorMessage);
|
|
}
|
|
|
|
// Validate each appointment's structure
|
|
const invalidEntries = appointmentsData.filter(
|
|
(appointment) =>
|
|
!appointment.facultyId || !appointment.courseId || !Array.isArray(appointment.tasks)
|
|
);
|
|
|
|
if (invalidEntries.length > 0) {
|
|
const errorMessage = `Invalid appointments detected: ${JSON.stringify(
|
|
invalidEntries
|
|
)}`;
|
|
console.error(errorMessage);
|
|
throw new Error(errorMessage);
|
|
}
|
|
|
|
try {
|
|
const url = `${BASE_URL}/appointments`;
|
|
const response = await fetchData(url, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ appointments: appointmentsData }), // Send appointments as an array
|
|
});
|
|
|
|
console.log("Appointments saved successfully:", response);
|
|
return response;
|
|
} catch (error) {
|
|
console.error("Error saving appointments:", error.message);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
// Update course status
|
|
export const updateCourseStatus = async (courseId) => {
|
|
if (!courseId) {
|
|
throw new Error("Course ID is required to update the status");
|
|
}
|
|
|
|
const url = `${BASE_URL}/courses/${courseId}`;
|
|
return fetchData(url, {
|
|
method: "PATCH",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ status: "submitted" }), // Update status to "Submitted"
|
|
});
|
|
};
|
|
|
|
export const sendEmail = async (formData, type) => {
|
|
try {
|
|
const url = `${BASE_URL}/send-email/${type}`;
|
|
const response = await fetch(url, {
|
|
method: "POST",
|
|
body: formData, // Directly pass FormData
|
|
});
|
|
|
|
if (!response.ok) {
|
|
let errorDetails = {};
|
|
try {
|
|
errorDetails = await response.json();
|
|
} catch (err) {
|
|
console.warn("Failed to parse error details:", err);
|
|
}
|
|
throw new Error(
|
|
`Error: ${response.statusText} (${response.status}) - ${
|
|
errorDetails.message || "No details available"
|
|
}`
|
|
);
|
|
}
|
|
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error( error.message);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export const createExcelBook = (teacherData, teacher) => {
|
|
const workbook = XLSX.utils.book_new();
|
|
|
|
// Extract academicYear and examPeriod from the first entry in the teacherData array
|
|
const { academicYear, examPeriod } = teacherData[0] || {};
|
|
|
|
// Define header information
|
|
const headerInfo = [
|
|
["Somaiya Vidyavihar University"],
|
|
["K. J. SOMAIYA COLLEGE OF ENGINEERING"],
|
|
[`Examination: ${examPeriod} ${academicYear}`],
|
|
[
|
|
"Appointment of Internal Examiners for Paper Setting / OR/PR/Assessment/Reassessment",
|
|
],
|
|
["Class: B Tech/M Tech/Honour/Minor"],
|
|
["Department - Computer Engineering"],
|
|
[],
|
|
];
|
|
|
|
const tableHeaders = [
|
|
[
|
|
"Sr No",
|
|
"Semester",
|
|
"Program",
|
|
"Course Code",
|
|
"Course Name",
|
|
"Exam Type",
|
|
"Year",
|
|
"Marks",
|
|
"Name",
|
|
"Affiliation/College",
|
|
"Highest Qualification",
|
|
"Career Experience",
|
|
"Oral/Practical",
|
|
"Assessment",
|
|
"Reassessment",
|
|
"Paper Setting",
|
|
"Moderation",
|
|
"PwD Paper Setting",
|
|
],
|
|
];
|
|
|
|
const dataRows = teacherData.map((row, index) => [
|
|
index + 1,
|
|
row.semester,
|
|
row.program,
|
|
row.courseCode,
|
|
row.courseName,
|
|
row.examType,
|
|
Math.ceil(row.semester/2),
|
|
row.marks,
|
|
row.Name,
|
|
row.affiliation,
|
|
row.qualification,
|
|
row.experience,
|
|
row.oralPractical,
|
|
row.assessment,
|
|
row.reassessment,
|
|
row.paperSetting,
|
|
row.moderation,
|
|
row.pwdPaperSetting,
|
|
]);
|
|
|
|
const sheetData = [...headerInfo, ...tableHeaders, ...dataRows];
|
|
const worksheet = XLSX.utils.aoa_to_sheet(sheetData);
|
|
|
|
// Add merged cells
|
|
worksheet["!merges"] = [
|
|
{ s: { r: 0, c: 0 }, e: { r: 0, c: 18 } },
|
|
{ s: { r: 1, c: 0 }, e: { r: 1, c: 18 } },
|
|
{ s: { r: 2, c: 0 }, e: { r: 2, c: 18 } },
|
|
{ s: { r: 3, c: 0 }, e: { r: 3, c: 18 } },
|
|
{ s: { r: 4, c: 0 }, e: { r: 4, c: 18 } },
|
|
];
|
|
|
|
// Define styles
|
|
const boldStyle = {
|
|
font: { bold: true, name: "Times New Roman", sz: 14 },
|
|
alignment: { horizontal: "center", vertical: "center" },
|
|
};
|
|
|
|
const redStyle = {
|
|
font: {
|
|
bold: true,
|
|
color: { rgb: "FF0000" },
|
|
name: "Times New Roman",
|
|
sz: 14,
|
|
},
|
|
alignment: { horizontal: "center", vertical: "center" },
|
|
};
|
|
|
|
const normalStyle = {
|
|
font: { name: "Times New Roman", sz: 12 },
|
|
};
|
|
|
|
// Apply styles to headers
|
|
const headerRanges = [
|
|
{ row: 0, style: boldStyle },
|
|
{ row: 1, style: boldStyle },
|
|
{ row: 2, style: boldStyle },
|
|
{ row: 3, style: boldStyle },
|
|
{ row: 4, style: redStyle },
|
|
];
|
|
|
|
headerRanges.forEach(({ row, style }) => {
|
|
for (let col = 0; col <= 18; col++) {
|
|
const cellAddress = XLSX.utils.encode_cell({ r: row, c: col });
|
|
if (!worksheet[cellAddress]) continue; // Skip empty cells
|
|
worksheet[cellAddress].s = style;
|
|
}
|
|
});
|
|
|
|
// Set column widths for better readability
|
|
worksheet["!cols"] = [
|
|
{ wch: 10 }, // Sr No
|
|
{ wch: 12 }, // Semester
|
|
{ wch: 15 }, // Course Code
|
|
{ wch: 25 }, // Course Name
|
|
{ wch: 15 }, // Exam Type
|
|
{ wch: 10 }, // Year
|
|
{ wch: 10 }, // Marks
|
|
{ wch: 15 }, // Surname
|
|
{ wch: 15 }, // First Name
|
|
{ wch: 15 }, // Middle Name
|
|
{ wch: 20 }, // Affiliation
|
|
{ wch: 20 }, // Qualification
|
|
{ wch: 15 }, // Career Experience
|
|
{ wch: 15 }, // Oral/Practical
|
|
{ wch: 15 }, // Assessment
|
|
{ wch: 15 }, // Reassessment
|
|
{ wch: 15 }, // Paper Setting
|
|
{ wch: 15 }, // Moderation
|
|
{ wch: 15 }, // PwD Paper Setting
|
|
];
|
|
|
|
// Apply normal font style to all cells
|
|
Object.keys(worksheet).forEach((key) => {
|
|
if (worksheet[key] && key[0] !== "!") {
|
|
worksheet[key].s = worksheet[key].s || normalStyle;
|
|
}
|
|
});
|
|
|
|
// Add worksheet to workbook and save file
|
|
XLSX.utils.book_append_sheet(workbook, worksheet, teacher);
|
|
return workbook;
|
|
} |