This commit is contained in:
2025-11-06 01:40:00 +01:00
parent de5f54f4bc
commit 602c5a40f1
108 changed files with 9859 additions and 1382 deletions

View File

@@ -0,0 +1,23 @@
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'
import { globalIgnores } from 'eslint/config'
export default tseslint.config([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs['recommended-latest'],
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])

View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="reset.css">
<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>

4267
absolete_frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@tailwindcss/vite": "^4.1.16",
"@types/react-router": "^5.1.20",
"axios": "^1.13.0",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-icons": "^5.5.0",
"react-router-dom": "^7.8.1",
"tailwindcss": "^4.1.16"
},
"devDependencies": {
"@eslint/js": "^9.33.0",
"@types/axios": "^0.9.36",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@vitejs/plugin-react": "^5.0.0",
"eslint": "^9.33.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.3.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.39.1",
"vite": "^7.1.2"
}
}

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 823 B

After

Width:  |  Height:  |  Size: 823 B

View File

Before

Width:  |  Height:  |  Size: 705 B

After

Width:  |  Height:  |  Size: 705 B

View File

Before

Width:  |  Height:  |  Size: 366 B

After

Width:  |  Height:  |  Size: 366 B

View File

Before

Width:  |  Height:  |  Size: 722 B

After

Width:  |  Height:  |  Size: 722 B

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -15,11 +15,12 @@ interface GlobalContextType {
setUser: React.Dispatch<React.SetStateAction<User | null>>;
}
// vytvoříme a exportneme kontext
// vytvoříme a exportneme kontext !!!
export const UserContext = createContext<GlobalContextType | null>(null);
// hook pro použití kontextu
// zabal routy do téhle komponenty!!!
export const UserContextProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [user, setUser] = useState<User | null>(null);

View File

@@ -0,0 +1,27 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

View File

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

View File

@@ -0,0 +1,25 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

View File

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

46
frontend/App.tsx Normal file
View File

@@ -0,0 +1,46 @@
import React from 'react';
import '@radix-ui/themes/styles.css';
import { Theme } from '@radix-ui/themes';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './src/pages/Home';
import Portfolio from './src/pages/Portfolio';
import Services from './src/pages/Services';
import About from './src/pages/About';
import Blog from './src/pages/Blog';
import Contact from './src/pages/Contact';
import NotFound from './src/pages/NotFound';
const App: React.FC = () => {
return (
<Theme appearance="inherit" radius="large" scaling="100%">
<Router>
<main className="min-h-screen font-inter">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/portfolio" element={<Portfolio />} />
<Route path="/portfolio/*" element={<Portfolio />} />
<Route path="/services" element={<Services />} />
<Route path="/services/*" element={<Services />} />
<Route path="/about" element={<About />} />
<Route path="/about/*" element={<About />} />
<Route path="/blog" element={<Blog />} />
<Route path="/contact" element={<Contact />} />
<Route path="*" element={<NotFound />} />
</Routes>
<ToastContainer
position="top-right"
autoClose={3000}
newestOnTop
closeOnClick
pauseOnHover
/>
</main>
</Router>
</Theme>
);
}
export default App;

View File

@@ -1,11 +1,11 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import { defineConfig, globalIgnores } from 'eslint/config'
import globals from 'globals'
import tseslint from 'typescript-eslint'
import { globalIgnores } from 'eslint/config'
export default tseslint.config([
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
@@ -15,9 +15,12 @@ export default tseslint.config([
reactHooks.configs['recommended-latest'],
reactRefresh.configs.vite,
],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
},
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])
])

View File

@@ -1,14 +1,26 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="reset.css">
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/x-icon" href="https://meku.dev/favicon.ico" />
<title>Modern Portfolio</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<meta name="generator" content="Meku" />
<meta name="description" content="Modern Portfolio Generated with Meku" />
<meta name="author" content="Meku" />
<meta property="og:title" content="Modern Portfolio" />
<meta property="og:description" content="Modern Portfolio Generated with Meku" />
<meta property="og:type" content="website" />
<meta property="og:image" content="https://meku.dev/images/meku.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@meku_dev" />
<meta name="twitter:image" content="https://meku.dev/images/meku.png" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script type="module" src="/index.tsx"></script>
</body>
</html>
</html>

50
frontend/index.tsx Normal file
View File

@@ -0,0 +1,50 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './styles.css';
// Send logs to parent frame (like a preview system)
function postToParent(level: string, ...args: any[]): void {
if (window.parent !== window) {
window.parent.postMessage(
{
type: 'iframe-console',
level,
args,
},
'*'
);
}
}
// Global error handler
window.onerror = function (message, source, lineno, colno, error) {
const errPayload = {
message,
source,
lineno,
colno,
stack: error?.stack,
};
postToParent('error', '[Meku_Error_Caught]', errPayload);
};
// Unhandled promise rejection
window.onunhandledrejection = function (event) {
postToParent('error', '[Meku_Error_Caught]', { reason: event.reason });
};
// Patch console
(['log', 'warn', 'info', 'error'] as const).forEach((level) => {
const original = console[level];
console[level] = (...args: any[]) => {
postToParent(level, ...args);
original(...args);
};
});
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{
"name": "frontend",
"name": "modern-portfolio",
"private": true,
"version": "0.0.0",
"type": "module",
@@ -10,27 +10,35 @@
"preview": "vite preview"
},
"dependencies": {
"@tailwindcss/vite": "^4.1.16",
"@types/react-router": "^5.1.20",
"axios": "^1.13.0",
"globals": "^16.4.0",
"@hookform/resolvers": "^5.2.1",
"@radix-ui/themes": "^3.2.1",
"@vitejs/plugin-react": "^5.0.2",
"autoprefixer": "^10.4.19",
"date-fns": "^3.6.0",
"framer-motion": "^12.12.2",
"lucide-react": "^0.462.0",
"postcss": "^8.4.38",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-icons": "^5.5.0",
"react-router-dom": "^7.8.1",
"tailwindcss": "^4.1.16"
"react-hook-form": "^7.53.0",
"react-resizable-panels": "^2.1.3",
"react-router-dom": "^6.23.0",
"react-toastify": "^11.0.5",
"recharts": "^2.12.7",
"tailwindcss": "^3.4.1",
"vite": "^7.1.6",
"zod": "^3.23.8",
"@supabase/supabase-js": "^2.57.4"
},
"devDependencies": {
"@eslint/js": "^9.33.0",
"@types/axios": "^0.9.36",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@vitejs/plugin-react": "^5.0.0",
"eslint": "^9.33.0",
"@eslint/js": "^9.35.0",
"@types/react": "^19.1.13",
"@types/react-dom": "^19.1.9",
"eslint": "^9.35.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.3.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.39.1",
"vite": "^7.1.2"
"typescript-eslint": "^8.43.0"
}
}
}

View File

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

View File

@@ -0,0 +1,14 @@
User-agent: Googlebot
Allow: /
User-agent: Bingbot
Allow: /
User-agent: Twitterbot
Allow: /
User-agent: facebookexternalhit
Allow: /
User-agent: *
Allow: /

View File

@@ -0,0 +1,149 @@
import React, { useState } from 'react';
import { Heart, ShoppingCart, Star } from 'lucide-react';
const DonateShop: React.FC = () => {
const [donatedItems, setDonatedItems] = useState<Set<number>>(new Set());
const products = [
{
id: 1,
name: 'Coffee Support',
price: 5,
originalPrice: 10,
image: 'https://images.unsplash.com/photo-1509042239860-f550ce710b93?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
description: 'Fuel my coding sessions with a virtual coffee',
rating: 4.8,
reviews: 124
},
{
id: 2,
name: 'Meal Contribution',
price: 15,
originalPrice: 25,
image: 'https://images.unsplash.com/photo-1565299624946-b28f40a0ca4b?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
description: 'Help me focus on creating amazing projects',
rating: 4.9,
reviews: 89
},
{
id: 3,
name: 'Project Boost',
price: 25,
originalPrice: 40,
image: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
description: 'Support the development of new features',
rating: 5.0,
reviews: 67
},
{
id: 4,
name: 'Monthly Patron',
price: 50,
originalPrice: 75,
image: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
description: 'Ongoing support for continuous improvement',
rating: 4.7,
reviews: 45
}
];
const handleDonate = (productId: number) => {
setDonatedItems(prev => new Set(prev).add(productId));
// Here you would integrate with a payment processor like Stripe
alert(`Thank you for your donation of $${products.find(p => p.id === productId)?.price}!`);
};
return (
<section className="py-16 bg-background">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold text-text sm:text-4xl">
Support My Creative Journey
</h2>
<p className="mt-4 text-lg text-lines max-w-2xl mx-auto">
Instead of buying products, consider donating to support my creative journey
</p>
</div>
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
{products.map((product) => (
<div
key={product.id}
className="bg-background-light rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2 group border border-lines overflow-hidden"
>
<div className="relative overflow-hidden">
<img
className="w-full h-48 object-cover group-hover:scale-110 transition-transform duration-300"
src={product.image}
alt={product.name}
loading="lazy"
/>
<div className="absolute top-4 left-4">
<div className="bg-other text-background px-2 py-1 rounded-full text-xs font-medium">
Donation
</div>
</div>
<div className="absolute top-4 right-4 flex items-center space-x-1">
<Star className="h-4 w-4 text-other fill-current" />
<span className="text-text text-sm font-medium bg-background/70 px-1 rounded">
{product.rating}
</span>
</div>
</div>
<div className="p-6">
<h3 className="text-xl font-semibold text-text mb-2">
{product.name}
</h3>
<p className="text-lines mb-4 text-sm">
{product.description}
</p>
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-2">
<span className="text-2xl font-bold text-text">
${product.price}
</span>
<span className="text-sm text-lines line-through">
${product.originalPrice}
</span>
</div>
<span className="text-sm text-lines">
({product.reviews} reviews)
</span>
</div>
<button
onClick={() => handleDonate(product.id)}
disabled={donatedItems.has(product.id)}
className={`w-full flex items-center justify-center px-4 py-3 rounded-lg text-sm font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-other focus:ring-offset-2 ${
donatedItems.has(product.id)
? 'bg-other/20 text-other cursor-not-allowed'
: 'bg-other text-background hover:bg-lines transform hover:scale-105'
}`}
>
{donatedItems.has(product.id) ? (
<>
<Heart className="h-5 w-5 mr-2 fill-current" />
Donated!
</>
) : (
<>
<ShoppingCart className="h-5 w-5 mr-2" />
Donate Now
</>
)}
</button>
</div>
</div>
))}
</div>
<div className="text-center mt-12">
<p className="text-sm text-lines">
All donations go towards improving my portfolio and creating more amazing projects
</p>
</div>
</div>
</section>
);
};
export default DonateShop;

View File

@@ -0,0 +1,99 @@
import React, { useState, useEffect } from 'react';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
const DroneVideoCarousel: React.FC = () => {
const [currentIndex, setCurrentIndex] = useState(0);
// Placeholder YouTube video IDs - replace with actual drone video IDs
const videos = [
{ id: 'dQw4w9WgXcQ', title: 'Drone Footage 1' },
{ id: 'dQw4w9WgXcQ', title: 'Drone Footage 2' },
{ id: 'dQw4w9WgXcQ', title: 'Drone Footage 3' },
{ id: 'dQw4w9WgXcQ', title: 'Drone Footage 4' }
];
const nextSlide = () => {
setCurrentIndex((prevIndex) => (prevIndex + 1) % videos.length);
};
const prevSlide = () => {
setCurrentIndex((prevIndex) => (prevIndex - 1 + videos.length) % videos.length);
};
useEffect(() => {
const timer = setInterval(nextSlide, 5000);
return () => clearInterval(timer);
}, []);
return (
<section className="py-20 bg-background">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold text-text sm:text-4xl">
Drone Videography
</h2>
<p className="mt-4 text-lg text-lines max-w-2xl mx-auto">
Capturing stunning aerial perspectives through professional drone footage
</p>
</div>
<div className="relative max-w-4xl mx-auto">
<div className="aspect-video bg-background-light rounded-xl overflow-hidden shadow-2xl border border-lines">
<AnimatePresence mode="wait">
<motion.div
key={currentIndex}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.5 }}
className="w-full h-full"
>
<iframe
src={`https://www.youtube.com/embed/${videos[currentIndex].id}?autoplay=0&mute=1&loop=1&playlist=${videos[currentIndex].id}`}
title={videos[currentIndex].title}
className="w-full h-full"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
/>
</motion.div>
</AnimatePresence>
</div>
<button
onClick={prevSlide}
className="absolute left-4 top-1/2 transform -translate-y-1/2 bg-boxes hover:bg-other text-text p-3 rounded-full transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-lines focus:ring-offset-2 focus:ring-offset-background"
aria-label="Previous video"
>
<ChevronLeft className="h-6 w-6" />
</button>
<button
onClick={nextSlide}
className="absolute right-4 top-1/2 transform -translate-y-1/2 bg-boxes hover:bg-other text-text p-3 rounded-full transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-lines focus:ring-offset-2 focus:ring-offset-background"
aria-label="Next video"
>
<ChevronRight className="h-6 w-6" />
</button>
<div className="flex justify-center mt-6 space-x-2">
{videos.map((_, index) => (
<button
key={index}
onClick={() => setCurrentIndex(index)}
className={`w-3 h-3 rounded-full transition-all duration-200 ${
index === currentIndex
? 'bg-other'
: 'bg-lines hover:bg-boxes'
}`}
aria-label={`Go to video ${index + 1}`}
/>
))}
</div>
</div>
</div>
</section>
);
};
export default DroneVideoCarousel;

View File

@@ -0,0 +1,127 @@
import React from 'react';
import { Instagram, Twitter, Youtube, Github, Linkedin, Gamepad2, Mail, Phone } from 'lucide-react';
const Footer: React.FC = () => {
const socialLinks = [
{ name: 'Instagram', icon: Instagram, href: '#', color: 'hover:text-other' },
{ name: 'Twitter', icon: Twitter, href: '#', color: 'hover:text-other' },
{ name: 'YouTube', icon: Youtube, href: '#', color: 'hover:text-other' },
{ name: 'GitHub', icon: Github, href: '#', color: 'hover:text-text' },
{ name: 'LinkedIn', icon: Linkedin, href: '#', color: 'hover:text-other' },
{ name: 'Steam', icon: Gamepad2, href: '#', color: 'hover:text-lines' }
];
const footerLinks = [
{
title: 'Portfolio',
links: [
{ name: 'Web Development', href: '/portfolio/web' },
{ name: 'Mobile Apps', href: '/portfolio/mobile' },
{ name: 'UI/UX Design', href: '/portfolio/design' }
]
},
{
title: 'Services',
links: [
{ name: 'Frontend Development', href: '/services/frontend' },
{ name: 'Backend Development', href: '/services/backend' },
{ name: 'Consulting', href: '/services/consulting' }
]
},
{
title: 'Company',
links: [
{ name: 'About', href: '/about' },
{ name: 'Blog', href: '/blog' },
{ name: 'Contact', href: '/contact' }
]
}
];
return (
<footer className="bg-background text-text border-t border-lines">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{/* Brand Section */}
<div className="lg:col-span-1">
<div className="flex items-center">
<span className="text-2xl font-bold text-other">
Portfolio
</span>
</div>
<p className="mt-4 text-lines text-sm">
Creating exceptional digital experiences through innovative web development and beautiful design.
</p>
<div className="mt-6 flex space-x-4">
{socialLinks.map((social) => {
const IconComponent = social.icon;
return (
<a
key={social.name}
href={social.href}
className={`text-lines ${social.color} transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-other focus:ring-offset-2 focus:ring-offset-background rounded`}
aria-label={social.name}
>
<IconComponent className="h-5 w-5" />
</a>
);
})}
</div>
<div className="mt-6 space-y-2">
<div className="flex items-center text-lines text-sm">
<Mail className="h-4 w-4 mr-2" />
hello@example.com
</div>
<div className="flex items-center text-lines text-sm">
<Phone className="h-4 w-4 mr-2" />
+1 (555) 123-4567
</div>
</div>
</div>
{/* Links Sections */}
{footerLinks.map((section) => (
<div key={section.title}>
<h3 className="text-sm font-semibold text-text uppercase tracking-wider">
{section.title}
</h3>
<ul className="mt-4 space-y-2">
{section.links.map((link) => (
<li key={link.name}>
<a
href={link.href}
className="text-lines hover:text-other transition-colors duration-200 text-sm focus:outline-none focus:ring-2 focus:ring-other focus:ring-offset-2 focus:ring-offset-background rounded"
>
{link.name}
</a>
</li>
))}
</ul>
</div>
))}
</div>
<div className="mt-8 pt-8 border-t border-boxes">
<div className="flex flex-col md:flex-row justify-between items-center">
<p className="text-lines text-sm">
© 2025 Portfolio. All rights reserved.
</p>
<div className="mt-4 md:mt-0 flex space-x-6">
<a href="/privacy" className="text-lines hover:text-text text-sm transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-other focus:ring-offset-2 focus:ring-offset-background rounded">
Privacy Policy
</a>
<a href="/terms" className="text-lines hover:text-text text-sm transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-other focus:ring-offset-2 focus:ring-offset-background rounded">
Terms of Service
</a>
</div>
<p className="text-lines text-sm mt-4 md:mt-0">
Built with by <a rel="nofollow" target="_blank" href="https://meku.dev" className="text-other hover:text-lines transition-colors duration-200">Meku.dev</a>
</p>
</div>
</div>
</div>
</footer>
);
};
export default Footer;

View File

@@ -0,0 +1,186 @@
import React, { useState, useRef, useEffect } from 'react';
import { ChevronDown, Menu, X } from 'lucide-react';
const Header: React.FC = () => {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [activeDropdown, setActiveDropdown] = useState<string | null>(null);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const handleMouseEnter = (menu: string) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
setActiveDropdown(menu);
};
const handleMouseLeave = () => {
timeoutRef.current = setTimeout(() => {
setActiveDropdown(null);
}, 150);
};
useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);
const navigationItems = [
{ name: 'Home', href: '/' },
{
name: 'Portfolio',
href: '/portfolio',
submenu: [
{ name: 'Web Development', href: '/portfolio/web' },
{ name: 'Mobile Apps', href: '/portfolio/mobile' },
{ name: 'UI/UX Design', href: '/portfolio/design' },
{ name: 'E-commerce', href: '/portfolio/ecommerce' }
]
},
{
name: 'Services',
href: '/services',
submenu: [
{ name: 'Frontend Development', href: '/services/frontend' },
{ name: 'Backend Development', href: '/services/backend' },
{ name: 'Full Stack Solutions', href: '/services/fullstack' },
{ name: 'Consulting', href: '/services/consulting' }
]
},
{
name: 'About',
href: '/about',
submenu: [
{ name: 'My Story', href: '/about/story' },
{ name: 'Skills', href: '/about/skills' },
{ name: 'Experience', href: '/about/experience' },
{ name: 'Testimonials', href: '/about/testimonials' }
]
},
{ name: 'Blog', href: '/blog' },
{ name: 'Contact', href: '/contact' }
];
return (
<header className="bg-background shadow-lg sticky top-0 z-50">
<nav className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" role="navigation" aria-label="Main navigation">
<div className="flex justify-between items-center h-16">
{/* Logo */}
<div className="flex-shrink-0">
<a href="/" className="text-2xl font-bold text-other hover:text-lines transition-colors">
Portfolio
</a>
</div>
{/* Desktop Navigation */}
<div className="hidden md:block">
<div className="ml-10 flex items-baseline space-x-4">
{navigationItems.map((item) => (
<div
key={item.name}
className="relative"
onMouseEnter={() => item.submenu && handleMouseEnter(item.name)}
onMouseLeave={handleMouseLeave}
>
<a
href={item.href}
className="flex items-center px-3 py-2 rounded-md text-sm font-medium text-text hover:text-other hover:bg-background-light transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-lines focus:ring-offset-2"
aria-haspopup={item.submenu ? 'true' : 'false'}
aria-expanded={activeDropdown === item.name ? 'true' : 'false'}
>
{item.name}
{item.submenu && (
<ChevronDown
className={`ml-1 h-4 w-4 transition-transform duration-200 ${
activeDropdown === item.name ? 'rotate-180' : ''
}`}
aria-hidden="true"
/>
)}
</a>
{/* Dropdown Menu */}
{item.submenu && (
<div
className={`absolute left-0 mt-2 w-56 rounded-md shadow-lg bg-background-light ring-1 ring-lines ring-opacity-30 transition-all duration-200 ${
activeDropdown === item.name
? 'opacity-100 visible transform translate-y-0'
: 'opacity-0 invisible transform -translate-y-2'
}`}
role="menu"
aria-orientation="vertical"
>
<div className="py-1">
{item.submenu.map((subItem) => (
<a
key={subItem.name}
href={subItem.href}
className="block px-4 py-2 text-sm text-text hover:bg-boxes hover:text-other transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-lines focus:ring-inset"
role="menuitem"
>
{subItem.name}
</a>
))}
</div>
</div>
)}
</div>
))}
</div>
</div>
{/* Mobile menu button */}
<div className="md:hidden">
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="inline-flex items-center justify-center p-2 rounded-md text-text hover:text-other hover:bg-background-light focus:outline-none focus:ring-2 focus:ring-inset focus:ring-lines transition-colors duration-200"
aria-expanded={isMobileMenuOpen}
aria-label="Toggle mobile menu"
>
{isMobileMenuOpen ? (
<X className="block h-6 w-6" aria-hidden="true" />
) : (
<Menu className="block h-6 w-6" aria-hidden="true" />
)}
</button>
</div>
</div>
{/* Mobile Navigation */}
<div className={`md:hidden transition-all duration-300 ease-in-out ${
isMobileMenuOpen ? 'max-h-screen opacity-100' : 'max-h-0 opacity-0 overflow-hidden'
}`}>
<div className="px-2 pt-2 pb-3 space-y-1 sm:px-3 bg-background-light rounded-lg mt-2">
{navigationItems.map((item) => (
<div key={item.name}>
<a
href={item.href}
className="block px-3 py-2 rounded-md text-base font-medium text-text hover:text-other hover:bg-boxes transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-lines focus:ring-offset-2"
>
{item.name}
</a>
{item.submenu && (
<div className="ml-4 space-y-1">
{item.submenu.map((subItem) => (
<a
key={subItem.name}
href={subItem.href}
className="block px-3 py-2 rounded-md text-sm text-lines hover:text-other hover:bg-boxes transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-lines focus:ring-offset-2"
>
{subItem.name}
</a>
))}
</div>
)}
</div>
))}
</div>
</div>
</nav>
</header>
);
};
export default Header;

View File

@@ -0,0 +1,59 @@
import React from 'react';
import { ArrowRight, Download } from 'lucide-react';
const Hero: React.FC = () => {
return (
<section className="relative bg-background-light py-20 lg:py-32 overflow-hidden">
<div className="absolute inset-0 bg-grid-pattern opacity-5"></div>
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="lg:grid lg:grid-cols-12 lg:gap-8 items-center">
<div className="sm:text-center md:max-w-2xl md:mx-auto lg:col-span-6 lg:text-left">
<h1 className="text-4xl font-bold text-text sm:text-5xl lg:text-6xl">
<span className="block">Creative</span>
<span className="block text-other">
Developer
</span>
</h1>
<p className="mt-6 text-lg text-lines sm:text-xl max-w-3xl">
I craft exceptional digital experiences through innovative web development,
combining cutting-edge technology with beautiful design to bring your ideas to life.
</p>
<div className="mt-8 sm:max-w-lg sm:mx-auto sm:text-center lg:text-left lg:mx-0">
<div className="flex flex-col sm:flex-row gap-4">
<a
href="/portfolio"
className="inline-flex items-center justify-center px-6 py-3 border border-transparent text-base font-medium rounded-lg text-background bg-other hover:bg-lines focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-other transition-all duration-200 transform hover:scale-105"
>
View My Work
<ArrowRight className="ml-2 h-5 w-5" aria-hidden="true" />
</a>
<a
href="/resume.pdf"
className="inline-flex items-center justify-center px-6 py-3 border-2 border-lines text-base font-medium rounded-lg text-text bg-transparent hover:bg-background hover:border-other hover:text-other focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-other transition-all duration-200"
>
<Download className="mr-2 h-5 w-5" aria-hidden="true" />
Download Resume
</a>
</div>
</div>
</div>
<div className="mt-12 relative sm:max-w-lg sm:mx-auto lg:mt-0 lg:max-w-none lg:mx-0 lg:col-span-6 lg:flex lg:items-center">
<div className="relative mx-auto w-full rounded-lg shadow-lg lg:max-w-md">
<div className="relative block w-full bg-boxes rounded-lg overflow-hidden">
<img
className="w-full h-96 object-cover"
src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=687&q=80"
alt="Professional developer portrait"
loading="lazy"
/>
<div className="absolute inset-0 bg-gradient-to-tr from-boxes/40 to-other/20"></div>
</div>
</div>
</div>
</div>
</div>
</section>
);
};
export default Hero;

View File

@@ -0,0 +1,110 @@
import React from 'react';
import { ExternalLink, Github } from 'lucide-react';
const Portfolio: React.FC = () => {
const projects = [
{
title: 'E-Commerce Platform',
description: 'A full-stack e-commerce solution with React, Node.js, and Stripe integration.',
image: 'https://images.unsplash.com/photo-1556742049-0cfed4f6a45d?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
tags: ['React', 'Node.js', 'MongoDB', 'Stripe'],
liveUrl: '#',
githubUrl: '#'
},
{
title: 'Task Management App',
description: 'A collaborative project management tool with real-time updates and team features.',
image: 'https://images.unsplash.com/photo-1611224923853-80b023f02d71?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
tags: ['Vue.js', 'Firebase', 'Tailwind CSS'],
liveUrl: '#',
githubUrl: '#'
},
{
title: 'Weather Dashboard',
description: 'A responsive weather application with location-based forecasts and data visualization.',
image: 'https://images.unsplash.com/photo-1504608524841-42fe6f032b4b?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
tags: ['React', 'TypeScript', 'Chart.js', 'API'],
liveUrl: '#',
githubUrl: '#'
}
];
return (
<section className="py-20 bg-background">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h2 className="text-3xl font-bold text-text sm:text-4xl">
Featured Projects
</h2>
<p className="mt-4 text-lg text-lines max-w-2xl mx-auto">
A showcase of my recent work and creative solutions
</p>
</div>
<div className="mt-16 grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
{projects.map((project, index) => (
<div
key={project.title}
className="bg-background-light rounded-xl shadow-lg overflow-hidden hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2 group border border-lines"
>
<div className="relative overflow-hidden">
<img
className="w-full h-48 object-cover group-hover:scale-110 transition-transform duration-300"
src={project.image}
alt={project.title}
loading="lazy"
/>
<div className="absolute inset-0 bg-gradient-to-t from-background/80 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div className="absolute top-4 right-4 flex space-x-2 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<a
href={project.liveUrl}
className="p-2 bg-boxes rounded-full hover:bg-other transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-lines focus:ring-offset-2"
aria-label={`View ${project.title} live`}
>
<ExternalLink className="h-4 w-4 text-text" />
</a>
<a
href={project.githubUrl}
className="p-2 bg-boxes rounded-full hover:bg-other transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-lines focus:ring-offset-2"
aria-label={`View ${project.title} source code`}
>
<Github className="h-4 w-4 text-text" />
</a>
</div>
</div>
<div className="p-6">
<h3 className="text-xl font-semibold text-text mb-2">
{project.title}
</h3>
<p className="text-lines mb-4">
{project.description}
</p>
<div className="flex flex-wrap gap-2">
{project.tags.map((tag) => (
<span
key={tag}
className="px-3 py-1 text-xs font-medium bg-boxes text-other rounded-full"
>
{tag}
</span>
))}
</div>
</div>
</div>
))}
</div>
<div className="text-center mt-12">
<a
href="/portfolio"
className="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-lg text-background bg-other hover:bg-lines focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-other transition-all duration-200 transform hover:scale-105"
>
View All Projects
</a>
</div>
</div>
</section>
);
};
export default Portfolio;

View File

@@ -0,0 +1,115 @@
import React from 'react';
import { Briefcase, Code, Database, Palette, Smartphone, Zap } from 'lucide-react';
const Skills: React.FC = () => {
const experience = [
{
title: 'Senior Full-Stack Developer',
company: 'Tech Innovations Inc.',
period: '2022 - Present',
description: 'Leading development of scalable web applications using React, Node.js, and cloud technologies.'
},
{
title: 'Frontend Developer',
company: 'Digital Solutions Ltd.',
period: '2020 - 2022',
description: 'Built responsive user interfaces and improved performance for e-commerce platforms.'
},
{
title: 'Junior Developer',
company: 'StartupXYZ',
period: '2019 - 2020',
description: 'Developed mobile applications and contributed to backend API development.'
}
];
const specificSkills = [
{ name: 'React', level: 95, icon: Code },
{ name: 'TypeScript', level: 90, icon: Code },
{ name: 'Node.js', level: 85, icon: Database },
{ name: 'Python', level: 80, icon: Code },
{ name: 'UI/UX Design', level: 75, icon: Palette },
{ name: 'Mobile Development', level: 70, icon: Smartphone }
];
return (
<section className="py-20 bg-background-light">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h2 className="text-3xl font-bold text-text sm:text-4xl">
Experience & Skills
</h2>
<p className="mt-4 text-lg text-lines max-w-2xl mx-auto">
A comprehensive overview of my professional journey and technical expertise
</p>
</div>
<div className="mt-16 grid grid-cols-1 lg:grid-cols-2 gap-12">
{/* Experience Section */}
<div>
<div className="flex items-center mb-8">
<div className="p-3 bg-other rounded-lg mr-4">
<Briefcase className="h-6 w-6 text-background" />
</div>
<h3 className="text-2xl font-bold text-text">Experience</h3>
</div>
<div className="space-y-6">
{experience.map((exp, index) => (
<div
key={index}
className="bg-background rounded-lg p-6 border border-lines"
>
<h4 className="text-lg font-semibold text-text mb-1">
{exp.title}
</h4>
<p className="text-other font-medium mb-2">
{exp.company} {exp.period}
</p>
<p className="text-lines text-sm">
{exp.description}
</p>
</div>
))}
</div>
</div>
{/* Specific Skills Section */}
<div>
<div className="flex items-center mb-8">
<div className="p-3 bg-boxes rounded-lg mr-4">
<Zap className="h-6 w-6 text-text" />
</div>
<h3 className="text-2xl font-bold text-text">Specific Skills</h3>
</div>
<div className="space-y-6">
{specificSkills.map((skill, index) => {
const IconComponent = skill.icon;
return (
<div key={skill.name} className="bg-background rounded-lg p-6 shadow-md border border-lines">
<div className="flex items-center justify-between mb-3">
<div className="flex items-center">
<div className="p-2 bg-boxes rounded-lg mr-3">
<IconComponent className="h-5 w-5 text-other" />
</div>
<span className="font-semibold text-text">{skill.name}</span>
</div>
<span className="text-sm font-medium text-lines">{skill.level}%</span>
</div>
<div className="w-full bg-background-light rounded-full h-2">
<div
className="bg-other h-2 rounded-full transition-all duration-1000 ease-out"
style={{ width: `${skill.level}%` }}
></div>
</div>
</div>
);
})}
</div>
</div>
</div>
</div>
</section>
);
};
export default Skills;

View File

@@ -0,0 +1,100 @@
import React from 'react';
import { TrendingUp, BarChart3 } from 'lucide-react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
// Placeholder data - replace with actual Trading212 API data
const data = [
{ date: '2024-01', value: 1000 },
{ date: '2024-02', value: 1200 },
{ date: '2024-03', value: 1100 },
{ date: '2024-04', value: 1400 },
{ date: '2024-05', value: 1300 },
{ date: '2024-06', value: 1600 }
];
const TradingGraph: React.FC = () => {
return (
<section className="py-16 bg-background-light">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold text-text sm:text-4xl">
Trading Performance
</h2>
<p className="mt-4 text-lg text-lines max-w-2xl mx-auto">
Real-time insights from Trading212 portfolio tracking
</p>
</div>
<div className="bg-background rounded-xl shadow-lg p-6 border border-lines">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center space-x-3">
<div className="p-2 bg-other rounded-lg">
<BarChart3 className="h-6 w-6 text-background" />
</div>
<div>
<h3 className="text-lg font-semibold text-text">Portfolio Value</h3>
<p className="text-sm text-lines">Trading212 Integration</p>
</div>
</div>
<div className="text-right">
<div className="text-2xl font-bold text-text">$1,600</div>
<div className="flex items-center text-sm text-other">
<TrendingUp className="h-4 w-4 mr-1" />
+12.5% this month
</div>
</div>
</div>
<div className="h-64">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={data}>
<CartesianGrid strokeDasharray="3 3" stroke="var(--c-lines)" />
<XAxis
dataKey="date"
stroke="var(--c-lines)"
fontSize={12}
tickLine={false}
axisLine={false}
/>
<YAxis
stroke="var(--c-lines)"
fontSize={12}
tickLine={false}
axisLine={false}
tickFormatter={(value) => `$${value}`}
/>
<Tooltip
contentStyle={{
backgroundColor: 'var(--c-background-light)',
border: '1px solid var(--c-lines)',
borderRadius: '8px',
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
color: 'var(--c-text)'
}}
labelStyle={{ color: 'var(--c-text)' }}
formatter={(value: number) => [`$${value}`, 'Value']}
/>
<Line
type="monotone"
dataKey="value"
stroke="var(--c-other)"
strokeWidth={3}
dot={{ fill: '#c026d3', strokeWidth: 2, r: 4 }}
activeDot={{ r: 6, stroke: '#c026d3', strokeWidth: 2, fill: '#ffffff' }}
/>
</LineChart>
</ResponsiveContainer>
</div>
<div className="mt-6 text-center">
<p className="text-sm text-slate-500">
API integration pending - displaying sample data
</p>
</div>
</div>
</div>
</section>
);
};
export default TradingGraph;

View File

@@ -0,0 +1,83 @@
import React from 'react';
import { ExternalLink } from 'lucide-react';
const WebsiteScreenshots: React.FC = () => {
const websites = [
{
title: 'E-Commerce Platform',
image: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
url: '#',
description: 'Modern online store with seamless checkout'
},
{
title: 'Portfolio Website',
image: 'https://images.unsplash.com/photo-1467232004584-a241de8bcf5d?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
url: '#',
description: 'Creative showcase for digital artists'
},
{
title: 'Business Landing Page',
image: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
url: '#',
description: 'Professional B2B service presentation'
},
{
title: 'Blog Platform',
image: 'https://images.unsplash.com/photo-1486312338219-ce68e2c6f44d?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
url: '#',
description: 'Content management system for writers'
}
];
return (
<section className="py-16 bg-background-light">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold text-text sm:text-4xl">
Website Screenshots
</h2>
<p className="mt-4 text-lg text-lines max-w-2xl mx-auto">
A glimpse of recent web development projects and designs
</p>
</div>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4">
{websites.map((website, index) => (
<div
key={website.title}
className="group bg-background rounded-lg shadow-md hover:shadow-lg transition-all duration-300 transform hover:-translate-y-1 border border-lines overflow-hidden"
>
<div className="relative overflow-hidden">
<img
className="w-full h-32 object-cover group-hover:scale-105 transition-transform duration-300"
src={website.image}
alt={`${website.title} screenshot`}
loading="lazy"
/>
<div className="absolute inset-0 bg-gradient-to-t from-background/80 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-end justify-end p-2">
<a
href={website.url}
className="p-2 bg-boxes rounded-full hover:bg-other transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-lines focus:ring-offset-2"
aria-label={`View ${website.title}`}
>
<ExternalLink className="h-4 w-4 text-text" />
</a>
</div>
</div>
<div className="p-4">
<h3 className="text-sm font-semibold text-text mb-1">
{website.title}
</h3>
<p className="text-xs text-lines">
{website.description}
</p>
</div>
</div>
))}
</div>
</div>
</section>
);
};
export default WebsiteScreenshots;

View File

@@ -0,0 +1,26 @@
import React from 'react';
import Header from '../components/Header';
import Footer from '../components/Footer';
const About: React.FC = () => {
return (
<div className="min-h-screen">
<Header />
<main className="py-20">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h1 className="text-4xl font-bold text-slate-800 sm:text-5xl">
About
</h1>
<p className="mt-4 text-lg text-slate-600 max-w-2xl mx-auto">
Ask Meku to generate content for this page.
</p>
</div>
</div>
</main>
<Footer />
</div>
);
};
export default About;

View File

@@ -0,0 +1,26 @@
import React from 'react';
import Header from '../components/Header';
import Footer from '../components/Footer';
const Blog: React.FC = () => {
return (
<div className="min-h-screen">
<Header />
<main className="py-20">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h1 className="text-4xl font-bold text-slate-800 sm:text-5xl">
Blog
</h1>
<p className="mt-4 text-lg text-slate-600 max-w-2xl mx-auto">
Ask Meku to generate content for this page.
</p>
</div>
</div>
</main>
<Footer />
</div>
);
};
export default Blog;

View File

@@ -0,0 +1,26 @@
import React from 'react';
import Header from '../components/Header';
import Footer from '../components/Footer';
const Contact: React.FC = () => {
return (
<div className="min-h-screen">
<Header />
<main className="py-20">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h1 className="text-4xl font-bold text-slate-800 sm:text-5xl">
Contact
</h1>
<p className="mt-4 text-lg text-slate-600 max-w-2xl mx-auto">
Ask Meku to generate content for this page.
</p>
</div>
</div>
</main>
<Footer />
</div>
);
};
export default Contact;

View File

@@ -0,0 +1,30 @@
import React from 'react';
import Header from '../components/Header';
import Hero from '../components/Hero';
import Skills from '../components/Skills';
import Portfolio from '../components/Portfolio';
import DroneVideoCarousel from '../components/DroneVideoCarousel';
import WebsiteScreenshots from '../components/WebsiteScreenshots';
import TradingGraph from '../components/TradingGraph';
import DonateShop from '../components/DonateShop';
import Footer from '../components/Footer';
const Home: React.FC = () => {
return (
<div className="min-h-screen">
<Header />
<main>
<Hero />
<Skills />
<Portfolio />
<DroneVideoCarousel />
<WebsiteScreenshots />
<TradingGraph />
<DonateShop />
</main>
<Footer />
</div>
);
};
export default Home;

View File

@@ -0,0 +1,179 @@
import React from 'react';
const NotFound: React.FC = () => {
return (
<section className="relative flex py-10 min-h-screen items-center justify-center overflow-hidden bg-black">
<div className="mx-auto relative z-30 w-full max-w-[600px] text-center px-4">
{/* Large 404 Text */}
<div className="mb-8">
<svg
width="472"
height="158"
viewBox="0 0 472 158"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M26.4028 0.5224C29.8616 0.5224 32.6655 3.3263 32.6655 6.7851V58.7187H75.8726V9.4306C75.8726 5.97182 78.6765 3.16794 82.1353 3.16791H102.236C105.694 3.16817 108.499 5.97196 108.499 9.4306V151.215C108.498 154.673 105.694 157.477 102.235 157.477H82.1353C78.6766 157.477 75.8727 154.673 75.8726 151.215V91.3437H23.0571C19.5983 91.3437 16.7944 88.5398 16.7944 85.081V78.1181H6.30322C2.84444 78.1181 0.0405444 75.3142 0.0405273 71.8554V6.7851C0.0405355 3.32631 2.84444 0.522409 6.30322 0.5224H26.4028Z"
fill="url(#paint0_linear_11881_2293)"
/>
<path
d="M262.328 82.4706C263.99 82.4708 265.338 83.8187 265.338 85.4814V97.8544H277.712C279.375 97.8546 280.723 99.2018 280.723 100.864V116.31C280.723 117.973 279.375 119.321 277.712 119.321H260.835C259.173 119.321 257.825 117.973 257.825 116.31V103.937H214.175V116.31C214.175 117.973 212.827 119.321 211.165 119.321H194.289C192.626 119.321 191.278 117.973 191.278 116.31V100.864C191.278 99.2017 192.626 97.8544 194.289 97.8544H207.02V85.4814C207.02 83.8186 208.368 82.4706 210.031 82.4706H262.328Z"
fill="url(#paint1_linear_11881_2293)"
/>
<path
d="M222.614 41.3251C224.277 41.3251 225.625 42.6731 225.625 44.3359V59.7812C225.625 61.4439 224.277 62.7919 222.614 62.7919H205.737C204.074 62.7917 202.727 61.4438 202.727 59.7812V44.3359C202.727 42.6733 204.074 41.3254 205.737 41.3251H222.614Z"
fill="url(#paint2_linear_11881_2293)"
/>
<path
d="M266.263 41.3251C267.926 41.3252 269.274 42.6732 269.274 44.3359V59.7812C269.274 61.4439 267.926 62.7918 266.263 62.7919H249.386C247.724 62.7918 246.375 61.4439 246.375 59.7812V44.3359C246.375 42.6732 247.724 41.3252 249.386 41.3251H266.263Z"
fill="url(#paint3_linear_11881_2293)"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M291.231 3.16693C313.322 3.16693 331.231 21.0755 331.231 43.1669V117.477L331.218 118.51C330.671 140.124 312.977 157.477 291.231 157.477H180.769C158.678 157.477 140.769 139.569 140.769 117.477V43.1669C140.769 21.0755 158.678 3.16693 180.769 3.16693H291.231ZM180.769 27.1669C171.932 27.1669 164.769 34.3304 164.769 43.1669V117.477C164.769 126.314 171.932 133.477 180.769 133.477H291.231C300.068 133.477 307.231 126.314 307.231 117.477V43.1669C307.231 34.3304 300.068 27.1669 291.231 27.1669H180.769Z"
fill="url(#paint4_linear_11881_2293)"
/>
<path
d="M389.865 0.5224C393.324 0.522421 396.127 3.32632 396.127 6.7851V58.7187H439.334V9.4306C439.334 5.9718 442.138 3.16791 445.597 3.16791H465.697C469.156 3.16791 471.959 5.9718 471.959 9.4306V151.215C471.959 154.673 469.155 157.477 465.697 157.477H445.597C442.138 157.477 439.335 154.673 439.334 151.215V91.3437H386.518C383.059 91.3436 380.255 88.5397 380.255 85.081V78.1181H369.765C366.306 78.1181 363.502 75.3142 363.502 71.8554V6.7851C363.502 3.3263 366.306 0.5224 369.765 0.5224H389.865Z"
fill="url(#paint5_linear_11881_2293)"
/>
<defs>
<linearGradient
id="paint0_linear_11881_2293"
x1="471.959"
y1="157.477"
x2="448.654"
y2="-49.8952"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#3D404D" />
<stop offset="1" stopColor="#8F95B2" />
</linearGradient>
<linearGradient
id="paint1_linear_11881_2293"
x1="471.959"
y1="157.477"
x2="448.654"
y2="-49.8952"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#3D404D" />
<stop offset="1" stopColor="#8F95B2" />
</linearGradient>
<linearGradient
id="paint2_linear_11881_2293"
x1="471.959"
y1="157.477"
x2="448.654"
y2="-49.8952"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#3D404D" />
<stop offset="1" stopColor="#8F95B2" />
</linearGradient>
<linearGradient
id="paint3_linear_11881_2293"
x1="471.959"
y1="157.477"
x2="448.654"
y2="-49.8952"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#3D404D" />
<stop offset="1" stopColor="#8F95B2" />
</linearGradient>
<linearGradient
id="paint4_linear_11881_2293"
x1="471.959"
y1="157.477"
x2="448.654"
y2="-49.8952"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#3D404D" />
<stop offset="1" stopColor="#8F95B2" />
</linearGradient>
<linearGradient
id="paint5_linear_11881_2293"
x1="471.959"
y1="157.477"
x2="448.654"
y2="-49.8952"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#3D404D" />
<stop offset="1" stopColor="#8F95B2" />
</linearGradient>
</defs>
</svg>
</div>
<h1 className="mb-4 text-3xl font-bold text-white sm:text-4xl">
OPPS! Page Not Found
</h1>
<p className="mb-8 text-base text-white/60 sm:text-lg">
We can&apos;t seem to find the page you are looking for!
</p>
<a
href={typeof window !== 'undefined' ? window.location.origin : '/'}
className="inline-flex items-center gap-2 rounded-full bg-white px-6 py-3 text-sm font-medium text-black transition-colors hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-black"
>
Back to homepage
</a>
{/* Footer */}
<div className="mt-16">
<p className="text-sm text-gray-600">
© {new Date().getFullYear()} - Meku.dev
</p>
</div>
</div>
<div className="absolute inset-0 bg-[url(https://meku.dev/images/grain.png)] bg-cover bg-center opacity-60 mix-blend-soft-light z-20"></div>
<div className="absolute bottom-0 left-0 right-0 z-10">
<svg
width="2192"
height="771"
viewBox="0 0 2192 771"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g opacity="0.35" filter="url(#filter0_f_3740_75)">
<path
d="M199.999 258.919C199.999 86.6144 601.152 347.404 1096 347.404C1590.85 347.404 1992 86.6146 1992 258.919C1992 431.223 1590.85 570.904 1096 570.904C601.152 570.904 199.999 431.223 199.999 258.919Z"
fill="#C0C2CF"
/>
</g>
<defs>
<filter
id="filter0_f_3740_75"
x="-0.000732422"
y="0.0515137"
width="2192"
height="770.852"
filterUnits="userSpaceOnUse"
colorInterpolationFilters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="100"
result="effect1_foregroundBlur_3740_75"
/>
</filter>
</defs>
</svg>
</div>
</section>
);
};
export default NotFound;

View File

@@ -0,0 +1,26 @@
import React from 'react';
import Header from '../components/Header';
import Footer from '../components/Footer';
const Portfolio: React.FC = () => {
return (
<div className="min-h-screen">
<Header />
<main className="py-20">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h1 className="text-4xl font-bold text-slate-800 sm:text-5xl">
Portfolio
</h1>
<p className="mt-4 text-lg text-slate-600 max-w-2xl mx-auto">
Ask Meku to generate content for this page.
</p>
</div>
</div>
</main>
<Footer />
</div>
);
};
export default Portfolio;

Some files were not shown because too many files have changed in this diff Show More