forked from emre/www_projectmycelium_com
feat: add nodes page with hosting information and benefits
- Created new NodePage with hero section explaining the Mycelium node network - Added NodeBenefits component showcasing three key advantages of hosting nodes - Integrated nodes route into navigation (header, footer, and routing configuration)
This commit is contained in:
@@ -11,6 +11,7 @@ const ComputePage = lazy(() => import('./pages/compute/ComputePage'));
|
||||
const StoragePage = lazy(() => import('./pages/storage/StoragePage'));
|
||||
const GpuPage = lazy(() => import('./pages/gpu/GpuPage'));
|
||||
const PodsPage = lazy(() => import('./pages/pods/PodsPage'));
|
||||
const NodePage = lazy(() => import('./pages/node/NodePage'));
|
||||
|
||||
function App() {
|
||||
return (
|
||||
@@ -27,6 +28,7 @@ function App() {
|
||||
<Route path="storage" element={<StoragePage />} />
|
||||
<Route path="gpu" element={<GpuPage />} />
|
||||
<Route path="pods" element={<PodsPage />} />
|
||||
<Route path="nodes" element={<NodePage />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</Suspense>
|
||||
|
||||
@@ -30,6 +30,9 @@ export function Footer() {
|
||||
<Link to="/pods" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
|
||||
Pods
|
||||
</Link>
|
||||
<Link to="/nodes" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
|
||||
Nodes
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
<div className="group relative -mx-4 flex items-center self-stretch p-4 transition-colors hover:bg-gray-100 sm:self-auto sm:rounded-2xl lg:mx-0 lg:self-auto lg:p-6">
|
||||
|
||||
@@ -70,6 +70,12 @@ export function Header() {
|
||||
>
|
||||
Pods
|
||||
</Link>
|
||||
<Link
|
||||
to="/nodes"
|
||||
className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
|
||||
>
|
||||
Nodes
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-6">
|
||||
@@ -155,6 +161,13 @@ export function Header() {
|
||||
>
|
||||
Pods
|
||||
</Link>
|
||||
<Link
|
||||
to="/nodes"
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
Nodes
|
||||
</Link>
|
||||
</div>
|
||||
<div className="py-6">
|
||||
<Button
|
||||
|
||||
115
src/pages/node/NodeBenefits.tsx
Normal file
115
src/pages/node/NodeBenefits.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import { SectionHeader, P, Eyebrow, CT, CP } from "@/components/Texts";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
type CircleIconProps = ComponentPropsWithoutRef<"svg">;
|
||||
|
||||
const CircleNumber1Icon = (props: CircleIconProps) => (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" {...props}>
|
||||
<path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2zm.994 5.886c-.83-.777-1.755-.394-1.701.404l-.006.114v8l.007.117a1 1 0 001.986 0l.007-.117V9l-.006-.114z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CircleNumber2Icon = (props: CircleIconProps) => (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" {...props}>
|
||||
<path d="M12 2a10 10 0 110 20 10 10 0 010-20zm1 5h-3a1 1 0 100 2h3v2h-2a2 2 0 00-2 2v2a2 2 0 002 2h3a1 1 0 100-2h-3v-2h2a2 2 0 002-2V9a2 2 0 00-2-2z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CircleNumber3Icon = (props: CircleIconProps) => (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" {...props}>
|
||||
<path d="M12 2a10 10 0 110 20 10 10 0 010-20zm1 5h-2a2 2 0 00-2 2 1 1 0 102 0h2v2h-2c-1.39 0-2.103 1.67-1.11 2.588l.11.098h3v2h-2a1 1 0 100 2h2a2 2 0 002-2v-2a2 2 0 00-.25-.97l-.068-.115.068-.115A1.99 1.99 0 0015 11V9a2 2 0 00-2-2z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const features = [
|
||||
{
|
||||
name: "Build Your Private Setup",
|
||||
description:
|
||||
"Run a node in your home or office and use it to host private workloads, models, and data. Experiment, deploy, or tinker locally, then offset your costs by sharing unused capacity with the network.",
|
||||
icon: CircleNumber1Icon,
|
||||
},
|
||||
{
|
||||
name: "Strengthen the Network",
|
||||
description:
|
||||
"Every node adds bandwidth, compute, and resilience to the Mycelium Grid. Together, hosts form the physical foundation for decentralized communication, storage, and AI.",
|
||||
icon: CircleNumber2Icon,
|
||||
},
|
||||
{
|
||||
name: "Earn Rewards",
|
||||
description:
|
||||
"Share storage, CPU, GPU, and bandwidth. When your resources are used, you earn network rewards based on real demand.",
|
||||
icon: CircleNumber3Icon,
|
||||
},
|
||||
];
|
||||
|
||||
export function NodeBenefits() {
|
||||
return (
|
||||
<section className="bg-[#121212] w-full max-w-8xl mx-auto">
|
||||
{/* Top spacing + border */}
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-800" />
|
||||
<div className="w-full border-t border-l border-r border-gray-800" />
|
||||
|
||||
<div className="relative px-6 lg:px-12 py-12 bg-[#111111] border border-t-0 border-b-0 border-gray-800 max-w-7xl mx-auto">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, amount: 0.3 }}
|
||||
transition={{ duration: 0.6, ease: "easeOut", delay: 0.1 }}
|
||||
className="mx-auto max-w-5xl text-center"
|
||||
>
|
||||
<Eyebrow color="accent">Host</Eyebrow>
|
||||
<SectionHeader
|
||||
className="text-3xl font-medium tracking-tight"
|
||||
color="light"
|
||||
>
|
||||
Benefits of Hosting Nodes
|
||||
</SectionHeader>
|
||||
|
||||
<P className="mt-6" color="light">
|
||||
Hosting a node gives you private compute, contributes to the global
|
||||
Mycelium infrastructure, and unlocks ways to earn from real network
|
||||
usage — all while keeping sovereignty and control.
|
||||
</P>
|
||||
</motion.div>
|
||||
|
||||
{/* Features */}
|
||||
<motion.ul
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true, amount: 0.2 }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
className="mx-auto mt-8 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-8 text-base/7 sm:grid-cols-2 sm:gap-y-16 lg:mx-12 lg:mt-12 lg:max-w-7xl lg:grid-cols-3"
|
||||
>
|
||||
{features.map((feature, index) => (
|
||||
<motion.li
|
||||
key={feature.name}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, amount: 0.2 }}
|
||||
transition={{
|
||||
duration: 0.45,
|
||||
delay: 0.3 + index * 0.15,
|
||||
ease: "easeOut",
|
||||
}}
|
||||
className="rounded-2xl border border-gray-300 bg-white/5 p-8 transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20 backdrop-blur-md"
|
||||
>
|
||||
<feature.icon className="mb-4 h-8 w-8 text-white" />
|
||||
<CT as="span" className="text-left font-semibold" color="light">
|
||||
{feature.name}
|
||||
</CT>
|
||||
<CP className="mt-2 text-left text-sm" color="light">
|
||||
{feature.description}
|
||||
</CP>
|
||||
</motion.li>
|
||||
))}
|
||||
</motion.ul>
|
||||
</div>
|
||||
|
||||
<div className="w-full border-b border-gray-800 bg-[#121212]" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
40
src/pages/node/NodeHero.tsx
Normal file
40
src/pages/node/NodeHero.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@/components/Button'
|
||||
import { Eyebrow, H3, P } from '@/components/Texts'
|
||||
|
||||
export function NodeHero() {
|
||||
return (
|
||||
<div className="">
|
||||
{/* Boxed container */}
|
||||
<div
|
||||
className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-gray-100 bg-white overflow-hidden"
|
||||
>
|
||||
{/* Inner padding */}
|
||||
<div className="px-6 py-16 lg:py-16">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<Eyebrow>MYCELIUM NODES</Eyebrow>
|
||||
<H3 as="h1" className="mt-4">
|
||||
Host a Node. Power the Network.
|
||||
</H3>
|
||||
<P className="mt-6 text-gray-800">
|
||||
The Mycelium Network runs on nodes hosted by people and organizations around the world. Each node adds capacity, resilience, and sovereignty, expanding a global network for private, distributed compute and AI.
|
||||
</P>
|
||||
|
||||
<div className="mt-10 flex items-center justify-center gap-x-6">
|
||||
<Button href="/host" variant="solid" color="cyan">
|
||||
Host a Node
|
||||
</Button>
|
||||
<Button href="#" variant="outline">
|
||||
Learn More
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* ✅ Bottom horizontal line with spacing */}
|
||||
<div className="w-full border-b border-gray-100" />
|
||||
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
14
src/pages/node/NodePage.tsx
Normal file
14
src/pages/node/NodePage.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import { NodeHero } from './NodeHero';
|
||||
import { NodeBenefits } from './NodeBenefits';
|
||||
|
||||
const NodePage: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<NodeHero />
|
||||
<NodeBenefits />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NodePage;
|
||||
Reference in New Issue
Block a user