Skip to content

Commit

Permalink
Merge pull request #98 from ar-io/develop
Browse files Browse the repository at this point in the history
Release v1.3.0 to production
  • Loading branch information
kunstmusik authored Oct 21, 2024
2 parents a7aec6d + e91d131 commit 857c82c
Show file tree
Hide file tree
Showing 19 changed files with 1,704 additions and 552 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ cache
dist-id.txt
dist-manifest.csv
dist-manifest.json
package-lock.json
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.3.0] - 2024-10-21

### Added

* New Dashboard home page that visualizes data for the state of the gateway network

## [1.2.0] - 2024-10-17

### Added
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@ar-io/network-portal",
"private": true,
"version": "1.2.0",
"version": "1.3.0",
"type": "module",
"scripts": {
"build": "yarn clean && tsc --build tsconfig.build.json && NODE_OPTIONS=--max-old-space-size=32768 vite build",
Expand All @@ -20,7 +20,7 @@
"deploy": "yarn build && permaweb-deploy --ant-process ${DEPLOY_ANT_PROCESS_ID}"
},
"dependencies": {
"@ar.io/sdk": "2.3.2-alpha.2",
"@ar.io/sdk": "2.3.2",
"@fontsource/rubik": "^5.0.19",
"@headlessui/react": "^1.7.19",
"@radix-ui/react-tooltip": "^1.0.7",
Expand All @@ -43,6 +43,7 @@
"react-dom": "^18.2.0",
"react-hot-toast": "^2.4.1",
"react-router-dom": "^6.22.1",
"recharts": "^2.13.0",
"zustand": "^4.5.1"
},
"devDependencies": {
Expand Down
7 changes: 4 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import WalletProvider from './components/WalletProvider';
import AppRouterLayout from './layout/AppRouterLayout';
import Loading from './pages/Loading';
import NotFound from './pages/NotFound';
import Dashboard from './pages/Dashboard';

// Main Pages
// const Dashboard = React.lazy(() => import('./pages/Dashboard'));
Expand All @@ -37,16 +38,16 @@ function App() {
const router = sentryCreateBrowserRouter(
createRoutesFromElements(
<Route element={<AppRouterLayout />} errorElement={<NotFound />}>
<Route index path="/" element={<Navigate to="/gateways" />} />
{/* <Route
<Route index path="/" element={<Navigate to="/dashboard" />} />
<Route
path="dashboard"
element={
<Suspense fallback={<Loading />}>
<Dashboard />
</Suspense>
}
/>
, */}
,
<Route
path="gateways/:ownerId/reports/:reportId"
element={
Expand Down
40 changes: 40 additions & 0 deletions src/components/Streak.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { StreakDownArrowIcon, StreakUpArrowIcon } from './icons';

const Streak = ({
streak,
fixedDigits = 0,
rightLabel = '',
}: {
streak: number;
fixedDigits?: number;
rightLabel?: string;
}) => {
if (streak === 0) {
return '';
}

if (streak === Number.NEGATIVE_INFINITY) {
return 'N/A';
}

const colorClasses =
streak > 0
? 'border-streak-up/[.56] bg-streak-up/[.1] text-streak-up'
: 'border-text-red/[.56] bg-text-red/[.1] text-text-red';
const icon =
streak > 0 ? (
<StreakUpArrowIcon className="size-3" />
) : (
<StreakDownArrowIcon className="size-3" />
);

return (
<div
className={`flex w-fit items-center gap-1 rounded-xl border py-0.5 pl-[.4375rem] pr-[.5625rem] ${colorClasses}`}
>
{icon} {Math.abs(streak).toFixed(fixedDigits)}{rightLabel}
</div>
);
};

export default Streak;
32 changes: 32 additions & 0 deletions src/hooks/useArNSStats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useGlobalState } from '@src/store';
import { useQuery } from '@tanstack/react-query';

export type ArNSStats = {
namesPurchased: number;
demandFactor: number;
activeAuctions: number;
};

const useArNSStats = () => {
const arioReadSDK = useGlobalState((state) => state.arIOReadSDK);

const res = useQuery<ArNSStats>({
queryKey: ['arNSStats', arioReadSDK],
queryFn: async () => {
if (!arioReadSDK) throw new Error('arIOReadSDK not initialized');

const demandFactor = await arioReadSDK.getDemandFactor();

const records = await arioReadSDK.getArNSRecords({ limit: 1});

return {
demandFactor,
namesPurchased: records.totalItems,
activeAuctions: 0,
};
},
});
return res;
};

export default useArNSStats;
37 changes: 37 additions & 0 deletions src/hooks/useGatewaysPerEpoch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useGlobalState } from '@src/store';
import { useQuery } from '@tanstack/react-query';
import useEpochs from './useEpochs';

export type GatewayEpochCount = {
epochIndex: number;
totalEligibleGateways: number;
};

const useGatewaysPerEpoch = () => {
const arioReadSDK = useGlobalState((state) => state.arIOReadSDK);
const { data: epochs } = useEpochs();

const res = useQuery<Array<GatewayEpochCount>>({
queryKey: ['gatewaysPerEpoch', epochs, arioReadSDK],
queryFn: () => {
if (!arioReadSDK || !epochs) {
throw new Error('arIOReadSDK not initialized or epochs not available');
}

return epochs
.filter((epoch) => epoch !== undefined)
.sort((a, b) => a!.epochIndex - b!.epochIndex)
.map((epoch) => {
if (!epoch) throw new Error('Epoch not available');
return {
epochIndex: epoch.epochIndex,
totalEligibleGateways:
epoch.distributions.totalEligibleGateways ||
Object.keys(epoch.distributions.rewards.eligible).length,
};
});
},
});
return res;
};
export default useGatewaysPerEpoch;
17 changes: 17 additions & 0 deletions src/hooks/useTokenSupply.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useGlobalState } from '@src/store';
import { useQuery } from '@tanstack/react-query';

const useTokenSupply = () => {
const arioReadSDK = useGlobalState((state) => state.arIOReadSDK);

const res = useQuery({
queryKey: ['tokenSupply'],
queryFn: () => {
if (!arioReadSDK) throw new Error('arIOReadSDK not initialized');
return arioReadSDK.getTokenSupply();
},
});
return res;
};

export default useTokenSupply;
17 changes: 13 additions & 4 deletions src/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
BinocularsIcon,
CloseDrawerIcon,
ContractIcon,
DashboardIcon,
DocsIcon,
GatewaysIcon,
LinkArrowIcon,
Expand All @@ -16,7 +17,11 @@ import {
} from '../components/icons';

const ROUTES_PRIMARY = [
// { title: 'Dashboard', icon: <DashboardIcon className="size-4" />, path: '/dashboard' },
{
title: 'Dashboard',
icon: <DashboardIcon className="size-4" />,
path: '/dashboard',
},
{
title: 'Gateways',
icon: <GatewaysIcon className="size-4" />,
Expand Down Expand Up @@ -60,7 +65,7 @@ const Sidebar = () => {

return (
<aside className={sideBarClasses}>
<div className="flex h-9 pb-16">
<div className="flex h-9 pb-24">
<ArioLogoIcon className="h-[1.6875rem] w-[2.125rem]" />
{sidebarOpen && (
<div className="pl-3">
Expand Down Expand Up @@ -106,7 +111,11 @@ const Sidebar = () => {
<hr className="text-divider" />
<div className="pt-6">
<div
className={sidebarOpen ? 'flex items-center justify-end' : 'flex items-center justify-center'}
className={
sidebarOpen
? 'flex items-center justify-end'
: 'flex items-center justify-center'
}
>
{sidebarOpen && (
<div className="grow pl-3 text-xs text-low/50">
Expand All @@ -116,7 +125,7 @@ const Sidebar = () => {
)}
<button onClick={() => setSidebarOpen(!sidebarOpen)}>
{sidebarOpen ? (
<CloseDrawerIcon className="size-5"/>
<CloseDrawerIcon className="size-5" />
) : (
<OpenDrawerIcon className="size-5" />
)}
Expand Down
14 changes: 0 additions & 14 deletions src/pages/Dashboard.tsx

This file was deleted.

46 changes: 46 additions & 0 deletions src/pages/Dashboard/ArNSStatsPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Placeholder from '@src/components/Placeholder';
import useArNSStats from '@src/hooks/useArNSStats';
import { formatWithCommas } from '@src/utils';

const ArNSStatsPanel = () => {
const { data: arnsStats } = useArNSStats();

return (
<div className="flex min-w-[22rem] flex-col rounded-xl border border-grey-500 px-6 py-5">
<div className=" text-sm text-mid">ArNS Names Purchased</div>
<div className="self-center px-24 py-6 text-center text-[2.625rem]">
{arnsStats ? (
formatWithCommas(arnsStats.namesPurchased)
) : (
<Placeholder />
)}
</div>
<div className="flex h-full justify-between align-bottom font-bold text-high">
<div className="flex flex-col place-items-start text-left text-xs">
<div className="grow" />
{arnsStats ? (
<>
<div>{arnsStats.demandFactor.toFixed(4)}</div>
<div>Demand Factor</div>
</>
) : (
<Placeholder />
)}
</div>
<div className="flex flex-col place-items-end text-right text-xs">
<div className="grow" />
{arnsStats ? (
<>
<div>0</div>
<div>Active Auctions</div>
</>
) : (
<Placeholder />
)}
</div>
</div>
</div>
);
};

export default ArNSStatsPanel;
Loading

0 comments on commit 857c82c

Please sign in to comment.