Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(explorer): new version integration #1683

Merged
merged 37 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
789ab16
refactor: tables ui component
MarcosNicolau Dec 23, 2024
449225b
feat: number of proofs and fee per proof on batches table
MarcosNicolau Dec 23, 2024
b86ea43
refactor: spacing in table
MarcosNicolau Dec 23, 2024
295d002
feat: enlarge view and ditch grid for flex
MarcosNicolau Dec 23, 2024
5faeb5a
feat: new card style
MarcosNicolau Dec 23, 2024
9e8ab1e
feat: landing stats
MarcosNicolau Dec 23, 2024
b3901d0
feat: use new cards and various style fixes
MarcosNicolau Dec 23, 2024
6a64cc1
Merge remote-tracking branch 'origin/explorer-new-version' into feat/…
MarcosNicolau Dec 23, 2024
599576f
fix: chart responsiveness and remove uneeded dark-mode listener
MarcosNicolau Dec 23, 2024
205252c
chore: remove animations
MarcosNicolau Dec 23, 2024
a20d95b
fix: chart responsive when updating
MarcosNicolau Dec 23, 2024
28f5f19
fix: typo
MarcosNicolau Dec 23, 2024
f72d3e7
Update explorer/lib/explorer_web/live/pages/home/index.ex
uri-99 Dec 23, 2024
bf0b386
fix: details
uri-99 Dec 23, 2024
04fc5c3
feat: change chart type to bar
MarcosNicolau Dec 24, 2024
93b3911
style: place tooltip in top of bar
MarcosNicolau Dec 24, 2024
0a0f4fd
chore: elixir warnings
MarcosNicolau Dec 24, 2024
dd9823d
feat: links in stats
MarcosNicolau Dec 24, 2024
f83d334
feat: show all contract addresses
MarcosNicolau Dec 24, 2024
5eb3065
feat: show fee_per_proof in usd in batches table
MarcosNicolau Dec 24, 2024
cbcb4aa
chore: fetch as much as 15 batches in charts
MarcosNicolau Dec 24, 2024
8a109ef
fix: make the whole stat clickable if link is present
MarcosNicolau Dec 24, 2024
9c494af
fix: tooltip names mixing
MarcosNicolau Dec 24, 2024
a30f444
fix: batch size tooltip data
MarcosNicolau Dec 24, 2024
e223a69
fix: get_latest_batches impair between mount and update
MarcosNicolau Dec 24, 2024
de24d55
refactor: rename /restakes routes for /restaked
MarcosNicolau Dec 26, 2024
043b828
feat: yAxis show 0, min and max values
MarcosNicolau Dec 30, 2024
a297474
fix: footer logo size
MarcosNicolau Dec 30, 2024
40c3c17
fix: chart responsiveness when resizing
MarcosNicolau Dec 30, 2024
fd15e8f
docs: chart function yTickCallbackShowMinAndMaxValues
MarcosNicolau Dec 30, 2024
a9a0427
fix: avg_cost_per_proof tooltip
MarcosNicolau Dec 30, 2024
7b3e97a
feat: network message on contracts component
MarcosNicolau Dec 30, 2024
9e27416
fix: searchbar text
MarcosNicolau Dec 30, 2024
22f42ff
fix: stats font size + total restaked in shorthand form
MarcosNicolau Dec 30, 2024
76caaa3
fix: convert_number_to_shorthand
MarcosNicolau Dec 30, 2024
4f6d2f4
fix: increase charts amount of data
MarcosNicolau Dec 30, 2024
fe5eb9c
feat: stats show full number on hover
MarcosNicolau Dec 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions explorer/assets/css/tooltip.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@
.chart-tooltip-dot {
height: 10px;
width: 10px;
top: -5px;
bottom: -5px;
border-radius: 100%;
background-color: hsl(var(--foreground));
opacity: 0.2;
background-color: hsl(var(--accent));
position: absolute;
transition: all 0.2s;
}

.chart-tooltip-dot:hover {
opacity: 0.5;
transform: scale(1.2);
cursor: pointer;
}
Expand All @@ -26,7 +24,7 @@
min-height: 50px;
min-width: 250px;
padding: 20px;
margin-top: 8px;
margin-bottom: 8px;
background-color: hsl(var(--card));
border-radius: 8px;
border-width: 1px;
Expand All @@ -49,6 +47,7 @@
.chart-tooltip-item {
display: flex;
justify-content: space-between;
gap: 5px;
width: 100%;
}

Expand All @@ -59,3 +58,4 @@
.chart-tooltip-item-value {
color: hsl(var(--foreground));
}

21 changes: 9 additions & 12 deletions explorer/assets/vendor/charts/batch_size.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { yTickCallbackShowMinAndMaxValues } from "./helpers";
import { alignedTooltip } from "./tooltip";

export const batchSizeCustomOptions = (options, data) => {
// show only min and max values
options.scales.y.ticks.callback = (_value, index, values) => {
const dataY = data.datasets[0].data.map((point) => parseFloat(point.y));
if (index === 0) return `${Math.min(...dataY)} proofs`;
if (index === values.length - 1) {
return `${Math.max(...dataY)} proofs`;
}
return "";
};
options.scales.y.ticks.callback = yTickCallbackShowMinAndMaxValues(
data,
(val) => `${val} proofs`
);

// show age min, mean and max age in x axis
options.scales.x.ticks.callback = (_value, index, values) => {
Expand All @@ -23,9 +20,10 @@ export const batchSizeCustomOptions = (options, data) => {

options.plugins.tooltip.external = (context) =>
alignedTooltip(context, {
name: "batch-size",
title: "Batch size",
items: [
{ title: "Cost", id: "cost" },
{ title: "Fee per proof", id: "cost" },
{ title: "Age", id: "age" },
{ title: "Merkle root", id: "merkle_root" },
{ title: "Block number", id: "block_number" },
Expand All @@ -40,16 +38,15 @@ export const batchSizeCustomOptions = (options, data) => {
onTooltipUpdate: (tooltipModel) => {
const dataset = tooltipModel.dataPoints[0].dataset;
const idx = tooltipModel.dataPoints[0].dataIndex;

const cost = `${dataset.data[idx].y} USD`;
const amount_of_proofs = dataset.data[idx].y;
const age = dataset.age[idx];
const merkleRootHash = dataset.merkle_root[idx];
const merkle_root = `${merkleRootHash.slice(
0,
6
)}...${merkleRootHash.slice(merkleRootHash.length - 4)}`;
const block_number = dataset.data[idx].x;
const amount_of_proofs = dataset.amount_of_proofs[idx];
const cost = `${dataset.fee_per_proof[idx]} USD`;

return {
cost,
Expand Down
18 changes: 8 additions & 10 deletions explorer/assets/vendor/charts/cost_per_proof.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { yTickCallbackShowMinAndMaxValues } from "./helpers";
import { alignedTooltip } from "./tooltip";

export const costPerProofCustomOptions = (options, data) => {
// show only min and max values
options.scales.y.ticks.callback = (_value, index, values) => {
const dataY = data.datasets[0].data.map((point) => parseFloat(point.y));
if (index === 0) return `${Math.min(...dataY)} USD`;
if (index === values.length - 1) {
return `${Math.max(...dataY)} USD`;
}
return "";
};
// show only 0, min and max values
options.scales.y.ticks.callback = yTickCallbackShowMinAndMaxValues(
data,
(val) => `${val} USD`
);

// show age min, mean and max age in x axis
options.scales.x.ticks.callback = (_value, index, values) => {
Expand All @@ -23,9 +20,10 @@ export const costPerProofCustomOptions = (options, data) => {

options.plugins.tooltip.external = (context) =>
alignedTooltip(context, {
name: "cost-per-proof",
title: "Cost per proof",
items: [
{ title: "Cost", id: "cost" },
{ title: "Fee per proof", id: "cost" },
{ title: "Age", id: "age" },
{ title: "Merkle root", id: "merkle_root" },
{ title: "Block number", id: "block_number" },
Expand Down
43 changes: 43 additions & 0 deletions explorer/assets/vendor/charts/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const findClosestIndex = (target, values) => {
let closestIndex = 0;
let smallestDiff = Math.abs(values[0] - target);
for (let i = 1; i < values.length; i++) {
const diff = Math.abs(values[i] - target);
if (diff < smallestDiff) {
closestIndex = i;
smallestDiff = diff;
}
}
return closestIndex;
};

/**
* A callback function to customize y-axis tick labels by showing only the zero, minimum and maximum data values.
*
* @param {Object} data - The chart data object containing datasets and their values.
* @param {Function} renderText - A function to format and render text for the tick labels.
* @returns {Function} - A callback function for Chart.js tick customization.
*
* The returned function compares the current tick index with the indices of the values closest
* to the minimum and maximum data points, and displays these values formatted using the
* `renderText` function.
*
* @example
* options.scales.y.ticks.callback = yTickCallbackShowMinAndMaxValues(data, (val) => `${val} USD`);
*/
export const yTickCallbackShowMinAndMaxValues =
(data, renderText) => (_value, index, values) => {
if (index === 0) return renderText(0);

const dataY = data.datasets[0].data.map((point) => parseFloat(point.y));
const sortedData = dataY.sort((a, b) => b - a);
const min = sortedData[0];
const max = sortedData[sortedData.length - 1];
const valsData = values.map((item) => item.value);
const idxClosestToMin = findClosestIndex(min, valsData);
const idxClosestToMax = findClosestIndex(max, valsData);

if (index == idxClosestToMin) return renderText(min);
if (index == idxClosestToMax) return renderText(max);
return "";
};
13 changes: 10 additions & 3 deletions explorer/assets/vendor/charts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const applyOptionsByChartId = (id, options, data) => {
export default {
mounted() {
this.initChart();
window.addEventListener("theme-changed", this.reinitChart.bind(this));
window.addEventListener("resize", this.resizeChart.bind(this));
},

updated() {
Expand All @@ -31,9 +31,8 @@ export default {
destroyed() {
if (this.chart) {
this.chart.destroy();
window.removeEventListener("resize", this.resizeChart.bind(this));
}

window.removeEventListener("theme-changed", this.reinitChart.bind(this));
},

initChart() {
Expand All @@ -51,6 +50,8 @@ export default {
data,
options,
});

this.resizeChart();
},

reinitChart() {
Expand All @@ -59,4 +60,10 @@ export default {
}
this.initChart();
},

resizeChart() {
if (this.chart) {
this.chart.resize();
}
},
};
24 changes: 14 additions & 10 deletions explorer/assets/vendor/charts/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const tooltipComponent = ({ title, isTooltipClickable, items }) => `
*
* @param {Object} context - The chart.js context, typically passed as `this` within chart hooks.
* @param {Object} params - An object containing configuration for the tooltip.
* @param {Object} params.name - A string that serves as an identifier for the tooltip.
* @param {string} params.title - The title text to display in the tooltip.
* @param {Array} params.items - An array of items (with ids) to be displayed inside the tooltip.
* @param {Array} params.onTooltipClick - A callback that receives `tooltipModel` and gets triggered when the tooltip is clicked.
Expand All @@ -36,6 +37,7 @@ const tooltipComponent = ({ title, isTooltipClickable, items }) => `
*
* @example
* alignedTooltip(context, {
* name: "my-chart",
* title: "Tooltip Title",
* items: [{ title: "Cost", id: "cost_id" }, { title: "Timestamp", id: "timestamp_id" }],
* onTooltipClick: (tooltipModel) => {
Expand All @@ -58,15 +60,15 @@ const tooltipComponent = ({ title, isTooltipClickable, items }) => `
*/
export const alignedTooltip = (
context,
{ title, items, onTooltipClick, onTooltipUpdate }
{ name, title, items, onTooltipClick, onTooltipUpdate }
) => {
const tooltipModel = context.tooltip;
let tooltipEl = document.getElementById("chartjs-tooltip");
let tooltipEl = document.getElementById(`chartjs-tooltip-${name}`);
if (!tooltipEl) {
tooltipEl = document.createElement("div");
tooltipEl.style = "transition: opacity 0.3s;";
tooltipEl.style = "transition: left 0.1s;";
tooltipEl.id = "chartjs-tooltip";
tooltipEl.id = `chartjs-tooltip-${name}`;
tooltipEl.innerHTML = tooltipComponent({
title,
isTooltipClickable: !!onTooltipClick,
Expand All @@ -81,6 +83,10 @@ export const alignedTooltip = (
tooltipEl.style.opacity = 0;
tooltipEl.style.zIndex = -1;
};
// this is needed to maintain responsiveness
window.addEventListener("resize", () => {
tooltipEl.remove();
});
if (onTooltipClick)
tooltipEl.querySelector(".chart-tooltip-dot").onclick = () =>
onTooltipClick(tooltipModel);
Expand All @@ -91,12 +97,6 @@ export const alignedTooltip = (
tooltipEl.style.opacity = 0;
return;
}
tooltipEl.classList.remove("above", "below", "no-transform");
if (tooltipModel.yAlign) {
tooltipEl.classList.add(tooltipModel.yAlign);
} else {
tooltipEl.classList.add("no-transform");
}

const values = onTooltipUpdate(tooltipModel);
items.forEach((item) => {
Expand All @@ -115,5 +115,9 @@ export const alignedTooltip = (
tooltipModel.caretX +
"px";
tooltipEl.style.top =
position.top + window.scrollY + tooltipModel.caretY + "px";
position.top -
tooltipEl.offsetHeight +
window.scrollY +
tooltipModel.caretY +
"px";
};
2 changes: 0 additions & 2 deletions explorer/assets/vendor/dark_mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ const setupThemeToggle = () => {
.getElementById("theme-toggle")
.addEventListener("click", function () {
toggleVisibility(!isDark());
// chart.js listens for this event to re-render the chart and update its colors
window.dispatchEvent(new Event("theme-changed"));
});
};

Expand Down
2 changes: 1 addition & 1 deletion explorer/assets/vendor/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class Tooltip {
setupFloatingUI() {
this.cleanup = autoUpdate(this.$parent, this.$tooltip, () => {
computePosition(this.$parent, this.$tooltip, {
placement: "top",
placement: "bottom",
middleware: [offset(5), flip(), shift({ padding: 5 })]
}).then(({ x, y }) => {
Object.assign(this.$tooltip.style, {
Expand Down
13 changes: 13 additions & 0 deletions explorer/lib/explorer/models/batches.ex
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,19 @@ defmodule Batches do
result -> result
end
end

def get_avg_fee_per_proof() do
query =
from(b in Batches,
where: b.is_verified == true,
select: avg(b.fee_per_proof)
)

case Explorer.Repo.one(query) do
nil -> 0
result -> result
end
end

def get_amount_of_verified_proofs() do
query = from(b in Batches,
Expand Down
2 changes: 1 addition & 1 deletion explorer/lib/explorer_web/components/assets_cta.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ defmodule AssetsCTAComponent do
View all active operators
</.tooltip>
</.link>
<.link navigate={~p"/restakes"} class="flex-1 flex flex-col justify-start gap-0.5 group">
<.link navigate={~p"/restaked"} class="flex-1 flex flex-col justify-start gap-0.5 group">
<div class="text-muted-foreground font-semibold flex gap-2 items-center">
<h2>
Total Restaked
Expand Down
51 changes: 51 additions & 0 deletions explorer/lib/explorer_web/components/batches_table.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
defmodule ExplorerWeb.BatchesTable do
use Phoenix.Component
use ExplorerWeb, :live_component

attr(:batches, :list, required: true)

def batches_table(assigns) do
~H"""
<.table id="batches" rows={@batches}>
<:col :let={batch} label="Batch Hash" class="text-left">
<.link navigate={~p"/batches/#{batch.merkle_root}"}>
<span class="inline-flex gap-x-3 items-center group-hover:text-foreground/80">
<%= Helpers.shorten_hash(batch.merkle_root, 6) %>
<.right_arrow />
<.tooltip>
<%= batch.merkle_root %>
</.tooltip>
</span>
</.link>
</:col>
<:col :let={batch} label="Status">
<.dynamic_badge_for_batcher status={Helpers.get_batch_status(batch)} />
</:col>
<:col :let={batch} label="Age">
<span class="md:px-0" title={batch.submission_timestamp}>
<%= batch.submission_timestamp |> Helpers.parse_timeago() %>
</span>
</:col>
<:col :let={batch} label="Block Number">
<%= batch.submission_block_number |> Helpers.format_number() %>
</:col>

<:col :let={batch} label="Fee per proof">
<%= case EthConverter.wei_to_usd(batch.fee_per_proof, 6) do %>
<% {:ok, usd} -> %>
<%= "#{usd} USD" %>
<% {:error, _} -> %>
<%= "N/A" %>
<% end %>
<.tooltip>
~= <%= EthConverter.wei_to_eth(batch.fee_per_proof, 6) %> ETH
</.tooltip>
</:col>

<:col :let={batch} label="Number of proofs">
<%= batch.amount_of_proofs |> Helpers.format_number() %>
</:col>
</.table>
"""
end
end
Loading