style(new): ui *

This commit is contained in:
Eshan Roy
2024-12-07 19:57:20 +05:30
parent d5b90950f2
commit 8b818a4a6a
24 changed files with 5964 additions and 0 deletions

25
.gitignore vendored Normal file
View File

@@ -0,0 +1,25 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.bolt

28
eslint.config.js Normal file
View File

@@ -0,0 +1,28 @@
import js from '@eslint/js';
import globals from 'globals';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint';
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
);

13
index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

4719
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

35
package.json Normal file
View File

@@ -0,0 +1,35 @@
{
"name": "vite-react-typescript-starter",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"lucide-react": "^0.344.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.22.3",
"octokit": "^3.1.2"
},
"devDependencies": {
"@eslint/js": "^9.9.1",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.18",
"eslint": "^9.9.1",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.11",
"globals": "^15.9.0",
"postcss": "^8.4.35",
"tailwindcss": "^3.4.1",
"typescript": "^5.5.3",
"typescript-eslint": "^8.3.0",
"vite": "^5.4.2"
}
}

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

26
src/App.tsx Normal file
View File

@@ -0,0 +1,26 @@
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { Layout } from './components/layout/Layout';
import { Home } from './pages/Home';
import { About } from './pages/About';
import { DownloadPage } from './pages/Download';
import { Donors } from './pages/Donors';
import { Maintainers } from './pages/Maintainers';
function App() {
return (
<Router>
<Layout>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/download" element={<DownloadPage />} />
<Route path="/donors" element={<Donors />} />
<Route path="/maintainers" element={<Maintainers />} />
</Routes>
</Layout>
</Router>
);
}
export default App;

View File

@@ -0,0 +1,50 @@
import React from 'react';
import { Github, Twitter } from 'lucide-react';
export function Footer() {
return (
<footer className="bg-[#3c3c3c] text-white py-8">
<div className="container mx-auto px-4">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
<div>
<h3 className="font-bold text-lg mb-4">Linux Mint</h3>
<p className="text-sm text-gray-300">
Linux Mint is an elegant, easy to use, up to date and comfortable
GNU/Linux desktop distribution.
</p>
</div>
<div>
<h3 className="font-bold text-lg mb-4">Quick Links</h3>
<ul className="space-y-2 text-sm">
<li><a href="/blog" className="hover:text-green-400">Blog</a></li>
<li><a href="/forums" className="hover:text-green-400">Forums</a></li>
<li><a href="/community" className="hover:text-green-400">Community</a></li>
</ul>
</div>
<div>
<h3 className="font-bold text-lg mb-4">Documentation</h3>
<ul className="space-y-2 text-sm">
<li><a href="/docs/installation" className="hover:text-green-400">Installation Guide</a></li>
<li><a href="/docs/user-guide" className="hover:text-green-400">User Guide</a></li>
<li><a href="/docs/release-notes" className="hover:text-green-400">Release Notes</a></li>
</ul>
</div>
<div>
<h3 className="font-bold text-lg mb-4">Connect</h3>
<div className="flex space-x-4">
<a href="https://github.com/linuxmint" className="hover:text-green-400">
<Github className="h-6 w-6" />
</a>
<a href="https://twitter.com/linux_mint" className="hover:text-green-400">
<Twitter className="h-6 w-6" />
</a>
</div>
</div>
</div>
<div className="mt-8 pt-8 border-t border-gray-700 text-center text-sm text-gray-400">
<p>© {new Date().getFullYear()} Linux Mint. All rights reserved.</p>
</div>
</div>
</footer>
);
}

View File

@@ -0,0 +1,92 @@
import React from 'react';
import { Link, useLocation } from 'react-router-dom';
import { Menu } from 'lucide-react';
export function Header() {
const location = useLocation();
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
const isActive = (path: string) => location.pathname === path;
return (
<header className="bg-[#3c3c3c] text-white">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between h-16">
<Link to="/" className="flex items-center space-x-2">
<img src="/mint-logo.svg" alt="Linux Mint" className="h-8 w-8" />
<span className="font-bold text-xl">Linux Mint</span>
</Link>
{/* Mobile menu button */}
<button
className="md:hidden"
onClick={() => setIsMenuOpen(!isMenuOpen)}
>
<Menu className="h-6 w-6" />
</button>
{/* Desktop navigation */}
<nav className="hidden md:flex space-x-8">
<NavLinks isActive={isActive} />
</nav>
</div>
{/* Mobile navigation */}
{isMenuOpen && (
<nav className="md:hidden py-4">
<div className="flex flex-col space-y-4">
<NavLinks isActive={isActive} />
</div>
</nav>
)}
</div>
</header>
);
}
function NavLinks({ isActive }: { isActive: (path: string) => boolean }) {
return (
<>
<Link
to="/"
className={`${
isActive('/') ? 'text-green-400' : 'hover:text-green-400'
} transition-colors`}
>
Home
</Link>
<Link
to="/about"
className={`${
isActive('/about') ? 'text-green-400' : 'hover:text-green-400'
} transition-colors`}
>
About
</Link>
<Link
to="/download"
className={`${
isActive('/download') ? 'text-green-400' : 'hover:text-green-400'
} transition-colors`}
>
Download
</Link>
<Link
to="/donors"
className={`${
isActive('/donors') ? 'text-green-400' : 'hover:text-green-400'
} transition-colors`}
>
Donors
</Link>
<Link
to="/maintainers"
className={`${
isActive('/maintainers') ? 'text-green-400' : 'hover:text-green-400'
} transition-colors`}
>
Maintainers
</Link>
</>
);
}

View File

@@ -0,0 +1,19 @@
import React from 'react';
import { Header } from './Header';
import { Footer } from './Footer';
interface LayoutProps {
children: React.ReactNode;
}
export function Layout({ children }: LayoutProps) {
return (
<div className="flex flex-col min-h-screen">
<Header />
<main className="flex-grow">
{children}
</main>
<Footer />
</div>
);
}

3
src/index.css Normal file
View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

10
src/main.tsx Normal file
View File

@@ -0,0 +1,10 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';
import './index.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>
);

152
src/pages/About.tsx Normal file
View File

@@ -0,0 +1,152 @@
import React from 'react';
import { Heart, Users, Shield } from 'lucide-react';
export function About() {
return (
<div className="py-12">
<div className="container mx-auto px-4">
{/* Mission Section */}
<section className="mb-16 text-center">
<h1 className="text-4xl font-bold mb-6">About Linux Mint</h1>
<p className="text-xl text-gray-600 max-w-3xl mx-auto">
Linux Mint is one of the most popular desktop Linux distributions and
used by millions of people. It is elegant, easy to use, comfortable,
and powerful.
</p>
</section>
{/* Values Section */}
<section className="mb-16">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<ValueCard
icon={<Heart className="h-12 w-12 text-red-500" />}
title="Our Mission"
description="To produce a modern, elegant and comfortable operating system which is both powerful and easy to use."
/>
<ValueCard
icon={<Users className="h-12 w-12 text-blue-500" />}
title="Community"
description="We believe in the power of open source and community collaboration to create amazing software."
/>
<ValueCard
icon={<Shield className="h-12 w-12 text-green-500" />}
title="Security"
description="We prioritize user privacy and system security through regular updates and careful system design."
/>
</div>
</section>
{/* History Section */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-8">Our History</h2>
<div className="bg-white rounded-lg shadow-lg p-8">
<div className="space-y-6">
<TimelineItem
year="2006"
title="The Beginning"
description="Linux Mint was founded by Clement Lefebvre and launched its first release, Ada."
/>
<TimelineItem
year="2008"
title="Growing Popular"
description="Linux Mint gained significant popularity and became one of the most widely used Linux distributions."
/>
<TimelineItem
year="2010"
title="Mint Tools"
description="Development of Mint-specific tools began, making system management easier for users."
/>
<TimelineItem
year="Present"
title="Continuing Innovation"
description="Linux Mint continues to innovate while maintaining its commitment to stability and user-friendliness."
/>
</div>
</div>
</section>
{/* Team Section */}
<section>
<h2 className="text-3xl font-bold mb-8">Leadership Team</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<TeamMember
name="Clement Lefebvre"
role="Project Leader"
image="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60"
/>
<TeamMember
name="Gwendal Le Bihan"
role="Development Lead"
image="https://images.unsplash.com/photo-1519085360753-af0119f7cbe7?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60"
/>
<TeamMember
name="Joseph Mills"
role="Community Manager"
image="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60"
/>
</div>
</section>
</div>
</div>
);
}
function ValueCard({
icon,
title,
description,
}: {
icon: React.ReactNode;
title: string;
description: string;
}) {
return (
<div className="text-center p-6 bg-white rounded-lg shadow-lg">
<div className="flex justify-center mb-4">{icon}</div>
<h3 className="text-xl font-bold mb-2">{title}</h3>
<p className="text-gray-600">{description}</p>
</div>
);
}
function TimelineItem({
year,
title,
description,
}: {
year: string;
title: string;
description: string;
}) {
return (
<div className="flex">
<div className="w-24 font-bold text-green-600">{year}</div>
<div>
<h3 className="font-bold mb-1">{title}</h3>
<p className="text-gray-600">{description}</p>
</div>
</div>
);
}
function TeamMember({
name,
role,
image,
}: {
name: string;
role: string;
image: string;
}) {
return (
<div className="text-center">
<img
src={image}
alt={name}
className="w-32 h-32 rounded-full mx-auto mb-4 object-cover"
/>
<h3 className="font-bold text-lg">{name}</h3>
<p className="text-gray-600">{role}</p>
</div>
);
}

185
src/pages/Donors.tsx Normal file
View File

@@ -0,0 +1,185 @@
import React from 'react';
import { Heart } from 'lucide-react';
export function Donors() {
return (
<div className="py-12">
<div className="container mx-auto px-4">
{/* Hero Section */}
<section className="text-center mb-16">
<Heart className="h-16 w-16 text-red-500 mx-auto mb-6" />
<h1 className="text-4xl font-bold mb-6">Our Amazing Donors</h1>
<p className="text-xl text-gray-600 max-w-3xl mx-auto">
Linux Mint is made possible thanks to the generous support of our
donors. We are grateful for their contributions to keep our project
running.
</p>
</section>
{/* Donation Tiers */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-8 text-center">Donation Tiers</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<DonationTier
title="Platinum Donors"
amount="$500+"
color="bg-gradient-to-r from-gray-200 to-gray-100"
donors={[
"TechCorp International",
"Global Systems Ltd",
"Future Computing Inc"
]}
/>
<DonationTier
title="Gold Donors"
amount="$100-$499"
color="bg-gradient-to-r from-yellow-100 to-yellow-50"
donors={[
"Sarah Johnson",
"Michael Chang",
"DataFlow Solutions",
"Robert Wilson",
"Emma Thompson"
]}
/>
<DonationTier
title="Silver Donors"
amount="$10-$99"
color="bg-gradient-to-r from-gray-100 to-white"
donors={[
"David Miller",
"Lisa Anderson",
"James Wilson",
"Maria Garcia",
"John Smith",
"And many more..."
]}
/>
</div>
</section>
{/* Become a Donor */}
<section className="bg-green-50 rounded-lg p-8 text-center">
<h2 className="text-3xl font-bold mb-4">Become a Donor</h2>
<p className="text-gray-600 mb-8 max-w-2xl mx-auto">
Your support helps us maintain and improve Linux Mint. Every donation,
big or small, makes a difference in keeping our project independent and
sustainable.
</p>
<button className="bg-green-600 text-white px-8 py-3 rounded-full hover:bg-green-700 transition-colors">
Make a Donation
</button>
</section>
{/* Monthly Report */}
<section className="mt-16">
<h2 className="text-3xl font-bold mb-8">Monthly Donation Report</h2>
<div className="bg-white rounded-lg shadow-lg p-8">
<div className="space-y-6">
<MonthlyStats
month="March 2024"
amount={15750}
donors={342}
averageDonation={46}
/>
<div className="border-t pt-6">
<h3 className="font-bold mb-4">How Donations Are Used</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<UsageCard
percentage={40}
category="Development"
description="Supporting core developers and infrastructure"
/>
<UsageCard
percentage={35}
category="Server Costs"
description="Maintaining mirrors and websites"
/>
<UsageCard
percentage={25}
category="Community"
description="Supporting community projects and events"
/>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
);
}
function DonationTier({
title,
amount,
color,
donors,
}: {
title: string;
amount: string;
color: string;
donors: string[];
}) {
return (
<div className={`rounded-lg shadow-lg p-6 ${color}`}>
<h3 className="text-xl font-bold mb-2">{title}</h3>
<p className="text-gray-600 mb-4">{amount}</p>
<ul className="space-y-2">
{donors.map((donor, index) => (
<li key={index} className="text-gray-700">{donor}</li>
))}
</ul>
</div>
);
}
function MonthlyStats({
month,
amount,
donors,
averageDonation,
}: {
month: string;
amount: number;
donors: number;
averageDonation: number;
}) {
return (
<div>
<h3 className="font-bold text-xl mb-4">{month}</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-gray-50 p-4 rounded-lg">
<p className="text-sm text-gray-600">Total Donations</p>
<p className="text-2xl font-bold">${amount.toLocaleString()}</p>
</div>
<div className="bg-gray-50 p-4 rounded-lg">
<p className="text-sm text-gray-600">Number of Donors</p>
<p className="text-2xl font-bold">{donors}</p>
</div>
<div className="bg-gray-50 p-4 rounded-lg">
<p className="text-sm text-gray-600">Average Donation</p>
<p className="text-2xl font-bold">${averageDonation}</p>
</div>
</div>
</div>
);
}
function UsageCard({
percentage,
category,
description,
}: {
percentage: number;
category: string;
description: string;
}) {
return (
<div className="bg-gray-50 p-4 rounded-lg">
<div className="text-2xl font-bold text-green-600 mb-2">{percentage}%</div>
<h4 className="font-bold mb-1">{category}</h4>
<p className="text-sm text-gray-600">{description}</p>
</div>
);
}

142
src/pages/Download.tsx Normal file
View File

@@ -0,0 +1,142 @@
import React from 'react';
import { Download as DownloadIcon, Monitor, Server, HardDrive } from 'lucide-react';
export function DownloadPage() {
return (
<div className="py-12">
<div className="container mx-auto px-4">
{/* Hero Section */}
<section className="text-center mb-16">
<h1 className="text-4xl font-bold mb-6">Download Linux Mint</h1>
<p className="text-xl text-gray-600 max-w-3xl mx-auto">
Choose the edition that best suits your needs. All versions are free to
download and use.
</p>
</section>
{/* Edition Cards */}
<section className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-16">
<EditionCard
title="Cinnamon Edition"
description="Modern, innovative features while being traditional and familiar."
icon={<Monitor className="h-12 w-12 text-green-600" />}
recommended={true}
/>
<EditionCard
title="MATE Edition"
description="Traditional desktop experience, highly stable and reliable."
icon={<Server className="h-12 w-12 text-blue-600" />}
/>
<EditionCard
title="Xfce Edition"
description="Lightweight and stable. Perfect for older computers."
icon={<HardDrive className="h-12 w-12 text-purple-600" />}
/>
</section>
{/* System Requirements */}
<section className="mb-16">
<h2 className="text-3xl font-bold mb-8">System Requirements</h2>
<div className="bg-white rounded-lg shadow-lg p-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<div>
<h3 className="text-xl font-bold mb-4">Minimum Requirements</h3>
<ul className="space-y-2">
<li>2GB RAM (4GB recommended)</li>
<li>20GB of disk space (100GB recommended)</li>
<li>1024×768 resolution</li>
</ul>
</div>
<div>
<h3 className="text-xl font-bold mb-4">Recommended</h3>
<ul className="space-y-2">
<li>4GB RAM or more</li>
<li>100GB of disk space or more</li>
<li>1920×1080 resolution or higher</li>
</ul>
</div>
</div>
</div>
</section>
{/* Download Mirrors */}
<section>
<h2 className="text-3xl font-bold mb-8">Download Mirrors</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<MirrorButton
region="North America"
speed="Fast"
url="https://mirrors.kernel.org/linuxmint/"
/>
<MirrorButton
region="Europe"
speed="Very Fast"
url="https://mirror.23media.com/linuxmint/"
/>
<MirrorButton
region="Asia"
speed="Fast"
url="https://mirror.xtom.com.hk/linuxmint/"
/>
</div>
</section>
</div>
</div>
);
}
function EditionCard({
title,
description,
icon,
recommended = false,
}: {
title: string;
description: string;
icon: React.ReactNode;
recommended?: boolean;
}) {
return (
<div className={`bg-white rounded-lg shadow-lg p-8 relative ${recommended ? 'border-2 border-green-500' : ''}`}>
{recommended && (
<div className="absolute top-4 right-4 bg-green-500 text-white px-2 py-1 rounded-full text-sm">
Recommended
</div>
)}
<div className="flex justify-center mb-4">{icon}</div>
<h3 className="text-xl font-bold mb-2 text-center">{title}</h3>
<p className="text-gray-600 text-center mb-6">{description}</p>
<div className="flex justify-center">
<button className="flex items-center space-x-2 bg-green-600 text-white px-6 py-2 rounded-full hover:bg-green-700 transition-colors">
<DownloadIcon className="h-5 w-5" />
<span>Download</span>
</button>
</div>
</div>
);
}
function MirrorButton({
region,
speed,
url,
}: {
region: string;
speed: string;
url: string;
}) {
return (
<a
href={url}
className="block bg-white rounded-lg shadow p-4 hover:shadow-lg transition-shadow"
>
<div className="flex justify-between items-center">
<div>
<h3 className="font-bold">{region}</h3>
<p className="text-sm text-gray-600">{speed}</p>
</div>
<DownloadIcon className="h-5 w-5 text-green-600" />
</div>
</a>
);
}

208
src/pages/Home.tsx Normal file
View File

@@ -0,0 +1,208 @@
import React from 'react';
import { Download, Shield, Terminal, Award, Users, Coffee, ArrowRight, Star } from 'lucide-react';
import { Link } from 'react-router-dom';
export function Home() {
return (
<div>
{/* Hero Section */}
<section className="bg-gradient-to-r from-green-600 to-green-700 text-white py-20">
<div className="container mx-auto px-4 text-center">
<h1 className="text-5xl font-bold mb-6">Welcome to Linux Mint</h1>
<p className="text-xl mb-8">From freedom comes elegance</p>
<div className="flex justify-center space-x-4">
<Link
to="/download"
className="bg-white text-green-700 px-8 py-3 rounded-full font-semibold hover:bg-gray-100 transition-colors"
>
Download Now
</Link>
<Link
to="/about"
className="border-2 border-white text-white px-8 py-3 rounded-full font-semibold hover:bg-white hover:text-green-700 transition-colors"
>
Learn More
</Link>
</div>
</div>
</section>
{/* Features Section */}
<section className="py-16 bg-white">
<div className="container mx-auto px-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<FeatureCard
icon={<Shield className="h-12 w-12 text-green-600" />}
title="Secure"
description="Regular security updates and a robust system architecture keep your data safe."
/>
<FeatureCard
icon={<Terminal className="h-12 w-12 text-green-600" />}
title="Powerful"
description="Full access to the terminal and system components for advanced users."
/>
<FeatureCard
icon={<Download className="h-12 w-12 text-green-600" />}
title="Free Forever"
description="Linux Mint is free and open source. No costs, no subscriptions."
/>
</div>
</div>
</section>
{/* Latest Release Section */}
<section className="py-16 bg-gray-100">
<div className="container mx-auto px-4">
<h2 className="text-3xl font-bold text-center mb-12">Latest Release</h2>
<div className="bg-white rounded-lg shadow-lg p-8">
<div className="flex flex-col md:flex-row items-center">
<div className="md:w-1/2 mb-8 md:mb-0">
<img
src="https://images.unsplash.com/photo-1629654297299-c8506221ca97?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1000&q=80"
alt="Linux Mint Desktop"
className="rounded-lg shadow-md"
/>
</div>
<div className="md:w-1/2 md:pl-8">
<h3 className="text-2xl font-bold mb-4">Linux Mint 21.3</h3>
<p className="text-gray-600 mb-6">
Experience the latest features and improvements in our newest
release. Enjoy enhanced performance, better hardware support, and
a refined user interface.
</p>
<div className="space-y-4">
<Feature title="New Cinnamon 6.0" description="Improved desktop experience with better performance" />
<Feature title="Updated Software Manager" description="Faster and more reliable package management" />
<Feature title="Enhanced Hardware Support" description="Better compatibility with latest devices" />
</div>
<Link
to="/download"
className="inline-flex items-center space-x-2 bg-green-600 text-white px-6 py-2 rounded-full hover:bg-green-700 transition-colors mt-6"
>
<span>Learn More</span>
<ArrowRight className="h-4 w-4" />
</Link>
</div>
</div>
</div>
</div>
</section>
{/* Why Choose Linux Mint */}
<section className="py-16 bg-white">
<div className="container mx-auto px-4">
<h2 className="text-3xl font-bold text-center mb-12">Why Choose Linux Mint?</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
<ReasonCard
icon={<Award className="h-10 w-10 text-yellow-500" />}
title="User-Friendly"
description="Perfect for beginners and experts alike"
/>
<ReasonCard
icon={<Users className="h-10 w-10 text-blue-500" />}
title="Active Community"
description="Get help and support when you need it"
/>
<ReasonCard
icon={<Coffee className="h-10 w-10 text-red-500" />}
title="Stable & Reliable"
description="Built on solid foundations"
/>
<ReasonCard
icon={<Star className="h-10 w-10 text-purple-500" />}
title="Feature Rich"
description="Everything you need, out of the box"
/>
</div>
</div>
</section>
{/* Community Stats */}
<section className="py-16 bg-gray-100">
<div className="container mx-auto px-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<StatCard number="15M+" label="Downloads" />
<StatCard number="100+" label="Countries" />
<StatCard number="500K+" label="Active Users" />
</div>
</div>
</section>
{/* CTA Section */}
<section className="py-16 bg-green-600 text-white">
<div className="container mx-auto px-4 text-center">
<h2 className="text-3xl font-bold mb-6">Ready to Try Linux Mint?</h2>
<p className="text-xl mb-8 max-w-2xl mx-auto">
Join millions of users worldwide and experience the freedom of Linux Mint.
</p>
<Link
to="/download"
className="inline-flex items-center space-x-2 bg-white text-green-600 px-8 py-3 rounded-full font-semibold hover:bg-gray-100 transition-colors"
>
<Download className="h-5 w-5" />
<span>Download Now</span>
</Link>
</div>
</section>
</div>
);
}
function FeatureCard({
icon,
title,
description,
}: {
icon: React.ReactNode;
title: string;
description: string;
}) {
return (
<div className="text-center p-6 bg-white rounded-lg shadow-lg hover:shadow-xl transition-shadow">
<div className="flex justify-center mb-4">{icon}</div>
<h3 className="text-xl font-bold mb-2">{title}</h3>
<p className="text-gray-600">{description}</p>
</div>
);
}
function Feature({ title, description }: { title: string; description: string }) {
return (
<div className="flex items-start space-x-3">
<div className="flex-shrink-0">
<div className="w-2 h-2 mt-2 rounded-full bg-green-500"></div>
</div>
<div>
<h4 className="font-semibold">{title}</h4>
<p className="text-sm text-gray-600">{description}</p>
</div>
</div>
);
}
function ReasonCard({
icon,
title,
description,
}: {
icon: React.ReactNode;
title: string;
description: string;
}) {
return (
<div className="text-center p-6 bg-white rounded-lg shadow hover:shadow-lg transition-shadow">
<div className="flex justify-center mb-4">{icon}</div>
<h3 className="text-lg font-bold mb-2">{title}</h3>
<p className="text-gray-600 text-sm">{description}</p>
</div>
);
}
function StatCard({ number, label }: { number: string; label: string }) {
return (
<div className="bg-white rounded-lg shadow-lg p-8 text-center">
<div className="text-4xl font-bold text-green-600 mb-2">{number}</div>
<div className="text-gray-600">{label}</div>
</div>
);
}

130
src/pages/Maintainers.tsx Normal file
View File

@@ -0,0 +1,130 @@
import React, { useEffect, useState } from 'react';
import { Github, Twitter, Globe, MapPin, Users } from 'lucide-react';
import { getMaintainers, type Maintainer } from '../services/github';
export function Maintainers() {
const [maintainers, setMaintainers] = useState<Maintainer[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchMaintainers = async () => {
try {
const data = await getMaintainers();
setMaintainers(data);
} catch (err) {
setError('Failed to load maintainers data');
console.error(err);
} finally {
setLoading(false);
}
};
fetchMaintainers();
}, []);
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-4 border-green-500 border-t-transparent"></div>
</div>
);
}
if (error) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<p className="text-red-500 text-xl">{error}</p>
<p className="text-gray-600 mt-2">Please try again later</p>
</div>
</div>
);
}
return (
<div className="py-12">
<div className="container mx-auto px-4">
<section className="text-center mb-16">
<h1 className="text-4xl font-bold mb-6">Meet Our Maintainers</h1>
<p className="text-xl text-gray-600 max-w-3xl mx-auto">
These dedicated individuals work tirelessly to make Linux Mint one of
the best Linux distributions available.
</p>
</section>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{maintainers.map((maintainer) => (
<MaintainerCard key={maintainer.login} maintainer={maintainer} />
))}
</div>
</div>
</div>
);
}
function MaintainerCard({ maintainer }: { maintainer: Maintainer }) {
return (
<div className="bg-white rounded-lg shadow-lg p-6">
<div className="flex items-center space-x-4 mb-4">
<img
src={maintainer.avatarUrl}
alt={maintainer.name || maintainer.login}
className="w-16 h-16 rounded-full"
/>
<div>
<h2 className="text-xl font-bold">{maintainer.name || maintainer.login}</h2>
<p className="text-gray-600">@{maintainer.login}</p>
</div>
</div>
{maintainer.bio && (
<p className="text-gray-700 mb-4">{maintainer.bio}</p>
)}
<div className="space-y-2 mb-4">
{maintainer.location && (
<div className="flex items-center space-x-2 text-gray-600">
<MapPin className="h-4 w-4" />
<span>{maintainer.location}</span>
</div>
)}
<div className="flex items-center space-x-2 text-gray-600">
<Users className="h-4 w-4" />
<span>{maintainer.followers} followers · {maintainer.following} following</span>
</div>
</div>
<div className="flex space-x-3">
<a
href={`https://github.com/${maintainer.login}`}
target="_blank"
rel="noopener noreferrer"
className="text-gray-600 hover:text-gray-900"
>
<Github className="h-5 w-5" />
</a>
{maintainer.twitterUsername && (
<a
href={`https://twitter.com/${maintainer.twitterUsername}`}
target="_blank"
rel="noopener noreferrer"
className="text-gray-600 hover:text-blue-400"
>
<Twitter className="h-5 w-5" />
</a>
)}
{maintainer.blog && (
<a
href={maintainer.blog}
target="_blank"
rel="noopener noreferrer"
className="text-gray-600 hover:text-green-600"
>
<Globe className="h-5 w-5" />
</a>
)}
</div>
</div>
);
}

49
src/services/github.ts Normal file
View File

@@ -0,0 +1,49 @@
import { Octokit } from 'octokit';
const octokit = new Octokit();
export interface Maintainer {
login: string;
name: string | null;
avatarUrl: string;
bio: string | null;
location: string | null;
blog: string | null;
twitterUsername: string | null;
followers: number;
following: number;
}
export async function getMaintainers(): Promise<Maintainer[]> {
const maintainerUsernames = [
'ClementLefebvre',
'mtwebster',
'JosephMills',
'leigh123linux',
'xenopeek'
];
const maintainers = await Promise.all(
maintainerUsernames.map(async (username) => {
try {
const { data } = await octokit.rest.users.getByUsername({ username });
return {
login: data.login,
name: data.name,
avatarUrl: data.avatar_url,
bio: data.bio,
location: data.location,
blog: data.blog,
twitterUsername: data.twitter_username,
followers: data.followers,
following: data.following,
};
} catch (error) {
console.error(`Error fetching data for ${username}:`, error);
return null;
}
})
);
return maintainers.filter((m): m is Maintainer => m !== null);
}

1
src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

8
tailwind.config.js Normal file
View File

@@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {},
},
plugins: [],
};

24
tsconfig.app.json Normal file
View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}

7
tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

22
tsconfig.node.json Normal file
View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}

10
vite.config.ts Normal file
View File

@@ -0,0 +1,10 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
optimizeDeps: {
exclude: ['lucide-react'],
},
});