From 07cf92153f5cc69d2e4276f85e223276a64b7af7 Mon Sep 17 00:00:00 2001 From: Shaun Orssaud <72015533+Shorssaud@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:59:52 +0900 Subject: [PATCH] ADD availabilities pages --- docker-compose.yml | 3 +- .../AvailableComponent/AvailableComponent.tsx | 97 ++++++++++++ .../layout/tabBarView/TabBarViewText.tsx | 71 +++++++++ frontend/src/data/models/AvailabilityModel.ts | 9 ++ .../src/pages/data/tabs/upload/UploadTab.tsx | 3 +- .../src/pages/marketplace/Marketplace.tsx | 22 ++- .../tabs/Availability/Availability.tsx | 72 +++++++++ .../tabs/Availability/OfferStorage.tsx | 142 ++++++++++++++++++ .../tabs/{create => Rosc}/CreateTab.tsx | 0 .../tabs/{status => Rosc}/StatusTab.tsx | 0 frontend/src/store.ts | 5 + 11 files changed, 415 insertions(+), 9 deletions(-) create mode 100644 frontend/src/components/AvailableComponent/AvailableComponent.tsx create mode 100644 frontend/src/components/layout/tabBarView/TabBarViewText.tsx create mode 100644 frontend/src/data/models/AvailabilityModel.ts create mode 100644 frontend/src/pages/marketplace/tabs/Availability/Availability.tsx create mode 100644 frontend/src/pages/marketplace/tabs/Availability/OfferStorage.tsx rename frontend/src/pages/marketplace/tabs/{create => Rosc}/CreateTab.tsx (100%) rename frontend/src/pages/marketplace/tabs/{status => Rosc}/StatusTab.tsx (100%) diff --git a/docker-compose.yml b/docker-compose.yml index a66f994..1a9c402 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,4 @@ +# please use localhost:8080 for codex_url for local development (without marketplace) services: client: build: @@ -7,6 +8,6 @@ services: ports: - "3000:80" environment: - - codex_url=http://kubernetes.docker.internal:31942 + - codex_url=http://kubernetes.docker.internal:31264 volumes: - ./deployment/nginx.template:/etc/nginx/templates/10-variables.conf.template:ro \ No newline at end of file diff --git a/frontend/src/components/AvailableComponent/AvailableComponent.tsx b/frontend/src/components/AvailableComponent/AvailableComponent.tsx new file mode 100644 index 0000000..03ccce6 --- /dev/null +++ b/frontend/src/components/AvailableComponent/AvailableComponent.tsx @@ -0,0 +1,97 @@ +import React from "react"; +import styled from "styled-components"; + +import { CircularProgress } from "@mui/material"; +import { MdCheck, MdError } from "react-icons/md"; +import AvailabilityModel from "../../data/models/AvailabilityModel"; +import constants from "../../util/Constants"; + +function AvailableComponent(props: { item: AvailabilityModel }) { + return ( + +
+

+ ID: + {props.item.id} +

+

+ Size: + {props.item.size} +

+
+
+

+ Duration: + {props.item.duration} +

+

+ Min Price: + {props.item.minPrice} +

+
+
+

+ Max Collateral: + {props.item.maxCollateral} +

+
+
+ ); +} + +export default AvailableComponent; + +const AvailableComponentWrapper = 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/components/layout/tabBarView/TabBarViewText.tsx b/frontend/src/components/layout/tabBarView/TabBarViewText.tsx new file mode 100644 index 0000000..1f69c1f --- /dev/null +++ b/frontend/src/components/layout/tabBarView/TabBarViewText.tsx @@ -0,0 +1,71 @@ +import React from "react"; +import styled from "styled-components"; + +function TabBarViewText(props: { + tabText: String[]; + children: React.ReactNode[]; +}) { + const [activeTab, setActiveTab] = React.useState(0); + + return ( + +
+ {props.tabText.map((text, index) => ( + + ))} +
+
{props.children[activeTab]}
+
+ ); +} + +export default TabBarViewText; + +const TabBarViewTextWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: start; + padding: 0; + margin: 0; + height: 100%; + + #tab-wrapper { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-evenly; + list-style: none; + padding: 0; + margin-bottom: 10px; + width: 100%; + } + + #tab-wrapper button { + flex: 1; + background-color: #141414; + color: #9e9e9e; + border: none; + padding: 24px; + cursor: pointer; + font-size: 1rem; + } + + #tab-view { + height: 100%; + width: 100%; + } + + @media (min-width: 768px) { + height: 100vh - 300px !important; + } +`; diff --git a/frontend/src/data/models/AvailabilityModel.ts b/frontend/src/data/models/AvailabilityModel.ts new file mode 100644 index 0000000..84a2b94 --- /dev/null +++ b/frontend/src/data/models/AvailabilityModel.ts @@ -0,0 +1,9 @@ +type AvailabilityModel = { + id: string; + size: string; + duration: string; + minPrice: string; + maxCollateral: string; +}; + +export default AvailabilityModel; \ No newline at end of file diff --git a/frontend/src/pages/data/tabs/upload/UploadTab.tsx b/frontend/src/pages/data/tabs/upload/UploadTab.tsx index 4be4247..44ba889 100644 --- a/frontend/src/pages/data/tabs/upload/UploadTab.tsx +++ b/frontend/src/pages/data/tabs/upload/UploadTab.tsx @@ -4,7 +4,7 @@ import styled from "styled-components"; import UploadedItemModel, { UploadedItemStatus, } from "../../../../data/models/UploadedItemModel"; -import UploadedItemComponent from "../../../../components/uploadedItem/UploadedItemComponent"; +import UploadedItemComponent from "../../../../components/UploadedItem/UploadedItemComponent"; import axios from "axios"; import { useDexyStore } from "../../../../store"; @@ -64,6 +64,7 @@ function UploadTab() { }) .then((response) => { newCid = response.data; + getDatas(); }); console.log("filesCopy failed"); } catch (error) { diff --git a/frontend/src/pages/marketplace/Marketplace.tsx b/frontend/src/pages/marketplace/Marketplace.tsx index 96e0bfc..b995c5a 100644 --- a/frontend/src/pages/marketplace/Marketplace.tsx +++ b/frontend/src/pages/marketplace/Marketplace.tsx @@ -1,22 +1,30 @@ import React from "react"; -import TabBarView from "../../components/layout/tabBarView/TabBarView"; +import TabBarViewText from "../../components/layout/tabBarView/TabBarViewText"; import styled from "styled-components"; import { MdFileUpload, MdFileDownload } from "react-icons/md"; -import UploadTab from "./tabs/status/StatusTab"; -import DownloadTab from "./tabs/create/CreateTab"; -import StatusTab from "./tabs/status/StatusTab"; -import CreateTab from "./tabs/create/CreateTab"; +import UploadTab from "./tabs/Rosc/StatusTab"; +import DownloadTab from "./tabs/Rosc/CreateTab"; +import StatusTab from "./tabs/Rosc/StatusTab"; +import CreateTab from "./tabs/Rosc/CreateTab"; +import OfferStorage from "./tabs/Availability/OfferStorage"; +import AvailabilitiesTab from "./tabs/Availability/Availability"; function MarketplacePage() { return (
- + - + + + + + + +
); } diff --git a/frontend/src/pages/marketplace/tabs/Availability/Availability.tsx b/frontend/src/pages/marketplace/tabs/Availability/Availability.tsx new file mode 100644 index 0000000..8b5902a --- /dev/null +++ b/frontend/src/pages/marketplace/tabs/Availability/Availability.tsx @@ -0,0 +1,72 @@ +import { useEffect, useRef, useState } from "react"; +import styled from "styled-components"; + +import { useDexyStore } from "../../../../store"; +import AvailableComponent from "../../../../components/AvailableComponent/AvailableComponent"; + +function AvailabilitiesTab() { + const { storageOffers, setStorageOffers, nodeInfo } = useDexyStore(); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + setIsLoading(true); + // Fetch purchase IDs + fetch(`/api/codex/v1/sales/availability`, { + headers: { + Authorization: nodeInfo.auth ? "Basic " + btoa(nodeInfo.auth) : "", + }, + }) + .then((response) => response.json()) + .then((data) => { + setStorageOffers(data); + setIsLoading(false); + }) + .catch((error) => { + console.error("Error fetching purchase IDs:", error); + setIsLoading(false); + }); + }, []); + + return ( + +
0 ? "60vh" : "0%", + }} + > + {isLoading ? ( +

Loading...

+ ) : ( + storageOffers.map((item) => ( + + )) + )} +
+
+ ); +} + +export default AvailabilitiesTab; + +const AvailabilitiesTabWrapper = 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/Availability/OfferStorage.tsx b/frontend/src/pages/marketplace/tabs/Availability/OfferStorage.tsx new file mode 100644 index 0000000..b5032c3 --- /dev/null +++ b/frontend/src/pages/marketplace/tabs/Availability/OfferStorage.tsx @@ -0,0 +1,142 @@ +import { useState } from "react"; +import constants from "../../../../util/Constants"; +import styled from "styled-components"; +import { useDexyStore } from "../../../../store"; + +function OfferStorage() { + const { ftdCid, setFtdCid, nodeInfo } = useDexyStore(); + + const [size, setSize,] = useState("file"); + const [duration, setDuration,] = useState("file"); + const [minPrice, setMinPrice,] = useState("file"); + const [maxCollateral, setMaxCollateral,] = useState("file"); + + + function upload(cid: string) { + fetch( + `/api/codex/v1/sales/availability`, + { + method: 'POST', + headers: + (nodeInfo.auth !== null && { + Authorization: + (nodeInfo.auth && "Basic " + btoa(nodeInfo.auth)) || "", + }) || + {}, + body: JSON.stringify({ + size: size, + duration: duration, + minPrice: minPrice, + maxCollateral: maxCollateral + }) + } + ) + // 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 ( + + { + setFtdCid(e.target.value); + }} + value={ftdCid} + /> +
+ setSize(e.target.value)} + /> +
+ setDuration(e.target.value)} + /> +
+ setMinPrice(e.target.value)} + /> +
+ setMaxCollateral(e.target.value)} + /> + +
+ ); +} + +export default OfferStorage; + +const OfferStorageWrapper = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + width: 75%; + + input { + flex: 3; + height: 60px; + padding: 10px 20px; + border: none; + background-color: ${constants.surfaceColor}; + color: ${constants.onSurfaceColor}; + width: 100%; + } + + input:focus { + outline: none; + border: 2px solid ${constants.primaryColor}; + } + + input:nth-child(1) { + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; + } + + #divider { + width: 2.5px; + height: 60px; + background-color: #555555; + } + + button { + flex: 2; + height: 60px; + border: none; + background-color: ${constants.primaryColor}; + color: ${constants.onPrimaryColor}; + font-size: 1rem; + cursor: pointer; + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; + width: 100%; + } + + @media (max-width: 1180px) { + width: 80%; + } + + @media (max-width: 768px) { + width: 85%; + } + + @media (max-width: 450px) { + width: 90%; + } +`; diff --git a/frontend/src/pages/marketplace/tabs/create/CreateTab.tsx b/frontend/src/pages/marketplace/tabs/Rosc/CreateTab.tsx similarity index 100% rename from frontend/src/pages/marketplace/tabs/create/CreateTab.tsx rename to frontend/src/pages/marketplace/tabs/Rosc/CreateTab.tsx diff --git a/frontend/src/pages/marketplace/tabs/status/StatusTab.tsx b/frontend/src/pages/marketplace/tabs/Rosc/StatusTab.tsx similarity index 100% rename from frontend/src/pages/marketplace/tabs/status/StatusTab.tsx rename to frontend/src/pages/marketplace/tabs/Rosc/StatusTab.tsx diff --git a/frontend/src/store.ts b/frontend/src/store.ts index b01be82..a63a00e 100644 --- a/frontend/src/store.ts +++ b/frontend/src/store.ts @@ -2,6 +2,7 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; import UploadedItemModel from "./data/models/UploadedItemModel"; import RequestForStorageContractModel from "./data/models/RequestForStorageContractModel"; +import AvailabilityModel from "./data/models/AvailabilityModel"; interface NodeInfo { baseUrl: string; nodeToConnectTo: string | null; @@ -16,6 +17,8 @@ interface DexyState { setUploads: (uploads: UploadedItemModel[]) => void; storageRequests: RequestForStorageContractModel[]; setStorageRequests: (storageRequests: RequestForStorageContractModel[]) => void; + storageOffers: AvailabilityModel[]; + setStorageOffers: (storageOffers: AvailabilityModel[]) => void; ftdCid: string; setFtdCid: (cid: string) => void; nodeInfo: NodeInfo; @@ -29,6 +32,8 @@ export const useDexyStore = create()( setUploads: (uploads) => set({ uploads }), storageRequests: [], setStorageRequests: (storageRequests) => set({ storageRequests }), + storageOffers: [], + setStorageOffers: (storageOffers) => set({ storageOffers }), ftdCid: "", setFtdCid: (cid) => set({ ftdCid: cid }), nodeInfo: {