feat: update main blog

This commit is contained in:
Oleg Proskurin 2026-01-20 19:28:34 +07:00
parent cff9197a6b
commit 7a9997cf79
3 changed files with 57 additions and 29 deletions

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import { useState } from 'react'; import { useState } from 'react';
import { Mail } from 'lucide-react'; import { submitEmail } from '@/lib/actions/waitlistActions';
export const BlogNewsletter = () => { export const BlogNewsletter = () => {
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
@ -13,28 +13,24 @@ export const BlogNewsletter = () => {
setStatus('loading'); setStatus('loading');
// TODO: Integrate with email service const result = await submitEmail(email);
// For now, just simulate success
await new Promise((resolve) => setTimeout(resolve, 500));
console.log('Newsletter subscription:', email);
setStatus('success'); if (result.success) {
setEmail(''); setStatus('success');
setEmail('');
// Reset status after 3 seconds } else {
setTimeout(() => setStatus('idle'), 3000); setStatus('error');
setTimeout(() => setStatus('idle'), 3000);
}
}; };
return ( return (
<div className="rounded-xl bg-[#161b28] p-6 text-center border border-white/5 shadow-xl relative overflow-hidden"> <div className="rounded-xl bg-[#161b28] p-6 text-center border border-white/5 shadow-xl relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-b from-violet-500/5 to-transparent pointer-events-none" /> <div className="absolute inset-0 bg-gradient-to-b from-violet-500/5 to-transparent pointer-events-none" />
<div className="relative z-10"> <div className="relative z-10">
<div className="w-12 h-12 bg-violet-500/10 rounded-xl flex items-center justify-center mx-auto mb-4 border border-violet-500/20">
<Mail className="w-5 h-5 text-violet-400" />
</div>
<h4 className="text-base font-bold text-white mb-2">Subscribe to Banatie</h4> <h4 className="text-base font-bold text-white mb-2">Subscribe to Banatie</h4>
<p className="text-sm text-gray-400 mb-6 leading-relaxed"> <p className="text-sm text-gray-400 mb-4 leading-relaxed">
Get the latest technical articles, tutorials, and updates delivered right to your inbox. Get the latest articles and updates delivered to your inbox.
</p> </p>
<form onSubmit={handleSubmit} className="space-y-3"> <form onSubmit={handleSubmit} className="space-y-3">
<input <input
@ -42,7 +38,7 @@ export const BlogNewsletter = () => {
value={email} value={email}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
placeholder="your@email.com" placeholder="your@email.com"
disabled={status === 'loading'} disabled={status === 'loading' || status === 'success'}
className="block w-full rounded-lg border border-white/10 bg-[#0B0F19] py-2 px-3 text-white text-sm placeholder:text-gray-600 focus:border-violet-500 focus:ring-1 focus:ring-violet-500 focus:outline-none disabled:opacity-50" className="block w-full rounded-lg border border-white/10 bg-[#0B0F19] py-2 px-3 text-white text-sm placeholder:text-gray-600 focus:border-violet-500 focus:ring-1 focus:ring-violet-500 focus:outline-none disabled:opacity-50"
/> />
<button <button
@ -52,7 +48,8 @@ export const BlogNewsletter = () => {
> >
{status === 'loading' && 'Subscribing...'} {status === 'loading' && 'Subscribing...'}
{status === 'success' && 'Subscribed!'} {status === 'success' && 'Subscribed!'}
{(status === 'idle' || status === 'error') && 'Subscribe'} {status === 'error' && 'Try again'}
{status === 'idle' && 'Subscribe'}
</button> </button>
</form> </form>
</div> </div>

View File

@ -1,16 +1,47 @@
'use client';
import { useState } from 'react';
type TabType = 'latest' | 'popular';
interface BlogPageHeaderProps { interface BlogPageHeaderProps {
title?: string; title?: string;
onTabChange?: (tab: TabType) => void;
} }
export const BlogPageHeader = ({ title = 'Latest Articles' }: BlogPageHeaderProps) => { export const BlogPageHeader = ({
title = 'Latest Articles',
onTabChange,
}: BlogPageHeaderProps) => {
const [activeTab, setActiveTab] = useState<TabType>('latest');
const handleTabClick = (tab: TabType) => {
setActiveTab(tab);
onTabChange?.(tab);
};
return ( return (
<div className="flex items-center justify-between mb-8 pb-6 border-b border-white/5"> <div className="flex items-center justify-between mb-8 pb-6 border-b border-white/5">
<h1 className="text-3xl font-bold text-white tracking-tight">{title}</h1> <h1 className="text-3xl font-bold text-white tracking-tight">{title}</h1>
<div className="flex items-center gap-2 text-sm text-gray-400 bg-white/5 p-1 rounded-lg border border-white/5"> <div className="flex items-center gap-2 text-sm text-gray-400 bg-white/5 p-1 rounded-lg border border-white/5">
<button className="px-3 py-1.5 rounded-md bg-[#111827] text-white shadow-sm font-medium text-xs transition-all"> <button
onClick={() => handleTabClick('latest')}
className={`px-3 py-1.5 rounded-md text-xs font-medium transition-all ${
activeTab === 'latest'
? 'bg-[#111827] text-white shadow-sm'
: 'hover:text-white'
}`}
>
Latest Latest
</button> </button>
<button className="px-3 py-1.5 rounded-md hover:text-white text-xs font-medium transition-colors"> <button
onClick={() => handleTabClick('popular')}
className={`px-3 py-1.5 rounded-md text-xs font-medium transition-all ${
activeTab === 'popular'
? 'bg-[#111827] text-white shadow-sm'
: 'hover:text-white'
}`}
>
Popular Popular
</button> </button>
</div> </div>

View File

@ -1,13 +1,13 @@
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { getAllPosts, getCategories } from './utils'; import { getAllPosts } from './utils';
import { import {
BlogBackground, BlogBackground,
BlogArticleCard, BlogArticleCard,
BlogPageHeader, BlogPageHeader,
BlogSearchInput, // BlogSearchInput,
BlogCategories, // BlogCategories,
BlogNewsletter, BlogNewsletter,
BlogTags, // BlogTags,
} from './_components'; } from './_components';
export const metadata: Metadata = { export const metadata: Metadata = {
@ -16,11 +16,11 @@ export const metadata: Metadata = {
'Articles, guides, and updates about AI-powered placeholder images.', 'Articles, guides, and updates about AI-powered placeholder images.',
}; };
const defaultTags = ['ai', 'image-generation', 'api', 'midjourney', 'flux']; // const defaultTags = ['ai', 'image-generation', 'api', 'midjourney', 'flux'];
export default function BlogPage() { export default function BlogPage() {
const posts = getAllPosts(); const posts = getAllPosts();
const categories = getCategories(); // const categories = getCategories();
return ( return (
<> <>
@ -53,10 +53,10 @@ export default function BlogPage() {
</div> </div>
<aside className="lg:col-span-4 xl:col-span-3 space-y-8"> <aside className="lg:col-span-4 xl:col-span-3 space-y-8">
<BlogSearchInput /> {/* <BlogSearchInput /> */}
<BlogCategories categories={categories} /> {/* <BlogCategories categories={categories} /> */}
<BlogNewsletter /> <BlogNewsletter />
<BlogTags tags={defaultTags} /> {/* <BlogTags tags={defaultTags} /> */}
</aside> </aside>
</div> </div>
</div> </div>