mirror of
https://github.com/logos-storage/logos-storage-frontend.git
synced 2026-01-02 05:13:13 +00:00
ADD availabilities pages
This commit is contained in:
parent
a1af932c82
commit
07cf92153f
@ -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
|
||||
@ -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 (
|
||||
<AvailableComponentWrapper>
|
||||
<div>
|
||||
<p>
|
||||
<span>ID: </span>
|
||||
{props.item.id}
|
||||
</p>
|
||||
<p>
|
||||
<span>Size: </span>
|
||||
{props.item.size}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<span>Duration: </span>
|
||||
{props.item.duration}
|
||||
</p>
|
||||
<p>
|
||||
<span>Min Price: </span>
|
||||
{props.item.minPrice}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<span>Max Collateral: </span>
|
||||
{props.item.maxCollateral}
|
||||
</p>
|
||||
</div>
|
||||
</AvailableComponentWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
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%;
|
||||
}
|
||||
`;
|
||||
71
frontend/src/components/layout/tabBarView/TabBarViewText.tsx
Normal file
71
frontend/src/components/layout/tabBarView/TabBarViewText.tsx
Normal file
@ -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 (
|
||||
<TabBarViewTextWrapper>
|
||||
<div id="tab-wrapper">
|
||||
{props.tabText.map((text, index) => (
|
||||
<button
|
||||
style={{
|
||||
color: activeTab === index ? "#6f11db" : "#9e9e9e",
|
||||
borderBottom: activeTab === index ? "2px solid #6f11db" : "none",
|
||||
}}
|
||||
onClick={() => setActiveTab(index)}
|
||||
key={index}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div id="tab-view">{props.children[activeTab]}</div>
|
||||
</TabBarViewTextWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
`;
|
||||
9
frontend/src/data/models/AvailabilityModel.ts
Normal file
9
frontend/src/data/models/AvailabilityModel.ts
Normal file
@ -0,0 +1,9 @@
|
||||
type AvailabilityModel = {
|
||||
id: string;
|
||||
size: string;
|
||||
duration: string;
|
||||
minPrice: string;
|
||||
maxCollateral: string;
|
||||
};
|
||||
|
||||
export default AvailabilityModel;
|
||||
@ -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) {
|
||||
|
||||
@ -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 (
|
||||
<div>
|
||||
<TabBarView tabIcons={[MdFileUpload, MdFileDownload, MdFileUpload, MdFileDownload]}>
|
||||
<TabBarViewText tabText={["ROSC Status", "ROSC create", "Availabilities", "Create Availability"]}>
|
||||
<StatusTab />
|
||||
<TabBarViewPage>
|
||||
<CreateTab />
|
||||
</TabBarViewPage>
|
||||
</TabBarView>
|
||||
<TabBarViewPage>
|
||||
<AvailabilitiesTab />
|
||||
</TabBarViewPage>
|
||||
<TabBarViewPage>
|
||||
<OfferStorage />
|
||||
</TabBarViewPage>
|
||||
</TabBarViewText>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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 (
|
||||
<AvailabilitiesTabWrapper>
|
||||
<div
|
||||
id="request-wrap"
|
||||
style={{
|
||||
maxHeight: storageOffers.length > 0 ? "60vh" : "0%",
|
||||
}}
|
||||
>
|
||||
{isLoading ? (
|
||||
<p>Loading...</p>
|
||||
) : (
|
||||
storageOffers.map((item) => (
|
||||
<AvailableComponent
|
||||
item={item}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</AvailabilitiesTabWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
`;
|
||||
@ -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 (
|
||||
<OfferStorageWrapper>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="CID"
|
||||
onChange={(e) => {
|
||||
setFtdCid(e.target.value);
|
||||
}}
|
||||
value={ftdCid}
|
||||
/>
|
||||
<div id="divider"></div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Size"
|
||||
onChange={(e) => setSize(e.target.value)}
|
||||
/>
|
||||
<div id="divider"></div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Duration"
|
||||
onChange={(e) => setDuration(e.target.value)}
|
||||
/>
|
||||
<div id="divider"></div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="MinPrice"
|
||||
onChange={(e) => setMinPrice(e.target.value)}
|
||||
/>
|
||||
<div id="divider"></div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="MaxCollateral"
|
||||
onChange={(e) => setMaxCollateral(e.target.value)}
|
||||
/>
|
||||
<button onClick={() => upload(ftdCid)}>Download</button>
|
||||
</OfferStorageWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
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%;
|
||||
}
|
||||
`;
|
||||
@ -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<DexyState>()(
|
||||
setUploads: (uploads) => set({ uploads }),
|
||||
storageRequests: [],
|
||||
setStorageRequests: (storageRequests) => set({ storageRequests }),
|
||||
storageOffers: [],
|
||||
setStorageOffers: (storageOffers) => set({ storageOffers }),
|
||||
ftdCid: "",
|
||||
setFtdCid: (cid) => set({ ftdCid: cid }),
|
||||
nodeInfo: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user