diff --git a/package-lock.json b/package-lock.json index de0236c..2bcc9b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "react-dom": "^18.2.0", "react-router-dom": "^6.11.2", "react-scripts": "5.0.1", + "react-spinners": "^0.13.8", "web-vitals": "^2.1.4" } }, @@ -14681,6 +14682,15 @@ } } }, + "node_modules/react-spinners": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.13.8.tgz", + "integrity": "sha512-3e+k56lUkPj0vb5NDXPVFAOkPC//XyhKPJjvcGjyMNPWsBKpplfeyialP74G7H7+It7KzhtET+MvGqbKgAqpZA==", + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", diff --git a/package.json b/package.json index 8cd58e8..c8cb977 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "react-dom": "^18.2.0", "react-router-dom": "^6.11.2", "react-scripts": "5.0.1", + "react-spinners": "^0.13.8", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/src/App.js b/src/App.js index c79110b..667ccf2 100644 --- a/src/App.js +++ b/src/App.js @@ -8,11 +8,12 @@ import AppContext from "./AppContext"; import Navbar_ from "./Components/Navbar/Navbar_"; -import HomePage from "./Pages/HomePage"; +import Home from "./Pages/Home"; import Levels from "./Pages/Levels"; import Content from "./Pages/Content"; import NewParagraph from "./Pages/NewParagraph"; import Standards from "./Pages/Standards"; +import Details from "./Pages/Details"; function App() { const iddrs_url = "http://localhost:8000/admin_api"; @@ -24,11 +25,12 @@ function App() { IDDRS - } /> + } /> } /> } /> } /> } /> + } /> diff --git a/src/Components/Content/ContentComp.js b/src/Components/Content/ContentComp.js index 08bcbac..e312300 100644 --- a/src/Components/Content/ContentComp.js +++ b/src/Components/Content/ContentComp.js @@ -51,8 +51,7 @@ const ContentComp = () => { }, }) .then((response) => { - setData(response.data.results); - console.log("Success!!!!!!!"); + setData(response.data.contents); }) .catch((error) => { console.log(error); @@ -61,6 +60,7 @@ const ContentComp = () => { return ( + { ))} )} + ); }; diff --git a/src/Components/Content/ContentList.js b/src/Components/Content/ContentList.js index a9ebbb0..c04b0fa 100644 --- a/src/Components/Content/ContentList.js +++ b/src/Components/Content/ContentList.js @@ -1,37 +1,39 @@ -import React, { useContext, useEffect, useState } from 'react'; -import axios from 'axios'; -import AppContext from "../../AppContext"; - +import React from "react"; +import { Link } from 'react-router-dom'; const ContentList = (props) => { - const [data, setData] = useState([]); - const level = props.level - const standard = props.standard - const url = useContext(AppContext); - //console.log(props.level) - //console.log(props.standard) - - useEffect(() => { - // Make the Axios request to the Django API - axios.get(url+'/content-list', { - params: { - level: level, - standard: standard - } - }) - .then(response => { - setData(response.data.results); - console.log("Success!!!!!!!") - }) - .catch(error => { - console.log(error); - }); - }, []); - - return ( -
ContentList
- ) -} +
+ {props.standardContnet.map((paragraph, index) => ( + + + + ))} +
+ ); +}; -export default ContentList \ No newline at end of file +export default ContentList; diff --git a/src/Components/HomePage/HomePage.js b/src/Components/HomePage/HomePage.js new file mode 100644 index 0000000..6c9183f --- /dev/null +++ b/src/Components/HomePage/HomePage.js @@ -0,0 +1,112 @@ +import React, { useContext, useState, useEffect } from "react"; +import AppContext from "../../AppContext"; +import Button from "react-bootstrap/esm/Button"; +import axios from "axios"; +import Row from "react-bootstrap/Row"; +import Col from "react-bootstrap/Col"; +import Classes from "./HomePage.module.css"; +import ClipLoader from "react-spinners/ClipLoader"; + +const HomePage = () => { + const url = useContext(AppContext); + const [newContentTracker, setNewContentTracker] = useState([]); + const [newContents, setNewContents] = useState(true); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + axios + .get(url + "/NewContentTracker/") + .then((response) => setNewContentTracker(response.data)) + .catch((error) => console.log(error)); + }, [newContents]); + + const handelClick = () => { + setIsLoading(true); + axios + .post(url + "/pre-process/") + .then((response) => { + setNewContents(false); + setIsLoading(false); + }) + .catch((error) => { + console.error("Error pre-processing:", error); + // Handle error condition + }); + }; + + return ( + <> + + +

IDDRS Admin Entry Mask

+ +

Pages:

+
    +
  1. +

    Content:

    +

    + To view the content of each standard by choosing the desired + level and standard. Users can click on each piece of content to + view it or modify it. +

    +
  2. +
  3. +

    New Paragraph:

    +

    + To add new paragraphs to the database to make them available for + searching. +

    +
  4. +
  5. +

    Levels:

    +

    + To show all levels. Users can enter new levels or modify + existing ones. +

    +
  6. +
  7. +

    Standards:

    +

    + To show all the standards ordered by levels. Users can modify or + add standards. +

    +
  8. +
+ +

Home Page:

+

+ In the home page, there is a pre-process button to make all the + modified and new entries searchable. The button only appears if + there are new paragraphs added. +

+ + {newContentTracker.length > 0 && ( + +
+
+ {newContentTracker.length} enterys are not yet searchable. + Please click Pre-Process button to make them searchable. +
+
+ + + )} +
+ {isLoading && ( +
+ +
+ )} + + ); +}; + +export default HomePage; diff --git a/src/Components/HomePage/HomePage.module.css b/src/Components/HomePage/HomePage.module.css new file mode 100644 index 0000000..564beab --- /dev/null +++ b/src/Components/HomePage/HomePage.module.css @@ -0,0 +1,9 @@ +.overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); /* Adjust the opacity as needed */ + z-index: 9999; + } \ No newline at end of file diff --git a/src/Components/LevelStandard/LevelStandard.js b/src/Components/LevelStandard/LevelStandard.js new file mode 100644 index 0000000..2933947 --- /dev/null +++ b/src/Components/LevelStandard/LevelStandard.js @@ -0,0 +1,73 @@ +import React, { useContext, useState, useEffect } from "react"; +import AppContext from "../../AppContext"; +import axios from "axios"; +import Form from "react-bootstrap/Form"; + +const LevelStandard = ({ onValueChange }) => { + const url = useContext(AppContext); + + const [levels, setLevels] = useState([]); + const [standards, setStandards] = useState([]); + + const [selectedLevel, setSelectedLevel] = useState(''); + // const [selectedStandard, setSelectedStandard] = useState([]); + + const [selectedStandards, setSelectedStandards] = useState([]); + const [showStandards, setShowStandards] = useState(false); + + useEffect(() => { + axios + .get(url + "/levels/") + .then((response) => setLevels(response.data)) + .catch((error) => console.log(error)); + }, []); + + useEffect(() => { + axios + .get(url + "/standards/") + .then((response) => setStandards(response.data)) + .catch((error) => console.log(error)); + }, []); + + const handleLevelChange = (e) => { + setSelectedLevel(e.target.value); + setSelectedStandards( + standards.filter((standard) => standard.standardLevel == e.target.value) + ); + setShowStandards(true); + }; + + const handleStandardChange = (e) =>{ + const selectedStandard = e.target.value; + onValueChange(selectedLevel, selectedStandard) + } + return ( + <> + + + {levels.map((level) => ( + + ))} + + + {showStandards && ( + + + {selectedStandards.map((standard) => ( + + ))} + + )} + + ); +}; + +export default LevelStandard; diff --git a/src/Components/LevelsList/LevelsList.js b/src/Components/LevelsList/LevelsList.js new file mode 100644 index 0000000..9345d5d --- /dev/null +++ b/src/Components/LevelsList/LevelsList.js @@ -0,0 +1,140 @@ +import React, { useContext, useState, useEffect } from "react"; +import AppContext from "../../AppContext"; +import axios from "axios"; +import Button from "react-bootstrap/Button"; +import Table from "react-bootstrap/Table"; +import Form from "react-bootstrap/Form"; +import Row from "react-bootstrap/Row"; +import Col from "react-bootstrap/Col"; + +const LevelsList = () => { + const url = useContext(AppContext); + const [editingRow, setEditingRow] = useState(null); + const [levels, setLevels] = useState([]); + + useEffect(() => { + axios + .get(url + "/levels/") + .then((response) => setLevels(response.data)) + .catch((error) => console.log(error)); + }, []); + + const handleEditClick = (rowData) => { + setEditingRow(rowData); + }; + const handleSubmit = (e) => { + // e.preventDefault(); + axios + .post(url + "/level-submit/", { + params: { + editingRow: editingRow, + }, + }) + .then((response) => { + console.log("Form submitted successfully:", response.data); + + }) + .catch((error) => { + console.error("Error submitting form:", error); + // Handle error condition + }); + }; + + const handleDeleteClick = (rowData) => { + axios + .post(url + "/level-delete/", { + params: { + rowData: rowData, + }, + }) + .then((response) => { + console.log("Form deleted successfully:", response.data); + window.location.reload(false); + }) + .catch((error) => { + console.error("Error deleting form:", error); + // Handle error condition + }); + }; + + return ( + <> +
+ + + + + setEditingRow({ ...editingRow, levelNumber: e.target.value }) + } + /> + + + + + + setEditingRow({ ...editingRow, levelName: e.target.value }) + } + /> + + + + + + setEditingRow({ ...editingRow, levelColor: e.target.value }) + } + /> + + + + + + + +
+ + + {levels.map((level) => ( + + + + + + + + ))} + +
{level.levelNumber}{level.levelName}{level.levelColor} + + + +
+ + ); +}; + +export default LevelsList; diff --git a/src/Components/NewParagraphForm/NewParagraphForm.js b/src/Components/NewParagraphForm/NewParagraphForm.js new file mode 100644 index 0000000..751a577 --- /dev/null +++ b/src/Components/NewParagraphForm/NewParagraphForm.js @@ -0,0 +1,186 @@ +import React, { useContext, useState, useEffect } from "react"; +import AppContext from "../../AppContext"; +import axios from "axios"; +import LevelStandard from "../LevelStandard/LevelStandard"; +import Form from "react-bootstrap/Form"; +import Row from "react-bootstrap/Row"; +import Col from "react-bootstrap/Col"; +import Container from "react-bootstrap/esm/Container"; +import Button from "react-bootstrap/Button"; + +const NewParagraphForm = () => { + const url = useContext(AppContext); + + const [selectedLevel, setSelectedLevel] = useState(""); + const [selectedStandard, setSelectedStandard] = useState(""); + + const [isSubmitted, setIsSubmitted] = useState(false); + + const [formData, setFormData] = useState({ + Heading1: "", + Heading2: "", + Heading3: "", + Heading4: "", + Paragraph: "", + PageNum: "", + }); + + const handleLevelStandard = (level, standard) => { + setSelectedLevel(level); + setSelectedStandard(standard); + }; + + const handleChange = (e) => { + setFormData({ ...formData, [e.target.name]: e.target.value }); + }; + + const handleSubmit = (e) => { + e.preventDefault(); + // Access the form values from the formData state + console.log(formData); + + axios + .post(url + "/content-create/", { + params: { + level: selectedLevel, + standard: selectedStandard, + formData: formData, + }, + }) + .then((response) => { + console.log("Form submitted successfully:", response.data); + setIsSubmitted(true); + setFormData({ + Heading1: "", + Heading2: "", + Heading3: "", + Heading4: "", + Paragraph: "", + PageNum: "", + }); + }) + .catch((error) => { + console.error("Error submitting form:", error); + // Handle error condition + }); + }; + useEffect(() => { + if (isSubmitted) { + // Automatically hide the popup after 3 seconds + const timeout = setTimeout(() => { + setIsSubmitted(false); + }, 3000); + + return () => clearTimeout(timeout); + } + }, [isSubmitted]); + + return ( + + {/* Popup message */} + {isSubmitted && ( +
+

Content added successfully!

+
+ )} +
+ + + + + Title + + + + + + + + + Sub-Title1 + + + + + + + + + Sub-Title2 + + + + + + + + + Sub-Title3 + + + + + + + + + Paragraph + + + + + + + + + Page Number + + + + + + +
+
+ ); +}; + +export default NewParagraphForm; diff --git a/src/Components/Paragraph/ParagraphDetails.js b/src/Components/Paragraph/ParagraphDetails.js new file mode 100644 index 0000000..2519579 --- /dev/null +++ b/src/Components/Paragraph/ParagraphDetails.js @@ -0,0 +1,224 @@ +import React, { useEffect, useState, useContext } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import AppContext from "../../AppContext"; +import axios from "axios"; + +import Form from "react-bootstrap/Form"; +import Row from "react-bootstrap/Row"; +import Col from "react-bootstrap/Col"; +import Container from "react-bootstrap/esm/Container"; +import Button from "react-bootstrap/Button"; + +const ParagraphDetails = ({ selectedParagraph }) => { + const url = useContext(AppContext); + const navigate = useNavigate(); + + const { level, standard, pk } = useParams(); + console.log(level, standard, pk); + const [paragraph, setParagraph] = useState([]); + + const [isSubmitted, setIsSubmitted] = useState(false); + + const [formData, setFormData] = useState({ + Heading1: "", + Heading2: "", + Heading3: "", + Heading4: "", + Paragraph: "", + PageNum: "", + }); + + useEffect(() => { + axios + .get(url + "/content-detail/", { + params: { + level: level, + standard: standard, + id: pk, + formData: formData, + }, + }) + .then((response) => { + setParagraph(response.data.paragraph); + setFormData({ + Heading1: response.data.paragraph.Heading1, + Heading2: response.data.paragraph.Heading2, + Heading3: response.data.paragraph.Heading3, + Heading4: response.data.paragraph.Heading4, + Paragraph: response.data.paragraph.Paragraph, + PageNum: response.data.paragraph.PageNum, + }) + }) + .catch((error) => console.log("There is an error:", error)); + }, [pk]); + + useEffect(() => { + if (isSubmitted) { + // Automatically hide the popup after 3 seconds + const timeout = setTimeout(() => { + setIsSubmitted(false); + }, 3000); + + return () => clearTimeout(timeout); + } + }, [isSubmitted]); + + + const handleSubmit = (e) => { + e.preventDefault(); + + axios + .post(url + "/content-update/", { + params: { + level: level, + standard: standard, + id: pk, + formData: formData, + }, + }) + .then((response) => { + console.log("Form submitted successfully:", response.data); + setIsSubmitted(true); + }) + .catch((error) => { + console.error("Error submitting form:", error); + // Handle error condition + }); + }; + + const handleChange = (e) => { + setFormData({ ...formData, [e.target.name]: e.target.value }); + }; + + const handleDelete = (e) =>{ + e.preventDefault(); + + axios + .post(url + "/content-delete/", { + params: { + level: level, + standard: standard, + id: pk + }, + }) + .then((response) => { + console.log("Deleted successfully:", response.data); + navigate('/content') + }) + .catch((error) => { + console.error("Error deleting:", error); + // Handle error condition + }); + + } + + return ( + + {/* Popup message */} + {isSubmitted && ( +
+

Content updated successfully!

+
+ )} +
+ + + Title + + + + + + + + + Sub-Title1 + + + + + + + + + Sub-Title2 + + + + + + + + + Sub-Title3 + + + + + + + + + Paragraph + + + + + + + + + Page Number + + + + + + + +
+
+ ); +}; + +export default ParagraphDetails; diff --git a/src/Components/StandardsList/StandardsList.js b/src/Components/StandardsList/StandardsList.js new file mode 100644 index 0000000..2df226d --- /dev/null +++ b/src/Components/StandardsList/StandardsList.js @@ -0,0 +1,173 @@ +import React, { useContext, useState, useEffect } from "react"; +import AppContext from "../../AppContext"; +import axios from "axios"; +import Button from "react-bootstrap/Button"; +import Table from "react-bootstrap/Table"; +import Form from "react-bootstrap/Form"; +import Row from "react-bootstrap/Row"; +import Col from "react-bootstrap/Col"; +import LevelsList from "../LevelsList/LevelsList"; + +const StandardsList = () => { + const url = useContext(AppContext); + const [standards, setStandards] = useState([]); + const [levels, setLevels] = useState([]); + const [editingRow, setEditingRow] = useState(null); + + useEffect(() => { + axios + .get(url + "/standards/") + .then((response) => + setStandards( + response.data.sort( + (prev, next) => prev.standardLevel - next.standardLevel + ) + ) + ) + .catch((error) => console.log(error)); + }, []); + + useEffect(() => { + axios + .get(url + "/levels/") + .then((response) => setLevels(response.data)) + .catch((error) => console.log(error)); + }, []); + + const handleSubmit = () => { + axios + .post(url + "/standard-submit/", { + params: { + editingRow: editingRow, + }, + }) + .then((response) => { + console.log("Form submitted successfully:", response.data); + }) + .catch((error) => { + console.error("Error submitting form:", error); + // Handle error condition + }); + }; + const handleEditClick = (rowData) => { + setEditingRow(rowData); + }; + const handleDeleteClick = (rowData) => { + axios + .post(url + "/standard-delete/", { + params: { + rowData: rowData, + }, + }) + .then((response) => { + console.log("Form deleted successfully:", response.data); + window.location.reload(false); + }) + .catch((error) => { + console.error("Error deleting form:", error); + // Handle error condition + }); + }; + + return ( + <> +
+ + + + setEditingRow({ + ...editingRow, + levelID: e.target.value, + }) + } + value={editingRow ? editingRow.levelID : ""} + > + + {levels.map((level, index) => ( + + ))} + + + + + + setEditingRow({ + ...editingRow, + standardNumber: e.target.value, + }) + } + /> + + + + + + setEditingRow({ + ...editingRow, + standardTitle: e.target.value, + }) + } + /> + + + + + + + +
+ + + + + + + + + + + {standards.map((standard, index) => ( + + + + + + + + + ))} + +
LevelStandardNameRevision
{standard.standardLevel}{standard.standardNumber}{standard.standardTitle}{standard.revision} + + + +
+ + ); +}; + +export default StandardsList; diff --git a/src/Pages/Details.js b/src/Pages/Details.js new file mode 100644 index 0000000..86e4735 --- /dev/null +++ b/src/Pages/Details.js @@ -0,0 +1,13 @@ +import React from 'react' +import ParagraphDetails from '../Components/Paragraph/ParagraphDetails' +import Container from 'react-bootstrap/esm/Container' + +const Details = () => { + return ( + + + + ) +} + +export default Details \ No newline at end of file diff --git a/src/Pages/Home.js b/src/Pages/Home.js new file mode 100644 index 0000000..5bb6aad --- /dev/null +++ b/src/Pages/Home.js @@ -0,0 +1,10 @@ +import React from "react"; +import HomePage from "../Components/HomePage/HomePage"; +import Container from "react-bootstrap/esm/Container"; +const Home = () =>{ + return ( + + ); +} + +export default Home; \ No newline at end of file diff --git a/src/Pages/HomePage.js b/src/Pages/HomePage.js deleted file mode 100644 index fcc5770..0000000 --- a/src/Pages/HomePage.js +++ /dev/null @@ -1,7 +0,0 @@ -import React from "react"; - -const HomePage = () =>{ - return (
Home Page
); -} - -export default HomePage; \ No newline at end of file diff --git a/src/Pages/Levels.js b/src/Pages/Levels.js index 27e3603..53df7aa 100644 --- a/src/Pages/Levels.js +++ b/src/Pages/Levels.js @@ -1,7 +1,11 @@ import React from "react"; +import LevelsList from "../Components/LevelsList/LevelsList" +import Container from "react-bootstrap/esm/Container"; const Levels = () => { - return
Levels
; + return ( + + ); }; export default Levels; diff --git a/src/Pages/NewParagraph.js b/src/Pages/NewParagraph.js index 85074b3..1b74ad4 100644 --- a/src/Pages/NewParagraph.js +++ b/src/Pages/NewParagraph.js @@ -1,7 +1,13 @@ import React from "react"; +import NewParagraphForm from "../Components/NewParagraphForm/NewParagraphForm"; +import Container from "react-bootstrap/esm/Container"; const NewParagraph = () => { - return
NewParagraph
; + return ( + + + + ); }; export default NewParagraph; diff --git a/src/Pages/Standards.js b/src/Pages/Standards.js index 6781bc7..bbbe007 100644 --- a/src/Pages/Standards.js +++ b/src/Pages/Standards.js @@ -1,7 +1,13 @@ import React from "react"; +import StandardsList from "../Components/StandardsList/StandardsList"; +import Container from "react-bootstrap/esm/Container"; const Standards = () => { - return
Standards
; + return ( + + + + ); }; export default Standards;