Skip to content

Commit

Permalink
Additional updates to Robots Hub (#607)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivntsng authored Nov 15, 2024
1 parent 69a8316 commit f78e61b
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 102 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 @@ -47,7 +47,7 @@ const GDPRBanner: React.FC = () => {
return (
<>
{isVisible && !showOptOutForm && (
<div className="bg-gray-12 p-4 fixed bottom-0 left-1/2 transform -translate-x-1/2 w-full max-w-lg flex flex-col items-center z-50 shadow-md rounded-lg">
<div className="bg-gray-12 p-4 fixed bottom-0 left-1/2 transform -translate-x-1/2 w-full max-w-lg flex flex-col items-center z-50 shadow-md rounded-lg border border-gray-2">
<div className="text-gray-1 text-xs sm:text-sm text-center mb-2 max-w-full">
We value your privacy 🔒 <br />
We use cookies to make it easier to interact with our website and to
Expand Down
35 changes: 22 additions & 13 deletions frontend/src/components/listing/FeaturedListings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,31 @@ export const FeaturedListingsProvider = ({
},
});

if (batchData?.listings) {
const orderedListings = featuredData.listing_ids
.map((id) => batchData.listings.find((listing) => listing.id === id))
.filter(
(listing): listing is NonNullable<typeof listing> =>
listing !== undefined,
)
.map((listing) => ({
id: listing.id,
username: listing.username ?? "",
slug: listing.slug,
name: listing.name,
}));
if (!batchData?.listings?.length) {
setFeaturedListings([]);
setFeaturedListingsStorage([]);
return;
}

const orderedListings = featuredData.listing_ids
.map((id) => batchData.listings.find((listing) => listing.id === id))
.filter(
(listing): listing is NonNullable<typeof listing> =>
listing !== undefined,
)
.map((listing) => ({
id: listing.id,
username: listing.username ?? "",
slug: listing.slug,
name: listing.name,
}));

if (orderedListings.length > 0) {
setFeaturedListings(orderedListings);
setFeaturedListingsStorage(orderedListings);
} else {
setFeaturedListings([]);
setFeaturedListingsStorage([]);
}
} catch (error) {
console.error("Error refreshing featured listings:", error);
Expand Down
54 changes: 39 additions & 15 deletions frontend/src/components/listing/ListingDeleteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import ROUTES from "@/lib/types/routes";

interface Props {
listingId: string;
className?: string;
initialFeatured?: boolean;
}

const ListingDeleteButton = (props: Props) => {
const { listingId } = props;
const { listingId, className, initialFeatured } = props;
const [deleting, setDeleting] = useState(false);
const [confirmDelete, setConfirmDelete] = useState(false);

Expand All @@ -24,21 +26,43 @@ const ListingDeleteButton = (props: Props) => {
const handleDelete = async () => {
setDeleting(true);

const { error } = await auth.client.DELETE(
"/listings/delete/{listing_id}",
{
params: {
path: { listing_id: listingId },
try {
if (initialFeatured) {
const featureResponse = await auth.client.DELETE(
"/listings/featured/{listing_id}",
{
params: {
path: { listing_id: listingId },
},
},
);

if (featureResponse.error) {
addErrorAlert("Failed to remove from featured listings");
setDeleting(false);
return;
}
}

const { error } = await auth.client.DELETE(
"/listings/delete/{listing_id}",
{
params: {
path: { listing_id: listingId },
},
},
},
);
);

if (error) {
addErrorAlert(error);
if (error) {
addErrorAlert(error);
setDeleting(false);
} else {
addAlert("Listing was deleted successfully", "success");
navigate(ROUTES.BOTS.BROWSE.path);
}
} catch {
addErrorAlert("An error occurred while deleting the listing");
setDeleting(false);
} else {
addAlert("Listing was deleted successfully", "success");
navigate(ROUTES.BOTS.BROWSE.path);
}
};

Expand All @@ -48,9 +72,9 @@ const ListingDeleteButton = (props: Props) => {
onClick={() => setConfirmDelete(true)}
variant="outline"
disabled={deleting}
className="flex items-center space-x-2 text-red-500 hover:text-red-600 hover:bg-red-100/10 w-full sm:w-auto"
className={`flex items-center text-red-600 hover:text-red-500 hover:border-red-600 ${className}`}
>
<FaTrash className="text-lg" />
<FaTrash className="mr-2 h-4 w-4" />
<span>{deleting ? "Deleting..." : "Delete Listing"}</span>
</Button>
<Modal isOpen={confirmDelete} onClose={() => setConfirmDelete(false)}>
Expand Down
10 changes: 8 additions & 2 deletions frontend/src/components/listing/ListingFeatureButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ interface Props {
listingId: string;
initialFeatured: boolean;
currentFeaturedCount?: number;
className?: string;
}

const ListingFeatureButton = (props: Props) => {
const { listingId, initialFeatured, currentFeaturedCount = 0 } = props;
const {
listingId,
initialFeatured,
currentFeaturedCount = 0,
className,
} = props;
const [isFeatured, setIsFeatured] = useState(initialFeatured);
const [isUpdating, setIsUpdating] = useState(false);

Expand Down Expand Up @@ -81,7 +87,7 @@ const ListingFeatureButton = (props: Props) => {
? "Remove from featured"
: "Add to featured"
}
className="flex items-center"
className={`flex items-center ${className}`}
>
<FaStar className="mr-2 h-4 w-4" />
<span className="mr-2">
Expand Down
38 changes: 29 additions & 9 deletions frontend/src/components/listing/ListingImageFlipper.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { FaChevronLeft, FaChevronRight } from "react-icons/fa";
import { useState } from "react";
import { FaChevronLeft, FaChevronRight, FaTimes } from "react-icons/fa";

import placeholder from "@/components/listing/pics/placeholder.jpg";
import { Artifact } from "@/components/listing/types";

import ListingArtifactRenderer from "./ListingArtifactRenderer";

interface Props {
artifacts: Artifact[];
name: string;
Expand All @@ -14,6 +13,7 @@ interface Props {

const ListingImageFlipper = (props: Props) => {
const { artifacts, name, currentImageIndex, setCurrentImageIndex } = props;
const [isFullScreen, setIsFullScreen] = useState(false);

if (artifacts.length === 0) {
return (
Expand All @@ -33,12 +33,13 @@ const ListingImageFlipper = (props: Props) => {

return (
<>
{/* Main image - full width on mobile, half width on desktop */}
<div className="w-full md:w-1/2 relative">
<div className="aspect-square bg-black rounded-lg overflow-hidden">
<ListingArtifactRenderer artifact={currentArtifact} />
</div>

<div className="relative">
<img
src={currentArtifact.urls.large}
alt={name}
onClick={() => setIsFullScreen(true)}
className="cursor-zoom-in rounded-lg w-[500px]"
/>
{/* Navigation arrows */}
{artifacts.length > 1 && (
<>
Expand All @@ -65,6 +66,25 @@ const ListingImageFlipper = (props: Props) => {
</>
)}
</div>

{isFullScreen && (
<div
className="fixed inset-0 z-50 bg-black/90 flex items-center justify-center"
onClick={() => setIsFullScreen(false)}
>
<button
onClick={() => setIsFullScreen(false)}
className="absolute top-4 right-4 text-gray-2 hover:text-gray-11 p-2"
>
<FaTimes className="w-6 h-6" />
</button>
<img
src={currentArtifact.urls.large}
alt={name}
className="max-h-[90vh] max-w-[90vw]"
/>
</div>
)}
</>
);
};
Expand Down
52 changes: 1 addition & 51 deletions frontend/src/components/listing/ListingImageGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,6 @@ interface Props {
listingId: string;
}

const FullScreenImage = ({
artifact,
onClose,
}: {
artifact: Artifact;
onClose: () => void;
}) => {
return (
<div
className="fixed inset-0 z-50 bg-black bg-opacity-90 flex items-center justify-center"
onClick={(e) => {
if (e.target === e.currentTarget) {
onClose();
}
}}
>
<button
onClick={onClose}
className="absolute top-4 right-4 text-white hover:text-gray-300 p-2"
>
<FaTimes className="w-6 h-6" />
</button>
<div className="max-h-[90vh] max-w-[90vw] relative">
<img
src={artifact.urls.large}
alt="Full screen view"
className="max-h-[90vh] max-w-[90vw] object-contain"
/>
</div>
</div>
);
};

const ListingImageItem = ({
artifact,
index,
Expand All @@ -59,7 +26,6 @@ const ListingImageItem = ({
setArtifacts,
setCurrentImageIndex,
listingId,
onFullScreen,
}: {
artifact: Artifact;
index: number;
Expand All @@ -69,7 +35,6 @@ const ListingImageItem = ({
setArtifacts: (artifacts: Artifact[]) => void;
setCurrentImageIndex: React.Dispatch<React.SetStateAction<number>>;
listingId: string;
onFullScreen: (artifact: Artifact) => void;
}) => {
const [isDeleting, setIsDeleting] = useState(false);
const [isUpdating, setIsUpdating] = useState(false);
Expand Down Expand Up @@ -160,10 +125,7 @@ const ListingImageItem = ({
className={`aspect-square rounded-lg overflow-hidden cursor-pointer relative ${
currentImageIndex === index ? "ring-2 ring-blue-500" : ""
} ${artifact.is_main ? "ring-2 ring-green-500" : ""}`}
onClick={() => {
setCurrentImageIndex(index);
onFullScreen(artifact);
}}
onClick={() => setCurrentImageIndex(index)}
>
<ListingArtifactRenderer artifact={artifact} />
{canEdit && (
Expand Down Expand Up @@ -206,19 +168,8 @@ const ListingImageGallery = ({ listingId, ...props }: Props) => {
canEdit,
} = props;

const [fullScreenArtifact, setFullScreenArtifact] = useState<Artifact | null>(
null,
);

return (
<>
{fullScreenArtifact && (
<FullScreenImage
artifact={fullScreenArtifact}
onClose={() => setFullScreenArtifact(null)}
/>
)}

{artifacts.length > 0 ? (
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4">
{artifacts.map((artifact, index) => (
Expand All @@ -232,7 +183,6 @@ const ListingImageGallery = ({ listingId, ...props }: Props) => {
artifacts={artifacts}
setArtifacts={setArtifacts}
setCurrentImageIndex={setCurrentImageIndex}
onFullScreen={setFullScreenArtifact}
/>
))}
</div>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/listing/ListingOnshape.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ const ListingOnshape = (props: Props) => {
Upload your URDF file:
<CopyableCode
code={`kscale urdf upload ${listingId} /path/to/your/urdf/directory/`}
className="bg-gray-12 text-green-600 p-4 rounded-lg mt-3 font-mono text-sm border border-gray-200"
className="bg-gray-12 text-green-600 p-2 sm:p-4 rounded-lg mt-3 font-mono text-xs sm:text-sm border border-gray-200 break-all whitespace-pre-wrap"
/>
</li>
</ol>
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/listing/ListingRegisterRobot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import { RegisterRobotModal } from "../modals/RegisterRobotModal";

interface Props {
listingId: string;
className?: string;
}

const ListingRegisterRobot = ({ listingId }: Props) => {
const ListingRegisterRobot = ({ listingId, className }: Props) => {
const [isRegisterModalOpen, setIsRegisterModalOpen] = useState(false);
const { isAuthenticated, currentUser } = useAuthentication();

Expand All @@ -25,7 +26,7 @@ const ListingRegisterRobot = ({ listingId }: Props) => {
<div className="flex flex-col items-start gap-3 mt-2">
<Button
variant="outline"
className="flex items-center"
className={`flex items-center ${className}`}
onClick={() => setIsRegisterModalOpen(true)}
>
<FaPlus className="mr-2 h-4 w-4" />
Expand Down
23 changes: 16 additions & 7 deletions frontend/src/components/listing/ListingRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const ListingRenderer = ({ listing }: { listing: ListingResponse }) => {
return (
<div className="max-w-6xl mx-auto sm:p-4 sm:pt-8">
{/* Main content area - flex column on mobile, row on desktop */}
<div className="flex flex-col md:flex-row gap-8 mb-8">
<div className="flex flex-col md:flex-row gap-8 mb-8 items-center">
<ListingImageFlipper
artifacts={artifacts}
name={name}
Expand Down Expand Up @@ -117,12 +117,21 @@ const ListingRenderer = ({ listing }: { listing: ListingResponse }) => {

{/* Build this robot */}
<div className="flex flex-col sm:flex-row sm:items-baseline gap-4">
{canEdit && <ListingDeleteButton listingId={listingId} />}
<ListingFeatureButton
listingId={listingId}
initialFeatured={isFeatured}
/>
<ListingRegisterRobot listingId={listingId} />
<div className="grid grid-cols-1 xs:grid-cols-3 gap-4 w-full">
{canEdit && (
<ListingDeleteButton
listingId={listingId}
className="w-full"
initialFeatured={isFeatured}
/>
)}
<ListingFeatureButton
listingId={listingId}
initialFeatured={isFeatured}
className="w-full"
/>
<ListingRegisterRobot listingId={listingId} className="w-full" />
</div>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit f78e61b

Please sign in to comment.