Skip to content

Commit e8a2eff

Browse files
committed
feat: aggregated proofs table view
1 parent 7d2d9fb commit e8a2eff

8 files changed

Lines changed: 204 additions & 2 deletions

File tree

explorer/lib/explorer/models/aggregated_proofs.ex

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
defmodule AggregatedProofs do
22
require Logger
33
use Ecto.Schema
4+
import Ecto.Query
45
import Ecto.Changeset
56

67
@primary_key {:number, :integer, autogenerate: false}
@@ -64,4 +65,22 @@ defmodule AggregatedProofs do
6465
def get_aggregated_proof_by_number(number) do
6566
Explorer.Repo.get_by(AggregatedProofs, number: number)
6667
end
68+
69+
def get_paginated_proofs(%{page: page, page_size: size}) do
70+
query =
71+
from(proof in AggregatedProofs,
72+
order_by: [desc: proof.block_number],
73+
limit: ^size,
74+
offset: ^((page - 1) * size),
75+
select: proof
76+
)
77+
78+
Explorer.Repo.all(query)
79+
end
80+
81+
def get_last_page(page_size) do
82+
total_proofs = Explorer.Repo.aggregate(AggregatedProofs, :count, :number)
83+
last_page = div(total_proofs, page_size)
84+
if rem(total_proofs, page_size) > 0, do: last_page + 1, else: last_page
85+
end
6786
end
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
defmodule ExplorerWeb.AggProofsTable do
2+
use Phoenix.Component
3+
use ExplorerWeb, :live_component
4+
5+
attr(:agg_proofs, :list, required: true)
6+
7+
def agg_proofs_table(assigns) do
8+
~H"""
9+
<.table id="agg_proofs" rows={@proofs}>
10+
<:col :let={proof} label="Merkle root" class="text-left">
11+
<.link navigate={~p"/aggregated_proofs/#{proof.number}"}>
12+
<span class="inline-flex gap-x-3 items-center group-hover:text-foreground/80">
13+
<%= Helpers.shorten_hash(proof.merkle_root, 6) %>
14+
<.right_arrow />
15+
<.tooltip>
16+
<%= proof.merkle_root %>
17+
</.tooltip>
18+
</span>
19+
</.link>
20+
</:col>
21+
<:col :let={proof} label="Status">
22+
<.dynamic_badge_for_agg_proof status={proof.status} />
23+
</:col>
24+
<:col :let={proof} label="Age">
25+
<span class="md:px-0" title={proof.age}>
26+
<%= proof.age %>
27+
</span>
28+
</:col>
29+
<:col :let={proof} label="Block Number">
30+
<%= proof.block_number |> Helpers.format_number() %>
31+
</:col>
32+
33+
<:col :let={proof} label="Number of proofs">
34+
<%= proof.number_of_proofs |> Helpers.format_number() %>
35+
</:col>
36+
</.table>
37+
"""
38+
end
39+
end

explorer/lib/explorer_web/components/footer.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ defmodule FooterComponent do
99
{"General",
1010
[
1111
{"Batches", "/batches"},
12+
{"Aggregation", "/aggregated_proofs"},
1213
{"Operators", "/operators"},
1314
{"Restake", "/restaked"}
1415
]},

explorer/lib/explorer_web/components/nav.ex

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ defmodule NavComponent do
5656
>
5757
Batches
5858
</.link>
59+
<.link
60+
class={
61+
active_view_class(assigns.socket.view, [
62+
ExplorerWeb.AggProofs.Index,
63+
ExplorerWeb.AggProof.Index
64+
])
65+
}
66+
navigate={~p"/aggregated_proofs"}
67+
>
68+
Aggregation
69+
</.link>
5970
<.link
6071
class={
6172
active_view_class(assigns.socket.view, [

explorer/lib/explorer_web/live/pages/agg_proof/index.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
defmodule ExplorerWeb.AggProofs.Index do
1+
defmodule ExplorerWeb.AggProof.Index do
22
require Logger
33
use ExplorerWeb, :live_view
44

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
defmodule ExplorerWeb.AggProofs.Index do
2+
require Logger
3+
import ExplorerWeb.AggProofsTable
4+
use ExplorerWeb, :live_view
5+
6+
@page_size 15
7+
8+
@impl true
9+
def mount(_, params, socket) do
10+
current_page = get_current_page(params)
11+
12+
proofs =
13+
AggregatedProofs.get_paginated_proofs(%{
14+
page: current_page,
15+
page_size: @page_size
16+
})
17+
|> Enum.map(fn proof -> proof |> Map.merge(%{age: "2 days ago"}) end)
18+
19+
# TODO fetch from aggregation not batch
20+
remaining_time = Helpers.get_next_scheduled_batch_remaining_time()
21+
22+
{
23+
:ok,
24+
assign(
25+
socket,
26+
proofs: proofs,
27+
current_page: current_page,
28+
last_page: AggregatedProofs.get_last_page(@page_size),
29+
next_scheduled_batch_remaining_time_percentage:
30+
Helpers.get_next_scheduled_batch_remaining_time_percentage(remaining_time),
31+
next_scheduled_batch_remaining_time: remaining_time
32+
)
33+
}
34+
end
35+
36+
@impl true
37+
def handle_event("change_page", %{"page" => page}, socket) do
38+
{:noreply, push_navigate(socket, to: ~p"/batches?page=#{page}")}
39+
end
40+
41+
defp get_current_page(params) do
42+
case params |> Map.get("page") do
43+
nil ->
44+
1
45+
46+
page ->
47+
case Integer.parse(page) do
48+
{number, _} ->
49+
if number < 1, do: 1, else: number
50+
51+
:error ->
52+
1
53+
end
54+
end
55+
end
56+
end
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<div class="flex flex-col space-y-3 text-foreground px-1 sm:max-w-lg md:max-w-3xl lg:max-w-5xl mx-auto capitalize">
2+
<.card_preheding>Aggregation</.card_preheding>
3+
<!-- Progress bar based on https://dev.to/mikekennedydev/progress-bar-text-colour-1gg6 -->
4+
<.live_component
5+
id="labeled-progress-bar"
6+
module={LabeledProgressBarComponent}
7+
percent_progress={@next_scheduled_batch_remaining_time_percentage}
8+
label={
9+
case @next_scheduled_batch_remaining_time do
10+
0 -> "Next aggregated proof is around the corner!"
11+
time -> "Next aggregated proof in #{time} minutes"
12+
end
13+
}/>
14+
<%= if @proofs != :empty and @proofs != [] do %>
15+
<.card_background class="w-full overflow-x-auto sm:col-span-2">
16+
<.agg_proofs_table proofs={@proofs} />
17+
</.card_background>
18+
<% else %>
19+
<.empty_card_background text="No aggregated proofs To Display." class="sm:col-span-2" />
20+
<% end %>
21+
<div class="flex gap-x-2 items-center justify-center w-full">
22+
<%= if @current_page >= 2 do %>
23+
<.link navigate={~p"/batches?page=#{1}"}>
24+
<.button class="text-muted-foreground group">
25+
First
26+
</.button>
27+
</.link>
28+
<% end %>
29+
<%= if @current_page > 1 do %>
30+
<.link navigate={~p"/aggregated_proofs?page=#{@current_page - 1}"}>
31+
<.button
32+
icon="arrow-right-solid"
33+
icon_class="group-hover:translate-x-1 transition-all duration-150"
34+
class="text-muted-foreground size-10 group rotate-180"
35+
>
36+
<span class="sr-only">Previous Page</span>
37+
</.button>
38+
</.link>
39+
<% end %>
40+
<form phx-submit="change_page" class="flex items-center">
41+
<label for="page" class="text-foreground sr-only">Page: </label>
42+
<input
43+
name="page"
44+
id="page"
45+
type="number"
46+
class={
47+
classes([
48+
"text-center border border-foreground/20 text-muted-foreground w-20 focus:ring-primary",
49+
"phx-submit-loading:opacity-75 rounded-lg bg-card hover:bg-muted py-2 px-3",
50+
"text-sm font-semibold leading-6 text-foregound active:text-foregound/80",
51+
"[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
52+
])
53+
}
54+
value={@current_page}
55+
min="1"
56+
/>
57+
</form>
58+
<%= if @current_page != @last_page do %>
59+
<.link navigate={~p"/aggregated_proofs?page=#{@current_page + 1}"}>
60+
<.button
61+
icon="arrow-right-solid"
62+
icon_class="group-hover:translate-x-1 transition-all duration-150"
63+
class="text-muted-foreground size-10 group"
64+
>
65+
<span class="sr-only">Next Page</span>
66+
</.button>
67+
</.link>
68+
<.link navigate={~p"/batches?page=#{@last_page}"}>
69+
<.button class="text-muted-foreground group">
70+
Last
71+
</.button>
72+
</.link>
73+
<% end %>
74+
</div>
75+
</div>

explorer/lib/explorer_web/router.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ defmodule ExplorerWeb.Router do
4747
live "/", Home.Index
4848
live "/batches/:merkle_root", Batch.Index
4949
live "/batches", Batches.Index
50-
live "/aggregated_proofs/:proof_number", AggProofs.Index
50+
live "/aggregated_proofs", AggProofs.Index
51+
live "/aggregated_proofs/:proof_number", AggProof.Index
5152
live "/restaked", Restakes.Index
5253
live "/restaked/:address", Restake.Index
5354
live "/operators", Operators.Index

0 commit comments

Comments
 (0)