Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { createPortal } from "react-dom";
import { Icon } from "@/components/Icon";
import {
EXTERNAL_GOVERNANCE_FRONTENDS,
type ExternalFrontend,
} from "@/config/externalGovernance";

interface ExternalGovernanceModalProps {
isOpen: boolean;
onClose: () => void;
}

export function ExternalGovernanceModal({
isOpen,
onClose,
}: ExternalGovernanceModalProps) {
if (!isOpen) return null;

return createPortal(
<div className="fixed inset-0 backdrop-blur-xs z-50 flex items-center justify-center p-4">
<div className="bg-ink border border-parchment/20 max-w-md w-full p-6 relative">
{/* Header */}
<div className="flex items-center justify-between mb-6">
<h3 className="font-oracle-standard text-xl text-parchment">
Governance
</h3>
<button
onClick={onClose}
className="text-parchment/60 hover:text-parchment transition-colors p-2"
aria-label="Close modal"
>
<Icon name="x" className="w-5 h-5" />
</button>
</div>

{/* Description */}
<p className="text-parchment/80 text-sm mb-6">
Access Aztec governance through one of the community-hosted frontends
below:
</p>

{/* Frontends List */}
<div className="space-y-4">
{EXTERNAL_GOVERNANCE_FRONTENDS.map((frontend, index) => (
<FrontendItem key={index} frontend={frontend} />
))}
</div>

{/* Close button */}
<button
onClick={onClose}
className="mt-6 w-full py-3 border border-parchment/20 text-parchment hover:border-parchment/40 transition-colors font-oracle-standard"
>
Close
</button>
</div>
</div>,
document.body
);
}

function FrontendItem({ frontend }: { frontend: ExternalFrontend }) {
const isComingSoon = !frontend.url;

return (
<div className="p-4 border border-parchment/10 hover:border-parchment/20 transition-colors">
<div className="flex items-center justify-between">
<div>
{isComingSoon ? (
<span className="font-oracle-standard text-parchment/60">
{frontend.name}
</span>
) : (
<a
href={frontend.url}
target="_blank"
rel="noopener noreferrer"
className="font-oracle-standard text-parchment hover:text-chartreuse transition-colors"
>
{frontend.name}
<Icon name="externalLink" className="w-4 h-4 inline ml-2" />
</a>
)}
<p className="text-sm text-parchment/60 mt-1">
Hosted by {frontend.hostedBy}
</p>
</div>
{isComingSoon && (
<span className="text-xs text-chartreuse font-oracle-standard px-2 py-1 border border-chartreuse/30">
Coming Soon
</span>
)}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ExternalGovernanceModal } from "./ExternalGovernanceModal";
102 changes: 52 additions & 50 deletions staking-dashboard/src/components/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,15 @@ import { useState } from "react"
import { Link } from "react-router-dom"
import { Icon } from "@/components/Icon"
import { CustomConnectButton } from "../CustomConnectButton"
import { ExternalGovernanceModal } from "@/components/ExternalGovernanceModal"

/**
* Main navigation bar component
* Fixed header with logo, navigation links, and wallet connection
*/
export const Navbar = () => {
const [isMenuOpen, setIsMenuOpen] = useState(false)

const menuItems = [
{ name: "POSITIONS", href: "/my-position" },
{ name: "GOVERNANCE", href: "/governance" },
{ name: "DOCS", href: "https://docs.aztec.network/" },
]
const [isGovernanceModalOpen, setIsGovernanceModalOpen] = useState(false)

const toggleMenu = () => {
setIsMenuOpen(!isMenuOpen)
Expand All @@ -36,27 +32,26 @@ export const Navbar = () => {
</Link>

<div className="hidden md:flex items-center space-x-6 lg:space-x-8">
{menuItems.map((item) =>
item.href.startsWith("/") ? (
<Link
key={item.name}
to={item.href}
className="font-oracle-standard text-sm uppercase tracking-wider text-parchment/80 hover:text-chartreuse transition-colors font-medium"
>
{item.name}
</Link>
) : (
<a
key={item.name}
href={item.href}
target="_blank"
rel="noopener noreferrer"
className="font-oracle-standard text-sm uppercase tracking-wider text-parchment/80 hover:text-chartreuse transition-colors font-medium"
>
{item.name}
</a>
)
)}
<Link
to="/my-position"
className="font-oracle-standard text-sm uppercase tracking-wider text-parchment/80 hover:text-chartreuse transition-colors font-medium"
>
POSITIONS
</Link>
<button
onClick={() => setIsGovernanceModalOpen(true)}
className="font-oracle-standard text-sm uppercase tracking-wider text-parchment/80 hover:text-chartreuse transition-colors font-medium"
>
GOVERNANCE
</button>
Comment on lines +41 to +46
<a
href="https://docs.aztec.network/"
target="_blank"
rel="noopener noreferrer"
className="font-oracle-standard text-sm uppercase tracking-wider text-parchment/80 hover:text-chartreuse transition-colors font-medium"
>
DOCS
</a>
<CustomConnectButton size="sm" />
</div>

Expand All @@ -77,35 +72,42 @@ export const Navbar = () => {
{isMenuOpen && (
<div className="md:hidden bg-ink/95 backdrop-blur-md border-t border-parchment/10 fixed top-20 left-0 right-0 max-h-[calc(100vh-5rem)] overflow-y-auto">
<div className="px-4 py-6 space-y-6">
{menuItems.map((item) =>
item.href.startsWith("/") ? (
<Link
key={item.name}
to={item.href}
onClick={closeMenu}
className="block font-oracle-standard text-base uppercase tracking-wider text-parchment hover:text-chartreuse transition-colors font-medium py-2"
>
{item.name}
</Link>
) : (
<a
key={item.name}
href={item.href}
target="_blank"
rel="noopener noreferrer"
onClick={closeMenu}
className="block font-oracle-standard text-base uppercase tracking-wider text-parchment hover:text-chartreuse transition-colors font-medium py-2"
>
{item.name}
</a>
)
)}
<Link
to="/my-position"
onClick={closeMenu}
className="block font-oracle-standard text-base uppercase tracking-wider text-parchment hover:text-chartreuse transition-colors font-medium py-2"
>
POSITIONS
</Link>
<button
onClick={() => {
closeMenu()
setIsGovernanceModalOpen(true)
}}
className="block font-oracle-standard text-base uppercase tracking-wider text-parchment hover:text-chartreuse transition-colors font-medium py-2 text-left w-full"
>
GOVERNANCE
</button>
Comment on lines +82 to +90
<a
href="https://docs.aztec.network/"
target="_blank"
rel="noopener noreferrer"
onClick={closeMenu}
className="block font-oracle-standard text-base uppercase tracking-wider text-parchment hover:text-chartreuse transition-colors font-medium py-2"
>
DOCS
</a>
<div className="pt-4 border-t border-parchment/10">
<CustomConnectButton fullWidth size="lg" />
</div>
</div>
</div>
)}

<ExternalGovernanceModal
isOpen={isGovernanceModalOpen}
onClose={() => setIsGovernanceModalOpen(false)}
/>
</nav>
)
};
23 changes: 23 additions & 0 deletions staking-dashboard/src/config/externalGovernance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* External Governance Frontends Configuration
*
* This file contains the list of external governance frontends that users
* can be directed to. Update this list as new frontends become available.
*/

export interface ExternalFrontend {
/** Display name of the frontend */
name: string;
/** Organization hosting this frontend */
hostedBy: string;
/** URL to the frontend - undefined means "Coming Soon" */
url?: string;
}

export const EXTERNAL_GOVERNANCE_FRONTENDS: ExternalFrontend[] = [
{
name: "Aztec Governance",
hostedBy: "Nethermind",
url: undefined, // Coming soon - replace with URL when live: "http://aztecgov.nethermind.io/"
},
];
11 changes: 4 additions & 7 deletions staking-dashboard/src/routes/AppRoutes.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
// routes/AppRoutes.jsx
import { Route, Routes } from "react-router-dom"
// routes/AppRoutes.tsx
import { Navigate, Route, Routes } from "react-router-dom"
import SharedLayout from "../layouts/SharedLayout"
import BaseLayout from "../layouts/BaseLayout"
import MinimalLayout from "../layouts/MinimalLayout"
import { MyPositionPage } from "../pages/ATP"
import { RegisterValidatorPage } from "../pages/RegisterValidator"
import { StakingProvidersPage, StakingProviderDetailPage } from "../pages/Providers"
import StakePortal from "@/pages/StakePortal/StakePortal"
import { NotFoundPage } from "@/pages/NotFound/NotFoundPage"
import { GovernancePage } from "../pages/Governance"

export default function AppRoutes() {
return (
Expand All @@ -21,9 +19,8 @@ export default function AppRoutes() {
<Route path="/stake" element={<StakePortal />} />
<Route path="/register-validator" element={<RegisterValidatorPage />} />
</Route>
<Route element={<MinimalLayout />}>
<Route path="/governance/:proposalId?" element={<GovernancePage />} />
</Route>
{/* Governance is disabled - redirect to home */}
<Route path="/governance/*" element={<Navigate to="/" replace />} />
<Route element={<BaseLayout />}>
<Route path="*" element={<NotFoundPage />} />
</Route>
Expand Down
Loading