UI updates
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -41,3 +41,4 @@ coverage/
|
||||
# Misc
|
||||
*.tar.gz
|
||||
*.zip
|
||||
.playwright-mcp/
|
||||
|
||||
@@ -3,6 +3,7 @@ export const dynamic = "force-dynamic"
|
||||
import { notFound } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import ShareButtons from '@/components/ShareButtons'
|
||||
|
||||
async function getArticleWithTags(slug: string) {
|
||||
try {
|
||||
@@ -27,12 +28,9 @@ async function getArticleWithTags(slug: string) {
|
||||
WHERE a.slug = ${slug} AND a.status = 'published'
|
||||
GROUP BY a.id, c.id
|
||||
`
|
||||
|
||||
if (rows.length === 0) return null
|
||||
|
||||
// Increment view count
|
||||
// Increment view count only here (not in generateMetadata)
|
||||
await sql`SELECT increment_view_count(${slug})`
|
||||
|
||||
return rows[0]
|
||||
} catch (error) {
|
||||
console.error('Error fetching article:', error)
|
||||
@@ -40,34 +38,46 @@ async function getArticleWithTags(slug: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Separate metadata fetch — no view count increment
|
||||
async function getArticleMeta(slug: string) {
|
||||
try {
|
||||
const { rows } = await sql`
|
||||
SELECT title_burmese, excerpt_burmese, featured_image
|
||||
FROM articles
|
||||
WHERE slug = ${slug} AND status = 'published'
|
||||
LIMIT 1
|
||||
`
|
||||
return rows[0] || null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function getRelatedArticles(articleId: number) {
|
||||
try {
|
||||
const { rows } = await sql`SELECT * FROM get_related_articles(${articleId}, 6)`
|
||||
return rows
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export default async function ImprovedArticlePage({ params }: { params: { slug: string } }) {
|
||||
export default async function ArticlePage({ params }: { params: { slug: string } }) {
|
||||
const article = await getArticleWithTags(params.slug)
|
||||
|
||||
if (!article) {
|
||||
notFound()
|
||||
}
|
||||
if (!article) notFound()
|
||||
|
||||
const relatedArticles = await getRelatedArticles(article.id)
|
||||
const publishedDate = new Date(article.published_at).toLocaleDateString('my-MM', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
day: 'numeric',
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white">
|
||||
{/* Hero Cover Image */}
|
||||
{article.featured_image && (
|
||||
<div className="relative h-[70vh] w-full overflow-hidden">
|
||||
<div className="relative h-[55vh] w-full overflow-hidden">
|
||||
<Image
|
||||
src={article.featured_image}
|
||||
alt={article.title_burmese}
|
||||
@@ -75,25 +85,20 @@ export default async function ImprovedArticlePage({ params }: { params: { slug:
|
||||
className="object-cover"
|
||||
priority
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/90 via-black/50 to-black/10" />
|
||||
|
||||
<div className="absolute inset-0 flex items-end">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 pb-16 w-full">
|
||||
{/* Category */}
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 pb-10 w-full">
|
||||
<Link
|
||||
href={`/category/${article.category_slug}`}
|
||||
className="inline-block mb-4 px-4 py-2 bg-primary rounded-full text-white font-semibold text-sm hover:bg-primary-dark transition-colors"
|
||||
className="inline-block mb-3 px-4 py-1.5 bg-primary rounded-full text-white font-semibold text-sm hover:bg-primary-dark transition-colors font-burmese"
|
||||
>
|
||||
{article.category_name_burmese}
|
||||
</Link>
|
||||
|
||||
{/* Title */}
|
||||
<h1 className="text-5xl md:text-6xl font-bold text-white mb-6 font-burmese leading-tight">
|
||||
<h1 className="text-2xl md:text-3xl lg:text-4xl font-bold text-white mb-4 font-burmese leading-snug line-clamp-3">
|
||||
{article.title_burmese}
|
||||
</h1>
|
||||
|
||||
{/* Meta */}
|
||||
<div className="flex flex-wrap items-center gap-4 text-white/90">
|
||||
<div className="flex flex-wrap items-center gap-3 text-white/80 text-sm">
|
||||
<span className="font-burmese">{publishedDate}</span>
|
||||
<span>•</span>
|
||||
<span className="font-burmese">{article.reading_time} မိနစ်</span>
|
||||
@@ -106,10 +111,10 @@ export default async function ImprovedArticlePage({ params }: { params: { slug:
|
||||
)}
|
||||
|
||||
{/* Article Content */}
|
||||
<article className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<article className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
{/* Tags */}
|
||||
{article.tags_burmese && article.tags_burmese.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2 mb-8 pb-8 border-b">
|
||||
<div className="flex flex-wrap gap-2 mb-8 pb-8 border-b border-gray-100">
|
||||
{article.tags_burmese.map((tag: string, idx: number) => (
|
||||
<Link
|
||||
key={idx}
|
||||
@@ -122,23 +127,18 @@ export default async function ImprovedArticlePage({ params }: { params: { slug:
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Article Body */}
|
||||
{/* Body */}
|
||||
<div className="article-content">
|
||||
<div dangerouslySetInnerHTML={{ __html: formatContent(article.content_burmese) }} />
|
||||
|
||||
{/* Additional Images Gallery */}
|
||||
{/* Image Gallery */}
|
||||
{article.images && article.images.length > 1 && (
|
||||
<div className="my-12">
|
||||
<h3 className="text-2xl font-bold mb-6 font-burmese">ဓာတ်ပုံများ</h3>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
<div className="my-10 not-prose">
|
||||
<h3 className="text-xl font-bold mb-4 font-burmese text-gray-900">ဓာတ်ပုံများ</h3>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
{article.images.slice(1).map((img: string, idx: number) => (
|
||||
<div key={idx} className="relative h-56 rounded-xl overflow-hidden image-zoom">
|
||||
<Image
|
||||
src={img}
|
||||
alt={`${article.title_burmese} - ${idx + 2}`}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
<div key={idx} className="relative h-48 rounded-xl overflow-hidden image-zoom">
|
||||
<Image src={img} alt={`${article.title_burmese} ${idx + 2}`} fill className="object-cover" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -147,8 +147,8 @@ export default async function ImprovedArticlePage({ params }: { params: { slug:
|
||||
|
||||
{/* Videos */}
|
||||
{article.videos && article.videos.length > 0 && (
|
||||
<div className="my-12">
|
||||
<h3 className="text-2xl font-bold mb-6 font-burmese">ဗီဒီယိုများ</h3>
|
||||
<div className="my-10 not-prose">
|
||||
<h3 className="text-xl font-bold mb-4 font-burmese text-gray-900">ဗီဒီယိုများ</h3>
|
||||
<div className="space-y-6">
|
||||
{article.videos.map((video: string, idx: number) => (
|
||||
<div key={idx} className="relative aspect-video rounded-xl overflow-hidden bg-gray-900 shadow-xl">
|
||||
@@ -162,21 +162,20 @@ export default async function ImprovedArticlePage({ params }: { params: { slug:
|
||||
|
||||
{/* Source Attribution */}
|
||||
{article.source_articles && article.source_articles.length > 0 && (
|
||||
<div className="mt-16 p-8 bg-gradient-to-br from-blue-50 to-indigo-50 rounded-2xl shadow-lg">
|
||||
<h3 className="text-2xl font-bold text-gray-900 mb-4 font-burmese flex items-center">
|
||||
<svg className="w-7 h-7 mr-3 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<div className="mt-12 p-6 bg-blue-50 rounded-2xl border border-blue-100">
|
||||
<h3 className="text-lg font-bold text-gray-900 mb-3 font-burmese flex items-center gap-2">
|
||||
<svg className="w-5 h-5 text-primary flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
မူရင်းသတင်းရင်းမြစ်များ
|
||||
</h3>
|
||||
<p className="text-sm text-gray-700 mb-6 font-burmese leading-relaxed">
|
||||
ဤဆောင်းပါးကို အောက်ပါမူရင်းသတင်းများမှ စုစည်း၍ မြန်မာဘာသာသို့ ပြန်ဆိုထားခြင်း ဖြစ်ပါသည်။ အားလုံးသော အကြွေးအရ မူရင်းစာရေးသူများနှင့် ထုတ်ပြန်သူများကို သက်ဆိုင်ပါသည်။
|
||||
<p className="text-sm text-gray-600 mb-4 font-burmese leading-relaxed">
|
||||
ဤဆောင်းပါးကို အောက်ပါမူရင်းသတင်းများမှ စုစည်း၍ မြန်မာဘာသာသို့ ပြန်ဆိုထားသည်။
|
||||
</p>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-3">
|
||||
{article.source_articles.map((source: any, index: number) => (
|
||||
<div key={index} className="bg-white p-5 rounded-xl shadow-sm hover:shadow-md transition-shadow border border-gray-100">
|
||||
<div className="flex items-start gap-4">
|
||||
<span className="flex-shrink-0 w-8 h-8 bg-primary text-white rounded-full flex items-center justify-center text-sm font-bold">
|
||||
<div key={index} className="bg-white p-4 rounded-xl border border-gray-100 flex items-start gap-3">
|
||||
<span className="flex-shrink-0 w-6 h-6 bg-primary text-white rounded-full flex items-center justify-center text-xs font-bold">
|
||||
{index + 1}
|
||||
</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
@@ -184,87 +183,56 @@ export default async function ImprovedArticlePage({ params }: { params: { slug:
|
||||
href={source.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary hover:text-primary-dark font-medium break-words hover:underline"
|
||||
className="text-primary hover:underline text-sm font-medium break-words"
|
||||
>
|
||||
{source.title}
|
||||
</a>
|
||||
{source.author && source.author !== 'Unknown' && (
|
||||
<p className="text-sm text-gray-600 mt-2">
|
||||
<span className="font-burmese font-semibold">စာရေးသူ:</span> {source.author}
|
||||
<p className="text-xs text-gray-500 mt-1 font-burmese">
|
||||
စာရေးသူ: {source.author}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<a
|
||||
href={source.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex-shrink-0 text-primary hover:text-primary-dark"
|
||||
title="Open source"
|
||||
>
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Share Section */}
|
||||
<div className="mt-12 py-8 border-y border-gray-200">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="font-burmese text-gray-700 font-semibold">မျှဝေပါ:</p>
|
||||
<div className="flex gap-3">
|
||||
<button className="px-4 py-2 bg-blue-600 text-white rounded-full hover:bg-blue-700 transition-colors">
|
||||
Facebook
|
||||
</button>
|
||||
<button className="px-4 py-2 bg-sky-500 text-white rounded-full hover:bg-sky-600 transition-colors">
|
||||
Twitter
|
||||
</button>
|
||||
<button className="px-4 py-2 bg-green-600 text-white rounded-full hover:bg-green-700 transition-colors">
|
||||
WhatsApp
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/* Share */}
|
||||
<div className="mt-10 py-8 border-t border-gray-100">
|
||||
<p className="font-burmese text-gray-700 font-semibold mb-4">မျှဝေပါ:</p>
|
||||
<ShareButtons title={article.title_burmese} />
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{/* Related Articles */}
|
||||
{relatedArticles.length > 0 && (
|
||||
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 bg-gray-50">
|
||||
<h2 className="text-3xl font-bold text-gray-900 mb-10 font-burmese">
|
||||
<section className="bg-gray-50 py-14">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-8 font-burmese">
|
||||
ဆက်စပ်ဆောင်းပါးများ
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{relatedArticles.map((related: any) => (
|
||||
<Link
|
||||
key={related.id}
|
||||
href={`/article/${related.slug}`}
|
||||
className="card card-hover"
|
||||
>
|
||||
<Link key={related.id} href={`/article/${related.slug}`} className="card card-hover">
|
||||
{related.featured_image && (
|
||||
<div className="relative h-48 w-full image-zoom">
|
||||
<Image
|
||||
src={related.featured_image}
|
||||
alt={related.title_burmese}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
<div className="relative h-44 w-full image-zoom">
|
||||
<Image src={related.featured_image} alt={related.title_burmese} fill className="object-cover" />
|
||||
</div>
|
||||
)}
|
||||
<div className="p-6">
|
||||
<h3 className="font-bold text-gray-900 font-burmese line-clamp-2 hover:text-primary transition-colors text-lg mb-3">
|
||||
<div className="p-5">
|
||||
<h3 className="font-bold text-gray-900 font-burmese line-clamp-3 hover:text-primary transition-colors leading-snug mb-2">
|
||||
{related.title_burmese}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 font-burmese line-clamp-2">
|
||||
<p className="text-sm text-gray-500 font-burmese line-clamp-2 leading-relaxed">
|
||||
{related.excerpt_burmese}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
@@ -272,28 +240,28 @@ export default async function ImprovedArticlePage({ params }: { params: { slug:
|
||||
}
|
||||
|
||||
function formatContent(content: string): string {
|
||||
let formatted = content
|
||||
.replace(/\n\n/g, '</p><p>')
|
||||
.replace(/## (.*?)\n/g, '<h2>$1</h2>')
|
||||
.replace(/### (.*?)\n/g, '<h3>$1</h3>')
|
||||
const formatted = content
|
||||
.replace(/### (.*?)(\n|$)/g, '<h3>$1</h3>')
|
||||
.replace(/## (.*?)(\n|$)/g, '<h2>$1</h2>')
|
||||
.replace(/# (.*?)(\n|$)/g, '<h1>$1</h1>')
|
||||
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
||||
.replace(/\*(.*?)\*/g, '<em>$1</em>')
|
||||
|
||||
.replace(/\n\n+/g, '</p><p>')
|
||||
.replace(/\n/g, '<br/>')
|
||||
return `<p>${formatted}</p>`
|
||||
}
|
||||
|
||||
function renderVideo(videoUrl: string) {
|
||||
let videoId = null
|
||||
|
||||
let videoId: string | null = null
|
||||
if (videoUrl.includes('youtube.com/watch')) {
|
||||
const match = videoUrl.match(/v=([^&]+)/)
|
||||
videoId = match ? match[1] : null
|
||||
const m = videoUrl.match(/v=([^&]+)/)
|
||||
videoId = m ? m[1] : null
|
||||
} else if (videoUrl.includes('youtu.be/')) {
|
||||
const match = videoUrl.match(/youtu\.be\/([^?]+)/)
|
||||
videoId = match ? match[1] : null
|
||||
const m = videoUrl.match(/youtu\.be\/([^?]+)/)
|
||||
videoId = m ? m[1] : null
|
||||
} else if (videoUrl.includes('youtube.com/embed/')) {
|
||||
const match = videoUrl.match(/embed\/([^?]+)/)
|
||||
videoId = match ? match[1] : null
|
||||
const m = videoUrl.match(/embed\/([^?]+)/)
|
||||
videoId = m ? m[1] : null
|
||||
}
|
||||
|
||||
if (videoId) {
|
||||
@@ -306,25 +274,12 @@ function renderVideo(videoUrl: string) {
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<iframe
|
||||
src={videoUrl}
|
||||
className="w-full h-full"
|
||||
allowFullScreen
|
||||
/>
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: { params: { slug: string } }) {
|
||||
const article = await getArticleWithTags(params.slug)
|
||||
|
||||
if (!article) {
|
||||
return {
|
||||
title: 'Article Not Found',
|
||||
}
|
||||
}
|
||||
|
||||
const article = await getArticleMeta(params.slug)
|
||||
if (!article) return { title: 'Article Not Found' }
|
||||
return {
|
||||
title: `${article.title_burmese} - Burmddit`,
|
||||
description: article.excerpt_burmese,
|
||||
|
||||
118
frontend/app/category/[slug]/page.tsx
Normal file
118
frontend/app/category/[slug]/page.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import { sql } from '@/lib/db'
|
||||
export const dynamic = "force-dynamic"
|
||||
import { notFound } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
|
||||
const CATEGORY_META: Record<string, { icon: string; color: string }> = {
|
||||
'ai-news': { icon: '📰', color: 'from-blue-600 to-blue-800' },
|
||||
'tutorials': { icon: '📚', color: 'from-purple-600 to-purple-800' },
|
||||
'tips-tricks': { icon: '💡', color: 'from-amber-500 to-orange-600' },
|
||||
'upcoming': { icon: '🚀', color: 'from-emerald-600 to-teal-700' },
|
||||
}
|
||||
|
||||
async function getCategory(slug: string) {
|
||||
try {
|
||||
const { rows } = await sql`SELECT * FROM categories WHERE slug = ${slug}`
|
||||
return rows[0] || null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function getArticlesByCategory(slug: string) {
|
||||
try {
|
||||
const { rows } = await sql`
|
||||
SELECT
|
||||
a.id, a.title_burmese, a.slug, a.excerpt_burmese,
|
||||
a.featured_image, a.reading_time, a.view_count, a.published_at,
|
||||
c.name_burmese as category_name_burmese, c.slug as category_slug
|
||||
FROM articles a
|
||||
JOIN categories c ON a.category_id = c.id
|
||||
WHERE c.slug = ${slug} AND a.status = 'published'
|
||||
ORDER BY a.published_at DESC
|
||||
LIMIT 30
|
||||
`
|
||||
return rows
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export default async function CategoryPage({ params }: { params: { slug: string } }) {
|
||||
const [category, articles] = await Promise.all([
|
||||
getCategory(params.slug),
|
||||
getArticlesByCategory(params.slug),
|
||||
])
|
||||
|
||||
if (!category) notFound()
|
||||
|
||||
const meta = CATEGORY_META[params.slug] ?? { icon: '📄', color: 'from-gray-600 to-gray-800' }
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Category Header */}
|
||||
<div className={`bg-gradient-to-br ${meta.color} text-white`}>
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||
<div className="text-5xl mb-4">{meta.icon}</div>
|
||||
<h1 className="text-4xl font-bold font-burmese mb-3">{category.name_burmese}</h1>
|
||||
<p className="text-white/80 font-burmese text-lg">
|
||||
ဆောင်းပါး {articles.length} ခု
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Articles Grid */}
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
{articles.length === 0 ? (
|
||||
<div className="text-center py-20 bg-white rounded-2xl shadow-sm">
|
||||
<div className="text-6xl mb-4">📭</div>
|
||||
<p className="text-xl text-gray-500 font-burmese">ဆောင်းပါးမရှိသေးပါ။ မကြာမီ ပြန်စစ်ကြည့်ပါ။</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{articles.map((article: any) => (
|
||||
<article key={article.id} className="card card-hover">
|
||||
{article.featured_image && (
|
||||
<Link href={`/article/${article.slug}`} className="block image-zoom">
|
||||
<div className="relative h-52 w-full">
|
||||
<Image
|
||||
src={article.featured_image}
|
||||
alt={article.title_burmese}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
<div className="p-6">
|
||||
<h2 className="text-lg font-bold text-gray-900 mb-3 font-burmese line-clamp-3 leading-snug">
|
||||
<Link href={`/article/${article.slug}`} className="hover:text-primary transition-colors">
|
||||
{article.title_burmese}
|
||||
</Link>
|
||||
</h2>
|
||||
<p className="text-gray-600 mb-4 font-burmese line-clamp-3 text-sm leading-relaxed">
|
||||
{article.excerpt_burmese}
|
||||
</p>
|
||||
<div className="flex items-center justify-between text-sm text-gray-500 pt-4 border-t border-gray-100">
|
||||
<span className="font-burmese">{article.reading_time} မိနစ်</span>
|
||||
<span>{article.view_count} views</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: { params: { slug: string } }) {
|
||||
const category = await getCategory(params.slug)
|
||||
if (!category) return { title: 'Category Not Found' }
|
||||
return {
|
||||
title: `${category.name_burmese} - Burmddit`,
|
||||
description: `${category.name_burmese} နှင့် ပတ်သက်သော နောက်ဆုံးရ AI ဆောင်းပါးများ`,
|
||||
}
|
||||
}
|
||||
@@ -17,24 +17,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Burmese Fonts - Better rendering */
|
||||
@font-face {
|
||||
font-family: 'Pyidaungsu';
|
||||
src: url('https://myanmar-tools-website.appspot.com/fonts/Pyidaungsu-2.5.3_Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
font-feature-settings: "liga" 1;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Pyidaungsu';
|
||||
src: url('https://myanmar-tools-website.appspot.com/fonts/Pyidaungsu-2.5.3_Bold.ttf') format('truetype');
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Burmese Fonts */
|
||||
.font-burmese {
|
||||
font-family: 'Pyidaungsu', 'Noto Sans Myanmar', 'Myanmar Text', sans-serif;
|
||||
font-family: 'Noto Sans Myanmar', 'Myanmar Text', sans-serif;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { Inter } from 'next/font/google'
|
||||
import { Inter, Noto_Sans_Myanmar } from 'next/font/google'
|
||||
import './globals.css'
|
||||
import Header from '@/components/Header'
|
||||
import Footer from '@/components/Footer'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
|
||||
const notoSansMyanmar = Noto_Sans_Myanmar({
|
||||
weight: ['300', '400', '500', '600', '700'],
|
||||
subsets: ['myanmar'],
|
||||
variable: '--font-burmese',
|
||||
display: 'swap',
|
||||
})
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Burmddit - Myanmar AI News & Tutorials',
|
||||
description: 'Daily AI news, tutorials, and tips in Burmese. Stay updated with the latest in artificial intelligence.',
|
||||
keywords: 'AI, Myanmar, Burmese, AI news, AI tutorials, machine learning, ChatGPT',
|
||||
title: 'Burmddit - Myanmar AI သတင်းများ',
|
||||
description: 'AI နှင့် နည်းပညာဆိုင်ရာ သတင်းများ၊ သင်ခန်းစာများနှင့် အကြံပြုချက်များကို မြန်မာဘာသာဖြင့် နေ့စဉ် ဖတ်ရှုနိုင်သော ပလက်ဖောင်း',
|
||||
keywords: 'AI, Myanmar, Burmese, AI news, AI tutorials, machine learning, ChatGPT, မြန်မာ AI',
|
||||
icons: {
|
||||
icon: "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' rx='20' fill='%232563eb'/><text x='50' y='72' font-size='60' text-anchor='middle' fill='white' font-family='Arial' font-weight='bold'>B</text></svg>",
|
||||
},
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
@@ -18,13 +27,8 @@ export default function RootLayout({
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="my" className="font-burmese">
|
||||
<head>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Myanmar:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
<body className={`${inter.className} bg-gray-50`}>
|
||||
<html lang="my" className={`${inter.variable} ${notoSansMyanmar.variable}`}>
|
||||
<body className={`${inter.className} bg-gray-50 antialiased`}>
|
||||
<Header />
|
||||
<main className="min-h-screen">
|
||||
{children}
|
||||
|
||||
32
frontend/app/not-found.tsx
Normal file
32
frontend/app/not-found.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="min-h-[70vh] flex items-center justify-center bg-gray-50">
|
||||
<div className="text-center px-4 max-w-lg">
|
||||
<div className="text-8xl mb-6">🔍</div>
|
||||
<h1 className="text-7xl font-bold text-gray-200 mb-4">404</h1>
|
||||
<h2 className="text-2xl font-bold text-gray-800 mb-4 font-burmese">
|
||||
စာမျက်နှာ မတွေ့ပါ
|
||||
</h2>
|
||||
<p className="text-gray-500 mb-8 font-burmese leading-relaxed">
|
||||
သင်ရှာနေသော စာမျက်နှာကို မတွေ့ပါ။ ဖျက်သိမ်းသွားခြင်း သို့မဟုတ် URL မှားနေနိုင်သည်။
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex items-center justify-center px-8 py-3 bg-primary text-white rounded-full font-semibold hover:bg-primary-dark transition-all font-burmese"
|
||||
>
|
||||
← ပင်မစာမျက်နှာ
|
||||
</Link>
|
||||
<Link
|
||||
href="/search"
|
||||
className="inline-flex items-center justify-center px-8 py-3 bg-white text-gray-700 border border-gray-300 rounded-full font-semibold hover:bg-gray-50 transition-all font-burmese"
|
||||
>
|
||||
🔍 ရှာဖွေရန်
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -81,7 +81,7 @@ export default async function ImprovedHome() {
|
||||
</Link>
|
||||
|
||||
{/* Title */}
|
||||
<h1 className="text-5xl md:text-6xl font-bold text-white mb-4 font-burmese leading-tight">
|
||||
<h1 className="text-2xl md:text-3xl lg:text-4xl font-bold text-white mb-4 font-burmese leading-snug line-clamp-3">
|
||||
<Link href={`/article/${featured.slug}`} className="hover:text-gray-200 transition-colors">
|
||||
{featured.title_burmese}
|
||||
</Link>
|
||||
|
||||
133
frontend/app/search/page.tsx
Normal file
133
frontend/app/search/page.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import { sql } from '@/lib/db'
|
||||
export const dynamic = "force-dynamic"
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
|
||||
async function searchArticles(query: string) {
|
||||
if (!query || query.trim().length < 2) return []
|
||||
try {
|
||||
const pattern = `%${query.trim()}%`
|
||||
const { rows } = await sql`
|
||||
SELECT
|
||||
a.id, a.title_burmese, a.slug, a.excerpt_burmese,
|
||||
a.featured_image, a.reading_time, a.view_count, a.published_at,
|
||||
c.name_burmese as category_name_burmese, c.slug as category_slug
|
||||
FROM articles a
|
||||
JOIN categories c ON a.category_id = c.id
|
||||
WHERE a.status = 'published'
|
||||
AND (
|
||||
a.title_burmese ILIKE ${pattern}
|
||||
OR a.excerpt_burmese ILIKE ${pattern}
|
||||
OR a.title ILIKE ${pattern}
|
||||
)
|
||||
ORDER BY a.published_at DESC
|
||||
LIMIT 20
|
||||
`
|
||||
return rows
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export default async function SearchPage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: { q?: string }
|
||||
}) {
|
||||
const query = searchParams.q ?? ''
|
||||
const results = await searchArticles(query)
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Search Header */}
|
||||
<div className="bg-white border-b border-gray-200">
|
||||
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-10">
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-6 font-burmese">ရှာဖွေရန်</h1>
|
||||
<form action="/search" method="GET">
|
||||
<div className="relative">
|
||||
<svg
|
||||
className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400"
|
||||
fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
<input
|
||||
type="search"
|
||||
name="q"
|
||||
defaultValue={query}
|
||||
placeholder="ဆောင်းပါးများ ရှာဖွေရန်..."
|
||||
className="w-full pl-12 pr-4 py-4 border-2 border-gray-200 rounded-2xl font-burmese text-lg focus:outline-none focus:border-primary transition-colors bg-gray-50"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Results */}
|
||||
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-10">
|
||||
{query && (
|
||||
<p className="text-sm text-gray-500 mb-6 font-burmese">
|
||||
“{query}” အတွက် ရလဒ် {results.length} ခု
|
||||
</p>
|
||||
)}
|
||||
|
||||
{!query && (
|
||||
<div className="text-center py-20 text-gray-400">
|
||||
<div className="text-6xl mb-4">🔍</div>
|
||||
<p className="font-burmese text-lg">ရှာဖွေလိုသည့် စကားလုံး ထည့်ပါ</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{query && results.length === 0 && (
|
||||
<div className="text-center py-20">
|
||||
<div className="text-6xl mb-4">😕</div>
|
||||
<p className="text-gray-500 font-burmese text-lg mb-4">ရလဒ်မတွေ့ပါ</p>
|
||||
<Link href="/" className="text-primary font-burmese hover:underline">
|
||||
ပင်မစာမျက်နှာသို့ ပြန်သွားရန်
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-6">
|
||||
{results.map((article: any) => (
|
||||
<Link
|
||||
key={article.id}
|
||||
href={`/article/${article.slug}`}
|
||||
className="flex gap-4 bg-white rounded-xl p-4 shadow-sm hover:shadow-md transition-all border border-gray-100 group"
|
||||
>
|
||||
{article.featured_image && (
|
||||
<div className="relative w-24 h-24 flex-shrink-0 rounded-lg overflow-hidden">
|
||||
<Image
|
||||
src={article.featured_image}
|
||||
alt={article.title_burmese}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1 min-w-0">
|
||||
<span className="inline-block px-2 py-0.5 bg-primary/10 text-primary rounded text-xs font-burmese mb-2">
|
||||
{article.category_name_burmese}
|
||||
</span>
|
||||
<h2 className="font-bold text-gray-900 font-burmese line-clamp-2 group-hover:text-primary transition-colors leading-snug mb-1">
|
||||
{article.title_burmese}
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500 font-burmese line-clamp-2 leading-relaxed">
|
||||
{article.excerpt_burmese}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export async function generateMetadata({ searchParams }: { searchParams: { q?: string } }) {
|
||||
const q = searchParams.q
|
||||
return {
|
||||
title: q ? `"${q}" - ရှာဖွေမှု - Burmddit` : 'ရှာဖွေရန် - Burmddit',
|
||||
}
|
||||
}
|
||||
@@ -1,69 +1,66 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
const categories = [
|
||||
{ href: '/category/ai-news', label: 'AI သတင်းများ' },
|
||||
{ href: '/category/tutorials', label: 'သင်ခန်းစာများ' },
|
||||
{ href: '/category/tips-tricks', label: 'အကြံပြုချက်များ' },
|
||||
{ href: '/category/upcoming', label: 'လာမည့်အရာများ' },
|
||||
]
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className="bg-gray-900 text-white mt-16">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{/* About */}
|
||||
{/* Brand */}
|
||||
<div>
|
||||
<h3 className="text-lg font-bold mb-4 font-burmese">Burmddit အကြောင်း</h3>
|
||||
<p className="text-gray-400 text-sm font-burmese">
|
||||
<Link href="/" className="flex items-center space-x-2 mb-4">
|
||||
<span className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center text-white font-bold text-lg">B</span>
|
||||
<span className="text-xl font-bold font-burmese">Burmddit</span>
|
||||
</Link>
|
||||
<p className="text-gray-400 text-sm font-burmese leading-relaxed">
|
||||
AI နှင့် နည်းပညာဆိုင်ရာ သတင်းများကို မြန်မာဘာသာဖြင့် နေ့စဉ် ထုတ်ပြန်ပေးသော ပလက်ဖောင်း
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Links */}
|
||||
{/* Categories */}
|
||||
<div>
|
||||
<h3 className="text-lg font-bold mb-4 font-burmese">အမျိုးအစားများ</h3>
|
||||
<h3 className="text-base font-bold mb-4 font-burmese">အမျိုးအစားများ</h3>
|
||||
<ul className="space-y-2 text-sm">
|
||||
<li>
|
||||
<a href="/category/ai-news" className="text-gray-400 hover:text-white font-burmese">
|
||||
AI သတင်းများ
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/category/tutorials" className="text-gray-400 hover:text-white font-burmese">
|
||||
သင်ခန်းစာများ
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/category/tips-tricks" className="text-gray-400 hover:text-white font-burmese">
|
||||
အကြံပြုချက်များ
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/category/upcoming" className="text-gray-400 hover:text-white font-burmese">
|
||||
လာမည့်အရာများ
|
||||
</a>
|
||||
{categories.map((c) => (
|
||||
<li key={c.href}>
|
||||
<Link href={c.href} className="text-gray-400 hover:text-white font-burmese transition-colors">
|
||||
{c.label}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Contact */}
|
||||
{/* Quick Links */}
|
||||
<div>
|
||||
<h3 className="text-lg font-bold mb-4">Contact</h3>
|
||||
<p className="text-gray-400 text-sm">
|
||||
<h3 className="text-base font-bold mb-4 font-burmese">အမြန်လင့်များ</h3>
|
||||
<ul className="space-y-2 text-sm">
|
||||
<li>
|
||||
<Link href="/search" className="text-gray-400 hover:text-white font-burmese transition-colors">
|
||||
🔍 ရှာဖွေရန်
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/" className="text-gray-400 hover:text-white font-burmese transition-colors">
|
||||
📰 နောက်ဆုံးရ သတင်းများ
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<p className="text-gray-500 text-xs mt-6 font-burmese leading-relaxed">
|
||||
Built with ❤️ for Myanmar tech community
|
||||
</p>
|
||||
<div className="mt-4 flex space-x-4">
|
||||
<a href="#" className="text-gray-400 hover:text-white">
|
||||
<span className="sr-only">Twitter</span>
|
||||
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" />
|
||||
</svg>
|
||||
</a>
|
||||
<a href="#" className="text-gray-400 hover:text-white">
|
||||
<span className="sr-only">GitHub</span>
|
||||
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path fillRule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 pt-8 border-t border-gray-800 text-center">
|
||||
<p className="text-gray-400 text-sm">
|
||||
© {new Date().getFullYear()} Burmddit. All rights reserved.
|
||||
<p className="text-gray-500 text-sm font-burmese">
|
||||
© {new Date().getFullYear()} Burmddit. မူပိုင်ခွင့် အာမခံသည်။
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,53 +1,104 @@
|
||||
'use client'
|
||||
import { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
|
||||
const navLinks = [
|
||||
{ href: '/', label: 'ပင်မစာမျက်နှာ' },
|
||||
{ href: '/category/ai-news', label: 'AI သတင်းများ' },
|
||||
{ href: '/category/tutorials', label: 'သင်ခန်းစာများ' },
|
||||
{ href: '/category/tips-tricks', label: 'အကြံပြုချက်များ' },
|
||||
{ href: '/category/upcoming', label: 'လာမည့်အရာများ' },
|
||||
]
|
||||
|
||||
export default function Header() {
|
||||
const [mobileOpen, setMobileOpen] = useState(false)
|
||||
const pathname = usePathname()
|
||||
|
||||
return (
|
||||
<header className="bg-white shadow-sm sticky top-0 z-50">
|
||||
<nav className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between items-center h-16">
|
||||
{/* Logo */}
|
||||
<Link href="/" className="flex items-center space-x-2">
|
||||
<span className="text-2xl font-bold text-primary-600">B</span>
|
||||
<span className="text-xl font-bold text-gray-900 font-burmese">
|
||||
Burmddit
|
||||
</span>
|
||||
<Link href="/" className="flex items-center space-x-2 flex-shrink-0">
|
||||
<span className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center text-white font-bold text-lg">B</span>
|
||||
<span className="text-xl font-bold text-gray-900 font-burmese">Burmddit</span>
|
||||
</Link>
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="hidden md:flex space-x-8">
|
||||
{/* Desktop Nav */}
|
||||
<div className="hidden md:flex items-center space-x-1">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
href="/"
|
||||
className="text-gray-700 hover:text-primary-600 font-medium font-burmese"
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className={`px-3 py-2 rounded-lg text-sm font-medium font-burmese transition-colors ${
|
||||
pathname === link.href
|
||||
? 'bg-primary/10 text-primary'
|
||||
: 'text-gray-700 hover:text-primary hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
ပင်မစာမျက်နှာ
|
||||
</Link>
|
||||
<Link
|
||||
href="/category/ai-news"
|
||||
className="text-gray-700 hover:text-primary-600 font-medium font-burmese"
|
||||
>
|
||||
AI သတင်းများ
|
||||
</Link>
|
||||
<Link
|
||||
href="/category/tutorials"
|
||||
className="text-gray-700 hover:text-primary-600 font-medium font-burmese"
|
||||
>
|
||||
သင်ခန်းစာများ
|
||||
</Link>
|
||||
<Link
|
||||
href="/category/tips-tricks"
|
||||
className="text-gray-700 hover:text-primary-600 font-medium font-burmese"
|
||||
>
|
||||
အကြံပြုချက်များ
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Search Icon */}
|
||||
<button className="p-2 text-gray-600 hover:text-primary-600">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
{/* Right: Search + Mobile Hamburger */}
|
||||
<div className="flex items-center space-x-1">
|
||||
<Link
|
||||
href="/search"
|
||||
className="p-2 text-gray-600 hover:text-primary hover:bg-gray-100 rounded-lg transition-colors"
|
||||
aria-label="ရှာဖွေရန်"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
</Link>
|
||||
|
||||
{/* Hamburger */}
|
||||
<button
|
||||
className="md:hidden p-2 text-gray-600 hover:text-primary hover:bg-gray-100 rounded-lg transition-colors"
|
||||
onClick={() => setMobileOpen(!mobileOpen)}
|
||||
aria-label="Menu"
|
||||
>
|
||||
{mobileOpen ? (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
{mobileOpen && (
|
||||
<div className="md:hidden pb-4 pt-2 border-t border-gray-100 space-y-1">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
className={`block px-4 py-3 rounded-lg text-sm font-medium font-burmese transition-colors ${
|
||||
pathname === link.href
|
||||
? 'bg-primary/10 text-primary'
|
||||
: 'text-gray-700 hover:text-primary hover:bg-gray-50'
|
||||
}`}
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
href="/search"
|
||||
onClick={() => setMobileOpen(false)}
|
||||
className="block px-4 py-3 rounded-lg text-sm font-medium font-burmese text-gray-700 hover:text-primary hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
🔍 ရှာဖွေရန်
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
|
||||
51
frontend/components/ShareButtons.tsx
Normal file
51
frontend/components/ShareButtons.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
'use client'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export default function ShareButtons({ title }: { title: string }) {
|
||||
const [url, setUrl] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
setUrl(window.location.href)
|
||||
}, [])
|
||||
|
||||
const encodedUrl = encodeURIComponent(url)
|
||||
const encodedTitle = encodeURIComponent(title)
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<a
|
||||
href={`https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-5 py-2.5 bg-blue-600 text-white rounded-full text-sm font-semibold hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
|
||||
</svg>
|
||||
Facebook
|
||||
</a>
|
||||
<a
|
||||
href={`https://twitter.com/intent/tweet?url=${encodedUrl}&text=${encodedTitle}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-5 py-2.5 bg-black text-white rounded-full text-sm font-semibold hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
||||
</svg>
|
||||
X (Twitter)
|
||||
</a>
|
||||
<a
|
||||
href={`https://wa.me/?text=${encodedTitle}%20${encodedUrl}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-5 py-2.5 bg-green-600 text-white rounded-full text-sm font-semibold hover:bg-green-700 transition-colors"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/>
|
||||
</svg>
|
||||
WhatsApp
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -9,20 +9,22 @@ const config: Config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
'burmese': ['Pyidaungsu', 'Noto Sans Myanmar', 'Myanmar Text', 'sans-serif'],
|
||||
'burmese': ['Noto Sans Myanmar', 'Myanmar Text', 'sans-serif'],
|
||||
},
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#f0f9ff',
|
||||
100: '#e0f2fe',
|
||||
200: '#bae6fd',
|
||||
300: '#7dd3fc',
|
||||
400: '#38bdf8',
|
||||
500: '#0ea5e9',
|
||||
600: '#0284c7',
|
||||
700: '#0369a1',
|
||||
800: '#075985',
|
||||
900: '#0c4a6e',
|
||||
DEFAULT: '#2563eb',
|
||||
dark: '#1e40af',
|
||||
50: '#eff6ff',
|
||||
100: '#dbeafe',
|
||||
200: '#bfdbfe',
|
||||
300: '#93c5fd',
|
||||
400: '#60a5fa',
|
||||
500: '#3b82f6',
|
||||
600: '#2563eb',
|
||||
700: '#1d4ed8',
|
||||
800: '#1e40af',
|
||||
900: '#1e3a8a',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user