Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,6 @@ const TechnologiesOfInterest = ({
onChange,
editable,
setEditable,
setIsDataChanged,
}) => {
return (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Modal, Button, ModalBody } from "react-bootstrap";

//modal for confirming if the user wants to delete selected articles
export default function ConfirmDeleteModal({
isOpen,
handleClose,
submitHandler,
}) {
return (
<Modal show={isOpen} onHide={handleClose} centered>
<Modal.Header closeButton className="border-0" />
<ModalBody>
<h5 className="text-center">
Are you sure you want to remove the article(s) you selected?
</h5>
<div className="d-flex justify-content-between mt-4">
<Button
className="border-0 text-dark fw-medium"
style={{ backgroundColor: "#B9B2B2" }}
onClick={handleClose}
>
Cancel
</Button>

<Button
className="border-0 text-dark fw-medium"
style={{ backgroundColor: "#24BEEF" }}
type="submit"
onClick={() => {
handleClose();
submitHandler();
}}
>
Yes
</Button>
</div>
</ModalBody>
</Modal>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Card, Container, Image, Row, Col, Button } from "react-bootstrap";
import { TrashFill, BookmarkPlusFill } from "react-bootstrap-icons";
import "./SavedArticleItem.scss";
export default function SavedArticleItem({
articleImg,
articleTitle,
articleDesc,
toBeDeleted,
deleteToggler,
}) {
return (
<Container
gap={3}
className={`${
toBeDeleted ? "to-be-deleted-article" : "kept-article"
} mb-3 p-3 rounded-4 mx-0`}
>
<Row>
<Col xs={3}>
<Image
src={articleImg}
className="object-fit-contain w-100"
height={100}
/>
</Col>
<Col xs={7}>
<Card.Body>
<Card.Title className="fw-bold">{articleTitle}</Card.Title>
<Card.Text className="mt-2">{articleDesc}</Card.Text>
</Card.Body>
</Col>
<Col xs={2} className="d-flex align-items-start justify-content-end">
<Button className="bg-transparent border-0">
{/*User will be able to either add back or remove the current article depending on article's state */}
{toBeDeleted ? (
<BookmarkPlusFill className="fs-4" onClick={deleteToggler} />
) : (
<TrashFill className="fs-4" onClick={deleteToggler} />
)}
</Button>
</Col>
</Row>
</Container>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@import "../../../../scss/variables";
@import "bootstrap/scss/functions";

.kept-article {
background: $primary;
}

//make article lighter if selected to be deleted
.to-be-deleted-article {
background: tint-color($primary, 50%);

& img,
.card-title,
.card-text {
opacity: 0.5;
}
}
150 changes: 147 additions & 3 deletions frontend/src/Components/Settings/SavedArticles/SavedArticles.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,148 @@
import { useEffect, useState } from "react";
import { Container, Form, Stack, Button } from "react-bootstrap";
import SavedArticleItem from "./SaveArticleItem/SavedArticleItem";
import ConfirmDeleteModal from "./ConfirmDeleteModal";

const test_articles = [
{
id: 0,
image:
"https://builtin.com/cdn-cgi/image/f=auto,quality=80,width=752,height=435/https://builtin.com/sites/www.builtin.com/files/styles/byline_image/public/2021-12/machine-learning-examples-applications.png",
title: "Intro to Machine Learning",
description: "Brief description about this topic...",
},
{
id: 1,
image:
"https://imageio.forbes.com/specials-images/dam/imageserve/966248982/960x0.jpg?height=456&width=711&fit=bounds",
title: "Intro to Machine Learning",
description: "Brief description about this topic...",
},
{
id: 2,
image:
"https://www.mathworks.com/solutions/machine-learning/_jcr_content/mainParsys/band_copy_1919605364/mainParsys/columns/a03cc495-1c23-4402-82ea-1c8fd4d25234/pictogram.adapt.full.medium.svg/1701252724596.svg",
title: "Intro to Machine Learning",
description: "Brief description about this topic...",
},
];

export default function SavedArticles() {
return <h1>Saved Articles</h1>;
}

//array all the articles currently not deleted
const [articles, setArticles] = useState([]);

//the state of the articles of whether they are being deleted or not, is object, key = article id, value = whether it is selected orn ot
const [isDeletedArticles, setIsDeletedArticles] = useState({});

//toggler of selected state for a specific article
//this returns a FUNCTION that toggles an article's state with the provided id
const articleToggleHandler = (id) => () =>
setIsDeletedArticles((prevArticles) => {
return { ...prevArticles, [id]: !prevArticles[id] };
});

//deselects all articles from deletion
const resetDeletionHandler = () => {
let resetArticles = {};
articles.forEach(({ id }) => {
resetArticles[id] = false;
});
setIsDeletedArticles(resetArticles);
};

//use effect to get articles upon page load once, also init selected state of every article as false
//just simulating retrieving articles
useEffect(() => {
let initArticles = async () => {
let retrieved_articles = await test_articles;
setArticles(retrieved_articles);

let initIsDeletedArticles = {};
retrieved_articles.forEach(({ id }) => {
initIsDeletedArticles[id] = false;
});
setIsDeletedArticles(initIsDeletedArticles);
};

initArticles();
}, []);

//submit handler (the yes button in modal does not trigger submit event)
//simply removed the selected articles from the displayed articles state
//insert backend actions here
const submitHandler = () => {
//filter out kept articles, replace articles state with them
let keptArticles = articles.filter(
(article) => !isDeletedArticles[article.id]
);
setArticles(keptArticles);

//reset selected state
let initIsDeletedArticles = {};
keptArticles.forEach(({ id }) => {
initIsDeletedArticles[id] = false;
});
setIsDeletedArticles(initIsDeletedArticles);
};

//state for whether delete confirmation modal is displayed or now
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);

//show and hide handlers
const showDeleteConfirm = () => setDeleteConfirmOpen(true);
const hideDeleteConfirm = () => setDeleteConfirmOpen(false);

return (
<Container className="my-3">
<Form>
<h4 className="ps-2 py-1 border-start border-4 border-primary">
My Bookmarks
</h4>
<Container className="p-0 pt-4">
{articles.map((article) => (
<SavedArticleItem
key={article.id}
articleImg={article.image}
articleDesc={article.description}
articleTitle={article.title}
toBeDeleted={isDeletedArticles[article.id]}
deleteToggler={articleToggleHandler(article.id)}
/>
))}
</Container>
{/*Remove/Cancel will only show if there are any articles selected to be deleted*/}
{Object.values(isDeletedArticles).some((isDeleted) => isDeleted) && (
<Stack
direction="horizontal"
gap={3}
className="mt-3 justify-content-end"
>
<div>
<Button
className="border-0 text-dark fw-medium"
style={{ backgroundColor: "#24BEEF" }}
onClick={showDeleteConfirm}
>
Remove
</Button>
</div>
<div>
<Button
className="border-0 text-dark fw-medium"
style={{ backgroundColor: "#B9B2B2" }}
onClick={resetDeletionHandler}
>
Cancel
</Button>
</div>
</Stack>
)}
<ConfirmDeleteModal
isOpen={deleteConfirmOpen}
handleClose={hideDeleteConfirm}
submitHandler={submitHandler}
/>
</Form>
</Container>
);
}