diff --git a/client/src/App.tsx b/client/src/App.tsx index 6a0aca5..3c25cc2 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -95,7 +95,7 @@ function App() { } /> } /> } /> - } /> + } /> } /> } /> } /> diff --git a/client/src/containers/Admin/NewsLetters/NewsLetters.module.css b/client/src/containers/Admin/NewsLetters/NewsLetters.module.css index 5f86d8e..2251c24 100644 --- a/client/src/containers/Admin/NewsLetters/NewsLetters.module.css +++ b/client/src/containers/Admin/NewsLetters/NewsLetters.module.css @@ -1,46 +1,46 @@ -/* NewsLetters.module.css */ +.container { + max-width: 900px; + margin: auto; + padding: 2rem; + font-family: Arial, sans-serif; +} + +.homeLink { + display: inline-block; + margin-bottom: 1rem; + text-decoration: none; + color: #0d6efd; +} .modalButton { - margin-top: 1.5rem; - } - - .modalTitle { - font-size: 1.5rem; - font-weight: bold; - } - - .modalContent { - padding: 1rem; - } - - .formGroup { - margin-bottom: 1rem; - } - - .formLabel { - font-size: 1.1rem; - font-weight: 500; - } - - .formInput { - width: 100%; - padding: 0.5rem; - font-size: 1rem; - border-radius: 4px; - } - - .formTextArea { - width: 100%; - padding: 0.5rem; - font-size: 1rem; - border-radius: 4px; - resize: vertical; - } - - .formActions { - display: flex; - justify-content: flex-end; - gap: 10px; - margin-top: 1rem; - } - \ No newline at end of file + margin-bottom: 1.5rem; +} + +.newsletterList { + margin-top: 2rem; +} + +.newsletterItems { + list-style: none; + padding: 0; +} + +.newsletterItem { + border: 1px solid #ddd; + border-radius: 6px; + padding: 1rem; + margin-bottom: 1rem; + background: #f9f9f9; +} + +.actions { + margin-top: 0.5rem; + display: flex; + gap: 10px; +} + +.error { + color: red; + margin-bottom: 1rem; + font-weight: bold; +} diff --git a/client/src/containers/Admin/NewsLetters/NewsLetters.tsx b/client/src/containers/Admin/NewsLetters/NewsLetters.tsx index 06a946d..5b89650 100644 --- a/client/src/containers/Admin/NewsLetters/NewsLetters.tsx +++ b/client/src/containers/Admin/NewsLetters/NewsLetters.tsx @@ -1,73 +1,680 @@ +// import React, { useState, useEffect } from "react"; +// import { Link } from "react-router-dom"; +// import { Modal, Button, Form } from "react-bootstrap"; // Importing Modal components +// import styles from "./NewsLetters.module.css"; // Importing CSS module +// import axios from "axios"; + +// // Define a type for a Newsletter object +// interface Newsletter { +// id: number; +// subject: string; +// description: string; +// authorId: number; +// archived: string; +// important: string; +// } + +// function NewsLetters() { +// const [showModal, setShowModal] = useState(false); // State to control modal visibility +// const [subject, setSubject] = useState(""); // Subject input state +// const [description, setDescription] = useState(""); // Description input state +// const [newsletters, setNewsletters] = useState([]); // Type the state as an array of newsletters + +// // Fetch newsletters from the database +// useEffect(() => { +// const fetchNewsletters = async () => { +// try { +// const response = await axios.get(`${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/pay`); // API endpoint to fetch newsletters +// setNewsletters(response.data); // Set newsletters from the API response +// } catch (error) { +// console.error("Error fetching newsletters:", error); +// } +// }; +// fetchNewsletters(); +// }, []); + +// const handleModalClose = () => setShowModal(false); // Close the modal +// const handleModalShow = () => setShowModal(true); // Open the modal + +// const handleSubjectChange = (e: React.ChangeEvent) => +// setSubject(e.target.value); // Handle subject change +// const handleDescriptionChange = (e: React.ChangeEvent) => +// setDescription(e.target.value); // Handle description change + +// const handleFormSubmit = async (e: React.FormEvent) => { +// e.preventDefault(); + +// // Handle form submission (e.g., saving the newsletter) +// const newsletterData = { +// subject, +// description, +// authorId: 1, // Assuming 1 is the admin's user ID +// }; + +// try { +// // Call API to create a new newsletter +// await axios.post(`${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/pay`, newsletterData); +// // Close the modal after submission +// setShowModal(false); +// // Refresh the newsletter list after creation +// const response = await axios.get(`${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/pay`); +// setNewsletters(response.data); +// } catch (error) { +// console.error("Error creating newsletter:", error); +// } +// }; + +// return ( +//
+//

News Letters

+// Home + +//
+// +//
+ +// {/* Modal for creating a newsletter */} +// +// +// +// Create A News Letter +// +// +// +//
+// +// Subject +// +// + +// +// Description +// +// + +//
+// +// +//
+//
+//
+//
+ +// {/* Display the list of newsletters */} +//
+//

Existing Newsletters

+//
    +// {newsletters.map((newsletter) => ( +//
  • +//
    +// Subject: {newsletter.subject} +//
    +//
    +// Description: {newsletter.description} +//
    +//
    +// Author: Admin +//
    +// {/* You can also add additional fields like published date, etc. */} +//
  • +// ))} +//
+//
+//
+// ); +// } + +// export default NewsLetters; + +// import React, { useState, useEffect } from "react"; +// import { Link } from "react-router-dom"; +// import { Modal, Button, Form, Spinner, Alert } from "react-bootstrap"; +// import axios from "axios"; +// import styles from "./NewsLetters.module.css"; + +// interface Newsletter { +// id: number; +// subject: string; +// description: string; +// authorId: number; +// archived: boolean; +// important: boolean; +// } + +// const NewsLetters: React.FC = () => { +// const [newsletters, setNewsletters] = useState([]); +// const [loading, setLoading] = useState(true); +// const [error, setError] = useState(""); + +// const [showModal, setShowModal] = useState(false); +// const [subject, setSubject] = useState(""); +// const [description, setDescription] = useState(""); +// const [submitting, setSubmitting] = useState(false); + +// // Fetch newsletters +// const fetchNewsletters = async () => { +// setLoading(true); +// setError(""); +// try { +// const response = await axios.get( +// `${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/pay` +// ); +// setNewsletters(response.data || []); +// } catch (err) { +// console.error(err); +// setError("Failed to load newsletters."); +// } finally { +// setLoading(false); +// } +// }; + +// useEffect(() => { +// fetchNewsletters(); +// }, []); + +// // Modal handlers +// const openModal = () => setShowModal(true); +// const closeModal = () => { +// if (!submitting) { +// setShowModal(false); +// setSubject(""); +// setDescription(""); +// } +// }; + +// // Create newsletter +// const handleFormSubmit = async (e: React.FormEvent) => { +// e.preventDefault(); +// setSubmitting(true); +// setError(""); + +// try { +// await axios.post( +// `${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/pay`, +// { subject, description, authorId: 1 } +// ); +// await fetchNewsletters(); +// closeModal(); +// } catch (err) { +// console.error(err); +// setError("Failed to create newsletter."); +// } finally { +// setSubmitting(false); +// } +// }; + +// // Archive/unarchive newsletter +// const toggleArchive = async (id: number, current: boolean) => { +// try { +// await axios.put( +// `${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/archive/${id}`, +// { archived: !current } +// ); +// setNewsletters((prev) => +// prev.map((n) => +// n.id === id ? { ...n, archived: !current } : n +// ) +// ); +// } catch (err) { +// console.error(err); +// setError("Failed to update newsletter."); +// } +// }; + +// // Delete newsletter +// const handleDelete = async (id: number) => { +// if (!window.confirm("Are you sure you want to delete this newsletter?")) return; +// try { +// await axios.delete( +// `${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/pay/${id}` +// ); +// setNewsletters((prev) => prev.filter((n) => n.id !== id)); +// } catch (err) { +// console.error(err); +// setError("Failed to delete newsletter."); +// } +// }; + +// return ( +//
+//
+//

Newsletters

+// +// Back Home +// +//
+ +//
+// +//
+ +// {error && {error}} + +// {/* Loading */} +// {loading ? ( +//
+// Loading newsletters... +//
+// ) : ( +//
+// {newsletters.length === 0 ? ( +//

No newsletters found.

+// ) : ( +// newsletters.map((n) => ( +//
+//

{n.subject}

+//

{n.description}

+//

Author: Admin

+//
+// +// +//
+// {n.archived && Archived} +// {n.important && Important} +//
+// )) +// )} +//
+// )} + +// {/* Modal */} +// +// +// Create a Newsletter +// +// +//
+// +// Subject +// setSubject(e.target.value)} +// required +// /> +// + +// +// Description +// setDescription(e.target.value)} +// required +// /> +// + +//
+// +// +//
+//
+//
+//
+//
+// ); +// }; + +// export default NewsLetters; + +// import React, { useState, useEffect } from "react"; +// import { Link } from "react-router-dom"; +// import { Modal, Button, Form, Spinner, Alert } from "react-bootstrap"; +// import axios from "axios"; +// import styles from "./NewsLetters.module.css"; + +// interface Newsletter { +// id: number; +// subject: string; +// description: string; +// authorId: number; +// archived: boolean; +// important: boolean; +// } + +// const NewsLetters: React.FC = () => { +// const [newsletters, setNewsletters] = useState([]); +// const [showModal, setShowModal] = useState(false); +// const [submitting, setSubmitting] = useState(false); +// const [subject, setSubject] = useState(""); +// const [description, setDescription] = useState(""); +// const [error, setError] = useState(""); + +// // Fetch all newsletters +// const fetchNewsletters = async () => { +// try { +// const response = await axios.get( +// `${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/pay` +// ); +// setNewsletters(response.data); +// } catch (err) { +// console.error("Error fetching newsletters:", err); +// setError("Failed to load newsletters."); +// } +// }; + +// useEffect(() => { +// fetchNewsletters(); +// }, []); + +// const handleModalClose = () => { +// setShowModal(false); +// setSubject(""); +// setDescription(""); +// setError(""); +// }; + +// const handleFormSubmit = async (e: React.FormEvent) => { +// e.preventDefault(); +// setSubmitting(true); +// setError(""); + +// try { +// await axios.post( +// `${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/pay`, +// { subject, description, authorId: 1 } +// ); +// setShowModal(false); // Close modal immediately +// setSubject(""); +// setDescription(""); +// await fetchNewsletters(); // Refresh newsletter list +// } catch (err) { +// console.error(err); +// setError("Failed to create newsletter. Check console for details."); +// } finally { +// setSubmitting(false); +// } +// }; + +// const handleArchiveToggle = async (id: number, archived: boolean) => { +// try { +// await axios.put( +// `${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/archive/${id}`, +// { archived: !archived } +// ); +// setNewsletters((prev) => +// prev.map((n) => +// n.id === id ? { ...n, archived: !archived } : n +// ) +// ); +// } catch (err) { +// console.error(err); +// setError("Failed to update archive status."); +// } +// }; + +// const handleDelete = async (id: number) => { +// if (!window.confirm("Are you sure you want to delete this newsletter?")) return; +// try { +// await axios.delete( +// `${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/pay/${id}` +// ); +// setNewsletters((prev) => prev.filter((n) => n.id !== id)); +// } catch (err) { +// console.error(err); +// setError("Failed to delete newsletter."); +// } +// }; + +// return ( +//
+//
+//

Newsletters

+// +// Back to Admin +// +//
+ +// {error && {error}} + +//
+// +//
+ +// {/* Modal */} +// +// +// Create Newsletter +// +// +//
+// +// Subject +// setSubject(e.target.value)} +// required +// /> +// + +// +// Description +// setDescription(e.target.value)} +// required +// /> +// + +//
+// +// +//
+//
+//
+//
+ +// {/* Newsletter List */} +//
+// {newsletters.length === 0 ? ( +//

No newsletters found.

+// ) : ( +// +// +// +// +// +// +// +// +// +// +// +// +// {newsletters.map((n) => ( +// +// +// +// +// +// +// +// +// ))} +// +//
SubjectDescriptionAuthorArchivedImportantActions
{n.subject}{n.description}Admin +// +// {n.important ? "Yes" : "No"} +// +//
+// )} +//
+//
+// ); +// }; + +// export default NewsLetters; import React, { useState, useEffect } from "react"; import { Link } from "react-router-dom"; -import { Modal, Button, Form } from "react-bootstrap"; // Importing Modal components -import styles from "./NewsLetters.module.css"; // Importing CSS module +import { Modal, Button, Form, Spinner } from "react-bootstrap"; import axios from "axios"; +import styles from "./NewsLetters.module.css"; -// Define a type for a Newsletter object interface Newsletter { id: number; subject: string; description: string; authorId: number; - archived: string; - important: string; + archived: boolean; + important: boolean; } function NewsLetters() { - const [showModal, setShowModal] = useState(false); // State to control modal visibility - const [subject, setSubject] = useState(""); // Subject input state - const [description, setDescription] = useState(""); // Description input state - const [newsletters, setNewsletters] = useState([]); // Type the state as an array of newsletters + const [showModal, setShowModal] = useState(false); + const [subject, setSubject] = useState(""); + const [description, setDescription] = useState(""); + const [newsletters, setNewsletters] = useState([]); + const [loading, setLoading] = useState(false); + const [submitting, setSubmitting] = useState(false); + const [error, setError] = useState(""); + + const API_URL = process.env.REACT_APP_SOCKET_IO_CLIENT_PORT || "http://localhost:3000"; + + // Fetch newsletters + const fetchNewsletters = async () => { + setLoading(true); + try { + const response = await axios.get(`${API_URL}/api/newsletter/pay`); + setNewsletters(response.data); + } catch (err) { + console.error("Error fetching newsletters:", err); + } finally { + setLoading(false); + } + }; - // Fetch newsletters from the database useEffect(() => { - const fetchNewsletters = async () => { - try { - const response = await axios.get(`${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/pay`); // API endpoint to fetch newsletters - setNewsletters(response.data); // Set newsletters from the API response - } catch (error) { - console.error("Error fetching newsletters:", error); - } - }; fetchNewsletters(); }, []); - const handleModalClose = () => setShowModal(false); // Close the modal - const handleModalShow = () => setShowModal(true); // Open the modal - - const handleSubjectChange = (e: React.ChangeEvent) => - setSubject(e.target.value); // Handle subject change - const handleDescriptionChange = (e: React.ChangeEvent) => - setDescription(e.target.value); // Handle description change + const handleModalClose = () => setShowModal(false); + const handleModalShow = () => setShowModal(true); const handleFormSubmit = async (e: React.FormEvent) => { e.preventDefault(); - - // Handle form submission (e.g., saving the newsletter) - const newsletterData = { - subject, - description, - authorId: 1, // Assuming 1 is the admin's user ID - }; + setSubmitting(true); + setError(""); try { - // Call API to create a new newsletter - await axios.post(`${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/pay`, newsletterData); - // Close the modal after submission + await axios.post(`${API_URL}/api/newsletter/pay`, { + subject, + description, + authorId: 1, + }); setShowModal(false); - // Refresh the newsletter list after creation - const response = await axios.get(`${process.env.REACT_APP_SOCKET_IO_CLIENT_PORT}/api/newsletter/pay`); - setNewsletters(response.data); - } catch (error) { - console.error("Error creating newsletter:", error); + setSubject(""); + setDescription(""); + await fetchNewsletters(); + } catch (err) { + console.error(err); + setError("Failed to create newsletter. Check console."); + } finally { + setSubmitting(false); + } + }; + + const toggleArchive = async (id: number, archived: boolean) => { + try { + await axios.put(`${API_URL}/api/newsletter/archive/${id}`, { archived: !archived }); + setNewsletters((prev) => + prev.map((n) => (n.id === id ? { ...n, archived: !archived } : n)) + ); + } catch (err) { + console.error("Error toggling archive:", err); + } + }; + + const deleteNewsletter = async (id: number) => { + if (!window.confirm("Are you sure you want to delete this newsletter?")) return; + try { + await axios.delete(`${API_URL}/api/newsletter/delete/${id}`); + setNewsletters((prev) => prev.filter((n) => n.id !== id)); + } catch (err) { + console.error("Error deleting newsletter:", err); } }; return ( -
-

News Letters

- Home +
+

Admin Newsletters

+ + Back Home +
- {/* Modal for creating a newsletter */} - + {/* Create Newsletter Modal */} + - - Create A News Letter - + Create Newsletter - + + {error &&
{error}
}
- - Subject + + Subject setSubject(e.target.value)} required /> - - Description + + Description setDescription(e.target.value)} required /> -
- -
- {/* Display the list of newsletters */} + {/* Existing Newsletters */}

Existing Newsletters

-
    - {newsletters.map((newsletter) => ( -
  • -
    - Subject: {newsletter.subject} -
    -
    - Description: {newsletter.description} -
    -
    - Author: Admin -
    - {/* You can also add additional fields like published date, etc. */} -
  • - ))} -
+ {loading ? ( + + ) : newsletters.length === 0 ? ( +

No newsletters found.

+ ) : ( +
    + {newsletters.map((newsletter) => ( +
  • +
    + Subject: {newsletter.subject} +
    +
    + Description: {newsletter.description} +
    +
    + Author: Admin +
    +
    + + +
    +
  • + ))} +
+ )}
); diff --git a/controllers/NewsLetterController.js b/controllers/NewsLetterController.js index b56b138..94e51ff 100644 --- a/controllers/NewsLetterController.js +++ b/controllers/NewsLetterController.js @@ -1,78 +1,134 @@ -const express = require("express"); -const router = express.Router(); -const db = require("../models"); -// const cors = require("cors"); +// const express = require("express"); +// const router = express.Router(); +// const db = require("../models"); +// // const cors = require("cors"); -// const app = express(); +// // const app = express(); -// // Allow all domains (use cautiously in production) -// app.use(cors()); +// // // Allow all domains (use cautiously in production) +// // app.use(cors()); -// Get all newsletters -// router.get("/pay", async (req, res) => { +// // Get all newsletters +// // router.get("/pay", async (req, res) => { +// // try { +// // const newsletters = await db.Newsletter.find(); // Assuming you're using a model named "Newsletter" +// // res.json(newsletters); +// // } catch (error) { +// // res.status(500).json({ message: "Error fetching newsletters." }); +// // } +// // }); + +// router.get("/pay", (req, res) => { +// db.NewsLetter.findAll().then((allLetters) => { +// res.json(allLetters); +// }); +// }); + +// // Create a new newsletter +// // router.post("/pay", async (req, res) => { +// // try { +// // // const { subject, description } = req.body; +// // const newNewsletter = new db.Newsletter(req.body); +// // await newNewsletter.save(); +// // res.status(201).json(newNewsletter); +// // } catch (error) { +// // res.status(500).json({ message: "Error creating newsletter." }); +// // } +// // }); + +// // router.post("/pay2", (req, res) => { +// // console.log(req.body); +// // db.Newsletter.create(req.body) +// // .then((newLetter) => { +// // res.json({ +// // error: false, +// // data: newLetter, +// // message: "Successfully created a new News Letter.", +// // }); +// // }) +// // .catch((err) => { +// // console.log(err); +// // res.status(500).json({ +// // error: true, +// // data: null, +// // message: "Unable to create new ticket.", +// // }); +// // }); +// // }); + +// router.post("/pay", (req, res) => { +// db.NewsLetter.create(req.body).then((newLetter) => { +// res.json(newAdmin); +// }); +// }); + +// // Update a newsletter +// router.put("/:id", async (req, res) => { // try { -// const newsletters = await db.Newsletter.find(); // Assuming you're using a model named "Newsletter" -// res.json(newsletters); +// const { subject, description } = req.body; +// const updatedNewsletter = await db.Newsletter.findByIdAndUpdate( +// req.params.id, +// { subject, description }, +// { new: true } +// ); +// res.json(updatedNewsletter); // } catch (error) { -// res.status(500).json({ message: "Error fetching newsletters." }); +// res.status(500).json({ message: "Error updating newsletter." }); // } // }); -router.get("/pay", (req, res) => { - db.NewsLetter.findAll().then((allLetters) => { - res.json(allLetters); - }); -}); - -// Create a new newsletter -// router.post("/pay", async (req, res) => { +// // Delete a newsletter +// router.delete("/:id", async (req, res) => { // try { -// // const { subject, description } = req.body; -// const newNewsletter = new db.Newsletter(req.body); -// await newNewsletter.save(); -// res.status(201).json(newNewsletter); +// await db.Newsletter.findByIdAndDelete(req.params.id); +// res.status(204).send(); // } catch (error) { -// res.status(500).json({ message: "Error creating newsletter." }); +// res.status(500).json({ message: "Error deleting newsletter." }); // } // }); -// router.post("/pay2", (req, res) => { -// console.log(req.body); -// db.Newsletter.create(req.body) -// .then((newLetter) => { -// res.json({ -// error: false, -// data: newLetter, -// message: "Successfully created a new News Letter.", -// }); -// }) -// .catch((err) => { -// console.log(err); -// res.status(500).json({ -// error: true, -// data: null, -// message: "Unable to create new ticket.", -// }); -// }); -// }); +// module.exports = router; -router.post("/pay", (req, res) => { - db.NewsLetter.create(req.body).then((newLetter) => { - res.json(newAdmin); - }); +const express = require("express"); +const router = express.Router(); +const db = require("../models"); + +// Get all newsletters +router.get("/pay", async (req, res) => { + try { + const allLetters = await db.NewsLetter.findAll(); + res.json(allLetters); + } catch (error) { + console.error(error); + res.status(500).json({ message: "Error fetching newsletters." }); + } +}); + +// Create a new newsletter +router.post("/pay", async (req, res) => { + try { + const newLetter = await db.NewsLetter.create(req.body); + res.status(201).json(newLetter); + } catch (error) { + console.error(error); + res.status(500).json({ message: "Error creating newsletter." }); + } }); // Update a newsletter router.put("/:id", async (req, res) => { try { - const { subject, description } = req.body; - const updatedNewsletter = await db.Newsletter.findByIdAndUpdate( - req.params.id, - { subject, description }, - { new: true } + const { subject, description, archived, important } = req.body; + + const updatedNewsletter = await db.NewsLetter.update( + { subject, description, archived, important }, + { where: { id: req.params.id }, returning: true } ); - res.json(updatedNewsletter); + + // Sequelize returns [numberOfAffectedRows, affectedRowsArray] + res.json(updatedNewsletter[1][0]); } catch (error) { + console.error(error); res.status(500).json({ message: "Error updating newsletter." }); } }); @@ -80,9 +136,10 @@ router.put("/:id", async (req, res) => { // Delete a newsletter router.delete("/:id", async (req, res) => { try { - await db.Newsletter.findByIdAndDelete(req.params.id); + await db.NewsLetter.destroy({ where: { id: req.params.id } }); res.status(204).send(); } catch (error) { + console.error(error); res.status(500).json({ message: "Error deleting newsletter." }); } });