feat: add cloud services dropdown menu in header
- Replaced individual cloud service pages with CallToAction components for Compute, Storage and GPU - Added dropdown menu in header to consolidate cloud service navigation options - Removed redundant page components (Compute.tsx, Storage.tsx, Gpu.tsx) that shared identical layouts - Updated route components to use new CallToAction components - Added ChevronDownIcon to visually indicate dropdown functionality
This commit is contained in:
		
							
								
								
									
										12
									
								
								src/App.tsx
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/App.tsx
									
									
									
									
									
								
							@@ -5,9 +5,9 @@ import CloudPage from './pages/cloud/CloudPage'
 | 
			
		||||
import NetworkPage from './pages/network/NetworkPage'
 | 
			
		||||
import AgentsPage from './pages/agents/AgentsPage'
 | 
			
		||||
import DownloadPage from './pages/download/DownloadPage'
 | 
			
		||||
import Compute from './pages/compute/Compute'
 | 
			
		||||
import Storage from './pages/storage/Storage'
 | 
			
		||||
import Gpu from './pages/gpu/Gpu'
 | 
			
		||||
import { CallToAction as ComputeCallToAction } from './pages/compute/CallToAction'
 | 
			
		||||
import { CallToAction as StorageCallToAction } from './pages/storage/CallToAction'
 | 
			
		||||
import { CallToAction as GpuCallToAction } from './pages/gpu/CallToAction'
 | 
			
		||||
 | 
			
		||||
function App() {
 | 
			
		||||
  return (
 | 
			
		||||
@@ -19,9 +19,9 @@ function App() {
 | 
			
		||||
          <Route path="network" element={<NetworkPage />} />
 | 
			
		||||
          <Route path="agents" element={<AgentsPage />} />
 | 
			
		||||
                    <Route path="download" element={<DownloadPage />} />
 | 
			
		||||
          <Route path="compute" element={<Compute />} />
 | 
			
		||||
          <Route path="storage" element={<Storage />} />
 | 
			
		||||
          <Route path="gpu" element={<Gpu />} />
 | 
			
		||||
          <Route path="compute" element={<ComputeCallToAction />} />
 | 
			
		||||
          <Route path="storage" element={<StorageCallToAction />} />
 | 
			
		||||
          <Route path="gpu" element={<GpuCallToAction />} />
 | 
			
		||||
        </Route>
 | 
			
		||||
      </Routes>
 | 
			
		||||
    </BrowserRouter>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
import { Link } from 'react-router-dom'
 | 
			
		||||
import { Dropdown } from './ui/Dropdown'
 | 
			
		||||
import { ChevronDownIcon } from '@heroicons/react/20/solid'
 | 
			
		||||
import { Container } from './Container'
 | 
			
		||||
import { Button } from './Button'
 | 
			
		||||
import pmyceliumLogo from '../images/logos/pmyceliumlogo.png'
 | 
			
		||||
@@ -13,12 +15,20 @@ export function Header() {
 | 
			
		||||
              <img src={pmyceliumLogo} alt="Mycelium" className="h-8 w-auto" />
 | 
			
		||||
            </Link>
 | 
			
		||||
            <div className="hidden lg:flex lg:gap-10">
 | 
			
		||||
              <Link
 | 
			
		||||
                to="/cloud"
 | 
			
		||||
                className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
 | 
			
		||||
              >
 | 
			
		||||
              <Dropdown
 | 
			
		||||
                buttonContent={
 | 
			
		||||
                  <>
 | 
			
		||||
                    Cloud
 | 
			
		||||
              </Link>
 | 
			
		||||
                    <ChevronDownIcon className="h-5 w-5" aria-hidden="true" />
 | 
			
		||||
                  </>
 | 
			
		||||
                }
 | 
			
		||||
                items={[
 | 
			
		||||
                  { name: 'Cloud', href: '/cloud' },
 | 
			
		||||
                  { name: 'Compute', href: '/compute' },
 | 
			
		||||
                  { name: 'Storage', href: '/storage' },
 | 
			
		||||
                  { name: 'GPU', href: '/gpu' },
 | 
			
		||||
                ]}
 | 
			
		||||
              />
 | 
			
		||||
              <Link
 | 
			
		||||
                to="/network"
 | 
			
		||||
                className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								src/components/ui/Dropdown.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/components/ui/Dropdown.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
import { Menu, Transition } from '@headlessui/react'
 | 
			
		||||
import { Fragment } from 'react'
 | 
			
		||||
import { Link } from 'react-router-dom'
 | 
			
		||||
 | 
			
		||||
interface DropdownItem {
 | 
			
		||||
  name: string
 | 
			
		||||
  href: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface DropdownProps {
 | 
			
		||||
  buttonContent: React.ReactNode
 | 
			
		||||
  items: DropdownItem[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function Dropdown({ buttonContent, items }: DropdownProps) {
 | 
			
		||||
  return (
 | 
			
		||||
    <Menu as="div" className="relative inline-block text-left">
 | 
			
		||||
      <div>
 | 
			
		||||
        <Menu.Button className="inline-flex w-full justify-center items-center gap-x-1.5 rounded-md bg-white text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors">
 | 
			
		||||
          {buttonContent}
 | 
			
		||||
        </Menu.Button>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <Transition
 | 
			
		||||
        as={Fragment}
 | 
			
		||||
        enter="transition ease-out duration-100"
 | 
			
		||||
        enterFrom="transform opacity-0 scale-95"
 | 
			
		||||
        enterTo="transform opacity-100 scale-100"
 | 
			
		||||
        leave="transition ease-in duration-75"
 | 
			
		||||
        leaveFrom="transform opacity-100 scale-100"
 | 
			
		||||
        leaveTo="transform opacity-0 scale-95"
 | 
			
		||||
      >
 | 
			
		||||
        <Menu.Items className="absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
 | 
			
		||||
          <div className="py-1">
 | 
			
		||||
            {items.map((item) => (
 | 
			
		||||
              <Menu.Item key={item.href}>
 | 
			
		||||
                {({ active }) => (
 | 
			
		||||
                  <Link
 | 
			
		||||
                    to={item.href}
 | 
			
		||||
                    className={`
 | 
			
		||||
                      ${active ? 'bg-gray-100 text-gray-900' : 'text-gray-700'}
 | 
			
		||||
                      'block px-4 py-2 text-sm'
 | 
			
		||||
                    `}
 | 
			
		||||
                  >
 | 
			
		||||
                    {item.name}
 | 
			
		||||
                  </Link>
 | 
			
		||||
                )}
 | 
			
		||||
              </Menu.Item>
 | 
			
		||||
            ))}
 | 
			
		||||
          </div>
 | 
			
		||||
        </Menu.Items>
 | 
			
		||||
      </Transition>
 | 
			
		||||
    </Menu>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								src/pages/compute/CallToAction.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/pages/compute/CallToAction.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
import { CircleBackground } from '../../components/CircleBackground'
 | 
			
		||||
import { Container } from '../../components/Container'
 | 
			
		||||
import { Button } from '../../components/Button'
 | 
			
		||||
 | 
			
		||||
export function CallToAction() {
 | 
			
		||||
  return (
 | 
			
		||||
    <section
 | 
			
		||||
      id="get-started"
 | 
			
		||||
      className="relative overflow-hidden bg-gray-900 py-20 sm:py-28"
 | 
			
		||||
    >
 | 
			
		||||
      <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
 | 
			
		||||
        <CircleBackground color="#06b6d4" className="animate-spin-slower" />
 | 
			
		||||
      </div>
 | 
			
		||||
      <Container className="relative">
 | 
			
		||||
        <div className="mx-auto max-w-2xl text-center">
 | 
			
		||||
          <h2 className="text-3xl lg:text-4xl font-medium tracking-tight text-white sm:text-4xl">
 | 
			
		||||
            Get Started Today
 | 
			
		||||
          </h2>
 | 
			
		||||
          <p className="mt-6 text-lg text-gray-300">
 | 
			
		||||
            Download the Mycelium app and step into the future of secure, peer-to-peer networking; fast, private, and decentralized.
 | 
			
		||||
          </p>
 | 
			
		||||
          <div className="mt-10 flex flex-wrap justify-center gap-x-6 gap-y-4">
 | 
			
		||||
            <Button to="/download" variant="solid" color="white">
 | 
			
		||||
              Get Mycelium Connector
 | 
			
		||||
            </Button>
 | 
			
		||||
            <Button
 | 
			
		||||
              to="https://threefold.info/mycelium_network/docs/"
 | 
			
		||||
              as="a"
 | 
			
		||||
              target="_blank"
 | 
			
		||||
              variant="outline"
 | 
			
		||||
              color="white"
 | 
			
		||||
            >
 | 
			
		||||
              Read Docs
 | 
			
		||||
            </Button>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </Container>
 | 
			
		||||
    </section>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
import { Footer } from "../components/Footer";
 | 
			
		||||
import { Header } from "../components/Header";
 | 
			
		||||
 | 
			
		||||
const Compute = () => {
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Header />
 | 
			
		||||
      <main className="bg-white">
 | 
			
		||||
        <div className="relative isolate">
 | 
			
		||||
          <div
 | 
			
		||||
            className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
 | 
			
		||||
            aria-hidden="true"
 | 
			
		||||
          >
 | 
			
		||||
            <div
 | 
			
		||||
              className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%-30rem)] sm:w-[72.1875rem]"
 | 
			
		||||
              style={{
 | 
			
		||||
                clipPath:
 | 
			
		||||
                  "polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)",
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </main>
 | 
			
		||||
      <Footer />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Compute;
 | 
			
		||||
							
								
								
									
										40
									
								
								src/pages/gpu/CallToAction.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/pages/gpu/CallToAction.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
import { CircleBackground } from '../../components/CircleBackground'
 | 
			
		||||
import { Container } from '../../components/Container'
 | 
			
		||||
import { Button } from '../../components/Button'
 | 
			
		||||
 | 
			
		||||
export function CallToAction() {
 | 
			
		||||
  return (
 | 
			
		||||
    <section
 | 
			
		||||
      id="get-started"
 | 
			
		||||
      className="relative overflow-hidden bg-gray-900 py-20 sm:py-28"
 | 
			
		||||
    >
 | 
			
		||||
      <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
 | 
			
		||||
        <CircleBackground color="#06b6d4" className="animate-spin-slower" />
 | 
			
		||||
      </div>
 | 
			
		||||
      <Container className="relative">
 | 
			
		||||
        <div className="mx-auto max-w-2xl text-center">
 | 
			
		||||
          <h2 className="text-3xl lg:text-4xl font-medium tracking-tight text-white sm:text-4xl">
 | 
			
		||||
            Get Started Today
 | 
			
		||||
          </h2>
 | 
			
		||||
          <p className="mt-6 text-lg text-gray-300">
 | 
			
		||||
            Download the Mycelium app and step into the future of secure, peer-to-peer networking; fast, private, and decentralized.
 | 
			
		||||
          </p>
 | 
			
		||||
          <div className="mt-10 flex flex-wrap justify-center gap-x-6 gap-y-4">
 | 
			
		||||
            <Button to="/download" variant="solid" color="white">
 | 
			
		||||
              Get Mycelium Connector
 | 
			
		||||
            </Button>
 | 
			
		||||
            <Button
 | 
			
		||||
              to="https://threefold.info/mycelium_network/docs/"
 | 
			
		||||
              as="a"
 | 
			
		||||
              target="_blank"
 | 
			
		||||
              variant="outline"
 | 
			
		||||
              color="white"
 | 
			
		||||
            >
 | 
			
		||||
              Read Docs
 | 
			
		||||
            </Button>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </Container>
 | 
			
		||||
    </section>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
import { Footer } from "../components/Footer";
 | 
			
		||||
import { Header } from "../components/Header";
 | 
			
		||||
 | 
			
		||||
const Gpu = () => {
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Header />
 | 
			
		||||
      <main className="bg-white">
 | 
			
		||||
        <div className="relative isolate">
 | 
			
		||||
          <div
 | 
			
		||||
            className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
 | 
			
		||||
            aria-hidden="true"
 | 
			
		||||
          >
 | 
			
		||||
            <div
 | 
			
		||||
              className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%-30rem)] sm:w-[72.1875rem]"
 | 
			
		||||
              style={{
 | 
			
		||||
                clipPath:
 | 
			
		||||
                  "polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)",
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </main>
 | 
			
		||||
      <Footer />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Gpu;
 | 
			
		||||
							
								
								
									
										40
									
								
								src/pages/storage/CallToAction.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/pages/storage/CallToAction.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
import { CircleBackground } from '../../components/CircleBackground'
 | 
			
		||||
import { Container } from '../../components/Container'
 | 
			
		||||
import { Button } from '../../components/Button'
 | 
			
		||||
 | 
			
		||||
export function CallToAction() {
 | 
			
		||||
  return (
 | 
			
		||||
    <section
 | 
			
		||||
      id="get-started"
 | 
			
		||||
      className="relative overflow-hidden bg-gray-900 py-20 sm:py-28"
 | 
			
		||||
    >
 | 
			
		||||
      <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
 | 
			
		||||
        <CircleBackground color="#06b6d4" className="animate-spin-slower" />
 | 
			
		||||
      </div>
 | 
			
		||||
      <Container className="relative">
 | 
			
		||||
        <div className="mx-auto max-w-2xl text-center">
 | 
			
		||||
          <h2 className="text-3xl lg:text-4xl font-medium tracking-tight text-white sm:text-4xl">
 | 
			
		||||
            Get Started Today
 | 
			
		||||
          </h2>
 | 
			
		||||
          <p className="mt-6 text-lg text-gray-300">
 | 
			
		||||
            Download the Mycelium app and step into the future of secure, peer-to-peer networking; fast, private, and decentralized.
 | 
			
		||||
          </p>
 | 
			
		||||
          <div className="mt-10 flex flex-wrap justify-center gap-x-6 gap-y-4">
 | 
			
		||||
            <Button to="/download" variant="solid" color="white">
 | 
			
		||||
              Get Mycelium Connector
 | 
			
		||||
            </Button>
 | 
			
		||||
            <Button
 | 
			
		||||
              to="https://threefold.info/mycelium_network/docs/"
 | 
			
		||||
              as="a"
 | 
			
		||||
              target="_blank"
 | 
			
		||||
              variant="outline"
 | 
			
		||||
              color="white"
 | 
			
		||||
            >
 | 
			
		||||
              Read Docs
 | 
			
		||||
            </Button>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </Container>
 | 
			
		||||
    </section>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
import { Footer } from "../components/Footer";
 | 
			
		||||
import { Header } from "../components/Header";
 | 
			
		||||
 | 
			
		||||
const Storage = () => {
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Header />
 | 
			
		||||
      <main className="bg-white">
 | 
			
		||||
        <div className="relative isolate">
 | 
			
		||||
          <div
 | 
			
		||||
            className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
 | 
			
		||||
            aria-hidden="true"
 | 
			
		||||
          >
 | 
			
		||||
            <div
 | 
			
		||||
              className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%-30rem)] sm:w-[72.1875rem]"
 | 
			
		||||
              style={{
 | 
			
		||||
                clipPath:
 | 
			
		||||
                  "polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)",
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </main>
 | 
			
		||||
      <Footer />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Storage;
 | 
			
		||||
		Reference in New Issue
	
	Block a user