From dad6b8a11571b8df71327222add51f5d77272d92 Mon Sep 17 00:00:00 2001 From: MatteoMer <30910760+MatteoMer@users.noreply.github.com> Date: Thu, 20 Feb 2025 10:29:56 +0100 Subject: [PATCH 1/2] UI modif --- .gitignore | 1 + index.html | 2 +- package.json | 4 +- src/App.vue | 22 +- src/explorer/Block.vue | 154 ++++++++-- src/explorer/Blocks.vue | 159 ++++++++++ src/explorer/Contract.vue | 97 +++++- src/explorer/Header.vue | 19 +- src/explorer/Home.vue | 358 +++++++++++++++++++---- src/explorer/NetworkSelector.vue | 50 +++- src/explorer/Transaction.vue | 134 ++++++++- src/explorer/Transactions.vue | 27 ++ src/explorer/components/Navbar.vue | 66 +++++ src/explorer/components/NetworkChart.vue | 97 ++++++ src/main.ts | 12 + src/style.css | 32 ++ tailwind.config.js | 21 +- 17 files changed, 1128 insertions(+), 127 deletions(-) create mode 100644 src/explorer/Blocks.vue create mode 100644 src/explorer/Transactions.vue create mode 100644 src/explorer/components/Navbar.vue create mode 100644 src/explorer/components/NetworkChart.vue diff --git a/.gitignore b/.gitignore index a547bf3..9bef1b4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ yarn-error.log* pnpm-debug.log* lerna-debug.log* +package-lock.json node_modules dist dist-ssr diff --git a/index.html b/index.html index 743db32..f672aab 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,7 @@ - +
diff --git a/package.json b/package.json index 63aae88..98f3a00 100644 --- a/package.json +++ b/package.json @@ -14,12 +14,14 @@ "@noir-lang/backend_barretenberg": "^0.30.0", "@noir-lang/noir_js": "^0.30.0", "asn1.js": "^5.4.1", + "chart.js": "^4.4.8", "dotenv": "^16.4.5", "face-api.js": "^0.22.2", "pkijs": "^3.2.4", "protobufjs": "^7.4.0", "rollup-plugin-copy": "^3.5.0", "vue": "^3.5.13", + "vue-chartjs": "^5.3.2", "vue-router": "^4.5.0" }, "devDependencies": { @@ -31,7 +33,7 @@ "rollup-plugin-visualizer": "^5.12.0", "tailwindcss": "^3.4.15", "typescript": "^5.7.2", - "vite": "^5.4.11", + "vite": "^5.4.14", "vue-tsc": "^2.1.10" } } diff --git a/src/App.vue b/src/App.vue index 276cb20..f5ef77f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -15,7 +15,7 @@ h3, h4, h5, h6 { - @apply font-anton uppercase; + @apply font-display uppercase; } h1 { @@ -27,27 +27,27 @@ h2 { } body { - @apply bg-primary; + @apply bg-background text-foreground; } button { - @apply bg-gray-900 font-anton uppercase rounded-lg px-4 py-2 text-white; + @apply font-display uppercase rounded-xl transition-all duration-200; } -button:hover:not(:disabled) { - @apply tracking-widest; +button:not([class*="bg-"]) { + @apply bg-white/80 backdrop-blur-sm border border-white/20 text-secondary hover:bg-secondary/5; } button:active:not(:disabled) { - @apply bg-secondary text-primary; + @apply bg-primary/10 text-primary border-primary/20; } button:disabled { - @apply bg-gray-500 text-opacity-50; + @apply opacity-50 cursor-not-allowed; } p a { - @apply border-b-2 border-secondary; + @apply border-b-2 border-primary; } i.spinner { @@ -58,14 +58,14 @@ i.spinner { input, select, textarea { - @apply text-primary font-mono p-2 rounded-lg leading-snug; + @apply text-foreground font-text p-2 rounded-lg leading-snug; } input[type="file"] { - @apply text-secondary text-sm; + @apply text-neutral text-sm; } label { - @apply font-anton bg-opacity-50 bg-black px-2 rounded-t-lg; + @apply font-display bg-secondary/10 px-2 rounded-t-lg text-secondary; } diff --git a/src/explorer/Block.vue b/src/explorer/Block.vue index e6741a8..a9aa125 100644 --- a/src/explorer/Block.vue +++ b/src/explorer/Block.vue @@ -1,5 +1,5 @@ + + diff --git a/src/explorer/Blocks.vue b/src/explorer/Blocks.vue new file mode 100644 index 0000000..7be4304 --- /dev/null +++ b/src/explorer/Blocks.vue @@ -0,0 +1,159 @@ + + + \ No newline at end of file diff --git a/src/explorer/Contract.vue b/src/explorer/Contract.vue index dda4ddd..0efb87b 100644 --- a/src/explorer/Contract.vue +++ b/src/explorer/Contract.vue @@ -14,16 +14,99 @@ watchEffect(() => { }); const data = computed(() => contractStore.value.data?.[contract_name.value]); + +const tabs = [ + { name: 'Overview', active: true }, + { name: 'Raw JSON', active: false } +]; + + diff --git a/src/explorer/Header.vue b/src/explorer/Header.vue index b6a3b4e..8c12c47 100644 --- a/src/explorer/Header.vue +++ b/src/explorer/Header.vue @@ -1,24 +1,9 @@ + diff --git a/src/explorer/Transaction.vue b/src/explorer/Transaction.vue index edf83ee..0e76732 100644 --- a/src/explorer/Transaction.vue +++ b/src/explorer/Transaction.vue @@ -14,21 +14,131 @@ watchEffect(() => { }); const data = computed(() => transactionStore.value.data?.[tx_hash.value]); + +const tabs = [ + { name: 'Overview', active: true }, + { name: 'Raw JSON', active: false } +]; + +const formatTimestamp = (date: Date) => { + return `${getTimeAgo(date)} (${date.toLocaleString()})`; +}; + +function getTimeAgo(date: Date) { + const seconds = Math.floor((new Date().getTime() - date.getTime()) / 1000); + if (seconds < 60) return `${seconds} secs ago`; + const minutes = Math.floor(seconds / 60); + if (minutes < 60) return `${minutes} mins ago`; + return `${Math.floor(minutes / 60)} hours ago`; +} + + diff --git a/src/explorer/Transactions.vue b/src/explorer/Transactions.vue new file mode 100644 index 0000000..a2fa838 --- /dev/null +++ b/src/explorer/Transactions.vue @@ -0,0 +1,27 @@ + + + \ No newline at end of file diff --git a/src/explorer/components/Navbar.vue b/src/explorer/components/Navbar.vue new file mode 100644 index 0000000..2901c0a --- /dev/null +++ b/src/explorer/components/Navbar.vue @@ -0,0 +1,66 @@ + + + \ No newline at end of file diff --git a/src/explorer/components/NetworkChart.vue b/src/explorer/components/NetworkChart.vue new file mode 100644 index 0000000..0d214aa --- /dev/null +++ b/src/explorer/components/NetworkChart.vue @@ -0,0 +1,97 @@ + + + \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 83a978c..bc9fe36 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,6 +6,8 @@ import Home from "@/explorer/Home.vue"; import Transaction from "./explorer/Transaction.vue"; import Block from "./explorer/Block.vue"; import Contract from "./explorer/Contract.vue"; +import Blocks from "./explorer/Blocks.vue"; +import Transactions from "./explorer/Transactions.vue"; const routes = [ { path: "/", component: Home, name: "Home" }, @@ -19,6 +21,16 @@ const routes = [ component: Block, name: "Block", }, + { + path: "/blocks", + component: Blocks, + name: "Blocks", + }, + { + path: "/transactions", + component: Transactions, + name: "Transactions", + }, { path: "/contract/:contract_name", component: Contract, diff --git a/src/style.css b/src/style.css index 5f16e3c..f3b6392 100644 --- a/src/style.css +++ b/src/style.css @@ -4,8 +4,40 @@ @layer base { :root { + --background: #F8F9FA; + --foreground: #1E2933; --color-primary: 224 72 46; --color-secondary: 245 245 245; --color-secondary-darker: 225 225 225; } } + +@layer components { + .card { + @apply bg-white rounded-2xl shadow-card p-6 transition-all duration-200 hover:shadow-lg; + } + + .section-title { + @apply text-2xl font-display font-bold text-secondary mb-4; + } + + .link-item { + @apply p-4 rounded-xl transition-all duration-200 hover:bg-neutral/5; + } + + .hash-text { + @apply font-mono text-xs text-neutral hover:text-primary transition-colors; + } + + .stat-card { + @apply bg-white/50 backdrop-blur-sm p-4 rounded-xl border border-white/20 shadow-sm; + } + + .data-grid { + @apply grid gap-4 md:grid-cols-2 lg:grid-cols-3; + } + + .list-item { + @apply flex items-center space-x-3 p-3 rounded-lg hover:bg-secondary/5 transition-colors; + } +} diff --git a/tailwind.config.js b/tailwind.config.js index ad8527f..f2010b9 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -4,14 +4,25 @@ export default { theme: { extend: { colors: { - primary: "rgb(var(--color-primary) / )", - secondary: "rgb(var(--color-secondary) / )", - "secondary-darker": "rgb(var(--color-secondary-darker) / )", + background: "var(--background)", + foreground: "var(--foreground)", + primary: "#DF6445", // Coral/Orange + secondary: "#1E2933", // Dark Blue/Navy + neutral: "#516273", // Gray/Slate + black: "#000000", // Pure Black + white: "#FFFFFF", // Pure White }, fontFamily: { - text: ["Roboto", "sans-serif"], - anton: ['"Anton"', "sans-serif"], + text: ["Inter", "sans-serif"], + display: ["Montserrat", "sans-serif"], }, + borderRadius: { + 'xl': '1rem', + '2xl': '1.5rem', + }, + boxShadow: { + 'card': '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)', + } }, }, plugins: [], From 46ad2202ac088dc3e4103c93a658c48935e6fad8 Mon Sep 17 00:00:00 2001 From: Bertrand Darbon Date: Fri, 28 Feb 2025 09:07:51 +0100 Subject: [PATCH 2/2] timestamp on tx page + remove some fake data --- bun.lockb | Bin 129443 -> 131238 bytes src/explorer/Block.vue | 49 +++++---- src/explorer/Home.vue | 192 ++++++++++++++++++----------------- src/explorer/Transaction.vue | 83 ++++++++------- src/state/transactions.ts | 1 + 5 files changed, 173 insertions(+), 152 deletions(-) diff --git a/bun.lockb b/bun.lockb index 402d8f90b9e4b0eee0ac6a627b30cbfcea7846e5..f6aed1cc6175cd152eb9ca5e6d0d34836e111158 100755 GIT binary patch delta 23428 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 m7pod-OVSdR6KK5qo2s+r_9xV;_9|{m$Or1JSe { const data = computed(() => blockStore.value.data?.[block_hash.value]); const tabs = [ - { name: 'Overview', active: true }, - { name: 'Raw JSON', active: false } + { name: "Overview", active: true }, + { name: "Raw JSON", active: false }, ]; const formatTimestamp = (date: Date) => { @@ -44,14 +44,14 @@ function getTimeAgo(date: Date | string | number) {

Block Details

-
@@ -79,24 +81,26 @@ function getTimeAgo(date: Date | string | number) {
Height: - #{{ data?.height || '...' }} + #{{ data?.height || "0" }}
Parent Block:
- {{ data?.parent_hash }} -
@@ -104,7 +108,7 @@ function getTimeAgo(date: Date | string | number) {
Timestamp: - {{ data?.timestamp ? formatTimestamp(data.timestamp) : '...' }} + {{ data?.timestamp ? formatTimestamp(data.timestamp) : "..." }}
@@ -143,8 +147,9 @@ function getTimeAgo(date: Date | string | number) {

Block Data

-{{ JSON.stringify(data, null, 2) }}
-                
+ {{ JSON.stringify(data, null, 2) }} +
diff --git a/src/explorer/Home.vue b/src/explorer/Home.vue index f38375b..19cab5c 100644 --- a/src/explorer/Home.vue +++ b/src/explorer/Home.vue @@ -3,41 +3,45 @@ import Header from "@/explorer/Header.vue"; import { blockStore, contractStore, transactionStore } from "@/state/data"; import { getNetworkApiUrl, network } from "@/state/network"; import { onMounted, ref } from "vue"; -import { Line } from 'vue-chartjs'; -import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from 'chart.js'; +import { Line } from "vue-chartjs"; +import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from "chart.js"; import NetworkChart from "@/explorer/components/NetworkChart.vue"; -import { useRouter } from 'vue-router'; +import { useRouter } from "vue-router"; const consensusInfo = ref(null); const router = useRouter(); -const searchQuery = ref(''); +const searchQuery = ref(""); // Register ChartJS components ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend); // Sample data for the charts (you can replace with real data) const transactionChartData = { - labels: ['6h ago', '5h ago', '4h ago', '3h ago', '2h ago', '1h ago', 'Now'], - datasets: [{ - label: 'Transactions per Hour', - data: [65, 59, 80, 81, 56, 55, 40], - fill: true, - borderColor: '#DF6445', - backgroundColor: 'rgba(223, 100, 69, 0.1)', - tension: 0.4 - }] + labels: ["6h ago", "5h ago", "4h ago", "3h ago", "2h ago", "1h ago", "Now"], + datasets: [ + { + label: "Transactions per Hour", + data: [65, 59, 80, 81, 56, 55, 40], + fill: true, + borderColor: "#DF6445", + backgroundColor: "rgba(223, 100, 69, 0.1)", + tension: 0.4, + }, + ], }; const blockTimeChartData = { - labels: ['6h ago', '5h ago', '4h ago', '3h ago', '2h ago', '1h ago', 'Now'], - datasets: [{ - label: 'Block Time (seconds)', - data: [0.5, 0.48, 0.52, 0.49, 0.51, 0.47, 0.5], - fill: true, - borderColor: '#DF6445', - backgroundColor: 'rgba(223, 100, 69, 0.1)', - tension: 0.4 - }] + labels: ["6h ago", "5h ago", "4h ago", "3h ago", "2h ago", "1h ago", "Now"], + datasets: [ + { + label: "Block Time (seconds)", + data: [0.5, 0.48, 0.52, 0.49, 0.51, 0.47, 0.5], + fill: true, + borderColor: "#DF6445", + backgroundColor: "rgba(223, 100, 69, 0.1)", + tension: 0.4, + }, + ], }; const fetchConsensusInfo = async () => { @@ -48,24 +52,24 @@ const fetchConsensusInfo = async () => { // Mock search function const handleSearch = () => { const query = searchQuery.value.trim(); - + if (query.match(/^[0-9]+$/)) { // Search by block height - router.push({ name: 'Block', params: { block_hash: query } }); + router.push({ name: "Block", params: { block_hash: query } }); } else if (query.length === 64) { // Search by transaction hash - router.push({ name: 'Transaction', params: { tx_hash: query } }); + router.push({ name: "Transaction", params: { tx_hash: query } }); } else if (query) { // Search by contract name - router.push({ name: 'Contract', params: { contract_name: query } }); + router.push({ name: "Contract", params: { contract_name: query } }); } }; // Keyboard shortcut onMounted(() => { fetchConsensusInfo(); - window.addEventListener('keydown', (e) => { - if ((e.metaKey || e.ctrlKey) && e.key === 'k') { + window.addEventListener("keydown", (e) => { + if ((e.metaKey || e.ctrlKey) && e.key === "k") { e.preventDefault(); document.querySelector('input[type="search"]')?.focus(); } @@ -89,25 +93,16 @@ function getTimeAgo(date: Date | string | number) {
-

- Explore Hylé Blockchain -

-

- Search transactions, explore blocks, or discover smart contracts -

+

Explore Hylé Blockchain

+

Search transactions, explore blocks, or discover smart contracts

- - +
@@ -116,29 +111,24 @@ function getTimeAgo(date: Date | string | number) { v-model="searchQuery" type="search" placeholder="Search by block height, transaction hash, or contract name..." - class="w-full bg-white/80 backdrop-blur-sm border border-white/20 rounded-xl - py-4 pl-12 pr-16 text-secondary placeholder-neutral/50 - focus:outline-none focus:ring-2 focus:ring-primary/20 - font-medium transition-all duration-200 text-lg" + class="w-full bg-white/80 backdrop-blur-sm border border-white/20 rounded-xl py-4 pl-12 pr-16 text-secondary placeholder-neutral/50 focus:outline-none focus:ring-2 focus:ring-primary/20 font-medium transition-all duration-200 text-lg" @keyup.enter="handleSearch" /> - @@ -157,67 +147,78 @@ function getTimeAgo(date: Date | string | number) {

{{ transactionStore.latest.length || "156,042" }}

-
24H Tx: 15,231
-
TPS: 23.5
+
24H Tx: ?
+
TPS: ?
- +

Current Block

- #{{ blockStore.latest[0] ? blockStore.data[blockStore.latest[0]].height : '37,382' }} + #{{ blockStore.latest[0] ? blockStore.data[blockStore.latest[0]].height : "37,382" }}

-
Avg Time: 0.5s
-
Gas: 0.02064
+
Avg Time: ?
+
Gas: ?
- +

Smart Contracts

{{ Object.keys(contractStore.data).length || "8" }}

-
24H New: 2
-
Active: 6
+
24H New: ?
+
Active: ?
- +

Network

{{ network }}

-
Validators: 4
-
Nodes: 12
+
Validators: ?
+
Nodes: ?
+
@@ -226,7 +227,7 @@ function getTimeAgo(date: Date | string | number) {

Latest Blocks

- Height {{ blockStore.latest[0] ? blockStore.data[blockStore.latest[0]].height : '37,382' }} + Height {{ blockStore.latest[0] ? blockStore.data[blockStore.latest[0]].height : "37,382" }}
@@ -243,24 +244,29 @@ function getTimeAgo(date: Date | string | number) { Block #{{ blockStore.data[hash].height }} - {{ hash.slice(0, 10) }}...{{ hash.slice(-6) }} + {{ hash.slice(0, 10) }}...{{ hash.slice(-6) }}
{{ getTimeAgo(blockStore.data[hash].timestamp) }}
- Transactions: {{ blockStore.tx_hashes_by_block?.[hash]?.length || '7' }} + Transactions: + {{ + blockStore.tx_hashes_by_block?.[hash]?.length || "?" + }} Size: 24.5 KB
- - Finalized - + Finalized
- @@ -273,7 +279,7 @@ function getTimeAgo(date: Date | string | number) {

Latest Transactions

- {{ transactionStore.latest.length || '15' }} recent + {{ transactionStore.latest.length || "15" }} recent
@@ -287,7 +293,9 @@ function getTimeAgo(date: Date | string | number) {
- {{ tx_hash.slice(0, 10) }}...{{ tx_hash.slice(-6) }} + {{ tx_hash.slice(0, 10) }}...{{ tx_hash.slice(-6) }} {{ transactionStore.data[tx_hash].transaction_type }} @@ -307,7 +315,7 @@ function getTimeAgo(date: Date | string | number) {
- diff --git a/src/explorer/Transaction.vue b/src/explorer/Transaction.vue index 0e76732..98077b7 100644 --- a/src/explorer/Transaction.vue +++ b/src/explorer/Transaction.vue @@ -1,35 +1,42 @@ @@ -40,14 +47,14 @@ function getTimeAgo(date: Date) {

Transaction Details

-
@@ -75,53 +84,51 @@ function getTimeAgo(date: Date) {
Block: - - #{{ data?.block_hash?.slice(0, 8) || '...' }} + #{{ data?.block_hash?.slice(0, 8) || "..." }}
-
+
Transaction Type: - {{ data?.transaction_type || 'BlobTransaction' }} + {{ data?.transaction_type || "BlobTransaction" }}
Status:
-
- {{ data?.transaction_status || 'Pending' }} +
+ {{ data?.transaction_status || "Pending" }}
Timestamp: - {{ formatTimestamp(new Date()) }} + {{ block?.timestamp ? formatTimestamp(block.timestamp) : "?" }}
-
- -
Version: {{ data?.version || 1 }}
- -
Fee: - 0.02064 HYLE + ?
@@ -129,8 +136,8 @@ function getTimeAgo(date: Date) {

Transaction Data

-
-{{ JSON.stringify(data, null, 2) }}
+                
{{ JSON.stringify(data, null, 2) }}
                 
diff --git a/src/state/transactions.ts b/src/state/transactions.ts index ee9bd19..f9a4bd1 100644 --- a/src/state/transactions.ts +++ b/src/state/transactions.ts @@ -32,5 +32,6 @@ export class TransactionStore { const response = await fetch(`${getNetworkApiUrl(this.network)}/v1/indexer/transaction/hash/${tx_hash}?no_cache=${Date.now()}`); let item = await response.json(); this.data[item.tx_hash] = item; + return item; } }