diff --git a/frontend/src/components/gdpr/gdprbanner.tsx b/frontend/src/components/gdpr/gdprbanner.tsx index 05736898..445cc90e 100644 --- a/frontend/src/components/gdpr/gdprbanner.tsx +++ b/frontend/src/components/gdpr/gdprbanner.tsx @@ -47,7 +47,7 @@ const GDPRBanner: React.FC = () => { return ( <> {isVisible && !showOptOutForm && ( -
+
We value your privacy 🔒
We use cookies to make it easier to interact with our website and to diff --git a/frontend/src/components/listing/FeaturedListings.tsx b/frontend/src/components/listing/FeaturedListings.tsx index 85e989d3..2882c367 100644 --- a/frontend/src/components/listing/FeaturedListings.tsx +++ b/frontend/src/components/listing/FeaturedListings.tsx @@ -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 => - 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 => + 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); diff --git a/frontend/src/components/listing/ListingDeleteButton.tsx b/frontend/src/components/listing/ListingDeleteButton.tsx index f8cad95d..40e37a0f 100644 --- a/frontend/src/components/listing/ListingDeleteButton.tsx +++ b/frontend/src/components/listing/ListingDeleteButton.tsx @@ -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); @@ -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); } }; @@ -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}`} > - + {deleting ? "Deleting..." : "Delete Listing"} setConfirmDelete(false)}> diff --git a/frontend/src/components/listing/ListingFeatureButton.tsx b/frontend/src/components/listing/ListingFeatureButton.tsx index 8c634b72..b956bb3c 100644 --- a/frontend/src/components/listing/ListingFeatureButton.tsx +++ b/frontend/src/components/listing/ListingFeatureButton.tsx @@ -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); @@ -81,7 +87,7 @@ const ListingFeatureButton = (props: Props) => { ? "Remove from featured" : "Add to featured" } - className="flex items-center" + className={`flex items-center ${className}`} > diff --git a/frontend/src/components/listing/ListingImageFlipper.tsx b/frontend/src/components/listing/ListingImageFlipper.tsx index 2625ab8a..2d5180f0 100644 --- a/frontend/src/components/listing/ListingImageFlipper.tsx +++ b/frontend/src/components/listing/ListingImageFlipper.tsx @@ -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; @@ -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 ( @@ -33,12 +33,13 @@ const ListingImageFlipper = (props: Props) => { return ( <> - {/* Main image - full width on mobile, half width on desktop */} -
-
- -
- +
+ {name} setIsFullScreen(true)} + className="cursor-zoom-in rounded-lg w-[500px]" + /> {/* Navigation arrows */} {artifacts.length > 1 && ( <> @@ -65,6 +66,25 @@ const ListingImageFlipper = (props: Props) => { )}
+ + {isFullScreen && ( +
setIsFullScreen(false)} + > + + {name} +
+ )} ); }; diff --git a/frontend/src/components/listing/ListingImageGallery.tsx b/frontend/src/components/listing/ListingImageGallery.tsx index 529b1d67..ac550acf 100644 --- a/frontend/src/components/listing/ListingImageGallery.tsx +++ b/frontend/src/components/listing/ListingImageGallery.tsx @@ -17,39 +17,6 @@ interface Props { listingId: string; } -const FullScreenImage = ({ - artifact, - onClose, -}: { - artifact: Artifact; - onClose: () => void; -}) => { - return ( -
{ - if (e.target === e.currentTarget) { - onClose(); - } - }} - > - -
- Full screen view -
-
- ); -}; - const ListingImageItem = ({ artifact, index, @@ -59,7 +26,6 @@ const ListingImageItem = ({ setArtifacts, setCurrentImageIndex, listingId, - onFullScreen, }: { artifact: Artifact; index: number; @@ -69,7 +35,6 @@ const ListingImageItem = ({ setArtifacts: (artifacts: Artifact[]) => void; setCurrentImageIndex: React.Dispatch>; listingId: string; - onFullScreen: (artifact: Artifact) => void; }) => { const [isDeleting, setIsDeleting] = useState(false); const [isUpdating, setIsUpdating] = useState(false); @@ -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)} > {canEdit && ( @@ -206,19 +168,8 @@ const ListingImageGallery = ({ listingId, ...props }: Props) => { canEdit, } = props; - const [fullScreenArtifact, setFullScreenArtifact] = useState( - null, - ); - return ( <> - {fullScreenArtifact && ( - setFullScreenArtifact(null)} - /> - )} - {artifacts.length > 0 ? (
{artifacts.map((artifact, index) => ( @@ -232,7 +183,6 @@ const ListingImageGallery = ({ listingId, ...props }: Props) => { artifacts={artifacts} setArtifacts={setArtifacts} setCurrentImageIndex={setCurrentImageIndex} - onFullScreen={setFullScreenArtifact} /> ))}
diff --git a/frontend/src/components/listing/ListingOnshape.tsx b/frontend/src/components/listing/ListingOnshape.tsx index 9a2793e2..72ca79a0 100644 --- a/frontend/src/components/listing/ListingOnshape.tsx +++ b/frontend/src/components/listing/ListingOnshape.tsx @@ -384,7 +384,7 @@ const ListingOnshape = (props: Props) => { Upload your URDF file: diff --git a/frontend/src/components/listing/ListingRegisterRobot.tsx b/frontend/src/components/listing/ListingRegisterRobot.tsx index dc027abd..727cd1a8 100644 --- a/frontend/src/components/listing/ListingRegisterRobot.tsx +++ b/frontend/src/components/listing/ListingRegisterRobot.tsx @@ -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(); @@ -25,7 +26,7 @@ const ListingRegisterRobot = ({ listingId }: Props) => {