Skip to content

Commit

Permalink
Upvoted listings (#433)
Browse files Browse the repository at this point in the history
* 338: View upvoted listings

* 338: View upvoted listings

* Updated UI to match theme

* Resolved linting issue

---------

Co-authored-by: casper <[email protected]>
  • Loading branch information
ivntsng and ivntsng authored Oct 1, 2024
1 parent 8b8c550 commit fafe47b
Show file tree
Hide file tree
Showing 6 changed files with 2,800 additions and 2 deletions.
142 changes: 142 additions & 0 deletions frontend/src/components/listings/UpvotedGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { useEffect, useState } from "react";
import { FaChevronLeft, FaChevronRight } from "react-icons/fa";
import { Link, useNavigate, useSearchParams } from "react-router-dom";

import ListingGridCard from "@/components/listings/ListingGridCard";
import Spinner from "@/components/ui/Spinner";
import { paths } from "@/gen/api";
import { useAlertQueue } from "@/hooks/useAlertQueue";
import { useAuthentication } from "@/hooks/useAuth";

type ListingInfo =
paths["/listings/batch"]["get"]["responses"][200]["content"]["application/json"]["listings"][number];

const UpvotedGrid = () => {
const auth = useAuthentication();
const { addErrorAlert } = useAlertQueue();
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();

const page = searchParams.get("page");
const pageNumber = parseInt(page || "1", 10);
if (isNaN(pageNumber) || pageNumber < 1) {
navigate("/404");
}

const [listingIds, setListingIds] = useState<string[] | null>(null);
const [hasMore, setHasMore] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState(true);
const [listingInfo, setListingInfo] = useState<ListingInfo[] | null>(null);

useEffect(() => {
fetchUpvotedListings();
}, [pageNumber]);

const fetchUpvotedListings = async () => {
setIsLoading(true);
setListingIds(null);
setListingInfo(null);

const { data, error } = await auth.client.GET("/listings/upvotes", {
params: {
query: {
page: pageNumber,
},
},
});
if (error) {
addErrorAlert(error);
} else {
setListingIds(data.upvoted_listing_ids);
setHasMore(data.has_more);
}
setIsLoading(false);
};

useEffect(() => {
if (listingIds && listingIds.length > 0) {
fetchListingDetails();
}
}, [listingIds]);

const fetchListingDetails = async () => {
if (!listingIds) {
return;
}

const { data, error } = await auth.client.GET("/listings/batch", {
params: {
query: {
ids: listingIds,
},
},
});

if (error) {
addErrorAlert(error);
} else {
setListingInfo(data.listings);
}
};

const prevButton = pageNumber > 1;
const nextButton = hasMore;

return (
<>
{isLoading ? (
<div className="flex justify-center items-center h-64">
<Spinner />
</div>
) : (
<>
{listingInfo && listingInfo.length > 0 ? (
<div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 sm:gap-6">
{listingInfo.map((listing) => (
<Link to={`/item/${listing.id}`} key={listing.id}>
<ListingGridCard
listingId={listing.id}
listing={listing}
showDescription={true}
/>
</Link>
))}
</div>
<div className="flex justify-between mt-6">
<button
className={`px-4 py-2 rounded flex items-center justify-center ${prevButton ? "bg-gray-12 text-white" : "bg-gray-7 text-gray-12 cursor-not-allowed"}`}
onClick={() => {
if (prevButton) {
setSearchParams({ page: (pageNumber - 1).toString() });
}
}}
disabled={!prevButton}
>
<FaChevronLeft className="text-xs mr-2" />
<span>Previous</span>
</button>
<button
className={`px-4 py-2 rounded flex items-center justify-center ${nextButton ? "bg-gray-12 text-white" : "bg-gray-7 text-gray-12 cursor-not-allowed"}`}
onClick={() => {
if (nextButton) {
setSearchParams({ page: (pageNumber + 1).toString() });
}
}}
disabled={!nextButton}
>
<span>Next</span>
<FaChevronRight className="text-xs ml-2" />
</button>
</div>
</div>
) : (
<p>You have not upvoted any listings</p>
)}
</>
)}
</>
);
};

export default UpvotedGrid;
28 changes: 26 additions & 2 deletions frontend/src/components/pages/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";

import ListingGrid from "@/components/listings/ListingGrid";
import UpvotedGrid from "@/components/listings/UpvotedGrid";
import { Card, CardContent, CardHeader } from "@/components/ui/Card";
import { Input, TextArea } from "@/components/ui/Input/Input";
import Spinner from "@/components/ui/Spinner";
import { Button } from "@/components/ui/button";
import { paths } from "@/gen/api";
import { useAlertQueue } from "@/hooks/useAlertQueue";
import { useAuthentication } from "@/hooks/useAuth";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@radix-ui/react-tabs";
import { format } from "date-fns";

type UserResponse =
Expand Down Expand Up @@ -183,13 +185,35 @@ export const RenderProfile = (props: RenderProfileProps) => {
)}
</CardContent>
</Card>

<Card className="w-full max-w-4xl mx-auto">
<CardHeader>
<h2 className="text-2xl font-bold">Listings</h2>
</CardHeader>
<CardContent>
{listingIds && <ListingGrid listingIds={listingIds} />}
<div className="flex flex-col items-center space-y-4">
<Tabs defaultValue="own" className="w-full">
<TabsList className="flex justify-center space-x-4 mb-4">
<TabsTrigger
value="own"
className="px-4 py-2 rounded-full transition-colors duration-300 hover:bg-gray-8 data-[state=active]:bg-primary-9 data-[state=active]:text-white"
>
Overview
</TabsTrigger>
<TabsTrigger
value="upvoted"
className="px-4 py-2 rounded-full transition-colors duration-300 hover:bg-gray-8 data-[state=active]:bg-primary-9 data-[state=active]:text-white"
>
Upvoted
</TabsTrigger>
</TabsList>
<TabsContent value="own">
{listingIds && <ListingGrid listingIds={listingIds} />}
</TabsContent>
<TabsContent value="upvoted">
<UpvotedGrid />
</TabsContent>
</Tabs>
</div>
</CardContent>
</Card>
</div>
Expand Down
56 changes: 56 additions & 0 deletions frontend/src/gen/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,23 @@ export interface paths {
patch?: never;
trace?: never;
};
"/listings/upvotes": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** Get Upvoted Listings */
get: operations["get_upvoted_listings_listings_upvotes_get"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/listings/{id}": {
parameters: {
query?: never;
Expand Down Expand Up @@ -1248,6 +1265,13 @@ export interface components {
/** Artifacts */
artifacts: components["schemas"]["SingleArtifactResponse"][];
};
/** UpvotedListingsResponse */
UpvotedListingsResponse: {
/** Upvoted Listing Ids */
upvoted_listing_ids: string[];
/** Has More */
has_more: boolean;
};
/** UserInfoResponseItem */
UserInfoResponseItem: {
/** Id */
Expand Down Expand Up @@ -1963,6 +1987,38 @@ export interface operations {
};
};
};
get_upvoted_listings_listings_upvotes_get: {
parameters: {
query?: {
/** @description Page number for pagination */
page?: number;
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["UpvotedListingsResponse"];
};
};
/** @description Validation Error */
422: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
get_listing_listings__id__get: {
parameters: {
query?: never;
Expand Down
Loading

0 comments on commit fafe47b

Please sign in to comment.