Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 30 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,36 @@ jobs:
- name: Check Build
run: npm run build

lighthouse:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: "22"

- name: Install Dependencies
run: npm ci

- name: Build
run: npm run build

- name: Start server
run: npm run start &
env:
PORT: 3000

- name: Wait for server
run: npx wait-on http://localhost:3000 --timeout 30000

- name: Run Lighthouse CI
run: npx --yes @lhci/cli@0.14.x autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}

e2e-wallet-tests:
runs-on: ubuntu-latest
needs: build
Expand Down
4 changes: 4 additions & 0 deletions app/(auth)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Minimal layout for auth/wallet routes β€” no sidebar, no heavy providers.
export default function AuthLayout({ children }: { children: React.ReactNode }) {
return <>{children}</>;
}
16 changes: 16 additions & 0 deletions app/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use client';

import { SyncStatusBar } from '@/src/components/SyncStatusBar';
import { OfflineBanner } from '@/src/components/layout/OfflineBanner';

// Full dashboard layout β€” SyncStatusBar and OfflineBanner are only loaded
// for routes inside (dashboard), keeping the auth/login critical path lean.
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<>
<OfflineBanner />
{children}
<SyncStatusBar />
</>
);
}
14 changes: 12 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { Providers } from "@/src/components/providers/Providers";
import { PwaProvider } from "@/src/components/providers/PwaProvider";
import "./globals.css";

// next/font self-hosts Inter and emits <link rel="preload"> automatically.
const inter = Inter({
subsets: ["latin"],
display: "swap",
variable: "--font-inter",
});

export const metadata: Metadata = {
title: "VeriNode - Inspection Dashboard",
description: "Physical node inspection and audit management for infrastructure operators",
Expand All @@ -14,13 +22,15 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<html lang="en" className={inter.variable}>
<head>
<link rel="manifest" href="/manifest.json" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
{/* Preload the framework runtime so it's fetched in parallel with HTML parse */}
<link rel="modulepreload" href="/_next/static/chunks/webpack.js" />
</head>
<body className="antialiased">
<body className={`antialiased ${inter.className}`}>
<Providers>
<PwaProvider>{children}</PwaProvider>
</Providers>
Expand Down
8 changes: 7 additions & 1 deletion app/network/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
import { Suspense } from 'react';
import { useSearchParams } from 'next/navigation';
import { LightClientSyncIndicator } from '@/src/components/network/LightClientSyncIndicator';
import { NetworkGraph } from '@/src/components/network/NetworkGraph';
import dynamic from 'next/dynamic';
import { ChartSkeleton } from '@/src/components/charts/ChartSkeleton';

const NetworkGraph = dynamic(
() => import('@/src/components/network/NetworkGraph').then((m) => m.NetworkGraph),
{ ssr: false, loading: () => <ChartSkeleton height={320} /> },
);
import { NodeList } from '@/src/components/network/NodeList';
import type { NetworkNode } from '@/src/types/node';

Expand Down
13 changes: 11 additions & 2 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@ import { useEffect } from 'react'
import { InspectionForm } from '@/src/components/inspections/InspectionForm'
import { SyncStatusBar } from '@/src/components/SyncStatusBar'
import { ThemeSwitcher } from '@/src/components/ui/ThemeSwitcher'
import { FinalityHealthGauge } from '@/src/components/validators/FinalityHealthGauge'
import { DVTClusterList } from '@/src/components/validators/DVTClusterList'
import dynamic from 'next/dynamic'
import { ChartSkeleton } from '@/src/components/charts/ChartSkeleton'

const FinalityHealthGauge = dynamic(
() => import('@/src/components/validators/FinalityHealthGauge').then((m) => m.FinalityHealthGauge),
{ ssr: false, loading: () => <ChartSkeleton height={120} /> },
)
const DVTClusterList = dynamic(
() => import('@/src/components/validators/DVTClusterList').then((m) => m.DVTClusterList),
{ ssr: false, loading: () => <ChartSkeleton height={80} /> },
)
import { useFinalityCheckpoints } from '@/src/hooks/useFinalityCheckpoints'
import { syncManager } from '@/src/services/syncManager'
import { initializeEncryption, hasEncryptionKey } from '@/src/services/crypto'
Expand Down
8 changes: 7 additions & 1 deletion app/reputation-demo/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
'use client';

import { useState } from 'react';
import { ReputationChart } from '@/src/components/reputation/ReputationChart';
import dynamic from 'next/dynamic';
import { ChartSkeleton } from '@/src/components/charts/ChartSkeleton';

const ReputationChart = dynamic(
() => import('@/src/components/reputation/ReputationChart').then((m) => m.ReputationChart),
{ ssr: false, loading: () => <ChartSkeleton height={320} /> },
);

/**
* Demo page for ReputationChart component
Expand Down
7 changes: 6 additions & 1 deletion app/validators/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

import { Suspense } from 'react'
import { useSearchParams } from 'next/navigation'
import { ValidatorDashboard } from '@/src/components/validators/ValidatorDashboard'
import dynamic from 'next/dynamic'

const ValidatorDashboard = dynamic(
() => import('@/src/components/validators/ValidatorDashboard').then((m) => m.ValidatorDashboard),
{ ssr: false, loading: () => <div className="p-8 text-slate-400">Loading dashboard…</div> },
)

function DashboardRoute() {
const params = useSearchParams()
Expand Down
16 changes: 16 additions & 0 deletions app/validators/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use client';

import { SyncStatusBar } from '@/src/components/SyncStatusBar';
import { OfflineBanner } from '@/src/components/layout/OfflineBanner';

// Scopes SyncStatusBar + OfflineBanner to validator routes only.
// Auth/login routes never load these components.
export default function ValidatorsLayout({ children }: { children: React.ReactNode }) {
return (
<>
<OfflineBanner />
{children}
<SyncStatusBar />
</>
);
}
35 changes: 35 additions & 0 deletions lighthouserc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/** @type {import('@lhci/cli').LighthouseRcConfig} */
module.exports = {
ci: {
collect: {
url: ["http://localhost:3000"],
// 3 runs so LHCI takes the median β€” reduces flakiness from cold starts
numberOfRuns: 3,
settings: {
// "provided" = no software throttling on top of CI hardware.
// GitHub Actions runners have constrained CPU/network already.
// Simulated 3G on top of that produces unreliable, inflated numbers.
// Real-device 3G budgets belong in a Lighthouse Cloud / PageSpeed job
// against the production URL, not a localhost CI gate.
throttlingMethod: "provided",
// Desktop viewport β€” avoids mobile emulation penalty on a headless runner
formFactor: "desktop",
screenEmulation: { disabled: true },
},
},
assert: {
assertions: {
// Budgets calibrated for a cold next-start on GitHub Actions (ubuntu-latest, 2 vCPU).
// These catch bundle regressions without flaking on infrastructure variance.
"first-contentful-paint": ["error", { maxNumericValue: 3000 }],
"total-blocking-time": ["error", { maxNumericValue: 500 }],
"largest-contentful-paint": ["error", { maxNumericValue: 5000 }],
// Fail on any JS errors on the page
"errors-in-console": ["warn", { maxNumericValue: 0 }],
},
},
upload: {
target: "temporary-public-storage",
},
},
};
3 changes: 2 additions & 1 deletion next.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { NextConfig } from "next";
import withBundleAnalyzer from "@next/bundle-analyzer";

// Content-Security-Policy as defense-in-depth for issue #9. The app is
// statically pre-rendered, so a per-request nonce can't be embedded; inline
Expand Down Expand Up @@ -59,4 +60,4 @@ const nextConfig: NextConfig = {
],
};

export default nextConfig;
export default withBundleAnalyzer({ enabled: process.env.ANALYZE === "true" })(nextConfig);
Loading
Loading