Hướng dẫn thử thách
1 / 1
Build an fCC Forum Leaderboard
In this lab, you will build a freeCodeCamp forum leaderboard that displays the latest topics, users, and replies from the [freeCodeCamp forum](https://forum.freecodecamp.org/). The HTML, CSS and part of the JS have been provided for you. Feel free to explore them.
The API endpoint returns JSON in the following shape:
```json
{
"users": [
{
"id": 6,
"username": "QuincyLarson",
"name": "Quincy Larson",
"avatar_template": "/user_avatar/QuincyLarson_{size}.png"
},
{
"id": 576147,
"username": "JOY-OKORO",
"name": "Joy Okoro",
"avatar_template": "/user_avatar/JOY-OKORO_{size}.png"
}
],
"topic_list": {
"topics": [
{
"id": 684569,
"title": "The freeCodeCamp Podcast is back – now with video",
"slug": "the-freecodecamp-podcast-is-back-now-with-video",
"posts_count": 8,
"views": 542,
"bumped_at": "2024-04-15T16:01:26.403Z",
"category_id": 1,
"posters": [
{ "user_id": 6 },
{ "user_id": 576147 }
]
}
]
}
}
```
Note that `avatar_template` values are relative paths: they start with `/` and contain a `{size}` placeholder that should be replaced with a pixel size. The `avatarUrl` constant holds the base URL to prepend when the path is relative.
**Objective:** Fulfill the user stories below and get all the tests to pass to complete the lab.
**User Stories:**
1. You should have a function named `timeAgo` that takes a timestamp in the ISO 8601 format as the argument.
1. The `timeAgo` function should compute the time difference between the time passed as an argument and the current time and return:
- `xm ago` (`x` represents minutes) if the amount of minutes that have passed is less than `60`.
- `xh ago` (`x` represents hours) if the amount of hours that have passed is less than `24`.
- `xd ago` (`x` represents days) otherwise.
1. You should have a function named `viewCount` that takes the number of views of a post as the argument.
1. If the value of the views passed as the argument is greater than or equal to `1000`, the `viewCount` function should return a string with the views value divided by `1000`, rounded down to the nearest whole number and the letter `k` appended to it. Otherwise, it should return the views value.
1. You should have a function named `forumCategory` that takes the id of a selected category as the argument.
1. The `forumCategory` function should verify that the selected category id is a property of the `allCategories` object and should return a string containing an anchor element with:
- the text of the `category` key of the selected category.
- a class of `category` followed by the `className` property of the selected category.
- an `href` formed by appending `<className>/<id>` to `forumCategoryUrl` (note: `forumCategoryUrl` already ends with `/`, so do not add an extra `/` separator), where `<className>` is the `className` property of the selected category and `id` is the argument passed to `forumCategory`.
1. If the `allCategories` object does not have the selected category id as its property, `category` should be indicated as `General` and `className` should be indicated as `general`.
1. You should have a function named `avatars` that takes two arrays representing posters and users, respectively.
1. The `avatars` function should return a string made by joining `img` elements, one for each `user_id` in the `posters` array. Find the `img` URL by looking up the `user_id` property in the `posters` array and find the matching `id` property in the `users` array.
1. The `avatars` function should set each avatar's size by accessing the `avatar_template` property of the matched user object (found in the `users` array by comparing its `id` to the `user_id` in `posters`) and replacing the `{size}` placeholder in the URL string with `30`.
1. Each image element should have an alt text with the value of the `name` property of the poster.
1. Each `img` element should have a `src` attribute set to the `avatar_template` of the matched user. If `avatar_template` starts with `/`, prepend `avatarUrl` directly to it.
1. You should have a function named `showLatestPosts` that takes a single parameter.
1. The `showLatestPosts` should extract the `users` and `topic_list` properties from the object passed as argument. Also, it should process the following properties of the objects from the `topics` array, which is contained in `topic_list`:
- `id`: the id of the post
- `title`: the title of the post
- `views`: the number of views of the post
- `posts_count`: the number of replies to the topic
- `slug`: the slug of the post
- `posters`: the posters for that topic
- `category_id`: an integer indicating the category id for the post
- `bumped_at`: a timestamp in the ISO 8601 format
1. The `showLatestPosts` should set the inner HTML of `#posts-container` to a string made by joining `tr` elements, one for each item in `topics`.
1. Each `tr` element should have five `td` elements in it:
- a `td` containing two anchor elements, one with the class of `post-title`, an `href` of `<forumTopicUrl><slug>/<id>`, an anchor text of `<title>`, and one obtained by calling `forumCategory` with `category_id`.
- a `td` containing a `div` element with class `avatar-container` that contains the images returned by the `avatars` function called with `posters` and `users` as arguments.
- a `td` containing the number of replies to the post. _Hint:_ use `posts_count - 1`.
- a `td` containing the number of views of the post.
- a `td` containing the time passed since the last activity.
1. You should have an async function named `fetchData`.
1. The `fetchData` function should request data from `forumLatest` and call `showLatestPosts` passing it the response parsed as JSON.
1. If there's an error when fetching data, the `fetchData` function should log the error to the console. You should specifically use `console.log` for this.
# --before-all--
```js
const _testUsers = [
{
avatar_template: '/user_avatar/QuincyLarson_{size}.png',
id: 6,
name: 'Quincy Larson',
username: 'QuincyLarson'
},
{
avatar_template: '/user_avatar/jwilkins.oboe_{size}.png',
id: 285941,
name: 'Jessica Wilkins',
username: 'jwilkins.oboe'
},
{
avatar_template: '/user_avatar/ilenia_{size}.png',
id: 170865,
name: 'Ilenia',
username: 'ilenia'
}
];
const _testPosters = [
{ user_id: 6 },
{ user_id: 285941 },
{ user_id: 170865 }
];
const _testData = {
users: _testUsers,
topic_list: {
topics: [
{
bumped_at: '2024-04-15T16:01:26.403Z',
category_id: 1,
id: 684569,
posters: [{ user_id: 6 }, { user_id: 170865 }, { user_id: 285941 }],
posts_count: 8,
slug: 'the-freecodecamp-podcast-is-back-now-with-video',
title: 'The freeCodeCamp Podcast is back – now with video',
views: 542
},
{
bumped_at: '2024-04-19T13:52:03.523Z',
category_id: 421,
id: 686149,
posters: [{ user_id: 170865 }],
posts_count: 1,
slug: 'problem-with-making-changes-to-styles-js',
title: 'Problem with making changes to styles. (JS)',
views: 9
}
]
}
};
window.fetch = () =>
new Promise(resolve =>
setTimeout(
() =>
resolve({
ok: true,
status: 200,
statusText: 'OK',
json: () => Promise.resolve(_testData),
text: () => Promise.resolve(JSON.stringify(_testData))
}),
0
)
);
```
Vượt qua bài kiểm tra hiện tại để mở khóa bài tiếp theo.
main.go
UTF-8 • Tab Size: 2Kiểm tra bài:⌘↵