110 lines
4.2 KiB
React
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>
|
|
)
|
|
}
|