Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion dv-marketplace/config.ts

This file was deleted.

52 changes: 52 additions & 0 deletions dv-marketplace/src/assets/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,55 @@
justify-content: 'center';
margin-top: 15px;
}


.thumbnail-image-wrapper {
width: 100%;
aspect-ratio: 1 / 1; /* Makes it a square */
overflow: hidden;
position: relative;

}

.thumbnail-image-wrapper img {
width: 100%;
height: 100%;
object-fit: cover; /* Ensures no distortion */
object-position: center;

}

.thumbnail-caption {

text-align: left;
flex-grow: 1;
display: flex;
align-items: flex-start;
padding: 10px;
}

.thumbnail-caption span {

text-align: left;
max-height: 3.6em; /* Adjust based on desired number of lines */
overflow-y: auto;
padding: 0.5rem;
font-size: 0.9rem;
line-height: 1.2rem;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE 10+ */

}


.thumbnail-title {

font-weight: bold;
font-size: 1rem;
white-space: nowrap; /* Prevent line breaks */
overflow: hidden; /* Hide overflow */
text-overflow: ellipsis; /* Add "..." if too long */
width: 100%;
display: block;

}
44 changes: 44 additions & 0 deletions dv-marketplace/src/components/UI/FormInputFieldsRefactor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
interface FormInputFieldProps {
label: string;
name: string;
id: string;
value?: string | undefined;
htmlType?: string;
datalist?:string;
}

export const FormInputTextField = ({ label, name, id, value, datalist}: FormInputFieldProps) => {
return (
<div className="mb-3">
<label htmlFor={id} className="form-label">{label}</label>
<input type="text" className="form-control" id={id} name={name} defaultValue={value} list={datalist}/>
</div>
);
}

export const FormInputTextArea = ({ label, name, id, value}: FormInputFieldProps) => {
return (
<div className="mb-3">
<label htmlFor={id} className="form-label">{label}</label>
<textarea className="form-control" id={id} name={name} defaultValue={value} />
</div>
);
}

export const FormInputCheckbox = ({ label, name, id, value}: FormInputFieldProps) => {
return (
<div className="mb-3 form-check">
<input type="checkbox" className="form-check-input" id={id} name={name} defaultChecked={value === "true"} />
<label className="form-check-label" htmlFor={id}>{label}</label>
</div>
);
}

export const FormInput = ({ label, name, id, value, htmlType}: FormInputFieldProps) => {
return (
<div className="mb-3">
<label htmlFor={id} className="form-label">{label}</label>
<input type={htmlType} className="form-control" id={id} name={name} defaultValue={value} />
</div>
);
}
14 changes: 10 additions & 4 deletions dv-marketplace/src/components/UI/MarketplaceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface CardDeckProps {
imageId?: number | undefined;
text?: string | undefined;
link?: string | undefined;
onClick?: () => void | undefined;
}

export const MarketplaceLinkCard = ({ header, imageId, text, link, children }: CardDeckProps) => {
Expand All @@ -23,10 +24,11 @@ export const MarketplaceLinkCard = ({ header, imageId, text, link, children }: C
);
}

export const MarketplaceCard = ({ header, imageId, text, link, children }: CardDeckProps) => {
export const MarketplaceCard = ({ header, imageId, text, link, children, onClick }: CardDeckProps) => {

return (
<div className="col-12 col-sm-6 col-md-6 col-lg-3 mb-2 px-0">
<div className="col-12 col-sm-6 col-md-6 col-lg-3 mb-2 px-0"
onClick={onClick} style={{ cursor: onClick ? 'pointer' : 'default' }}>
<BaseCard header={header} imageId={imageId} text={text} link={link}>
{children}
</BaseCard>
Expand All @@ -52,17 +54,21 @@ export const BaseCard = ({ header, imageId, text, children }: CardDeckProps) =>
<Card>
{header && (
<Card.Header>
<h5 className="text-center">{header}</h5>
<h5 className="tumbnail-title">{header}</h5>
</Card.Header>
)}
{imageId && (
<div className='thumbnail-image-wrapper'>
<Card.Img variant="top" src={getImageUrl(imageId)} className="rounded-5 p-1"
style={{ maxHeight: '200px', minHeight: '100px', objectFit: 'scale-down' }}/>
</div>
)}
<Card.Body>
{text && (
<Card.Text className="card-text">
<Card.Text className="thumbnail-caption">
<span>
{text.length > 100 ? `${text.substring(0, 100)}...` : text}
</span>
</Card.Text>
)}
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,27 @@ export default function useThemeSwitcher() {
const userContext = useContext(UserContext);
const [themeClass, setThemeClass] = useState("bi bi-circle-half");

const handleThemeChange = (newTheme: Theme) => {
userContext.setTheme(newTheme);
};
const handleThemeChange = (newTheme: Theme) => {


userContext.setTheme(newTheme as Theme);

useEffect(() => {

document.documentElement.setAttribute("data-bs-theme", userContext.theme || "auto");
const savedTheme = localStorage.getItem("theme") as Theme;

if (savedTheme !== userContext.theme && userContext.theme !== undefined) {
console.log("Theme changed to: ", userContext.theme);
localStorage.setItem("theme", userContext.theme);
if (newTheme === Theme.AUTO) {
setThemeClass("bi bi-circle-half");
} else if (newTheme === Theme.LIGHT) {
setThemeClass("bi bi-sun");
} else if (newTheme === Theme.DARK) {
setThemeClass("bi bi-moon");
}

localStorage.setItem("theme", newTheme);
};

useEffect(() => {

document.documentElement.setAttribute("data-bs-theme", userContext.theme || "auto");

if (userContext.theme === Theme.AUTO) {
setThemeClass("bi bi-circle-half");
} else if (userContext.theme === Theme.LIGHT) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const UserContext = createContext<UserContextType>({
setUser: () => {},
showLogin: false,
setShowLogin: () => {},
theme: Theme.AUTO,

setTheme: () => {}

});
Expand All @@ -31,9 +31,6 @@ const UserContextProvider = ({ children }: { children: React.ReactNode }) => {
} else {
setTheme(Theme.AUTO);
}

console.log(savedTheme)

}, []);

useEffect((): void => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ export default function useEditImageForm({ tool }: { tool: ExternalTool | undefi
const handleImageSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
console.log("FormData: ", formData);
const data = await postFormRequest(`/api/tools/${tool?.id}/images`, formData);
console.log("Data: ", data);
if (Array.isArray(data)) {
for (const item of data) {
images.push(item);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import type { Manifest } from "../../../../types/MarketplaceTypes";
import { createFormChangeHandler } from "../../../UI/FormInputFields";
import useMarketplaceApiRepo from "../../../../repositories/useMarketplaceApiRepo";
import { toast } from "react-toastify";


export default function useEditManifestForm(initialManifest?: Manifest, show?: boolean) {
Expand Down Expand Up @@ -78,7 +79,7 @@ export default function useEditManifestForm(initialManifest?: Manifest, show?: b
setFormManifest(data);

} catch (err) {
console.error("Invalid JSON file", err);
toast.error("Invalid JSON file format. Please upload a valid manifest JSON file.");
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useContext, useState } from "react";
import { UserContext } from "../../context/UserContextProvider";
import axios from "axios";
import useMarketplaceApiRepo from "../../../repositories/useMarketplaceApiRepo";
import { toast } from "react-toastify";



Expand All @@ -24,9 +25,9 @@ export default function useLoginCustomForm() {
try {
const response = await axios.post(`${BASE_URL}/api/auth/login`, { username, password });
userContext.setUser(response.data);
console.log('Login successful');
toast.success('Login successful!');
} catch (error) {
console.error('Login failed', error);
toast.error('Login failed. Please check your credentials.');
}
handleClose();
};
Expand Down
61 changes: 61 additions & 0 deletions dv-marketplace/src/components/pages/ImagesCarrouselView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { forwardRef, useImperativeHandle, useState } from "react";
import { Image } from "../../types/MarketplaceTypes";
import { Modal } from "react-bootstrap";
import useMarketplaceApiRepo from "../../repositories/useMarketplaceApiRepo";

type ImagesCarrouselViewProps = {
images: Image[] | undefined;
selected?: number;
};

export type ModalRef = {
open: (selected: number) => void;
close: () => void;
}



const ImagesCarrouselView = forwardRef<ModalRef, ImagesCarrouselViewProps>(({ images, selected = 0 }, ref) => {

const [show, setShow] = useState(false);
const { getImageUrl } = useMarketplaceApiRepo();

const [currentIndex, setCurrentIndex] = useState(selected);
const handleClose = () => setShow(false);
const handleShow = (display: number) => {
setCurrentIndex(display);
setShow(true);
}


useImperativeHandle(ref, () => ({
open: handleShow,
close: handleClose,
}));

return (
<Modal
show={show}
onHide={handleClose}
size="lg"
centered
>
<Modal.Header closeButton>
<Modal.Title>Image Viewer</Modal.Title>
</Modal.Header>
<Modal.Body className="p-0 d-flex justify-content-center align-items-center" style={{ background: "#000" }}>
<img
src={getImageUrl(images?.[currentIndex]?.imageId ?? 0)}
alt="Selected"
className="img-fluid"
style={{ maxHeight: "80vh", maxWidth: "100%", objectFit: "contain" }}
/>
</Modal.Body>
</Modal>



)
});

export default ImagesCarrouselView;
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { ExternalTool } from '../../../types/MarketplaceTypes';
import axios from "axios";
import useMarketplaceApiRepo from "../../../repositories/useMarketplaceApiRepo";
import { UserContext } from "../../context/UserContextProvider";
import { toast } from "react-toastify";

export default function useMarketplaceHome(onlyMine = false) {

Expand All @@ -21,7 +22,7 @@ export default function useMarketplaceHome(onlyMine = false) {
const response = await axios.get(url);
setTools(response.data);
} catch (error) {
console.error('Error fetching tools:', error);
toast.error(`Error fetching tools:`);
}
};

Expand Down
32 changes: 23 additions & 9 deletions dv-marketplace/src/components/pages/ViewExternalTool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { InnerCardDeck } from "../UI/CardDeck";
import { RowCard, MarketplaceCard, BaseCard } from "../UI/MarketplaceCard";
import InstallExToolFrame from "./InstallExToolFrame";
import useViewExternalTool from "./useViewExternalTool";
import { useEffect } from "react";
import { useEffect, useRef } from "react";
import ImagesCarrouselView, { ModalRef } from "./ImagesCarrouselView";
import { toast } from "react-toastify";



Expand All @@ -31,12 +33,18 @@ const ViewExternalTool = () => {
const response = await axios.get(`${BASE_URL}/api/tools/${id}`);
setTool(response.data as ExternalTool);
} catch (error) {
console.error("Error fetching the tool:", error);
toast.error(`Error fetching tool`);
}
};
fetchTool();
}, [id, BASE_URL, setTool]);

const modalRef = useRef<ModalRef>(null);

const openModal = (selected: number) => {
modalRef.current?.open(selected);
}

return (
<div className="container" style={{ marginTop: "120px" }}>

Expand Down Expand Up @@ -131,18 +139,24 @@ const ViewExternalTool = () => {
</div>

</div>
<ImagesCarrouselView ref={modalRef} images={tool?.images} selected={0} />
<InnerCardDeck>

{tool?.images.map((image: Image) => (
<MarketplaceCard
key={image.imageId}
imageId={image.storedResourceId}>
</MarketplaceCard>

{tool?.images.map((image: Image, idx: number) => (
<>
{/* <div onClick={() => openModal(idx)} style={{ cursor: "pointer" }}> */}
<MarketplaceCard
key={image.imageId}
imageId={image.storedResourceId}
onClick={() => openModal(idx)}>
</MarketplaceCard>
{/* </div> */}
</>
))}
</InnerCardDeck>

<InstallExToolFrame manifest={toolToInstall} showModal={showModal} setShowModal={setShowModal} />




Expand Down
Loading
Loading