Skip to content

Commit

Permalink
adding back some more frontend features
Browse files Browse the repository at this point in the history
  • Loading branch information
codekansas committed Jul 31, 2024
1 parent 595aa18 commit b2b647e
Show file tree
Hide file tree
Showing 20 changed files with 266 additions and 78 deletions.
5 changes: 3 additions & 2 deletions frontend/src/components/files/UploadImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ interface ImageUploadProps {
imageId?: string | null;
}

const ImageUploadComponent: React.FC<ImageUploadProps> = ({ imageId }) => {
const ImageUploadComponent = (props: ImageUploadProps) => {
const { imageId } = props;

const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [compressedFile, setCompressedFile] = useState<File | null>(null);
const [uploadStatus, setUploadStatus] = useState<string | null>(null);
Expand Down Expand Up @@ -205,7 +207,6 @@ const ImageUploadComponent: React.FC<ImageUploadProps> = ({ imageId }) => {
crop={crop}
aspect={1}
onChange={(c) => {
console.log(c);
setCrop(c);
}}
onComplete={handleCropComplete}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/files/UploadURDF.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface URDFUploadProps {
onUploadSuccess: (url: string) => void;
}

const URDFUploadComponent: React.FC<URDFUploadProps> = () => {
const URDFUploadComponent = ({}: URDFUploadProps) => {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [uploadStatus, setUploadStatus] = useState<string | null>(null);
const [fileError, setFileError] = useState<string | null>(null);
Expand Down
110 changes: 110 additions & 0 deletions frontend/src/components/listing/ListingArtifacts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import ImageComponent from "components/files/ViewImage";
import { components } from "gen/api";
import { useAlertQueue } from "hooks/alerts";
import { useAuthentication } from "hooks/auth";
import { useEffect, useState } from "react";
import { Carousel, Row, Spinner } from "react-bootstrap";

const EmptyCarouselItem = ({ loading }: { loading: boolean }) => {
// TODO: Render a better default loading state.
return (
<Carousel.Item>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
overflow: "hidden",
}}
>
{loading ? <Spinner animation="border" /> : <p>No artifacts</p>}
</div>
</Carousel.Item>
);
};

interface Props {
listing_id: string;
edit: boolean;
}

const ListingArtifacts = (props: Props) => {
const { listing_id } = props;

const auth = useAuthentication();
const { addErrorAlert } = useAlertQueue();

const [artifacts, setArtifacts] = useState<
components["schemas"]["ListArtifactsResponse"] | null
>(null);

useEffect(() => {
const fetchArtifacts = async () => {
const { data, error } = await auth.client.GET("/artifacts/{listing_id}", {
params: {
path: { listing_id },
},
});

if (error) {
addErrorAlert(error);
} else {
setArtifacts(data);
}
};
fetchArtifacts();
}, [listing_id]);

return (
<Row className="mb-3">
<Carousel
indicators
data-bs-theme="dark"
style={{ border: "1px solid #ccc" }}
interval={null}
controls={artifacts !== null && artifacts.artifacts.length > 1}
>
{artifacts === null || artifacts.artifacts.length === 0 ? (
<EmptyCarouselItem loading={true} />
) : (
artifacts.artifacts.map((artifact, key) => (
<Carousel.Item key={key}>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
overflow: "hidden",
}}
>
{artifact.artifact_type === "image" ? (
<ImageComponent
imageId={artifact.artifact_id}
size={"large"}
caption={"caption"}
/>
) : (
<p>Unhandled artifact type: {artifact.artifact_type}</p>
)}
</div>
<Carousel.Caption
style={{
backgroundColor: "rgba(255, 255, 255, 0.5)",
color: "black",
padding: "0.1rem",
// Put the caption at the top
top: 10,
bottom: "unset",
}}
>
{artifact.description}
</Carousel.Caption>
</Carousel.Item>
))
)}
</Carousel>
</Row>
);
};

export default ListingArtifacts;
1 change: 1 addition & 0 deletions frontend/src/components/listing/ListingChildren.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Row } from "react-bootstrap";

interface Props {
child_ids: string[];
edit: boolean;
}

const ListingChildren = (props: Props) => {
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/components/listing/ListingDeleteButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import TCButton from "components/files/TCButton";
import { humanReadableError } from "constants/backend";
import { useAlertQueue } from "hooks/alerts";
import { useAuthentication } from "hooks/auth";
import { useState } from "react";
Expand All @@ -14,7 +13,7 @@ const ListingDeleteButton = (props: Props) => {
const { listing_id } = props;
const [deleting, setDeleting] = useState(false);

const { addAlert } = useAlertQueue();
const { addAlert, addErrorAlert } = useAlertQueue();
const auth = useAuthentication();
const navigate = useNavigate();

Expand All @@ -31,7 +30,7 @@ const ListingDeleteButton = (props: Props) => {
);

if (error) {
addAlert(humanReadableError(error), "error");
addErrorAlert(error);
setDeleting(false);
} else {
addAlert("Listing deleted successfully", "success");
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/listing/ListingDescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Row } from "react-bootstrap";

interface Props {
description: string | null;
edit: boolean;
}

const ListingDescription = (props: Props) => {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/listing/ListingTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Row } from "react-bootstrap";

interface Props {
title: string;
edit: boolean;
}

const ListingTitle = (props: Props) => {
Expand Down
50 changes: 36 additions & 14 deletions frontend/src/gen/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,15 +277,15 @@ export interface paths {
patch?: never;
trace?: never;
};
"/artifacts/image/{image_id}/{size}": {
"/artifacts/url/{artifact_type}/{artifact_id}": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** Images Url */
get: operations["images_url_artifacts_image__image_id___size__get"];
/** Artifact Url */
get: operations["artifact_url_artifacts_url__artifact_type___artifact_id__get"];
put?: never;
post?: never;
delete?: never;
Expand All @@ -294,15 +294,15 @@ export interface paths {
patch?: never;
trace?: never;
};
"/artifacts/{artifact_type}/{artifact_id}": {
"/artifacts/{listing_id}": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** Urdf Url */
get: operations["urdf_url_artifacts__artifact_type___artifact_id__get"];
/** List Artifacts */
get: operations["list_artifacts_artifacts__listing_id__get"];
put?: never;
post?: never;
delete?: never;
Expand Down Expand Up @@ -392,6 +392,27 @@ export interface components {
/** Detail */
detail?: components["schemas"]["ValidationError"][];
};
/** ListArtifactsItem */
ListArtifactsItem: {
/** Artifact Id */
artifact_id: string;
/**
* Artifact Type
* @enum {string}
*/
artifact_type: "image" | "urdf" | "mjcf";
/** Description */
description: string | null;
/** Timestamp */
timestamp: number;
/** Url */
url: string;
};
/** ListArtifactsResponse */
ListArtifactsResponse: {
/** Artifacts */
artifacts: components["schemas"]["ListArtifactsItem"][];
};
/** ListListingsResponse */
ListListingsResponse: {
/** Listings */
Expand Down Expand Up @@ -922,13 +943,15 @@ export interface operations {
};
};
};
images_url_artifacts_image__image_id___size__get: {
artifact_url_artifacts_url__artifact_type___artifact_id__get: {
parameters: {
query?: never;
query?: {
size?: "small" | "large";
};
header?: never;
path: {
image_id: string;
size: "small" | "large";
artifact_type: "image" | "urdf" | "mjcf";
artifact_id: string;
};
cookie?: never;
};
Expand All @@ -954,13 +977,12 @@ export interface operations {
};
};
};
urdf_url_artifacts__artifact_type___artifact_id__get: {
list_artifacts_artifacts__listing_id__get: {
parameters: {
query?: never;
header?: never;
path: {
artifact_type: "urdf" | "mjcf";
artifact_id: string;
listing_id: string;
};
cookie?: never;
};
Expand All @@ -972,7 +994,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"application/json": unknown;
"application/json": components["schemas"]["ListArtifactsResponse"];
};
};
/** @description Validation Error */
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/hooks/alerts.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { humanReadableError } from "constants/backend";
import {
createContext,
ReactNode,
Expand Down Expand Up @@ -29,6 +30,7 @@ interface AlertQueueContextProps {
alerts: Map<string, [string | ReactNode, AlertType]>;
removeAlert: (alertId: string) => void;
addAlert: (alert: string | ReactNode, kind: AlertType) => void;
addErrorAlert: (alert: any) => void;
}

const AlertQueueContext = createContext<AlertQueueContextProps | undefined>(
Expand Down Expand Up @@ -69,6 +71,13 @@ export const AlertQueueProvider = (props: AlertQueueProviderProps) => {
[generateAlertId],
);

const addErrorAlert = useCallback(
(alert: any | undefined) => {
addAlert(humanReadableError(alert), "error");
},
[addAlert],
);

const removeAlert = useCallback((alertId: string) => {
setAlerts((prev) => {
const newAlerts = new Map(prev);
Expand All @@ -83,6 +92,7 @@ export const AlertQueueProvider = (props: AlertQueueProviderProps) => {
alerts,
removeAlert,
addAlert,
addErrorAlert,
}}
>
{children}
Expand Down
21 changes: 14 additions & 7 deletions frontend/src/pages/ListingDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import ListingArtifacts from "components/listing/ListingArtifacts";
import ListingChildren from "components/listing/ListingChildren";
import ListingDeleteButton from "components/listing/ListingDeleteButton";
import ListingDescription from "components/listing/ListingDescription";
import ListingTitle from "components/listing/ListingTitle";
import { humanReadableError } from "constants/backend";
import { paths } from "gen/api";
import { useAlertQueue } from "hooks/alerts";
import { useAuthentication } from "hooks/auth";
Expand All @@ -21,16 +21,23 @@ const RenderListing = (props: RenderListingProps) => {
const { listing } = props;
return (
<Col>
<ListingTitle title={listing.name} />
<ListingDescription description={listing.description} />
<ListingChildren child_ids={listing.child_ids} />
<ListingTitle title={listing.name} edit={listing.owner_is_user} />
<ListingDescription
description={listing.description}
edit={listing.owner_is_user}
/>
<ListingChildren
child_ids={listing.child_ids}
edit={listing.owner_is_user}
/>
<ListingArtifacts listing_id={listing.id} edit={listing.owner_is_user} />
{listing.owner_is_user && <ListingDeleteButton listing_id={listing.id} />}
</Col>
);
};

const ListingDetails = () => {
const { addAlert } = useAlertQueue();
const { addErrorAlert } = useAlertQueue();
const auth = useAuthentication();
const { id } = useParams();
const [listing, setListing] = useState<ListingResponse | null>(null);
Expand All @@ -50,12 +57,12 @@ const ListingDetails = () => {
},
});
if (error) {
addAlert(humanReadableError(error), "error");
addErrorAlert(error);
} else {
setListing(data);
}
} catch (err) {
addAlert(humanReadableError(err), "error");
addErrorAlert(err);
}
};
fetchListing();
Expand Down
Loading

0 comments on commit b2b647e

Please sign in to comment.