diff --git a/frontend/components/SiteMenu.tsx b/frontend/components/SiteMenu.tsx
new file mode 100644
index 0000000..aba4d1e
--- /dev/null
+++ b/frontend/components/SiteMenu.tsx
@@ -0,0 +1,42 @@
+import React from "https://esm.sh/react@17.0.2";
+import styled from "https://esm.sh/styled-components";
+
+import Anchor from "https://deno.land/x/aleph/framework/react/components/Anchor.ts";
+import { useRouter } from "https://deno.land/x/aleph/framework/react/hooks.ts";
+
+const MenuContainer = styled.div`
+ display: flex;
+ justify-content: center;
+ padding: 0;
+ margin-bottom: 40px;
+`;
+
+const MenuItem = styled.p<{ active: boolean }>`
+ margin: 0 10px;
+ display: block;
+ font-size: 20px;
+ text-align: center;
+ color: #ffd9aa;
+ text-shadow: #000 3px 3px 0px;
+ text-decoration: none;
+
+ border-bottom: ${(props) => (props.active ? "1px solid #555" : "0 solid #fff")};
+`;
+
+const SiteMenuComponent = () => {
+ const router = useRouter();
+ const routePath = router.routePath;
+
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+export default SiteMenuComponent;
diff --git a/frontend/components/SiteTitle.tsx b/frontend/components/SiteTitle.tsx
index 59bb12d..c83637d 100644
--- a/frontend/components/SiteTitle.tsx
+++ b/frontend/components/SiteTitle.tsx
@@ -4,7 +4,7 @@ import styled from "https://esm.sh/styled-components";
import config from "../back/config/config.ts";
const Title = styled.h1`
- margin-top: 40px;
+ margin: 40px 0 20px;
font-size: 42px;
text-align: center;
color: #d97b08;
diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx
index 69b1410..49d4ecb 100644
--- a/frontend/pages/index.tsx
+++ b/frontend/pages/index.tsx
@@ -1,14 +1,14 @@
-import React, { useEffect, useState } from "https://esm.sh/react@17.0.2";
+import React from "https://esm.sh/react@17.0.2";
import styled from "https://esm.sh/styled-components";
import config from "../back/config/config.ts";
-import { IBlock } from "../back/blocks/index.ts";
import { Container } from "../components/Container.ts";
import { Content } from "../components/Content.ts";
import { BlockContainer, Block } from "../components/Block.tsx";
import { Donation } from "../components/Donation.tsx";
import SiteTitle from "../components/SiteTitle.tsx";
+import SiteMenu from "../components/SiteMenu.tsx";
import { useStoreState } from "../state/index.ts";
const DescriptionBlock = styled.div`
@@ -70,6 +70,7 @@ export default function Blocks() {
+
{config.fork.info.map((text, index) => (
{text}
diff --git a/frontend/pages/miners.tsx b/frontend/pages/miners.tsx
new file mode 100644
index 0000000..f17ab2d
--- /dev/null
+++ b/frontend/pages/miners.tsx
@@ -0,0 +1,132 @@
+import React, { useMemo } from "https://esm.sh/react@17.0.2";
+import styled from "https://esm.sh/styled-components";
+
+import config from "../back/config/config.ts";
+
+import { Container } from "../components/Container.ts";
+import { Content } from "../components/Content.ts";
+import SiteTitle from "../components/SiteTitle.tsx";
+import { useStoreState } from "../state/index.ts";
+import SiteMenu from "../components/SiteMenu.tsx";
+
+const Table = styled.table`
+ max-width: 100%;
+ width: 1000px;
+
+ box-shadow: #000 3px 3px 14px;
+ border-radius: 6px;
+ margin: auto;
+ border: 0;
+ border-collapse: collapse;
+ border-radius: 8px;
+ overflow: hidden;
+`;
+
+const TableHead = styled.thead`
+ background-color: #383838;
+`;
+
+const TableBody = styled.tbody`
+ text-align: center;
+ background-color: #434343;
+
+ tr:hover {
+ background-color: #505050;
+ }
+`;
+
+const TableRow = styled.tr`
+ border-bottom: 2px solid #393939;
+
+ &:last-child {
+ border-bottom: 0;
+ }
+`;
+
+const TableHeader = styled.th`
+ color: #efefef;
+ padding: 9px;
+`;
+
+const Cell = styled.td`
+ color: #f0f0f0;
+ padding: 19px;
+`;
+
+const SignallingCell = styled.td`
+ padding: 16px;
+ text-align: center;
+`;
+
+interface IMinerData {
+ [key: string]: {
+ name: string;
+ signals: boolean;
+ };
+}
+
+export default function Miners() {
+ const blocks = useStoreState((store) => store.blocks);
+
+ const forkName = config.fork.name;
+
+ const miners = useMemo(() => {
+ // We have to reverse the array as we have to check
+ // for the latest block by a miner to decide whether they
+ // are signalling or not.
+ const blocksReversed = blocks.slice(0);
+ blocksReversed.reverse();
+
+ return blocksReversed.reduce((prev, currBlock) => {
+ if (!currBlock.miner) {
+ return prev;
+ }
+
+ if (!prev[currBlock.miner]) {
+ prev[currBlock.miner] = {
+ name: currBlock.miner,
+ signals: currBlock.signals ?? false,
+ };
+ }
+ return prev;
+ }, {} as IMinerData);
+ }, [blocks]);
+
+ return (
+
+
+ {forkName} activation - Miners
+
+
+
+
+
+
+
+ Miner name
+ Signals
+
+
+
+
+ {Object.entries(miners).map(([_, miner]) => {
+ return (
+
+ {miner.name} |
+
+ {miner.signals && <>✅>}
+ {!miner.signals && <>🚫>}
+
+
+ );
+ })}
+
+ AntMiner |
+ 🚫
+
+
+
+
+
+ );
+}