diff --git a/package-lock.json b/package-lock.json index d042ea2..a26285a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "i18next-browser-languagedetector": "^8.2.0", "react": "^19.1.1", "react-dom": "^19.1.1", - "react-i18next": "^16.0.0" + "react-i18next": "^16.0.0", + "react-router-dom": "^7.9.3" }, "devDependencies": { "@eslint/js": "^9.36.0", @@ -1537,6 +1538,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2813,6 +2823,44 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.9.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.3.tgz", + "integrity": "sha512-4o2iWCFIwhI/eYAIL43+cjORXYn/aRQPgtFRRZb3VzoyQ5Uej0Bmqj7437L97N9NJW4wnicSwLOLS+yCXfAPgg==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.9.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.3.tgz", + "integrity": "sha512-1QSbA0TGGFKTAc/aWjpfW/zoEukYfU4dc1dLkT/vvf54JoGMkW+fNA+3oyo2gWVW1GM7BxjJVHz5GnPJv40rvg==", + "license": "MIT", + "dependencies": { + "react-router": "7.9.3" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2915,6 +2963,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/package.json b/package.json index d77d072..5adffe9 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "i18next-browser-languagedetector": "^8.2.0", "react": "^19.1.1", "react-dom": "^19.1.1", - "react-i18next": "^16.0.0" + "react-i18next": "^16.0.0", + "react-router-dom": "^7.9.3" }, "devDependencies": { "@eslint/js": "^9.36.0", diff --git a/src/App.tsx b/src/App.tsx index bf94143..bcb1544 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,5 @@ import { useTranslation } from 'react-i18next' -import LanguageSelector from './components/LanguageSelector' -import ThemeToggle from './components/ThemeToggle' +import Layout from './components/Layout' import './App.css' function App() { @@ -23,26 +22,7 @@ function App() { ] return ( -
- {/* Navigation Header */} - - + {/* Hero Section */}
@@ -165,41 +145,7 @@ function App() {
- - {/* Footer */} - - + ) } diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx new file mode 100644 index 0000000..c9c9e10 --- /dev/null +++ b/src/components/Footer.tsx @@ -0,0 +1,43 @@ +import { useTranslation } from 'react-i18next' + +function Footer() { + const { t } = useTranslation() + + return ( + + ) +} + +export default Footer \ No newline at end of file diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx new file mode 100644 index 0000000..6efd065 --- /dev/null +++ b/src/components/Layout.tsx @@ -0,0 +1,22 @@ +import { type ReactNode } from 'react' +import Nav from './Nav' +import Footer from './Footer' + +interface LayoutProps { + children: ReactNode + currentPage?: string +} + +function Layout({ children, currentPage }: LayoutProps) { + return ( +
+
+ ) +} + +export default Layout \ No newline at end of file diff --git a/src/components/Nav.tsx b/src/components/Nav.tsx new file mode 100644 index 0000000..8e2a2ae --- /dev/null +++ b/src/components/Nav.tsx @@ -0,0 +1,35 @@ +import { useTranslation } from 'react-i18next' +import { Link } from 'react-router-dom' +import LanguageSelector from './LanguageSelector' +import ThemeToggle from './ThemeToggle' + +interface NavProps { + currentPage?: string +} + +function Nav({ currentPage }: NavProps) { + const { t } = useTranslation() + + return ( + + ) +} + +export default Nav \ No newline at end of file diff --git a/src/locales/en.json b/src/locales/en.json index b85dec1..2a16eac 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1,6 +1,7 @@ { "nav": { "logo": "CS Community", + "home": "Home", "servers": "Servers", "blog": "Blog", "git": "Git", @@ -71,5 +72,14 @@ "languages": { "english": "English", "chinese": "中文" + }, + "friends": { + "title": "Friend Links", + "subtitle": "Discover amazing communities and resources from our partner websites", + "allCategories": "All Categories", + "visitSite": "Visit Site", + "wantToJoin": "Want to Join Our Friend Links?", + "joinDescription": "If you have a gaming-related website or community, we'd love to add you to our friend links network!", + "contactUs": "Contact Us" } } \ No newline at end of file diff --git a/src/locales/zh.json b/src/locales/zh.json index 343f26a..332ef91 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -1,6 +1,7 @@ { "nav": { "logo": "CS 社区", + "home": "首页", "servers": "服务器", "blog": "博客", "git": "开源", @@ -71,5 +72,14 @@ "languages": { "english": "English", "chinese": "中文" + }, + "friends": { + "title": "友情链接", + "subtitle": "发现来自我们合作伙伴网站的精彩社区和资源", + "allCategories": "全部类别", + "visitSite": "访问网站", + "wantToJoin": "想加入我们的友情链接吗?", + "joinDescription": "如果您有游戏相关的网站或社区,我们很乐意将您添加到我们的友情链接网络中!", + "contactUs": "联系我们" } } \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index 41fb950..f62c9dc 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,14 +1,21 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' +import { BrowserRouter as Router, Routes, Route } from 'react-router-dom' import './index.css' import './i18n' import { ThemeProvider } from './contexts/ThemeContext' import App from './App.tsx' +import Friends from './pages/Friends.tsx' createRoot(document.getElementById('root')!).render( - + + + } /> + } /> + + , ) diff --git a/src/pages/Friends.tsx b/src/pages/Friends.tsx new file mode 100644 index 0000000..1029bcd --- /dev/null +++ b/src/pages/Friends.tsx @@ -0,0 +1,252 @@ +import { useTranslation } from 'react-i18next' +import Layout from '../components/Layout' +import '../App.css' + +interface FriendLink { + id: number + name: string + description: string + url: string + avatar?: string + category: string +} + +function Friends() { + const { t } = useTranslation() + + // Mock friend links data + const friendLinks: FriendLink[] = [ + { + id: 1, + name: "GameDev Hub", + description: "A community for game developers sharing tutorials and resources", + url: "https://gamedevhub.example.com", + category: "Development" + }, + { + id: 2, + name: "Counter-Strike Pro", + description: "Professional Counter-Strike training and analysis platform", + url: "https://cspro.example.com", + category: "Gaming" + }, + { + id: 3, + name: "Tech Blog Central", + description: "Latest technology news and programming tutorials", + url: "https://techblog.example.com", + category: "Technology" + }, + { + id: 4, + name: "Esports Arena", + description: "Comprehensive esports coverage and tournament information", + url: "https://esportsarena.example.com", + category: "Esports" + }, + { + id: 5, + name: "Open Source Gaming", + description: "Open source gaming projects and community contributions", + url: "https://opensourcegaming.example.com", + category: "Open Source" + }, + { + id: 6, + name: "Gaming News Daily", + description: "Daily gaming news, reviews, and industry updates", + url: "https://gamingnews.example.com", + category: "News" + }, + { + id: 7, + name: "React Game Dev", + description: "Building games with React and modern web technologies", + url: "https://reactgamedev.example.com", + category: "Development" + }, + { + id: 8, + name: "Competitive Gaming", + description: "Tips, strategies, and guides for competitive gaming", + url: "https://competitivegaming.example.com", + category: "Gaming" + } + ] + + const categories = [...new Set(friendLinks.map(link => link.category))] + + return ( + + {/* Friends Page Content */} +
+
+

+ {t('friends.title')} +

+

+ {t('friends.subtitle')} +

+
+ + {/* Category Filter */} +
+ + {categories.map(category => ( + + ))} +
+ + {/* Friends Grid */} +
+ {friendLinks.map(friend => ( +
{ + e.currentTarget.style.transform = 'translateY(-5px)' + e.currentTarget.style.boxShadow = '0 10px 30px var(--accent-shadow)' + }} + onMouseLeave={(e) => { + e.currentTarget.style.transform = 'translateY(0)' + e.currentTarget.style.boxShadow = 'none' + }} + > +
+
+ {friend.name.charAt(0)} +
+
+

{friend.name}

+ + {friend.category} + +
+
+

+ {friend.description} +

+ { + e.currentTarget.style.background = 'var(--accent-color)' + e.currentTarget.style.color = 'white' + }} + onMouseLeave={(e) => { + e.currentTarget.style.background = 'transparent' + e.currentTarget.style.color = 'var(--accent-color)' + }} + > + {t('friends.visitSite')} + +
+ ))} +
+ + {/* Call to Action */} +
+

+ {t('friends.wantToJoin')} +

+

+ {t('friends.joinDescription')} +

+ { + e.currentTarget.style.transform = 'translateY(-2px)' + }} + onMouseLeave={(e) => { + e.currentTarget.style.transform = 'translateY(0)' + }} + > + {t('friends.contactUs')} + +
+
+
+ ) +} + +export default Friends \ No newline at end of file