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 (
+
+
+
+ {children}
+
+
+
+ )
+}
+
+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 => (
+
+ ))}
+
+
+ {/* Call to Action */}
+
+
+
+ )
+}
+
+export default Friends
\ No newline at end of file