Styling and Accessibility

Overarching Project


It’s again time to continue with the “Reddit”-themed overarching project.

We recommend working on the overarching project only after completing all other assignments in this part. For deeper learning, it can also help to take a short break of a few days before starting the project.

The overarching project in this part is divided into five steps. The steps are divided so that you always first implement server-side functionality and then client-side functionality. This way, you can focus on one side of the application at a time. In this part, you’ll first implement functionality for upvoting and downvoting posts and comments, after which you’ll get to add styles to the application.

First, in the sixteenth step, you’ll add server-side APIs for upvoting and downvoting.

WSD Overarching Project, Step 16

0 / 35 points

In this step, you will implement a set of voting-related API using a new database schema to store vote information on the server-side. At the end of this step, your server-side application has the following functionalities:

  • API for upvote and downvote for posts
  • API for upvote and downvote for comments
  • Modify the response to the GET request and create request to include voting information for posts and comments

Database Schema

First, add a new migration file to the database-migrations folder of the project. The migration will be used to create a new table votes. The table vote will be used to store information about upvote and downvote in our application

You can use any name for the migration file (as long as it follows the naming convention of the other migration files). The contents of the migration file should be as follows:

CREATE TYPE vote_type AS ENUM ('upvote', 'downvote');
CREATE TABLE votes (
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
post_id INTEGER NOT NULL REFERENCES posts(id) ON DELETE CASCADE,
vote vote_type NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (user_id, post_id)
);

The vote table stores votes from users on a specific post/comment. One key design of this table is that PRIMARY KEY (user_id, post_id) ensures that a user can only vote once on any given post/comment. The other key design is that the vote column is restricted to either upvote or downvote

After adding the migration file, run the migration to add the table to the database.

Tasks

The task is to implement the following API routes:

  • POST /api/communities/:communityId/posts/:postId/upvote Create an upvote for a post in the votes table. After creating the upvote, return the post that was just upvoted. The response JSON must also include two additional fields: upvotes and downvotes, which represent the total number of upvote and downvote that this post currently has.

    Example response:

    {
    "id": 1,
    "title": "Post 1 title",
    "content": "Post 1 content",
    "community_id": ":communityId",
    "parent_post_id": null,
    "upvotes": <total number of upvote this post has>,
    "downvotes": <total number of downvote this post has>,
    "created_at": "..."
    }

You can add these extra fields to the response either by using a SQL query using JOINTS, or by injecting the response object in your code and populating those fields with data from a simple sql command.

  • POST /api/communities/:communityId/posts/:postId/downvote Create a downvote for a post in the votes table. After creating the downvote, return the post that was just downvoted. The response JSON must also include two additional fields: upvotes and downvotes, which represent the total number of upvotes and downvotes that this post currently has.

    Example response:

    {
    "id": 1,
    "title": "Post 1 title",
    "content": "Post 1 content",
    "community_id": ":communityId",
    "parent_post_id": null,
    "upvotes": <total number of upvote this post has>,
    "downvotes": <total number of downvote this post has>,
    "created_at": "..."
    }
  • POST /api/communities/:communityId/posts/:postId/comments/:commentId/upvote Create an upvote for a comment in the votes table. After creating the upvote, return the comment that was just upvoted. The response JSON must also include two additional fields: upvotes and downvotes, which represent the total number of upvote and downvote that this comment currently has.

    Example response:

    {
    "id": 1,
    "title": null,
    "content": "Comment 1 content",
    "community_id": ":communityId",
    "parent_post_id": <parent post id as number>,
    "upvotes": <total number of upvote this comment has>,
    "downvotes": <total number of downvote this comment has>,
    "created_at": "..."
    }
  • POST /api/communities/:communityId/posts/:postId/comments/:commentId/downvote Create a downvote for a comment in the votes table. After creating the downvote, return the comment that was just downvoted. The response JSON must also include two additional fields: upvotes and downvotes, which represent the total number of upvote and downvote that this comment currently has.

    Example response:

    {
    "id": 1,
    "title": null,
    "content": "Comment 1 content",
    "community_id": ":communityId",
    "parent_post_id": <parent post id as number>,
    "upvotes": <total number of upvote this comment has>,
    "downvotes": <total number of downvote this comment has>,
    "created_at": "..."
    }

Remember to use authenticate() middleware for these API requests.

  • Error handling The vote table should allow each user to vote only once on a given post or comment. If the same user votes again on a post/comment they have already voted on, the new vote must overwrite the previous one. To implement this, first check whether the user already has a vote on the target post/comment. If they do, remove the old vote and then insert the new vote into the table.

  • Modify response of GET request and create request to posts and comments Update the responses of the following API endpoints:

    • GET /api/communities/:communityId/posts
    • POST /api/communities/:communityId/posts
    • GET /api/communities/:communityId/posts/:postId
    • GET /api/communities/:communityId/posts/:postId/comments
    • POST /api/communities/:communityId/posts/:postId/comments The JSON response for each of these endpoints must also include two additional fields: upvotes and downvotes for every post/comment object. The upvotes and downvotes fields of newly created posts/comments are 0.

Submission

Once ready, zip the contents of the server folder of your overarching project and submit the zip file below. The controllers folder should be at the root of the zip file. The expected file structure of the project is as follows.

Follow the names exactly as given, as the automated tests will check for them. Your project may have additional files (e.g., Dockerfile, deno.json).

Expected file structure:

.
├── controllers
│ ├── commentController.js (updated)
│ ├── postController.js (updated)
│ └── ...
├── repositories
│ ├── commentRepository.js (updated)
│ ├── postRepository.js (updated)
│ └── ...
├── app-run.js
├── app.js (updated)
└── middlewares.js

Then, in the seventeenth step, you’ll add client-side functionality for voting.

WSD Overarching Project, Step 17

0 / 40 points

In this step, you will implement a set of voting-related APIs, integrating voting into the shared state of posts and comments, and develop components for voting and displaying votes for posts and comments on the client side. By the end of this step, your application should include the following new functionality:

  • A set of client-side voting-related APIs for posts and comments.
  • Updated shared states for post and comment to integrate voting.
  • Updated post and comment components to integrate voting.
  • Visual constraint for voting button.

Tasks

The task is divided into parts to help structure your work. Each part corresponds to a specific module or component with its own responsibilities.

postsApi.js

Updating postsApi.js file in the src/lib/apis folder so that they have functions to interact with the voting API from the server-side

The vote-related API endpoints are as follows:

  • POST /api/communities/:communityId/posts/:postId/upvote → creates a new upvote for a given post, returning the upvoted post.
  • POST /api/communities/:communityId/posts/:postId/downvote → creates a new downvote for a given post, returning the downvoted post.

For more details about these API endpoints, refer to the Overarching Project Step 16 instructions.

postState.svelte.js

Updating postState.svelte.js file so that the shared state provides functionality for upvoting and downvoting posts.

PostList.svelte

Updating PostList.svelte in the src/lib/components/posts folder. This component should have these additional functionalities:

  • Display the amount of upvotes and downvotes the post has.
  • A button with text “Upvote” for upvoting the post.
  • A button with text “Downvote” for downvoting the post.
  • Upvote and downvote buttons are only visible if the user is authenticated.

commentsApi.js

Updating commentsApi.js file in the src/lib/apis folder so that they have functions to interact with the voting API from the server-side

The vote-related APIs endpoints are as follows:

  • POST /api/communities/:communityId/posts/:postId/comments/:commentId/upvote → creates a new upvote for a given comment, returning the upvoted comment.
  • POST /api/communities/:communityId/posts/:postId/comments/:commentId/downvote → creates a new downvote for a given comment, returning the downvoted comment.

For more details about these API endpoints, refer to the Overarching Project Step 16 instructions.

commentState.svelte.js

Updating commentState.svelte.js file so that the shared state provides functionality for upvoting and downvoting comments.

CommentList.svelte

Updating CommentList.svelte in the src/lib/components/comments folder. This component should have these additional functionalities:

  • Display the amount of upvotes and downvotes the comment has
  • A button with text “Upvote” for upvoting the comment
  • A button with text “Downvote” for downvoting the comment
  • Upvote and downvote buttons are only visible if the user is authenticated

Submission

Once ready, zip the contents of your client/src folder and submit the zip file below. The routes folder should be at the root of the zip file. Follow the file and folder names exactly, as automated tests depend on them. Your project may include additional files (e.g., Dockerfile, deno.json).

Expected file structure:

.
├── lib
│ └── apis
│ │ ├── commentsApi.js (updated)
│ │ ├── postsApi.js (updated)
│ │ └── ...
│ ├── components
│ │ ├── comments
│ │ │ ├── CommentList.svelte (updated)
│ │ │ └── ...
│ │ ├── posts
│ │ │ ├── PostList.svelte (updated)
│ │ │ └── ...
│ │ └── ...
│ └── states
│ ├── postState.svelte.js (updated)
│ ├── commentState.svelte.js (updated)
│ └── ...
└── routes
└── ...

Then, in the eighteenth step, you’ll add server-side functionality for showing posts on the main page.

WSD Overarching Project, Step 18

0 / 30 points

In this step, you will implement an API that is used to retrieve information displayed in the home page (”/”) of the client-side.

Tasks

The task is to implement the following API route:

  • GET /api/homepage Retrieve from the database all the posts created in the last 3 days from all communities, sorted by property created_at (newest post first), and return them as a JSON document. Aside from the normal fields for the posts, the response JSON must also include three additional fields: upvotes, downvotes, and comments, which represent the total number of upvotes, downvotes, and comments that this post currently has. This API does not require authenticate() middleware since we still want our non-users to see the home page.

    Example JSON:

    [
    {
    "id": 1,
    "title": "Post 1 title",
    "content": "Post 1 content",
    "community_id": "CommunityId1",
    "parent_post_id": null,
    "created_by": ...,
    "created_at": ...,
    "upvotes": <total number of upvote this post has>,
    "downvotes": <total number of downvote this post has>,
    "comments": <total number of comments this post has>,
    },
    {
    "id": 2,
    "title": "Post 2 title",
    "content": "Post 2 content",
    "community_id": "CommunityId2",
    "parent_post_id": null,
    "created_by": ...,
    "created_at": ...,
    "upvotes": <total number of upvote this post has>,
    "downvotes": <total number of downvote this post has>,
    "comments": <total number of comments this post has>,
    }
    ]

You can add these extra fields to the response either by using a SQL query using JOINS, or by injecting the response object in your code and populating those fields with data from a simple SQL command.

Submission

Once ready, zip the contents of the server folder of your overarching project and submit the zip file below. The controllers folder should be at the root of the zip file. The expected file structure of the project is as follows.

Follow the names exactly as given, as the automated tests will check for them. Your project may have additional files (e.g., Dockerfile, deno.json).

Expected file structure:

.
├── controllers
│ ├── postController.js (updated)
│ └── ...
├── repositories
│ ├── postRepository.js (updated)
│ └── ...
├── app-run.js
├── app.js (updated)
└── middlewares.js

Then, in the nineteenth step, you’ll add client-side functionality for showing posts on the main page.

WSD Overarching Project, Step 19

0 / 35 points

Since the Home page is very empty right now, in this step, you will implement a new Home page (/) that displays a list of posts created within the last 3 days, an API that retrieve those posts, a component for displaying in the home page, a shared state to manage the home page, and a navigational bar in layout. By the end of this step, your application should include the following new functionality:

  • An updated postsApi.js file that includes API to /api/homepage
  • A shared state for managing posts displayed on the home page.
  • A list component to display posts on the home page.
  • An updated layout with a navigational bar

Tasks

The task is divided into parts to help structure your work. Each part corresponds to a specific module or component with its own responsibilities.

postsApi.js

Updating postsApi.js file in the src/lib/apis folder so that it has a function to interact with the GET /api/homepage endpoint on the server side. The endpoint returns an array of post objects.

homePageState.svelte.js

Creating a new file homePageState.svelte.js in folder src/lib/states to manage the home page. This file should provide functionality for listing posts retrieved from /api/homepage, along with a function to initialize the home page state.

HomePageList.svelte

Create a file HomePageList.svelte in the src/lib/components/homePage folder (create any missing folders). This component should provide the following functionality:

  • Display the list of posts from the home page state, sorted by created_at in descending order (newest first).
  • For each post, display the title, content, upvote count, downvote count, and comment count.
  • The title of each post must be a clickable link whose href is /communities/[communityId]/posts/[postId] (note the plural communities), linking to the individual post page.

Homepage (src/routes/+page.svelte)

Modify the homepage file so that it imports and displays the HomePageList component. Also, fetch all posts shown in the home page by calling the initialization function from the shared home page state when the page loads.

Layout (src/routes/+layout.svelte)

Add a navigation bar to the Layout file that:

  • Lives inside a <header> element.
  • Includes at least two links with the exact text shown below (case matters):
    • One link with text “Home” pointing to /
    • One link with text “Communities” pointing to /communities
  • Should be visually appealing and easy to use.

Keep other things in the layout, such as the text Hello, {user email}! shown to authenticated users — this must continue to render after your changes.

Submission

Once ready, zip the contents of your client/src folder and submit the zip file below. The routes folder should be at the root of the zip file. Follow the file and folder names exactly, as automated tests depend on them. Your project may include additional files (e.g., Dockerfile, deno.json).

Expected file structure:

.
├── lib
│ └── apis
│ │ ├── postsApi.js (updated)
│ │ └── ...
│ ├── components
│ │ ├── homePage
│ │ │ └── HomePageList.svelte (new)
│ │ └── ...
│ └── states
│ ├── homePageState.svelte.js (new)
│ └── ...
└── routes
├── +layout.svelte (updated)
├── +page.svelte (updated)
└── ...

Finally, in the twentieth step, you’ll add styles to the application.

WSD Overarching Project, Step 20

0 / 50 points

In this part of the overarching project, your task is to add styles to the application using Tailwind CSS and Skeleton.

Your goal is to retain all existing functionality — for example:

  • The page at / should still display the existing welcome text, such as “Welcome to the home page!”
  • The page at /communities should still list the communities and allow adding a community
  • Dynamic pages should still show the community name
  • Do not change the semantics — e.g., upvotes must still be done with buttons labeled “Upvote”

Styling choices are up to you, but aim to learn the basics of styling. As a bonus, you can aim for an UI that is visually appealing and easy to navigate.

Once ready, submit the contents of the client/src folder as a .zip. Because styling is subjective, automated tests will not check for specific styles. The tests are the same as for Step 19.