200 lines
9.8 KiB
TypeScript
200 lines
9.8 KiB
TypeScript
|
|
import React from 'react';
|
||
|
|
import { useParams, Link } from 'react-router-dom';
|
||
|
|
import { useI18n } from '../hooks/useI18n';
|
||
|
|
import { SEO } from '../components/SEO';
|
||
|
|
import { TableOfContents } from '../components/TableOfContents';
|
||
|
|
import { Newsletter } from '../components/Newsletter';
|
||
|
|
import { PostCard } from '../components/PostCard';
|
||
|
|
import { formatDate } from '../lib/utils';
|
||
|
|
import { ArrowLeft, Clock, Twitter, Linkedin, Link as LinkIcon, Cpu, Terminal } from 'lucide-react';
|
||
|
|
import { motion } from 'motion/react';
|
||
|
|
import Markdown from 'react-markdown';
|
||
|
|
|
||
|
|
export default function PostDetail() {
|
||
|
|
const { slug } = useParams<{ slug: string }>();
|
||
|
|
const { lang, posts, t } = useI18n();
|
||
|
|
|
||
|
|
const validPosts = posts || [];
|
||
|
|
const post = validPosts.find(p => p.slug === slug);
|
||
|
|
const relatedPosts = validPosts.filter(p => p.slug !== slug && (p.category === post?.category || p.featured)).slice(0, 3);
|
||
|
|
|
||
|
|
if (!post) {
|
||
|
|
return (
|
||
|
|
<div className="container mx-auto px-6 py-40 text-center font-mono uppercase tracking-widest text-tech-muted">
|
||
|
|
<h1 className="text-4xl font-bold mb-8 text-tech-text">[error] 404_NOT_FOUND</h1>
|
||
|
|
<Link to={`/${lang}/blog`} className="text-tech-primary inline-flex items-center gap-2 font-bold bg-tech-surface px-6 py-2 rounded">
|
||
|
|
<ArrowLeft size={16} /> RETURN_TO_BASE
|
||
|
|
</Link>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="bg-white min-h-screen">
|
||
|
|
<SEO
|
||
|
|
title={post.title}
|
||
|
|
description={post.description}
|
||
|
|
image={post.image}
|
||
|
|
article
|
||
|
|
author={post.author}
|
||
|
|
datePublished={post.date}
|
||
|
|
category={post.category}
|
||
|
|
/>
|
||
|
|
|
||
|
|
{/* Technical Header */}
|
||
|
|
<section className="pt-32 pb-16 relative bg-white border-b border-slate-100">
|
||
|
|
<div className="container mx-auto px-6 lg:px-12 relative z-10">
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0, y: 10 }}
|
||
|
|
animate={{ opacity: 1, y: 0 }}
|
||
|
|
>
|
||
|
|
<Link to={`/${lang}/blog`} className="inline-flex items-center gap-2 text-tech-muted text-[10px] font-mono font-bold mb-8 hover:text-tech-text transition-colors uppercase tracking-widest">
|
||
|
|
<ArrowLeft size={12} /> cd ../blog
|
||
|
|
</Link>
|
||
|
|
|
||
|
|
<div className="flex items-center gap-3 mb-8">
|
||
|
|
<span className="category-tag">
|
||
|
|
{post.category}
|
||
|
|
</span>
|
||
|
|
<div className="w-1 h-1 rounded-full bg-tech-border" />
|
||
|
|
<span className="text-tech-muted text-[10px] uppercase font-mono font-bold tracking-widest flex items-center gap-1">
|
||
|
|
<Clock size={10} /> READ_TIME: {post.readingTime}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<h1 className="text-4xl md:text-5xl lg:text-7xl font-extrabold text-tech-text leading-[1] tracking-tighter mb-8 max-w-5xl">
|
||
|
|
{post.title}
|
||
|
|
</h1>
|
||
|
|
|
||
|
|
<p className="text-lg md:text-xl text-tech-muted mb-12 max-w-3xl leading-relaxed font-medium">
|
||
|
|
{post.description}
|
||
|
|
</p>
|
||
|
|
|
||
|
|
<div className="flex flex-col md:flex-row items-center gap-6 pt-8 border-t border-tech-border">
|
||
|
|
<div className="flex items-center gap-3">
|
||
|
|
<div className="w-10 h-10 rounded bg-tech-surface flex items-center justify-center font-mono font-bold text-tech-muted border border-tech-border">
|
||
|
|
{post.author[0]}
|
||
|
|
</div>
|
||
|
|
<div className="text-left leading-tight">
|
||
|
|
<span className="font-bold text-tech-text block text-sm">{post.author}</span>
|
||
|
|
<span className="text-tech-muted text-[10px] font-mono font-bold uppercase">system_editor</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div className="hidden md:block h-8 w-px bg-tech-border" />
|
||
|
|
<div className="text-left leading-tight">
|
||
|
|
<span className="text-tech-muted text-[10px] font-mono font-bold uppercase block">timestamp</span>
|
||
|
|
<span className="font-bold text-tech-text text-sm">{formatDate(post.date, lang)}</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</motion.div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
{/* Featured Technical Image */}
|
||
|
|
<section className="container mx-auto px-6 lg:px-12 py-12">
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0 }}
|
||
|
|
animate={{ opacity: 1 }}
|
||
|
|
transition={{ delay: 0.1 }}
|
||
|
|
className="aspect-video lg:aspect-[21/7] rounded-lg overflow-hidden border border-tech-border bg-tech-surface relative"
|
||
|
|
>
|
||
|
|
<img src={post.image} className="w-full h-full object-cover opacity-90 transition-opacity hover:opacity-100" alt={post.title} referrerPolicy="no-referrer" />
|
||
|
|
<div className="absolute bottom-4 right-4 bg-tech-text/80 backdrop-blur-md px-3 py-1 rounded text-[10px] font-mono text-white border border-slate-700 uppercase">IMG_REF: {post.slug}.jpg</div>
|
||
|
|
</motion.div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
{/* Content Section */}
|
||
|
|
<section className="container mx-auto px-6 lg:px-12 pb-32">
|
||
|
|
<div className="grid grid-cols-1 lg:grid-cols-[1fr_300px] gap-16">
|
||
|
|
|
||
|
|
{/* Main Article body */}
|
||
|
|
<article className="markdown-body w-full max-w-3xl border-r border-slate-50 pr-0 lg:pr-16">
|
||
|
|
<Markdown>{post.content}</Markdown>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
{/* Sidebar */}
|
||
|
|
<aside className="space-y-12">
|
||
|
|
<div className="sticky top-32">
|
||
|
|
<TableOfContents content={post.content} />
|
||
|
|
|
||
|
|
<div className="mt-8 p-6 bg-tech-surface rounded border border-tech-border">
|
||
|
|
<h4 className="text-[10px] font-mono font-bold uppercase tracking-widest text-tech-muted mb-4">./bio_metadata</h4>
|
||
|
|
<div className="flex items-center gap-3 mb-4">
|
||
|
|
<div className="w-10 h-10 rounded bg-white flex items-center justify-center font-bold text-tech-muted border border-tech-border">
|
||
|
|
{post.author[0]}
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<span className="font-bold text-tech-text block text-sm">{post.author}</span>
|
||
|
|
<span className="text-tech-primary text-[10px] font-mono font-bold uppercase">@alex_nexus</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<p className="text-xs text-tech-muted leading-relaxed font-medium">
|
||
|
|
Technical architect with 10+ years optimizing distributed systems and multimodal AI agents.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="mt-8 flex flex-col gap-2">
|
||
|
|
<div className="text-[10px] font-mono font-bold text-tech-muted uppercase tracking-widest mb-2">Compartilhar_Modulo</div>
|
||
|
|
<div className="flex gap-2">
|
||
|
|
<a
|
||
|
|
href={`https://twitter.com/intent/tweet?text=${encodeURIComponent(post.title)}&url=${encodeURIComponent(window.location.href)}`}
|
||
|
|
target="_blank"
|
||
|
|
rel="noopener noreferrer"
|
||
|
|
className="w-8 h-8 rounded bg-white border border-tech-border flex items-center justify-center text-tech-muted hover:text-tech-primary hover:border-tech-primary transition-all cursor-pointer"
|
||
|
|
>
|
||
|
|
<Twitter size={14} />
|
||
|
|
</a>
|
||
|
|
<a
|
||
|
|
href={`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(window.location.href)}`}
|
||
|
|
target="_blank"
|
||
|
|
rel="noopener noreferrer"
|
||
|
|
className="w-8 h-8 rounded bg-white border border-tech-border flex items-center justify-center text-tech-muted hover:text-tech-primary hover:border-tech-primary transition-all cursor-pointer"
|
||
|
|
>
|
||
|
|
<Linkedin size={14} />
|
||
|
|
</a>
|
||
|
|
<button
|
||
|
|
onClick={() => {
|
||
|
|
navigator.clipboard.writeText(window.location.href);
|
||
|
|
alert('Link copiado!');
|
||
|
|
}}
|
||
|
|
className="w-8 h-8 rounded bg-white border border-tech-border flex items-center justify-center text-tech-muted hover:text-tech-text hover:border-tech-text transition-all cursor-pointer"
|
||
|
|
>
|
||
|
|
<LinkIcon size={14} />
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</aside>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
{/* Related Technical Modules */}
|
||
|
|
{relatedPosts.length > 0 && (
|
||
|
|
<section className="py-24 bg-tech-surface border-t border-tech-border">
|
||
|
|
<div className="container mx-auto px-6 lg:px-12">
|
||
|
|
<div className="flex items-center justify-between mb-12">
|
||
|
|
<div>
|
||
|
|
<div className="flex items-center gap-2 text-tech-primary text-[10px] font-mono font-bold uppercase tracking-widest mb-4">
|
||
|
|
<div className="w-8 h-px bg-tech-primary" />
|
||
|
|
<span>REL_MODULES</span>
|
||
|
|
</div>
|
||
|
|
<h2 className="text-3xl font-bold tracking-tight text-tech-text">Módulos Relacionados</h2>
|
||
|
|
</div>
|
||
|
|
<Link to={`/${lang}/blog`} className="text-[10px] font-mono font-bold text-tech-muted hover:text-tech-text transition-colors uppercase tracking-widest border border-tech-border px-4 py-2 rounded">view_all_entries</Link>
|
||
|
|
</div>
|
||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-0 border-t border-l border-tech-border">
|
||
|
|
{relatedPosts.map(post => (
|
||
|
|
<div key={post.id} className="border-r border-b border-tech-border p-1 bg-white">
|
||
|
|
<PostCard post={post} />
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
)}
|
||
|
|
|
||
|
|
<Newsletter />
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|