Skip to content

Commit

Permalink
Multiple fixes: Implemented list sorting, Added AddProjectButton to C…
Browse files Browse the repository at this point in the history
…ard (#119)

* fixed list sorting by creating ListsPageHeader component, refactored PageHeader component by creating AddProjectButton.tsx

* modified project model to include projectLogoUrl, and projectBannerUrl | implemented input focus when VoteModal is opened | added AddProjectsButton.tsx to projects Card

* fixed inconsistent container length with short project descriptions in projects Card when in grids view.
  • Loading branch information
blahkheart authored Oct 12, 2023
1 parent f45f13f commit 2350a00
Show file tree
Hide file tree
Showing 23 changed files with 626 additions and 359 deletions.
4 changes: 2 additions & 2 deletions packages/nextjs/components/ballot/AllBallots.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import VoteModal from "../op/modals/VoteModal";
import useSWR from "swr";
import { useAccount } from "wagmi";
import * as solid from "@heroicons/react/20/solid";
import ListHeader from "~~/components/lists/ListHeader";
import YourBallot from "~~/components/op/projects/YourBallot";
import ProjectsPageHeader from "~~/components/projects/ProjectsPageHeader";
import Sidebar from "~~/components/shared/Sidebar";
import { useBallot } from "~~/context/BallotContext";
import { fetcher } from "~~/utils/fetcher";
Expand Down Expand Up @@ -169,7 +169,7 @@ const AllBallots = () => {
{!wallet ? <YourBallot /> : <Sidebar />}
<div>
<div className="container mx-auto">
<ListHeader
<ProjectsPageHeader
titleHeader="My ballot"
display="grids"
onCategoryChange={setSelectedCategory}
Expand Down
13 changes: 9 additions & 4 deletions packages/nextjs/components/lists/AllLists.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import useSWR from "swr";
import { useSignMessage } from "wagmi";
import { useAccount } from "wagmi";
import Card from "~~/components/lists/Card";
import ListHeader from "~~/components/lists/ListHeader";
import ListsPageHeader from "~~/components/lists/ListsPageHeader";
import YourBallot from "~~/components/op/projects/YourBallot";
import Sidebar from "~~/components/shared/Sidebar";
import { ListDocument } from "~~/models/List";
Expand All @@ -30,7 +30,6 @@ const AllLists: React.FC = () => {
const [lists, setLists] = useState<ListDocument[] | undefined>([]);
const { isDisconnected } = useAccount();
const [wallet, setWallet] = useState<boolean | false>(false);

const { signMessageAsync } = useSignMessage({
onSettled(data, error) {
error && notification.error(`${error}`);
Expand Down Expand Up @@ -89,7 +88,11 @@ const AllLists: React.FC = () => {
};

const filteredProjects =
selectedCategory === "all" ? lists : lists?.filter(list => list?.tags?.includes(selectedCategory));
selectedCategory === "all"
? lists
: selectedCategory === "liked"
? lists?.filter(list => list?.likes?.length)
: lists?.filter(list => list?.tags?.includes(selectedCategory));

if (lists && lists.length === 0 && isLoadingList) {
return (
Expand All @@ -115,11 +118,13 @@ const AllLists: React.FC = () => {
</div>
) : (
<div className="w-full pb-10 mb-5">
<ListHeader
<ListsPageHeader
displayList={displayList}
titleHeader="Lists"
display={display}
onCategoryChange={onCategoryChange}
lists={lists}
onShuffleLists={setLists}
/>
<div
className={`px-4 w-full grid pt-8 gap-4 ${
Expand Down
44 changes: 42 additions & 2 deletions packages/nextjs/components/lists/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,50 @@
import React from "react";
import React, { useEffect, useState } from "react";
import Image from "next/image";
import Link from "next/link";
import { Spinner } from "../Spinner";
import AddListButton from "../op/btn/AddListButton";
import { Address } from "../scaffold-eth";
import { useAccount } from "wagmi";
import { HeartIcon as HeartFilledIcon } from "@heroicons/react/20/solid";
import { HeartIcon } from "@heroicons/react/24/outline";
import { ProjectDocument } from "~~/models/Project";
import { IList } from "~~/types/list";

const Card = ({ list, onLike, isLoading, loadingList }: any) => {
const { address } = useAccount();
const { name, creator, projects, likes, description, tags } = list;
const isLiked = likes.includes(address);
const [populatedList, setPopulatedList] = useState<IList | undefined>();

const loadListProjectsData = async () => {
const response = await fetch("/api/projects");
const _projects = await response.json();
let _sharedProject = {};
const _populatedSharedProjects = [];
for (let i = 0; i < list.projects.length; i++) {
const projectId = list.projects[i].project;
const [p] = _projects.filter((project: ProjectDocument) => project._id === projectId);
const projectVotes = list.projects[i].votes;
_sharedProject = {
id: p._id,
name: p.name,
votes: projectVotes,
listId: list._id,
};
_populatedSharedProjects.push(_sharedProject);
}
return { ...list, populatedProjects: _populatedSharedProjects };
};

useEffect(() => {
if (!list) return;
const populateList = async () => {
const data = await loadListProjectsData();
setPopulatedList(data);
};
populateList();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [list]);

return (
<div className=" w-full border rounded-lg border-gray-300 p-4 ">
Expand Down Expand Up @@ -85,11 +120,16 @@ const Card = ({ list, onLike, isLoading, loadingList }: any) => {
{tags && tags.length > 0 ? (
<div className="flex">
<span className="px-2 py-1 text-sm text-customGray bg-customWhite rounded-md mr-2"> {tags[0]} </span>
<span className="px-2 py-1 text-sm text-customGray bg-customWhite rounded-md mr-2"> +{tags.length} </span>
{tags.length > 1 && (
<span className="px-2 py-1 text-sm text-customGray bg-customWhite rounded-md mr-2">
+{tags.length - 1}
</span>
)}
</div>
) : (
""
)}
{populatedList ? <AddListButton list={populatedList} customClass="card-btn" /> : <Spinner />}
</div>
</div>
);
Expand Down
133 changes: 133 additions & 0 deletions packages/nextjs/components/lists/ListsPageHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import React, { useEffect, useState } from "react";
import { ArrowsUpDownIcon, HeartIcon, ListBulletIcon, Squares2X2Icon } from "@heroicons/react/24/outline";
import { shuffle } from "~~/utils/shuffle";

type CategoryInfo = {
category: string;
projectsCount: number;
};

function ListsPageHeader({ displayList, titleHeader, display, onCategoryChange, onShuffleLists, lists }: any) {
const [active, setActive] = useState("all");
const [categories, setCategories] = useState<CategoryInfo[]>([]);

const handleButtonClick = (options: string) => {
setActive(options);
onCategoryChange(options);
};

const handleShuffle = () => {
if (lists) {
const _shuffledProjects = shuffle([...lists]);
onShuffleLists(_shuffledProjects);
}
};

useEffect(() => {
try {
function getCategories(lists: any): CategoryInfo[] {
if (!lists) return [];
const recordedTags = new Set<string>();
const tagCount: Record<string, number> = {};

lists.forEach((project: any) => {
project.tags.map((tag: string) => {
if (recordedTags.has(tag)) {
tagCount[tag]++;
} else {
recordedTags.add(tag);
tagCount[tag] = 1;
}
});
});

return Array.from(recordedTags).map(category => ({
category: category,
projectsCount: tagCount[category],
}));
}
setCategories(getCategories(lists));
} catch (e) {
console.log("ERR_SETTING_CATEGORIES", e);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
<div>
<div className="flex justify-between flex-col xs:flex-row gap-2 px-4">
<h1 className="font-bold text-2xl leading-8 ">{titleHeader}</h1>
<div className="flex gap-2 items-center">
<div
className={`w-fit border-[1px] border-neutral p-2 rounded cursor-pointer hover:bg-customWhite hover:text-black ${
display === "colums" ? "bg-customWhite text-black" : ""
}`}
onClick={() => displayList("colums")}
>
<ListBulletIcon className="w-[24px] h-[24px]" />
</div>
<div
className={`w-fit border-[1px] border-neutral p-2 rounded cursor-pointer hover:bg-customWhite hover:text-black ${
display === "grids" ? "bg-customWhite text-black" : ""
}`}
onClick={() => displayList("grids")}
>
<Squares2X2Icon className="w-[24px] h-[24px]" />
</div>
<div className="h-[18px] border-l-2 border-neutral mx-[12px] "></div>
<button
onClick={() => handleShuffle()}
className="flex items-center justify-center px-4 py-2 rounded border-neutral border-[1px] gap-2 cursor-pointer hover:bg-customWhite hover:text-black"
>
<span className="flex ">
<ArrowsUpDownIcon className="w-[15px] h-[25px]" />
</span>
Shuffle
</button>
</div>
</div>

<div className="flex flex-wrap items-center gap-3 px-4 pt-8">
{titleHeader === "Lists" && (
<>
<button
onClick={() => handleButtonClick("all")}
className={`px-4 py-2 rounded-md font-normal text-base leading-6 font-inter ${
active == "all" ? "bg-[#FF0520] text-white" : "bg-customWhite text-customGrayBtn"
}`}
>
All
</button>
<button
onClick={() => handleButtonClick("liked")}
className={`px-4 py-2 rounded-md font-normal text-base leading-6 font-inter ${
active == "liked" ? "bg-[#FF0520] text-white" : "bg-customWhite text-customGrayBtn"
}`}
>
<span className="flex">
<HeartIcon className="w-4 mr-2" />
Liked
</span>
</button>
<div className="h-[18px] border-l-2 border-neutral mx-[12px] "></div>
</>
)}
{categories &&
categories.map((category, index) => (
<button
key={index}
onClick={() => handleButtonClick(`${category.category}`)}
className={`px-4 py-2 rounded-md font-normal text-base leading-6 font-inter ${
active == `${category.category}` ? "bg-[#FF0520] text-white" : "bg-customWhite text-customGrayBtn"
}`}
>
{category.category}
<span className="px-2 py-1 bg-white text-black font-bold rounded ml-2 ">{category.projectsCount}</span>
</button>
))}
</div>
</div>
);
}

export default ListsPageHeader;
57 changes: 6 additions & 51 deletions packages/nextjs/components/lists/SharedProjects.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import React, { useState } from "react";
import Image from "next/image";
import AddListToBallotModal from "../op/modals/AddListToBallotModal";
import { AdjustmentsHorizontalIcon, SquaresPlusIcon } from "@heroicons/react/20/solid";
import AddListButton from "../op/btn/AddListButton";
import { AdjustmentsHorizontalIcon } from "@heroicons/react/20/solid";
import CustomProjectButton from "~~/components/op/btn/CustomProjectButton";
import EditDistributionModal from "~~/components/op/modals/EditDistributionModal";
import LoadingModal from "~~/components/op/modals/LoadingModal";
import SuccessModal from "~~/components/op/modals/SuccessModal";
import { useBallot } from "~~/context/BallotContext";
import { IList } from "~~/types/list";

interface Props {
Expand All @@ -16,31 +12,9 @@ interface Props {
const SharedProjects: React.FC<Props> = ({ list }) => {
const { projects, populatedProjects } = list;
const [editBallot, setEditBallot] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const [loadingMessage, setLoadingMessage] = useState("");
const [successMessage, setSuccessMessage] = useState("");
const [isAddListToBallotModal, setIsAddListToBallotModal] = useState(false);
const { dispatch } = useBallot();

const handleEditModal = (close: boolean, edit = false) => {
setIsAddListToBallotModal(false);
setEditBallot(!close && edit);
};

const addProjectToBallot = () => {
setIsAddListToBallotModal(false);
setLoadingMessage("Adding to ballot");
setSuccessMessage("Projects added successfully");
setIsLoading(true);
dispatch({
type: "ADD_LIST",
projects: populatedProjects,
});
setTimeout(() => {
setIsLoading(false);
setIsSuccess(true);
}, 1000);
const handleEditModal = () => {
setEditBallot(!editBallot);
};

return (
Expand All @@ -53,20 +27,14 @@ const SharedProjects: React.FC<Props> = ({ list }) => {
</h3>
<div className="grid grid-flow-col gap-3 sm:gap-6">
<CustomProjectButton
onClick={() => handleEditModal(false, true)}
onClick={handleEditModal}
text="Edit distribution"
customClassName="border-[#d3dde7] py-2 border-2 text-[#4d4f52]"
>
<AdjustmentsHorizontalIcon className="w-5 h-5" />
</CustomProjectButton>

<CustomProjectButton
onClick={() => setIsAddListToBallotModal(true)}
text="Add to ballot"
customClassName="bg-OPred py-2 rounded-lg border-OPred text-[#ffffff]"
>
<SquaresPlusIcon className="w-5 h-5" />
</CustomProjectButton>
<AddListButton list={list} toggleEditModal={editBallot} />
</div>
</div>
</div>
Expand Down Expand Up @@ -106,19 +74,6 @@ const SharedProjects: React.FC<Props> = ({ list }) => {
<p>Total</p>
<p>{projects?.reduce((sum, p) => sum + p.votes, 0)} OP</p>
</div>
{editBallot && <EditDistributionModal list={list} onClose={() => handleEditModal(true, false)} />}
{isLoading && <LoadingModal message={loadingMessage} />}
{isSuccess && <SuccessModal message={successMessage} onClose={() => setIsSuccess(false)} />}
{isAddListToBallotModal && (
<AddListToBallotModal
onClose={() => setIsAddListToBallotModal(false)}
handleAddBallot={() => addProjectToBallot()}
listName={list.name}
projectList={populatedProjects}
userTotal={projects?.reduce((sum, p) => sum + p.votes, 0)}
edit={() => handleEditModal(false, true)}
/>
)}
</div>
);
};
Expand Down
Loading

0 comments on commit 2350a00

Please sign in to comment.