Individual Contributions - bounswe/bounswe2024group6 GitHub Wiki

Elif Nur Deniz

Responsibilities

Throughout this milestone, I focused on frontend development, design enhancements, and feature implementation to improve the user experience and ensure smooth functionality. My responsibilities included:

  1. Feature Development:
  • Designed and implemented quiz-related components, such as the quiz feed, quiz card, and quiz results page.
  • Created functionalities like bookmarking posts, sorting and filtering forum posts, and improving profile page features.
  • Developed interactive elements, including user popovers, profile edit pages, and quiz continuation features.
  • Connected frontend features with backend APIs, including all profile page features, quiz functionalities, and fixed problems in user interactions like follow/unfollow, bookmark etc.
  1. Design Enhancements:
  • Improved the frontend design for a polished milestone presentation.
  • Fixed dark mode display issues across various pages.
  • Enhanced user interaction through intuitive navigation and UI adjustments.
  1. Bug Fixing and Testing:
  • Fixed significant issues, such as updating follower counts, missing usernames, non-functional tags, and many more.
  • Conducted unit testing for profile pages and other features to ensure code reliability.
  1. Planning:
  • Reviewed and coordinated backend JSON request/response formats for profile, post and quiz-related data. Reported all missing elements and pointed possible future errors.
  • Decided on the application tags, documented them and added them in the related fields in the code.
  • Listed milestone requirements for reports and participated in demo preparation.
  • Participated in milestone planning and demo preparation.

Main Contributions

  • Created core components like quiz pages, profile pages.
  • Enabled bookmarking posts and created a tab to display bookmarked posts on the profile page alongside with created posts.
  • Added functionalities like sorting forum posts by date and like count and filtering posts by tags.
  • Developed a popover feature for user details in posts.
  • Connected frontend features to backend APIs for seamless functionality.
  • Improved frontend UI for the milestone presentation, continuously searched for design/applications related issues and fixed many of them.
  • Implemented unit tests for profile-related features, including edit and view functionalities.
  • Reviewed backend JSON responses for consistency and reliability.
  • Finalized application tags and integrated them into the code.

API Contributions

I was responsible for creating quiz related pages, connecting post and profile related pages to backend, and enhancing design overall.

User Card Feature

import React from "react";
import { Avatar, Button, Card, CardBody, CardFooter, CardHeader } from "@nextui-org/react";
import { Suspense, useState, useEffect } from "react";
import axios from "axios";
import { BASE_URL } from "../../lib/baseURL";
import { Profile, ProfileResponse } from "../../types";
import { convertProfileResponseToProfile } from "./utils";
import { AuthActions } from "../../components/auth/utils.tsx";
import { useNavigate, useLocation } from "react-router-dom";
import Cookies from "js-cookie";

type Props = {
    username: string;
};

export const UserCard = ({
    username,
}: Props) => {
    const [isFollowed, setIsFollowed] = React.useState(false);
    const { getToken } = AuthActions();
    const token = getToken("access");
    const [profile, setProfile] = useState<Profile | null>(null);
    const [followCount, setFollowCount] = useState(0);
    const navigate = useNavigate();

    useEffect(() => {
        if (username) {
            axios
                .get(`${BASE_URL}/profile/${username}/`, {
                    headers: {
                        Authorization: `Bearer ${token}`,
                        "Content-Type": "application/json",
                    },
                })
                .then((response) => {
                    const data: ProfileResponse = response.data;
                    console.log(data);
                    const profile = convertProfileResponseToProfile(data);
                    setIsFollowed(profile.is_followed);
                    setProfile(profile);
                    setFollowCount(profile.followers);
                })
                .catch((error) => {
                    console.log(error);
                });
        }
    }, [username, token]);

    const toggleFollow = () => {
        axios
          .post(
            `${BASE_URL}/profile/${isFollowed ? "unfollow" : "follow"}/`,
            {
              username: profile?.username,
            },
            {
              headers: {
                "Content-Type": "application/json",
                Authorization: `Bearer ${token}`,
              },
            }
          )
          .then((response) => {
            console.log(response.data);
            setFollowCount(response.data.follower_count);
          })
          .catch((error) => {
            console.log(error);
          });
        setIsFollowed(!isFollowed);
      };

    return (
        <Card shadow="none" className="w-[300px] border-none bg-transparent">
            <CardHeader className="justify-between">
                <div className="flex gap-3">
                    <Avatar isBordered radius="full" size="md" src="https://nextui.org/avatars/avatar-1.png" />
                    <div className="flex flex-col items-start justify-center">
                        <h4 className="text-small font-semibold leading-none text-default-600">{profile?.username}</h4>
                        <h5 className="text-small tracking-tight text-default-500">@{profile?.level}</h5>
                    </div>
                </div>
                <div className="flex flex-row items-center gap-2">
                    {profile && profile.username !== Cookies.get("username") &&
                        <Button
                            className={isFollowed ? "bg-transparent text-foreground border-default-200" : ""}
                            color="primary"
                            radius="full"
                            size="sm"
                            variant={isFollowed ? "bordered" : "solid"}
                            onClick={toggleFollow}
                        >
                            {isFollowed ? "Unfollow" : "Follow"}
                        </Button>}
                    <Button color="primary" variant="faded" size="sm" radius="full" onClick={() => navigate(`/profile/${username}`)}>
                        Profile
                    </Button>
                </div>
            </CardHeader>
            <CardBody className="px-3 py-0">
                <p className="text-small pl-px text-default-500">
                    {profile?.bio || "Hey, new learner here!"}
                </p>
            </CardBody>
            <CardFooter className="gap-3">
                <div className="flex gap-1">
                    <p className="font-semibold text-default-600 text-small">{profile?.following}</p>
                    <p className=" text-default-500 text-small">Following</p>
                </div>
                <div className="flex gap-1">
                    <p className="font-semibold text-default-600 text-small">{followCount}</p>
                    <p className="text-default-500 text-small">Followers</p>
                </div>
            </CardFooter>
        </Card>
    );
};

Connecting Profile To Backend I did all major connections, added sorted posts and bookmarks to the profile.

  // All profile related data and sorting posts based on creation date
  useEffect(() => {
    if (username) {
      setIsLoading(true);
      axios
        .get(`${BASE_URL}/profile/${username}/`, {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        })
        .then((response) => {
          const data: ProfileResponse = response.data;
          console.log(data);
          const profile = convertProfileResponseToProfile(data);
          const sortedVersion = [...profile.posts].sort((a, b) => {
            return (
              new Date(b.post.created_at).getTime() -
              new Date(a.post.created_at).getTime()
            );
          });
          setFollowCount(profile.followers);
          setIsFollowing(profile.is_followed);
          setSortedPosts(sortedVersion);
          setProfile(profile);
        })
        .catch((error) => {
          console.log(error);
        })
        .finally(() => {
          setIsLoading(false);
        });
    }
  }, [username, token]);
  

  // Getting bookmarked posts
  useEffect(() => {
    if (username === Cookies.get("username")) {
      console.log("Fetching bookmarked posts");
      axios
        .post(
          `${BASE_URL}/get_bookmarked_posts/`,
          {
            username: profile?.username,
          },
          {
            headers: {
              Authorization: `Bearer ${token}`,
            },
          }
        )
        .then((response) => {
          console.log(response);
          const bookmarked = response.data.map(convertPostResponseToPost);
          setBookmarkedPosts(bookmarked);
        })
        .catch((error) => {
          console.log(error);
        });
    } else {
      console.log("Not fetching bookmarked posts");
    }
  }, [token]);

Sorting And Filtering Features In Forum

  const [sortFilter, setSortFilter] = useState<string>("Most Recent");
  const handleSelectionChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setSortFilter(e.target.value);
  };
  
  const filteredPosts = posts.filter((post) => {
    // Show all posts if no tags are selected
    if (selectedTags.length === 0) return true;

    // Check if the post has at least one tag from the selectedTags
    return post.post.tags.some((tag) => selectedTags.includes(tag));
  });

  const sortedPosts = [...filteredPosts].sort((a, b) => {
    switch (sortFilter) {
      case "Most Recent":
        return (
          new Date(b.post.created_at).getTime() -
          new Date(a.post.created_at).getTime()
        );
      case "Most Liked":
        return b.engagement.likes - a.engagement.likes;
      default:
        return 0;
    }
  });
  
  const handleTagClick = (tag: string) => {
    if (selectedTags.includes(tag)) {
      setSelectedTags(selectedTags.filter((t) => t !== tag));
    } else {
      setSelectedTags([...selectedTags, tag]);
    }
  };

  <div className="flex w-[740px] justify-between items-center  mt-4">
        <Select
          onChange={handleSelectionChange}
          placeholder="Sort By"
          defaultSelectedKeys={["Most Recent"]}
          className="w-44 text-black"
        >
          {SortFilters.map((sortFilter) => (
            <SelectItem key={sortFilter}>{sortFilter}</SelectItem>
          ))}
        </Select>
        <div className="flex flex-row gap-2">
          <Select
            placeholder="Difficulty"
            selectionMode="multiple"
            className="w-32 text-black"
          >
            {DifficultyTags.map((tag) => (
              <SelectItem onPress={() => handleTagClick(tag)} key={tag}>
                {tag}
              </SelectItem>
            ))}
          </Select>
          <Select
            placeholder="Categories"
            selectionMode="multiple"
            className="w-32 text-black"
          >
            {Tags.map((tag) => (
              <SelectItem onPress={() => handleTagClick(tag)} key={tag}>
                {tag}
              </SelectItem>
            ))}
          </Select>
        </div>

Code/Non-Code Related Significant Issues

Task Duration
Choosing Tags for Quizzes and Forum 2 hours
Redesigning Profile Page 2 hours
Initial Designs of Quiz Components and Pages 5 hours
Create Quiz Feed 3 hours
[Lab]: Prepare 2 User Stories 1.5 hours
[Lab] Frontend: Implement Quiz Resume Feature 30 minutes
Planning: Customer Milestone 2 Demo Plan #509 2 hours
Change Profile Icon's Funtionality #521 30 minutes
Revising Tags in Forum #526 2 hours
Revise Quiz Card #528 2.5 hours
Connect Frontend Features to the Backend #536 6 hours
Creating Profile Related JSON File Template #537 30 minutes
Create a Popover For Avatars In Posts And Quizzes #539 3 hours
Fix Profile button #540 45 minutes
Create Profile Edit Page #542 6 hours
Forum Sorting Issue #547 2 hours
Make Filtering Functional #551 2 hours
Implement visiting the profile pages of other users #559 3.5 hours
Fix Profile Page Path Issue #568 2 hours
Review Quiz Endpoints #582 40 minutes
Review Profile Endpoints #584 40 minutes
List Bookmarked Posts In Profile #629 6 hours
Missing Usernames in Profile Posts #637 40 minutes
Update Follower Count After Follow Operation #644 1 hour
Small Changes To Profile And Quizzes #647 4 hours
Profile Unit Tests #690 1 hour
Creating Implemented Requirements #704 40 minutes

Pull Requests

Pull Requests Link Action
added edit-profile tests link assigned
Design improvements 1, 2 assigned
Fix follower count issue link assigned
added bookmarked posts to profile link assigned
implemented popover user cards link assigned
created edit-profile page, added missing types to Profile link assigned
implemented other/own users' profile views link assigned
Added filtering functionality to forum link assigned
sorted posts in profile and fixed difficulty tag issue link assigned
implemented forum sorting methods link assigned
Fixed the background issue with the profile avatar link assigned
added photo feature to quiz details page link assigned
redesigned quiz component and added photo to it link assigned
feat(frontend): edited compose post and forum tags link assigned
fix(frontend): changed navbar icon's functionality link assigned
feat(frontend): Initial Designs of Quiz Components and Pages assigned
feat(frontend): reorganised profile page and added options to profile icon in navbar link assigned

I reviewed almost all frontend pull requests.

⚠️ **GitHub.com Fallback** ⚠️