Skip to content

Commit

Permalink
Various UI updates
Browse files Browse the repository at this point in the history
  • Loading branch information
ivntsng committed Nov 11, 2024
1 parent 22112ef commit d311c35
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 95 deletions.
2 changes: 1 addition & 1 deletion frontend/src/components/gdpr/gdprbanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const GDPRBanner: React.FC = () => {
Back
</button>
<button
className="bg-primary-9 text-white rounded-full px-4 py-2 transition-colors duration-300 hover:bg-primary-8"
className="bg-primary-9 text-black rounded-full px-4 py-2 transition-colors duration-300 hover:bg-gray-11"
onClick={handleSaveOptOutPreferences}
>
Save Preferences
Expand Down
80 changes: 2 additions & 78 deletions frontend/src/components/listing/ListingMetadata.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import { useState } from "react";
import {
FaArrowDown,
FaArrowUp,
FaClipboard,
FaEye,
FaUser,
} from "react-icons/fa";
import { FaClipboard, FaEye, FaUser } from "react-icons/fa";
import { useNavigate } from "react-router-dom";

import { useAlertQueue } from "@/hooks/useAlertQueue";
import { useAuthentication } from "@/hooks/useAuth";
import ROUTES from "@/lib/types/routes";

interface Props {
Expand All @@ -20,93 +12,25 @@ interface Props {
listingSlug: string | null;
views: number;
createdAt: number;
userVote: boolean | null;
}

const ListingMetadata = ({
listingId,
creatorId,
creatorName,
creatorUsername,
listingSlug,
views,
createdAt,
userVote: initialUserVote,
}: Props) => {
const auth = useAuthentication();
const [voting, setVoting] = useState(false);
const [userVote, setUserVote] = useState(initialUserVote);
const { addErrorAlert, addAlert } = useAlertQueue();
const { addAlert } = useAlertQueue();
const navigate = useNavigate();

const handleVote = async (vote: boolean) => {
setVoting(true);
try {
if (userVote === vote) {
const response = await auth.client.DELETE("/listings/{id}/vote", {
params: {
path: {
id: listingId,
},
},
});
if (response.error) {
addErrorAlert(response.error);
} else {
addAlert("Vote removed", "success");
setUserVote(null);
}
} else {
const response = await auth.client.POST("/listings/{id}/vote", {
params: {
path: {
id: listingId,
},
query: {
upvote: vote,
},
},
});
if (response.error) {
addErrorAlert(response.error);
} else {
addAlert("Vote added", "success");
setUserVote(vote);
}
}
} catch (error) {
addErrorAlert(error);
} finally {
setVoting(false);
}
};

const listingTag = `${creatorUsername}/${listingSlug}`;

return (
<>
{/* Metadata container - all items aligned left */}
<div className="flex flex-wrap items-center gap-4 text-sm mb-2">
{/* Voting buttons */}
<div className="flex items-center gap-2 text-gray-600">
<button
className={`p-1 hover:bg-gray-100 rounded ${userVote === true && !voting ? "text-green-500" : ""}`}
onClick={() => handleVote(true)}
disabled={voting}
>
<FaArrowUp />
</button>
<button
className={`p-1 hover:bg-gray-100 rounded ${
userVote === false && !voting ? "text-red-500" : ""
}`}
onClick={() => handleVote(false)}
disabled={voting}
>
<FaArrowDown />
</button>
</div>

{/* Creator button */}
<div className="flex items-center gap-2">
<button
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/listing/ListingName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import { FaPencilAlt, FaSave, FaTimes } from "react-icons/fa";
import { useAlertQueue } from "@/hooks/useAlertQueue";
import { useAuthentication } from "@/hooks/useAuth";

import ListingVote from "./ListingVote";

interface Props {
listingId: string;
name: string;
edit: boolean;
userVote: boolean | null;
}

const ListingName = (props: Props) => {
const { listingId, name: initialName, edit } = props;
const { listingId, name: initialName, edit, userVote } = props;

const auth = useAuthentication();
const { addAlert, addErrorAlert } = useAlertQueue();
Expand Down Expand Up @@ -98,6 +101,7 @@ const ListingName = (props: Props) => {
</>
)}
</h1>
<ListingVote listingId={listingId} userVote={userVote} />
</div>
);
};
Expand Down
24 changes: 14 additions & 10 deletions frontend/src/components/listing/ListingRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ const ListingRenderer = ({ listing }: { listing: ListingResponse }) => {
{/* Right side - Header and details - full width on mobile, half width on desktop */}
<div className="w-full md:w-1/2 mt-8">
{/* Header */}
<ListingName listingId={listingId} name={name} edit={canEdit} />
<ListingName
listingId={listingId}
name={name}
edit={canEdit}
userVote={userVote}
/>

<hr className="border-gray-200 my-4" />

Expand Down Expand Up @@ -91,6 +96,14 @@ const ListingRenderer = ({ listing }: { listing: ListingResponse }) => {

<hr className="border-gray-2 my-4" />

<ListingDescription
listingId={listingId}
description={description}
edit={canEdit}
/>

<hr className="border-gray-200 my-4" />

{/* Build this robot */}
<div className="flex items-baseline gap-4">
<ListingRegisterRobot listingId={listingId} />
Expand All @@ -99,15 +112,6 @@ const ListingRenderer = ({ listing }: { listing: ListingResponse }) => {
initialFeatured={isFeatured}
/>
</div>

<hr className="border-gray-200 my-4" />

{/* Description */}
<ListingDescription
listingId={listingId}
description={description}
edit={canEdit}
/>
</div>
</div>

Expand Down
89 changes: 89 additions & 0 deletions frontend/src/components/listing/ListingVote.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { useState } from "react";
import { FaArrowDown, FaArrowUp } from "react-icons/fa";

import { useAlertQueue } from "@/hooks/useAlertQueue";
import { useAuthentication } from "@/hooks/useAuth";

interface Props {
listingId: string;
userVote: boolean | null;
}

const ListingVote = ({ listingId, userVote: initialUserVote }: Props) => {
const auth = useAuthentication();
const [voting, setVoting] = useState(false);
const [userVote, setUserVote] = useState(initialUserVote);
const { addErrorAlert, addAlert } = useAlertQueue();

const handleVote = async (vote: boolean) => {
if (!auth.isAuthenticated) {
addErrorAlert("You must be logged in to vote");
return;
}

setVoting(true);
try {
if (userVote === vote) {
const response = await auth.client.DELETE("/listings/{id}/vote", {
params: {
path: {
id: listingId,
},
},
});
if (response.error) {
addErrorAlert(response.error);
} else {
addAlert("Vote removed", "success");
setUserVote(null);
}
} else {
const response = await auth.client.POST("/listings/{id}/vote", {
params: {
path: {
id: listingId,
},
query: {
upvote: vote,
},
},
});
if (response.error) {
addErrorAlert(response.error);
} else {
addAlert("Vote added", "success");
setUserVote(vote);
}
}
} catch (error) {
addErrorAlert(error);
} finally {
setVoting(false);
}
};

return (
<div className="flex items-center gap-2 text-gray-600">
<button
className={`p-1 hover:bg-gray-100 rounded ${
userVote === true && !voting ? "text-green-500" : ""
}`}
onClick={() => handleVote(true)}
disabled={voting || !auth.isAuthenticated}
>
<FaArrowUp />
</button>
<button
className={`p-1 hover:bg-gray-100 rounded ${
userVote === false && !voting ? "text-red-500" : ""
}`}
onClick={() => handleVote(false)}
disabled={voting || !auth.isAuthenticated}
>
<FaArrowDown />
</button>
</div>
);
};

export default ListingVote;
3 changes: 3 additions & 0 deletions frontend/src/components/nav/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState } from "react";
import { FaExternalLinkAlt } from "react-icons/fa";
import { FaBars } from "react-icons/fa6";
import { Link, useLocation, useNavigate } from "react-router-dom";

Expand Down Expand Up @@ -75,8 +76,10 @@ const Navbar = () => {
href={item.path}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2"
>
{item.name}
<FaExternalLinkAlt className="h-3 w-3 text-gray-1" />
</a>
</Button>
) : (
Expand Down
22 changes: 17 additions & 5 deletions frontend/src/components/nav/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FaTimes } from "react-icons/fa";
import { FaExternalLinkAlt, FaTimes } from "react-icons/fa";
import { useNavigate } from "react-router-dom";

import Logo from "@/components/Logo";
Expand All @@ -11,21 +11,30 @@ interface SidebarItemProps {
title: string;
icon?: JSX.Element;
onClick: () => void;
isExternal?: boolean;
}

interface SidebarProps {
show: boolean;
onClose: () => void;
}

const SidebarItem = ({ icon, title, onClick }: SidebarItemProps) => (
const SidebarItem = ({
icon,
title,
onClick,
isExternal,
}: SidebarItemProps) => (
<li>
<button
onClick={onClick}
className="w-full flex items-center py-2 px-3 text-xl text-gray-1 hover:bg-gray-1 hover:text-primary-9 rounded-md"
>
{icon && <span className="mr-2">{icon}</span>}
{title}
<span className="flex items-center gap-2">
{title}
{isExternal && <FaExternalLinkAlt className="h-3 w-3" />}
</span>
</button>
</li>
);
Expand Down Expand Up @@ -72,7 +81,7 @@ const Sidebar = ({ show, onClose }: SidebarProps) => {
title={listing.name}
onClick={() =>
handleItemClick(
`/item/${listing.username}/${listing.slug || listing.id}`,
`/bot/${listing.username}/${listing.slug || listing.id}`,
)
}
/>
Expand All @@ -86,7 +95,10 @@ const Sidebar = ({ show, onClose }: SidebarProps) => {
key={item.name}
title={item.name}
icon={item.icon}
onClick={() => handleItemClick(item.path)}
onClick={() =>
handleItemClick(item.path, item.isExternal)
}
isExternal={item.isExternal}
/>
))}
</div>
Expand Down

0 comments on commit d311c35

Please sign in to comment.