Add Friends page with layout and navigation; update localization for new content

This commit is contained in:
2025-10-03 15:59:25 +08:00
parent 0149a79bef
commit 1b745c7ffc
10 changed files with 440 additions and 60 deletions

View File

@@ -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 (
<div className="app">
{/* Navigation Header */}
<nav className="navbar">
<div className="nav-container">
<div className="nav-logo">
<h2>🎯 {t('nav.logo')}</h2>
</div>
<div className="nav-links">
<a href="#servers">{t('nav.servers')}</a>
<a href="#blog">{t('nav.blog')}</a>
<a href="https://git.cialloo.com">{t('nav.git')}</a>
<a href="#forum">{t('nav.forum')}</a>
<a href="#friends">{t('nav.friends')}</a>
<ThemeToggle />
<LanguageSelector />
<button className="join-btn">{t('nav.joinNow')}</button>
</div>
</div>
</nav>
<Layout currentPage="home">
{/* Hero Section */}
<section className="hero">
<div className="hero-content">
@@ -165,41 +145,7 @@ function App() {
</div>
</div>
</section>
{/* Footer */}
<footer className="footer">
<div className="footer-container">
<div className="footer-section">
<h4>{t('footer.community')}</h4>
<p>{t('footer.communityDesc')}</p>
</div>
<div className="footer-section">
<h4>{t('footer.quickLinks')}</h4>
<a href="#servers">{t('nav.servers')}</a>
<a href="#blog">{t('nav.blog')}</a>
<a href="https://git.cialloo.com">{t('nav.git')}</a>
<a href="#forum">{t('nav.forum')}</a>
<a href="#friends">{t('nav.friends')}</a>
</div>
<div className="footer-section">
<h4>{t('footer.communityLinks')}</h4>
<a href="#">{t('footer.discord')}</a>
<a href="#">{t('footer.steamGroup')}</a>
<a href="#">{t('footer.tournaments')}</a>
<a href="#">{t('footer.support')}</a>
</div>
<div className="footer-section">
<h4>{t('footer.legal')}</h4>
<a href="#">{t('footer.privacyPolicy')}</a>
<a href="#">{t('footer.termsOfService')}</a>
<a href="#">{t('footer.contact')}</a>
</div>
</div>
<div className="footer-bottom">
<p>{t('footer.copyright')}</p>
</div>
</footer>
</div>
</Layout>
)
}

43
src/components/Footer.tsx Normal file
View File

@@ -0,0 +1,43 @@
import { useTranslation } from 'react-i18next'
function Footer() {
const { t } = useTranslation()
return (
<footer className="footer">
<div className="footer-container">
<div className="footer-section">
<h4>{t('footer.community')}</h4>
<p>{t('footer.communityDesc')}</p>
</div>
<div className="footer-section">
<h4>{t('footer.quickLinks')}</h4>
<a href="/">{t('nav.home')}</a>
<a href="/#servers">{t('nav.servers')}</a>
<a href="/#blog">{t('nav.blog')}</a>
<a href="https://git.cialloo.com">{t('nav.git')}</a>
<a href="/#forum">{t('nav.forum')}</a>
<a href="/friends">{t('nav.friends')}</a>
</div>
<div className="footer-section">
<h4>{t('footer.communityLinks')}</h4>
<a href="#">{t('footer.discord')}</a>
<a href="#">{t('footer.steamGroup')}</a>
<a href="#">{t('footer.tournaments')}</a>
<a href="#">{t('footer.support')}</a>
</div>
<div className="footer-section">
<h4>{t('footer.legal')}</h4>
<a href="#">{t('footer.privacyPolicy')}</a>
<a href="#">{t('footer.termsOfService')}</a>
<a href="#">{t('footer.contact')}</a>
</div>
</div>
<div className="footer-bottom">
<p>{t('footer.copyright')}</p>
</div>
</footer>
)
}
export default Footer

22
src/components/Layout.tsx Normal file
View File

@@ -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 (
<div className="app">
<Nav currentPage={currentPage} />
<main>
{children}
</main>
<Footer />
</div>
)
}
export default Layout

35
src/components/Nav.tsx Normal file
View File

@@ -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 (
<nav className="navbar">
<div className="nav-container">
<div className="nav-logo">
<h2>🎯 {t('nav.logo')}</h2>
</div>
<div className="nav-links">
<a href="/" className={currentPage === 'home' ? 'active' : ''}>{t('nav.home')}</a>
<a href="/#servers" className={currentPage === 'servers' ? 'active' : ''}>{t('nav.servers')}</a>
<a href="/#blog" className={currentPage === 'blog' ? 'active' : ''}>{t('nav.blog')}</a>
<a href="https://git.cialloo.com" className={currentPage === 'git' ? 'active' : ''}>{t('nav.git')}</a>
<a href="/#forum" className={currentPage === 'forum' ? 'active' : ''}>{t('nav.forum')}</a>
<Link to="/friends" className={currentPage === 'friends' ? 'active' : ''}>{t('nav.friends')}</Link>
<ThemeToggle />
<LanguageSelector />
<button className="join-btn">{t('nav.joinNow')}</button>
</div>
</div>
</nav>
)
}
export default Nav

View File

@@ -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"
}
}

View File

@@ -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": "联系我们"
}
}

View File

@@ -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(
<StrictMode>
<ThemeProvider>
<App />
<Router>
<Routes>
<Route path="/" element={<App />} />
<Route path="/friends" element={<Friends />} />
</Routes>
</Router>
</ThemeProvider>
</StrictMode>,
)

252
src/pages/Friends.tsx Normal file
View File

@@ -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 (
<Layout currentPage="friends">
{/* Friends Page Content */}
<section className="friends-section" style={{ padding: '120px 2rem 80px', maxWidth: '1200px', margin: '0 auto' }}>
<div className="friends-header" style={{ textAlign: 'center', marginBottom: '3rem' }}>
<h1 className="section-title" style={{ fontSize: '3rem', marginBottom: '1rem' }}>
{t('friends.title')}
</h1>
<p style={{ fontSize: '1.2rem', color: 'var(--text-secondary)', maxWidth: '600px', margin: '0 auto' }}>
{t('friends.subtitle')}
</p>
</div>
{/* Category Filter */}
<div className="friends-categories" style={{ display: 'flex', justifyContent: 'center', gap: '1rem', marginBottom: '3rem', flexWrap: 'wrap' }}>
<button className="category-btn active" style={{
padding: '0.5rem 1rem',
border: '1px solid var(--accent-color)',
background: 'var(--accent-color)',
color: 'white',
borderRadius: '20px',
cursor: 'pointer',
transition: 'all 0.3s ease'
}}>
{t('friends.allCategories')}
</button>
{categories.map(category => (
<button key={category} className="category-btn" style={{
padding: '0.5rem 1rem',
border: '1px solid var(--border-color)',
background: 'var(--bg-card)',
color: 'var(--text-primary)',
borderRadius: '20px',
cursor: 'pointer',
transition: 'all 0.3s ease'
}}>
{category}
</button>
))}
</div>
{/* Friends Grid */}
<div className="friends-grid" style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
gap: '2rem'
}}>
{friendLinks.map(friend => (
<div key={friend.id} className="friend-card" style={{
background: 'var(--bg-card)',
border: '1px solid var(--border-color)',
borderRadius: '12px',
padding: '2rem',
transition: 'all 0.3s ease',
cursor: 'pointer'
}}
onMouseEnter={(e) => {
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'
}}
>
<div className="friend-header" style={{ display: 'flex', alignItems: 'center', marginBottom: '1rem' }}>
<div className="friend-avatar" style={{
width: '50px',
height: '50px',
background: 'var(--accent-color)',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '1.5rem',
marginRight: '1rem',
color: 'white'
}}>
{friend.name.charAt(0)}
</div>
<div>
<h3 style={{ margin: '0 0 0.5rem 0', color: 'var(--text-primary)' }}>{friend.name}</h3>
<span style={{
background: 'var(--accent-color)',
color: 'white',
padding: '0.25rem 0.75rem',
borderRadius: '12px',
fontSize: '0.8rem',
fontWeight: 'bold'
}}>
{friend.category}
</span>
</div>
</div>
<p style={{ color: 'var(--text-secondary)', marginBottom: '1.5rem', lineHeight: '1.6' }}>
{friend.description}
</p>
<a
href={friend.url}
target="_blank"
rel="noopener noreferrer"
className="friend-link"
style={{
display: 'inline-block',
background: 'transparent',
color: 'var(--accent-color)',
border: '2px solid var(--accent-color)',
padding: '0.75rem 1.5rem',
borderRadius: '6px',
textDecoration: 'none',
fontWeight: 'bold',
transition: 'all 0.3s ease',
width: '100%',
textAlign: 'center'
}}
onMouseEnter={(e) => {
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')}
</a>
</div>
))}
</div>
{/* Call to Action */}
<div className="friends-cta" style={{
textAlign: 'center',
marginTop: '4rem',
padding: '3rem',
background: 'var(--bg-card)',
borderRadius: '12px',
border: '1px solid var(--border-color)'
}}>
<h2 style={{ color: 'var(--text-primary)', marginBottom: '1rem' }}>
{t('friends.wantToJoin')}
</h2>
<p style={{ color: 'var(--text-secondary)', marginBottom: '2rem', maxWidth: '600px', margin: '0 auto 2rem' }}>
{t('friends.joinDescription')}
</p>
<a
href="mailto:contact@cialloo.com?subject=Friend Link Request"
style={{
display: 'inline-block',
background: 'var(--accent-color)',
color: 'white',
padding: '1rem 2rem',
borderRadius: '8px',
textDecoration: 'none',
fontWeight: 'bold',
fontSize: '1.1rem',
transition: 'transform 0.3s ease'
}}
onMouseEnter={(e) => {
e.currentTarget.style.transform = 'translateY(-2px)'
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'translateY(0)'
}}
>
{t('friends.contactUs')}
</a>
</div>
</section>
</Layout>
)
}
export default Friends