Files
Orchestrator/privat/CV/andreknie.de/src/pages/Articles.jsx
T

110 lines
4.2 KiB
React

import React, { useState } from 'react'
import { Search } from 'lucide-react'
import { useContent } from '../hooks/useContent'
import SEOHead from '../components/SEOHead'
import { decodeHtml } from '../utils/decodeHtml'
import './Articles.css'
export default function Articles() {
const [searchQuery, setSearchQuery] = useState('')
const { items: posts } = useContent('posts', { pageSize: 100 })
const filteredPosts = posts?.filter(post => {
if (!searchQuery) return true
const query = searchQuery.toLowerCase()
return (
(post.title && post.title.toLowerCase().includes(query)) ||
(post.excerpt && post.excerpt.toLowerCase().includes(query)) ||
(post.searchText && post.searchText.toLowerCase().includes(query))
)
})
return (
<main className="page-container articles-page">
<SEOHead
url="/artikel"
title="Artikel & Beiträge"
description="Aktuelle Artikel und LinkedIn-Beiträge zu den Themen Softwareentwicklung, Leadership und Technologie."
/>
<header className="page-header">
<div className="app-container">
<h1 className="hero-title">
Meine <span>Artikel</span>
</h1>
<p className="hero-subtitle">
Gedanken, Analysen und Erfahrungen rund um Software Engineering, Führung und Technologie.
</p>
</div>
</header>
<div className="app-container" style={{ marginBottom: '40px' }}>
<div style={{ maxWidth: '800px', margin: '0 auto', position: 'relative' }}>
<div style={{ position: 'absolute', left: '16px', top: '50%', transform: 'translateY(-50%)', color: 'var(--text-secondary)' }}>
<Search size={20} />
</div>
<input
type="text"
placeholder="Artikel durchsuchen..."
aria-label="Artikel durchsuchen"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
style={{
width: '100%',
padding: '16px 80px 16px 48px',
borderRadius: '12px',
border: '1px solid rgba(255,255,255,0.1)',
background: 'var(--glass-bg)',
color: 'var(--text-primary)',
fontSize: '1rem',
outline: 'none'
}}
/>
<div style={{ position: 'absolute', right: '16px', top: '50%', transform: 'translateY(-50%)', color: 'var(--text-secondary)', fontSize: '0.9rem' }}>
{filteredPosts?.length || 0} / {posts?.length || 0}
</div>
</div>
</div>
<section className="articles-grid app-container">
{filteredPosts?.map((post) => (
<article key={post.id || post.slug} className="article-card glass-card">
{post.image && (
<div className="article-image">
<img src={post.image} alt={post.title} />
</div>
)}
<div className="article-content">
{post.date && (
<span className="article-date">
{new Date(post.date).toLocaleDateString('de-DE', {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</span>
)}
<h2 className="article-title">{decodeHtml(post.title)}</h2>
<p className="article-excerpt">
{decodeHtml(post.excerpt || (post.searchText ? post.searchText.substring(0, 150) + '...' : ''))}
</p>
<div style={{ display: 'flex', gap: '16px' }}>
<a href={`/artikel/${post.slug}`} className="article-link">
Weiterlesen
</a>
{post.url && post.url.includes('/podcast') && (
<a href={post.url} className="article-link" style={{ color: 'var(--accent-color)' }}>
Zum Podcast
</a>
)}
</div>
</div>
</article>
))}
{(!filteredPosts || filteredPosts.length === 0) && (
<p className="no-articles">Keine Artikel für diese Suche gefunden.</p>
)}
</section>
</main>
)
}