From 1a3c101eba3a06346c1da6a2892086594bc2e873 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 28 Jun 2024 13:24:36 -0400 Subject: [PATCH 01/15] refactor: refactored to useState --- frontend/replacement.dev.json | 2 +- frontend/widgets/src/QueryApi.Dashboard.jsx | 84 ++++++++------------- 2 files changed, 33 insertions(+), 53 deletions(-) diff --git a/frontend/replacement.dev.json b/frontend/replacement.dev.json index 1a233cb9d..1b56e05bc 100644 --- a/frontend/replacement.dev.json +++ b/frontend/replacement.dev.json @@ -1,6 +1,6 @@ { "REPL_ACCOUNT_ID": "dev-queryapi.dataplatform.near", "REPL_GRAPHQL_ENDPOINT": "https://near-queryapi.dev.api.pagoda.co", - "REPL_EXTERNAL_APP_URL": "https://queryapi-frontend-vcqilefdcq-ew.a.run.app", + "REPL_EXTERNAL_APP_URL": "http://localhost:3000", "REPL_REGISTRY_CONTRACT_ID": "dev-queryapi.dataplatform.near" } diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index 0686fa078..ebe3eb3c0 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -1,21 +1,13 @@ const accountId = context.accountId; -const [selected_accountId, selected_indexerName] = props.selectedIndexerPath - ? props.selectedIndexerPath.split("/") - : [undefined, undefined]; - -const activeTab = props.view == "create-new-indexer" ? "create-new-indexer" : props.selectedIndexerPath ? "indexer" : "explore" -const activeIndexerView = props.activeIndexerView ?? "editor"; +const [selected_accountId, selected_indexerName] = props.selectedIndexerPath ? props.selectedIndexerPath.split("/") : [undefined, undefined]; const limit = 7; -let totalIndexers = 0; - -State.init({ - activeTab: activeTab, - activeIndexerView: activeIndexerView, - my_indexers: [], - all_indexers: [], - selected_indexer: undefined, - selected_account: undefined, -}); + +const [activeTab, setActiveTab] = useState(props.view === "create-new-indexer" ? "create-new-indexer" : props.selectedIndexerPath ? "indexer" : "explore"); +const [activeIndexerTabView, setActiveIndexerTabView] = useState(props.activeIndexerView ?? "editor"); +const [myIndexers, setMyIndexers] = useState([]); +const [allIndexers, setAllIndexers] = useState([]); +const [selectedIndexerName, setSelectedIndexerName] = useState(''); +const [totalIndexers, setTotalIndexers] = useState(0); Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { const indexers = []; @@ -34,11 +26,10 @@ Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { (indexer) => indexer.accountId === accountId ); - State.update({ - my_indexers: my_indexers, - all_indexers: indexers, - totalIndexers: total_indexers, - }); + setMyIndexers(my_indexers); + setAllIndexers(indexers) + setTotalIndexers(total_indexers); + }); const Subheading = styled.h2` @@ -303,26 +294,15 @@ const SignUpLink = styled.a` text-decoration: none; } `; -// TODO fix activeTab -// const previousSelectedTab = Storage.privateGet("queryapi:activeTab"); -// if (previousSelectedTab && previousSelectedTab !== state.activeTab) { -// State.update({ -// activeTab: previousSelectedTab, -// }); -// } const selectTab = (tabName) => { Storage.privateSet("queryapi:activeTab", tabName); - State.update({ - activeTab: tabName, - }); + setActiveTab(tabName); }; const selectIndexerPage = (viewName) => { Storage.privateSet("queryapi:activeIndexerTabView", viewName); - State.update({ - activeIndexerView: viewName, - }); + setActiveIndexerTabView(viewName); }; const indexerView = (accountId, indexerName) => { const editUrl = `https://dev.near.org/${REPL_ACCOUNT_ID}/widget/QueryApi.App?selectedIndexerPath=${accountId}/${indexerName}`; @@ -373,20 +353,20 @@ const indexerView = (accountId, indexerName) => { }; return ( - + selectTab("explore")} - selected={state.activeTab === "explore"} + selected={activeTab === "explore"} > Explore Indexers - {state.activeTab == "create-new-indexer" && ( + {activeTab == "create-new-indexer" && ( selectTab("create-new-indexer")} - selected={state.activeTab === "create-new-indexer"} + selected={activeTab === "create-new-indexer"} > Create New Indexer @@ -397,7 +377,7 @@ return ( selectTab("indexer")} - selected={state.activeTab === "indexer"} + selected={activeTab === "indexer"} > Indexer ({props.selectedIndexerPath}) @@ -405,7 +385,7 @@ return ( )}
-
+
{ + setSelectedIndexerName(""); State.update({ activeTab: "create-new-indexer", - selected_indexer: "", }); selectTab("create-new-indexer"); }} > Create New Indexer - {state.my_indexers.length > 0 && ( + {myIndexers.length > 0 && (

{accountId}'s Indexers - ({state.my_indexers.length}) + ({myIndexers.length})

)} - {state.activeTab === "create-new-indexer" && ( + {activeTab === "create-new-indexer" && (
)}
-
+
{state.indexers.length > 0 && - (state.selected_indexer != undefined ? ( -

{state.selected_indexer}

+ (selectedIndexerName ? ( +

{selectedIndexerName}

) : (

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

))} @@ -488,15 +468,15 @@ return ( accountId: selected_accountId ?? state.indexers[0].accountId, path: "query-api-editor", tab: props.tab, - activeView: state.activeIndexerView + activeView: activeIndexerView }} />
- {state.activeTab === "create-new-indexer" && ( + {activeTab === "create-new-indexer" && (
{state.indexers.length > 0 && - (state.selected_indexer != undefined ? ( -

{state.selected_indexer}

+ (selectedIndexerName ? ( +

{selectedIndexerName}

) : (

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

))} From 69bdfd7ae88270cac0832803f2e3d6c20af0606d Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 28 Jun 2024 18:09:53 -0400 Subject: [PATCH 02/15] feat:prototype --- frontend/widgets/src/QueryApi.Dashboard.jsx | 318 ++++++++++- .../src/QueryApi.DashboardTEMPFILE.jsx | 497 ++++++++++++++++++ 2 files changed, 813 insertions(+), 2 deletions(-) create mode 100644 frontend/widgets/src/QueryApi.DashboardTEMPFILE.jsx diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index ebe3eb3c0..1a8348d67 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -32,6 +32,271 @@ Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { }); + +// TOP HALF +const Hero = styled.div` + display: flex; + justify-content: center; + align-items: center; + height: 349px; /* Static height */ + width: 100%; /* Full width */ + background: linear-gradient( + 268.88deg, + rgba(2, 255, 133, 0.2) 1.75%, + rgba(2, 133, 255, 0.08) 54.6%, + rgba(2, 27, 255, 0.08) 84.31% + ); + // , url('https://s3-alpha-sig.figma.com/img/f856/12b1/14c8f8fd2894d48314a47b98531b3002?Expires=1720396800&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=iC8KBVqIyZDHU2~xisqW3kuwC8nLk5POGZqHyVGNcAWcLwep3jEocxIrZI9hR5VUfiXwetmD6pXTdHxScqfIMjwvIsccAhEAkzD9t5xasMfuC5vHKel9t96-CGMeMikD3No92ObNZ-eGFdo2QAnrNVNxufsdwhYUKRbXuZSquC2A2qx9kzYxv7pyUjR3QGxg8UkMqhmZiKogoiLL~727aERO3PUIiSlMMH~kRFKVyK4UnJFERuroJ9L3EZTfgBG90EUM5MYTVqLIeeA1gWeYPkfTlYghAWwOx60B2wdLk5WTgmqytRZxbqsCiN8u92ZKZjmBzFcZZcWF9eONAqdDvA__'); + // background-size: 100%; + // background-position: right; + // background-repeat: no-repeat; +`; + +const Headline = styled.h1` + font-family: 'FK Grotesk Variable', sans-serif; + font-weight: 700; + width: 369px; + font-size: 24px; + line-height: 31.2px; +`; + +const Subheadline = styled.p` + font-family: 'Mona Sans', sans-serif; + font-weight: 400; + font-size: 14px; + line-height: 18.2px; + letter-spacing: 1.5%; +`; + +const Container = styled.div` + display: flex; + justify-content: center; + align-items: center; +`; + +const HeadlineContainer = styled.div` + width: 364px; + height: 168px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-right: 80px; /* Gap between HeadlineContainer and WidgetContainer */ +`; + +const WidgetContainer = styled.div` + width: 301px; + height: 365px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + box-shadow: 0 8.2px 19.92px 0 rgba(0, 0, 0, 0.1), 0 2.34px 2.34px 0 rgba(0, 0, 0, 0.15); + margin-top: 158px; /* Gap between WidgetContainer and HeadlineContainer */ + background: #fff; + border-radius: 10px; +`; + +const SubContainer = styled.div` + width: 262.5px; + height: 330px; //270px later +`; + +const SubContainerTitle = styled.h2` + font-family: 'Product Sans', sans-serif; + font-weight: 700; + font-size: 14px; + line-height: 14.06px; + color: #333; + margin: 0; +`; + +const InputWrapper = styled.div` + display: flex; + align-items: center; + width: 364px; + height: 40px; + border: 1px solid #ccc; + border-radius: 6px; + padding: 0; + overflow: hidden; +`; + +const StyledInput = styled.input` + flex: 1; + height: 100%; + border: none; + outline: none; + padding: 8px 12px; + border-radius: 6px 0 0 6px; +`; + +const GreenButton = styled.button` + width: 84px; + background-color: #37CD83; + border: none; + border-radius: 0px 6px 6px 0px; + color: white; + cursor: pointer; + padding: 8px 16px; + display: flex; + align-items: center; + justify-content: center; +`; + +//BOTTOM HALF +const Divider = styled.div` + height: 40px; + width: 100%; +` +const ExploreIndexersContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + height: 100%; /* Static height */ + width: 100%; /* Full width */ +`; + + +const ExploreIndexersHeading = styled.h2` + font-family: 'FK Grotesk Variable', sans-serif; + font-size: 20px; + font-weight: 400; + line-height: 26px; + letter-spacing: 0.015em; + text-align: left; +`; + +const ExploreContent = styled.div` + width: 745px; + display: flex; + flex-direction: column; + gap: 24px; +` + +const SearchIndexerContainer = styled.div` + display: flex; + align-items: center; + width: 269px; + height: 40px; + padding: 8px 12px; + gap: 8px; + border-radius: 50px; + border: 1px solid #E3E3E0; + background-color: white; +`; + +const SearchInput = styled.input` + flex: 1; + border: none; + outline: none; + font-family: 'Mona Sans', sans-serif; + font-weight: 450; + font-size: 14px; + line-height: 21px; + letter-spacing: 2%; + &::placeholder { + color: #a9a9a9; /* Example placeholder color */ + } +`; + +const SearchIndexerButton = styled.button` + background-color: transparent; + border: none; + color: black; + padding: 8px 16px; + cursor: pointer; + font-family: 'Mona Sans', sans-serif; + font-weight: 450; + font-size: 14px; + line-height: 21px; + letter-spacing: 2%; +`; + +const MagnifyingGlass = styled.span` + font-size: 14px; /* Adjust as necessary */ + margin-right: 8px; /* Adjust as necessary */ +`; + +// TABLE + +const TableContainer = styled.div` + width: 745px; + margin: 0 auto; +`; + +const Table = styled.table` + width: 100%; + border-collapse: collapse; + border-radius: 8px; /* Adjust the border radius as needed */ + overflow: hidden; /* Ensures border radius is applied to the table */ +`; + +const TableHeader = styled.thead` + background-color: #F0F0F1; +`; + +const TableHeaderCell = styled.th` + font-family: 'Mona Sans', sans-serif; + font-weight: 450; + font-size: 10px; + line-height: 14px; + letter-spacing: 2%; + text-align: left; + padding: 8px; +`; + +const TableRow = styled.tr` + &:nth-child(even) { + background-color: #f9f9f9; + } +`; + +const TableCell = styled.td` + font-family: 'Mona Sans', sans-serif; + font-weight: 400; + font-size: 14px; + line-height: 21px; + letter-spacing: 2%; + padding: 8px; + text-align: left; +`; + +const data = allIndexers.map((indexer) => ({ + indexer: indexer.indexerName, + weeklyRequest: indexer.weeklyRequest || 150, + lastUpdated: indexer.lastUpdated || '2023-06-25', + status: indexer.status || 'Active', +})); + +function CustomTable() { + return ( + + + + + Indexer + Weekly Request + Last Updated + Status + + + + {data.map((row, index) => ( + + {row.indexer} + {row.weeklyRequest} + {row.lastUpdated} + {row.status} + + ))} + +
+
+ ); +} + +// BELOW IS ORIGINAL STYLED COMPONENTS const Subheading = styled.h2` display: block; margin: 0; @@ -304,6 +569,7 @@ const selectIndexerPage = (viewName) => { Storage.privateSet("queryapi:activeIndexerTabView", viewName); setActiveIndexerTabView(viewName); }; + const indexerView = (accountId, indexerName) => { const editUrl = `https://dev.near.org/${REPL_ACCOUNT_ID}/widget/QueryApi.App?selectedIndexerPath=${accountId}/${indexerName}`; const statusUrl = `https://dev.near.org/${REPL_ACCOUNT_ID}/widget/QueryApi.App?selectedIndexerPath=${accountId}/${indexerName}&view=indexer&activeIndexerView=status`; @@ -352,6 +618,7 @@ const indexerView = (accountId, indexerName) => { ); }; + return ( @@ -384,9 +651,43 @@ return ( )} + +
- + + + Launch an indexer in minutes + Get a working indexer exportable to your Near react application faster than ever. Extract on-chain data, and easily query it using GraphQL endpoints and subscriptions. + + + Start + + + + + Customize indexer + + + + + + + + Explore indexers on Near + + 🔍 + + {"➡️"} + + {CustomTable()} + + + + + {/* selectTab("explore")} @@ -431,8 +732,21 @@ return ( -
+ */}
+ + + + + + + + + + + + +
{ + const indexers = []; + const total_indexers = 0; + Object.keys(data).forEach((accountId) => { + Object.keys(data[accountId]).forEach((functionName) => { + indexers.push({ + accountId: accountId, + indexerName: functionName, + }); + total_indexers += 1; + }); + }); + + let my_indexers = indexers.filter( + (indexer) => indexer.accountId === accountId + ); + + setMyIndexers(my_indexers); + setAllIndexers(indexers) + setTotalIndexers(total_indexers); + +}); + +const Subheading = styled.h2` + display: block; + margin: 0; + font-size: 14px; + line-height: 20px; + color: ${(p) => (p.bold ? "#11181C !important" : "#687076 !important")}; + font-weight: ${(p) => (p.bold ? "600" : "400")}; + font-size: ${(p) => (p.small ? "12px" : "14px")}; + overflow: ${(p) => (p.ellipsis ? "hidden" : "visible")}; + text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "unset")}; + white-space: nowrap; + outline: none; +`; + +const Editor = styled.div` +`; +const Status = styled.div` +`; + +const Wrapper = styled.div` + margin-inline: 12px; + margin-top: calc(var(--body-top-padding) * -1); +`; + +const NavBarLogo = styled.a` + padding-top: 0.3125rem; + padding-bottom: 0.3125rem; + margin-right: .01rem; + font-size: 1.25rem; + text-decoration: none; + white-space: nowrap; +`; +const Main = styled.div` + display: block; +`; + +const Section = styled.div` + padding-top: 0px; + border-left: none; + border-right: none; + display: ${(p) => (p.active ? "block" : "none")}; + margin: ${(p) => (p.negativeMargin ? "0 -12px" : "0")}; +`; + +const Tabs = styled.div` + display: none; + height: 48px; + background: #f8f9fa; + border-top: 1px solid #eceef0; + border-bottom: 1px solid #eceef0; + margin-bottom: ${(p) => (p.noMargin ? "0" : p.halfMargin ? "24px" : "24px")}; + + display: flex; + margin-left: -12px; + margin-right: -12px; + + button { + flex: 1; + } +`; +const Content = styled.div` + background-color: #f7f7f7; + padding: 2em; + border-radius: 5px; +`; + +const Title = styled.h1` + font-size: 1.5em; + text-align: center; + color: palevioletred; +`; + +const TabsButton = styled.button` + font-weight: 600; + font-size: 14px; + line-height: 16px; + padding: 0 12px; + position: relative; + color: ${(p) => (p.selected ? "#11181C" : "#687076")}; + background: none; + border: none; + outline: none; + &:hover { + color: #11181c; + } + + &::after { + content: ""; + display: ${(p) => (p.selected ? "block" : "none")}; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 3px; + background: #0091ff; + } +`; +const H2 = styled.h2` + font-size: 19px; + line-height: 22px; + color: #11181c; + margin: 0 0 8px; +`; +const Card = styled.div` + border-radius: 12px; + background: #fff; + border: ${(div) => (div.selected ? "1px solid black" : "1px solid #eceef0")}; + box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), + 0px 1px 2px rgba(16, 24, 40, 0.06); +`; + +const CardBody = styled.div` + padding: 16px; + display: flex; + gap: 16px; + align-items: center; + + > * { + min-width: 0; + } +`; + +const CardFooter = styled.div` + display: flex; + justify-content: space-around; + flex-wrap: wrap; + gap: 4px; + padding: 16px; + border-top: 1px solid #eceef0; +`; + +const TextLink = styled.a` + display: block; + margin: 0; + font-size: 14px; + line-height: 20px; + color: ${(p) => (p.bold ? "#11181C !important" : "#687076 !important")}; + font-weight: ${(p) => (p.bold ? "600" : "400")}; + font-size: ${(p) => (p.small ? "12px" : "14px")}; + overflow: ${(p) => (p.ellipsis ? "hidden" : "visible")}; + text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "unset")}; + white-space: nowrap; + outline: none; + + &:focus, + &:hover { + text-decoration: underline; + } +`; + +const Thumbnail = styled.a` + display: block; + width: 48px; + height: 48px; + flex-shrink: 0; + border: 1px solid #eceef0; + border-radius: 8px; + overflow: hidden; + outline: none; + transition: border-color 200ms; + + &:focus, + &:hover { + border-color: #d0d5dd; + } + + img { + object-fit: cover; + width: 100%; + height: 100%; + } +`; + +const CardWrapper = styled.div` + margin: 0 0 16px; +`; + +const sharedButtonStyles = ` + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 16px; + margin-bottom: 12px; + height: 32px; + border-radius: 6px; + font-weight: 600; + font-size: 12px; + line-height: 15px; + text-align: center; + cursor: pointer; + + &:hover, + &:focus { + text-decoration: none; + outline: none; + } + + i { + color: #7E868C; + } + + .bi-16 { + font-size: 16px; + } +`; + +const Button = styled.button` + ${sharedButtonStyles} + color: ${(p) => (p.primary ? "#fff" : "#11181C")} !important; + background: ${(p) => (p.primary ? "#0091FF" : "#FBFCFD")}; + border: ${(p) => (p.primary ? "none" : "1px solid #D7DBDF")}; + + &:hover, + &:focus { + background: ${(p) => (p.primary ? "#0484e5" : "#ECEDEE")}; + } +`; + +const ButtonLink = styled.a` + ${sharedButtonStyles} + color: ${(p) => { + if (p.primary) return "#fff"; + else if (p.danger) return "#fff"; + else return "#11181C"; + }} !important; + background: ${(p) => { + if (p.primary) return "#0091FF"; + else if (p.danger) return "#dc3545"; + else return "#FBFCFD"; + }}; + border: ${(p) => (p.primary ? "none" : "1px solid #D7DBDF")}; + + &:hover, + &:focus { + background: ${(p) => { + if (p.primary) return "#0484e5"; + else if (p.danger) return "#b22b38"; + else return "#ECEDEE"; + }} +`; + +const SignUpLink = styled.a` + --blue: RGBA(13, 110, 253, 1); + display: ${({ hidden }) => (hidden ? "none" : "inline-block")}; + font-size: 14px; + cursor: pointer; + color: var(--blue); + text-decoration: none; + margin-left: 0.1em; + padding: 0; + white-space: nowrap; + + &:hover { + color: var(--blue); + text-decoration: none; + } + + &:visited { + color: var(--blue); + text-decoration: none; + } +`; + +const selectTab = (tabName) => { + Storage.privateSet("queryapi:activeTab", tabName); + setActiveTab(tabName); +}; + +const selectIndexerPage = (viewName) => { + Storage.privateSet("queryapi:activeIndexerTabView", viewName); + setActiveIndexerTabView(viewName); +}; +const indexerView = (accountId, indexerName) => { + const editUrl = `https://dev.near.org/${REPL_ACCOUNT_ID}/widget/QueryApi.App?selectedIndexerPath=${accountId}/${indexerName}`; + const statusUrl = `https://dev.near.org/${REPL_ACCOUNT_ID}/widget/QueryApi.App?selectedIndexerPath=${accountId}/${indexerName}&view=indexer&activeIndexerView=status`; + const playgroundLink = `https://cloud.hasura.io/public/graphiql?endpoint=${REPL_GRAPHQL_ENDPOINT}/v1/graphql&header=x-hasura-role%3A${accountId.replaceAll( + ".", + "_" + )}`; + + return ( + + + + + + +
+ + {indexerName} + + + @{accountId} + +
+
+ + + selectIndexerPage("status")}> + View Status + + selectIndexerPage("editor")}> + {accountId === context.accountId ? "Edit Indexer" : "View Indexer"} + + + View In Playground + + +
+ ); +}; + +return ( + + + selectTab("explore")} + selected={activeTab === "explore"} + > + Explore Indexers + + {activeTab == "create-new-indexer" && ( + selectTab("create-new-indexer")} + selected={activeTab === "create-new-indexer"} + > + Create New Indexer + + )} + + {props.selectedIndexerPath && ( + <> + selectTab("indexer")} + selected={activeTab === "indexer"} + > + Indexer ({props.selectedIndexerPath}) + + + )} + +
+
+ selectTab("explore")} + > + + QueryApi + + + + (Documentation) + +
+ { + setSelectedIndexerName(""); + State.update({ + activeTab: "create-new-indexer", + }); + selectTab("create-new-indexer"); + }} + > + Create New Indexer + + {myIndexers.length > 0 && ( +

+ {accountId}'s Indexers + ({myIndexers.length}) +

+ )} + +
+
+
+ {activeTab === "create-new-indexer" && ( +
+ +
+ )} +
+
+ + {state.indexers.length > 0 && + (selectedIndexerName ? ( +

{selectedIndexerName}

+ ) : ( +

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

+ ))} + +
+ {activeTab === "create-new-indexer" && ( +
+ {state.indexers.length > 0 && + (selectedIndexerName ? ( +

{selectedIndexerName}

+ ) : ( +

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

+ ))} + +
+ )} +
+
+
+); From f1821f71fa3f95e715136f2ec3b458af39619f03 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 12 Jul 2024 13:47:14 -0400 Subject: [PATCH 03/15] feat: added checkboxes --- frontend/widgets/src/QueryApi.Dashboard.jsx | 135 +++++++++++++++++++- 1 file changed, 128 insertions(+), 7 deletions(-) diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index 1a8348d67..d44da2ece 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -6,19 +6,114 @@ const [activeTab, setActiveTab] = useState(props.view === "create-new-indexer" ? const [activeIndexerTabView, setActiveIndexerTabView] = useState(props.activeIndexerView ?? "editor"); const [myIndexers, setMyIndexers] = useState([]); const [allIndexers, setAllIndexers] = useState([]); -const [selectedIndexerName, setSelectedIndexerName] = useState(''); -const [totalIndexers, setTotalIndexers] = useState(0); +const [selectedIndexerName, setSelectedIndexerName] = useState('') +const [checkedItems, setCheckedItems] = useState({}); + +const CheckboxContainer = styled.div` + margin-bottom: 16px; +`; + +const CheckboxLabel = styled.label` + display: block; + margin-bottom: 8px; +`; + +const SubCheckboxContainer = styled.div` + margin-left: 24px; + margin-top: 8px; +`; + +const Checkbox = styled.input` + margin-right: 8px; +`; + +const handleParentChange = (methodName) => { + setCheckedItems((prevState) => { + const newState = { ...prevState }; + const parentChecked = !prevState[methodName]; + + newState[methodName] = parentChecked; + + // Check/uncheck immediate sub-checkboxes + if (parentChecked) { + checkboxData.forEach((item) => { + if (item.method_name === methodName && item.schema.properties) { + Object.keys(item.schema.properties).forEach((property) => { + newState[`${methodName}_${property}`] = true; + }); + } + }); + } else { + // Uncheck all sub-checkboxes if parent is unchecked + checkboxData.forEach((item) => { + if (item.method_name === methodName && item.schema.properties) { + Object.keys(item.schema.properties).forEach((property) => { + newState[`${methodName}_${property}`] = false; + }); + } + }); + } + + return newState; + }); +}; + +const handleChildChange = (parent, child) => { + setCheckedItems((prevState) => ({ + ...prevState, + [`${parent}_${child}`]: !prevState[`${parent}_${child}`], + })); +}; + + +const checkBoxData = [ + { + method_name: 'harvest_meta', + schema: { + type: 'object', + }, + }, + { + method_name: 'ADD_KEY', + schema: { + type: 'object', + }, + }, + { + method_name: 'add_authorized_farm_token', + schema: { + type: 'object', + properties: { + token_id: { + type: 'string', + }, + }, + required: ['token_id'], + }, + }, + { + method_name: 'add_authorized_user', + schema: { + type: 'object', + properties: { + accoint_id: { + type: 'string', + }, + }, + required: ['accoint_id'], + }, + }, +]; + Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { const indexers = []; - const total_indexers = 0; Object.keys(data).forEach((accountId) => { Object.keys(data[accountId]).forEach((functionName) => { indexers.push({ accountId: accountId, indexerName: functionName, }); - total_indexers += 1; }); }); @@ -28,11 +123,8 @@ Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { setMyIndexers(my_indexers); setAllIndexers(indexers) - setTotalIndexers(total_indexers); - }); - // TOP HALF const Hero = styled.div` display: flex; @@ -111,6 +203,9 @@ const SubContainerTitle = styled.h2` margin: 0; `; +const SubContainerContent = styled.div` ` + + const InputWrapper = styled.div` display: flex; align-items: center; @@ -669,6 +764,32 @@ return ( Customize indexer + +
+ {checkBoxData.map((item, index) => ( + + + handleParentChange(item.method_name)} + type="checkbox" id={item.method_name} /> + {item.method_name} + + {item.schema.properties && ( + + {Object.keys(item.schema.properties).map((property, subIndex) => ( + + handleChildChange(item.method_name, property)} + /> + {property}: {item.schema.properties[property].type} + + ))} + + )} + + ))} +
+
From 04f690c69c0d5c3385dc11d25dbe26ce7c7c2a68 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 12 Jul 2024 15:13:36 -0400 Subject: [PATCH 04/15] feat:refactor and removed dead code --- frontend/widgets/src/QueryApi.Dashboard.jsx | 525 +++----------------- 1 file changed, 76 insertions(+), 449 deletions(-) diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index d44da2ece..d6cd0f3b0 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -1,70 +1,10 @@ const accountId = context.accountId; -const [selected_accountId, selected_indexerName] = props.selectedIndexerPath ? props.selectedIndexerPath.split("/") : [undefined, undefined]; -const limit = 7; const [activeTab, setActiveTab] = useState(props.view === "create-new-indexer" ? "create-new-indexer" : props.selectedIndexerPath ? "indexer" : "explore"); const [activeIndexerTabView, setActiveIndexerTabView] = useState(props.activeIndexerView ?? "editor"); -const [myIndexers, setMyIndexers] = useState([]); -const [allIndexers, setAllIndexers] = useState([]); -const [selectedIndexerName, setSelectedIndexerName] = useState('') -const [checkedItems, setCheckedItems] = useState({}); - -const CheckboxContainer = styled.div` - margin-bottom: 16px; -`; - -const CheckboxLabel = styled.label` - display: block; - margin-bottom: 8px; -`; - -const SubCheckboxContainer = styled.div` - margin-left: 24px; - margin-top: 8px; -`; - -const Checkbox = styled.input` - margin-right: 8px; -`; - -const handleParentChange = (methodName) => { - setCheckedItems((prevState) => { - const newState = { ...prevState }; - const parentChecked = !prevState[methodName]; - - newState[methodName] = parentChecked; - - // Check/uncheck immediate sub-checkboxes - if (parentChecked) { - checkboxData.forEach((item) => { - if (item.method_name === methodName && item.schema.properties) { - Object.keys(item.schema.properties).forEach((property) => { - newState[`${methodName}_${property}`] = true; - }); - } - }); - } else { - // Uncheck all sub-checkboxes if parent is unchecked - checkboxData.forEach((item) => { - if (item.method_name === methodName && item.schema.properties) { - Object.keys(item.schema.properties).forEach((property) => { - newState[`${methodName}_${property}`] = false; - }); - } - }); - } - - return newState; - }); -}; - -const handleChildChange = (parent, child) => { - setCheckedItems((prevState) => ({ - ...prevState, - [`${parent}_${child}`]: !prevState[`${parent}_${child}`], - })); -}; +const [allIndexers, setAllIndexers] = useState([]); +const [checkboxState, setCheckboxState] = useState(initialCheckboxState); const checkBoxData = [ { @@ -105,25 +45,74 @@ const checkBoxData = [ }, ]; - -Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { - const indexers = []; - Object.keys(data).forEach((accountId) => { - Object.keys(data[accountId]).forEach((functionName) => { - indexers.push({ - accountId: accountId, - indexerName: functionName, +useEffect(() => { + Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { + const indexers = []; + Object.keys(data).forEach((accountId) => { + Object.keys(data[accountId]).forEach((functionName) => { + indexers.push({ + accountId: accountId, + indexerName: functionName, + }); }); }); + setAllIndexers(indexers) }); +}, []); - let my_indexers = indexers.filter( - (indexer) => indexer.accountId === accountId - ); +const initialCheckboxState = checkBoxData.reduce((acc, item) => { + acc[item.method_name] = false; + if (item.schema.properties) { + Object.keys(item.schema.properties).forEach(property => { + acc[`${item.method_name}::${property}`] = false; + }); + } + return acc; +}, {}); + +const handleParentChange = (methodName) => { + const newState = { ...checkboxState }; + const isChecked = !checkboxState[methodName]; + newState[methodName] = isChecked; + checkBoxData.forEach(item => { + if (item.method_name === methodName && item.schema.properties) { + Object.keys(item.schema.properties).forEach(property => { + newState[`${methodName}::${property}`] = isChecked; + }); + } + }); + setCheckboxState(newState); +}; + +const handleChildChange = (childId) => { + setCheckboxState({ + ...checkboxState, + [childId]: !checkboxState[childId], + }); +}; - setMyIndexers(my_indexers); - setAllIndexers(indexers) -}); +const CheckboxContainer = styled.div` + margin-bottom: 10px; +`; + +const CheckboxLabel = styled.label` + display: flex; + align-items: center; + cursor: pointer; + font-size: 16px; + margin-bottom: 5px; +`; + +const SubCheckboxContainer = styled.div` + margin-left: 20px; + border-left: 2px solid #ccc; + padding-left: 10px; +`; + +const Checkbox = styled.input` + margin-right: 10px; + cursor: pointer; +`; // TOP HALF const Hero = styled.div` @@ -392,38 +381,11 @@ function CustomTable() { } // BELOW IS ORIGINAL STYLED COMPONENTS -const Subheading = styled.h2` - display: block; - margin: 0; - font-size: 14px; - line-height: 20px; - color: ${(p) => (p.bold ? "#11181C !important" : "#687076 !important")}; - font-weight: ${(p) => (p.bold ? "600" : "400")}; - font-size: ${(p) => (p.small ? "12px" : "14px")}; - overflow: ${(p) => (p.ellipsis ? "hidden" : "visible")}; - text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "unset")}; - white-space: nowrap; - outline: none; -`; - -const Editor = styled.div` -`; -const Status = styled.div` -`; - const Wrapper = styled.div` margin-inline: 12px; margin-top: calc(var(--body-top-padding) * -1); `; -const NavBarLogo = styled.a` - padding-top: 0.3125rem; - padding-bottom: 0.3125rem; - margin-right: .01rem; - font-size: 1.25rem; - text-decoration: none; - white-space: nowrap; -`; const Main = styled.div` display: block; `; @@ -452,17 +414,7 @@ const Tabs = styled.div` flex: 1; } `; -const Content = styled.div` - background-color: #f7f7f7; - padding: 2em; - border-radius: 5px; -`; -const Title = styled.h1` - font-size: 1.5em; - text-align: center; - color: palevioletred; -`; const TabsButton = styled.button` font-weight: 600; @@ -489,171 +441,7 @@ const TabsButton = styled.button` background: #0091ff; } `; -const H2 = styled.h2` - font-size: 19px; - line-height: 22px; - color: #11181c; - margin: 0 0 8px; -`; -const Card = styled.div` - border-radius: 12px; - background: #fff; - border: ${(div) => (div.selected ? "1px solid black" : "1px solid #eceef0")}; - box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), - 0px 1px 2px rgba(16, 24, 40, 0.06); -`; -const CardBody = styled.div` - padding: 16px; - display: flex; - gap: 16px; - align-items: center; - - > * { - min-width: 0; - } -`; - -const CardFooter = styled.div` - display: flex; - justify-content: space-around; - flex-wrap: wrap; - gap: 4px; - padding: 16px; - border-top: 1px solid #eceef0; -`; - -const TextLink = styled.a` - display: block; - margin: 0; - font-size: 14px; - line-height: 20px; - color: ${(p) => (p.bold ? "#11181C !important" : "#687076 !important")}; - font-weight: ${(p) => (p.bold ? "600" : "400")}; - font-size: ${(p) => (p.small ? "12px" : "14px")}; - overflow: ${(p) => (p.ellipsis ? "hidden" : "visible")}; - text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "unset")}; - white-space: nowrap; - outline: none; - - &:focus, - &:hover { - text-decoration: underline; - } -`; - -const Thumbnail = styled.a` - display: block; - width: 48px; - height: 48px; - flex-shrink: 0; - border: 1px solid #eceef0; - border-radius: 8px; - overflow: hidden; - outline: none; - transition: border-color 200ms; - - &:focus, - &:hover { - border-color: #d0d5dd; - } - - img { - object-fit: cover; - width: 100%; - height: 100%; - } -`; - -const CardWrapper = styled.div` - margin: 0 0 16px; -`; - -const sharedButtonStyles = ` - display: inline-flex; - align-items: center; - gap: 8px; - padding: 8px 16px; - margin-bottom: 12px; - height: 32px; - border-radius: 6px; - font-weight: 600; - font-size: 12px; - line-height: 15px; - text-align: center; - cursor: pointer; - - &:hover, - &:focus { - text-decoration: none; - outline: none; - } - - i { - color: #7E868C; - } - - .bi-16 { - font-size: 16px; - } -`; - -const Button = styled.button` - ${sharedButtonStyles} - color: ${(p) => (p.primary ? "#fff" : "#11181C")} !important; - background: ${(p) => (p.primary ? "#0091FF" : "#FBFCFD")}; - border: ${(p) => (p.primary ? "none" : "1px solid #D7DBDF")}; - - &:hover, - &:focus { - background: ${(p) => (p.primary ? "#0484e5" : "#ECEDEE")}; - } -`; - -const ButtonLink = styled.a` - ${sharedButtonStyles} - color: ${(p) => { - if (p.primary) return "#fff"; - else if (p.danger) return "#fff"; - else return "#11181C"; - }} !important; - background: ${(p) => { - if (p.primary) return "#0091FF"; - else if (p.danger) return "#dc3545"; - else return "#FBFCFD"; - }}; - border: ${(p) => (p.primary ? "none" : "1px solid #D7DBDF")}; - - &:hover, - &:focus { - background: ${(p) => { - if (p.primary) return "#0484e5"; - else if (p.danger) return "#b22b38"; - else return "#ECEDEE"; - }} -`; - -const SignUpLink = styled.a` - --blue: RGBA(13, 110, 253, 1); - display: ${({ hidden }) => (hidden ? "none" : "inline-block")}; - font-size: 14px; - cursor: pointer; - color: var(--blue); - text-decoration: none; - margin-left: 0.1em; - padding: 0; - white-space: nowrap; - - &:hover { - color: var(--blue); - text-decoration: none; - } - - &:visited { - color: var(--blue); - text-decoration: none; - } -`; const selectTab = (tabName) => { Storage.privateSet("queryapi:activeTab", tabName); @@ -665,58 +453,9 @@ const selectIndexerPage = (viewName) => { setActiveIndexerTabView(viewName); }; -const indexerView = (accountId, indexerName) => { - const editUrl = `https://dev.near.org/${REPL_ACCOUNT_ID}/widget/QueryApi.App?selectedIndexerPath=${accountId}/${indexerName}`; - const statusUrl = `https://dev.near.org/${REPL_ACCOUNT_ID}/widget/QueryApi.App?selectedIndexerPath=${accountId}/${indexerName}&view=indexer&activeIndexerView=status`; - const playgroundLink = `https://cloud.hasura.io/public/graphiql?endpoint=${REPL_GRAPHQL_ENDPOINT}/v1/graphql&header=x-hasura-role%3A${accountId.replaceAll( - ".", - "_" - )}`; - - return ( - - - - - - -
- - {indexerName} - - - @{accountId} - -
-
- - - selectIndexerPage("status")}> - View Status - - selectIndexerPage("editor")}> - {accountId === context.accountId ? "Edit Indexer" : "View Indexer"} - - - View In Playground - - -
- ); -}; - - return ( - + {/* selectTab("explore")} @@ -745,7 +484,7 @@ return ( )} - + */}
@@ -770,16 +509,22 @@ return ( handleParentChange(item.method_name)} - type="checkbox" id={item.method_name} /> + /> {item.method_name} {item.schema.properties && ( {Object.keys(item.schema.properties).map((property, subIndex) => ( - handleChildChange(item.method_name, property)} + handleChildChange(`${item.method_name}::${property}`)} /> {property}: {item.schema.properties[property].type} @@ -808,124 +553,6 @@ return ( - {/* selectTab("explore")} - > - - QueryApi - - - - (Documentation) - -
- { - setSelectedIndexerName(""); - State.update({ - activeTab: "create-new-indexer", - }); - selectTab("create-new-indexer"); - }} - > - Create New Indexer - - {myIndexers.length > 0 && ( -

- {accountId}'s Indexers - ({myIndexers.length}) -

- )} - -
*/} -
- - - - - - - - - - - - - -
- {activeTab === "create-new-indexer" && ( -
- -
- )} -
-
- - {state.indexers.length > 0 && - (selectedIndexerName ? ( -

{selectedIndexerName}

- ) : ( -

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

- ))} - -
- {activeTab === "create-new-indexer" && ( -
- {state.indexers.length > 0 && - (selectedIndexerName ? ( -

{selectedIndexerName}

- ) : ( -

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

- ))} - -
- )}
From 98d93f6acdeba09a266e8accacd6a65a338d1824 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 12 Jul 2024 17:03:39 -0400 Subject: [PATCH 05/15] feat:added query for contracts --- frontend/widgets/src/QueryApi.Dashboard.jsx | 242 +++++++++++++------- 1 file changed, 154 insertions(+), 88 deletions(-) diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index d6cd0f3b0..310e1d34f 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -1,64 +1,67 @@ const accountId = context.accountId; +const WILD_CARD = '*'; + +const validateContractId = (accountId) => { + accountId = accountId.trim(); + // Check if accountId is a wildcard '*' + if (accountId === WILD_CARD) return true; + // Check if accountId length is between 2 and 64 characters + const isLengthValid = accountId.length >= 2 && accountId.length <= 64; + if (!isLengthValid) return false; + // Check if accountId starts with '*.' || '*' remove for part verification + if (accountId.startsWith('*.')) accountId = accountId.slice(2); + if (accountId.startsWith('*')) accountId = accountId.slice(1); + + const parts = accountId.split('.'); + for (let part of parts) { + if (!part.match(/^[a-z\d]+([-_][a-z\d]+)*$/)) { + return false; + } + } + + return true; +}; + const [activeTab, setActiveTab] = useState(props.view === "create-new-indexer" ? "create-new-indexer" : props.selectedIndexerPath ? "indexer" : "explore"); const [activeIndexerTabView, setActiveIndexerTabView] = useState(props.activeIndexerView ?? "editor"); const [allIndexers, setAllIndexers] = useState([]); const [checkboxState, setCheckboxState] = useState(initialCheckboxState); -const checkBoxData = [ - { - method_name: 'harvest_meta', - schema: { - type: 'object', - }, - }, - { - method_name: 'ADD_KEY', - schema: { - type: 'object', - }, - }, - { - method_name: 'add_authorized_farm_token', - schema: { - type: 'object', - properties: { - token_id: { - type: 'string', - }, - }, - required: ['token_id'], - }, - }, - { - method_name: 'add_authorized_user', - schema: { - type: 'object', - properties: { - accoint_id: { - type: 'string', - }, - }, - required: ['accoint_id'], - }, - }, -]; +const [checkBoxData, setCheckBoxData] = useState([]); +const [loading, setLoading] = useState(false); +const [contractInputMessage, setContractInputMessage] = useState(''); +const [inputValue, setInputValue] = useState(''); +const [methodCount, setMethodCount] = useState(0); -useEffect(() => { - Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { - const indexers = []; - Object.keys(data).forEach((accountId) => { - Object.keys(data[accountId]).forEach((functionName) => { - indexers.push({ - accountId: accountId, - indexerName: functionName, - }); - }); - }); - setAllIndexers(indexers) - }); -}, []); +const handleFetchCheckboxData = async () => { + if (!validateContractId(inputValue)) { + setContractInputMessage('Invalid contract id'); + return; + } + + setLoading(true); + setContractInputMessage(''); + var url = 'https://europe-west1-pagoda-data-stack-prod.cloudfunctions.net/queryapi_wizard'; + asyncFetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + filter: '*pool.near' + }), + mode: 'cors' + }) + .then(response => { + console.log(response); + // if (!response.ok) { + // throw new Error(`HTTP error! status: ${response.status}`); + // } + }) + setLoading(false); +}; const initialCheckboxState = checkBoxData.reduce((acc, item) => { acc[item.method_name] = false; @@ -70,6 +73,21 @@ const initialCheckboxState = checkBoxData.reduce((acc, item) => { return acc; }, {}); +useEffect(() => { + // Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { + // const indexers = []; + // Object.keys(data).forEach((accountId) => { + // Object.keys(data[accountId]).forEach((functionName) => { + // indexers.push({ + // accountId: accountId, + // indexerName: functionName, + // }); + // }); + // }); + // setAllIndexers(indexers) + // }); +}, []); + const handleParentChange = (methodName) => { const newState = { ...checkboxState }; const isChecked = !checkboxState[methodName]; @@ -157,7 +175,7 @@ const Container = styled.div` const HeadlineContainer = styled.div` width: 364px; - height: 168px; + height: 193px; display: flex; flex-direction: column; justify-content: center; @@ -173,7 +191,7 @@ const WidgetContainer = styled.div` justify-content: center; align-items: center; box-shadow: 0 8.2px 19.92px 0 rgba(0, 0, 0, 0.1), 0 2.34px 2.34px 0 rgba(0, 0, 0, 0.15); - margin-top: 158px; /* Gap between WidgetContainer and HeadlineContainer */ + margin-top: 183px; /* Gap between WidgetContainer and HeadlineContainer */ background: #fff; border-radius: 10px; `; @@ -189,10 +207,34 @@ const SubContainerTitle = styled.h2` font-size: 14px; line-height: 14.06px; color: #333; - margin: 0; + margin-bottom: 6px; +`; + +const MethodsText = styled.div` + display: flex; + align-items: center; + font-size: 12px; + margin-bottom: 8px; +`; + +const MethodsSpan = styled.span` + display: flex; + align-items: center; + justify-content: center; + font-size: 11px; + font-weight: normal; + width: 23px; + height: 17px; + border-radius: 50px; + padding: 4px 6px; + background-color: #F3F3F2; `; -const SubContainerContent = styled.div` ` +const SubContainerContent = styled.div` + border: 1px solid #ccc; + height: 260px; + border-radius: 6px; +` const InputWrapper = styled.div` @@ -206,7 +248,7 @@ const InputWrapper = styled.div` overflow: hidden; `; -const StyledInput = styled.input` +const StyledInput = styled.input` flex: 1; height: 100%; border: none; @@ -215,6 +257,12 @@ const StyledInput = styled.input` border-radius: 6px 0 0 6px; `; +const ContractInputMessage = styled.p` + height: 25px; + font-size: 10px; + color: red; +` + const GreenButton = styled.button` width: 84px; background-color: #37CD83; @@ -394,8 +442,6 @@ const Section = styled.div` padding-top: 0px; border-left: none; border-right: none; - display: ${(p) => (p.active ? "block" : "none")}; - margin: ${(p) => (p.negativeMargin ? "0 -12px" : "0")}; `; const Tabs = styled.div` @@ -455,6 +501,7 @@ const selectIndexerPage = (viewName) => { return ( + {/* -
+
Launch an indexer in minutes Get a working indexer exportable to your Near react application faster than ever. Extract on-chain data, and easily query it using GraphQL endpoints and subscriptions. - - Start + setInputValue(e.target.value)} + /> + Start + {contractInputMessage} Customize indexer
- {checkBoxData.map((item, index) => ( - - - handleParentChange(item.method_name)} - /> - {item.method_name} - - {item.schema.properties && ( - - {Object.keys(item.schema.properties).map((property, subIndex) => ( - - handleChildChange(`${item.method_name}::${property}`)} - /> - {property}: {item.schema.properties[property].type} - + {loading ? ( +
Loading...
+ ) : ( +
+ {checkBoxData.length > 0 && ( + <> + + Methods {methodCount} + + {checkBoxData.map((item, index) => ( + + + handleParentChange(item.method_name)} + /> + {item.method_name} + + {item.schema.properties && ( + + {Object.keys(item.schema.properties).map((property, subIndex) => ( + + handleChildChange(`${item.method_name}::${property}`)} + /> + {property}: {item.schema.properties[property].type} + + ))} + + )} + ))} - + )} - - ))} +
+ )}
@@ -556,4 +621,5 @@ return (
+ ); From bae70c202e300a7bb32012a159aa8d7898773930 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 12 Jul 2024 18:24:10 -0400 Subject: [PATCH 06/15] feat: added noQuery Default text --- frontend/widgets/src/QueryApi.Dashboard.jsx | 184 +++++++++++++------- 1 file changed, 125 insertions(+), 59 deletions(-) diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index 310e1d34f..cd5848dc7 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -36,31 +36,38 @@ const [inputValue, setInputValue] = useState(''); const [methodCount, setMethodCount] = useState(0); const handleFetchCheckboxData = async () => { - if (!validateContractId(inputValue)) { - setContractInputMessage('Invalid contract id'); - return; - } - - setLoading(true); - setContractInputMessage(''); - var url = 'https://europe-west1-pagoda-data-stack-prod.cloudfunctions.net/queryapi_wizard'; - asyncFetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - filter: '*pool.near' - }), - mode: 'cors' - }) + // setCheckBoxData([]); + // setMethodCount(0); + + // if (!validateContractId(inputValue)) { + // setContractInputMessage('Invalid contract id'); + // return; + // } + + // setLoading(true); + // setContractInputMessage(''); + + const url = 'https://europe-west1-pagoda-data-stack-prod.cloudfunctions.net/queryapi_wizard'; + asyncFetch(url, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + filter: '*pool.near' + }), + } + ) .then(response => { - console.log(response); - // if (!response.ok) { - // throw new Error(`HTTP error! status: ${response.status}`); - // } + if (!response.ok) { + setError('There was an error fetching the data'); + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = response.body; + setCheckBoxData(data); + setMethodCount(data.length); }) - setLoading(false); }; const initialCheckboxState = checkBoxData.reduce((acc, item) => { @@ -109,6 +116,18 @@ const handleChildChange = (childId) => { }); }; +const NoQueryContainer = styled.div` + display: flex; + flex-direction: column; +`; +const NoQueryText = styled.p` + font-family: 'Mona Sans', sans-serif; + font-size: 10px; + line-height: 18.2px; + letter-spacing: 1.5%; + font-align: center; +`; + const CheckboxContainer = styled.div` margin-bottom: 10px; `; @@ -128,8 +147,21 @@ const SubCheckboxContainer = styled.div` `; const Checkbox = styled.input` - margin-right: 10px; cursor: pointer; + width: 21.6px; + height: 21.6px; + border-radius: 5.4px; + + border: 0.9px solid #DBDBD7; + padding: 5.4px; + + background-color: #FDFDFC; + box-shadow: 0 0.9px 1.8px 0 rgba(0, 0, 0, 0.1); + + vertical-align: middle; + margin-right: 7.2px; + + outline: none; `; // TOP HALF @@ -231,11 +263,30 @@ const MethodsSpan = styled.span` `; const SubContainerContent = styled.div` - border: 1px solid #ccc; height: 260px; - border-radius: 6px; ` +const ScrollableDiv = styled.div` +height: 260px; +overflow-y: auto; + ::-webkit-scrollbar { + width: 10px; + } + + ::-webkit-scrollbar-track { + background: #f1f1f1; + } + + ::-webkit-scrollbar-thumb { + background: #888; + border-radius: 5px; + } + + ::-webkit-scrollbar-thumb:hover { + background: #555; + } + +`; const InputWrapper = styled.div` display: flex; @@ -559,45 +610,60 @@ return (
{loading ? (
Loading...
- ) : ( -
- {checkBoxData.length > 0 && ( - <> + ) : (checkBoxData.length === 0) ? + + + + + + + Enter a smart contract address to see methods and events. + + : ( +
+ {checkBoxData.length > 0 && ( Methods {methodCount} - {checkBoxData.map((item, index) => ( - - - handleParentChange(item.method_name)} - /> - {item.method_name} - - {item.schema.properties && ( - - {Object.keys(item.schema.properties).map((property, subIndex) => ( - + )} + < ScrollableDiv > + { + checkBoxData.length > 0 && ( + <> + {checkBoxData.map((item, index) => ( + + handleChildChange(`${item.method_name}::${property}`)} + id={item.method_name} + checked={checkboxState[item.method_name]} + onChange={() => handleParentChange(item.method_name)} /> - {property}: {item.schema.properties[property].type} + {item.method_name} - ))} - - )} - - ))} - - )} -
- )} + {item.schema.properties && ( + + {Object.keys(item.schema.properties).map((property, subIndex) => ( + + handleChildChange(`${item.method_name}::${property}`)} + /> + {property}: {item.schema.properties[property].type} + + ))} + + )} + + ))} + + ) + } + +
+ )}
@@ -620,6 +686,6 @@ return (
-
+
); From edb76b37a809f2ee6b6bd018e0da048f6cded7cb Mon Sep 17 00:00:00 2001 From: kevin Date: Mon, 15 Jul 2024 15:53:09 -0400 Subject: [PATCH 07/15] feat: added NoQuery and onkeypress --- frontend/widgets/src/QueryApi.Dashboard.jsx | 552 +++++++++++--------- 1 file changed, 305 insertions(+), 247 deletions(-) diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index cd5848dc7..dbd9e72fd 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -1,131 +1,22 @@ -const accountId = context.accountId; - -const WILD_CARD = '*'; - -const validateContractId = (accountId) => { - accountId = accountId.trim(); - // Check if accountId is a wildcard '*' - if (accountId === WILD_CARD) return true; - // Check if accountId length is between 2 and 64 characters - const isLengthValid = accountId.length >= 2 && accountId.length <= 64; - if (!isLengthValid) return false; - // Check if accountId starts with '*.' || '*' remove for part verification - if (accountId.startsWith('*.')) accountId = accountId.slice(2); - if (accountId.startsWith('*')) accountId = accountId.slice(1); - - const parts = accountId.split('.'); - for (let part of parts) { - if (!part.match(/^[a-z\d]+([-_][a-z\d]+)*$/)) { - return false; - } - } - - return true; -}; - -const [activeTab, setActiveTab] = useState(props.view === "create-new-indexer" ? "create-new-indexer" : props.selectedIndexerPath ? "indexer" : "explore"); -const [activeIndexerTabView, setActiveIndexerTabView] = useState(props.activeIndexerView ?? "editor"); - -const [allIndexers, setAllIndexers] = useState([]); -const [checkboxState, setCheckboxState] = useState(initialCheckboxState); - -const [checkBoxData, setCheckBoxData] = useState([]); -const [loading, setLoading] = useState(false); -const [contractInputMessage, setContractInputMessage] = useState(''); -const [inputValue, setInputValue] = useState(''); -const [methodCount, setMethodCount] = useState(0); - -const handleFetchCheckboxData = async () => { - // setCheckBoxData([]); - // setMethodCount(0); - - // if (!validateContractId(inputValue)) { - // setContractInputMessage('Invalid contract id'); - // return; - // } - - // setLoading(true); - // setContractInputMessage(''); - - const url = 'https://europe-west1-pagoda-data-stack-prod.cloudfunctions.net/queryapi_wizard'; - asyncFetch(url, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - filter: '*pool.near' - }), - } - ) - .then(response => { - if (!response.ok) { - setError('There was an error fetching the data'); - throw new Error(`HTTP error! status: ${response.status}`); - } - const data = response.body; - setCheckBoxData(data); - setMethodCount(data.length); - }) -}; - -const initialCheckboxState = checkBoxData.reduce((acc, item) => { - acc[item.method_name] = false; - if (item.schema.properties) { - Object.keys(item.schema.properties).forEach(property => { - acc[`${item.method_name}::${property}`] = false; - }); - } - return acc; -}, {}); - -useEffect(() => { - // Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { - // const indexers = []; - // Object.keys(data).forEach((accountId) => { - // Object.keys(data[accountId]).forEach((functionName) => { - // indexers.push({ - // accountId: accountId, - // indexerName: functionName, - // }); - // }); - // }); - // setAllIndexers(indexers) - // }); -}, []); - -const handleParentChange = (methodName) => { - const newState = { ...checkboxState }; - const isChecked = !checkboxState[methodName]; - newState[methodName] = isChecked; - checkBoxData.forEach(item => { - if (item.method_name === methodName && item.schema.properties) { - Object.keys(item.schema.properties).forEach(property => { - newState[`${methodName}::${property}`] = isChecked; - }); - } - }); - setCheckboxState(newState); -}; - -const handleChildChange = (childId) => { - setCheckboxState({ - ...checkboxState, - [childId]: !checkboxState[childId], - }); -}; - const NoQueryContainer = styled.div` display: flex; flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + height: 100%; `; + const NoQueryText = styled.p` - font-family: 'Mona Sans', sans-serif; - font-size: 10px; - line-height: 18.2px; - letter-spacing: 1.5%; - font-align: center; + margin-top: 16px; + font-size: 16px; + color: #000; + text-align: center; +`; + +const NoQuerySVG = styled.svg` + height: 100px; + width: 100%; `; const CheckboxContainer = styled.div` @@ -151,16 +42,12 @@ const Checkbox = styled.input` width: 21.6px; height: 21.6px; border-radius: 5.4px; - border: 0.9px solid #DBDBD7; padding: 5.4px; - background-color: #FDFDFC; box-shadow: 0 0.9px 1.8px 0 rgba(0, 0, 0, 0.1); - vertical-align: middle; margin-right: 7.2px; - outline: none; `; @@ -267,25 +154,44 @@ const SubContainerContent = styled.div` ` const ScrollableDiv = styled.div` height: 260px; -overflow-y: auto; - ::-webkit-scrollbar { - width: 10px; - } +width: 100%; +overflow-x: auto; +overflow-y: auto; - ::-webkit-scrollbar-track { - background: #f1f1f1; - } - ::-webkit-scrollbar-thumb { - background: #888; - border-radius: 5px; - } +&::-webkit-scrollbar { + height: 12px; +} - ::-webkit-scrollbar-thumb:hover { - background: #555; - } +&::-webkit-scrollbar-thumb { + background-color: #888; + border-radius: 6px; +} + +&::-webkit-scrollbar-thumb:hover { + background-color: #555; +} + +&::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 6px; +} + +&::-webkit-scrollbar-track-piece { + background: #f9f9f9; +} +/* Firefox */ +scrollbar-width: thin; +scrollbar-color: #888 #f1f1f1; + +/* Edge and IE */ +-ms-overflow-style: -ms-autohiding-scrollbar; + +-ms-scroll-chaining: none; +-ms-scroll-snap-type: mandatory; +-ms-scroll-snap-points-x: snapInterval(0%, 100%); `; const InputWrapper = styled.div` @@ -314,7 +220,7 @@ const ContractInputMessage = styled.p` color: red; ` -const GreenButton = styled.button` +const SearchButton = styled.button` width: 84px; background-color: #37CD83; border: none; @@ -384,10 +290,12 @@ const SearchInput = styled.input` `; const SearchIndexerButton = styled.button` - background-color: transparent; + flex:1; + border-radius: 50px; + background-color: #f0f0f0; border: none; color: black; - padding: 8px 16px; + padding: 8px 31px; cursor: pointer; font-family: 'Mona Sans', sans-serif; font-weight: 450; @@ -396,13 +304,16 @@ const SearchIndexerButton = styled.button` letter-spacing: 2%; `; -const MagnifyingGlass = styled.span` - font-size: 14px; /* Adjust as necessary */ - margin-right: 8px; /* Adjust as necessary */ +const MagnifyingGlass = styled.svg` + width: 16px; + height: 16px; `; -// TABLE - +const SearchArrow = styled.svg` + width: 20px; + height: 20px; +` +/** TABLE STYLES*/ const TableContainer = styled.div` width: 745px; margin: 0 auto; @@ -445,40 +356,6 @@ const TableCell = styled.td` text-align: left; `; -const data = allIndexers.map((indexer) => ({ - indexer: indexer.indexerName, - weeklyRequest: indexer.weeklyRequest || 150, - lastUpdated: indexer.lastUpdated || '2023-06-25', - status: indexer.status || 'Active', -})); - -function CustomTable() { - return ( - - - - - Indexer - Weekly Request - Last Updated - Status - - - - {data.map((row, index) => ( - - {row.indexer} - {row.weeklyRequest} - {row.lastUpdated} - {row.status} - - ))} - -
-
- ); -} - // BELOW IS ORIGINAL STYLED COMPONENTS const Wrapper = styled.div` margin-inline: 12px; @@ -512,7 +389,6 @@ const Tabs = styled.div` } `; - const TabsButton = styled.button` font-weight: 600; font-size: 14px; @@ -539,6 +415,170 @@ const TabsButton = styled.button` } `; +const accountId = context.accountId; +const WILD_CARD = '*'; + +const validateContractId = (accountId) => { + accountId = accountId.trim(); + // Check if accountId is a wildcard '*' + if (accountId === WILD_CARD) return true; + // Check if accountId length is between 2 and 64 characters + const isLengthValid = accountId.length >= 2 && accountId.length <= 64; + if (!isLengthValid) return false; + // Check if accountId starts with '*.' || '*' remove for part verification + if (accountId.startsWith('*.')) accountId = accountId.slice(2); + if (accountId.startsWith('*')) accountId = accountId.slice(1); + + const parts = accountId.split('.'); + for (let part of parts) { + if (!part.match(/^[a-z\d]+([-_][a-z\d]+)*$/)) { + return false; + } + } + + return true; +}; + +const [activeTab, setActiveTab] = useState(props.view === "create-new-indexer" ? "create-new-indexer" : props.selectedIndexerPath ? "indexer" : "explore"); +const [activeIndexerTabView, setActiveIndexerTabView] = useState(props.activeIndexerView ?? "editor"); + +const [allIndexers, setAllIndexers] = useState([]); +const [checkboxState, setCheckboxState] = useState(initialCheckboxState); + +const [checkBoxData, setCheckBoxData] = useState([]); +const [loading, setLoading] = useState(false); +const [contractInputMessage, setContractInputMessage] = useState(''); +const [inputValue, setInputValue] = useState(''); +const [methodCount, setMethodCount] = useState(0); + +const handleFetchCheckboxData = async () => { + setCheckBoxData([]); + setMethodCount(0); + setContractInputMessage(''); + + if (!validateContractId(inputValue)) { + setContractInputMessage('Invalid contract id'); + return; + } + + setLoading(true); + + const url = 'https://europe-west1-pagoda-data-stack-prod.cloudfunctions.net/queryapi_wizard'; + asyncFetch(url, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + filter: inputValue, + }), + } + ) + .then(response => { + if (!response.ok) { + setError('There was an error fetching the data'); + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = response.body; + + if (data.length === 0) { + setContractInputMessage('No methods found for this contract'); + setLoading(false); + return; + }; + + setCheckBoxData(data); + setMethodCount(data.length); + setLoading(false); + }).catch(error => { + setLoading(false); + setError('There was an error fetching the data'); + }); + +}; + +const initialCheckboxState = checkBoxData.reduce((acc, item) => { + acc[item.method_name] = false; + if (item.schema.properties) { + Object.keys(item.schema.properties).forEach(property => { + acc[`${item.method_name}::${property}`] = false; + }); + } + return acc; +}, {}); + +const handleParentChange = (methodName) => { + const newState = { ...checkboxState }; + const isChecked = !checkboxState[methodName]; + newState[methodName] = isChecked; + checkBoxData.forEach(item => { + if (item.method_name === methodName && item.schema.properties) { + Object.keys(item.schema.properties).forEach(property => { + newState[`${methodName}::${property}`] = isChecked; + }); + } + }); + setCheckboxState(newState); +}; + +const handleChildChange = (childId) => { + setCheckboxState({ + ...checkboxState, + [childId]: !checkboxState[childId], + }); +}; + + +useEffect(() => { + // Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { + // const indexers = []; + // Object.keys(data).forEach((accountId) => { + // Object.keys(data[accountId]).forEach((functionName) => { + // indexers.push({ + // accountId: accountId, + // indexerName: functionName, + // }); + // }); + // }); + // setAllIndexers(indexers) + // }); +}); + +const data = allIndexers.map((indexer) => ({ + indexer: indexer.indexerName, + weeklyRequest: indexer.weeklyRequest || 150, + lastUpdated: indexer.lastUpdated || '2023-06-25', + status: indexer.status || 'Active', +})); + +function CustomTable() { + return ( + + + + + Indexer + Weekly Request + Last Updated + Status + + + + {data.map((row, index) => ( + + {row.indexer} + {row.weeklyRequest} + {row.lastUpdated} + {row.status} + + ))} + +
+
+ ); +} + const selectTab = (tabName) => { Storage.privateSet("queryapi:activeTab", tabName); @@ -584,9 +624,7 @@ return ( )} */} -
-
@@ -598,8 +636,9 @@ return ( placeholder="yoursmartcontract.pool.near" value={inputValue} onChange={(e) => setInputValue(e.target.value)} + onKeyPress={(event) => event.key === 'Enter' && handleFetchCheckboxData()} /> - Start + Start {contractInputMessage} @@ -607,64 +646,80 @@ return ( Customize indexer -
- {loading ? ( -
Loading...
- ) : (checkBoxData.length === 0) ? + {loading ? ( +
Loading...
+ ) : (checkBoxData.length === 0) ? + <> - - - - - + + + + + + + + + + Enter a smart contract address to see methods and events. - : ( -
- {checkBoxData.length > 0 && ( - - Methods {methodCount} - - )} - < ScrollableDiv > - { - checkBoxData.length > 0 && ( - <> - {checkBoxData.map((item, index) => ( - - - handleParentChange(item.method_name)} - /> - {item.method_name} - - {item.schema.properties && ( - - {Object.keys(item.schema.properties).map((property, subIndex) => ( - - handleChildChange(`${item.method_name}::${property}`)} - /> - {property}: {item.schema.properties[property].type} - - ))} - - )} - - ))} - - ) - } - -
- )} -
+ + : ( +
+ {checkBoxData.length > 0 && ( + + Methods {methodCount} + + )} + < ScrollableDiv > + { + checkBoxData.length > 0 && ( + <> + {checkBoxData.map((item, index) => ( + + + handleParentChange(item.method_name)} + /> + {item.method_name} + + {item.schema.properties && ( + + {Object.keys(item.schema.properties).map((property, subIndex) => ( + + handleChildChange(`${item.method_name}::${property}`)} + /> + {property}: {item.schema.properties[property].type} + + ))} + + )} + + ))} + + ) + } + +
+ )}
@@ -675,17 +730,20 @@ return ( Explore indexers on Near - 🔍 + - {"➡️"} + + + {CustomTable()} - -
- ); + + + + From bbf24844964c433472b74d196c52e7008f384895 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 16 Jul 2024 15:09:08 -0400 Subject: [PATCH 08/15] chore --- frontend/widgets/src/QueryApi.Dashboard.jsx | 50 ++++++++++++++------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index dbd9e72fd..18c1ab203 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -90,6 +90,8 @@ const Container = styled.div` display: flex; justify-content: center; align-items: center; + height: 100%; + width: 100%; `; const HeadlineContainer = styled.div` @@ -218,7 +220,7 @@ const ContractInputMessage = styled.p` height: 25px; font-size: 10px; color: red; -` +`; const SearchButton = styled.button` width: 84px; @@ -356,7 +358,7 @@ const TableCell = styled.td` text-align: left; `; -// BELOW IS ORIGINAL STYLED COMPONENTS +// ORIGINAL STYLED COMPONENTS const Wrapper = styled.div` margin-inline: 12px; margin-top: calc(var(--body-top-padding) * -1); @@ -415,6 +417,18 @@ const TabsButton = styled.button` } `; +const LoadingSpinner = () => { + const spinnerStyle = { + width: '40px', + height: '40px', + border: '4px solid rgba(0, 0, 0, 0.1)', + borderLeftColor: 'black', + borderRadius: '50%', + animation: 'spin 1s linear infinite', + }; + return
; +}; + const accountId = context.accountId; const WILD_CARD = '*'; @@ -489,6 +503,7 @@ const handleFetchCheckboxData = async () => { }; setCheckBoxData(data); + console.log(data); setMethodCount(data.length); setLoading(false); }).catch(error => { @@ -499,6 +514,7 @@ const handleFetchCheckboxData = async () => { }; const initialCheckboxState = checkBoxData.reduce((acc, item) => { + //Select eveyrthing by default. acc[item.method_name] = false; if (item.schema.properties) { Object.keys(item.schema.properties).forEach(property => { @@ -531,18 +547,18 @@ const handleChildChange = (childId) => { useEffect(() => { - // Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { - // const indexers = []; - // Object.keys(data).forEach((accountId) => { - // Object.keys(data[accountId]).forEach((functionName) => { - // indexers.push({ - // accountId: accountId, - // indexerName: functionName, - // }); - // }); - // }); - // setAllIndexers(indexers) - // }); + Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { + const indexers = []; + Object.keys(data).forEach((accountId) => { + Object.keys(data[accountId]).forEach((functionName) => { + indexers.push({ + accountId: accountId, + indexerName: functionName, + }); + }); + }); + setAllIndexers(indexers) + }); }); const data = allIndexers.map((indexer) => ({ @@ -647,7 +663,9 @@ return ( Customize indexer {loading ? ( -
Loading...
+ + + ) : (checkBoxData.length === 0) ? <> @@ -672,7 +690,7 @@ return ( - Enter a smart contract address to see methods and events. + Enter a smart contract filter, For example "*.pool.near, *.poolv1.near" : ( From f8f23b6ccedc3aea25a83c2e518840a494685628 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 16 Jul 2024 17:10:53 -0400 Subject: [PATCH 09/15] chore: added visual fixes post sync with Xuan --- frontend/widgets/src/QueryApi.Dashboard.jsx | 74 ++++++++++++--------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index 18c1ab203..26156fe1d 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -10,13 +10,15 @@ const NoQueryContainer = styled.div` const NoQueryText = styled.p` margin-top: 16px; font-size: 16px; - color: #000; + font-family: 'Mona Sans', sans-serif; + color: #A1A09A; text-align: center; `; const NoQuerySVG = styled.svg` height: 100px; width: 100%; + color: #A1A09A; `; const CheckboxContainer = styled.div` @@ -71,7 +73,7 @@ const Hero = styled.div` `; const Headline = styled.h1` - font-family: 'FK Grotesk Variable', sans-serif; + font-family: 'Mona Sans', sans-serif; font-weight: 700; width: 369px; font-size: 24px; @@ -81,8 +83,9 @@ const Headline = styled.h1` const Subheadline = styled.p` font-family: 'Mona Sans', sans-serif; font-weight: 400; - font-size: 14px; + font-size: 16px; line-height: 18.2px; + color: #717069; letter-spacing: 1.5%; `; @@ -127,7 +130,7 @@ const SubContainerTitle = styled.h2` font-weight: 700; font-size: 14px; line-height: 14.06px; - color: #333; + color: #7F7E77; margin-bottom: 6px; `; @@ -217,11 +220,19 @@ const StyledInput = styled.input` `; const ContractInputMessage = styled.p` + margin-top: 8px; height: 25px; font-size: 10px; - color: red; + color: #D95C4A; + width: 100%; `; +const WarningSVG = styled.svg` + height: 16px; + width: 16px; + margin-right: 4px; +` + const SearchButton = styled.button` width: 84px; background-color: #37CD83; @@ -250,9 +261,9 @@ const ExploreIndexersContainer = styled.div` const ExploreIndexersHeading = styled.h2` - font-family: 'FK Grotesk Variable', sans-serif; + font-family: 'Mona Sans', sans-serif; font-size: 20px; - font-weight: 400; + font-weight: 500; line-height: 26px; letter-spacing: 0.015em; text-align: left; @@ -360,7 +371,6 @@ const TableCell = styled.td` // ORIGINAL STYLED COMPONENTS const Wrapper = styled.div` - margin-inline: 12px; margin-top: calc(var(--body-top-padding) * -1); `; @@ -425,8 +435,23 @@ const LoadingSpinner = () => { borderLeftColor: 'black', borderRadius: '50%', animation: 'spin 1s linear infinite', + textAlign: 'center', + display: 'flex', + justifyContent: 'center', + alignCenter: 'center', }; - return
; + + const LoadingContainer = styled.div` + text-align: center; + width: 100%; + `; + + const LoadingSpinnerContainer = styled.div` + display: flex; + justify-content: center; + font-size: 14px; + ` + return
<>Generating Methods; }; const accountId = context.accountId; @@ -649,48 +674,31 @@ return ( Get a working indexer exportable to your Near react application faster than ever. Extract on-chain data, and easily query it using GraphQL endpoints and subscriptions. setInputValue(e.target.value)} onKeyPress={(event) => event.key === 'Enter' && handleFetchCheckboxData()} /> Start - {contractInputMessage} + {contractInputMessage ?? <> {contractInputMessage}} + Customize indexer - {loading ? ( + {!loading ? ( ) : (checkBoxData.length === 0) ? <> - - - - - - - - - + - Enter a smart contract filter, For example "*.pool.near, *.poolv1.near" + No smart contract address entered : ( From 5d514a7cdd91e3fc9997ad2b12f00b03aad15dbf Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 16 Jul 2024 17:11:10 -0400 Subject: [PATCH 10/15] chore: added visual fixes post sync with Xuan --- frontend/widgets/src/QueryApi.Dashboard.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index 26156fe1d..18e4362d5 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -688,7 +688,7 @@ return ( Customize indexer - {!loading ? ( + {loading ? ( From a6a72c98869ace6357ddad6b572fd2ad02d0734e Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 18 Jul 2024 18:22:49 -0400 Subject: [PATCH 11/15] feat: revamp the navigation --- frontend/replacement.dev.json | 4 +- frontend/widgets/src/QueryApi.Dashboard.jsx | 382 ++++++++++-------- .../widgets/src/QueryApi.IndexerExplorer.jsx | 223 ++++++++-- 3 files changed, 402 insertions(+), 207 deletions(-) diff --git a/frontend/replacement.dev.json b/frontend/replacement.dev.json index 783dd1a8d..8580237d6 100644 --- a/frontend/replacement.dev.json +++ b/frontend/replacement.dev.json @@ -1,7 +1,7 @@ { "REPL_ACCOUNT_ID": "dev-queryapi.dataplatform.near", "REPL_GRAPHQL_ENDPOINT": "https://near-queryapi.dev.api.pagoda.co", - "REPL_EXTERNAL_APP_URL": "https://queryapi-frontend-vcqilefdcq-ew.a.run.app", + "REPL_EXTERNAL_APP_URL": "http://localhost:3000", "REPL_REGISTRY_CONTRACT_ID": "dev-queryapi.dataplatform.near", "REPL_QUERY_API_USAGE_URL": "https://storage.googleapis.com/databricks-near-query-runner/output/query-api-usage/indexers_dev.json" -} +} \ No newline at end of file diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index 18e4362d5..3cf0294a8 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -58,8 +58,8 @@ const Hero = styled.div` display: flex; justify-content: center; align-items: center; - height: 349px; /* Static height */ - width: 100%; /* Full width */ + height: 349px; + width: 100%; background: linear-gradient( 268.88deg, rgba(2, 255, 133, 0.2) 1.75%, @@ -187,11 +187,9 @@ overflow-y: auto; background: #f9f9f9; } -/* Firefox */ scrollbar-width: thin; scrollbar-color: #888 #f1f1f1; -/* Edge and IE */ -ms-overflow-style: -ms-autohiding-scrollbar; -ms-scroll-chaining: none; @@ -255,8 +253,8 @@ const ExploreIndexersContainer = styled.div` display: flex; justify-content: center; align-items: center; - height: 100%; /* Static height */ - width: 100%; /* Full width */ + height: 100%; + width: 100%; `; @@ -298,7 +296,7 @@ const SearchInput = styled.input` line-height: 21px; letter-spacing: 2%; &::placeholder { - color: #a9a9a9; /* Example placeholder color */ + color: #a9a9a9; } `; @@ -335,8 +333,8 @@ const TableContainer = styled.div` const Table = styled.table` width: 100%; border-collapse: collapse; - border-radius: 8px; /* Adjust the border radius as needed */ - overflow: hidden; /* Ensures border radius is applied to the table */ + border-radius: 8px; + overflow: hidden; `; const TableHeader = styled.thead` @@ -385,45 +383,25 @@ const Section = styled.div` `; const Tabs = styled.div` - display: none; - height: 48px; - background: #f8f9fa; - border-top: 1px solid #eceef0; - border-bottom: 1px solid #eceef0; - margin-bottom: ${(p) => (p.noMargin ? "0" : p.halfMargin ? "24px" : "24px")}; - display: flex; - margin-left: -12px; - margin-right: -12px; - - button { - flex: 1; - } + border-bottom: 2px solid #ccc; + background-color: #f0f0f0; `; const TabsButton = styled.button` - font-weight: 600; - font-size: 14px; - line-height: 16px; - padding: 0 12px; - position: relative; - color: ${(p) => (p.selected ? "#11181C" : "#687076")}; - background: none; + flex: 1; + padding: 1rem; border: none; - outline: none; - &:hover { - color: #11181c; - } + background: ${props => (props.selected ? '#3acd83' : 'transparent')}; + color: ${props => (props.selected ? '#fff' : '#333')}; + font-family: 'Mona Sans', sans-serif; + font-size: 1rem; + font-weight: ${props => (props.selected ? 'bold' : 'normal')}; + cursor: pointer; + transition: background-color 0.3s, color 0.3s; - &::after { - content: ""; - display: ${(p) => (p.selected ? "block" : "none")}; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 3px; - background: #0091ff; + &:hover { + background: #e0e0e0; } `; @@ -477,7 +455,7 @@ const validateContractId = (accountId) => { return true; }; - +console.log(props); const [activeTab, setActiveTab] = useState(props.view === "create-new-indexer" ? "create-new-indexer" : props.selectedIndexerPath ? "indexer" : "explore"); const [activeIndexerTabView, setActiveIndexerTabView] = useState(props.activeIndexerView ?? "editor"); @@ -620,7 +598,6 @@ function CustomTable() { ); } - const selectTab = (tabName) => { Storage.privateSet("queryapi:activeTab", tabName); setActiveTab(tabName); @@ -632,9 +609,16 @@ const selectIndexerPage = (viewName) => { }; return ( - + + + selectTab("launchpad")} + selected={activeTab === "launchpad"} + > + Launchpad + - {/* selectTab("explore")} @@ -642,130 +626,194 @@ return ( > Explore Indexers - {activeTab == "create-new-indexer" && ( - selectTab("create-new-indexer")} - selected={activeTab === "create-new-indexer"} - > - Create New Indexer - - )} - {props.selectedIndexerPath && ( - <> - selectTab("indexer")} - selected={activeTab === "indexer"} - > - Indexer ({props.selectedIndexerPath}) - - - )} - */} + selectTab("indexer")} + selected={activeTab === "indexer"} + > + Indexer ({selectedIndexerPath}) + + + {/* Start of Default Code */}
-
- - - - Launch an indexer in minutes - Get a working indexer exportable to your Near react application faster than ever. Extract on-chain data, and easily query it using GraphQL endpoints and subscriptions. - - setInputValue(e.target.value)} - onKeyPress={(event) => event.key === 'Enter' && handleFetchCheckboxData()} - /> - Start - - {contractInputMessage ?? <> {contractInputMessage}} - - - - - Customize indexer - - {loading ? ( - - - - ) : (checkBoxData.length === 0) ? - <> - - - - No smart contract address entered - - - : ( -
- {checkBoxData.length > 0 && ( - - Methods {methodCount} - - )} - < ScrollableDiv > - { - checkBoxData.length > 0 && ( - <> - {checkBoxData.map((item, index) => ( - - - handleParentChange(item.method_name)} - /> - {item.method_name} - - {item.schema.properties && ( - - {Object.keys(item.schema.properties).map((property, subIndex) => ( - - handleChildChange(`${item.method_name}::${property}`)} - /> - {property}: {item.schema.properties[property].type} - - ))} - - )} - - ))} - - ) - } - -
- )} -
-
-
-
-
- - - - Explore indexers on Near - - - - - - - - {CustomTable()} - - -
+ + {/* End of Default Code */} + {activeTab === 'launchpad' && ( +
+ + + + Launch an indexer in minutes + Get a working indexer exportable to your Near react application faster than ever. Extract on-chain data, and easily query it using GraphQL endpoints and subscriptions. + + setInputValue(e.target.value)} + onKeyPress={(event) => event.key === 'Enter' && handleFetchCheckboxData()} + /> + Start + + {contractInputMessage ?? <> {contractInputMessage}} + + + + + Customize indexer + + {loading ? ( + + + + ) : (checkBoxData.length === 0) ? + <> + + + + No smart contract address entered + + + : ( +
+ {checkBoxData.length > 0 && ( + + Methods {methodCount} + + )} + < ScrollableDiv > + { + checkBoxData.length > 0 && ( + <> + {checkBoxData.map((item, index) => ( + + + handleParentChange(item.method_name)} + /> + {item.method_name} + + {item.schema.properties && ( + + {Object.keys(item.schema.properties).map((property, subIndex) => ( + + handleChildChange(`${item.method_name}::${property}`)} + /> + {property}: {item.schema.properties[property].type} + + ))} + + )} + + ))} + + ) + } + +
+ )} +
+
+
+
+
+ + + + Explore indexers on Near + + + + + + + + {CustomTable()} + + +
) + } + + {activeTab === 'explore' && ( +
+ +
+ )} + + + + {/*
+ {state.activeTab === "create-new-indexer" && ( +
+ +
+ )} +
*/} + + + {/*
+ + {state.indexers.length > 0 && + (state.selected_indexer != undefined ? ( +

{state.selected_indexer}

+ ) : ( +

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

+ ))} + +
+ {state.activeTab === "create-new-indexer" && ( +
+ {state.indexers.length > 0 && + (state.selected_indexer != undefined ? ( +

{state.selected_indexer}

+ ) : ( +

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

+ ))} + +
+ )} +
*/} + + {/* NOT FOUND DEFAULT */}
); diff --git a/frontend/widgets/src/QueryApi.IndexerExplorer.jsx b/frontend/widgets/src/QueryApi.IndexerExplorer.jsx index 550604178..abb9ca61d 100644 --- a/frontend/widgets/src/QueryApi.IndexerExplorer.jsx +++ b/frontend/widgets/src/QueryApi.IndexerExplorer.jsx @@ -130,22 +130,35 @@ const Text = styled.p` } } `; - const Items = styled.div` display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; + padding: 1rem; @media (max-width: 1200px) { grid-template-columns: repeat(2, minmax(0, 1fr)); } @media (max-width: 800px) { - grid-template-columns: minmax(0, 1fr); + grid-template-columns: 1fr; + } +`; + +const Item = styled.div` + background-color: #fff; + border: 1px solid #ddd; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + overflow: hidden; + transition: transform 0.3s, box-shadow 0.3s; + + &:hover { + transform: translateY(-4px); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); } `; -const Item = styled.div``; const Button = styled.button` display: block; @@ -243,6 +256,106 @@ const TextLink = styled.a` } `; + +const NavBarContainer = styled.div` + display: flex; + align-items: center; + background-color: #f0f0f0; + padding: 0.5rem 1rem; + border-bottom: 1px solid #ccc; + justify-content: space-between; +`; + +const LeftGroup = styled.div` + display: flex; + align-items: center; +`; + +const NavBarLogo = styled.a` + display: flex; + align-items: center; + color: #333; + text-decoration: none; + font-size: 0.875rem; + font-weight: bold; + margin-right: 1rem; + &:hover { + text-decoration: none; + color: #333; + } + +`; + +const SignUpLink = styled.a` + color: #0070f3; + text-decoration: none; + font-size: 0.75rem; + margin-right: 1rem; +`; + +const ButtonWrapper = styled.div` + display: flex; + align-items: center; +`; +const ButtonLink = styled.a` + display: inline-block; + padding: 0.5rem 1rem; + background-color: black; + color: #fff; + text-decoration: none; + border-radius: 4px; + font-size: 0.75rem; + font-weight: bold; + cursor: pointer; + transition: background-color 0.3s, color 0.3s, transform 0.2s; + + &:hover { + background-color: #333; + color: #fff; + text-decoration: none; + outline: none; + } + + &:focus { + outline: none; + text-decoration: none; + } +`; + +const ToggleWrapper = styled.div` + display: flex; + align-items: center; +`; + +const ToggleButton = styled.button` + flex: 1; + padding: 0.5rem; + border: none; + border-radius: 20px; + background-color: ${props => (props.selected ? 'black' : '#e0e0e0')}; + color: ${props => (props.selected ? '#fff' : '#333')}; + font-size: 0.75rem; + font-weight: ${props => (props.selected ? 'bold' : 'normal')}; + cursor: pointer; + transition: background-color 0.3s, color 0.3s, transform 0.2s; + text-align: center; + min-width: 100px; + max-width: 150px; + + &:not(:last-child) { + margin-right: 4px; + } + + &:hover { + background-color: ${props => (props.selected ? '#333' : '#d0d0d0')}; + color: ${props => (props.selected ? '#fff' : '#000')}; + } + + &:focus { + outline: none; + } +`; + const LoadingSpinner = () => { const spinnerStyle = { width: '40px', @@ -277,21 +390,59 @@ const LoadingSpinner = () => { }; return ( - - - setSelectedTab("my-indexers")} - selected={selectedTab === "my-indexers"} - > - My Indexers - - setSelectedTab("all")} - selected={selectedTab === "all"} - > - All - - + + + + + + + QueryApi + + + + (Documentation) + + + + { + setActiveTab("create-new-indexer"); + setSelectedIndexerName(""); + selectTab("create-new-indexer"); + }} + > + Create New Indexer + + + + + + setSelectedTab("my-indexers")} + selected={selectedTab === "my-indexers"} + > + My Indexers + + setSelectedTab("all")} + selected={selectedTab === "all"} + > + All Indexers + + + + {error && {error}} {selectedTab === "all" && ( @@ -302,16 +453,14 @@ return ( ) : ( - <> - {allIndexers.map((indexer, i) => ( - - - - ))} - + {allIndexers.map((indexer, i) => ( + + + + ))} )} @@ -338,16 +487,14 @@ return ( ) : ( - <> - {myIndexers.map((indexer, i) => ( - - - - ))} - + {myIndexers.map((indexer, i) => ( + + + + ))} )} From a0f008909a3d10689c3a0537fbef6f8c697ffad1 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 19 Jul 2024 14:06:38 -0400 Subject: [PATCH 12/15] chore --- frontend/widgets/src/QueryApi.Dashboard.jsx | 100 ++-- .../src/QueryApi.DashboardTEMPFILE.jsx | 497 ------------------ frontend/widgets/src/QueryApi.Editor.jsx | 10 + frontend/widgets/src/QueryApi.IndexerCard.jsx | 1 + 4 files changed, 62 insertions(+), 546 deletions(-) delete mode 100644 frontend/widgets/src/QueryApi.DashboardTEMPFILE.jsx diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index 3cf0294a8..e73b5d195 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -468,6 +468,8 @@ const [contractInputMessage, setContractInputMessage] = useState(''); const [inputValue, setInputValue] = useState(''); const [methodCount, setMethodCount] = useState(0); +const [selectedIndexer, setSelectedIndexer] = useState(props.selectedIndexerPath); + const handleFetchCheckboxData = async () => { setCheckBoxData([]); setMethodCount(0); @@ -548,7 +550,10 @@ const handleChildChange = (childId) => { }); }; - +useEffect(() => { + console.log(selectedIndexer.split('/')[0]); + console.log(selectedIndexer.split('/')[1]); +}); useEffect(() => { Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { const indexers = []; @@ -636,10 +641,8 @@ return ( - {/* Start of Default Code */} -
- {/* End of Default Code */} +
{activeTab === 'launchpad' && (
@@ -740,16 +743,56 @@ return ( {CustomTable()} -
) - } + + )} {activeTab === 'explore' && (
- +
)} - + {activeTab === 'indexer' && ( +
+ {/* {state.indexers.length > 0 && + (state.selected_indexer != undefined ? ( +

{state.selected_indexer}

+ ) : ( +

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

+ ))} */} + + {/* {state.activeTab === "create-new-indexer" && ( +
+ {state.indexers.length > 0 && + (state.selected_indexer != undefined ? ( +

{state.selected_indexer}

+ ) : ( +

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

+ ))} + +
+ )} */} +
+ )} {/*
*/} - {/*
- - {state.indexers.length > 0 && - (state.selected_indexer != undefined ? ( -

{state.selected_indexer}

- ) : ( -

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

- ))} - -
- {state.activeTab === "create-new-indexer" && ( -
- {state.indexers.length > 0 && - (state.selected_indexer != undefined ? ( -

{state.selected_indexer}

- ) : ( -

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

- ))} - -
- )} -
*/} - {/* NOT FOUND DEFAULT */}
diff --git a/frontend/widgets/src/QueryApi.DashboardTEMPFILE.jsx b/frontend/widgets/src/QueryApi.DashboardTEMPFILE.jsx deleted file mode 100644 index 486dd5ceb..000000000 --- a/frontend/widgets/src/QueryApi.DashboardTEMPFILE.jsx +++ /dev/null @@ -1,497 +0,0 @@ -const accountId = context.accountId; -const [selected_accountId, selected_indexerName] = props.selectedIndexerPath ? props.selectedIndexerPath.split("/") : [undefined, undefined]; -const limit = 7; - -const [activeTab, setActiveTab] = useState(props.view === "create-new-indexer" ? "create-new-indexer" : props.selectedIndexerPath ? "indexer" : "explore"); -const [activeIndexerTabView, setActiveIndexerTabView] = useState(props.activeIndexerView ?? "editor"); -const [myIndexers, setMyIndexers] = useState([]); -const [allIndexers, setAllIndexers] = useState([]); -const [selectedIndexerName, setSelectedIndexerName] = useState(''); -const [totalIndexers, setTotalIndexers] = useState(0); - -Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { - const indexers = []; - const total_indexers = 0; - Object.keys(data).forEach((accountId) => { - Object.keys(data[accountId]).forEach((functionName) => { - indexers.push({ - accountId: accountId, - indexerName: functionName, - }); - total_indexers += 1; - }); - }); - - let my_indexers = indexers.filter( - (indexer) => indexer.accountId === accountId - ); - - setMyIndexers(my_indexers); - setAllIndexers(indexers) - setTotalIndexers(total_indexers); - -}); - -const Subheading = styled.h2` - display: block; - margin: 0; - font-size: 14px; - line-height: 20px; - color: ${(p) => (p.bold ? "#11181C !important" : "#687076 !important")}; - font-weight: ${(p) => (p.bold ? "600" : "400")}; - font-size: ${(p) => (p.small ? "12px" : "14px")}; - overflow: ${(p) => (p.ellipsis ? "hidden" : "visible")}; - text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "unset")}; - white-space: nowrap; - outline: none; -`; - -const Editor = styled.div` -`; -const Status = styled.div` -`; - -const Wrapper = styled.div` - margin-inline: 12px; - margin-top: calc(var(--body-top-padding) * -1); -`; - -const NavBarLogo = styled.a` - padding-top: 0.3125rem; - padding-bottom: 0.3125rem; - margin-right: .01rem; - font-size: 1.25rem; - text-decoration: none; - white-space: nowrap; -`; -const Main = styled.div` - display: block; -`; - -const Section = styled.div` - padding-top: 0px; - border-left: none; - border-right: none; - display: ${(p) => (p.active ? "block" : "none")}; - margin: ${(p) => (p.negativeMargin ? "0 -12px" : "0")}; -`; - -const Tabs = styled.div` - display: none; - height: 48px; - background: #f8f9fa; - border-top: 1px solid #eceef0; - border-bottom: 1px solid #eceef0; - margin-bottom: ${(p) => (p.noMargin ? "0" : p.halfMargin ? "24px" : "24px")}; - - display: flex; - margin-left: -12px; - margin-right: -12px; - - button { - flex: 1; - } -`; -const Content = styled.div` - background-color: #f7f7f7; - padding: 2em; - border-radius: 5px; -`; - -const Title = styled.h1` - font-size: 1.5em; - text-align: center; - color: palevioletred; -`; - -const TabsButton = styled.button` - font-weight: 600; - font-size: 14px; - line-height: 16px; - padding: 0 12px; - position: relative; - color: ${(p) => (p.selected ? "#11181C" : "#687076")}; - background: none; - border: none; - outline: none; - &:hover { - color: #11181c; - } - - &::after { - content: ""; - display: ${(p) => (p.selected ? "block" : "none")}; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 3px; - background: #0091ff; - } -`; -const H2 = styled.h2` - font-size: 19px; - line-height: 22px; - color: #11181c; - margin: 0 0 8px; -`; -const Card = styled.div` - border-radius: 12px; - background: #fff; - border: ${(div) => (div.selected ? "1px solid black" : "1px solid #eceef0")}; - box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), - 0px 1px 2px rgba(16, 24, 40, 0.06); -`; - -const CardBody = styled.div` - padding: 16px; - display: flex; - gap: 16px; - align-items: center; - - > * { - min-width: 0; - } -`; - -const CardFooter = styled.div` - display: flex; - justify-content: space-around; - flex-wrap: wrap; - gap: 4px; - padding: 16px; - border-top: 1px solid #eceef0; -`; - -const TextLink = styled.a` - display: block; - margin: 0; - font-size: 14px; - line-height: 20px; - color: ${(p) => (p.bold ? "#11181C !important" : "#687076 !important")}; - font-weight: ${(p) => (p.bold ? "600" : "400")}; - font-size: ${(p) => (p.small ? "12px" : "14px")}; - overflow: ${(p) => (p.ellipsis ? "hidden" : "visible")}; - text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "unset")}; - white-space: nowrap; - outline: none; - - &:focus, - &:hover { - text-decoration: underline; - } -`; - -const Thumbnail = styled.a` - display: block; - width: 48px; - height: 48px; - flex-shrink: 0; - border: 1px solid #eceef0; - border-radius: 8px; - overflow: hidden; - outline: none; - transition: border-color 200ms; - - &:focus, - &:hover { - border-color: #d0d5dd; - } - - img { - object-fit: cover; - width: 100%; - height: 100%; - } -`; - -const CardWrapper = styled.div` - margin: 0 0 16px; -`; - -const sharedButtonStyles = ` - display: inline-flex; - align-items: center; - gap: 8px; - padding: 8px 16px; - margin-bottom: 12px; - height: 32px; - border-radius: 6px; - font-weight: 600; - font-size: 12px; - line-height: 15px; - text-align: center; - cursor: pointer; - - &:hover, - &:focus { - text-decoration: none; - outline: none; - } - - i { - color: #7E868C; - } - - .bi-16 { - font-size: 16px; - } -`; - -const Button = styled.button` - ${sharedButtonStyles} - color: ${(p) => (p.primary ? "#fff" : "#11181C")} !important; - background: ${(p) => (p.primary ? "#0091FF" : "#FBFCFD")}; - border: ${(p) => (p.primary ? "none" : "1px solid #D7DBDF")}; - - &:hover, - &:focus { - background: ${(p) => (p.primary ? "#0484e5" : "#ECEDEE")}; - } -`; - -const ButtonLink = styled.a` - ${sharedButtonStyles} - color: ${(p) => { - if (p.primary) return "#fff"; - else if (p.danger) return "#fff"; - else return "#11181C"; - }} !important; - background: ${(p) => { - if (p.primary) return "#0091FF"; - else if (p.danger) return "#dc3545"; - else return "#FBFCFD"; - }}; - border: ${(p) => (p.primary ? "none" : "1px solid #D7DBDF")}; - - &:hover, - &:focus { - background: ${(p) => { - if (p.primary) return "#0484e5"; - else if (p.danger) return "#b22b38"; - else return "#ECEDEE"; - }} -`; - -const SignUpLink = styled.a` - --blue: RGBA(13, 110, 253, 1); - display: ${({ hidden }) => (hidden ? "none" : "inline-block")}; - font-size: 14px; - cursor: pointer; - color: var(--blue); - text-decoration: none; - margin-left: 0.1em; - padding: 0; - white-space: nowrap; - - &:hover { - color: var(--blue); - text-decoration: none; - } - - &:visited { - color: var(--blue); - text-decoration: none; - } -`; - -const selectTab = (tabName) => { - Storage.privateSet("queryapi:activeTab", tabName); - setActiveTab(tabName); -}; - -const selectIndexerPage = (viewName) => { - Storage.privateSet("queryapi:activeIndexerTabView", viewName); - setActiveIndexerTabView(viewName); -}; -const indexerView = (accountId, indexerName) => { - const editUrl = `https://dev.near.org/${REPL_ACCOUNT_ID}/widget/QueryApi.App?selectedIndexerPath=${accountId}/${indexerName}`; - const statusUrl = `https://dev.near.org/${REPL_ACCOUNT_ID}/widget/QueryApi.App?selectedIndexerPath=${accountId}/${indexerName}&view=indexer&activeIndexerView=status`; - const playgroundLink = `https://cloud.hasura.io/public/graphiql?endpoint=${REPL_GRAPHQL_ENDPOINT}/v1/graphql&header=x-hasura-role%3A${accountId.replaceAll( - ".", - "_" - )}`; - - return ( - - - - - - -
- - {indexerName} - - - @{accountId} - -
-
- - - selectIndexerPage("status")}> - View Status - - selectIndexerPage("editor")}> - {accountId === context.accountId ? "Edit Indexer" : "View Indexer"} - - - View In Playground - - -
- ); -}; - -return ( - - - selectTab("explore")} - selected={activeTab === "explore"} - > - Explore Indexers - - {activeTab == "create-new-indexer" && ( - selectTab("create-new-indexer")} - selected={activeTab === "create-new-indexer"} - > - Create New Indexer - - )} - - {props.selectedIndexerPath && ( - <> - selectTab("indexer")} - selected={activeTab === "indexer"} - > - Indexer ({props.selectedIndexerPath}) - - - )} - -
-
- selectTab("explore")} - > - - QueryApi - - - - (Documentation) - -
- { - setSelectedIndexerName(""); - State.update({ - activeTab: "create-new-indexer", - }); - selectTab("create-new-indexer"); - }} - > - Create New Indexer - - {myIndexers.length > 0 && ( -

- {accountId}'s Indexers - ({myIndexers.length}) -

- )} - -
-
-
- {activeTab === "create-new-indexer" && ( -
- -
- )} -
-
- - {state.indexers.length > 0 && - (selectedIndexerName ? ( -

{selectedIndexerName}

- ) : ( -

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

- ))} - -
- {activeTab === "create-new-indexer" && ( -
- {state.indexers.length > 0 && - (selectedIndexerName ? ( -

{selectedIndexerName}

- ) : ( -

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

- ))} - -
- )} -
-
-
-); diff --git a/frontend/widgets/src/QueryApi.Editor.jsx b/frontend/widgets/src/QueryApi.Editor.jsx index 73544babe..8781becb2 100644 --- a/frontend/widgets/src/QueryApi.Editor.jsx +++ b/frontend/widgets/src/QueryApi.Editor.jsx @@ -3,6 +3,7 @@ const tab = props.tab || ""; const activeView = props.activeView || "editor"; let accountId = props.accountId || context.accountId; let externalAppUrl = `${REPL_EXTERNAL_APP_URL}/${path}?accountId=${accountId}`; +console.log("props", props); if (props.indexerName) { externalAppUrl += `&indexerName=${props.indexerName}`; @@ -71,6 +72,15 @@ const requestHandler = (request, response) => { } }; + +console.log('critical info', + externalAppUrl, + path, + initialViewHeight, + initialPayload, + requestHandler +) + // NearSocialBridgeCore widget is the core that makes all the "magic" happens return ( number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); From ded43f28865ef2678343573094cbe789a501c1a0 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 19 Jul 2024 14:47:46 -0400 Subject: [PATCH 13/15] finish nav bar --- frontend/replacement.dev.json | 4 +- frontend/widgets/src/QueryApi.Dashboard.jsx | 92 ++++++++------------- frontend/widgets/src/QueryApi.Editor.jsx | 10 --- frontend/widgets/src/QueryApi.NotFound.jsx | 39 +++++++++ 4 files changed, 77 insertions(+), 68 deletions(-) create mode 100644 frontend/widgets/src/QueryApi.NotFound.jsx diff --git a/frontend/replacement.dev.json b/frontend/replacement.dev.json index 8580237d6..783dd1a8d 100644 --- a/frontend/replacement.dev.json +++ b/frontend/replacement.dev.json @@ -1,7 +1,7 @@ { "REPL_ACCOUNT_ID": "dev-queryapi.dataplatform.near", "REPL_GRAPHQL_ENDPOINT": "https://near-queryapi.dev.api.pagoda.co", - "REPL_EXTERNAL_APP_URL": "http://localhost:3000", + "REPL_EXTERNAL_APP_URL": "https://queryapi-frontend-vcqilefdcq-ew.a.run.app", "REPL_REGISTRY_CONTRACT_ID": "dev-queryapi.dataplatform.near", "REPL_QUERY_API_USAGE_URL": "https://storage.googleapis.com/databricks-near-query-runner/output/query-api-usage/indexers_dev.json" -} \ No newline at end of file +} diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index e73b5d195..3e5b22c50 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -405,6 +405,17 @@ const TabsButton = styled.button` } `; +const AlertText = styled.p` +font-family: 'Mona Sans', sans-serif; +font-size: 14px; +line-height: 21px; +text-align: center; +color:red; +margin: 0; +padding: 0; +bg-color: #f9f9f9; +` + const LoadingSpinner = () => { const spinnerStyle = { width: '40px', @@ -455,7 +466,7 @@ const validateContractId = (accountId) => { return true; }; -console.log(props); + const [activeTab, setActiveTab] = useState(props.view === "create-new-indexer" ? "create-new-indexer" : props.selectedIndexerPath ? "indexer" : "explore"); const [activeIndexerTabView, setActiveIndexerTabView] = useState(props.activeIndexerView ?? "editor"); @@ -508,7 +519,6 @@ const handleFetchCheckboxData = async () => { }; setCheckBoxData(data); - console.log(data); setMethodCount(data.length); setLoading(false); }).catch(error => { @@ -550,10 +560,6 @@ const handleChildChange = (childId) => { }); }; -useEffect(() => { - console.log(selectedIndexer.split('/')[0]); - console.log(selectedIndexer.split('/')[1]); -}); useEffect(() => { Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { const indexers = []; @@ -637,7 +643,7 @@ return ( onClick={() => selectTab("indexer")} selected={activeTab === "indexer"} > - Indexer ({selectedIndexerPath}) + Indexer ({selectedIndexer}) @@ -645,6 +651,7 @@ return (
{activeTab === 'launchpad' && (
+ Please note that this page is currently under development. Features may be incomplete or inaccurate @@ -754,68 +761,41 @@ return (
)} + {activeTab === "create-new-indexer" && ( +
+ +
+ )} + {activeTab === 'indexer' && (
- {/* {state.indexers.length > 0 && - (state.selected_indexer != undefined ? ( -

{state.selected_indexer}

- ) : ( -

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

- ))} */} - {/* {state.activeTab === "create-new-indexer" && ( -
- {state.indexers.length > 0 && - (state.selected_indexer != undefined ? ( -

{state.selected_indexer}

- ) : ( -

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

- ))} - -
- )} */}
)} - {/*
- {state.activeTab === "create-new-indexer" && ( -
- -
- )} -
*/} - - - {/* NOT FOUND DEFAULT */} + {!['launchpad', 'explore', 'indexer', 'create-new-indexer'].includes(activeTab) && ( + + )} +
); diff --git a/frontend/widgets/src/QueryApi.Editor.jsx b/frontend/widgets/src/QueryApi.Editor.jsx index 8781becb2..73544babe 100644 --- a/frontend/widgets/src/QueryApi.Editor.jsx +++ b/frontend/widgets/src/QueryApi.Editor.jsx @@ -3,7 +3,6 @@ const tab = props.tab || ""; const activeView = props.activeView || "editor"; let accountId = props.accountId || context.accountId; let externalAppUrl = `${REPL_EXTERNAL_APP_URL}/${path}?accountId=${accountId}`; -console.log("props", props); if (props.indexerName) { externalAppUrl += `&indexerName=${props.indexerName}`; @@ -72,15 +71,6 @@ const requestHandler = (request, response) => { } }; - -console.log('critical info', - externalAppUrl, - path, - initialViewHeight, - initialPayload, - requestHandler -) - // NearSocialBridgeCore widget is the core that makes all the "magic" happens return ( + 404 + Page Not Found + Go back to Home + +); From 530d09fca16bdf73fdf47ccbab8f1dacfb760104 Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 19 Jul 2024 16:24:15 -0400 Subject: [PATCH 14/15] feat: added feature flag --- frontend/widgets/src/QueryApi.Dashboard.jsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index 3e5b22c50..8d2c4535f 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -466,6 +466,7 @@ const validateContractId = (accountId) => { return true; }; +const IS_DEV = `${REPL_EXTERNAL_APP_URL}` === "https://queryapi-frontend-vcqilefdcq-ew.a.run.app" || `${REPL_EXTERNAL_APP_URL}` === "http://localhost:3000"; const [activeTab, setActiveTab] = useState(props.view === "create-new-indexer" ? "create-new-indexer" : props.selectedIndexerPath ? "indexer" : "explore"); const [activeIndexerTabView, setActiveIndexerTabView] = useState(props.activeIndexerView ?? "editor"); @@ -622,13 +623,15 @@ const selectIndexerPage = (viewName) => { return ( - selectTab("launchpad")} - selected={activeTab === "launchpad"} - > - Launchpad - + {!IS_DEV && ( + selectTab("launchpad")} + selected={activeTab === "launchpad"} + > + Launchpad + + )} - {activeTab === 'launchpad' && ( + {activeTab === 'launchpad' && IS_DEV && (
Please note that this page is currently under development. Features may be incomplete or inaccurate From d139bf05777c82a739197eded749540a5190e54c Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 19 Jul 2024 16:26:17 -0400 Subject: [PATCH 15/15] fix: isDEV --- frontend/widgets/src/QueryApi.Dashboard.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index 8d2c4535f..ac4760bbb 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -623,7 +623,7 @@ const selectIndexerPage = (viewName) => { return ( - {!IS_DEV && ( + {IS_DEV && ( selectTab("launchpad")}