Skip to content

Commit

Permalink
User page and self management (#356)
Browse files Browse the repository at this point in the history
* User Listings

* Error resolved

* API types
  • Loading branch information
gaurav-jo1 authored Sep 5, 2024
1 parent 255e8b8 commit 602ad98
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 5 deletions.
8 changes: 8 additions & 0 deletions frontend/src/components/listing/ListingHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ const NavigationButtons = ({ listing }: Props) => {

const ListingHeader = (props: Props) => {
const { listing } = props;
const navigate = useNavigate();

return (
<div className="relative p-4 mb-4">
Expand All @@ -153,6 +154,13 @@ const ListingHeader = (props: Props) => {
Posted {formatTimeSince(new Date(listing.created_at * 1000))}
</div>
</div>
{listing.creator_name && (
<div className="text-sm mt-1 flex items-center text-blue-600 hover:text-blue-800 hover:underline cursor-pointer">
<p onClick={() => navigate(`/profile/${listing.creator_id}`)}>
By {listing.creator_name}
</p>{" "}
</div>
)}
</div>
<NavigationButtons {...props} />
</div>
Expand Down
58 changes: 53 additions & 5 deletions frontend/src/components/pages/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { paths } from "gen/api";
import { useAlertQueue } from "hooks/useAlertQueue";
import { useAuthentication } from "hooks/useAuth";

import ListingGrid from "components/listings/ListingGrid";
import { Button } from "components/ui/Button/Button";
import { Input, TextArea } from "components/ui/Input/Input";
import Spinner from "components/ui/Spinner";
Expand Down Expand Up @@ -158,6 +159,9 @@ const Profile = () => {
const [user, setUser] = useState<UserResponse | null>(null);
const [canEdit, setCanEdit] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [listingIds, setListingIds] = useState<string[] | null>(null);

const pageNumber = parseInt("1", 10);

useEffect(() => {
const fetchUser = async () => {
Expand Down Expand Up @@ -198,9 +202,46 @@ const Profile = () => {
}
}
};
const fetchUserListing = async () => {
if (id === undefined) {
const { data, error } = await auth.client.GET("/listings/me", {
params: {
query: {
page: pageNumber,
},
},
});

if (error) {
addErrorAlert(error);
} else {
setListingIds(data.listing_ids);
}
} else {
try {
const { data, error } = await auth.client.GET("/listings/user/{id}", {
params: {
path: { id },
query: {
page: pageNumber,
},
},
});

if (error) {
addErrorAlert(error);
} else {
setListingIds(data.listing_ids);
}
} catch (err) {
console.error("Failed to fetch User Listings", err);
}
}
};

if (!auth.isLoading) {
fetchUser();
fetchUserListing();
}
}, [id, auth.currentUser, auth.isLoading, auth.client, addErrorAlert]);

Expand Down Expand Up @@ -229,11 +270,18 @@ const Profile = () => {
}

return user ? (
<RenderProfile
user={user}
onUpdateProfile={handleUpdateProfile}
canEdit={canEdit}
/>
<>
<RenderProfile
user={user}
onUpdateProfile={handleUpdateProfile}
canEdit={canEdit}
/>
{listingIds && (
<div className="mt-4">
<ListingGrid listingIds={listingIds} />
</div>
)}
</>
) : (
<div className="flex justify-center items-center pt-8">
<p>User not found</p>
Expand Down
55 changes: 55 additions & 0 deletions frontend/src/gen/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,23 @@ export interface paths {
patch?: never;
trace?: never;
};
"/listings/user/{id}": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** List User Listings */
get: operations["list_user_listings_listings_user__id__get"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/listings/me": {
parameters: {
query?: never;
Expand Down Expand Up @@ -775,6 +792,8 @@ export interface components {
user_vote: boolean | null;
/** Creator Id */
creator_id: string;
/** Creator Name */
creator_name: string | null;
};
/** GetTokenResponse */
GetTokenResponse: {
Expand Down Expand Up @@ -1604,6 +1623,42 @@ export interface operations {
};
};
};
list_user_listings_listings_user__id__get: {
parameters: {
query: {
/** @description Page number for pagination */
page: number;
/** @description Search query string */
search_query?: string;
};
header?: never;
path: {
id: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ListListingsResponse"];
};
};
/** @description Validation Error */
422: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
list_my_listings_listings_me_get: {
parameters: {
query: {
Expand Down
18 changes: 18 additions & 0 deletions store/app/routers/listings.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,18 @@ async def dump_listings(
return DumpListingsResponse(listings=await crud.dump_listings())


@listings_router.get("/user/{id}", response_model=ListListingsResponse)
async def list_user_listings(
id: str,
crud: Annotated[Crud, Depends(Crud.get)],
page: int = Query(description="Page number for pagination"),
search_query: str = Query(None, description="Search query string"),
) -> ListListingsResponse:
listings, has_next = await crud.get_user_listings(id, page, search_query=search_query)
listing_ids = [listing.id for listing in listings]
return ListListingsResponse(listing_ids=listing_ids, has_next=has_next)


@listings_router.get("/me", response_model=ListListingsResponse)
async def list_my_listings(
crud: Annotated[Crud, Depends(Crud.get)],
Expand Down Expand Up @@ -219,6 +231,7 @@ class GetListingResponse(BaseModel):
score: int
user_vote: bool | None
creator_id: str # Add this line
creator_name: str | None


@listings_router.get("/{id}", response_model=GetListingResponse)
Expand All @@ -238,6 +251,10 @@ async def get_listing(
if user and (vote := await crud.get_user_vote(user.id, id)) is not None:
user_vote = vote.is_upvote

creator_name = None
if (creator := await crud.get_user_public(listing.user_id)) is not None:
creator_name = " ".join(filter(None, [creator.first_name, creator.last_name]))

return GetListingResponse(
id=listing.id,
name=listing.name,
Expand All @@ -251,6 +268,7 @@ async def get_listing(
score=listing.score,
user_vote=user_vote,
creator_id=listing.user_id, # Add this line
creator_name=creator_name,
)


Expand Down
10 changes: 10 additions & 0 deletions tests/test_listings.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ def test_listings(test_client: TestClient, tmpdir: Path) -> None:
data = response.json()
assert data["artifacts"][0]["artifact_id"] is not None

# List user listings
response = test_client.get("/users/public/me", headers=auth_headers)
assert response.status_code == status.HTTP_200_OK, response.json()
id = response.json()["id"]

response = test_client.get(f"/listings/user/{id}", headers=auth_headers, params={"page": 1})
assert response.status_code == status.HTTP_200_OK, response.json()
items = response.json()["listing_ids"]
assert (num_listings := len(items)) >= 1

# Checks my own listings.
response = test_client.get(
"/listings/me",
Expand Down

0 comments on commit 602ad98

Please sign in to comment.