From da9ddac069730125661f9bec8ca94d5777cc4a6a Mon Sep 17 00:00:00 2001 From: Baranowski Date: Wed, 5 Apr 2023 14:59:26 +0200 Subject: [PATCH] feat(cactus-cmd-gui-app): add common cacti gui app -Add GUI app to visualize ledger data stored in supabase by persistence plugins Closes: #2264 Depends on: #2259 Signed-off-by: Eryk Baranowski eryk.baranowski@fujitsu.com --- packages/cactus-cmd-gui-app/README.md | 48 ++++++ packages/cactus-cmd-gui-app/index.html | 16 ++ packages/cactus-cmd-gui-app/package.json | 76 ++++++++++ packages/cactus-cmd-gui-app/postcss.config.js | 5 + .../cactus-cmd-gui-app/src/App.module.css | 6 + packages/cactus-cmd-gui-app/src/App.tsx | 18 +++ .../cactus-cmd-gui-app/src/assets/favicon.ico | Bin 0 -> 15086 bytes .../AccountCard/AccountCard.module.css | 19 +++ .../components/AccountCard/AccountCard.tsx | 19 +++ .../components/BlockCard/BlockCard.module.css | 33 +++++ .../src/components/BlockCard/BlockCard.tsx | 28 ++++ .../CardWrapper/CardWrapper.module.css | 56 +++++++ .../components/CardWrapper/CardWrapper.tsx | 140 ++++++++++++++++++ .../src/components/Chart/Chart.module.css | 22 +++ .../src/components/Chart/Chart.tsx | 66 +++++++++ .../src/components/Chart/LineChart.tsx | 80 ++++++++++ .../Pagination/Pagination.module.css | 34 +++++ .../src/components/Pagination/Pagination.tsx | 66 +++++++++ .../components/TokenHeader/TokenAccount.tsx | 20 +++ .../TokenHeader/TokenHeader.module.css | 58 ++++++++ .../components/TokenHeader/TokenHeader.tsx | 52 +++++++ .../components/UI/Button/Button.module.css | 67 +++++++++ .../src/components/UI/Button/Button.tsx | 28 ++++ .../components/UI/CustomTable/CustomTable.css | 53 +++++++ .../components/UI/CustomTable/CustomTable.tsx | 47 ++++++ .../src/components/UI/Menu/Menu.module.css | 21 +++ .../src/components/UI/Menu/Menu.tsx | 23 +++ .../components/UI/Search/Search.module.css | 31 ++++ .../src/components/UI/Search/Search.tsx | 47 ++++++ packages/cactus-cmd-gui-app/src/index.css | 13 ++ packages/cactus-cmd-gui-app/src/index.tsx | 14 ++ packages/cactus-cmd-gui-app/src/logo.svg | 1 + .../src/pages/Accounts/Accounts.module.css | 6 + .../src/pages/Accounts/Accounts.tsx | 63 ++++++++ .../src/pages/Blocks/Blocks.module.css | 0 .../src/pages/Blocks/Blocks.tsx | 59 ++++++++ .../src/pages/Dashboard/Dashboard.module.css | 4 + .../src/pages/Dashboard/Dashboard.tsx | 98 ++++++++++++ .../src/pages/Details/BlockDetails.tsx | 59 ++++++++ .../src/pages/Details/Details.module.css | 22 +++ .../src/pages/Details/TokenDetails.tsx | 59 ++++++++ .../pages/Details/TokenTransactionDetails.tsx | 62 ++++++++ .../src/pages/Details/TransactionDetails.tsx | 100 +++++++++++++ .../src/pages/ERC20/ERC20.module.css | 11 ++ .../src/pages/ERC20/ERC20.tsx | 72 +++++++++ .../src/pages/ERC721/ERC721.module.css | 11 ++ .../src/pages/ERC721/ERC721.tsx | 117 +++++++++++++++ .../cactus-cmd-gui-app/src/pages/Pages.tsx | 45 ++++++ .../SingleTokenHistory.module.css | 6 + .../SingleTokenHistory/SingleTokenHistory.tsx | 116 +++++++++++++++ .../Transactions/Transactions.module.css | 12 ++ .../src/pages/Transactions/Transactions.tsx | 64 ++++++++ .../src/schema/supabase-types.ts | 103 +++++++++++++ .../src/schema/token-standards.ts | 4 + .../src/supabase-client.tsx | 7 + packages/cactus-cmd-gui-app/styles.d.ts | 4 + .../cactus-cmd-gui-app/tailwind.config.ts | 11 ++ packages/cactus-cmd-gui-app/tsconfig.json | 20 +++ packages/cactus-cmd-gui-app/vite.config.ts | 13 ++ tsconfig.json | 3 + 60 files changed, 2358 insertions(+) create mode 100644 packages/cactus-cmd-gui-app/README.md create mode 100644 packages/cactus-cmd-gui-app/index.html create mode 100644 packages/cactus-cmd-gui-app/package.json create mode 100644 packages/cactus-cmd-gui-app/postcss.config.js create mode 100644 packages/cactus-cmd-gui-app/src/App.module.css create mode 100644 packages/cactus-cmd-gui-app/src/App.tsx create mode 100644 packages/cactus-cmd-gui-app/src/assets/favicon.ico create mode 100644 packages/cactus-cmd-gui-app/src/components/AccountCard/AccountCard.module.css create mode 100644 packages/cactus-cmd-gui-app/src/components/AccountCard/AccountCard.tsx create mode 100644 packages/cactus-cmd-gui-app/src/components/BlockCard/BlockCard.module.css create mode 100644 packages/cactus-cmd-gui-app/src/components/BlockCard/BlockCard.tsx create mode 100644 packages/cactus-cmd-gui-app/src/components/CardWrapper/CardWrapper.module.css create mode 100644 packages/cactus-cmd-gui-app/src/components/CardWrapper/CardWrapper.tsx create mode 100644 packages/cactus-cmd-gui-app/src/components/Chart/Chart.module.css create mode 100644 packages/cactus-cmd-gui-app/src/components/Chart/Chart.tsx create mode 100644 packages/cactus-cmd-gui-app/src/components/Chart/LineChart.tsx create mode 100644 packages/cactus-cmd-gui-app/src/components/Pagination/Pagination.module.css create mode 100644 packages/cactus-cmd-gui-app/src/components/Pagination/Pagination.tsx create mode 100644 packages/cactus-cmd-gui-app/src/components/TokenHeader/TokenAccount.tsx create mode 100644 packages/cactus-cmd-gui-app/src/components/TokenHeader/TokenHeader.module.css create mode 100644 packages/cactus-cmd-gui-app/src/components/TokenHeader/TokenHeader.tsx create mode 100644 packages/cactus-cmd-gui-app/src/components/UI/Button/Button.module.css create mode 100644 packages/cactus-cmd-gui-app/src/components/UI/Button/Button.tsx create mode 100644 packages/cactus-cmd-gui-app/src/components/UI/CustomTable/CustomTable.css create mode 100644 packages/cactus-cmd-gui-app/src/components/UI/CustomTable/CustomTable.tsx create mode 100644 packages/cactus-cmd-gui-app/src/components/UI/Menu/Menu.module.css create mode 100644 packages/cactus-cmd-gui-app/src/components/UI/Menu/Menu.tsx create mode 100644 packages/cactus-cmd-gui-app/src/components/UI/Search/Search.module.css create mode 100644 packages/cactus-cmd-gui-app/src/components/UI/Search/Search.tsx create mode 100644 packages/cactus-cmd-gui-app/src/index.css create mode 100644 packages/cactus-cmd-gui-app/src/index.tsx create mode 100644 packages/cactus-cmd-gui-app/src/logo.svg create mode 100644 packages/cactus-cmd-gui-app/src/pages/Accounts/Accounts.module.css create mode 100644 packages/cactus-cmd-gui-app/src/pages/Accounts/Accounts.tsx create mode 100644 packages/cactus-cmd-gui-app/src/pages/Blocks/Blocks.module.css create mode 100644 packages/cactus-cmd-gui-app/src/pages/Blocks/Blocks.tsx create mode 100644 packages/cactus-cmd-gui-app/src/pages/Dashboard/Dashboard.module.css create mode 100644 packages/cactus-cmd-gui-app/src/pages/Dashboard/Dashboard.tsx create mode 100644 packages/cactus-cmd-gui-app/src/pages/Details/BlockDetails.tsx create mode 100644 packages/cactus-cmd-gui-app/src/pages/Details/Details.module.css create mode 100644 packages/cactus-cmd-gui-app/src/pages/Details/TokenDetails.tsx create mode 100644 packages/cactus-cmd-gui-app/src/pages/Details/TokenTransactionDetails.tsx create mode 100644 packages/cactus-cmd-gui-app/src/pages/Details/TransactionDetails.tsx create mode 100644 packages/cactus-cmd-gui-app/src/pages/ERC20/ERC20.module.css create mode 100644 packages/cactus-cmd-gui-app/src/pages/ERC20/ERC20.tsx create mode 100644 packages/cactus-cmd-gui-app/src/pages/ERC721/ERC721.module.css create mode 100644 packages/cactus-cmd-gui-app/src/pages/ERC721/ERC721.tsx create mode 100644 packages/cactus-cmd-gui-app/src/pages/Pages.tsx create mode 100644 packages/cactus-cmd-gui-app/src/pages/SingleTokenHistory/SingleTokenHistory.module.css create mode 100644 packages/cactus-cmd-gui-app/src/pages/SingleTokenHistory/SingleTokenHistory.tsx create mode 100644 packages/cactus-cmd-gui-app/src/pages/Transactions/Transactions.module.css create mode 100644 packages/cactus-cmd-gui-app/src/pages/Transactions/Transactions.tsx create mode 100644 packages/cactus-cmd-gui-app/src/schema/supabase-types.ts create mode 100644 packages/cactus-cmd-gui-app/src/schema/token-standards.ts create mode 100644 packages/cactus-cmd-gui-app/src/supabase-client.tsx create mode 100644 packages/cactus-cmd-gui-app/styles.d.ts create mode 100644 packages/cactus-cmd-gui-app/tailwind.config.ts create mode 100644 packages/cactus-cmd-gui-app/tsconfig.json create mode 100644 packages/cactus-cmd-gui-app/vite.config.ts diff --git a/packages/cactus-cmd-gui-app/README.md b/packages/cactus-cmd-gui-app/README.md new file mode 100644 index 0000000000..52fccdcfb7 --- /dev/null +++ b/packages/cactus-cmd-gui-app/README.md @@ -0,0 +1,48 @@ +# `@hyperledger/cactus-cmd-gui-app` + +This component allows viewing ledger data in Supabase. The data is fed to supabase by persistence plugins for each ledgers. + +## Summary + +- [Remarks](#remarks) +- [Getting Started](#getting-started) +- [Contributing](#contributing) +- [License](#license) +- [Acknowledgments](#acknowledgments) + +## Remarks + +- Plugin requires running Supabase and persistence plugins in order to properly view ledger data. +- Currently, only ethereum based ledgers are supported. + +## Getting Started + +Clone the git repository on your local machine. Follow these instructions that will get you a copy of the project up and running on your local machine for development and testing purposes. + +### Prerequisites + +In the root of the project, execute the command to install and build the dependencies. It will also build this GUI front-end component: + +```sh +yarn run build +``` + +### Usage +- Run Supabase instance (see documentation for detailed instructions). For development purposes, you can use our image located in `tools/docker/supabase-all-in-one`. +- Run one or more persistence plugins: + - [Ethereum](../cactus-plugin-persistence-ethereum) +- Edit [Supabase configuration file](./src/supabase-client.tsx), set correct supabase API URL and service_role key. +- Execute `yarn run start` in this package directory. +- The running application address: http://localhost:3001/ (can be changed in [Vite configuration](./vite.config.ts)) + +## Contributing + +We welcome contributions to Hyperledger Cactus in many forms, and there’s always plenty to do! + +Please review [CONTIRBUTING.md](../../CONTRIBUTING.md) to get started. + +## License + +This distribution is published under the Apache License Version 2.0 found in the [LICENSE](../../LICENSE) file. + +## Acknowledgments \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/index.html b/packages/cactus-cmd-gui-app/index.html new file mode 100644 index 0000000000..6da7c5535b --- /dev/null +++ b/packages/cactus-cmd-gui-app/index.html @@ -0,0 +1,16 @@ + + + + + + + + Cacti GUI + + + +
+ + + + diff --git a/packages/cactus-cmd-gui-app/package.json b/packages/cactus-cmd-gui-app/package.json new file mode 100644 index 0000000000..f2ef238cf7 --- /dev/null +++ b/packages/cactus-cmd-gui-app/package.json @@ -0,0 +1,76 @@ +{ + "name": "@hyperledger/cactus-cmd-gui-app", + "version": "1.1.3", + "description": "Cactus GUI for visualizing ledger data.", + "keywords": [ + "Hyperledger", + "Cactus", + "Integration", + "Blockchain", + "Distributed Ledger Technology" + ], + "homepage": "https://github.com/hyperledger/cactus#readme", + "bugs": { + "url": "https://github.com/hyperledger/cactus/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/hyperledger/cactus.git" + }, + "license": "Apache-2.0", + "author": { + "name": "Hyperledger Cactus Contributors", + "email": "cactus@lists.hyperledger.org", + "url": "https://www.hyperledger.org/use/cactus" + }, + "contributors": [ + { + "name": "Please add yourself to the list of contributors", + "email": "your.name@example.com", + "url": "https://example.com" + }, + { + "name": "Eryk Baranowski", + "email": "eryk.baranowski@fujitsu.com", + "url": "https://www.fujitsu.com/global/" + }, + { + "name": "Barnaba Pawelczak", + "email": "barnaba.pawelczak@fujitsu.com", + "url": "https://www.fujitsu.com/global/" + } + ], + "scripts": { + "start": "vite", + "serve": "vite preview", + "build": "yarn run build:prod:frontend", + "build:prod:frontend": "vite build" + }, + "devDependencies": { + "autoprefixer": "10.4.8", + "postcss": "8.4.16", + "supabase": "1.28.4", + "typescript-plugin-css-modules": "4.1.1", + "vite": "3.0.0", + "vite-plugin-solid": "2.3.0" + }, + "dependencies": { + "@solidjs/router": "0.4.2", + "@supabase/supabase-js": "1.35.6", + "apexcharts": "3.36.0", + "chart.js": "3.9.1", + "moment": "2.29.4", + "solid-apexcharts": "0.1.6", + "solid-icons": "1.0.4", + "solid-js": "1.5.7", + "solid-slider": "1.3.9", + "typescript": "4.9.4" + }, + "engines": { + "npm": ">=6" + }, + "publishConfig": { + "access": "public" + }, + "watch": {} +} diff --git a/packages/cactus-cmd-gui-app/postcss.config.js b/packages/cactus-cmd-gui-app/postcss.config.js new file mode 100644 index 0000000000..a47ef4f952 --- /dev/null +++ b/packages/cactus-cmd-gui-app/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + autoprefixer: {}, + }, +}; diff --git a/packages/cactus-cmd-gui-app/src/App.module.css b/packages/cactus-cmd-gui-app/src/App.module.css new file mode 100644 index 0000000000..2ed9ee38c6 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/App.module.css @@ -0,0 +1,6 @@ +.main { + width: 100%; +} +.content { + margin: 1rem 2rem; +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/App.tsx b/packages/cactus-cmd-gui-app/src/App.tsx new file mode 100644 index 0000000000..a810c8f041 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/App.tsx @@ -0,0 +1,18 @@ +import { Component } from "solid-js"; +import Pages from "./pages/Pages"; +import Menu from "./components/UI/Menu/Menu"; +// @ts-expect-error +import styles from "./App.module.css"; + +const App: Component = () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default App; diff --git a/packages/cactus-cmd-gui-app/src/assets/favicon.ico b/packages/cactus-cmd-gui-app/src/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b836b2bccac650e0e7d90514083add91d2c027ff GIT binary patch literal 15086 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 WLaxim%Fn{v*G = (props) => { + const params = useParams(); + const navigate = useNavigate(); + const handleClick = () => { + navigate(`/${params.standard}/${props.address}`); + }; + return ( +
+ {props.address} +
+ ); +}; + +export default AccountCard; diff --git a/packages/cactus-cmd-gui-app/src/components/BlockCard/BlockCard.module.css b/packages/cactus-cmd-gui-app/src/components/BlockCard/BlockCard.module.css new file mode 100644 index 0000000000..3e90ee76fa --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/BlockCard/BlockCard.module.css @@ -0,0 +1,33 @@ +.block-card { + display: flex; + gap: 1rem; + background-color: rgb(252, 249, 249); + height: 5rem; + align-items: center; + justify-content: space-around; + width: 100%; + padding: 1rem 0rem; + margin-top: 5px; + border-radius: 10px; + border: .5px solid rgb(242, 245, 242); + max-height: 100vh; + font-size: 14px; +} + +.block-card:hover { + cursor: pointer; + background-color: #ffffff; +} + +.block-num { + color: rgb(12, 105, 12); +} + +.block-hash { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: min-content; + max-width: 50%; +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/components/BlockCard/BlockCard.tsx b/packages/cactus-cmd-gui-app/src/components/BlockCard/BlockCard.tsx new file mode 100644 index 0000000000..bf6908b249 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/BlockCard/BlockCard.tsx @@ -0,0 +1,28 @@ +import { useNavigate } from "@solidjs/router"; +import { HiSolidHashtag } from "solid-icons/hi"; +import { Component } from "solid-js"; +// @ts-expect-error +import styles from "./BlockCard.module.css"; + +const BlockCard: Component<{ + number: string; + created_at: Date; + hash: string; +}> = (props) => { + const navigate = useNavigate(); + const handleClick = () => { + navigate(`/blockDetails/${props.number}`); + }; + + return ( +
+ {props.created_at.toLocaleString()} + {props.number} + + {props.hash} + +
+ ); +}; + +export default BlockCard; diff --git a/packages/cactus-cmd-gui-app/src/components/CardWrapper/CardWrapper.module.css b/packages/cactus-cmd-gui-app/src/components/CardWrapper/CardWrapper.module.css new file mode 100644 index 0000000000..f5f2b7fa97 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/CardWrapper/CardWrapper.module.css @@ -0,0 +1,56 @@ +.wrapper { + background-color: rgb(253, 253, 253); + padding: 1rem; + border-radius: 10px; + border: 1px solid rgb(233, 236, 233); + height: fit-content; +} + +.wrapper-cards { + width: 100%; + display: flex; + justify-content: center; + padding: 1rem; +} + +.wrapper-title { + display: inline-block; + font-weight: 700; + margin-left: 1rem; + margin-top: .7rem; + font-size: 1.2rem; + color: rgb(9, 75, 9); +} + +.wrapper-btns { + display: flex; + justify-content: flex-end; + padding-right: 1rem; +} + +.wrapper-header { + width: 100%; + display: flex; + justify-content: space-between; + padding: 0 1rem; +} + +.wrapper-columns { + display: flex; + justify-content: space-around; + background-color: rgb(243, 239, 239); + align-items: center; + border-radius: 10px; + border: 1px solid rgb(233, 236, 233); + height: 50px; +} + +.wrapper-columns span { + display: flex; + width: 150px; +} + +.wrapper-search { + display: flex; + gap: 5px; +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/components/CardWrapper/CardWrapper.tsx b/packages/cactus-cmd-gui-app/src/components/CardWrapper/CardWrapper.tsx new file mode 100644 index 0000000000..0e7ce28d43 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/CardWrapper/CardWrapper.tsx @@ -0,0 +1,140 @@ +import { createSignal, createEffect, ParentComponent } from "solid-js"; +import Button from "../UI/Button/Button"; +// @ts-expect-error +import styles from "./CardWrapper.module.css"; +import { TbCactus } from "solid-icons/tb"; +import { useNavigate } from "@solidjs/router"; +import Search from "../UI/Search/Search"; +import CustomTable from "../UI/CustomTable/CustomTable"; +import { TableProps } from "../../schema/supabase-types"; +import Pagination from "../Pagination/Pagination"; + +type cardWrapperProp = { + filters?: string[]; + data: any[]; + display: string; + trimmed?: boolean; + columns?: TableProps; + title: string; + getSearchValue?: (val: string) => {}; +}; + +const pageSize: number = 2; + +const CardWrapper: ParentComponent = (props) => { + const navigate = useNavigate(); + const [searchKey, setSearchKey] = createSignal(""); + const [filteredData, setFilteredData] = createSignal([]); + const [paginatedData, setPaginatedData] = createSignal([]); + const [currentPage, setCurrentPage] = createSignal(1); + const [totalPages, setTotalPages] = createSignal(1); + + const handleGoToPage = (pageNumber: number) => { + if (pageNumber < 1 || pageNumber > totalPages()) return; + setCurrentPage(pageNumber); + }; + + const handleNextPage = () => { + if (currentPage() === totalPages()) return; + setCurrentPage((prev) => prev + 1); + }; + + const handlePrevPage = () => { + if (currentPage() === 1) return; + setCurrentPage((prev) => prev - 1); + }; + + const filterData = () => { + const { filters, data } = props; + if (searchKey().length === 0) { + setFilteredData(data); + return; + } + let newData = data.filter((row) => { + let isMatch: boolean = false; + filters?.forEach((property) => { + if (row[property].toString().toLowerCase().includes(searchKey())) { + isMatch = true; + } + }); + return isMatch; + }); + setFilteredData(newData); + }; + + const handleSearch = () => { + filterData(); + if (props.getSearchValue) { + props.getSearchValue(searchKey()); + } + }; + + createEffect(() => { + setFilteredData(props.data); + }); + + createEffect(() => { + if (filteredData().length <= pageSize) { + setPaginatedData(filteredData()); + } else { + const firstEl = currentPage() * pageSize - pageSize; + setPaginatedData(filteredData().slice(firstEl, firstEl + pageSize)); + } + }); + + createEffect(() => { + const pageNum = Math.ceil(filteredData().length / pageSize); + setTotalPages(pageNum); + }); + + return ( +
+
+ + {props.title} + + {props.filters && ( +
+ setSearchKey(e)} + type="text" + placeholder="Type to search" + /> + +
+ )} +
+
+ {props?.columns && props.data?.length > 0 && ( + + )} + {props.children} +
+
+ {" "} + {props.trimmed && ( + + )} +
+ {!props.trimmed && ( + + )} +
+ ); +}; + +export default CardWrapper; diff --git a/packages/cactus-cmd-gui-app/src/components/Chart/Chart.module.css b/packages/cactus-cmd-gui-app/src/components/Chart/Chart.module.css new file mode 100644 index 0000000000..e0c1d37be2 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/Chart/Chart.module.css @@ -0,0 +1,22 @@ +.chart-wrapper { + width: 100%; + height:fit-content; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: rgb(253, 253, 253); + border-radius: 10px; + border: 1px solid rgb(238, 238, 238); +} +.chart-wrapper-line{ + padding:1rem; + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: rgb(253, 253, 253); + border-radius: 10px; + border: 1px solid rgb(238, 238, 238); +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/components/Chart/Chart.tsx b/packages/cactus-cmd-gui-app/src/components/Chart/Chart.tsx new file mode 100644 index 0000000000..bc3f6d7fd0 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/Chart/Chart.tsx @@ -0,0 +1,66 @@ +import { SolidApexCharts } from "solid-apexcharts"; +import { createSignal, createEffect, Component } from "solid-js"; +// @ts-expect-error +import styles from "./Chart.module.css"; +import { ERC20Txn } from "../../schema/supabase-types"; + +const Chart: Component<{ chartData: any }> = (props) => { + const [chartProps, setChartProps] = createSignal<{ + series: any; + options: any; + }>({ + series: { + list: [ + { + name: "balance", + data: [], + }, + ], + }, + options: { + chart: { + id: "solidchart-example", + }, + xaxis: { + type: "datetime", + }, + }, + }); + + createEffect(async () => { + const { chartData } = props; + + setChartProps({ + options: { + chart: { + id: "solidchart-example", + }, + xaxis: { + categories: chartData()?.map((txn: ERC20Txn) => txn.token_address), + }, + }, + series: { + list: [ + { + name: "balance", + data: chartData()?.map((txn: ERC20Txn) => txn.balance), + }, + ], + }, + }); + }); + + return ( +
+ Balance + +
+ ); +}; + +export default Chart; diff --git a/packages/cactus-cmd-gui-app/src/components/Chart/LineChart.tsx b/packages/cactus-cmd-gui-app/src/components/Chart/LineChart.tsx new file mode 100644 index 0000000000..02904bd7bb --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/Chart/LineChart.tsx @@ -0,0 +1,80 @@ +import { SolidApexCharts } from "solid-apexcharts"; +import { createSignal, createEffect, Component } from "solid-js"; +// @ts-expect-error +import styles from "./Chart.module.css"; +import moment from "moment"; +import { balanceDate } from "../../schema/supabase-types"; + +const LineChart: Component<{ chartData: any }> = (props) => { + const [chartProps, setChartProps] = createSignal({ + series: { + list: [ + { + name: "balance", + data: [], + }, + ], + }, + options: { + chart: { + id: "solidchart-example", + }, + xaxis: { + categories: [], + }, + }, + }); + + createEffect(async () => { + const { chartData } = props; + + setChartProps({ + options: { + chart: { + id: "solidchart-example", + }, + stroke: { + curve: "stepline", + }, + tooltip: { + x: { + show: true, + format: "dd MM yyyy h:mm", + formatter: undefined, + }, + }, + xaxis: { + type: "datetime", + categories: chartData()?.map((txn: balanceDate) => + moment(txn.created_at).format("YYYY-MM-DD h:mm:ss a"), + ), + labels: { + format: "dd MM yyyy h:mm", + }, + }, + }, + series: { + list: [ + { + name: "balance", + data: chartData()?.map((txn: balanceDate) => txn.balance), + }, + ], + }, + }); + }); + + return ( +
+ +
+ ); +}; + +export default LineChart; diff --git a/packages/cactus-cmd-gui-app/src/components/Pagination/Pagination.module.css b/packages/cactus-cmd-gui-app/src/components/Pagination/Pagination.module.css new file mode 100644 index 0000000000..2f371c7a9b --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/Pagination/Pagination.module.css @@ -0,0 +1,34 @@ +.pagination { + width: 100%; + padding: 1rem; + justify-content: flex-end; + display: flex; + align-items: center; + gap: 10px; +} + +.pagination-counter { + height: 2.5rem; + display: flex; + align-items: center; + padding: 0 1rem; + border-radius: 10px; + border: 1px solid rgb(204, 206, 205); +} + +.pagination-jump { + display: flex; + gap: 10px; + padding: 9px 1rem; + background-color: rgb(233, 229, 229); + border-radius: 10px; +} + +input { + border-radius: 10px; + border: 1px solid rgb(54, 51, 224); + padding: 0 .5rem; + width: 7rem; + text-align: center; + font-size: 1rem; +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/components/Pagination/Pagination.tsx b/packages/cactus-cmd-gui-app/src/components/Pagination/Pagination.tsx new file mode 100644 index 0000000000..bb772ac1b0 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/Pagination/Pagination.tsx @@ -0,0 +1,66 @@ +// @ts-expect-error +import styles from "./Pagination.module.css"; +import Button from "../UI/Button/Button"; +import { + FaSolidAngleRight, + FaSolidAngleLeft, + FaSolidAnglesLeft, + FaSolidAnglesRight, +} from "solid-icons/fa"; +import { Component, createSignal } from "solid-js"; + +type pagination = { + current: number; + total: number; + goToPage: (pageNumber: number) => void; + goNextPage: () => void; + goPrevPage: () => void; +}; + +const Pagination: Component = (props) => { + let inputRef: any; + const getInputValue = () => + inputRef?.value ? inputRef.value : props.current; + const [goToPageVisible, setGoToPageVisible] = createSignal(false); + + return ( +
+ + + + {goToPageVisible() === true && ( +
+ + +
+ )} + + +
+ ); +}; + +export default Pagination; diff --git a/packages/cactus-cmd-gui-app/src/components/TokenHeader/TokenAccount.tsx b/packages/cactus-cmd-gui-app/src/components/TokenHeader/TokenAccount.tsx new file mode 100644 index 0000000000..9cf09912cd --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/TokenHeader/TokenAccount.tsx @@ -0,0 +1,20 @@ +// @ts-expect-error +import styles from "./TokenHeader.module.css"; +import { BiRegularWallet } from "solid-icons/bi"; +import { Component } from "solid-js"; + +const TokenAccount: Component<{ accountNum: string }> = (props) => { + return ( +
+ + {" "} + {" "} + {props.accountNum} + +
+ ); +}; + +export default TokenAccount; diff --git a/packages/cactus-cmd-gui-app/src/components/TokenHeader/TokenHeader.module.css b/packages/cactus-cmd-gui-app/src/components/TokenHeader/TokenHeader.module.css new file mode 100644 index 0000000000..ab9937325a --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/TokenHeader/TokenHeader.module.css @@ -0,0 +1,58 @@ +.token-header { + display: flex; + height: 5rem; + width: 100%; + gap: 1rem; +} + +.token-details { + width: 70%; + height: 100%; + border: 1px solid rgb(240, 236, 236); + border-radius: 10px; + gap: 3rem; + padding: .25rem 2rem; + display: flex; + justify-content: flex-start; + align-items: center; + background-color: rgb(247, 245, 245); +} + +.token-details div { + display: flex; + align-items: center; + gap: 1rem; +} + +.token-icon { + font-size: 2.5rem; + height: 100%; + transform: translateY(10%); + color: rgb(34, 70, 70); +} + +.token-account { + font-size: 18px; + width: min-content; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background-color: rgb(247, 245, 245); + border-radius: 10px; + padding: 1rem; + padding-left: 2rem; +} + +.token-account span { + display: flex; + align-items: center; + gap: .5rem; +} + +.token-account-icon { + color: rgb(22, 92, 65); + font-size: 28px; + height: 30px; + width: 30px; +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/components/TokenHeader/TokenHeader.tsx b/packages/cactus-cmd-gui-app/src/components/TokenHeader/TokenHeader.tsx new file mode 100644 index 0000000000..f13726c553 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/TokenHeader/TokenHeader.tsx @@ -0,0 +1,52 @@ +// @ts-expect-error +import styles from "./TokenHeader.module.css"; +import TokenAccount from './TokenAccount' +import { Component } from 'solid-js' +import { createSignal, createEffect } from 'solid-js' +import { TokenMetadata20 } from '../../schema/supabase-types' +import { supabase } from '../../supabase-client' + +const TokenHeader: Component<{ accountNum: string; token_address: string }> = ( + props, +) => { + const [tokenData, setTokenData] = createSignal(); + + createEffect(async () => { + try { + const { data, error } = await supabase + .from(`token_metadata_erc20`) + .select("*") + .match({ address: props.token_address }); + console.log(data); + if (data?.[0]) { + setTokenData(data[0]); + } else { + throw new Error("Failed to load token details"); + } + } catch (error: any) { + console.error(error.message); + } + }, []); + + return ( +
+
+ + Address: {props.token_address} + + + Created at: {tokenData()?.created_at} + + + Total supply: + {tokenData()?.total_supply} + +
+ +
+ ); +}; + +export default TokenHeader; + + diff --git a/packages/cactus-cmd-gui-app/src/components/UI/Button/Button.module.css b/packages/cactus-cmd-gui-app/src/components/UI/Button/Button.module.css new file mode 100644 index 0000000000..33879c4e41 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/UI/Button/Button.module.css @@ -0,0 +1,67 @@ +.button { + color: rgb(102, 117, 106); + background-color: rgb(248, 248, 250); + height: 2.5rem; + display: flex; + align-items: center; + justify-content: center; + width: max-content; + min-width: 100px; + padding: 10px; + border: 1px solid rgb(32, 133, 77); + font-family: 'Roboto'; + border-radius: 10px; +} + +.button:hover { + background-color: rgb(219, 219, 224); + transform: scale(1.01); + cursor: pointer; +} + +.button-primary { + background-color: rgb(244, 247, 245); + color:rgb(30, 75, 30); + border-radius: 5px; + width:150px; +} + +.button-primary:hover { background-color: rgb(226, 253, 219);} + +.button-warn { background-color: rgb(155, 22, 13);} + +.button-warn:hover { background-color: rgb(114, 22, 16);} + +.button-menu{ + border:none; + background: transparent; + height: 100%; + transition: background-color 0.5s ease-out; + position:relative; +} +.button-menu:hover{ + background-color: rgb(224, 225, 228); +} +.button-menu:hover:after { + content: ''; + display: block; + position: absolute; + left: 0; + right: 0; + bottom: 1px; + width: 100%; + height: 1px; + border-bottom: 1px solid green; +} + +.button-link { + background: transparent; + border:none; + height: min-content; + color:rgb(64, 64, 228); +} + +.button-link:hover{ + background: transparent; + color:rgb(78, 78, 236); +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/components/UI/Button/Button.tsx b/packages/cactus-cmd-gui-app/src/components/UI/Button/Button.tsx new file mode 100644 index 0000000000..c69ad06233 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/UI/Button/Button.tsx @@ -0,0 +1,28 @@ +import { ParentComponent } from "solid-js"; +// @ts-expect-error +import styles from "./Button.module.css"; + +const Button: ParentComponent<{ + type?: string; + onClick: () => void; + disabled?: boolean; +}> = (props) => { + type ObjectKey = keyof typeof styles; + const buttonTypeStyle = `button-${props.type}` as ObjectKey; + + const handleClick = (e: MouseEvent) => { + e.stopPropagation(); + props.onClick(); + }; + + return ( + + ); +}; + +export default Button; diff --git a/packages/cactus-cmd-gui-app/src/components/UI/CustomTable/CustomTable.css b/packages/cactus-cmd-gui-app/src/components/UI/CustomTable/CustomTable.css new file mode 100644 index 0000000000..d94c8789f0 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/UI/CustomTable/CustomTable.css @@ -0,0 +1,53 @@ +table { + border-collapse: separate; + border-spacing: 0; + width: 100%; + } + + tbody tr { + background-color: rgb(248, 248, 248); + border: 1px solid rgb(219, 241, 232); + border-radius: 10px; + } + + tbody tr:hover { + cursor: pointer; + background-color: rgb(235, 240, 237); + } + + th { + background-color: rgb(240, 235, 235); + border-style: none; + border-bottom: solid 1px rgb(223, 218, 218); + padding: 10px; + } + + td { + min-height: 2rem; + border-style: none; + border-bottom: solid 4px rgb(255, 255, 255); + padding: 1.5rem .5rem; + text-align: center; + } + + tr { + min-height: 20rem; + background-color: rgb(90, 103, 116); + padding: 1rem; + } + + tr:first-child th:first-child { + border-top-left-radius: 10px; + } + + tr:first-child th:last-child { + border-top-right-radius: 10px; + } + + tr:last-child td:first-child { + border-bottom-left-radius: 10px; + } + + tr:last-child td:last-child { + border-bottom-right-radius: 10px; + } \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/components/UI/CustomTable/CustomTable.tsx b/packages/cactus-cmd-gui-app/src/components/UI/CustomTable/CustomTable.tsx new file mode 100644 index 0000000000..a171762032 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/UI/CustomTable/CustomTable.tsx @@ -0,0 +1,47 @@ +import { Component } from "solid-js"; +import "./CustomTable.css"; +import { TableProps, TableProperty } from "../../../schema/supabase-types"; + +const CustomTable: Component<{ cols: TableProps; data: any[] }> = (props) => { + const getObjPropVal = (objProp: string[], row: any) => { + if (objProp.length === 1) return row[objProp[0]]; + else { + return objProp.map((prop) => ( + <> + {row[prop]} +

+ + )); + } + }; + const handleRowClick = (row: any) => { + props.cols.onClick.action(row[props.cols.onClick.prop]); + }; + + return ( + + + + {props.cols.schema.map((col, idx) => ( + + ))} + + + + {props.data.map((row) => { + return ( + + {props.cols.schema.map((col: TableProperty, idx) => ( + + ))} + + ); + })} + +
{col.display}
handleRowClick(row)}> + {getObjPropVal(col.objProp, row)} +
+ ); +}; + +export default CustomTable; diff --git a/packages/cactus-cmd-gui-app/src/components/UI/Menu/Menu.module.css b/packages/cactus-cmd-gui-app/src/components/UI/Menu/Menu.module.css new file mode 100644 index 0000000000..e239d0e5ea --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/UI/Menu/Menu.module.css @@ -0,0 +1,21 @@ +.navigation { + display: flex; + gap: 1rem; + width: 100%; + justify-content: flex-start; + padding-left: 8rem; + height: 4rem; + background-color: rgb(245, 243, 243); +} + +.logo-wrapper { + display: flex; + justify-content: center; + align-items: center; + margin-right:3rem; +} + +.logo { + height: 70%; + filter: invert(28%) sepia(14%) saturate(2925%) hue-rotate(69deg) brightness(97%) contrast(89%); +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/components/UI/Menu/Menu.tsx b/packages/cactus-cmd-gui-app/src/components/UI/Menu/Menu.tsx new file mode 100644 index 0000000000..fe5a7709ce --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/UI/Menu/Menu.tsx @@ -0,0 +1,23 @@ +import Button from "../Button/Button"; +import { useNavigate } from "@solidjs/router"; +// @ts-expect-error +import styles from "./Menu.module.css"; + +const Menu = () => { + const navigate = useNavigate(); + return ( + + ); +}; + +export default Menu; diff --git a/packages/cactus-cmd-gui-app/src/components/UI/Search/Search.module.css b/packages/cactus-cmd-gui-app/src/components/UI/Search/Search.module.css new file mode 100644 index 0000000000..3d7bf2b7cc --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/UI/Search/Search.module.css @@ -0,0 +1,31 @@ +.input { + border: none; + border-radius: 10px; + width: 30rem; + height: 2.5rem; + background-color: rgb(240, 236, 236); + padding: 1rem; + font-size: 16px; +} + +.input-wrapper { + position: relative; +} + +.input-reset { + background: transparent; + position: absolute; + right: .5rem; + border: none; + height: 2.5rem; + width: 2.5rem; + font-size: 1.5rem; + cursor: pointer; +} + +.input-reset:hover { + color: green; +} +.input-reset-icon { + pointer-events: none; +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/components/UI/Search/Search.tsx b/packages/cactus-cmd-gui-app/src/components/UI/Search/Search.tsx new file mode 100644 index 0000000000..054d9bdb0d --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/components/UI/Search/Search.tsx @@ -0,0 +1,47 @@ +// @ts-expect-error +import styles from "./Search.module.css"; +import { Component, createSignal } from "solid-js"; +import { BiRegularReset } from "solid-icons/bi"; + +const Search: Component<{ + type: string; + onKeyUp: (value: string) => void; + placeholder: string; +}> = (props) => { + const [val, setValue] = createSignal(""); + + const handleInput = (e: InputEvent | ClipboardEvent) => { + const inputValue = (e.currentTarget as HTMLInputElement).value; + console.log(inputValue); + if (inputValue) { + setValue(inputValue); + props.onKeyUp(inputValue); + } + }; + + const handleReset = () => { + setValue(""); + props.onKeyUp(""); + }; + + return ( +
+ handleInput(e)} + onPaste={(e) => handleInput(e)} + /> + +
+ ); +}; + +export default Search; diff --git a/packages/cactus-cmd-gui-app/src/index.css b/packages/cactus-cmd-gui-app/src/index.css new file mode 100644 index 0000000000..f20b719bef --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/index.css @@ -0,0 +1,13 @@ +@import url('https://fonts.googleapis.com/css?family=Roboto'); +*{ + box-sizing: border-box; +} + +body { + margin: 0; + font-family: 'Roboto'; +} + +h1,h2,h3,h4,h5 { + color:rgb(36, 143, 36); +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/index.tsx b/packages/cactus-cmd-gui-app/src/index.tsx new file mode 100644 index 0000000000..388b856121 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/index.tsx @@ -0,0 +1,14 @@ +/* @refresh reload */ +import { render } from "solid-js/web"; +import { Router } from "@solidjs/router"; +import "./index.css"; +import App from "./App"; + +render( + () => ( + + + + ), + document.getElementById("root")!, +); diff --git a/packages/cactus-cmd-gui-app/src/logo.svg b/packages/cactus-cmd-gui-app/src/logo.svg new file mode 100644 index 0000000000..025aa303c5 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/pages/Accounts/Accounts.module.css b/packages/cactus-cmd-gui-app/src/pages/Accounts/Accounts.module.css new file mode 100644 index 0000000000..43c52de4a5 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/Accounts/Accounts.module.css @@ -0,0 +1,6 @@ +.accounts-wrapper{ + width:100%; + display:grid; + grid-template-columns: repeat(auto-fit, 35rem); + gap: 1rem; +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/pages/Accounts/Accounts.tsx b/packages/cactus-cmd-gui-app/src/pages/Accounts/Accounts.tsx new file mode 100644 index 0000000000..808b40ab9b --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/Accounts/Accounts.tsx @@ -0,0 +1,63 @@ +import { createSignal, createEffect } from "solid-js"; +import { useParams, useNavigate } from "@solidjs/router"; +import { supabase } from "../../supabase-client"; +import CardWrapper from "../../components/CardWrapper/CardWrapper"; + +const Accounts = () => { + const params = useParams(); + const navigate = useNavigate(); + const [accounts, setAccounts] = createSignal<{ address: string }[]>([]); + const [searchKey, setSearchKey] = createSignal(""); + + const tableProps = { + onClick: { + action: (param: string) => navigate(`/${params.standard}/${param}`), + prop: "address", + }, + schema: [ + { + display: "Account address", + objProp: ["address"], + }, + ], + }; + + const fetchAccounts = async () => { + try { + const { data, error } = await supabase + .from(`token_${params.standard.toLowerCase()}`) + .select("account_address"); + if (data) { + const objData = [ + ...new Set(data.map((el) => el.account_address)), + ].map((el) => ({ address: el })); + setAccounts(objData); + } + if (error) { + console.error(error.message); + } + } catch (error) { + console.error(error.message); + } + }; + + createEffect(async () => { + await fetchAccounts(); + }, []); + + return ( +
+ setSearchKey(e)} + > +
+ ); +}; + +export default Accounts; diff --git a/packages/cactus-cmd-gui-app/src/pages/Blocks/Blocks.module.css b/packages/cactus-cmd-gui-app/src/pages/Blocks/Blocks.module.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cactus-cmd-gui-app/src/pages/Blocks/Blocks.tsx b/packages/cactus-cmd-gui-app/src/pages/Blocks/Blocks.tsx new file mode 100644 index 0000000000..80cf5ef676 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/Blocks/Blocks.tsx @@ -0,0 +1,59 @@ +import { createSignal, createEffect } from "solid-js"; +import { useNavigate } from "@solidjs/router"; +import { supabase } from "../../supabase-client"; +// @ts-expect-error +import styles from "./Blocks.module.css"; +import CardWrapper from "../../components/CardWrapper/CardWrapper"; +import { Block } from "../../schema/supabase-types"; + +type ObjectKey = keyof typeof styles; + +const Blocks = () => { + const navigate = useNavigate(); + const [block, setBlock] = createSignal([]); + + const blocksTableProps = { + onClick: { + action: (param: string) => navigate(`/blockDetails/${param}`), + prop: "number", + }, + schema: [ + { display: "created at", objProp: ["created_at"] }, + { display: "block number", objProp: ["number"] }, + { display: "hash", objProp: ["hash"] }, + ], + }; + + const fetchBlock = async () => { + try { + const { data, error } = await supabase.from("block").select("*"); + if (data) { + setBlock(data); + } + if (error) { + console.error(error.message); + } + } catch (error) { + console.error(error.message); + } + }; + + createEffect(async () => { + await fetchBlock(); + }, []); + + return ( +
+ +
+ ); +}; + +export default Blocks; diff --git a/packages/cactus-cmd-gui-app/src/pages/Dashboard/Dashboard.module.css b/packages/cactus-cmd-gui-app/src/pages/Dashboard/Dashboard.module.css new file mode 100644 index 0000000000..7651dd81a5 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/Dashboard/Dashboard.module.css @@ -0,0 +1,4 @@ +.dashboard-wrapper { + display: flex; + gap: 1rem; +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/pages/Dashboard/Dashboard.tsx b/packages/cactus-cmd-gui-app/src/pages/Dashboard/Dashboard.tsx new file mode 100644 index 0000000000..8f2af05236 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/Dashboard/Dashboard.tsx @@ -0,0 +1,98 @@ +// @ts-expect-error +import styles from "./Dashboard.module.css"; +import { createSignal, createEffect, Show } from "solid-js"; +import { useNavigate, useParams } from "@solidjs/router"; +import { supabase } from "../../supabase-client"; +import CardWrapper from "../../components/CardWrapper/CardWrapper"; +import { Transaction } from "../../schema/supabase-types"; +import { Block } from "../../schema/supabase-types"; +const Dashboard = () => { + const navigate = useNavigate(); + const [transaction, setTransaction] = createSignal([]); + const [block, setBlock] = createSignal([]); + const txnTableProps = { + onClick: { + action: (param: string) => { + navigate(`/view/${param}`); + }, + prop: "id", + }, + schema: [ + { display: "transaction id", objProp: ["id"] }, + { display: "sender/recipient", objProp: ["from", "to"] }, + { display: "token value", objProp: ["eth_value"] }, + ], + }; + const blocksTableProps = { + onClick: { + action: (param: string) => { + navigate(`/blockDetails/${param}`); + }, + prop: "number", + }, + schema: [ + { display: "created at", objProp: ["created_at"] }, + { display: "block number", objProp: ["number"] }, + { display: "hash", objProp: ["hash"] }, + ], + }; + + const fetchTransactions = async () => { + try { + const { data, error } = await supabase.from("transaction").select("*"); + if (data) { + setTransaction(data); + } + if (error) { + console.error(error.message); + } + } catch (error) { + console.error(error.message); + } + }; + + const fetchBlock = async () => { + try { + const { data, error } = await supabase.from("block").select("*"); + if (data) { + setBlock(data); + } + if (error) { + console.error(error.message); + } + } catch (error) { + console.error(error.message); + } + }; + + createEffect(async () => { + await fetchBlock(); + }, []); + + createEffect(async () => { + await fetchTransactions(); + }, []); + + return ( +
+
+ + +
+
+ ); +}; + +export default Dashboard; diff --git a/packages/cactus-cmd-gui-app/src/pages/Details/BlockDetails.tsx b/packages/cactus-cmd-gui-app/src/pages/Details/BlockDetails.tsx new file mode 100644 index 0000000000..926ee57963 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/Details/BlockDetails.tsx @@ -0,0 +1,59 @@ +// @ts-expect-error +import styles from "./Details.module.css"; +import { createEffect, createSignal, Show } from "solid-js"; +import { useParams } from "@solidjs/router"; +import { supabase } from "../../supabase-client"; +import { Block } from "../../schema/supabase-types"; + +const blockDetails = () => { + const [details, setDetails] = createSignal({}); + const params = useParams(); + + createEffect(async () => { + try { + const { data, error } = await supabase + .from("block") + .select("*") + .match({ number: params.number }); + if (data?.[0]) { + setDetails(data[0]); + } else { + throw new Error("Failed to load block details"); + } + } catch (error) { + console.error(error.message); + } + }, []); + + return ( +
+
+ Failed to load details
}> +

Block Details

+ + Address: {details().number}{" "} + + + {" "} + Created at: + {details().created_at} + + + Hash: + {details().hash} + + + Number of transaction: + {details().number_of_tx} + + + Sync at: + {details().sync_at} + + +
+ + ); +}; + +export default blockDetails; diff --git a/packages/cactus-cmd-gui-app/src/pages/Details/Details.module.css b/packages/cactus-cmd-gui-app/src/pages/Details/Details.module.css new file mode 100644 index 0000000000..a5968af3d6 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/Details/Details.module.css @@ -0,0 +1,22 @@ +h1{ + padding: 0; + margin: 0; + margin-bottom: .5rem; +} +.details { + display: flex; + gap: .75rem; +} +.details-card{ + display: flex; + flex-direction: column; + gap: 15px; + border: 1px solid rgb(230, 224, 224); + border-radius: 10px; + padding: 1.5rem 2rem; + width:45%; +} +span { + display: inline-block; + font-size: 1.1rem; +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/pages/Details/TokenDetails.tsx b/packages/cactus-cmd-gui-app/src/pages/Details/TokenDetails.tsx new file mode 100644 index 0000000000..5dc1119982 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/Details/TokenDetails.tsx @@ -0,0 +1,59 @@ +// @ts-expect-error +import styles from "./Details.module.css"; +import { createEffect, createSignal } from "solid-js"; +import { useParams, useNavigate } from "@solidjs/router"; +import { supabase } from "../../supabase-client"; +import { STANDARDS } from "../../schema/token-standards"; +import { TokenMetadata20, TokenMetadata721 } from "../../schema/supabase-types"; +const TokenDetails = () => { + const [tokenData, setTokenData] = createSignal< + TokenMetadata20 | TokenMetadata721 | any + >(); + + const params = useParams(); + const navigate = useNavigate(); + + createEffect(async () => { + try { + const { data, error } = await supabase + .from(`token_metadata_${params.standard.toLowerCase()}`) + .select("*") + .match({ address: params.address }); + if (data?.[0]) { + setTokenData(data[0]); + } else { + throw new Error("Failed to load token details"); + } + } catch (error) { + console.error(error.message); + } + }, []); + + return ( +
+
+

Token Details

+ + Adress: {tokenData()?.address}{" "} + + + Created at: + {tokenData()?.created_at} + + + Name: + {tokenData()?.name} + + + Symbol: + {tokenData()?.symbol} + + {params.standard === STANDARDS.erc20 && ( + total_supply : {tokenData()?.total_supply} + )} +
+
+ ); +}; + +export default TokenDetails; diff --git a/packages/cactus-cmd-gui-app/src/pages/Details/TokenTransactionDetails.tsx b/packages/cactus-cmd-gui-app/src/pages/Details/TokenTransactionDetails.tsx new file mode 100644 index 0000000000..16c7b987e3 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/Details/TokenTransactionDetails.tsx @@ -0,0 +1,62 @@ +// @ts-expect-error +import styles from "./Details.module.css"; +import { createEffect, createSignal } from "solid-js"; +import { useParams } from "@solidjs/router"; +import { supabase } from "../../supabase-client"; +import { STANDARDS } from "../../schema/token-standards"; +import { ERC20Txn, ERC721Txn } from "../../schema/supabase-types"; + +const TokenTransactionDetails = () => { + const [txnData, setTxnData] = createSignal({}); + const params = useParams(); + + createEffect(async () => { + try { + const { data, error } = await supabase + .from(`token_${params.standard.toLowerCase()}`) + .select("*") + .match({ account_address: params.address }); + if (data?.[0]) { + setTxnData(data[0]); + } else { + throw new Error("Failed to load transaction details"); + } + } catch (error) { + console.error(error.message); + } + }, []); + + return ( +
+
+

Details of Transaction

+ + {" "} + Address: + {txnData()?.account_address}{" "} + + + {" "} + Created_at: + {txnData()?.token_address} + + {params.standard === STANDARDS.erc20 && ( + + {" "} + Balance: + {txnData()?.balance} + + )} + {params.standard === STANDARDS.erc721 && ( + + {" "} + Uri: + {txnData()?.uri} + + )} +
+
+ ); +}; + +export default TokenTransactionDetails; diff --git a/packages/cactus-cmd-gui-app/src/pages/Details/TransactionDetails.tsx b/packages/cactus-cmd-gui-app/src/pages/Details/TransactionDetails.tsx new file mode 100644 index 0000000000..3f444542ac --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/Details/TransactionDetails.tsx @@ -0,0 +1,100 @@ +import { createEffect, createSignal } from "solid-js"; +import { useParams, useNavigate } from "@solidjs/router"; +import { supabase } from "../../supabase-client"; +import CardWrapper from "../../components/CardWrapper/CardWrapper"; +// @ts-expect-error +import styles from "./Details.module.css"; +import { Transaction, TokenTransfer } from "../../schema/supabase-types"; + +const TransactionsDetails = () => { + const [details, setDetails] = createSignal({}); + const [transfers, setTransfers] = createSignal([]); + const params = useParams(); + + const detailsTableProps = { + onClick: { + action: () => {}, + prop: "id", + }, + schema: [ + { display: "transfer id", objProp: ["id"] }, + { display: "sender/recipient", objProp: ["sender", "recipient"] }, + { display: "value", objProp: ["value"] }, + ], + }; + + const fetchDetails = async () => { + try { + const { data, error } = await supabase + .from("transaction") + .select("*") + .match({ id: params.id }); + if (data?.[0]) { + setDetails(data[0]); + } else { + throw new Error("Failed to load transaction details"); + } + } catch (error) { + console.error(error.message); + } + }; + + const fetchTransfers = async () => { + try { + const { data, error } = await supabase + .from("token_transfer") + .select("*") + .match({ transaction_id: params.id }); + if (data) { + setTransfers(data); + } else { + throw new Error("Failed to load transfers"); + } + } catch (error) { + console.error(error.message); + } + }; + + createEffect(async () => { + await fetchDetails(); + await fetchTransfers(); + }, []); + + return ( +
+
+

Details of Transaction

+ + {" "} + Hash: {details().hash}{" "} + + + Block: + {details().block_number} + + + From: + {details().from} + + + To: + {details().to}{" "} + + + {" "} + Value:   {details().eth_value} + +
+ +
+ ); +}; + +export default TransactionsDetails; diff --git a/packages/cactus-cmd-gui-app/src/pages/ERC20/ERC20.module.css b/packages/cactus-cmd-gui-app/src/pages/ERC20/ERC20.module.css new file mode 100644 index 0000000000..4a078f4499 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/ERC20/ERC20.module.css @@ -0,0 +1,11 @@ +.erc-content { + display: flex; + gap:2rem; +} + +.erc-wrap{ + display: flex; + gap:1rem; + flex-direction: column; + align-items: flex-end; +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/pages/ERC20/ERC20.tsx b/packages/cactus-cmd-gui-app/src/pages/ERC20/ERC20.tsx new file mode 100644 index 0000000000..460d3ddb90 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/ERC20/ERC20.tsx @@ -0,0 +1,72 @@ +import { useParams, useNavigate } from "@solidjs/router"; +import { createSignal, createEffect } from "solid-js"; +import TokenAccount from "../../components/TokenHeader/TokenAccount"; +import { supabase } from "../../supabase-client"; +import CardWrapper from "../../components/CardWrapper/CardWrapper"; +import Chart from "../../components/Chart/Chart"; +// @ts-expect-error +import styles from "./ERC20.module.css"; +import { ERC20Txn } from "../../schema/supabase-types"; + +const ERC20 = () => { + const params = useParams(); + const navigate = useNavigate(); + const [token_erc20, setToken_erc20] = createSignal([]); + + const ercTableProps = { + onClick: { + action: (token_address: string) => + navigate(`/ERC20/trend/${params.account}/${token_address}`), + prop: "token_address", + }, + schema: [ + { + display: "token address", + objProp: ["token_address"], + }, + { + display: "balance", + objProp: ["balance"], + }, + ], + }; + + const fetchERC20 = async () => { + try { + const { data, error } = await supabase + .from("token_erc20") + .select() + .eq("account_address", params.account); + if (data) { + setToken_erc20(data); + } + if (error) { + throw new Error(error.message); + } + } catch (error: any) { + console.error(error.message); + } + }; + + createEffect(async () => { + await fetchERC20(); + }, []); + + return ( +
+ +
+ + +
+
+ ); +}; + +export default ERC20; diff --git a/packages/cactus-cmd-gui-app/src/pages/ERC721/ERC721.module.css b/packages/cactus-cmd-gui-app/src/pages/ERC721/ERC721.module.css new file mode 100644 index 0000000000..625787c53b --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/ERC721/ERC721.module.css @@ -0,0 +1,11 @@ +.erc-content { + display: flex; + gap:2rem; +} + +.erc-wrap{ + display: flex; + gap:1rem; + flex-direction: column; + align-items: center; +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/pages/ERC721/ERC721.tsx b/packages/cactus-cmd-gui-app/src/pages/ERC721/ERC721.tsx new file mode 100644 index 0000000000..dd797bf568 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/ERC721/ERC721.tsx @@ -0,0 +1,117 @@ +import { useParams, useNavigate } from "@solidjs/router"; +import { createSignal, createEffect, Show } from "solid-js"; +import TokenAccount from "../../components/TokenHeader/TokenAccount"; +import { supabase } from "../../supabase-client"; +import CardWrapper from "../../components/CardWrapper/CardWrapper"; +// @ts-expect-error +import styles from "./ERC721.module.css"; +import { ERC721Txn } from "../../schema/supabase-types"; +import { TokenMetadata721 } from "../../schema/supabase-types"; + +const ERC721 = () => { + const params = useParams(); + const navigate = useNavigate(); + const [token_erc721, setToken_erc721] = createSignal([]); + const [tokenMetadata, setTokenMetadata] = createSignal( + [], + ); + + const ercTableProps = { + onClick: { + action: (param: string) => navigate(`/token-details/ERC721/${param}`), + prop: "token_address", + }, + schema: [ + { + display: "symbol", + objProp: ["symbol"], + }, + { + display: "URI", + objProp: ["uri"], + }, + ], + }; + const metaProps = { + onClick: { + action: () => {}, + prop: "", + }, + schema: [ + { + display: "created at", + objProp: ["created_at"], + }, + { + display: "sender/recipient", + objProp: ["sender", "recipient"], + }, + { + display: "token address", + objProp: ["token_address"], + }, + ], + }; + + const fetchERC721 = async () => { + try { + const { data, error } = await supabase + .from("erc721_txn_meta_view") + .select() + .eq("account_address", params.account); + if (data) { + setToken_erc721(data); + } + if (error) { + throw new Error(error.message); + } + } catch (error) { + console.error(error.message); + } + }; + + const fetchMetadata = async () => { + try { + const { data, error } = await supabase + .from(`erc721_token_history_view`) + .select("*"); + if (data) { + setTokenMetadata(data); + } + if (error) { + console.error(error.message); + } + } catch (error) { + console.error(error.message); + } + }; + + createEffect(async () => { + await fetchERC721(); + await fetchMetadata(); + }, []); + + return ( +
+ +
+ + +
+
+ ); +}; + +export default ERC721; diff --git a/packages/cactus-cmd-gui-app/src/pages/Pages.tsx b/packages/cactus-cmd-gui-app/src/pages/Pages.tsx new file mode 100644 index 0000000000..06bdf30f18 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/Pages.tsx @@ -0,0 +1,45 @@ +import { Routes, Route } from "@solidjs/router"; +import TransactionDetails from "./Details/TransactionDetails"; +import blockDetails from "./Details/BlockDetails"; +import TokenTransactionDetails from "./Details/TokenTransactionDetails"; +import TokenDetails from "./Details/TokenDetails"; +import Dashboard from "./Dashboard/Dashboard"; +import Transactions from "./Transactions/Transactions"; +import Blocks from "./Blocks/Blocks"; +import Accounts from "./Accounts/Accounts"; +import ERC20 from "./ERC20/ERC20"; +import ERC721 from "./ERC721/ERC721"; +import SingleTokenHistory from "./SingleTokenHistory/SingleTokenHistory"; + +const Pages = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; +export default Pages; diff --git a/packages/cactus-cmd-gui-app/src/pages/SingleTokenHistory/SingleTokenHistory.module.css b/packages/cactus-cmd-gui-app/src/pages/SingleTokenHistory/SingleTokenHistory.module.css new file mode 100644 index 0000000000..843b4ce9d3 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/SingleTokenHistory/SingleTokenHistory.module.css @@ -0,0 +1,6 @@ +.token-history{ +display:flex; +flex-direction: column; +gap: 1rem; + +} diff --git a/packages/cactus-cmd-gui-app/src/pages/SingleTokenHistory/SingleTokenHistory.tsx b/packages/cactus-cmd-gui-app/src/pages/SingleTokenHistory/SingleTokenHistory.tsx new file mode 100644 index 0000000000..9ed1ab8ab8 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/SingleTokenHistory/SingleTokenHistory.tsx @@ -0,0 +1,116 @@ +import { createSignal, createEffect } from "solid-js"; +import { supabase } from "../../supabase-client"; +import CardWrapper from "../../components/CardWrapper/CardWrapper"; +import LineChart from "../../components/Chart/LineChart"; +// @ts-expect-error +import styles from "./SingleTokenHistory.module.css"; +import TokenHeader from "../../components/TokenHeader/TokenHeader"; +import { useNavigate, useParams } from "@solidjs/router"; +import { TokenHistoryItem20 } from "../../schema/supabase-types"; +import { balanceDate } from "../../schema/supabase-types"; + +const SingleTokenHistory = () => { + type ObjectKey = keyof typeof styles; + const [transactions, setTransactions] = createSignal( + [], + ); + const [balanceHistory, setBalanceHistory] = createSignal([]); + const navigate = useNavigate(); + const params = useParams(); + + const tokenTableProps = { + onClick: { + action: (param: string) => navigate(`/view/${param}`), + prop: "id", + }, + schema: [ + { + display: "created at", + objProp: ["created_at"], + }, + { + display: "transaction hash", + objProp: ["transaction_hash"], + }, + { + display: "sender/recipient", + objProp: ["sender", "recipient"], + }, + { + display: "token address", + objProp: ["token_address"], + }, + { + display: "token value", + objProp: ["value"], + }, + ], + }; + + const calcTokenBalance = (txnData: TokenHistoryItem20[]) => { + let balance = 0; + const balances = txnData.map((txn) => { + let txn_value = txn.value || 0; + let account = params.account; + if (txn.recipient !== account) { + txn_value *= -1; + } + balance += txn_value; + return { + created_at: txn.created_at + "Z", + balance: balance, + }; + }); + return balances; + }; + + const fetchTransactions = async () => { + try { + const { data, error } = await supabase + .from("erc20_token_history_view") + .select("*") + .match({ token_address: params.address }).or(`sender.eq.${params.account}, recipient.eq.${params.account}`); + if (data) { + setTransactions(data); + setBalanceHistory(calcTokenBalance(data)); + } + if (error) { + console.error(error.message); + } + } catch (error:any) { + console.error(error.message); + } + }; + + createEffect(async () => { + await fetchTransactions(); + }, []); + + return ( +
+ + +
+ {transactions().length > 0 ? ( + + ) : ( +

No data

+ )} +
+
+ ); +}; + +export default SingleTokenHistory; diff --git a/packages/cactus-cmd-gui-app/src/pages/Transactions/Transactions.module.css b/packages/cactus-cmd-gui-app/src/pages/Transactions/Transactions.module.css new file mode 100644 index 0000000000..7ff183b0da --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/Transactions/Transactions.module.css @@ -0,0 +1,12 @@ +.transactions{ + display: flex; + flex-direction: column; + +}.transactions-search { + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 2rem; + + +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/src/pages/Transactions/Transactions.tsx b/packages/cactus-cmd-gui-app/src/pages/Transactions/Transactions.tsx new file mode 100644 index 0000000000..22081e8ae1 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/pages/Transactions/Transactions.tsx @@ -0,0 +1,64 @@ +import { createSignal, createEffect, Show } from "solid-js"; +import { useNavigate, useParams } from "@solidjs/router"; +import { supabase } from "../../supabase-client"; +import CardWrapper from "../../components/CardWrapper/CardWrapper"; +// @ts-expect-error +import styles from "./Transactions.module.css"; +import { Transaction } from "../../schema/supabase-types"; +const Transactions = () => { + const navigate = useNavigate(); + const [transactions, setTransactions] = createSignal([]); + + const txnTableProps = { + onClick: { + action: (param: string) => navigate(`/view/${param}`), + prop: "id", + }, + schema: [ + { + display: "transaction id", + objProp: ["id"], + }, + { + display: "sender/recipient", + objProp: ["from", "to"], + }, + { + display: "token value", + objProp: ["eth_value"], + }, + ], + }; + + const fetchTransactions = async () => { + try { + const { data } = await supabase.from("transaction").select("*"); + if (data) { + setTransactions(data); + } else { + throw new Error("Failed to load transactions"); + } + } catch (error) { + console.error(error.message); + } + }; + + createEffect(async () => { + await fetchTransactions(); + }, []); + + return ( +
+ +
+ ); +}; + +export default Transactions; diff --git a/packages/cactus-cmd-gui-app/src/schema/supabase-types.ts b/packages/cactus-cmd-gui-app/src/schema/supabase-types.ts new file mode 100644 index 0000000000..45b608e68e --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/schema/supabase-types.ts @@ -0,0 +1,103 @@ +export interface ERC20Txn { + account_address: string; + token_address: string; + uri: string; + token_id: number; + id: string; + balance: number; + last_owner_change: string; +} + +export interface ERC721Txn { + account_address: string; + token_address: string; + uri: string; + token_id: number; + id: string; + last_owner_change: string; +} + +export interface TokenMetadata20 { + address: string; + name: string; + symbol: string; + total_supply: number; + created_at: string; +} + +export interface TokenMetadata721 { + address: string; + name: string; + symbol: string; + created_at: string; +} + +export interface Block { + number: number; + created_at: string; + hash: string; + number_of_tx: number; + sync_at: string; +} + +export interface TokenTransfer { + transaction_id: string; + sender: string; + recipient: string; + value: number; + id: string; +} + +export interface Transaction { + index: number; + hash: string; + block_number: number; + from: string; + to: string; + eth_value: number; + method_signature: string; + method_name: string; + id: string; +} + +export interface TokenHistoryItem { + transaction_hash: string | null; + token_address: string | null; + created_at: string | null; + sender: string | null; + recipient: string | null; +} + +export interface TokenHistoryItem721 extends TokenHistoryItem { + token_id: number | null; +} + +export interface TokenHistoryItem20 extends TokenHistoryItem { + value: number | null; +} + +export interface TokenTransactionMetadata721 { + account_address: string; + token_address: string; + uri: string; + symbol: string; +} + +export interface TableProperty { + display: string; + objProp: string[]; +} + +export interface TableRowClick { + action: (param: string) => void; + prop: string; +} +export interface TableProps { + onClick: TableRowClick; + schema: TableProperty[]; +} + +export interface balanceDate { + created_at: string; + balance: number; +} diff --git a/packages/cactus-cmd-gui-app/src/schema/token-standards.ts b/packages/cactus-cmd-gui-app/src/schema/token-standards.ts new file mode 100644 index 0000000000..65ad5a514c --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/schema/token-standards.ts @@ -0,0 +1,4 @@ +export const STANDARDS = { + erc20: "ERC20", + erc721: "ERC721", +}; diff --git a/packages/cactus-cmd-gui-app/src/supabase-client.tsx b/packages/cactus-cmd-gui-app/src/supabase-client.tsx new file mode 100644 index 0000000000..11dbd850c8 --- /dev/null +++ b/packages/cactus-cmd-gui-app/src/supabase-client.tsx @@ -0,0 +1,7 @@ +import { createClient } from "@supabase/supabase-js"; + +let supabaseUrl = "http://localhost:8000"; +let supabaseKey = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE"; + +export const supabase = createClient(supabaseUrl, supabaseKey); diff --git a/packages/cactus-cmd-gui-app/styles.d.ts b/packages/cactus-cmd-gui-app/styles.d.ts new file mode 100644 index 0000000000..f2d12bb56c --- /dev/null +++ b/packages/cactus-cmd-gui-app/styles.d.ts @@ -0,0 +1,4 @@ +declare module "*.module.css" { + const classes: { [key: string]: string }; + export default classes; +} diff --git a/packages/cactus-cmd-gui-app/tailwind.config.ts b/packages/cactus-cmd-gui-app/tailwind.config.ts new file mode 100644 index 0000000000..06602b374a --- /dev/null +++ b/packages/cactus-cmd-gui-app/tailwind.config.ts @@ -0,0 +1,11 @@ +module.exports = { + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + darkMode: false, // or 'media' or 'class' + theme: { + extend: {}, + }, + variants: { + extend: {}, + }, + plugins: [], +}; diff --git a/packages/cactus-cmd-gui-app/tsconfig.json b/packages/cactus-cmd-gui-app/tsconfig.json new file mode 100644 index 0000000000..f82f4cc92f --- /dev/null +++ b/packages/cactus-cmd-gui-app/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "jsx": "preserve", + "jsxImportSource": "solid-js", + "plugins": [ + { + "name": "typescript-plugin-css-modules" + } + ], + "composite": true, + "outDir": "./dist/out-tsc", + "rootDir": "./src", + "skipLibCheck": true, + "tsBuildInfoFile": "../../.build-cache/cactus-cmd-gui-app.tsbuildinfo" + }, + "include": [ + "./src" + ] +} \ No newline at end of file diff --git a/packages/cactus-cmd-gui-app/vite.config.ts b/packages/cactus-cmd-gui-app/vite.config.ts new file mode 100644 index 0000000000..279d6a4fca --- /dev/null +++ b/packages/cactus-cmd-gui-app/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "vite"; +import solidPlugin from "vite-plugin-solid"; + +export default defineConfig({ + plugins: [solidPlugin()], + server: { + port: 3001, + }, + build: { + target: "esnext", + outDir: "./dist/lib", + }, +}); diff --git a/tsconfig.json b/tsconfig.json index 345afed42a..5d01837d9c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,9 @@ { "path": "./packages/cactus-cmd-socketio-server/tsconfig.json" }, + { + "path": "./packages/cactus-cmd-gui-app/tsconfig.json" + }, { "path": "./packages/cactus-common/tsconfig.json" },