final changes

This commit is contained in:
sanikapendurkar
2025-06-10 18:38:01 +05:30
parent 56bbb4596f
commit dd2ceadee5
30 changed files with 1455 additions and 392 deletions

16
backend/config/db.js Normal file
View File

@@ -0,0 +1,16 @@
const mongoose = require("mongoose");
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log("MongoDB connected");
} catch (err) {
console.error("MongoDB connection error:", err);
process.exit(1);
}
};
module.exports = connectDB;

View File

@@ -0,0 +1,78 @@
const DepartmentEmail = require("../models/DepartmentEmail");
const Program = require("../models/Program");
exports.getAllDepartmentEmails = async (req, res) => {
try {
const departmentEmails = await DepartmentEmail.find();
res.json(departmentEmails);
} catch (error) {
console.error("Error fetching department emails:", error);
res.status(500).json({ error: "Error fetching department emails" });
}
};
exports.getDepartmentsByProgram = async (req, res) => {
const program = await Program.findOne({ name: req.params.program });
res.json(program ? program.departments : []);
};
exports.getEmailsByDepartment = async (req, res) => {
const departmentEmails = await DepartmentEmail.findOne({ department: req.params.department });
res.json(departmentEmails ? { emails: departmentEmails.emails } : { emails: [] });
};
exports.updateDepartmentEmails = async (req, res) => {
const { department } = req.params;
const { emails } = req.body;
if (!emails || !Array.isArray(emails)) {
return res.status(400).json({ error: "Emails array is required" });
}
try {
const updatedDepartment = await DepartmentEmail.findOneAndUpdate(
{ department },
{ emails },
{ new: true, upsert: true }
);
res.json(updatedDepartment);
} catch (error) {
console.error("Error updating department emails:", error);
res.status(500).json({ error: "Error updating department emails" });
}
};
exports.createDepartment = async (req, res) => {
const { department, emails, program } = req.body;
if (!department || !emails || !Array.isArray(emails) || !program) {
return res.status(400).json({ error: "Department, program, and emails array are required" });
}
try {
const newDepartment = new DepartmentEmail({ department, emails });
await newDepartment.save();
await Program.findOneAndUpdate(
{ name: program },
{ $addToSet: { departments: department } },
{ upsert: true }
);
res.status(201).json(newDepartment);
} catch (error) {
console.error("Error creating department:", error);
res.status(500).json({ error: "Error creating department" });
}
};
exports.deleteDepartment = async (req, res) => {
try {
const { department } = req.params;
await DepartmentEmail.findOneAndDelete({ department });
res.json({ message: "Department deleted successfully" });
} catch (error) {
console.error("Error deleting department:", error);
res.status(500).json({ error: "Error deleting department" });
}
};

View File

@@ -0,0 +1,136 @@
const nodemailer = require("nodemailer");
const moment = require("moment");
const Meeting = require("../models/Meeting");
const DepartmentEmail = require("../models/DepartmentEmail");
const sendEmail = async (req, res) => {
try {
const { program, department, subject, body, agenda, date, startTime, endTime, recipients } = req.body;
const files = req.files;
console.log({ program, department, subject, body, agenda, date, startTime, endTime, recipients, files });
if (!program || !department || !subject || !body || !date || !startTime || !endTime || !recipients) {
return res.status(400).json({ error: "All fields are required!" });
}
const departmentEmails = await DepartmentEmail.findOne({ department });
if (!departmentEmails) {
return res.status(404).json({ error: "Department emails not found" });
}
const allRecipients = [...new Set([...departmentEmails.emails, ...recipients])];
const conflict = await Meeting.findOne({
date,
startTime: { $lt: endTime },
endTime: { $gt: startTime },
});
const formattedDate = moment(date, "YYYY-MM-DD").format("dddd, MMMM Do YYYY");
const formattedStartTime = moment(startTime, "HH:mm").format("hh:mm A");
const formattedEndTime = moment(endTime, "HH:mm").format("hh:mm A");
const formattedStart = moment(`${date} ${startTime}`, "YYYY-MM-DD HH:mm").utc().format("YYYYMMDDTHHmmss[Z]");
const formattedEnd = moment(`${date} ${endTime}`, "YYYY-MM-DD HH:mm").utc().format("YYYYMMDDTHHmmss[Z]");
const zoomLink = "https://us05web.zoom.us/j/2655616202?pwd=S3d6RjVPNnhmQ3AzTlZsRC9GYmNHQT09";
const calendarLink = `https://calendar.google.com/calendar/render?action=TEMPLATE&text=${encodeURIComponent(
subject
)}&details=${encodeURIComponent(`${body}\nJoin Zoom Meeting: ${zoomLink}`)}&dates=${formattedStart}/${formattedEnd}`;
const formattedAgenda = Array.isArray(agenda) && agenda.length > 0
? agenda.map((item, index) => `<li>${index + 1}. ${item}</li>`).join("")
: "<li>No agenda provided</li>";
const newMeeting = new Meeting({
program,
department,
subject,
body,
agenda,
date,
startTime,
endTime,
recipients,
attachments: files.map((file) => ({ filename: file.filename, path: file.path })),
});
await newMeeting.save();
let conflictDetails = null;
if (conflict) {
conflictDetails = {
department: conflict.department,
date: conflict.date,
startTime: conflict.startTime,
endTime: conflict.endTime,
recipients: conflict.recipients,
subject: conflict.subject,
agenda: conflict.agenda || "No agenda provided",
};
console.log("Conflict detected! Details:", conflictDetails);
}
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL,
pass: process.env.EMAIL_PASSWORD,
},
});
const mailOptions = {
from: process.env.EMAIL,
to: recipients,
subject,
html: `
<p><strong>Respected Board Members,</strong></p>
<p><strong>Greetings from Somaiya Vidyavihar University !!</strong></p>
<p><strong>We are glad to invite you to the Board of Studies meeting: </strong></p>
<p>${body}</p>
<p><strong>Program:</strong> ${program}</p>
<p><strong>Department:</strong> ${department}</p>
<p><strong>Date:</strong> ${formattedDate}</p>
<p><strong>Time:</strong> ${formattedStartTime} - ${formattedEndTime}</p>
<p><strong>Zoom Meeting Details:</strong></p>
<p><strong>Meeting ID: </strong>265 561 6202</p>
<p><strong>Meeting Password: </strong>123456</p>
<p><strong>Zoom Meeting Link: </strong><a href="${zoomLink}" target="_blank">Join the Meeting</a></p>
<p><strong>Agenda:</strong></p>
<ul>${formattedAgenda}</ul>
<p><a href="${calendarLink}">📅 Click here to add this event to Google Calendar</a></p>
${
conflictDetails
? `<p><strong>Conflict detected!</strong></p>
<p><strong>Conflicting Meeting Details:</strong></p>
<ul>
<li><strong>Department:</strong> ${conflictDetails.department}</li>
<li><strong>Date:</strong> ${conflictDetails.date}</li>
<li><strong>Time:</strong> ${conflictDetails.startTime} - ${conflictDetails.endTime}</li>
<li><strong>Subject:</strong> ${conflictDetails.subject}</li>
<li><strong>Agenda:</strong> ${conflictDetails.agenda}</li>
</ul>`
: ""
}
`,
attachments: files.map((file) => ({
filename: file.filename,
path: file.path,
})),
};
await transporter.sendMail(mailOptions);
res.status(200).json({
message: conflictDetails ? "Conflict detected! Email sent successfully." : "Email sent successfully!",
conflictDetails,
calendarLink,
});
} catch (error) {
console.error("Error sending email:", error);
res.status(500).json({ error: "Error sending email" });
}
};
module.exports = { sendEmail };

View File

@@ -0,0 +1,18 @@
const Meeting = require("../models/Meeting");
exports.getMeetings = async (req, res) => {
try {
const meetings = await Meeting.find();
const formattedMeetings = meetings.map((meeting) => {
const formattedAgenda = meeting.agenda
? meeting.agenda.map((item, index) => `<li>${index + 1}. ${item}</li>`).join("")
: "<li>No agenda provided</li>";
return { ...meeting.toObject(), formattedAgenda };
});
res.json(formattedMeetings);
} catch (error) {
console.error("Error fetching meetings:", error);
res.status(500).json({ error: "Error fetching meetings" });
}
};

View File

@@ -0,0 +1,11 @@
const multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
destination: "uploads/",
filename: (req, file, cb) => {
cb(null, Date.now() + path.extname(file.originalname));
},
});
module.exports = multer({ storage });

View File

@@ -0,0 +1,8 @@
const mongoose = require("mongoose");
const DepartmentEmailSchema = new mongoose.Schema({
department: String,
emails: [String]
});
module.exports = mongoose.model("DepartmentEmail", DepartmentEmailSchema);

16
backend/models/Meeting.js Normal file
View File

@@ -0,0 +1,16 @@
const mongoose = require("mongoose");
const meetingSchema = new mongoose.Schema({
program: String,
department: String,
subject: String,
body: String,
agenda: [String],
date: String,
startTime: String,
endTime: String,
recipients: [String],
attachments: [{ filename: String, path: String }],
});
module.exports = mongoose.model("Meeting", meetingSchema);

26
backend/models/Program.js Normal file
View File

@@ -0,0 +1,26 @@
const express = require("express");
const {
getAllDepartmentEmails,
getDepartmentsByProgram,
getEmailsByDepartment,
updateDepartmentEmails,
createDepartment,
deleteDepartment,
} = require("../controllers/departmentController");
const router = express.Router();
router.get("/programs", async (req, res) => {
const Program = require("../models/Program");
const programs = await Program.find();
res.json(programs);
});
router.get("/departments/:program", getDepartmentsByProgram);
router.get("/department-emails/:department", getEmailsByDepartment);
router.get("/department-emails", getAllDepartmentEmails);
router.put("/department-emails/:department", updateDepartmentEmails);
router.post("/department-emails", createDepartment);
router.delete("/department-emails/:department", deleteDepartment);
module.exports = router;

View File

@@ -0,0 +1,26 @@
const express = require("express");
const {
getAllDepartmentEmails,
getDepartmentsByProgram,
getEmailsByDepartment,
updateDepartmentEmails,
createDepartment,
deleteDepartment,
} = require("../controllers/departmentController");
const router = express.Router();
router.get("/programs", async (req, res) => {
const Program = require("../models/Program");
const programs = await Program.find();
res.json(programs);
});
router.get("/departments/:program", getDepartmentsByProgram);
router.get("/department-emails/:department", getEmailsByDepartment);
router.get("/department-emails", getAllDepartmentEmails);
router.put("/department-emails/:department", updateDepartmentEmails);
router.post("/department-emails", createDepartment);
router.delete("/department-emails/:department", deleteDepartment);
module.exports = router;

View File

@@ -0,0 +1,9 @@
const express = require("express");
const upload = require("../middleware/upload");
const { sendEmail } = require("../controllers/emailController");
const router = express.Router();
router.post("/send-email", upload.array("attachments"), sendEmail);
module.exports = router;

View File

@@ -0,0 +1,8 @@
const express = require("express");
const { getMeetings } = require("../controllers/meetingController");
const router = express.Router();
router.get("/meetings", getMeetings);
module.exports = router;

View File

@@ -1,185 +1,26 @@
//server.js
require("dotenv").config();
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const cors = require("cors");
const nodemailer = require("nodemailer");
const multer = require("multer");
const bodyParser = require("body-parser");
const path = require("path");
const moment = require("moment");
const connectDB = require("./config/db");
const emailRoutes = require("./routes/emailRoutes");
const meetingRoutes = require("./routes/meetingRoutes");
const departmentRoutes = require("./routes/departmentRoutes");
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use("/uploads", express.static("uploads"));
app.use("/uploads", express.static(path.join(__dirname, "uploads")));
// Multer setup for file uploads
const storage = multer.diskStorage({
destination: "uploads/",
filename: (req, file, cb) => {
cb(null, Date.now() + path.extname(file.originalname));
},
});
const upload = multer({ storage });
connectDB();
// MongoDB connection
mongoose
.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log("Connected to MongoDB"))
.catch((err) => console.error("MongoDB Connection Error:", err));
app.use("/api", emailRoutes);
app.use("/api", meetingRoutes);
app.use("/api", departmentRoutes);
// Meeting Schema
const meetingSchema = new mongoose.Schema({
program: String,
department: String,
subject: String,
body: String,
agenda: [String],
date: String,
startTime: String,
endTime: String,
recipients: [String],
attachments: [{ filename: String, path: String }],
});
const Meeting = mongoose.model("Meeting", meetingSchema);
app.get("/api/meetings", async (req, res) => {
try {
const meetings = await Meeting.find(); // Fetching all meetings
// Format agenda in HTML for each meeting
const formattedMeetings = meetings.map((meeting) => {
const formattedAgenda = meeting.agenda
? meeting.agenda.map((item, index) => `<li>${index + 1}. ${item}</li>`).join("")
: "<li>No agenda provided</li>";
return { ...meeting.toObject(), formattedAgenda }; // Add formatted agenda to each meeting
});
res.json(formattedMeetings); // Send formatted meetings
} catch (error) {
console.error("Error fetching meetings:", error);
res.status(500).json({ error: "Error fetching meetings" });
}
});
// API to send email
app.post("/api/send-email", upload.array("attachments"), async (req, res) => {
try {
const { program, department, subject, body, agenda, date, startTime, endTime, recipients } = req.body;
const files = req.files;
console.log({ program, department, subject, body, agenda, date, startTime, endTime, recipients, files });
if (!program || !department || !subject || !body || !date || !startTime || !endTime || !recipients) {
return res.status(400).json({ error: "All fields are required!" });
}
// Check for meeting conflicts
const conflict = await Meeting.findOne({
date,
startTime: { $lt: endTime },
endTime: { $gt: startTime },
});
if (conflict) {
// Return the details of the conflicting meeting
const conflictDetails = {
department: conflict.department,
date: conflict.date,
startTime: conflict.startTime,
endTime: conflict.endTime,
recipients: conflict.recipients,
subject: conflict.subject,
};
return res.status(409).json({
error: "Meeting conflict detected! Please choose a different time.",
conflictDetails
});
}
// Format date and time properly
const formattedDate = moment(date, "YYYY-MM-DD").format("dddd, MMMM Do YYYY");
const formattedStartTime = moment(startTime, "HH:mm").format("hh:mm A");
const formattedEndTime = moment(endTime, "HH:mm").format("hh:mm A");
// Format Google Calendar event link
const formattedStart = moment(`${date} ${startTime}`, "YYYY-MM-DD HH:mm").utc().format("YYYYMMDDTHHmmss[Z]");
const formattedEnd = moment(`${date} ${endTime}`, "YYYY-MM-DD HH:mm").utc().format("YYYYMMDDTHHmmss[Z]");
const zoomLink = "https://us05web.zoom.us/j/2655616202?pwd=S3d6RjVPNnhmQ3AzTlZsRC9GYmNHQT09";
const calendarLink = `https://calendar.google.com/calendar/render?action=TEMPLATE&text=${encodeURIComponent(
subject
)}&details=${encodeURIComponent(`${body}\nJoin Zoom Meeting: ${zoomLink}`)}&dates=${formattedStart}/${formattedEnd}`;
// Format agenda into pointwise list
const formattedAgenda = agenda
? agenda.map((item, index) => `<li>${index + 1}. ${item}</li>`).join("")
: "<li>No agenda provided</li>";
// Store meeting in DB
const newMeeting = new Meeting({
program,
department,
subject,
body,
agenda,
date,
startTime,
endTime,
recipients,
attachments: files.map((file) => ({ filename: file.filename, path: file.path })),
});
await newMeeting.save();
// Email transporter
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL,
pass: process.env.EMAIL_PASSWORD,
},
});
// Email options
const mailOptions = {
from: process.env.EMAIL,
to: recipients,
subject,
html: `
<p><strong>Respected Board Members,</strong></p>
<p><strong>Greetings from Somaiya Vidyavihar University !!</strong></p>
<p><strong>We are glad to invite you to the Board of Studies meeting: </strong></p>
<p>${body}</p>
<p><strong>Program:</strong> ${program}</p>
<p><strong>Department:</strong> ${department}</p>
<p><strong>Date:</strong> ${formattedDate}</p>
<p><strong>Time:</strong> ${formattedStartTime} - ${formattedEndTime}</p>
<p><strong>Zoom Meeting Details:</strong></p>
<p><strong>Meeting ID: </strong>265 561 6202</p>
<p><strong>Meeting Password: </strong>123456</p>
<p><strong>Zoom Meeting Link: </strong><a href="https://us05web.zoom.us/j/2655616202?pwd=S3d6RjVPNnhmQ3AzTlZsRC9GYmNHQT09" target="_blank">Join the Meeting</a></p>
<p><strong>Agenda:</strong></p>
<ul>${formattedAgenda}</ul>
<p><a href="${calendarLink}">📅 Click here to add this event to Google Calendar</a></p>
`,
attachments: files.map((file) => ({
filename: file.filename,
path: file.path,
})),
};
// Send email
await transporter.sendMail(mailOptions);
res.status(200).json({ message: "Email sent successfully!", calendarLink });
} catch (error) {
console.error("Error sending email:", error);
res.status(500).json({ error: "Error sending email" });
}
});
// Start server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,22 +1,25 @@
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Page1 from './Page1';
import Page1 from './Page1';
import Page2 from './Page2';
import Page3 from './Page3';
import Page4 from './Page4';
import Page5 from './Page5';
import { GoogleOAuthProvider } from '@react-oauth/google';
import Page3 from './Page3';
import Page4 from './Page4';
import Page5 from './Page5';
import Page6 from './Page6';
import { GoogleOAuthProvider } from '@react-oauth/google';
const App = () => {
return (
<GoogleOAuthProvider clientId="YOUR_GOOGLE_OAUTH_CLIENT_ID">
<GoogleOAuthProvider clientId="YOUR_GOOGLE_OAUTH_CLIENT_ID">
<Router>
<Routes>
<Route path="/" element={<Page1 />} />
<Route path="/page2" element={<Page2 />} />
<Route path="/page2" element={<Page2 />} />
<Route path="/page3" element={<Page3 />} />
<Route path="/page4" element={<Page4 />} />
<Route path="/page5" element={<Page5 />} />
<Route path="/page6" element={<Page6 />} />
</Routes>
</Router>
</GoogleOAuthProvider>

View File

@@ -1,3 +1,5 @@
/*Page1.css*/
/* General Styles */
* {
margin: 0;

View File

@@ -1,3 +1,5 @@
//Page1.js
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { GoogleLogin } from '@react-oauth/google'; // Import GoogleLogin component
@@ -31,7 +33,7 @@ const Page1 = () => {
// Allowed credentials
const allowedCredentials = [
{ email: "hodcomp@somaiya.edu", password: "hodcomp123!" },
{ email: "hodcomp@somaiya.edu", password: "hodcomp123" },
{ email: "director.kjsse@somaiya.edu", password: "directorkjsse2024" },
{ email: "dean.engg@somaiya.edu", password: "deanengg2024" },
{ email: "hodextc@somaiya.edu", password: "hodextc2024" },

View File

@@ -1,3 +1,5 @@
/*Page2.css*/
/* General Styles */
body {
margin: 0;

View File

@@ -1,3 +1,5 @@
//Page2.js
import React, { useState } from "react";
import { Link } from "react-router-dom";
import './Page2.css'; // CSS File import
@@ -30,7 +32,7 @@ const Page2 = () => {
<li><Link to="/page2">Home</Link></li>
<li><a href="#">Schedule</a></li>
<li><Link to="/page5">Meeting DataBase</Link></li>
<li><a href="#">Contact</a></li>
<li><Link to="/page6">Edit DataBase</Link></li>
<li><Link to="/">Log Out</Link></li>
</ul>
</nav>

View File

@@ -1,4 +1,5 @@
/* Page3.css */
.container {
width: 100%;
max-width: 600px;

View File

@@ -1,3 +1,5 @@
//Page3.js
import React, { useState } from "react";
import './Page3.css'

View File

@@ -1,3 +1,5 @@
/*Page4.css*/
/* Container for the content area */
.content-area {
width: 100%;

View File

@@ -1,28 +1,20 @@
//Page4.js
import React, { useState } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import './Page2.css';
import './Page4.css'; // Import the same CSS file for consistent styling
import { FaTimes } from "react-icons/fa"; // Red cross icon for agenda/removal
import './Page4.css';
import { FaTimes } from "react-icons/fa";
import image from './components/image.png';
import somaiyatrust from './components/somaiyatrust.png';
import redlines from './components/redlines.png';
const programs = ["BTech", "MTech", "PhD"];
const departments = [
"Artificial Intelligence & Data Science",
"Computer Engineering",
"Computer & Communication Engineering",
"Computer Science & Business Systems",
"Electronics Engineering (VLSI Design & Technology)",
"Electronics & Computer Engineering",
"Electronics & Telecommunication Engineering",
"Information Technology",
"Mechanical Engineering",
"Robotics & Artificial Intelligence",
];
import { useEffect } from "react";
const Page4 = () => {
const [programs, setPrograms] = useState([]);
const [departments, setDepartments] = useState([]);
const [departmentEmails, setDepartmentEmails] = useState([]);
const [program, setProgram] = useState("");
const [department, setDepartment] = useState("");
const [subject, setSubject] = useState("");
@@ -34,6 +26,33 @@ const Page4 = () => {
const [recipients, setRecipients] = useState([""]);
const [attachments, setAttachments] = useState([]);
useEffect(() => {
axios.get("http://localhost:5000/api/programs")
.then(response => setPrograms(response.data))
.catch(error => console.error("Error fetching programs:", error));
}, []);
useEffect(() => {
if (program) {
axios.get(`http://localhost:5000/api/departments/${program}`)
.then(response => setDepartments(response.data))
.catch(error => console.error("Error fetching departments:", error));
} else {
setDepartments([]);
}
}, [program]);
useEffect(() => {
if (department) {
axios.get(`http://localhost:5000/api/department-emails/${department}`)
.then(response => {
setDepartmentEmails(response.data.emails);
setRecipients([...response.data.emails]);
})
.catch(error => console.error("Error fetching department emails:", error));
}
}, [department]);
const addAgenda = () => setAgenda([...agenda, ""]);
const handleAgendaChange = (index, value) => {
const updatedAgenda = [...agenda];
@@ -86,7 +105,7 @@ const Page4 = () => {
} catch (err) {
if (err.response?.status === 409) {
const conflictDetails = err.response.data.conflictDetails;
alert(`Conflict Detected:
alert(`Conflict Detected :
Department: ${conflictDetails.department}
Date: ${conflictDetails.date}
Time: ${conflictDetails.startTime} - ${conflictDetails.endTime}
@@ -114,7 +133,7 @@ const Page4 = () => {
<li><Link to="/page2">Home</Link></li>
<li><Link to="/page3">Schedule</Link></li>
<li><Link to="/page5">Meeting DataBase</Link></li>
<li><a href="#">Contact</a></li>
<li><Link to="/page6">Edit DataBase</Link></li>
<li><Link to="/">Log Out</Link></li>
</ul>
</nav>
@@ -134,13 +153,11 @@ const Page4 = () => {
{/* Content Area (Right Side) */}
<div className="content-area">
<h1>BOS Email Management System</h1>
<label>Program:</label>
<label>Programme:</label>
<select onChange={(e) => setProgram(e.target.value)} value={program}>
<option value="">Choose a Program</option>
<option value="">Choose a Programme</option>
{programs.map((prog, idx) => (
<option key={idx} value={prog}>
{prog}
</option>
<option key={idx} value={prog.name}>{prog.name}</option>
))}
</select>

View File

@@ -1,141 +1,309 @@
/* Ensure body has margin 0 and full height for scrolling */
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
background-color: #f4f4f4;
min-height: 100vh;
overflow-y: auto; /* Ensure scrolling */
/*Page5.css*/
/* Base Styles */
:root {
--primary-red: #B7202E;
--dark-red: #8a0000;
--light-red: #d32f2f;
--white: #ffffff;
--light-gray: #f5f5f5;
--medium-gray: #e0e0e0;
--dark-gray: #757575;
--black: #212121;
--shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
}
/* Page Container */
.page-container {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: #f4f4f4;
}
html, body {
margin: 0;
padding: 0;
height: auto; /* Let height grow naturally */
overflow-x: hidden; /* Hide only horizontal scroll */
overflow-y: auto; /* Allow vertical scroll */
}
/* Header Bar */
.header-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
background-color: var(--white);
box-shadow: var(--shadow);
position: sticky;
top: 0;
z-index: 100;
}
.leftlogo, .rightlogo {
height: 50px;
width: auto;
}
.college-name {
color: var(--primary-red);
font-size: 1.5rem;
margin: 0;
}
/* Navigation Bar */
.navbar {
background-color: var(--primary-red);
padding: 0 20px;
}
.navbar ul {
list-style-type: none;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.navbar li {
padding: 15px 20px;
}
.navbar a {
color: var(--white);
text-decoration: none;
font-weight: 500;
transition: var(--transition);
}
.navbar a:hover, .navbar a.active {
color: var(--white);
text-decoration: underline;
text-underline-offset: 5px;
}
/* Main Content */
.meetings-page-container {
flex: 1;
padding: 30px;
max-width: 1400px;
margin: 40px auto 0 auto; /* 40px top margin to separate from navbar */
width: 100%;
}
.page-title {
text-align: center;
color: var(--primary-red);
margin-bottom: 30px;
font-size: 2rem;
}
.no-meetings {
text-align: center;
color: var(--dark-gray);
font-size: 1.2rem;
margin-top: 40px;
}
/* Meetings Grid Container */
.meetings-grid-container {
width: 100%;
overflow: hidden;
}
.meetings-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 25px;
padding: 10px;
}
/* Meeting Cards */
.meeting-card {
background-color: var(--white);
border-radius: 8px;
box-shadow: var(--shadow);
border-left: 4px solid var(--primary-red);
overflow: hidden;
transition: var(--transition);
display: flex;
flex-direction: column;
min-height: 150px;
}
.meeting-card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
}
.card-summary {
padding: 20px;
cursor: pointer;
}
.meeting-title {
color: var(--black);
font-size: 1.2rem;
margin: 0 0 10px 0;
line-height: 1.3;
}
.meeting-meta {
display: flex;
justify-content: space-between;
color: var(--dark-gray);
font-size: 0.9rem;
margin-bottom: 8px;
}
.meeting-department {
color: var(--primary-red);
font-weight: 500;
font-size: 0.9rem;
}
.card-details {
padding: 20px;
background-color: var(--light-gray);
border-top: 1px solid var(--medium-gray);
flex: 1;
}
.detail-row {
display: flex;
margin-bottom: 10px;
line-height: 1.4;
}
.detail-label {
font-weight: 600;
color: var(--primary-red);
min-width: 100px;
}
.detail-section {
margin: 20px 0;
}
.detail-section h3 {
color: var(--primary-red);
font-size: 1.1rem;
margin-bottom: 10px;
border-bottom: 1px solid var(--medium-gray);
padding-bottom: 5px;
}
.agenda-list, .attachments-list {
padding-left: 20px;
margin: 10px 0;
}
.agenda-list li, .attachments-list li {
margin-bottom: 8px;
line-height: 1.4;
}
.attachments-list a {
color: var(--primary-red);
text-decoration: none;
transition: var(--transition);
}
.attachments-list a:hover {
text-decoration: underline;
}
.recipients-list {
word-break: break-all;
}
/* Calendar Button */
.calendar-button {
display: block;
background-color: var(--primary-red);
color: var(--white) !important;
text-align: center;
padding: 10px;
border-radius: 5px;
margin-top: 20px;
text-decoration: none;
transition: var(--transition);
}
.calendar-button:hover {
background-color: var(--dark-red);
}
/* Footer */
footer {
background-color: var(--primary-red);
color: var(--white);
padding: 20px;
text-align: center;
}
.terms {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
}
.terms a {
color: var(--white);
text-decoration: none;
}
.footer-image {
height: 30px;
}
/* Responsive Design */
@media (max-width: 1024px) {
.meetings-grid {
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
/* Main container for the meetings */
.meetings-container {
background-color: #ffffff;
border-radius: 10px; /* Rounded corners */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border: 1px solid #e0e0e0; /* Light border for the box */
box-sizing: border-box;
max-width: 1200px;
margin: 20px auto; /* Added margin to ensure some space from top */
padding: 20px;
}
/* Heading for the page */
h1 {
text-align: center;
font-size: 2rem;
margin-bottom: 20px;
}
/* List of meetings */
.meetings-list {
display: flex;
}
@media (max-width: 768px) {
.header-bar {
flex-direction: column;
gap: 20px;
}
/* Styling for each meeting box */
.meeting-box {
background-color: #ffffff;
padding: 20px;
border-radius: 10px; /* Rounded corners */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-left: 5px solid #B7202E; /* Changed to the new color */
overflow: hidden; /* Prevents overflow */
box-sizing: border-box;
}
/* Styling for meeting titles */
.meeting-box h2 {
font-size: 1.5rem;
color: #333;
margin-bottom: 10px;
}
/* Styling for meeting details like Program, Date, etc. */
.meeting-box p {
font-size: 1rem;
color: #555;
}
/* Styling for links like Google Calendar and attachments */
.meeting-box a {
text-decoration: none;
color: #B7202E; /* Changed to the new color */
font-weight: bold;
display: inline-block;
margin-top: 10px;
}
.meeting-box a:hover {
text-decoration: underline;
}
/* Button for Google Calendar link */
.google-calendar-link {
margin-top: 10px;
display: inline-block;
padding: 10px 20px;
background-color: #B7202E; /* Changed to the new color */
color: white;
border-radius: 5px;
text-align: center;
}
.google-calendar-link:hover {
background-color: #9e1c25; /* Darkened shade of the same color */
}
/* Ensure that meetings overflow properly on the page */
body {
display: flex;
flex-direction: column;
justify-content: flex-start;
overflow-y: auto;
}
.meeting-box ul {
list-style-type: none;
padding-left: 0;
}
.meeting-box ul li {
font-size: 1rem;
color: #444;
margin-bottom: 5px;
}
/* Responsive Design for smaller screens */
@media (max-width: 768px) {
.meeting-box {
padding: 15px;
}
.meeting-box h2 {
font-size: 1.2rem;
}
.meeting-box p,
.meeting-box ul li {
font-size: 0.9rem;
}
.google-calendar-link {
font-size: 0.9rem;
}
padding: 10px;
}
/* Button for Google Calendar link */
.google-calendar-link {
margin-top: 10px;
display: inline-block;
padding: 10px 20px;
background-color: #B7202E; /* Red color */
color: white !important; /* Text color set to white */
border-radius: 5px;
text-align: center;
.college-name {
margin: 10px 0;
font-size: 1.2rem;
}
.google-calendar-link:hover {
background-color: #9e1c25; /* Darkened shade of red on hover */
}
.navbar li {
padding: 10px 15px;
}
.meetings-page-container {
padding: 20px 15px;
}
}
@media (max-width: 480px) {
.meetings-grid {
grid-template-columns: 1fr;
}
.detail-row {
flex-direction: column;
}
.detail-label {
margin-bottom: 5px;
}
.meeting-meta {
flex-direction: column;
gap: 5px;
}
}

View File

@@ -1,83 +1,164 @@
//Page5.js
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
import './Page5.css';
import somaiyatrust from './components/somaiyatrust.png';
import image from './components/image.png';
import redlines from './components/redlines.png';
const Page5 = () => {
const [meetings, setMeetings] = useState([]);
const [expandedIndex, setExpandedIndex] = useState(null);
// Fetch meetings data from the server
useEffect(() => {
axios.get('http://localhost:5000/api/meetings') // Updated to backend URL
axios.get('http://localhost:5000/api/meetings')
.then((response) => {
setMeetings(response.data);
// Sort meetings by date (newest first) and time
const sortedMeetings = response.data.sort((a, b) => {
const dateA = new Date(`${a.date}T${a.startTime}`);
const dateB = new Date(`${b.date}T${b.startTime}`);
return dateB - dateA;
});
setMeetings(sortedMeetings);
})
.catch((error) => {
console.error('Error fetching meetings:', error);
});
}, []);
const toggleExpand = (index) => {
setExpandedIndex(prev => (prev === index ? null : index));
};
return (
<div className="meetings-container">
<h1>Meeting Details</h1>
<div className="meetings-list">
<div className="page-container">
{/* Header Bar */}
<header className="header-bar">
<img src={image} alt="Left Logo" className="leftlogo" />
<h1 className="college-name">Somaiya Scheduler</h1>
<img src={somaiyatrust} alt="Right Logo" className="rightlogo" />
</header>
{/* Navigation Bar */}
<nav className="navbar">
<ul>
<li><Link to="/page2">Home</Link></li>
<li><Link to="/page3">Schedule</Link></li>
<li><Link to="/page5" className="active">Meeting DataBase</Link></li>
<li><Link to="/page6">Manage Emails</Link></li>
<li><a href="#">Contact</a></li>
<li><Link to="/">Log Out</Link></li>
</ul>
</nav>
{/* Main Content */}
<div className="meetings-page-container">
<h1 className="page-title">Meeting Details</h1>
{meetings.length === 0 ? (
<p>No meetings available</p>
<p className="no-meetings">No meetings available</p>
) : (
meetings.map((meeting, index) => (
<div key={index} className="meeting-box">
<h2>{meeting.subject}</h2>
<p><strong>Program:</strong> {meeting.program}</p>
<p><strong>Department:</strong> {meeting.department}</p>
<p><strong>Date:</strong> {meeting.date}</p>
<p><strong>Time:</strong> {meeting.startTime} - {meeting.endTime}</p>
<div className="meetings-grid-container">
<div className="meetings-grid">
{meetings.map((meeting, index) => (
<div
key={index}
className={`meeting-card ${expandedIndex === index ? 'expanded' : ''}`}
>
<div className="card-summary" onClick={() => toggleExpand(index)}>
<h2 className="meeting-title">{meeting.subject}</h2>
<div className="meeting-meta">
<span className="meeting-date">{meeting.date}</span>
<span className="meeting-time">{meeting.startTime} - {meeting.endTime}</span>
</div>
<div className="meeting-department">{meeting.department}</div>
</div>
<p><strong>Agenda:</strong></p>
{meeting.agenda && meeting.agenda.length > 0 ? (
<ul>
{meeting.agenda.map((item, idx) => (
<li key={idx}>{item}</li>
))}
</ul>
) : (
<p>No agenda provided</p>
)}
{expandedIndex === index && (
<div className="card-details">
<div className="detail-row">
<span className="detail-label">Program:</span>
<span>{meeting.program}</span>
</div>
<p><strong>Body:</strong> {meeting.body}</p>
<div className="detail-section">
<h3>Agenda</h3>
{meeting.agenda && meeting.agenda.length > 0 ? (
<ul className="agenda-list">
{meeting.agenda.map((item, idx) => (
<li key={idx}>{item}</li>
))}
</ul>
) : (
<p>No agenda provided</p>
)}
</div>
<p><strong>Recipients:</strong> {meeting.recipients}</p>
<div className="detail-section">
<h3>Details</h3>
<p>{meeting.body}</p>
</div>
{/* Display Attachments if available */}
<p><strong>Attachments:</strong></p>
{meeting.attachments && meeting.attachments.length > 0 ? (
<ul>
{meeting.attachments.map((file, idx) => (
<li key={idx}>
<a href={`/uploads/${file.filename}`} target="_blank" rel="noopener noreferrer">
{file.filename}
<div className="detail-section">
<h3>Recipients</h3>
<div className="recipients-list">
{meeting.recipients.join(', ')}
</div>
</div>
<div className="detail-section">
<h3>Attachments</h3>
{meeting.attachments && meeting.attachments.length > 0 ? (
<ul className="attachments-list">
{meeting.attachments.map((file, idx) => (
<li key={idx}>
<a
href={`/uploads/${file.filename}`}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
>
{file.filename}
</a>
</li>
))}
</ul>
) : (
<p>No attachments available</p>
)}
</div>
<a
href={`https://calendar.google.com/calendar/render?action=TEMPLATE&text=${encodeURIComponent(
meeting.subject
)}&details=${encodeURIComponent(meeting.body)}&dates=${meeting.date}T${meeting.startTime}/${meeting.date}T${meeting.endTime}`}
target="_blank"
rel="noopener noreferrer"
className="calendar-button"
onClick={(e) => e.stopPropagation()}
>
Add to Google Calendar
</a>
</li>
))}
</ul>
) : (
<p>No attachments available</p>
)}
<a
href={`https://calendar.google.com/calendar/render?action=TEMPLATE&text=${encodeURIComponent(
meeting.subject
)}&details=${encodeURIComponent(meeting.body)}&dates=${meeting.date}T${meeting.startTime}/${meeting.date}T${meeting.endTime}`}
target="_blank"
rel="noopener noreferrer"
className="google-calendar-link"
>
Add to Google Calendar
</a>
</div>
)}
</div>
))}
</div>
))
</div>
)}
</div>
{/* Footer */}
<footer>
<div className="terms">
<a href="#">Terms and Policies</a>
<img src={redlines} alt="Footer Logo" className="footer-image" />
</div>
</footer>
</div>
);
};
export default Page5;
export default Page5;

307
frontend/src/Page6.css Normal file
View File

@@ -0,0 +1,307 @@
/*Page6.css*/
/* Root Variables (optional, for consistency) */
:root {
--primary-red: #B7202E;
--dark-red: #8a0000;
--white: #ffffff;
--light-gray: #f5f5f5;
--medium-gray: #e0e0e0;
--dark-gray: #757575;
--shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
}
/* Page Container */
.page-container {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: #f4f4f4;
}
/* Header Bar */
.header-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
background-color: var(--white);
box-shadow: var(--shadow);
position: sticky;
top: 0;
z-index: 100;
}
.leftlogo, .rightlogo {
height: 50px;
width: auto;
}
.college-name {
color: var(--primary-red);
font-size: 1.5rem;
margin: 0;
text-align: center;
}
/* Navbar */
.navbar {
background-color: var(--primary-red);
padding: 0 20px;
}
.navbar ul {
list-style: none;
display: flex;
justify-content: center;
flex-wrap: wrap;
margin: 0;
padding: 0;
}
.navbar li {
padding: 15px 20px;
}
.navbar a {
color: var(--white);
text-decoration: none;
font-weight: 500;
transition: var(--transition);
}
.navbar a:hover, .navbar a.active {
text-decoration: underline;
text-underline-offset: 5px;
}
/* Main Content */
.email-management-container {
flex: 1;
padding: 30px;
max-width: 1400px;
margin: 0 auto;
width: 100%;
}
.email-management-container h1 {
text-align: center;
color: var(--primary-red);
margin-bottom: 30px;
font-size: 2rem;
}
/* Search Bar */
.search-bar {
margin-bottom: 20px;
}
.search-bar input {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
}
/* Grid Layout */
.departments-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 25px;
padding: 10px;
}
/* Department Card Style */
.department-card {
background-color: var(--white);
border-radius: 8px;
box-shadow: var(--shadow);
border-left: 4px solid var(--primary-red);
padding: 20px;
transition: var(--transition);
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 150px;
}
.department-card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
}
.department-card h3 {
margin-top: 0;
color: var(--primary-red);
font-size: 1.1rem;
border-bottom: 1px solid var(--medium-gray);
padding-bottom: 8px;
}
.department-card ul {
list-style: none;
padding-left: 0;
margin-bottom: 10px;
}
.department-card li {
padding: 5px 0;
border-bottom: 1px solid var(--medium-gray);
color: var(--dark-gray);
font-size: 0.95rem;
}
/* Actions */
.department-actions {
display: flex;
gap: 10px;
margin-top: 10px;
}
.edit-btn, .delete-btn, .add-department-btn, .save-btn, .cancel-btn {
display: flex;
align-items: center;
gap: 5px;
padding: 8px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.edit-btn {
background-color: #4CAF50;
color: white;
}
.delete-btn {
background-color: #f44336;
color: white;
}
.add-department-btn {
background-color: #2196F3;
color: white;
margin-bottom: 20px;
}
/* Edit Form */
.edit-form {
background-color: var(--light-gray);
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input {
width: 100%;
padding: 8px;
border: 1px solid var(--medium-gray);
border-radius: 4px;
}
.email-input-group {
display: flex;
gap: 10px;
margin-bottom: 10px;
align-items: center;
}
.email-input-group input {
flex: 1;
}
.remove-email {
background: none;
border: none;
color: #f44336;
cursor: pointer;
font-size: 16px;
}
.add-email {
background-color: #2196F3;
color: white;
border: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
gap: 5px;
}
.form-actions {
display: flex;
gap: 10px;
margin-top: 20px;
}
/* Footer */
footer {
background-color: var(--primary-red);
color: var(--white);
padding: 20px;
text-align: center;
}
.terms {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
}
.terms a {
color: var(--white);
text-decoration: none;
}
.footer-image {
height: 30px;
}
/* Responsive */
@media (max-width: 1024px) {
.departments-list {
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
}
@media (max-width: 768px) {
.header-bar {
flex-direction: column;
padding: 10px;
}
.college-name {
margin: 10px 0;
font-size: 1.2rem;
}
.navbar li {
padding: 10px 15px;
}
.email-management-container {
padding: 20px 15px;
}
}
@media (max-width: 480px) {
.departments-list {
grid-template-columns: 1fr;
}
}

281
frontend/src/Page6.js Normal file
View File

@@ -0,0 +1,281 @@
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
import { FaEdit, FaTrash, FaPlus, FaSave, FaTimes } from 'react-icons/fa';
import './Page6.css';
import image from './components/image.png';
import somaiyatrust from './components/somaiyatrust.png';
import redlines from './components/redlines.png';
const Page6 = () => {
const [departments, setDepartments] = useState([]);
const [programs, setPrograms] = useState([]);
const [newDepartment, setNewDepartment] = useState('');
const [newEmails, setNewEmails] = useState(['']);
const [selectedProgram, setSelectedProgram] = useState('');
const [editingDepartment, setEditingDepartment] = useState(null);
const [isAddingNew, setIsAddingNew] = useState(false);
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
fetchDepartments();
fetchPrograms();
}, []);
const fetchDepartments = async () => {
try {
const response = await axios.get('http://localhost:5000/api/department-emails');
setDepartments(response.data);
} catch (error) {
console.error('Error fetching departments:', error);
}
};
const fetchPrograms = async () => {
try {
const response = await axios.get('http://localhost:5000/api/programs');
setPrograms(response.data);
} catch (error) {
console.error('Error fetching programs:', error);
}
};
const handleAddEmail = () => {
setNewEmails([...newEmails, '']);
};
const handleNewEmailChange = (index, value) => {
const updatedEmails = [...newEmails];
updatedEmails[index] = value;
setNewEmails(updatedEmails);
};
const handleRemoveEmail = (index) => {
const updatedEmails = newEmails.filter((_, i) => i !== index);
setNewEmails(updatedEmails);
};
const startEditing = (department) => {
setEditingDepartment(department);
setNewEmails([...department.emails]);
setIsAddingNew(false);
};
const cancelEditing = () => {
setEditingDepartment(null);
setIsAddingNew(false);
setNewDepartment('');
setNewEmails(['']);
setSelectedProgram('');
};
const saveDepartment = async () => {
try {
const emailsToSave = newEmails.filter(email => email.trim() !== '');
if (emailsToSave.length === 0) {
alert('At least one email is required');
return;
}
if (isAddingNew) {
if (!newDepartment.trim()) {
alert('Department name is required');
return;
}
if (!selectedProgram) {
alert('Please select a program');
return;
}
await axios.post('http://localhost:5000/api/department-emails', {
department: newDepartment,
emails: emailsToSave,
program: selectedProgram,
});
} else {
await axios.put(`http://localhost:5000/api/department-emails/${editingDepartment.department}`, {
emails: emailsToSave
});
}
fetchDepartments();
cancelEditing();
} catch (error) {
console.error('Error saving department:', error);
alert('Error saving department. Please try again.');
}
};
const deleteDepartment = async (departmentName) => {
if (window.confirm(`Are you sure you want to delete ${departmentName}?`)) {
try {
await axios.delete(`http://localhost:5000/api/department-emails/${departmentName}`);
fetchDepartments();
} catch (error) {
console.error('Error deleting department:', error);
alert('Error deleting department. Please try again.');
}
}
};
const filteredDepartments = departments.filter(dept =>
dept.department.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<div className="page-container">
{/* Header Bar */}
<header className="header-bar">
<img src={image} alt="Left Logo" className="leftlogo" />
<h1 className="college-name">Somaiya Scheduler</h1>
<img src={somaiyatrust} alt="Right Logo" className="rightlogo" />
</header>
{/* Navigation Bar */}
<nav className="navbar">
<ul>
<li><Link to="/page2">Home</Link></li>
<li><Link to="/page3">Schedule</Link></li>
<li><Link to="/page5">Meeting DataBase</Link></li>
<li><Link to="/page6">Manage Emails</Link></li>
<li><a href="#">Contact</a></li>
<li><Link to="/">Log Out</Link></li>
</ul>
</nav>
{/* Main Content */}
<div className="email-management-container">
<h1>Department Email Management</h1>
<div className="search-bar">
<input
type="text"
placeholder="Search departments..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
{editingDepartment || isAddingNew ? (
<div className="edit-form">
<h2>{isAddingNew ? 'Add New Department' : `Editing: ${editingDepartment.department}`}</h2>
{isAddingNew && (
<>
<div className="form-group">
<label>Program:</label>
<select
value={selectedProgram}
onChange={(e) => setSelectedProgram(e.target.value)}
>
<option value="">Select Program</option>
{programs.map((prog, idx) => (
<option key={idx} value={prog.name}>{prog.name}</option>
))}
</select>
</div>
<div className="form-group">
<label>Department Name:</label>
<input
type="text"
value={newDepartment}
onChange={(e) => setNewDepartment(e.target.value)}
placeholder="Enter department name"
/>
</div>
</>
)}
<div className="form-group">
<label>Emails:</label>
{newEmails.map((email, index) => (
<div key={index} className="email-input-group">
<input
type="email"
value={email}
onChange={(e) => handleNewEmailChange(index, e.target.value)}
placeholder="Enter email"
/>
{newEmails.length > 1 && (
<button
type="button"
className="remove-email"
onClick={() => handleRemoveEmail(index)}
>
<FaTimes />
</button>
)}
</div>
))}
<button
type="button"
className="add-email"
onClick={handleAddEmail}
>
<FaPlus /> Add Email
</button>
</div>
<div className="form-actions">
<button type="button" className="save-btn" onClick={saveDepartment}>
<FaSave /> Save
</button>
<button type="button" className="cancel-btn" onClick={cancelEditing}>
<FaTimes /> Cancel
</button>
</div>
</div>
) : (
<>
<button
className="add-department-btn"
onClick={() => {
setIsAddingNew(true);
setEditingDepartment(null);
}}
>
<FaPlus /> Add New Department
</button>
<div className="departments-list">
{filteredDepartments.length === 0 ? (
<p>No departments found</p>
) : (
filteredDepartments.map((dept) => (
<div key={dept._id} className="department-card">
<h3>{dept.department}</h3>
<ul>
{dept.emails.map((email, idx) => (
<li key={idx}>{email}</li>
))}
</ul>
<div className="department-actions">
<button onClick={() => startEditing(dept)} className="edit-btn">
<FaEdit /> Edit
</button>
<button onClick={() => deleteDepartment(dept.department)} className="delete-btn">
<FaTrash /> Delete
</button>
</div>
</div>
))
)}
</div>
</>
)}
</div>
{/* Footer */}
<footer>
<div className="terms">
<a href="#">Terms and Policies</a>
<img src={redlines} alt="Footer Logo" className="footer-image" />
</div>
</footer>
</div>
);
};
export default Page6;