diff --git a/frontend/src/components/RequestForStorageItem/RequestForStorageItemComponent.tsx b/frontend/src/components/RequestForStorageItem/RequestForStorageItemComponent.tsx new file mode 100644 index 0000000..9f07ab8 --- /dev/null +++ b/frontend/src/components/RequestForStorageItem/RequestForStorageItemComponent.tsx @@ -0,0 +1,89 @@ +import React from "react"; +import styled from "styled-components"; + +import { CircularProgress } from "@mui/material"; +import { MdCheck, MdError } from "react-icons/md"; +import RequestForStorageContractModel from "../../data/models/RequestForStorageContractModel"; +import constants from "../../util/Constants"; + +function RequestForStorageContractComponent(props: { item: RequestForStorageContractModel }) { + return ( + +
+

+ Request ID: + {props.item.requestId} +

+
+
+

+ Client: + {props.item.request.client} +

+
+
+

+ Content: + {props.item.request.content.cid} +

+
+
+ ); +} + +export default RequestForStorageContractComponent; + +const RequestForStorageContractComponentWrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: space-between; + background-color: ${constants.surfaceColor}; + border-radius: 8px; + padding: 10px; + width: 80%; + margin-top: 20px; + + div { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + width: 100%; + margin: 5px; + } + + div p:nth-child(1) { + text-align: start; + } + + div p:nth-child(2) { + text-align: end; + } + + p { + flex: 1; + font-size: 1rem; + text-align: start; + margin: 5px; + } + + p span { + font-weight: bold; + } + + #cid { + flex: 2; + } + + @media (max-width: 1180px) { + width: 85%; + } + + @media (max-width: 768px) { + width: 90%; + } + + @media (max-width: 450px) { + width: 95%; + } +`; diff --git a/frontend/src/data/models/RequestForStorageContractModel.ts b/frontend/src/data/models/RequestForStorageContractModel.ts index 8272892..726f5d7 100644 --- a/frontend/src/data/models/RequestForStorageContractModel.ts +++ b/frontend/src/data/models/RequestForStorageContractModel.ts @@ -1,17 +1,31 @@ -enum RequestForStorageContractStatus { - UPLOADING = "UPLOADING", - UPLOADED = "UPLOADED", - FAILED = "FAILED", -} - type RequestForStorageContractModel = { - purchaseid: string; - lastModified: string; - reward: string; - duration: string; - collateral: string; - status: RequestForStorageContractStatus; + requestId: string; + request: Request; + state: string; + error: string; }; -export default RequestForStorageContractModel; -export { RequestForStorageContractStatus }; +type Request = { + client: string; + ask: Ask; + content: Content; + expiry: string; + nonce: string; + id: string; + } + + type Ask = { + slots: number; + slotSize: string; + duration: string; + proofProbability: string; + reward: string; + collateral: string; + maxSlotLoss: number; + } + + type Content = { + cid: string; + } + +export default RequestForStorageContractModel; \ No newline at end of file diff --git a/frontend/src/pages/marketplace/Marketplace.tsx b/frontend/src/pages/marketplace/Marketplace.tsx index 8e1f971..96e0bfc 100644 --- a/frontend/src/pages/marketplace/Marketplace.tsx +++ b/frontend/src/pages/marketplace/Marketplace.tsx @@ -3,16 +3,16 @@ import TabBarView from "../../components/layout/tabBarView/TabBarView"; import styled from "styled-components"; import { MdFileUpload, MdFileDownload } from "react-icons/md"; -import UploadTab from "./tabs/status/status"; +import UploadTab from "./tabs/status/StatusTab"; import DownloadTab from "./tabs/create/CreateTab"; -import AvailableTab from "./tabs/status/status"; +import StatusTab from "./tabs/status/StatusTab"; import CreateTab from "./tabs/create/CreateTab"; function MarketplacePage() { return (
- + diff --git a/frontend/src/pages/marketplace/tabs/status/StatusTab.tsx b/frontend/src/pages/marketplace/tabs/status/StatusTab.tsx new file mode 100644 index 0000000..ea42216 --- /dev/null +++ b/frontend/src/pages/marketplace/tabs/status/StatusTab.tsx @@ -0,0 +1,109 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { useDropzone } from "react-dropzone"; +import styled from "styled-components"; +import RequestForStorageContractModel from "../../../../data/models/RequestForStorageContractModel"; +import axios from "axios"; + +import { useDexyStore } from "../../../../store"; +import constants from "../../../../util/Constants"; +import RequestForStorageContractComponent from "../../../../components/RequestForStorageItem/RequestForStorageItemComponent"; + +function StatusTab() { + const { storageRequests, setStorageRequests, nodeInfo } = useDexyStore(); + const purchaseIds = useRef([]); + const purchaseInfo = useRef([]); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + setIsLoading(true); + // Fetch purchase IDs + fetch(`/api/codex/v1/storage/purchases`, { + headers: { + Authorization: nodeInfo.auth ? "Basic " + btoa(nodeInfo.auth) : "", + }, + }) + .then((response) => response.json()) + .then((data) => { + purchaseIds.current = data; + setIsLoading(false); + }) + .catch((error) => { + console.error("Error fetching purchase IDs:", error); + setIsLoading(false); + }); + }, []); + + useEffect(() => { + if (purchaseIds.current.length > 0) { + setIsLoading(true); + // Fetch purchase info for each purchase ID + Promise.all( + purchaseIds.current.map((purchaseId) => + fetch(`/api/codex/v1/storage/purchases/${purchaseId}`, { + headers: { + Authorization: nodeInfo.auth ? "Basic " + btoa(nodeInfo.auth) : "", + }, + }).then((response) => response.json()) + ) + ) + .then((data) => { + purchaseInfo.current = data; + setIsLoading(false); + }) + .catch((error) => { + console.error("Error fetching purchase info:", error); + setIsLoading(false); + }); + } + }, [purchaseIds.current]); + + useEffect(() => { + if (purchaseInfo.current.length !== storageRequests.length) { + setStorageRequests(purchaseInfo.current); + } + }, [purchaseInfo.current]); + + return ( + +
0 ? "60vh" : "0%", + }} + > + {isLoading ? ( +

Loading...

+ ) : ( + storageRequests.map((item) => ( + + )) + )} +
+
+ ); +} + +export default StatusTab; + +const StatusTabWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + width: 100%; + padding: 16px; + + #request-wrap { + display: flex; + flex-direction: column; + align-items: center; + justify-content: start; + width: 100%; + overflow-y: scroll; + margin-top: 16px; + } +`; diff --git a/frontend/src/pages/marketplace/tabs/status/status.tsx b/frontend/src/pages/marketplace/tabs/status/status.tsx deleted file mode 100644 index 05177e4..0000000 --- a/frontend/src/pages/marketplace/tabs/status/status.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { useCallback, useEffect, useRef } from "react"; -import { useDropzone } from "react-dropzone"; -import styled from "styled-components"; -import UploadedItemModel, { - UploadedItemStatus, -} from "../../../../data/models/UploadedItemModel"; -import UploadedItemComponent from "../../../../components/uploadedItem/UploadedItemComponent"; -import axios from "axios"; - -import { useDexyStore } from "../../../../store"; -import constants from "../../../../util/Constants"; - -function AvailableTab() { -// fetch( -// `/api/codex/v1/storage/request/${cid}`, -// { -// headers: -// (nodeInfo.auth !== null && { -// Authorization: -// (nodeInfo.auth && "Basic " + btoa(nodeInfo.auth)) || "", -// }) || -// {}, -// body: JSON.stringify({ -// reward: reward, -// duration: duration, -// proofProbability: proofProbability, -// collateral: collateral -// }) -// } -// ) -// // create a popup in the browser to show if the upload was successful -// .then((response) => { -// if (response.status === 200) { -// alert("Upload successful!"); -// } else { -// alert("Upload failed!"); -// } -// }) -// } - return ( - - {/*
0 ? "60vh" : "0%", - }} - > - {uploads.map((file) => ( - - ))} -
*/} -
- ); -} - -export default AvailableTab; - -const AvailableTabWrapper = styled.div` - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100%; - width: 100%; - padding: 16px; - - #dropzone { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - width: 100%; - border: 2px dashed #9e9e9e; - border-radius: 8px; - } - - p { - font-size: 1rem; - text-align: center; - } - - #uploaded-items-wrap { - display: flex; - flex-direction: column; - align-items: center; - justify-content: start; - width: 100%; - overflow-y: scroll; - margin-top: 16px; - } -`;