Files
2024-05-20 10:26:30 -05:00

7.3 KiB

id, title, challengeType, dashedName
id title challengeType dashedName
646494e11d0cab03caee204c Step 54 0 step-54

--description--

Your project is almost complete. It is just missing one last piece.

Users should be able to click on any post title and be directed to the actual post on the freeCodeCamp forum.

Start by changing the existing paragraph element inside the first td element to be an anchor element.

--hints--

You should replace the p element with an a element.

const regex = /<a\s+class\s*=\s*('|")post-title\1\s*>(?:.|\n)*?<\/a>/;
assert(code.match(regex));

--seed--

--seed-contents--

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>fCC Forum Leaderboard</title>
    <link rel="stylesheet" href="./styles.css" />
  </head>
  <body>
    <header>
      <nav>
        <img
          class="fcc-logo"
          src="https://cdn.freecodecamp.org/platform/universal/fcc_primary.svg"
          alt="freeCodeCamp logo"
        />
      </nav>
      <h1 class="title">Latest Topics</h1>
    </header>
    <main>
      <div class="table-wrapper">
        <table>
          <thead>
            <tr>
              <th id="topics">Topics</th>
              <th id="avatars">Avatars</th>
              <th id="replies">Replies</th>
              <th id="views">Views</th>
              <th id="activity">Activity</th>
            </tr>
          </thead>
          <tbody id="posts-container"></tbody>
        </table>
      </div>
    </main>
    <script src="./script.js"></script>
  </body>
</html>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  --main-bg-color: #2a2a40;
  --black: #000;
  --dark-navy: #0a0a23;
  --dark-grey: #d0d0d5;
  --medium-grey: #dfdfe2;
  --light-grey: #f5f6f7;
  --peach: #f28373;
  --salmon-color: #f0aea9;
  --light-blue: #8bd9f6;
  --light-orange: #f8b172;
  --light-green: #93cb5b;
  --golden-yellow: #f1ba33;
  --gold: #f9aa23;
  --green: #6bca6b;
}

body {
  background-color: var(--main-bg-color);
}

nav {
  background-color: var(--dark-navy);
  padding: 10px 0;
}

.fcc-logo {
  width: 210px;
  display: block;
  margin: auto;
}

.title {
  margin: 25px 0;
  text-align: center;
  color: var(--light-grey);
}

.table-wrapper {
  padding: 0 25px;
  overflow-x: auto;
}

table {
  width: 100%;
  color: var(--dark-grey);
  margin: auto;
  table-layout: fixed;
  border-collapse: collapse;
  overflow-x: scroll;
}

#topics {
  text-align: start;
  width: 60%;
}

th {
  border-bottom: 2px solid var(--dark-grey);
  padding-bottom: 10px;
  font-size: 1.3rem;
}

td:not(:first-child) {
  text-align: center;
}

td {
  border-bottom: 1px solid var(--dark-grey);
  padding: 20px 0;
}

.post-title {
  font-size: 1.2rem;
  color: var(--medium-grey);
  text-decoration: none;
}

.category {
  padding: 3px;
  color: var(--black);
  text-decoration: none;
  display: block;
  width: fit-content;
  margin: 10px 0 10px;
}

.career {
  background-color: var(--salmon-color);
}

.feedback,
.html-css {
  background-color: var(--light-blue);
}

.support {
  background-color: var(--light-orange);
}

.general {
  background-color: var(--light-green);
}

.javascript {
  background-color: var(--golden-yellow);
}

.backend {
  background-color: var(--gold);
}

.python {
  background-color: var(--green);
}

.motivation {
  background-color: var(--peach);
}

.avatar-container {
  display: flex;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
}

.avatar-container img {
  width: 30px;
  height: 30px;
}

@media (max-width: 750px) {
  .table-wrapper {
    padding: 0 15px;
  }

  table {
    width: 700px;
  }

  th {
    font-size: 1.2rem;
  }

  .post-title {
    font-size: 1.1rem;
  }
}
const forumLatest = "https://cdn.freecodecamp.org/curriculum/forum-latest/latest.json";
const forumTopicUrl = "https://forum.freecodecamp.org/t/";
const forumCategoryUrl = "https://forum.freecodecamp.org/c/";
const avatarUrl = "https://sea1.discourse-cdn.com/freecodecamp";

const postsContainer = document.getElementById("posts-container");

const allCategories = {
  299: { category: "Career Advice", className: "career" },
  409: { category: "Project Feedback", className: "feedback" },
  417: { category: "freeCodeCamp Support", className: "support" },
  421: { category: "JavaScript", className: "javascript" },
  423: { category: "HTML - CSS", className: "html-css" },
  424: { category: "Python", className: "python" },
  432: { category: "You Can Do This!", className: "motivation" },
  560: { category: "Backend Development", className: "backend" },
};

const forumCategory = (id) => {
  let selectedCategory = {};

  if (allCategories.hasOwnProperty(id)) {
    const { className, category } = allCategories[id];

    selectedCategory.className = className;
    selectedCategory.category = category;
  } else {
    selectedCategory.className = "general";
    selectedCategory.category = "General";
    selectedCategory.id = 1;
  }
  const url = `${forumCategoryUrl}${selectedCategory.className}/${id}`;
  const linkText = selectedCategory.category;
  const linkClass = `category ${selectedCategory.className}`;

  return `<a href="${url}" class="${linkClass}" target="_blank">
    ${linkText}
  </a>`;
};

const timeAgo = (time) => {
  const currentTime = new Date();
  const lastPost = new Date(time);

  const timeDifference = currentTime - lastPost;
  const msPerMinute = 1000 * 60;

  const minutesAgo = Math.floor(timeDifference / msPerMinute);
  const hoursAgo = Math.floor(minutesAgo / 60);
  const daysAgo = Math.floor(hoursAgo / 24);

  if (minutesAgo < 60) {
    return `${minutesAgo}m ago`;
  }

  if (hoursAgo < 24) {
    return `${hoursAgo}h ago`;
  }

  return `${daysAgo}d ago`;
};

const viewCount = (views) => {
  const thousands = Math.floor(views / 1000);

  if (views >= 1000) {
    return `${thousands}k`;
  }

  return views;
};

const avatars = (posters, users) => {
  return posters
    .map((poster) => {
      const user = users.find((user) => user.id === poster.user_id);
      if (user) {
        const avatar = user.avatar_template.replace(/{size}/, 30);
        const userAvatarUrl = avatar.startsWith("/user_avatar/")
          ? avatarUrl.concat(avatar)
          : avatar;
        return `<img src="${userAvatarUrl}" alt="${user.name}" />`;
      }
    })
    .join("");
};

const fetchData = async () => {
  try {
    const res = await fetch(forumLatest);
    const data = await res.json();
    showLatestPosts(data);
  } catch (err) {
    console.log(err);
  }
};

fetchData();

const showLatestPosts = (data) => {
  const { topic_list, users } = data;
  const { topics } = topic_list;

  postsContainer.innerHTML = topics.map((item) => {
    const {
      id,
      title,
      views,
      posts_count,
      slug,
      posters,
      category_id,
      bumped_at,
    } = item;

    return `
    <tr>
      <td>
--fcc-editable-region--
        <p class="post-title">${title}</p>
--fcc-editable-region--

        ${forumCategory(category_id)}
      </td>
      <td>
        <div class="avatar-container">
          ${avatars(posters, users)}
        </div>
      </td>
      <td>${posts_count - 1}</td>
      <td>${viewCount(views)}</td>
      <td>${timeAgo(bumped_at)}</td>
    </tr>`;
  }).join("");
};