cleint front-end

This commit is contained in:
louai98 2023-05-25 16:14:22 +02:00
parent 98b53a0432
commit 06171b0b62
17 changed files with 180 additions and 76 deletions

View File

@ -1,19 +1,23 @@
import logo from "./logo.svg"; import logo from "./logo.svg";
import "./App.css"; import "./App.css";
import { useState, useEffect } from "react";
import Search from "./Components/Layout/Search/Search"; import Search from "./Components/Layout/Search/Search";
import Filter from "./Components/Layout/Filter/Filter"; import Filter from "./Components/Layout/Filter/Filter";
import ComplainceFilter from "./Components/Layout/Filter/ComplainceFilter"; import ComplainceFilter from "./Components/Layout/Filter/ComplainceFilter";
import Results from "./Components/Layout/Results/Results"; import Results from "./Components/Layout/Results/Results";
import { Fragment, useState, useEffect } from "react"; import AppliedFilters from "./Components/Layout/AppliedFilters/AppliedFilters";
import IddrsImg from "./Static/Imgs/IDDRS.png"
import axios from "axios"; import axios from "axios";
import FeaturedQuestions from "./Components/Layout/FeaturedQuestions/FeaturedQuestions"; import FeaturedQuestions from "./Components/Layout/FeaturedQuestions/FeaturedQuestions";
import WordCloud_ from "./Components/Layout/WordCloud_/WordCloud_"; import WordCloud_ from "./Components/Layout/WordCloud_/WordCloud_";
import Stack from "react-bootstrap/esm/Stack"; import Stack from "react-bootstrap/esm/Stack";
import Container from "react-bootstrap/esm/Container"; import Container from "react-bootstrap/esm/Container";
import Image from 'react-bootstrap/Image';
function App() { function App() {
const [data, setData] = useState(""); const [data, setData] = useState("");
const [phrase, setPhrase] = useState("");
const [showResults, setShowResults] = useState(false);
const [results, setResults] = useState([]); const [results, setResults] = useState([]);
const [filteredResults, setFilteredResults] = useState([]); const [filteredResults, setFilteredResults] = useState([]);
const [filtersComplaince, setFiltersComplaince] = useState([]); const [filtersComplaince, setFiltersComplaince] = useState([]);
@ -32,9 +36,11 @@ function App() {
event.preventDefault(); event.preventDefault();
// Send the input value to Django // Send the input value to Django
axios axios
.post("http://localhost:8000/get_input/", { data }) .post("http://localhost:8000/client_api/get_input/", { data })
.then((response) => { .then((response) => {
setPhrase(data['phrase'])
setResults(response.data.results); setResults(response.data.results);
setShowResults(true)
setFilteredResults(response.data.results); setFilteredResults(response.data.results);
}) })
.catch((error) => { .catch((error) => {
@ -45,8 +51,9 @@ function App() {
const handleSubmit_ = (value) => { const handleSubmit_ = (value) => {
// Send the input value to Django // Send the input value to Django
axios axios
.post("http://localhost:8000/get_input/", { data: { phrase: value } }) .post("http://localhost:8000/client_api/get_input/", { data: { phrase: value } })
.then((response) => { .then((response) => {
setShowResults(true);
setResults(response.data.results); setResults(response.data.results);
setFilteredResults(response.data.results); setFilteredResults(response.data.results);
}) })
@ -106,21 +113,31 @@ function App() {
}; };
return ( return (
<Fragment>
<main> <main>
<Container> <Container>
<Image className="mt-5" src={IddrsImg} alt="IDDRS" fluid />
<Search onSubmit={handleSubmit} onChange={handleChange} /> <Search onSubmit={handleSubmit} onChange={handleChange} />
<Filter onFilterChange={handleStandardsFilterChange} /> {showResults && <Filter onFilterChange={handleStandardsFilterChange} />}
{showResults && (
<ComplainceFilter onFilterChange={handleComplainceFilterChange} /> <ComplainceFilter onFilterChange={handleComplainceFilterChange} />
<Results results={filteredResults} /> )}
<Stack direction="horizontal" gap={3} as="div" className="d-flex justify-content-between"> {filtersStandards.length > 0 && <AppliedFilters standards={filtersStandards} />}
{showResults && (
<Results
results={filteredResults}
complainceFilter={filtersComplaince}
searchedPhrase = {phrase}
/>
)}
<Stack className="mt-5" direction="horizontal" gap={2}>
<FeaturedQuestions handleButtonClick={handleSubmit_} /> <FeaturedQuestions handleButtonClick={handleSubmit_} />
<WordCloud_ /> <WordCloud_ />
</Stack> </Stack>
</Container> </Container>
</main> </main>
</Fragment>
); );
} }

5
src/AppContext.js Normal file
View File

@ -0,0 +1,5 @@
import React from "react";
const AppContext = React.createContext();
export default AppContext;

View File

@ -0,0 +1,25 @@
import React from "react";
import Stack from "react-bootstrap/esm/Stack";
import Accordion from "react-bootstrap/Accordion";
const AppliedFilters = (props) => {
return (
<div className="mt-5">
<Accordion defaultActiveKey="0">
<Accordion.Item eventKey="0">
<Accordion.Header><h5>The following filter/s are applied: </h5></Accordion.Header>
<Accordion.Body>
<Stack className="ms-5">
{props.standards.map((standard, index) => (
<h6 className="fw-lighter fst-italic" key={index}>
{standard}
</h6>
))}
</Stack>
</Accordion.Body>
</Accordion.Item>
</Accordion>
</div>
);
};
export default AppliedFilters;

View File

@ -1,5 +1,4 @@
import React, { useState } from "react"; import React, { useState } from "react";
import Container from "react-bootstrap/esm/Container";
import Stack from "react-bootstrap/esm/Stack"; import Stack from "react-bootstrap/esm/Stack";
import Card from "react-bootstrap/Card"; import Card from "react-bootstrap/Card";
@ -18,7 +17,6 @@ const FeaturedQuestions = ({ handleButtonClick }) => {
]; ];
return ( return (
<div>
<Card className="p-3"> <Card className="p-3">
<h4 className="text-decoration-underline"> <h4 className="text-decoration-underline">
Featured Questions About IDDRS Framework Featured Questions About IDDRS Framework
@ -33,7 +31,6 @@ const FeaturedQuestions = ({ handleButtonClick }) => {
))} ))}
</Stack> </Stack>
</Card> </Card>
</div>
); );
}; };

View File

@ -1,7 +1,6 @@
import { useState } from "react"; import { useState } from "react";
import ToggleButton from "react-bootstrap/ToggleButton"; import ToggleButton from "react-bootstrap/ToggleButton";
import ToggleButtonGroup from "react-bootstrap/ToggleButtonGroup"; import ToggleButtonGroup from "react-bootstrap/ToggleButtonGroup";
import Container from "react-bootstrap/esm/Container";
import classes from "./ComplainceFilter.module.css"; import classes from "./ComplainceFilter.module.css";
function ComplainceFilter({ onFilterChange }) { function ComplainceFilter({ onFilterChange }) {
@ -14,15 +13,14 @@ function ComplainceFilter({ onFilterChange }) {
* not using it in this example so we will omit it. * not using it in this example so we will omit it.
*/ */
const handleChange = (val) => { const handleChange = (val) => {
//console.log(val);
setValue(val); setValue(val);
onFilterChange(val); onFilterChange(val);
}; };
const complaince = ["Shall", "Should", "May", "Must", "Can"]; const complaince = ["Shall", "Should", "May", "Must", "Can"];
return ( return (
<div className="mt-3"> <div className="mt-5 d-flex align-items-baseline">
<h5>Compliance Check:</h5> <h5 className="me-3">Compliance Check:</h5>
<ToggleButtonGroup type="checkbox" value={value} onChange={handleChange}> <ToggleButtonGroup type="checkbox" value={value} onChange={handleChange}>
{complaince.map((comp, index) => ( {complaince.map((comp, index) => (
<ToggleButton <ToggleButton
@ -30,7 +28,7 @@ function ComplainceFilter({ onFilterChange }) {
variant="outline-secondary" variant="outline-secondary"
key={index} key={index}
id={`comp-btn-${index}`} id={`comp-btn-${index}`}
className={classes["comp-btn"]} className={classes['hover-effect']}
value={comp} value={comp}
> >
{comp} {comp}

View File

@ -0,0 +1,5 @@
.hover-effect:hover{
background-color: #6c757d !important; /* Set the background color on hover */
color: #fff !important; /* Set the text color on hover */
/* Add any other styles you want for the hover effect */
}

View File

@ -1,9 +1,9 @@
import React, { useEffect, useState, Fragment } from "react"; import React, { useEffect, useState } from "react";
import Container from "react-bootstrap/Container";
import Dropdown from "react-bootstrap/Dropdown"; import Dropdown from "react-bootstrap/Dropdown";
import classes from "../../../Static/styles.module.css"; import classes from "../../../Static/styles.module.css";
import ToggleButton from "react-bootstrap/esm/ToggleButton"; import ToggleButton from "react-bootstrap/esm/ToggleButton";
import ToggleButtonGroup from "react-bootstrap/esm/ToggleButtonGroup"; import ToggleButtonGroup from "react-bootstrap/esm/ToggleButtonGroup";
import filterClasses from "./Filter.module.css";
import axios from "axios"; import axios from "axios";
@ -21,21 +21,21 @@ const Filter = ({ onFilterChange }) => {
useEffect(() => { useEffect(() => {
axios axios
.get("http://127.0.0.1:8000/levels/") .get("http://127.0.0.1:8000/client_api/levels/")
.then((response) => setLevels(response.data)) .then((response) => setLevels(response.data))
.catch((error) => console.log(error)); .catch((error) => console.log(error));
}, []); }, []);
useEffect(() => { useEffect(() => {
axios axios
.get("http://127.0.0.1:8000/standards/") .get("http://127.0.0.1:8000/client_api/standards/")
.then((response) => setStandards(response.data)) .then((response) => setStandards(response.data))
.catch((error) => console.log(error)); .catch((error) => console.log(error));
}, []); }, []);
return ( return (
<div className="mt-5"> <div className="mt-5">
<h5>Filter by standards:</h5> <h5>Filter by Standards:</h5>
<div className="d-flex justify-content-between mt-2"> <div className="d-flex justify-content-between mt-2">
{levels.map((level) => ( {levels.map((level) => (
@ -66,7 +66,7 @@ const Filter = ({ onFilterChange }) => {
variant="outline-secondary" variant="outline-secondary"
key={standard.id} key={standard.id}
id={`std-btn-${standard.id}`} id={`std-btn-${standard.id}`}
//className={classes["comp-btn"]} className={classes[`list-items-${standard.standardLevel}`]}
value={standard.standardTitle} value={standard.standardTitle}
> >
{standard.standardTitle} {standard.standardTitle}

View File

@ -1,6 +1,5 @@
import React, { useState } from "react"; import React, { useState } from "react";
import Card from "../../UI/Card/Card"; import Card from "../../UI/Card/Card";
import Container from "react-bootstrap/esm/Container";
import Stack from "react-bootstrap/Stack"; import Stack from "react-bootstrap/Stack";
import ResultsItem from "./ResultsItem"; import ResultsItem from "./ResultsItem";
import ResultModal from "../../UI/ResultModal/ResultModal"; import ResultModal from "../../UI/ResultModal/ResultModal";
@ -39,7 +38,7 @@ const Results = (props) => {
{/* <PDFGenerator results={props.results} /> */} {/* <PDFGenerator results={props.results} /> */}
<Card className="secondary p-1"> <Card className="secondary p-1">
<h2 className="text-secondary text-center">Searched Phrase</h2> <h2 className="text-secondary text-center">{props.searchedPhrase}</h2>
<Stack gap={3} className="mt-3"> <Stack gap={3} className="mt-3">
{currentPost.map((result) => ( {currentPost.map((result) => (
@ -50,6 +49,8 @@ const Results = (props) => {
title={result.Title} title={result.Title}
heading1={result.Heading1} heading1={result.Heading1}
paragraph={result.Paragraph} paragraph={result.Paragraph}
complainceFilter = {props.complainceFilter}
pageNumber = {result.PageNum}
></ResultsItem> ></ResultsItem>
))} ))}
</Stack> </Stack>
@ -74,6 +75,7 @@ const Results = (props) => {
heading4={selectedResult ? selectedResult.Heading4 : ""} heading4={selectedResult ? selectedResult.Heading4 : ""}
pageNumber={selectedResult ? selectedResult.PageNum : ""} pageNumber={selectedResult ? selectedResult.PageNum : ""}
paragraph={selectedResult ? selectedResult.Paragraph : ""} paragraph={selectedResult ? selectedResult.Paragraph : ""}
sentence={selectedResult ? selectedResult.Sentence : ""}
></ResultModal> ></ResultModal>

View File

@ -1,27 +1,34 @@
import React from "react"; import React from "react";
import Card from "react-bootstrap/Card"; import Card from "react-bootstrap/Card";
import classes from "../../../Static/styles.module.css" import classes from "../../../Static/styles.module.css";
import Stack from "react-bootstrap/esm/Stack";
import Badge from "react-bootstrap/Badge";
const ResultsItem = (props) => { const ResultsItem = (props) => {
const truncateText = (text, maxLength) => { const truncateText = (text, maxLength) => {
if (text.length > maxLength) { if (text.length > maxLength) {
return text.substring(0, maxLength) + "..."; return text.substring(0, maxLength) + "...";
} else { } else {
return text; return text;
} }
} };
return ( return (
<Stack direction="horizontal">
<Card onClick={props.onClick}> <Card onClick={props.onClick}>
<Card.Header className={classes[`level-text-${props.level}`]}> <Card.Header className={classes[`level-text-${props.level}`]}>
Level {props.level}, {props.title} Level {props.level}, {props.title} <span className="float-end">Page: {props.pageNumber}</span>
</Card.Header> </Card.Header>
<Card.Body> <Card.Body>
<Card.Title>{props.heading1}</Card.Title> <Card.Title>{props.heading1}</Card.Title>
<Card.Text>{truncateText(props.paragraph, 300)} </Card.Text> <Card.Text>{truncateText(props.paragraph, 300)} </Card.Text>
</Card.Body> </Card.Body>
</Card> </Card>
<Stack gap={2}>
{props.complainceFilter.map((comp) => (
<Badge bg="secondary">{comp}</Badge>
))}
</Stack>
</Stack>
); );
}; };

View File

@ -1,12 +1,9 @@
import React, { useState } from "react"; import React, { useState } from "react";
import Input from "../../UI/Input/Input"; import Input from "../../UI/Input/Input";
import Button_ from "../../UI/Button/Button_";
import Form from "react-bootstrap/Form"; import Form from "react-bootstrap/Form";
import Container from "react-bootstrap/Container"; import Button from "react-bootstrap/esm/Button";
const Search = (props) => { const Search = (props) => {
return ( return (
<div className="mt-5"> <div className="mt-5">
<Form <Form
@ -24,10 +21,8 @@ const Search = (props) => {
aria-describedby="basic-addon1" aria-describedby="basic-addon1"
onChange={props.onChange} onChange={props.onChange}
></Input> ></Input>
<Button id="submitSearch" type="submit" variant="outline-secondary" size="lg">Search</Button>
<Button_ id="submitSearch" type="submit" className="btn-default">
Search
</Button_>
</Form> </Form>
</div> </div>
); );

View File

@ -1,7 +1,5 @@
import React, { Fragment } from "react"; import React, { Fragment } from "react";
import { render } from "react-dom";
import WordCloud from "react-d3-cloud"; import WordCloud from "react-d3-cloud";
import Container from "react-bootstrap/esm/Container";
import Card from "react-bootstrap/Card"; import Card from "react-bootstrap/Card";
const data = [ const data = [
@ -90,18 +88,18 @@ const data = [
const WordCloud_ = () => { const WordCloud_ = () => {
return ( return (
<div> <Card className="w-50">
<Card> <h4 className="text-decoration-underline text-center">
<h4 className="text-decoration-underline">
Most Frequent Key-Phrases Most Frequent Key-Phrases
</h4> </h4>
<WordCloud <WordCloud
width={1000} //width={200}
data={data} data={data}
font="Times" font="Times"
fontStyle="italic" fontStyle="italic"
//fontWeight="bold" //fontWeight="bold"
fontSize={(word) => Math.log2(word.value) * 3} fontSize={(word) => Math.log2(word.value) * 5 }
//fontSize={33}
spiral="rectangular" spiral="rectangular"
rotate={0} rotate={0}
fill = "gray" fill = "gray"
@ -111,7 +109,6 @@ const WordCloud_ = () => {
}} }}
/> />
</Card> </Card>
</div>
); );
}; };

View File

@ -5,17 +5,17 @@ import InputGroup from "react-bootstrap/InputGroup";
const Input = (props) => { const Input = (props) => {
return ( return (
<InputGroup size="lg">
<Form.Control <Form.Control
placeholder={props.placeholder} placeholder={props.placeholder}
type={props.type} type={props.type}
id={props.id} id={props.id}
name={props.name} name={props.name}
aria-label={props['aria-label']} aria-label={props["aria-label"]}
aria-describedby = {props['aria-describedby']} aria-describedby={props["aria-describedby"]}
onChange={props.onChange} onChange={props.onChange}
/> />
</InputGroup>
); );
}; };

View File

@ -1,22 +1,47 @@
import React, { useState } from "react"; import React, { useState } from "react";
import Modal from "react-bootstrap/Modal"; import Modal from "react-bootstrap/Modal";
import Button from "react-bootstrap/esm/Button"; import Button from "react-bootstrap/esm/Button";
import classes from "../../../Static/styles.module.css" import classes from "../../../Static/styles.module.css";
const ResultModal = (props) => { const ResultModal = (props) => {
const HighlightSubstring = ({ text, substring }) => {
const highlightStyle = {
backgroundColor: "yellow",
fontWeight: "bold",
};
// Split the text into parts based on the substring
const parts = text.split(new RegExp(`(${substring})`, "gi"));
console.log(parts)
// Wrap the matching parts with a span and apply the highlight style
const highlightedText = parts.map((part, index) => {
if (part == substring) {
return (
<span key={index} style={highlightStyle}>
{part}
</span>
);
}
return part;
});
return highlightedText;
};
return ( return (
<Modal <Modal
{...props} {...props}
size='xl' size="xl"
aria-labelledby="example-custom-modal-styling-title" aria-labelledby="example-custom-modal-styling-title"
centered centered
onHide={props.handleClose} onHide={props.handleClose}
> >
<Modal.Header closeButton> <Modal.Header closeButton>
<Modal.Title id="example-custom-modal-styling-title"> <Modal.Title id="example-custom-modal-styling-title">
<div className={classes[`level-text-${props.level}`]}>Level {props.level}</div> <div className={classes[`level-text-${props.level}`]}>
Level {props.level}
</div>
<div className="mb-3">IDDRS - {props.module}</div> <div className="mb-3">IDDRS - {props.module}</div>
<p className="fs-5 fw-light mb-0">{props.heading1}</p> <p className="fs-5 fw-light mb-0">{props.heading1}</p>
<p className="fs-6 fw-light mb-0">{props.heading2}</p> <p className="fs-6 fw-light mb-0">{props.heading2}</p>
@ -25,8 +50,10 @@ const ResultModal = (props) => {
</Modal.Title> </Modal.Title>
</Modal.Header> </Modal.Header>
<Modal.Body> <Modal.Body>
<p className="fs-6 fw-light">{props.paragraph}</p> <p className="fs-6 fw-light"><HighlightSubstring text={props.paragraph} substring={props.sentence} /></p>
<div className="fs-5 fw-light mt-4"><p class="text-end">Page: {props.pageNumber}</p></div> <div className="fs-5 fw-light mt-4">
<p class="text-end">Page: {props.pageNumber}</p>
</div>
</Modal.Body> </Modal.Body>
<Modal.Footer> <Modal.Footer>
<Button onClick={props.handleClose}>Close</Button> <Button onClick={props.handleClose}>Close</Button>

View File

@ -1,7 +1,6 @@
import React, { useState } from "react"; import React, { useState } from "react";
import classes from "./ResultsPagination.module.css"; import classes from "./ResultsPagination.module.css";
import Button from "react-bootstrap/esm/Button"; import Button from "react-bootstrap/esm/Button";
import Pagination from "react-bootstrap/Pagination";
const ResultsPagination = ({ const ResultsPagination = ({
totalPosts, totalPosts,

BIN
src/Static/Imgs/IDDRS.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@ -51,3 +51,33 @@
.level-text-6 { .level-text-6 {
color: #CF7AB2 !important; color: #CF7AB2 !important;
} }
.list-items-1:hover {
color: #fff !important;
background-color: #f07f4e54 !important;
}
.list-items-2:hover {
color: #fff !important;
background-color: #008DCA54 !important;
}
.list-items-3:hover {
color: #fff !important;
background-color: #00A55454 !important;
}
.list-items-4:hover {
color: #fff !important;
background-color: #7366A354 !important;
}
.list-items-5:hover {
color: #fff !important;
background-color: #D1000754 !important;
}
.list-items-6:hover {
color: #fff !important;
background-color: #CF7AB254 !important;
}