diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..25c8fdba
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+package-lock.json
\ No newline at end of file
diff --git a/README.md b/README.md
index 95e87fdf..0642631e 100644
--- a/README.md
+++ b/README.md
@@ -1,60 +1,42 @@
-`#html` `#css` `#js` `#dom` `#JSON` `#HTTP` `#API` `#Bootstrap` `#master-in-software-development`
+# News Blog with API
+Our blog adopts a design inspired by well-known newspapers and news websites, such as The Guardian, The Washington Post, The New York Times. Our goal for the design focused on simplicity and functionality for the user.
-# Blog with API
+It was important for us to be able to access different endpoints for the {JSON}Placeholder API. Although we did not incorporate POST and DELETE functions for the blog posts, the code is scalable and this implementation is planned for future versions.
-
-
-
+
-> In this pill you will put into practice the knowledge learned about making HTTP requests to create a blog consuming the information from a third-party API. You will also learn how to use Bootstrap Framework for the layout.
-## Index
+## Planning
-- [Requirements](#requirements)
-- [Repository](#repository)
-- [Technologies used](#technologies-used)
-- [Project delivery](#project-delivery)
-- [Resources](#resources)
+We began the project by outlining the project, using tools like Trello for workflow, as well as Figma and Miró for basic design plans.
-## Requirements
-
-- You must use semantic HTML5 elements for all the contents of the application
-- You must use JSON server library to create your own local repository
-- You must use fecth to do the requests
-- You have to use Bootstrap Framework for the Layout and the styles
-
-
-## Repository
+Day 1 of the project was dedicated toward planning the functionality and design of the app.
-First of all you must fork this project into your GitHub account.
+Day 2 began by setting up a local JSON server for testing, then moving on to basic HTML structure and Javascript functionality.
-To create a fork on GitHub is as easy as clicking the “fork” button on the repository page.
+On Day 3, we focused on accessing the JSON data and displaying successfully on the main page, testing several methods, finally settling on *fetch*, using a mix of standard and async functions.
-
+On Day 4, the design and visual aspect was improved by further integrating Bootstrap 5 classes into the HTML and Javascript. Then time was spent refactoring main functions.
-## Technologies used
-\* HTML
+
-\* CSS
+
-\* JS
+
-\* Bootstrap
-
-\* HTTP Requests
-
-\* JSON
-
-\* API
+## Requirements
-## Project delivery
+-Use {JSON}Placeholder API with endpoints for users, comments, and posts.
+-Use fetch method for HTTP requests.
+-Use Bootstrap to add style to the website.
-To deliver this project you must follow the steps indicated in the document:
+## Attribution
-- [Submitting a solution](https://www.notion.so/Submitting-a-solution-524dab1a71dd4b96903f26385e24cdb6)
+Illustration on main page from Undraw (https://undraw.co/illustrations)
+Icon on header by Icons8 (https://icons8.com)
-## Resources
+## The Team
-- [JSON server](https://github.com/typicode/json-server)
-- [Official Bootstrap](https://getbootstrap.com/)
\ No newline at end of file
+Alejandro Gaerste Steger (https://github.com/Gaerste/)
+Blake Johnson (https://github.com/blakejohns5)
\ No newline at end of file
diff --git a/assets/header_img.png b/assets/header_img.png
new file mode 100644
index 00000000..06fca57b
Binary files /dev/null and b/assets/header_img.png differ
diff --git a/assets/icons8-bugle-50.png b/assets/icons8-bugle-50.png
new file mode 100644
index 00000000..c34bfb78
Binary files /dev/null and b/assets/icons8-bugle-50.png differ
diff --git a/assets/miro_img01.png b/assets/miro_img01.png
new file mode 100644
index 00000000..e711c20f
Binary files /dev/null and b/assets/miro_img01.png differ
diff --git a/assets/miro_img02.png b/assets/miro_img02.png
new file mode 100644
index 00000000..c4f261cc
Binary files /dev/null and b/assets/miro_img02.png differ
diff --git a/assets/miro_img03.png b/assets/miro_img03.png
new file mode 100644
index 00000000..55217488
Binary files /dev/null and b/assets/miro_img03.png differ
diff --git a/assets/undraw_news_re_6uub_tall_fit.svg b/assets/undraw_news_re_6uub_tall_fit.svg
new file mode 100644
index 00000000..4c1f29ca
--- /dev/null
+++ b/assets/undraw_news_re_6uub_tall_fit.svg
@@ -0,0 +1,226 @@
+
+
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..e437cb04
--- /dev/null
+++ b/index.html
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+ The Dispatch
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..e9d17b0d
--- /dev/null
+++ b/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "blog-with-api",
+ "version": "1.0.0",
+ "description": "`#html` `#css` `#js` `#dom` `#JSON` `#HTTP` `#API` `#Bootstrap` `#master-in-software-development`",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "server": "json-server --watch ./src/data/db.json"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/Gaerste/blog-with-api.git"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/Gaerste/blog-with-api/issues"
+ },
+ "homepage": "https://github.com/Gaerste/blog-with-api#readme",
+ "dependencies": {
+ "json-server": "^0.17.0"
+ }
+}
diff --git a/src/css/style.css b/src/css/style.css
new file mode 100644
index 00000000..5fe13363
--- /dev/null
+++ b/src/css/style.css
@@ -0,0 +1,72 @@
+@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,300;0,6..72,400;0,6..72,500;0,6..72,600;0,6..72,700;0,6..72,800;1,6..72,300;1,6..72,400;1,6..72,500;1,6..72,600;1,6..72,700;1,6..72,800&display=swap');
+/* fonts */
+@font-face {
+ font-family: "old london";
+ src: url("/src/fonts/old_london/OldLondon.ttf");
+}
+/* header src */
+.src__header{
+ transition: all 0.85s cubic-bezier(0.68, -0.55, 0.265, 1.55);
+ content: '';
+ width: 50%;
+ height: 100%;
+ background: black;
+ height: 4vh;
+}
+/* btn src */
+.btn{
+ transition: all 0.85s cubic-bezier(0.68, -0.55, 0.265, 1.55);
+ content: '';
+ width: 50%;
+ height: 100%;
+ background: black;
+ height: 4vh;
+ width: 6vw;
+ text-align: center;
+}
+/* main title */
+.blog__header--title{
+ text-decoration: none;
+ margin: 0 2rem;
+ font-family: 'old london';
+ font-size: 3.5rem;
+}
+/* main background-image */
+.main__content{
+ background-image: url("/assets/undraw_news_re_6uub_tall_fit.svg");
+ background-attachment: fixed;
+ background-repeat: no-repeat;
+ background-size: 53vh;
+ background-position: left;
+ overflow: hidden;
+ background-color: var(--bg-custom);
+}
+.blog__header{
+ background-color: var(--bg-header-custom);
+}
+/* color of the website */
+:root {
+ --bg-custom: #f2ddc3;
+ --bg-header-custom: #ebba7e;
+}
+
+/* nav logo */
+.navbar__logo-img {
+ transform: rotate(-45deg);
+ margin: 0 2rem;
+}
+
+/* post style */
+.post__container {
+ border-bottom: solid .5rem var(--bg-header-custom);
+ border-radius: 1rem 1rem 0 0;
+ font-family: 'Newsreader', serif;
+}
+/* modal font */
+.modal {
+ font-family: 'Newsreader', serif;
+}
+/* color modal */
+.modal__dialog {
+ background-color: hsla(0, 0%, 100%, .25);
+}
diff --git a/data/comments.json b/src/data/comments.json
similarity index 100%
rename from data/comments.json
rename to src/data/comments.json
diff --git a/data/db.json b/src/data/db.json
similarity index 100%
rename from data/db.json
rename to src/data/db.json
diff --git a/data/posts.json b/src/data/posts.json
similarity index 100%
rename from data/posts.json
rename to src/data/posts.json
diff --git a/data/users.json b/src/data/users.json
similarity index 100%
rename from data/users.json
rename to src/data/users.json
diff --git a/src/fonts/old_london/OldLondon.ttf b/src/fonts/old_london/OldLondon.ttf
new file mode 100644
index 00000000..f24a3401
Binary files /dev/null and b/src/fonts/old_london/OldLondon.ttf differ
diff --git a/src/fonts/old_london/OldLondonAlternate.ttf b/src/fonts/old_london/OldLondonAlternate.ttf
new file mode 100644
index 00000000..6010ffdd
Binary files /dev/null and b/src/fonts/old_london/OldLondonAlternate.ttf differ
diff --git a/src/fonts/old_london/Olondon_.otf b/src/fonts/old_london/Olondon_.otf
new file mode 100644
index 00000000..9a68b206
Binary files /dev/null and b/src/fonts/old_london/Olondon_.otf differ
diff --git a/src/fonts/old_london/Olondona.otf b/src/fonts/old_london/Olondona.otf
new file mode 100644
index 00000000..56161c85
Binary files /dev/null and b/src/fonts/old_london/Olondona.otf differ
diff --git a/src/js/info-modal.js b/src/js/info-modal.js
new file mode 100644
index 00000000..0dc0cfc3
--- /dev/null
+++ b/src/js/info-modal.js
@@ -0,0 +1,96 @@
+import { getComments } from "./main.js";
+
+//modal display in the main content
+function openPost() {
+ const modalOpen = new bootstrap.Modal(document.getElementById("modal"));
+ const commentsBtn = document.getElementById("commentsContentBtn");
+ modalOpen.show();
+ removeComments();
+ commentsBtn.addEventListener("click", loadComments);
+}
+
+//show the content of body and title
+function showTitleBody(event) {
+ const postId = event.target.getAttribute("data-post-id");
+ const modalTitle = document.getElementById("modalTitle");
+ const modalBody = document.getElementById("modalText");
+ const postData = fetch("https://jsonplaceholder.typicode.com/posts/");
+ try {
+ postData
+ .then((response) => {
+ return response.json();
+ })
+ .then((data) => {
+ let obj = data.find((item) => item.id == postId); // whe could make typeof!!
+ modalTitle.textContent = obj.title;
+ modalBody.textContent = obj.body;
+ });
+ } catch (error) {
+ alert("Error Data");
+ }
+ setCommentsId(postId);
+}
+
+// Show the email and user in post modal
+function showUserEmail(event) {
+ const userId = event.target.getAttribute("data-user-id");
+ const modalUser = document.getElementById("modalUsername");
+ const modalEmail = document.getElementById("modalEmail");
+ const userData = fetch("https://jsonplaceholder.typicode.com/users");
+ try {
+ userData
+ .then((response) => {
+ return response.json();
+ })
+ .then((data) => {
+ let obj = data.find((item) => item.id == userId); // whe could make typeof!!
+ modalUser.textContent = obj.username;
+ modalEmail.textContent = obj.email;
+ });
+ } catch (error) {
+ alert("Error Data");
+ }
+}
+// Gives comments section a data attribute, to match with the id from the comments json data.
+function setCommentsId(postId) {
+ const commentsContent = document.getElementById("commentsContentBody");
+ commentsContent.setAttribute("data-post-id", `${postId}`);
+}
+
+// Removes comments before displaying more comments
+function removeComments() {
+ const commentsBody = document.getElementById("commentsContentBody");
+ while (commentsBody.firstElementChild) {
+ commentsBody.removeChild(commentsBody.lastElementChild);
+ }
+}
+
+// Loads comments into the bottom of the modal, adds comments formatting
+async function loadComments() {
+ const commentsContent = document.getElementById("commentsContentBody");
+ const commentsTitle = document.createElement("h4");
+ commentsTitle.textContent = "Comments:";
+ commentsTitle.classList.add("fw-bold");
+
+ const modalPostId = parseInt(commentsContent.getAttribute("data-post-id")); // read the attribute for which comments
+ const commentsData = await getComments();
+ const commentsSet = commentsData.filter((comment) => {
+ return comment.postId == modalPostId;
+ });
+ commentsContent.append(commentsTitle);
+ commentsSet.map((comment) => {
+ const commentName = document.createElement("h5");
+ const email = document.createElement("p");
+ const body = document.createElement("p");
+ commentName.textContent = comment.name;
+
+ email.textContent = comment.email;
+ body.textContent = comment.body;
+ body.classList.add("border-bottom", "pb-3");
+
+ commentsContent.append(commentName, email, body);
+ });
+}
+
+//EXPORT
+export { openPost, showTitleBody, showUserEmail };
diff --git a/src/js/main.js b/src/js/main.js
new file mode 100644
index 00000000..662f6568
--- /dev/null
+++ b/src/js/main.js
@@ -0,0 +1,85 @@
+import { openPost, showTitleBody, showUserEmail } from "./info-modal.js";
+import { getSearchResults, displaySearchResults } from "./search.js";
+
+// Variable and listener for searchbar functions
+const searchBtn = document.getElementById('headerSearchBtn');
+searchBtn.addEventListener('click', function () {
+ getSearchResults()
+ displaySearchResults();
+});
+
+// Fetch posts from api for posts, return as .json data, and pass to displayPosts function.
+async function getPostData() {
+ try {
+ const response = await fetch("https://jsonplaceholder.typicode.com/posts/");
+ const postData = response.json();
+ return postData;
+ } catch(error) {
+ alert('Error Data');
+ }
+}
+
+// Call function to define data for window onload
+async function manageData () {
+ const data = await getPostData();
+ displayPosts(data)
+}
+
+window.onload = manageData;
+
+// Get Comments from json server
+async function getComments () {
+ try {
+ const response = await fetch('https://jsonplaceholder.typicode.com/comments');
+ const commentsData = await response.json();
+ return commentsData;
+ } catch(error) {
+ alert('Error Data');
+ }
+}
+
+// Shows the blog posts with title and body on the main page
+function displayPosts(data) {
+ const dataContainer = document.getElementById("postDisplay");
+
+ data.map((post) => {
+ const postContainer = document.createElement("div");
+ const blogTitle = document.createElement("h4");
+ const blogPost = document.createElement("p");
+
+ postContainer.classList.add(
+ "post__container",
+ "shadow-sm",
+ "mx-1",
+ "col-sm-12",
+ "col-md-6",
+ "col-xxl-3",
+ "p-3",
+ "mb-5",
+ "bg-body",
+ "container-xxl"
+ );
+ postContainer.setAttribute("data-post-id", `${post.id}`);
+ postContainer.setAttribute("data-user-id", `${post.userId}`);
+
+ blogTitle.classList.add("post__title",);
+ blogTitle.setAttribute("data-post-id", `${post.id}`);
+ blogTitle.setAttribute("data-user-id", `${post.userId}`);
+
+ blogPost.classList.add("post__blog--post", );
+ blogPost.setAttribute("data-post-id", `${post.id}`);
+ blogPost.setAttribute("data-user-id", `${post.userId}`);
+ blogTitle.textContent = post.title;
+ blogPost.textContent = post.body;
+
+ postContainer.append(blogTitle, blogPost);
+ dataContainer.append(postContainer);
+ postContainer.addEventListener("click", (e) => {
+ showTitleBody(e);
+ openPost();
+ showUserEmail(e);
+ });
+ });
+}
+
+export { getPostData, getComments, displayPosts };
\ No newline at end of file
diff --git a/src/js/search.js b/src/js/search.js
new file mode 100644
index 00000000..0052306b
--- /dev/null
+++ b/src/js/search.js
@@ -0,0 +1,32 @@
+import { getPostData, displayPosts } from './main.js'
+
+// Define string for search results from user entry in search bar
+// Filter posts from json that have a title which includes search string and reurn results
+async function getSearchResults () {
+ const searchString = document.getElementById('headerSearch').value;
+ const posts = await getPostData();
+ const searchResults = posts.filter((post) => {
+ return post.title.includes(searchString);
+ })
+ return searchResults;
+ }
+
+// Takes posts from the search results, if they exist, removes currently viewed posts and passes search results to display posts.
+async function displaySearchResults () {
+ const searchResults = await getSearchResults();
+ if (searchResults.length > 0) {
+ removePosts();
+ displayPosts(searchResults);
+ }
+}
+
+// Remove currently viewed posts on main page.
+function removePosts () {
+ const dataContainer = document.getElementById("postDisplay");
+ while (dataContainer.firstElementChild) {
+ dataContainer.removeChild(dataContainer.lastElementChild);
+ }
+}
+
+
+ export { getSearchResults, displaySearchResults, removePosts}
\ No newline at end of file