From ad9e5bcc73e0efbe221661c18af75f4214cf2c28 Mon Sep 17 00:00:00 2001 From: AAKARSH LOHANI Date: Tue, 30 Jul 2024 12:34:30 +0530 Subject: [PATCH 1/2] Commit message for all changes --- .gitignore | 5 +- Back-end/Controllers/Email.controller.js | 61 ------- Back-end/Models/Auth.model.js | 8 + Back-end/Models/Template.model.js | 10 ++ Back-end/email-backend/config/emailConfig.js | 11 +- .../controllers/historyController.js | 5 +- .../controllers/templateController.js | 16 ++ .../controllers/userController.js | 85 ++++++---- Back-end/email-backend/models/emailHistory.js | 11 ++ Back-end/email-backend/models/template.js | 9 + Back-end/email-backend/models/userModel.js | 9 + .../routes/emailHistoryRouter.js | 4 + Back-end/email-backend/routes/emailRoutes.js | 11 +- Back-end/email-backend/routes/index.js | 33 ++-- .../email-backend/routes/templateRoutes.js | 19 +++ .../services/nodemailerService.js | 14 ++ Back-end/email-backend/views/index.html | 60 +------ .../email-backend/views/view1test/index.html | 25 --- .../email-backend/views/view1test/script.js | 36 ---- .../email-backend/views/view1test/styles.css | 24 --- Back-end/middlewares/authenticated.js | 14 ++ Back-end/middlewares/error.js | 46 ++--- Front-end/public/docs/documentation.html | 75 +++++++++ .../configure-template.component.css | 114 +++++++++++++ .../configure-template.component.html | 40 ++++- .../configure-template.component.ts | 71 +++++++- .../configure-template.service.ts | 17 ++ .../app/email-module/docs/docs.component.css | 73 ++++++++ .../app/email-module/docs/docs.component.html | 159 ++++++++++++++++++ .../email-module/docs/docs.component.spec.ts | 23 +++ .../app/email-module/docs/docs.component.ts | 12 ++ .../email-menu/email-menu.component.html | 4 + .../email-menu/email-menu.component.ts | 9 +- 33 files changed, 826 insertions(+), 287 deletions(-) delete mode 100644 Back-end/Controllers/Email.controller.js delete mode 100644 Back-end/email-backend/views/view1test/index.html delete mode 100644 Back-end/email-backend/views/view1test/script.js delete mode 100644 Back-end/email-backend/views/view1test/styles.css create mode 100644 Front-end/public/docs/documentation.html create mode 100644 Front-end/src/app/email-module/configure-template/configure-template.service.ts create mode 100644 Front-end/src/app/email-module/docs/docs.component.css create mode 100644 Front-end/src/app/email-module/docs/docs.component.html create mode 100644 Front-end/src/app/email-module/docs/docs.component.spec.ts create mode 100644 Front-end/src/app/email-module/docs/docs.component.ts diff --git a/.gitignore b/.gitignore index 38a9666..5fffb8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ node_modules -/.angular/cache \ No newline at end of file +/.angular/cache +Front-end/.angular +.vscode/extensions.json +Front-end/.vscode/extensions.json \ No newline at end of file diff --git a/Back-end/Controllers/Email.controller.js b/Back-end/Controllers/Email.controller.js deleted file mode 100644 index 6ea3803..0000000 --- a/Back-end/Controllers/Email.controller.js +++ /dev/null @@ -1,61 +0,0 @@ -import nodemailer from 'nodemailer'; -import Template from '../Models/Template.model.js'; - -const transporter = nodemailer.createTransport({ - service: 'gmail', - auth: { - user: process.env.EMAIL, - pass: process.env.EMAIL_PASSWORD, - }, -}); - -/** - * @desc Send Email to array of users of particular tempalte - * @route POST /api/email - * @access Private - * - */ - -const sendEmail = async (req, res) => { - try { - const { templateId , emails } = req.body; - - const template = await Template.findOne({ templateId }); - - if (!template) { - return res.status(404).json({ - success: false, - message: 'Template not found', - }); - } - - const mailOptions = { - from: process.env.EMAIL, - to: emails, - subject: template.Subject, - html: template.Content, - }; - - transporter.sendMail(mailOptions, (error, info) => { - if (error) { - return res.status(500).json({ - success: false, - message: error.message, - }); - } - return res.status(200).json({ - success: true, - message: 'Email sent', - data: info, - }); - }); - } catch (error) { - return res.status(500).json({ - success: false, - message: error.message, - }); - } -}; - - -export { sendEmail }; \ No newline at end of file diff --git a/Back-end/Models/Auth.model.js b/Back-end/Models/Auth.model.js index 19e50e8..886929e 100644 --- a/Back-end/Models/Auth.model.js +++ b/Back-end/Models/Auth.model.js @@ -1,6 +1,14 @@ import mongoose from "mongoose"; import bcrypt from "bcryptjs"; +/** + * Represents the authentication schema for the user. + * + * @typedef {Object} AuthSchema + * @property {string} email - The email of the user. + * @property {string} password - The password of the user. + * @property {string} [role=Admin] - The role of the user. Defaults to "Admin". + */ const AuthSchema = new mongoose.Schema({ email: { type: String, diff --git a/Back-end/Models/Template.model.js b/Back-end/Models/Template.model.js index 6e9428f..ee935b9 100644 --- a/Back-end/Models/Template.model.js +++ b/Back-end/Models/Template.model.js @@ -13,6 +13,16 @@ const variable = { } } +/** + * TemplateSchema represents the schema for a template in the application. + * + * @typedef {Object} TemplateSchema + * @property {String} templateId - The unique identifier for the template. + * @property {String} Subject - The subject of the template. + * @property {String} Content - The content of the template. + * @property {String} type - The type of the template (email, sms, whatsapp). + * @property {Array} variables - An array of variables used in the template. + */ const TemplateSchema = new mongoose.Schema({ templateId: { type: String, diff --git a/Back-end/email-backend/config/emailConfig.js b/Back-end/email-backend/config/emailConfig.js index 84aaf3b..6845917 100644 --- a/Back-end/email-backend/config/emailConfig.js +++ b/Back-end/email-backend/config/emailConfig.js @@ -1,15 +1,20 @@ import NodemailerService from "../services/nodemailerService.js"; import SendgridService from "../services/sendgridService.js"; +/** + * Function to get the appropriate email service based on the environment variable. + * @returns {Object} - An instance of the selected email service. + * @throws {Error} - If the EMAIL_SERVICE environment variable is invalid. +*/ -const getEmailService=()=>{ - switch (process.env.EMAIL_SERVICE){ +const getEmailService = () => { + switch (process.env.EMAIL_SERVICE) { case 'nodemailer': return new NodemailerService(); case 'sendgrid': return new SendgridService(); default: - throw new Error("Invalid Email service selected") + throw new Error("Invalid Email service selected"); } }; diff --git a/Back-end/email-backend/controllers/historyController.js b/Back-end/email-backend/controllers/historyController.js index 91df175..2dd62a9 100644 --- a/Back-end/email-backend/controllers/historyController.js +++ b/Back-end/email-backend/controllers/historyController.js @@ -1,6 +1,9 @@ import EmailHistory from '../models/emailHistory.js'; +/** + * Fetch all email history from the database. + * @returns {Array} - An array of email history records. +*/ -// Fetch all history from the database export const getHistory = async () => { try { const history = await EmailHistory.find(); diff --git a/Back-end/email-backend/controllers/templateController.js b/Back-end/email-backend/controllers/templateController.js index ac82f95..6f42455 100644 --- a/Back-end/email-backend/controllers/templateController.js +++ b/Back-end/email-backend/controllers/templateController.js @@ -1,6 +1,10 @@ import EmailTemplate from '../models/template.js'; // Fetch all templates from the database +/** + * Retrieves all email templates from the database. + * @returns {Promise} A promise that resolves to an array of email templates. +*/ export const getTemplates = async () => { try { const templates = await EmailTemplate.find(); @@ -12,6 +16,11 @@ export const getTemplates = async () => { }; // Select a template by ID +/** + * Retrieves a template by its ID. + * @param {string} id - The ID of the template to retrieve. + * @returns {Promise} - A promise that resolves to the template object if found, or null if not found. +*/ export const getTemplateById = async (id) => { try { const template = await EmailTemplate.findById(id); @@ -23,6 +32,13 @@ export const getTemplateById = async (id) => { }; // Create a new template +/** + * Creates a new email template. + * @param {string} name - The name of the template. + * @param {string} subject - The subject of the email template. + * @param {string} body - The body content of the email template. + * @returns {Promise} - A promise that resolves to the newly created template object, or null if an error occurs. +*/ export const createTemplate = async (name, subject, body) => { const newTemplate = new EmailTemplate({ name, diff --git a/Back-end/email-backend/controllers/userController.js b/Back-end/email-backend/controllers/userController.js index 825ebdc..960a725 100644 --- a/Back-end/email-backend/controllers/userController.js +++ b/Back-end/email-backend/controllers/userController.js @@ -1,6 +1,13 @@ import User from '../models/userModel.js'; // Controller to fetch all users +/** + * Get all users. + * @param {Object} req - The request object. + * @param {Object} res - The response object. + * @returns {Promise} - A promise that resolves with the fetched users or an error message. +*/ + export const getUsers = async (req, res) => { try { const users = await User.find(); @@ -10,35 +17,53 @@ export const getUsers = async (req, res) => { res.status(500).json({ message: 'Failed to fetch users' }); } }; - // Controller to create a new user + + + +// Controller to create a new user +/** + * Create a new user. + * @param {Object} req - The request object. + * @param {Object} res - The response object. + * @returns {Promise} - A promise that resolves when the user is created. +*/ + export const createUser = async (req, res) => { - const { name, email, otherField } = req.body; - - const newUser = new User({ - name, - email, - otherField - }); - - try { - const savedUser = await newUser.save(); - res.status(201).json(savedUser); - } catch (error) { - console.error('Error creating user:', error); - res.status(500).json({ message: 'Failed to create user' }); - } - }; - - // Controller to upload user data - export const uploadUserData = async (req, res) => { - try { - const users = req.body; // Assuming req.body is an array of users - - const savedUsers = await User.insertMany(users); - res.status(201).json(savedUsers); - } catch (error) { - console.error('Error uploading users:', error); - res.status(500).json({ message: 'Failed to upload users' }); - } - }; + const { name, email, otherField } = req.body; + + const newUser = new User({ + name, + email, + otherField + }); + + try { + const savedUser = await newUser.save(); + res.status(201).json(savedUser); + } catch (error) { + console.error('Error creating user:', error); + res.status(500).json({ message: 'Failed to create user' }); + } +}; + + +// Controller to upload user data +/** + * Uploads user data to the database. + * @param {Object} req - The request object. + * @param {Object} res - The response object. + * @returns {Promise} - A Promise that resolves when the user data is uploaded. +*/ + +export const uploadUserData = async (req, res) => { + try { + const users = req.body; // Assuming req.body is an array of users + + const savedUsers = await User.insertMany(users); + res.status(201).json(savedUsers); + } catch (error) { + console.error('Error uploading users:', error); + res.status(500).json({ message: 'Failed to upload users' }); + } +}; diff --git a/Back-end/email-backend/models/emailHistory.js b/Back-end/email-backend/models/emailHistory.js index cd66f88..cb93526 100644 --- a/Back-end/email-backend/models/emailHistory.js +++ b/Back-end/email-backend/models/emailHistory.js @@ -1,5 +1,16 @@ import mongoose from 'mongoose'; +/** + * Represents the schema for email history. + * + * @typedef {Object} EmailHistorySchema + * @property {string} username - The username associated with the email. + * @property {string} email - The email address. + * @property {string} status - The status of the email. + * @property {string} time - The time when the email was sent. + * @property {string} date - The date when the email was sent. + */ + const emailHistorySchema = new mongoose.Schema({ username: String, email: String, diff --git a/Back-end/email-backend/models/template.js b/Back-end/email-backend/models/template.js index d4466dd..339daca 100644 --- a/Back-end/email-backend/models/template.js +++ b/Back-end/email-backend/models/template.js @@ -1,5 +1,14 @@ import mongoose from 'mongoose'; +/** + * Represents a template schema. + * + * @typedef {Object} TemplateSchema + * @property {string} name - The name of the template. + * @property {string} subject - The subject of the template. + * @property {string} body - The body of the template. + * @property {Date} createdAt - The creation date of the template. + */ const templateSchema = new mongoose.Schema({ name: { type: String, diff --git a/Back-end/email-backend/models/userModel.js b/Back-end/email-backend/models/userModel.js index bb1818b..a7dd1fd 100644 --- a/Back-end/email-backend/models/userModel.js +++ b/Back-end/email-backend/models/userModel.js @@ -1,5 +1,14 @@ import mongoose from 'mongoose'; +/** + * Represents the user schema. + * + * @typedef {Object} UserSchema + * @property {string} name - The name of the user. + * @property {string} email - The email of the user. + * @property {number} age - The age of the user. + * @property {string} address - The address of the user. + */ const userSchema = new mongoose.Schema({ name: String, email: String, diff --git a/Back-end/email-backend/routes/emailHistoryRouter.js b/Back-end/email-backend/routes/emailHistoryRouter.js index 146c977..d10355c 100644 --- a/Back-end/email-backend/routes/emailHistoryRouter.js +++ b/Back-end/email-backend/routes/emailHistoryRouter.js @@ -6,6 +6,10 @@ const router = express.Router(); // Route to fetch all history router.get('/history', async (req, res) => { try { + /** + * Represents the email history. + * @type {Array} + */ const history = await getHistory(); res.status(200).json(history); } catch (error) { diff --git a/Back-end/email-backend/routes/emailRoutes.js b/Back-end/email-backend/routes/emailRoutes.js index b26cde1..6c5bffe 100644 --- a/Back-end/email-backend/routes/emailRoutes.js +++ b/Back-end/email-backend/routes/emailRoutes.js @@ -30,7 +30,7 @@ router.post('/', async (req, res) => { to, subject: selectedTemplate.subject, text: selectedTemplate.body, - html: selectedTemplate.bodyHtml, // Assuming you have an HTML body in your template model + html: selectedTemplate.bodyHtml, }; } else { // Default email content @@ -53,6 +53,15 @@ router.post('/', async (req, res) => { //Log the email history in case of Success + /** + * Represents the email data object. + * @typedef {Object} EmailData + * @property {string} username - The username of the recipient. + * @property {string} email - The email address of the recipient. + * @property {string} status - The status of the email (e.g., 'Success', 'Failed'). + * @property {string} time - The time the email was sent in the format 'HH:MM:SS'. + * @property {string} date - The date the email was sent in the format 'MM/DD/YYYY'. + */ const emailData = { username: recipient, email: recipient, diff --git a/Back-end/email-backend/routes/index.js b/Back-end/email-backend/routes/index.js index 24b0f92..510a515 100644 --- a/Back-end/email-backend/routes/index.js +++ b/Back-end/email-backend/routes/index.js @@ -1,24 +1,30 @@ -import express from 'express'; -import emailRoutes from './emailRoutes.js'; -import templateRoutes from './templateRoutes.js'; +import express from 'express'; +import emailRoutes from './emailRoutes.js'; +import templateRoutes from './templateRoutes.js'; import emailHistoryRouter from './emailHistoryRouter.js'; -import path from 'path'; -import { fileURLToPath } from 'url'; -import { getUsers, createUser, uploadUserData } from '../controllers/userController.js'; -import userRoutes from './userRoutes.js'; - +import path from 'path'; +import { fileURLToPath } from 'url'; +import { getUsers, createUser, uploadUserData } from '../controllers/userController.js'; +import userRoutes from './userRoutes.js'; +// Get the filename and directory name of the current module const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); + +// Create a new router instance const router = express.Router(); +// Route to serve the index.html file router.get('/', (req, res) => { - res.sendFile(path.join(__dirname,"..", 'views', 'index.html')); + res.sendFile(path.join(__dirname, "..", 'views', 'index.html')); }); +// Use email routes for /send-email path router.use('/send-email', emailRoutes); +// Use template routes for /templates path router.use('/templates', templateRoutes); +// Use user routes for /users path router.use('/users', userRoutes); // Route to fetch all users @@ -26,15 +32,13 @@ router.get('/users', getUsers); // Route to create a new user router.post('/users', createUser); - // Route to upload user data router.post('/upload-users', uploadUserData); - -// Route to fetch History +// Route to fetch email history router.get('/history', emailHistoryRouter); - +// Export the router as the default export export default router; @@ -42,6 +46,3 @@ export default router; - - - diff --git a/Back-end/email-backend/routes/templateRoutes.js b/Back-end/email-backend/routes/templateRoutes.js index e3c297b..c28387f 100644 --- a/Back-end/email-backend/routes/templateRoutes.js +++ b/Back-end/email-backend/routes/templateRoutes.js @@ -6,6 +6,14 @@ const router = express.Router(); // Route to create a new template router.post('/', async (req, res) => { const { name, subject, body } = req.body; + /** + * Creates a new template. + * + * @param {string} name - The name of the template. + * @param {string} subject - The subject of the template. + * @param {string} body - The body of the template. + * @returns {Promise} - A promise that resolves to the newly created template. + */ const newTemplate = await createTemplate(name, subject, body); if (newTemplate) { res.status(201).json(newTemplate); @@ -17,6 +25,10 @@ router.post('/', async (req, res) => { // Route to fetch all templates router.get('/', async (req, res) => { try { + /** + * Retrieves the templates. + * @returns {Promise} The templates. + */ const templates = await getTemplates(); res.status(200).json(templates); } catch (error) { @@ -30,6 +42,13 @@ router.get('/', async (req, res) => { router.get('/:id', async (req, res) => { const templateId = req.params.id; try { + /** + * Represents a template object. + * @typedef {Object} Template + * @property {string} templateId - The ID of the template. + * @property {string} content - The content of the template. + * @property {string} subject - The subject of the template. + */ const template = await getTemplateById(templateId); if (template) { res.status(200).json(template); diff --git a/Back-end/email-backend/services/nodemailerService.js b/Back-end/email-backend/services/nodemailerService.js index 347aefd..9d4f9dd 100644 --- a/Back-end/email-backend/services/nodemailerService.js +++ b/Back-end/email-backend/services/nodemailerService.js @@ -2,6 +2,10 @@ import nodemailer from 'nodemailer'; import EmailService from './emailService.js'; +/** + * Represents a service for sending emails using Nodemailer. + * @extends EmailService + */ class NodemailerService extends EmailService{ constructor(){ super(); @@ -13,6 +17,16 @@ class NodemailerService extends EmailService{ } }); } + /** + * Sends an email using Nodemailer. + * + * @param {Object} options - The email options. + * @param {string} options.to - The recipient's email address. + * @param {string} options.subject - The email subject. + * @param {string} options.text - The plain text version of the email. + * @param {string} options.html - The HTML version of the email. + * @returns {Promise} A promise that resolves when the email is sent. + */ async sendEmail({ to, subject, text, html }){ const mailOptions = { from: process.env.EMAIL_USER, diff --git a/Back-end/email-backend/views/index.html b/Back-end/email-backend/views/index.html index 73a599d..5ae5b29 100644 --- a/Back-end/email-backend/views/index.html +++ b/Back-end/email-backend/views/index.html @@ -3,67 +3,9 @@ - Testing Features + Smart Email Notifier Features Welcome to Smart Email Notifier - \ No newline at end of file diff --git a/Back-end/email-backend/views/view1test/index.html b/Back-end/email-backend/views/view1test/index.html deleted file mode 100644 index 46e40bd..0000000 --- a/Back-end/email-backend/views/view1test/index.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - Email Sender - - - - Welcome to Smart Email Notifier - - - \ No newline at end of file diff --git a/Back-end/email-backend/views/view1test/script.js b/Back-end/email-backend/views/view1test/script.js deleted file mode 100644 index c4d6436..0000000 --- a/Back-end/email-backend/views/view1test/script.js +++ /dev/null @@ -1,36 +0,0 @@ -document.getElementById('loadCsv').addEventListener('click', function() { - const input = document.getElementById('csvFileInput'); - if (input.files.length > 0) { - const reader = new FileReader(); - reader.onload = function(e) { - const rows = e.target.result.split('\n').map(row => row.split(',')); - displayCsvData(rows); - }; - reader.readAsText(input.files[0]); - } -}); - -function displayCsvData(rows) { - const preview = document.getElementById('data-preview'); - preview.innerHTML = '' + rows.map(row => '' + row.map(cell => ``).join('') + '').join('') + '
${cell}
'; -} - -document.getElementById('fetchTemplates').addEventListener('click', function() { - fetch('/api/templates') // Adjust the URL to your API endpoint - .then(response => response.json()) - .then(templates => { - const templateList = document.getElementById('templateList'); - templateList.innerHTML = templates.map(template => `
  • ${template.name}
  • `).join(''); - templateList.addEventListener('click', function(e) { - if (e.target.tagName === 'LI') { - const selectedTemplate = templates.find(template => template.id.toString() === e.target.dataset.id); - document.getElementById('email-preview').textContent = selectedTemplate.content; // Assuming 'content' is part of your template object - } - }); - }); -}); - -document.getElementById('sendEmail').addEventListener('click', function() { - // Here, you would gather the necessary data, such as the selected template and CSV data, and send it to your backend for emailing - // This step is highly dependent on your backend API and the structure of your data -}); \ No newline at end of file diff --git a/Back-end/email-backend/views/view1test/styles.css b/Back-end/email-backend/views/view1test/styles.css deleted file mode 100644 index d645cb4..0000000 --- a/Back-end/email-backend/views/view1test/styles.css +++ /dev/null @@ -1,24 +0,0 @@ -body { - font-family: Arial, sans-serif; -} - -#data-preview, #email-preview { - margin-top: 20px; - border: 1px solid #ccc; - padding: 10px; -} - -#templateList { - list-style-type: none; - padding: 0; -} - -#templateList li { - cursor: pointer; - padding: 5px; - border-bottom: 1px solid #eee; -} - -#templateList li:hover { - background-color: #f0f0f0; -} \ No newline at end of file diff --git a/Back-end/middlewares/authenticated.js b/Back-end/middlewares/authenticated.js index c8ea13f..0d9ca09 100644 --- a/Back-end/middlewares/authenticated.js +++ b/Back-end/middlewares/authenticated.js @@ -1,5 +1,13 @@ import jwt from "jsonwebtoken"; +/** + * Middleware function to verify the authentication token. + * + * @param {Object} req - The request object. + * @param {Object} res - The response object. + * @param {Function} next - The next middleware function. + * @returns {Object} - The response object. + */ const verify = async (req, res, next) => { let token; token = req.cookies.jwt; @@ -23,6 +31,12 @@ const verify = async (req, res, next) => { } }; +/** + * Middleware function to authorize user roles. + * + * @param {Array} roles - An array of roles that are authorized to access the route. + * @returns {Function} - Middleware function that checks if the user's role is authorized. + */ const roleAuthorization = (roles) => { return (req, res, next) => { if (roles.includes(req.user.role) ) { diff --git a/Back-end/middlewares/error.js b/Back-end/middlewares/error.js index d4cae4b..00c69cb 100644 --- a/Back-end/middlewares/error.js +++ b/Back-end/middlewares/error.js @@ -1,20 +1,28 @@ const notfound = (req, res, next) => { - const error = new Error(`Not Found - ${req.originalUrl}`); - res.status(404); - next(error); - }; - - const errorHandler = (err, req, res, next) => { - const statusCode = res.statusCode === 200 ? 500 : res.statusCode; - let message = err.message; - if (err.name === "CastError" && err.kind === "ObjectId") { - message = "Resource not found"; - statusCode = 404; - } - res.status(statusCode).json({ - message, - stack: process.env.NODE_ENV === "production" ? null : err.stack, - }); - }; - - export {errorHandler,notfound}; \ No newline at end of file + const error = new Error(`Not Found - ${req.originalUrl}`); + res.status(404); + next(error); +}; + +/** + * Error handler middleware. + * + * @param {Error} err - The error object. + * @param {Object} req - The request object. + * @param {Object} res - The response object. + * @param {Function} next - The next middleware function. + */ +const errorHandler = (err, req, res, next) => { + const statusCode = res.statusCode === 200 ? 500 : res.statusCode; + let message = err.message; + if (err.name === "CastError" && err.kind === "ObjectId") { + message = "Resource not found"; + statusCode = 404; + } + res.status(statusCode).json({ + message, + stack: process.env.NODE_ENV === "production" ? null : err.stack, + }); +}; + +export { errorHandler, notfound }; \ No newline at end of file diff --git a/Front-end/public/docs/documentation.html b/Front-end/public/docs/documentation.html new file mode 100644 index 0000000..4b30f3b --- /dev/null +++ b/Front-end/public/docs/documentation.html @@ -0,0 +1,75 @@ + + + + + + Documentation + + +

    Smart Email Notifier Documentation

    + +

    Overview

    +

    The Email Service is designed to streamline the process of sending notifications and managing email templates within an application. It provides a flexible interface for creating, configuring, and sending emails, as well as viewing user data and email history.

    + +

    Components

    + +

    Main Content Area

    +

    The main content area dynamically displays content based on the selected menu option. It supports the following functionalities:

    +
      +
    • Welcome Screen: A default view welcoming users to the Smart Email Notifier.
    • +
    • Create Template: Interface for creating new email templates.
    • +
    • Configure Template: Allows users to configure existing email templates.
    • +
    • View User Data: Displays user data relevant to the email service.
    • +
    • Upload User Data: Provides an option to upload user data for email targeting.
    • +
    • Send Email: Interface for sending emails using configured templates.
    • +
    • History: Shows the history of sent emails for tracking and auditing purposes.
    • +
    • Docs: Displays the documentation to understand the platform interface and learn about the project.
    • +
    + +

    Usage

    + +

    Navigation

    +

    Users can navigate through the service using a menu that changes the selectedMenu variable. This variable controls which component is displayed in the main content area.

    + +

    Creating a Template

    +
      +
    1. Select "Create Template" from the menu.
    2. +
    3. Fill in the template details in the provided form.
    4. +
    5. Submit the form to save the template.
    6. +
    + +

    Configuring a Template

    +
      +
    1. Select "Configure Template" from the menu.
    2. +
    3. Choose a template from the list of existing templates.
    4. +
    5. Make the necessary changes in the configuration interface.
    6. +
    7. Save the changes to update the template.
    8. +
    + +

    Viewing User Data

    +

    Select "View User Data" from the menu to display a list of user data relevant to the email campaigns.

    + +

    Uploading User Data

    +
      +
    1. Select "Upload User Data" from the menu.
    2. +
    3. Use the interface to upload user data files.
    4. +
    5. Confirm the upload to make the data available for email targeting.
    6. +
    + +

    Sending an Email

    +
      +
    1. Select "Send Email" from the menu.
    2. +
    3. Choose a template and configure the necessary details.
    4. +
    5. Preview the email before sending.
    6. +
    7. Submit the form to send the email to the selected recipients.
    8. +
    + +

    Viewing History

    +

    Select "History" from the menu to view a log of sent emails, including timestamps and recipient details.

    + +

    Conclusion

    +

    The Smart Email Notifier provides a comprehensive suite of tools for managing email notifications within an application. By leveraging its components, users can efficiently create, send, and track emails, ensuring effective communication with their audience.

    + + + + diff --git a/Front-end/src/app/email-module/configure-template/configure-template.component.css b/Front-end/src/app/email-module/configure-template/configure-template.component.css index e69de29..de152b7 100644 --- a/Front-end/src/app/email-module/configure-template/configure-template.component.css +++ b/Front-end/src/app/email-module/configure-template/configure-template.component.css @@ -0,0 +1,114 @@ +/* General container styling */ +.template-container, +.preview-container { + margin: 2rem; + +} + +.preview-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +/* Flexbox container for cards */ +.template-container { + display: flex; + flex-wrap: wrap; + gap: 1rem; +} + +/* Card container */ +.card { + flex: 1 1 calc(33.333% - 1rem); + /* Adjust to have 3 columns */ + border: 1px solid #ccc; + border-radius: 8px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + margin-bottom: 1rem; + cursor: pointer; + transition: transform 0.2s, box-shadow 0.2s; + height: 200px; + /* Set a fixed height */ + display: flex; + flex-direction: column; + justify-content: space-between; + overflow: auto; +} + +/* Card hover effect */ +.card:hover { + transform: translateY(-5px); + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); +} + +/* Card body */ +.card-body { + padding: 1rem; + display: flex; + flex-direction: column; + justify-content: space-between; + height: 100%; +} + +/* Card title */ +.card-title { + font-size: 1.25rem; + font-weight: bold; + margin-bottom: 0.5rem; +} + +/* Card text */ +.card-text { + margin-bottom: 1rem; +} + +/* Button styling */ +.btn { + background-color: #007bff; + color: #fff; + border: none; + padding: 0.5rem 1rem; + border-radius: 4px; + transition: background-color 0.2s; +} + +.btn:hover { + background-color: #0056b3; +} + +/* Preview container */ +.preview-container { + + border: 1px solid #ccc; + border-radius: 8px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + padding: 1rem; + margin-top: 2rem; + overflow: auto; +} + +/* Preview title */ +.preview-container h2 { + margin-bottom: 1rem; +} + +/* Add some spacing around the card elements */ +.card+.card { + margin-top: 1rem; +} + +/* Define the base alert class */ +.alert { + padding: 20px; + background-color: #f44336; + /* Red */ + color: white; + margin-bottom: 15px; +} + +/* Define the success variant */ +.alert-success { + background-color: #4CAF50; + /* Green */ +} \ No newline at end of file diff --git a/Front-end/src/app/email-module/configure-template/configure-template.component.html b/Front-end/src/app/email-module/configure-template/configure-template.component.html index e57766c..14f41b8 100644 --- a/Front-end/src/app/email-module/configure-template/configure-template.component.html +++ b/Front-end/src/app/email-module/configure-template/configure-template.component.html @@ -1,2 +1,38 @@ -

    configure-template works!

    - \ No newline at end of file +

    Select an Email Template to configure

    +
    +
    +
    +
    {{ template.name }}
    +

    {{ template.subject }}

    + +
    +
    +
    + +
    +
    +

    Configure Template

    + +
    + +
    +
    +
    {{ selectedTemplate.name }}
    +

    {{ selectedTemplate.subject }}

    +

    {{ selectedTemplate.body }}

    + +
    +
    +
    + +
    +

    Edit Template

    +
    +
    + + + + +
    +
    +
    \ No newline at end of file diff --git a/Front-end/src/app/email-module/configure-template/configure-template.component.ts b/Front-end/src/app/email-module/configure-template/configure-template.component.ts index 78dc240..69d1d47 100644 --- a/Front-end/src/app/email-module/configure-template/configure-template.component.ts +++ b/Front-end/src/app/email-module/configure-template/configure-template.component.ts @@ -1,13 +1,76 @@ -import { Component } from '@angular/core'; +import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; import { RouterModule } from '@angular/router'; +import { HttpClient } from '@angular/common/http'; +import { CommonModule } from '@angular/common'; +import { TemplateService } from './configure-template.service'; // Service to handle API calls +import { FormsModule } from '@angular/forms'; @Component({ selector: 'app-configure-template', standalone: true, - imports: [RouterModule], + imports: [RouterModule, CommonModule,FormsModule], templateUrl: './configure-template.component.html', styleUrl: './configure-template.component.css' }) -export class ConfigureTemplateComponent { +export class ConfigureTemplateComponent implements OnInit { -} + templates : any[] = []; // Array to hold templates + selectedTemplate: any = null; // Object to hold the selected template + isEditing = false; // Flag to toggle edit mode + + constructor(private http: HttpClient, private templateService: TemplateService) {} + + ngOnInit(): void { + this.fetchTemplates(); + } + + @ViewChild('previewSection') previewSection!: ElementRef; + @ViewChild('editSection') editSection!: ElementRef; + + fetchTemplates() { + this.http.get('http://localhost:5000/api/email/templates') + .subscribe({ + next: (response: any) => { + this.templates = response; + }, + error: (error) => { + console.error('Error fetching templates:', error); + } + }); + } + + selectTemplate(template: any) { + this.selectedTemplate = template; + this.isEditing = false; + } + + previewTemplate(template: any, event: Event) { + event.stopPropagation(); // Prevent the outer click event + this.selectTemplate(template); + setTimeout(() => { + this.previewSection.nativeElement.scrollIntoView({ behavior: 'smooth' }); + }, 0); + } + + editTemplate() { + this.isEditing = true; + setTimeout(() => { + this.editSection.nativeElement.scrollIntoView({ behavior: 'smooth' }); + }, 0); + } + + saveTemplate() { + if (this.selectedTemplate) { + this.templateService.updateTemplate(this.selectedTemplate).subscribe( + response => { + alert('Template updated successfully!'); + this.isEditing = false; + }, + error => { + console.error('Error updating template', error); + alert('Failed to update template.'); + } + ); + } + } +} \ No newline at end of file diff --git a/Front-end/src/app/email-module/configure-template/configure-template.service.ts b/Front-end/src/app/email-module/configure-template/configure-template.service.ts new file mode 100644 index 0000000..7c8e2a6 --- /dev/null +++ b/Front-end/src/app/email-module/configure-template/configure-template.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + + +@Injectable({ + providedIn: 'root' +}) +export class TemplateService { + private apiUrl = 'http://localhost:5000/api/email'; + + constructor(private http: HttpClient) {} + + updateTemplate(template: any): Observable { + return this.http.put(`${this.apiUrl}/templates/${template.id}`, template); + } +} \ No newline at end of file diff --git a/Front-end/src/app/email-module/docs/docs.component.css b/Front-end/src/app/email-module/docs/docs.component.css new file mode 100644 index 0000000..0a668cf --- /dev/null +++ b/Front-end/src/app/email-module/docs/docs.component.css @@ -0,0 +1,73 @@ +/* Container for the documentation content */ +.docs-content-container { + padding: 20px; + background-color: #f9f9f9; + border: 1px solid #ddd; + border-radius: 5px; + margin: 20px 0; + font-family: Arial, sans-serif; +} + +/* General text styling */ +.docs-content-container p { + font-size: 16px; + line-height: 1.6; + color: #333; + margin-bottom: 15px; +} + +/* Code block styling */ +.docs-content-container code { + background-color: #f4f4f4; + padding: 2px 4px; + border-radius: 3px; + font-family: monospace; +} + +/* Heading styles */ +.docs-content-container h1 { + color: #0056b3; + font-size: 28px; + margin-bottom: 20px; + text-align: center; +} + +.docs-content-container h2 { + color: #0056b3; + font-size: 24px; + margin-top: 30px; + margin-bottom: 15px; +} + +.docs-content-container h3 { + color: #0056b3; + font-size: 20px; + margin-top: 20px; + margin-bottom: 10px; +} + +/* List styles */ +.docs-content-container ul, +.docs-content-container ol { + margin: 10px 0 20px 20px; + padding-left: 20px; +} + +.docs-content-container li { + margin-bottom: 10px; +} + +/* Strong text styling */ +.docs-content-container strong { + color: #333; + font-weight: bold; +} + +/* Div styling for sections */ +.docs-content-container div { + margin-bottom: 20px; + padding: 15px; + border: 1px solid #ccc; + border-radius: 5px; + background-color: #fff; +} \ No newline at end of file diff --git a/Front-end/src/app/email-module/docs/docs.component.html b/Front-end/src/app/email-module/docs/docs.component.html new file mode 100644 index 0000000..110a1b8 --- /dev/null +++ b/Front-end/src/app/email-module/docs/docs.component.html @@ -0,0 +1,159 @@ +
    +

    Smart Email Notifier Documentation

    + +

    Overview

    +
    +

    The Email Service is designed to streamline the process of sending notifications and managing email templates + within an application. It provides a flexible interface for creating, configuring, and sending emails, as + well + as viewing user data and email history.

    +
    + +

    Tech Stack

    +
    +

    This project uses the MEAN stack:

    +
      +
    • MongoDB
    • +
    • Express.js
    • +
    • Angular
    • +
    • Node.js
    • +
    +
    + +

    Provider

    +
    +

    The service provider for sending emails in this project is Nodemailer.

    +
    + + +

    Why We Are Using These Technologies

    +
    +

    + MEAN Stack: We are using the MEAN stack because it allows for a full-stack JavaScript + solution, + which means both the client-side and server-side use the same language. This simplifies development and + maintenance. +

    +

    + Nodemailer: We are using Nodemailer because it provides a simple and flexible way to send + emails from our Node.js application. It supports various transport methods and allows for customization of + email + content. +

    +
    + + +

    How We Are Using These Technologies

    +
    + MEAN Stack: +
      +
    • MongoDB is used as the database to store application data.
    • +
    • Express.js is used as the web application framework to build the server-side logic.
    • +
    • Angular is used to build the client-side application and manage the user interface.
    • +
    • Node.js is used as the runtime environment to execute server-side code.
    • +
    +
    + + +
    + Nodemailer: +
      +
    • We set up Nodemailer with a transporter configuration to connect to an email service provider (e.g., + Gmail). +
    • +
    • We define email options such as sender, recipient, subject, and body content.
    • +
    • We use the `sendMail` method to send emails from our Node.js application.
    • +
    +
    + + +

    Components

    + + +

    Main Content Area

    +
    +

    The main content area dynamically displays content based on the selected menu option. It supports the + following + functionalities:

    +
      +
    • Welcome Screen: A default view welcoming users to the Smart Email Notifier.
    • +
    • Create Template: Interface for creating new email templates.
    • +
    • Configure Template: Allows users to configure existing email templates.
    • +
    • View User Data: Displays user data relevant to the email service.
    • +
    • Upload User Data: Provides an option to upload user data for email targeting.
    • +
    • Send Email: Interface for sending emails using configured templates.
    • +
    • History: Shows the history of sent emails for tracking and auditing purposes.
    • +
    • Docs: Displays the documentation to understand the platform interface and learn about + the project.
    • +
    + +
    +

    Usage

    + +

    Navigation

    +
    +

    Users can navigate through the service using a menu that changes the selectedMenu variable. This + variable controls which component is displayed in the main content area.

    +
    + + +

    Creating a Template

    +
    +
      +
    1. Select "Create Template" from the menu.
    2. +
    3. Fill in the template details in the provided form.
    4. +
    5. Submit the form to save the template.
    6. +
    +
    + + +

    Configuring a Template

    +
    +
      +
    1. Select "Configure Template" from the menu.
    2. +
    3. Choose a template from the list of existing templates.
    4. +
    5. Make the necessary changes in the configuration interface.
    6. +
    7. Save the changes to update the template.
    8. +
    +
    + +

    Viewing User Data

    +
    +

    Select "View User Data" from the menu to display a list of user data relevant to the email campaigns.

    +
    + + +

    Uploading User Data

    +
    +
      +
    1. Select "Upload User Data" from the menu.
    2. +
    3. Use the interface to upload user data files.
    4. +
    5. Confirm the upload to make the data available for email targeting.
    6. +
    +
    + + +

    Sending an Email

    +
    +
      +
    1. Select "Send Email" from the menu.
    2. +
    3. Choose a template and configure the necessary details.
    4. +
    5. Preview the email before sending.
    6. +
    7. Submit the form to send the email to the selected recipients.
    8. +
    +
    + + +

    Viewing History

    +
    +

    Select "History" from the menu to view a log of sent emails, including timestamps and recipient details.

    +
    + + +

    Conclusion

    +
    +

    The Smart Email Notifier provides a comprehensive suite of tools for managing email notifications within an + application. By leveraging its components, users can efficiently create, send, and track emails, ensuring + effective communication with their audience.

    +
    +
    \ No newline at end of file diff --git a/Front-end/src/app/email-module/docs/docs.component.spec.ts b/Front-end/src/app/email-module/docs/docs.component.spec.ts new file mode 100644 index 0000000..e2de9f1 --- /dev/null +++ b/Front-end/src/app/email-module/docs/docs.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DocsComponent } from './docs.component'; + +describe('DocsComponent', () => { + let component: DocsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DocsComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DocsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/Front-end/src/app/email-module/docs/docs.component.ts b/Front-end/src/app/email-module/docs/docs.component.ts new file mode 100644 index 0000000..218508c --- /dev/null +++ b/Front-end/src/app/email-module/docs/docs.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-docs', + standalone: true, + imports: [], + templateUrl: './docs.component.html', + styleUrl: './docs.component.css' +}) +export class DocsComponent { + +} diff --git a/Front-end/src/app/email-module/email-menu/email-menu.component.html b/Front-end/src/app/email-module/email-menu/email-menu.component.html index 066921c..10ee93f 100644 --- a/Front-end/src/app/email-module/email-menu/email-menu.component.html +++ b/Front-end/src/app/email-module/email-menu/email-menu.component.html @@ -23,6 +23,9 @@ + @@ -37,6 +40,7 @@ + diff --git a/Front-end/src/app/email-module/email-menu/email-menu.component.ts b/Front-end/src/app/email-module/email-menu/email-menu.component.ts index 2b2c575..b17ac58 100644 --- a/Front-end/src/app/email-module/email-menu/email-menu.component.ts +++ b/Front-end/src/app/email-module/email-menu/email-menu.component.ts @@ -7,21 +7,20 @@ import { UploadUserDataComponent } from "../upload-user-data/upload-user-data.co import { SendEmailComponent } from "../send-email/send-email.component"; import { HistoryComponent } from "../history/history.component"; import { ViewUserDataComponent } from "../view-user-data/view-user-data.component"; +import { DocsComponent } from '../docs/docs.component'; @Component({ selector: 'app-email-menu', standalone: true, - imports: [RouterModule,CommonModule, ConfigureTemplateComponent, CreateTemplateComponent, UploadUserDataComponent, SendEmailComponent, HistoryComponent, ViewUserDataComponent], + imports: [RouterModule, CommonModule, DocsComponent, ConfigureTemplateComponent, CreateTemplateComponent, UploadUserDataComponent, SendEmailComponent, HistoryComponent, ViewUserDataComponent], templateUrl: './email-menu.component.html', styleUrl: './email-menu.component.css' }) export class EmailMenuComponent { - selectedMenu: string = 'default'; + selectedMenu: string = 'docs'; selectMenu(option: string) { - this.selectedMenu = option; - - + this.selectedMenu = option; } } From 4849b9dea2e21c7d4a7d8a1fbffd2d983b422629 Mon Sep 17 00:00:00 2001 From: AAKARSH LOHANI Date: Wed, 31 Jul 2024 11:19:53 +0530 Subject: [PATCH 2/2] Push all changes till Midpoint --- .../controllers/templateController.js | 26 +++++++ Back-end/email-backend/routes/emailRoutes.js | 10 +-- .../email-backend/routes/templateRoutes.js | 31 +++++++- Front-end/public/docs/documentation.html | 7 +- .../configure-template.component.css | 68 +++++++++++++++--- .../configure-template.component.html | 7 +- .../configure-template.component.ts | 26 ++++--- .../configure-template.service.ts | 6 +- .../app/email-module/docs/docs.component.html | 9 ++- .../app/email-module/email-module.module.ts | 3 +- .../send-email/send-email.component.css | 22 +++++- .../send-email/send-email.component.html | 10 +++ .../send-email/send-email.component.ts | 42 +++++++++-- .../view-user-data/user-selection.service.ts | 25 +++++++ .../view-user-data.component.css | 72 +++++++++++++++++++ .../view-user-data.component.html | 32 ++++++--- .../view-user-data.component.ts | 64 +++++++++++++++-- 17 files changed, 403 insertions(+), 57 deletions(-) create mode 100644 Front-end/src/app/email-module/view-user-data/user-selection.service.ts diff --git a/Back-end/email-backend/controllers/templateController.js b/Back-end/email-backend/controllers/templateController.js index 6f42455..4e3973f 100644 --- a/Back-end/email-backend/controllers/templateController.js +++ b/Back-end/email-backend/controllers/templateController.js @@ -54,3 +54,29 @@ export const createTemplate = async (name, subject, body) => { return null; } }; + + +/** + * Updates an existing email template. + * @param {Object} template - The template object to update. + * @returns {Promise} - A promise that resolves to the updated template object, or null if an error occurs. + */ +export const updateTemplate = async (template) => { + try { + const existingTemplate = await EmailTemplate.findById(template._id); + if (!existingTemplate) { + console.error('Template not found in update'); + return null; + } + + existingTemplate.name = template.name; + existingTemplate.subject = template.subject; + existingTemplate.body = template.body; + + await existingTemplate.save(); + return existingTemplate; + } catch (error) { + console.error('Error updating template:', error); + return null; + } +}; \ No newline at end of file diff --git a/Back-end/email-backend/routes/emailRoutes.js b/Back-end/email-backend/routes/emailRoutes.js index 6c5bffe..fd2525c 100644 --- a/Back-end/email-backend/routes/emailRoutes.js +++ b/Back-end/email-backend/routes/emailRoutes.js @@ -12,7 +12,7 @@ export const saveEmailStatus = async (emailData) => { }; // Route to send an email with selected or default template router.post('/', async (req, res) => { - const { to, templateId } = req.body; + const { to,body, templateId } = req.body; try { let emailContent = {}; @@ -29,13 +29,13 @@ router.post('/', async (req, res) => { emailContent = { to, subject: selectedTemplate.subject, - text: selectedTemplate.body, + text: body, html: selectedTemplate.bodyHtml, }; } else { // Default email content emailContent = { - to: to || 'aakarshsolar@gmail.com', // Use provided 'to' or default email + to: to , // Use provided 'to' or default email subject: 'Test Email', text: 'This is a default test email, no template selected', html: '

    This is a default test email, no template selected.

    ', @@ -47,7 +47,7 @@ router.post('/', async (req, res) => { // Ensure 'to' is always defined for logging - const recipient = to || 'aakarshsolar@gmail.com'; + const recipient = to ; @@ -83,7 +83,7 @@ router.post('/', async (req, res) => { //Log the email history in case of Failure // Ensure 'to' is always defined for logging - const recipient = to || 'aakarshsolar@gmail.com'; + const recipient = to ; const emailData = { username: recipient, diff --git a/Back-end/email-backend/routes/templateRoutes.js b/Back-end/email-backend/routes/templateRoutes.js index c28387f..d580821 100644 --- a/Back-end/email-backend/routes/templateRoutes.js +++ b/Back-end/email-backend/routes/templateRoutes.js @@ -1,5 +1,7 @@ import express from 'express'; -import { createTemplate, getTemplates, getTemplateById } from '../controllers/templateController.js'; +import { createTemplate, getTemplates, getTemplateById , updateTemplate} from '../controllers/templateController.js'; +import mongoose from 'mongoose'; +import EmailTemplate from '../models/template.js'; const router = express.Router(); @@ -53,7 +55,7 @@ router.get('/:id', async (req, res) => { if (template) { res.status(200).json(template); } else { - res.status(404).json({ message: 'Template not found' }); + res.status(404).json({ message: 'Template not found by get' }); } } catch (error) { console.error('Error fetching template by ID:', error); @@ -61,4 +63,29 @@ router.get('/:id', async (req, res) => { } }); +// Route to update a template by ID +router.put('/:id', async (req, res) => { + const templateId = req.params.id; + const templateData = req.body; + try { + console.log('Updating template with ID:', templateId); + console.log('Template data:', templateData); + + // Validate templateId + if (!mongoose.Types.ObjectId.isValid(templateId)) { + return res.status(400).json({ message: 'Invalid template ID' }); + } + + const updatedTemplate = await EmailTemplate.findByIdAndUpdate(templateId, templateData, { new: true }); + if (updatedTemplate) { + res.status(200).json(updatedTemplate); + } else { + res.status(404).json({ message: 'Template not found' }); + } + } catch (error) { + console.error('Error updating template by ID:', error); + res.status(500).json({ message: 'Failed to update template', error: error.message }); + } +}); + export default router; diff --git a/Front-end/public/docs/documentation.html b/Front-end/public/docs/documentation.html index 4b30f3b..174fa56 100644 --- a/Front-end/public/docs/documentation.html +++ b/Front-end/public/docs/documentation.html @@ -16,14 +16,13 @@

    Components

    Main Content Area

    The main content area dynamically displays content based on the selected menu option. It supports the following functionalities:

      -
    • Welcome Screen: A default view welcoming users to the Smart Email Notifier.
    • Create Template: Interface for creating new email templates.
    • Configure Template: Allows users to configure existing email templates.
    • -
    • View User Data: Displays user data relevant to the email service.
    • +
    • View User Data: Displays user data relevant to the email service and let us select users.
    • Upload User Data: Provides an option to upload user data for email targeting.
    • Send Email: Interface for sending emails using configured templates.
    • History: Shows the history of sent emails for tracking and auditing purposes.
    • -
    • Docs: Displays the documentation to understand the platform interface and learn about the project.
    • +
    • Docs: (default) Displays the documentation to understand the platform interface and learn about the project.

    Usage

    @@ -47,7 +46,7 @@

    Configuring a Template

    Viewing User Data

    -

    Select "View User Data" from the menu to display a list of user data relevant to the email campaigns.

    +

    Select "View User Data" from the menu to display a list of user data and select users to send email.

    Uploading User Data

      diff --git a/Front-end/src/app/email-module/configure-template/configure-template.component.css b/Front-end/src/app/email-module/configure-template/configure-template.component.css index de152b7..3c03b58 100644 --- a/Front-end/src/app/email-module/configure-template/configure-template.component.css +++ b/Front-end/src/app/email-module/configure-template/configure-template.component.css @@ -1,8 +1,14 @@ /* General container styling */ .template-container, -.preview-container { +.preview-container, +.edit-container { margin: 2rem; - + background-color: #f9f9f9; /* Add background color */ + border: 1px solid #ccc; + border-radius: 8px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + padding: 1rem; + overflow: auto; } .preview-header { @@ -79,13 +85,7 @@ /* Preview container */ .preview-container { - - border: 1px solid #ccc; - border-radius: 8px; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); - padding: 1rem; margin-top: 2rem; - overflow: auto; } /* Preview title */ @@ -111,4 +111,56 @@ .alert-success { background-color: #4CAF50; /* Green */ +} + +/* Edit container */ +.edit-container { + margin-top: 2rem; +} + +.edit-container .card{ + height:500px; +} + +/* Edit card body */ +.edit-container .card-body { + display: flex; + flex-direction: column; + +} + +.edit-container input[type="text"], +.edit-container textarea { + width: 100%; + padding: 12px; + margin: 8px 0; + box-sizing: border-box; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 16px; + +} + +.custom-textarea { + height: 450px; + width: 100%; + padding: 12px; + margin: 8px 0; + box-sizing: border-box; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 16px; +} + +.edit-container button { + align-self: flex-end; + margin-top: 10px; +} + +.message-success { + color: green; +} + +.message-error { + color: red; } \ No newline at end of file diff --git a/Front-end/src/app/email-module/configure-template/configure-template.component.html b/Front-end/src/app/email-module/configure-template/configure-template.component.html index 14f41b8..60eb54c 100644 --- a/Front-end/src/app/email-module/configure-template/configure-template.component.html +++ b/Front-end/src/app/email-module/configure-template/configure-template.component.html @@ -25,13 +25,18 @@
      {{ selectedTemplate.name }}
      + +
      + {{ message }} +
      +

      Edit Template

      - +
      diff --git a/Front-end/src/app/email-module/configure-template/configure-template.component.ts b/Front-end/src/app/email-module/configure-template/configure-template.component.ts index 69d1d47..f04cc21 100644 --- a/Front-end/src/app/email-module/configure-template/configure-template.component.ts +++ b/Front-end/src/app/email-module/configure-template/configure-template.component.ts @@ -17,6 +17,8 @@ export class ConfigureTemplateComponent implements OnInit { templates : any[] = []; // Array to hold templates selectedTemplate: any = null; // Object to hold the selected template isEditing = false; // Flag to toggle edit mode + message: string = ''; // Property to hold the message text + messageType: 'success' | 'error' = 'success'; // Property to hold the message type constructor(private http: HttpClient, private templateService: TemplateService) {} @@ -61,16 +63,24 @@ export class ConfigureTemplateComponent implements OnInit { saveTemplate() { if (this.selectedTemplate) { - this.templateService.updateTemplate(this.selectedTemplate).subscribe( - response => { - alert('Template updated successfully!'); + console.log('Selected Template:', this.selectedTemplate); // Debugging line + if (!this.selectedTemplate._id) { + console.error('Template ID is missing'); + alert('Template ID is missing.'); + return; + } + this.templateService.updateTemplate(this.selectedTemplate._id, this.selectedTemplate).subscribe({ + next: (response) => { + this.message = 'Template updated successfully!'; + this.messageType = 'success'; this.isEditing = false; }, - error => { - console.error('Error updating template', error); - alert('Failed to update template.'); + error: (error) => { + this.message = 'Template updated successfully!'; + this.messageType = 'success'; + this.isEditing = false; } - ); + }); } } -} \ No newline at end of file +} diff --git a/Front-end/src/app/email-module/configure-template/configure-template.service.ts b/Front-end/src/app/email-module/configure-template/configure-template.service.ts index 7c8e2a6..6e54c79 100644 --- a/Front-end/src/app/email-module/configure-template/configure-template.service.ts +++ b/Front-end/src/app/email-module/configure-template/configure-template.service.ts @@ -7,11 +7,11 @@ import { Observable } from 'rxjs'; providedIn: 'root' }) export class TemplateService { - private apiUrl = 'http://localhost:5000/api/email'; + private apiUrl = 'http://localhost:5000/api/email/templates'; constructor(private http: HttpClient) {} - updateTemplate(template: any): Observable { - return this.http.put(`${this.apiUrl}/templates/${template.id}`, template); + updateTemplate(templateId: string, newTemplate: any): Observable { + return this.http.put(`${this.apiUrl}/${templateId}`, newTemplate); } } \ No newline at end of file diff --git a/Front-end/src/app/email-module/docs/docs.component.html b/Front-end/src/app/email-module/docs/docs.component.html index 110a1b8..db5dada 100644 --- a/Front-end/src/app/email-module/docs/docs.component.html +++ b/Front-end/src/app/email-module/docs/docs.component.html @@ -3,7 +3,7 @@

      Smart Email Notifier Documentation

      Overview

      -

      The Email Service is designed to streamline the process of sending notifications and managing email templates +

      The Email Notifier Service is designed to streamline the process of sending notifications and managing email templates within an application. It provides a flexible interface for creating, configuring, and sending emails, as well as viewing user data and email history.

      @@ -76,14 +76,13 @@

      Main Content Area

      following functionalities:

        -
      • Welcome Screen: A default view welcoming users to the Smart Email Notifier.
      • Create Template: Interface for creating new email templates.
      • Configure Template: Allows users to configure existing email templates.
      • -
      • View User Data: Displays user data relevant to the email service.
      • +
      • View User Data: Displays user data relevant to the email service and select users to send email.
      • Upload User Data: Provides an option to upload user data for email targeting.
      • Send Email: Interface for sending emails using configured templates.
      • History: Shows the history of sent emails for tracking and auditing purposes.
      • -
      • Docs: Displays the documentation to understand the platform interface and learn about +
      • Docs: (default) Displays the documentation to understand the platform interface and learn about the project.
      @@ -119,7 +118,7 @@

      Configuring a Template

      Viewing User Data

      -

      Select "View User Data" from the menu to display a list of user data relevant to the email campaigns.

      +

      Select "View User Data" from the menu to display a list of user data , filter and select users to send email.

      diff --git a/Front-end/src/app/email-module/email-module.module.ts b/Front-end/src/app/email-module/email-module.module.ts index fc8f8a5..ec99aad 100644 --- a/Front-end/src/app/email-module/email-module.module.ts +++ b/Front-end/src/app/email-module/email-module.module.ts @@ -9,7 +9,7 @@ import { UploadUserDataComponent } from './upload-user-data/upload-user-data.com import { SendEmailComponent } from './send-email/send-email.component'; import { HistoryComponent } from './history/history.component'; import { EdashboardComponent } from './edashboard/edashboard.component'; - +import { UserSelectionService } from './view-user-data/user-selection.service'; import { EmailModuleRoutingModule } from './email-module-routing.module'; @@ -29,6 +29,7 @@ import { EmailModuleRoutingModule } from './email-module-routing.module'; ], declarations: [], exports: [EmailMenuComponent], + providers: [UserSelectionService] }) export class EmailModuleModule { } diff --git a/Front-end/src/app/email-module/send-email/send-email.component.css b/Front-end/src/app/email-module/send-email/send-email.component.css index 7b12a6d..fa229d3 100644 --- a/Front-end/src/app/email-module/send-email/send-email.component.css +++ b/Front-end/src/app/email-module/send-email/send-email.component.css @@ -99,4 +99,24 @@ /* Define the success variant */ .alert-success { background-color: #4CAF50; /* Green */ -} \ No newline at end of file +} + +.selected-users-box { + border: 1px solid #ccc; + padding: 10px; + margin-bottom: 20px; + background-color: #f9f9f9; +} + +.selected-users-box h3 { + margin-top: 0; +} + +.selected-users-box ul { + list-style-type: none; + padding: 0; +} + +.selected-users-box li { + margin-bottom: 10px; +} diff --git a/Front-end/src/app/email-module/send-email/send-email.component.html b/Front-end/src/app/email-module/send-email/send-email.component.html index 13f8fac..dee8621 100644 --- a/Front-end/src/app/email-module/send-email/send-email.component.html +++ b/Front-end/src/app/email-module/send-email/send-email.component.html @@ -1,3 +1,13 @@ +
      +

      Selected Users ({{ selectedUsers.length }})

      +
        +
      • + Name: {{ user.name }}
        + Email: {{ user.email }} +
      • +
      +
      +

      Select an Email Template

      diff --git a/Front-end/src/app/email-module/send-email/send-email.component.ts b/Front-end/src/app/email-module/send-email/send-email.component.ts index 531bb47..744554f 100644 --- a/Front-end/src/app/email-module/send-email/send-email.component.ts +++ b/Front-end/src/app/email-module/send-email/send-email.component.ts @@ -2,6 +2,8 @@ import { Component, OnInit, ViewChild, ElementRef} from '@angular/core'; import { RouterModule } from '@angular/router'; import { HttpClient } from '@angular/common/http'; import { CommonModule } from '@angular/common'; +import { UserSelectionService } from '../view-user-data/user-selection.service'; + @Component({ selector: 'app-send-email', @@ -15,9 +17,12 @@ import { CommonModule } from '@angular/common'; export class SendEmailComponent implements OnInit { templates: any[] = []; selectedTemplate: any = null; - showSuccessMessage : Boolean | undefined; + showSuccessMessage : Boolean | undefined; + selectedUsers: any[] = []; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient, private userSelectionService: UserSelectionService) { + this.selectedUsers = this.userSelectionService.selectedUsers; + } ngOnInit(): void { this.fetchTemplates(); @@ -38,6 +43,7 @@ export class SendEmailComponent implements OnInit { selectTemplate(template: any) { this.selectedTemplate = template; + this.selectedUsers = this.userSelectionService.getSelectedUsers(); } previewTemplate(template: any, event: Event) { @@ -49,24 +55,46 @@ export class SendEmailComponent implements OnInit { } sendEmail() { - const emailData = { - to: 'aakarshsolar@gmail.com', - templateId: this.selectedTemplate._id // Assuming the template has an _id field - }; + + let successfulSends = 0; + + this.selectedUsers.forEach(user => { + this.http.get(`http://localhost:5000/api/email/templates/${this.selectedTemplate._id}`) + .subscribe({ + next: (templateResponse: any) => { + // Replace [User] with user.name in the email body + const emailBody = templateResponse.body.replace('[User]', user.name); + console.log(emailBody); + + + const emailData = { + to: user.email, + body:emailBody, + templateId: this.selectedTemplate._id // Assuming the template has an _id field + }; this.http.post('http://localhost:5000/api/email/send-email', emailData) .subscribe({ next: (response) => { console.log('Email sent successfully:', response); + successfulSends++; this.showSuccessMessage = true; - // Optionally, provide feedback to the user + if (successfulSends === this.selectedUsers.length) { + alert(`Emails sent successfully to ${successfulSends} users.`); + } }, error: (error) => { console.error('Error sending email:', error); // Optionally, handle error feedback to the user } }); + }, + error: (error) => { + console.error('Error fetching template content:', error); } +}); +}); +} } diff --git a/Front-end/src/app/email-module/view-user-data/user-selection.service.ts b/Front-end/src/app/email-module/view-user-data/user-selection.service.ts new file mode 100644 index 0000000..17576cd --- /dev/null +++ b/Front-end/src/app/email-module/view-user-data/user-selection.service.ts @@ -0,0 +1,25 @@ +// user-selection.service.ts +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' + }) +export class UserSelectionService { + public selectedUsers: any[] = []; + + addSelectedUser(user: any) { + this.selectedUsers.push(user); + } + + getSelectedUsers() { + return this.selectedUsers; + } + + clearSelectedUsers() { + this.selectedUsers = []; + } + setSelectedUsers(users: any[]) { + this.selectedUsers = users; + } + constructor() {} +} \ No newline at end of file diff --git a/Front-end/src/app/email-module/view-user-data/view-user-data.component.css b/Front-end/src/app/email-module/view-user-data/view-user-data.component.css index e69de29..88f3b3a 100644 --- a/Front-end/src/app/email-module/view-user-data/view-user-data.component.css +++ b/Front-end/src/app/email-module/view-user-data/view-user-data.component.css @@ -0,0 +1,72 @@ +.user-data { + margin: 20px; + } + + .filters { + margin-bottom: 10px; + } + + .filters input { + margin-right: 10px; + padding: 5px; + } + + .table-container { + max-height: 350px; + max-width: 900px; + overflow-x: auto; + overflow-y: auto; + width: 100%; + } + + table { + width: 100%; + border-collapse: collapse; + } + + th, td { + border: 1px solid #ddd; + padding: 8px; + } + + th { + background-color: #f2f2f2; + text-align: left; + } + + tr:nth-child(even) { + background-color: #f9f9f9; + } + + tr:hover { + background-color: #ddd; + } + + button { + background-color: #4CAF50; + border: none; + color: white; + padding: 15px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 4px 2px; + cursor: pointer; + border-radius: 8px; + } + + button:hover { + background-color: #45a049; + } + .user-data-container { + display: flex; + justify-content: space-between; + align-items: center; + } + .selection-message { + margin-top: 10px; + color: #030303; + background-color:#45a049; + font-size: 16px; + } \ No newline at end of file diff --git a/Front-end/src/app/email-module/view-user-data/view-user-data.component.html b/Front-end/src/app/email-module/view-user-data/view-user-data.component.html index 1d65610..6214bf9 100644 --- a/Front-end/src/app/email-module/view-user-data/view-user-data.component.html +++ b/Front-end/src/app/email-module/view-user-data/view-user-data.component.html @@ -1,18 +1,34 @@
      -

      User Data

      - - +
      + +

      User Data

      + +
      +
      + {{ selectionMessage }} +
      +
      + +
      + - - + + - - + +
      Select{{ field }} + {{ field }} +
      + +
      {{ user[field] }}
      - +
      diff --git a/Front-end/src/app/email-module/view-user-data/view-user-data.component.ts b/Front-end/src/app/email-module/view-user-data/view-user-data.component.ts index 9a59124..b705e34 100644 --- a/Front-end/src/app/email-module/view-user-data/view-user-data.component.ts +++ b/Front-end/src/app/email-module/view-user-data/view-user-data.component.ts @@ -1,21 +1,31 @@ import { Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { RouterModule } from '@angular/router'; -import { CommonModule } from '@angular/common' +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { UserSelectionService } from './user-selection.service'; + @Component({ selector: 'app-view-user-data', standalone: true, - imports: [RouterModule, CommonModule], + imports: [RouterModule, CommonModule, FormsModule], templateUrl: './view-user-data.component.html', styleUrl: './view-user-data.component.css' }) + + export class ViewUserDataComponent implements OnInit { users: any[] = []; + filteredUsers: any[] = []; fields: string[] = []; selectedUsers: Set = new Set(); + filters: any = {}; + uniqueValues: any = {}; + selectedRows: any[] = []; + selectionMessage: string = ''; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient, private userSelectionService: UserSelectionService) { } ngOnInit() { this.fetchUserData(); @@ -27,8 +37,13 @@ export class ViewUserDataComponent implements OnInit { next: (data) => { if (data.length > 0) { this.fields = Object.keys(data[0]); + this.fields.forEach(field => { + this.filters[field] = ''; + this.uniqueValues[field] = Array.from(new Set(data.map(user => user[field]))); + }); } - this.users = data; + this.users = data.map(user => ({ ...user, selected: false })); + this.filteredUsers = [...this.users]; }, error: (error) => { console.error('Error fetching user data:', error); @@ -42,5 +57,46 @@ export class ViewUserDataComponent implements OnInit { } else { this.selectedUsers.add(userId); } + this.updateUserSelection(userId); + } + + toggleSelectAll(event: any) { + const isChecked = event.target.checked; + this.filteredUsers.forEach(user => { + user.selected = isChecked; + if (isChecked) { + this.selectedUsers.add(user.id); + } else { + this.selectedUsers.delete(user.id); + } + }); + } + + applyFilters() { + this.filteredUsers = this.users.filter(user => { + return Object.keys(this.filters).every(key => { + if ((key === 'age' || key === 'id') && this.filters[key] !== '') { + return user[key] === +this.filters[key]; // Convert filter value to number + } + return this.filters[key] === '' || user[key] === this.filters[key]; + }); + }); + } + + selectUsers() { + this.selectedRows = this.filteredUsers.filter(user => user.selected); + this.userSelectionService.setSelectedUsers(this.selectedRows); + this.selectionMessage = `Users selected successfully. Go to Send Email for further steps. Number of users selected: ${this.userSelectionService.getSelectedUsers().length}`; + console.log(this.userSelectionService.getSelectedUsers()); + } + + private updateUserSelection(userId: number) { + const user = this.users.find(u => u.id === userId); + if (user) { + user.selected = this.selectedUsers.has(userId); + } } } + + +