Deploy UI/UX improvements - READY TO GO LIVE
✅ Modern design with better typography ✅ Hashtag/tag system with auto-tagging ✅ Full-width hero cover images ✅ Trending tags section ✅ Better article pages with share buttons ✅ Tag filtering pages (/tag/*) ✅ Build tested and passing ✅ CSS fixed and optimized ✅ @vercel/postgres added to dependencies Ready to deploy to burmddit.qikbite.asia
This commit is contained in:
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
frontend/node_modules/
|
||||||
|
backend/__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.Python
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
frontend/.next/
|
||||||
|
frontend/out/
|
||||||
|
frontend/build/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.production
|
||||||
|
*.env
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Backups
|
||||||
|
*.backup
|
||||||
|
*-backup-*
|
||||||
|
|
||||||
|
# Test coverage
|
||||||
|
coverage/
|
||||||
|
.nyc_output/
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
*.tar.gz
|
||||||
|
*.zip
|
||||||
159
deploy-ui-improvements.sh
Executable file
159
deploy-ui-improvements.sh
Executable file
@@ -0,0 +1,159 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Deploy Burmddit UI/UX Improvements
|
||||||
|
# Run this script to update your live site with the new design
|
||||||
|
|
||||||
|
set -e # Exit on error
|
||||||
|
|
||||||
|
echo "🎨 Burmddit UI/UX Deployment Script"
|
||||||
|
echo "===================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Get current directory
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
echo -e "${BLUE}Step 1: Backup existing files${NC}"
|
||||||
|
echo "--------------------------------"
|
||||||
|
cd frontend/app
|
||||||
|
|
||||||
|
# Backup old files
|
||||||
|
if [ -f "globals.css" ]; then
|
||||||
|
echo "Backing up globals.css..."
|
||||||
|
cp globals.css globals-backup-$(date +%Y%m%d-%H%M%S).css
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "page.tsx" ]; then
|
||||||
|
echo "Backing up page.tsx..."
|
||||||
|
cp page.tsx page-backup-$(date +%Y%m%d-%H%M%S).tsx
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "article/[slug]/page.tsx" ]; then
|
||||||
|
echo "Backing up article page..."
|
||||||
|
mkdir -p article/[slug]/backup
|
||||||
|
cp article/[slug]/page.tsx "article/[slug]/backup/page-backup-$(date +%Y%m%d-%H%M%S).tsx"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Backups created${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${BLUE}Step 2: Deploy new frontend files${NC}"
|
||||||
|
echo "-----------------------------------"
|
||||||
|
|
||||||
|
# Replace CSS
|
||||||
|
if [ -f "globals-improved.css" ]; then
|
||||||
|
echo "Deploying new CSS..."
|
||||||
|
mv globals-improved.css globals.css
|
||||||
|
echo -e "${GREEN}✓ CSS updated${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}⚠ globals-improved.css not found${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Replace homepage
|
||||||
|
if [ -f "page-improved.tsx" ]; then
|
||||||
|
echo "Deploying new homepage..."
|
||||||
|
mv page-improved.tsx page.tsx
|
||||||
|
echo -e "${GREEN}✓ Homepage updated${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}⚠ page-improved.tsx not found${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Replace article page
|
||||||
|
if [ -f "article/[slug]/page-improved.tsx" ]; then
|
||||||
|
echo "Deploying new article page..."
|
||||||
|
mv article/[slug]/page-improved.tsx article/[slug]/page.tsx
|
||||||
|
echo -e "${GREEN}✓ Article page updated${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}⚠ article page-improved.tsx not found${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Tag page should already be in place
|
||||||
|
if [ ! -f "tag/[slug]/page.tsx" ]; then
|
||||||
|
echo -e "${YELLOW}⚠ Tag page not found - check if it was copied${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}✓ Tag pages ready${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${BLUE}Step 3: Database Migration (Tags System)${NC}"
|
||||||
|
echo "-----------------------------------------"
|
||||||
|
|
||||||
|
# Check if DATABASE_URL is set
|
||||||
|
if [ -z "$DATABASE_URL" ]; then
|
||||||
|
echo -e "${YELLOW}DATABASE_URL not set. Please run migration manually:${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " psql \$DATABASE_URL < $SCRIPT_DIR/database/tags_migration.sql"
|
||||||
|
echo ""
|
||||||
|
echo "Or if you have connection details:"
|
||||||
|
echo " psql -h HOST -U USER -d DATABASE < $SCRIPT_DIR/database/tags_migration.sql"
|
||||||
|
echo ""
|
||||||
|
read -p "Press Enter to continue without migration, or Ctrl+C to exit..."
|
||||||
|
else
|
||||||
|
echo "Running tags migration..."
|
||||||
|
psql "$DATABASE_URL" < "$SCRIPT_DIR/database/tags_migration.sql" 2>&1 | grep -v "NOTICE" || true
|
||||||
|
echo -e "${GREEN}✓ Database migration complete${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${BLUE}Step 4: Install dependencies${NC}"
|
||||||
|
echo "------------------------------"
|
||||||
|
cd "$SCRIPT_DIR/frontend"
|
||||||
|
|
||||||
|
if [ -f "package.json" ]; then
|
||||||
|
if command -v npm &> /dev/null; then
|
||||||
|
echo "Installing/updating npm packages..."
|
||||||
|
npm install
|
||||||
|
echo -e "${GREEN}✓ Dependencies updated${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}⚠ npm not found - skip dependency update${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${BLUE}Step 5: Build frontend${NC}"
|
||||||
|
echo "-----------------------"
|
||||||
|
|
||||||
|
if command -v npm &> /dev/null; then
|
||||||
|
echo "Building production frontend..."
|
||||||
|
npm run build
|
||||||
|
echo -e "${GREEN}✓ Build complete${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}⚠ npm not found - skip build${NC}"
|
||||||
|
echo "If using Vercel/external deployment, push to Git and it will auto-build"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${GREEN}✓ DEPLOYMENT COMPLETE!${NC}"
|
||||||
|
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "🎉 New features deployed:"
|
||||||
|
echo " ✓ Modern design system"
|
||||||
|
echo " ✓ Hashtag/tag system"
|
||||||
|
echo " ✓ Cover images with overlays"
|
||||||
|
echo " ✓ Trending tags section"
|
||||||
|
echo " ✓ Better typography"
|
||||||
|
echo " ✓ Improved article pages"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}Next steps:${NC}"
|
||||||
|
echo "1. If using Vercel/Railway: Push to Git to trigger auto-deploy"
|
||||||
|
echo " cd $SCRIPT_DIR && git push origin main"
|
||||||
|
echo ""
|
||||||
|
echo "2. Test the new design at: burmddit.qikbite.asia"
|
||||||
|
echo ""
|
||||||
|
echo "3. If auto-tagging not working, update backend/publisher.py"
|
||||||
|
echo " (see UI-IMPROVEMENTS.md for code snippet)"
|
||||||
|
echo ""
|
||||||
|
echo "4. Clear browser cache if design doesn't update immediately"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}For rollback:${NC} Restore from backup files created in step 1"
|
||||||
|
echo ""
|
||||||
@@ -1,336 +0,0 @@
|
|||||||
import { sql } from '@vercel/postgres'
|
|
||||||
import { notFound } from 'next/navigation'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import Image from 'next/image'
|
|
||||||
|
|
||||||
async function getArticleWithTags(slug: string) {
|
|
||||||
try {
|
|
||||||
const { rows } = await sql`
|
|
||||||
SELECT
|
|
||||||
a.*,
|
|
||||||
c.name as category_name,
|
|
||||||
c.name_burmese as category_name_burmese,
|
|
||||||
c.slug as category_slug,
|
|
||||||
COALESCE(
|
|
||||||
array_agg(t.name_burmese) FILTER (WHERE t.id IS NOT NULL),
|
|
||||||
ARRAY[]::VARCHAR[]
|
|
||||||
) as tags_burmese,
|
|
||||||
COALESCE(
|
|
||||||
array_agg(t.slug) FILTER (WHERE t.id IS NOT NULL),
|
|
||||||
ARRAY[]::VARCHAR[]
|
|
||||||
) as tag_slugs
|
|
||||||
FROM articles a
|
|
||||||
JOIN categories c ON a.category_id = c.id
|
|
||||||
LEFT JOIN article_tags at ON a.id = at.article_id
|
|
||||||
LEFT JOIN tags t ON at.tag_id = t.id
|
|
||||||
WHERE a.slug = ${slug} AND a.status = 'published'
|
|
||||||
GROUP BY a.id, c.id
|
|
||||||
`
|
|
||||||
|
|
||||||
if (rows.length === 0) return null
|
|
||||||
|
|
||||||
// Increment view count
|
|
||||||
await sql`SELECT increment_view_count(${slug})`
|
|
||||||
|
|
||||||
return rows[0]
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching article:', error)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getRelatedArticles(articleId: number) {
|
|
||||||
try {
|
|
||||||
const { rows } = await sql`SELECT * FROM get_related_articles(${articleId}, 6)`
|
|
||||||
return rows
|
|
||||||
} catch (error) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function ImprovedArticlePage({ params }: { params: { slug: string } }) {
|
|
||||||
const article = await getArticleWithTags(params.slug)
|
|
||||||
|
|
||||||
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'
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-white">
|
|
||||||
{/* Hero Cover Image */}
|
|
||||||
{article.featured_image && (
|
|
||||||
<div className="relative h-[70vh] w-full overflow-hidden">
|
|
||||||
<Image
|
|
||||||
src={article.featured_image}
|
|
||||||
alt={article.title_burmese}
|
|
||||||
fill
|
|
||||||
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 flex items-end">
|
|
||||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 pb-16 w-full">
|
|
||||||
{/* Category */}
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
{article.category_name_burmese}
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{/* Title */}
|
|
||||||
<h1 className="text-5xl md:text-6xl font-bold text-white mb-6 font-burmese leading-tight">
|
|
||||||
{article.title_burmese}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
{/* Meta */}
|
|
||||||
<div className="flex flex-wrap items-center gap-4 text-white/90">
|
|
||||||
<span className="font-burmese">{publishedDate}</span>
|
|
||||||
<span>•</span>
|
|
||||||
<span className="font-burmese">{article.reading_time} မိနစ်</span>
|
|
||||||
<span>•</span>
|
|
||||||
<span>{article.view_count} views</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Article Content */}
|
|
||||||
<article className="max-w-4xl 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">
|
|
||||||
{article.tags_burmese.map((tag: string, idx: number) => (
|
|
||||||
<Link
|
|
||||||
key={idx}
|
|
||||||
href={`/tag/${article.tag_slugs[idx]}`}
|
|
||||||
className="tag tag-burmese"
|
|
||||||
>
|
|
||||||
#{tag}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Article Body */}
|
|
||||||
<div className="article-content">
|
|
||||||
<div dangerouslySetInnerHTML={{ __html: formatContent(article.content_burmese) }} />
|
|
||||||
|
|
||||||
{/* Additional Images 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">
|
|
||||||
{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>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Videos */}
|
|
||||||
{article.videos && article.videos.length > 0 && (
|
|
||||||
<div className="my-12">
|
|
||||||
<h3 className="text-2xl font-bold mb-6 font-burmese">ဗီဒီယိုများ</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">
|
|
||||||
{renderVideo(video)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 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">
|
|
||||||
<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>
|
|
||||||
<div className="space-y-4">
|
|
||||||
{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">
|
|
||||||
{index + 1}
|
|
||||||
</span>
|
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<a
|
|
||||||
href={source.url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-primary hover:text-primary-dark font-medium break-words hover:underline"
|
|
||||||
>
|
|
||||||
{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>
|
|
||||||
)}
|
|
||||||
</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>
|
|
||||||
</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">
|
|
||||||
ဆက်စပ်ဆောင်းပါးများ
|
|
||||||
</h2>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
|
||||||
{relatedArticles.map((related: any) => (
|
|
||||||
<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>
|
|
||||||
)}
|
|
||||||
<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">
|
|
||||||
{related.title_burmese}
|
|
||||||
</h3>
|
|
||||||
<p className="text-sm text-gray-600 font-burmese line-clamp-2">
|
|
||||||
{related.excerpt_burmese}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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>')
|
|
||||||
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
||||||
.replace(/\*(.*?)\*/g, '<em>$1</em>')
|
|
||||||
|
|
||||||
return `<p>${formatted}</p>`
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderVideo(videoUrl: string) {
|
|
||||||
let videoId = null
|
|
||||||
|
|
||||||
if (videoUrl.includes('youtube.com/watch')) {
|
|
||||||
const match = videoUrl.match(/v=([^&]+)/)
|
|
||||||
videoId = match ? match[1] : null
|
|
||||||
} else if (videoUrl.includes('youtu.be/')) {
|
|
||||||
const match = videoUrl.match(/youtu\.be\/([^?]+)/)
|
|
||||||
videoId = match ? match[1] : null
|
|
||||||
} else if (videoUrl.includes('youtube.com/embed/')) {
|
|
||||||
const match = videoUrl.match(/embed\/([^?]+)/)
|
|
||||||
videoId = match ? match[1] : null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (videoId) {
|
|
||||||
return (
|
|
||||||
<iframe
|
|
||||||
src={`https://www.youtube.com/embed/${videoId}`}
|
|
||||||
className="w-full h-full"
|
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
||||||
allowFullScreen
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<iframe
|
|
||||||
src={videoUrl}
|
|
||||||
className="w-full h-full"
|
|
||||||
allowFullScreen
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function generateMetadata({ params }: { params: { slug: string } }) {
|
|
||||||
const article = await getArticleWithTags(params.slug)
|
|
||||||
|
|
||||||
if (!article) {
|
|
||||||
return {
|
|
||||||
title: 'Article Not Found',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: `${article.title_burmese} - Burmddit`,
|
|
||||||
description: article.excerpt_burmese,
|
|
||||||
openGraph: {
|
|
||||||
title: article.title_burmese,
|
|
||||||
description: article.excerpt_burmese,
|
|
||||||
images: article.featured_image ? [article.featured_image] : [],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +1,30 @@
|
|||||||
import { sql } from '@/lib/db'
|
import { sql } from '@vercel/postgres'
|
||||||
import { notFound } from 'next/navigation'
|
import { notFound } from 'next/navigation'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
|
|
||||||
async function getArticle(slug: string) {
|
async function getArticleWithTags(slug: string) {
|
||||||
try {
|
try {
|
||||||
const { rows } = await sql`
|
const { rows } = await sql`
|
||||||
SELECT
|
SELECT
|
||||||
a.*,
|
a.*,
|
||||||
c.name as category_name,
|
c.name as category_name,
|
||||||
c.name_burmese as category_name_burmese,
|
c.name_burmese as category_name_burmese,
|
||||||
c.slug as category_slug
|
c.slug as category_slug,
|
||||||
|
COALESCE(
|
||||||
|
array_agg(t.name_burmese) FILTER (WHERE t.id IS NOT NULL),
|
||||||
|
ARRAY[]::VARCHAR[]
|
||||||
|
) as tags_burmese,
|
||||||
|
COALESCE(
|
||||||
|
array_agg(t.slug) FILTER (WHERE t.id IS NOT NULL),
|
||||||
|
ARRAY[]::VARCHAR[]
|
||||||
|
) as tag_slugs
|
||||||
FROM articles a
|
FROM articles a
|
||||||
JOIN categories c ON a.category_id = c.id
|
JOIN categories c ON a.category_id = c.id
|
||||||
|
LEFT JOIN article_tags at ON a.id = at.article_id
|
||||||
|
LEFT JOIN tags t ON at.tag_id = t.id
|
||||||
WHERE a.slug = ${slug} AND a.status = 'published'
|
WHERE a.slug = ${slug} AND a.status = 'published'
|
||||||
|
GROUP BY a.id, c.id
|
||||||
`
|
`
|
||||||
|
|
||||||
if (rows.length === 0) return null
|
if (rows.length === 0) return null
|
||||||
@@ -32,15 +41,15 @@ async function getArticle(slug: string) {
|
|||||||
|
|
||||||
async function getRelatedArticles(articleId: number) {
|
async function getRelatedArticles(articleId: number) {
|
||||||
try {
|
try {
|
||||||
const { rows } = await sql`SELECT * FROM get_related_articles(${articleId}, 5)`
|
const { rows } = await sql`SELECT * FROM get_related_articles(${articleId}, 6)`
|
||||||
return rows
|
return rows
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function ArticlePage({ params }: { params: { slug: string } }) {
|
export default async function ImprovedArticlePage({ params }: { params: { slug: string } }) {
|
||||||
const article = await getArticle(params.slug)
|
const article = await getArticleWithTags(params.slug)
|
||||||
|
|
||||||
if (!article) {
|
if (!article) {
|
||||||
notFound()
|
notFound()
|
||||||
@@ -54,185 +63,188 @@ export default async function ArticlePage({ params }: { params: { slug: string }
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div className="min-h-screen bg-white">
|
||||||
{/* Breadcrumb */}
|
{/* Hero Cover Image */}
|
||||||
<nav className="mb-6 text-sm">
|
{article.featured_image && (
|
||||||
<Link href="/" className="text-primary-600 hover:text-primary-700">
|
<div className="relative h-[70vh] w-full overflow-hidden">
|
||||||
ပင်မစာမျက်နှာ
|
<Image
|
||||||
</Link>
|
src={article.featured_image}
|
||||||
<span className="mx-2 text-gray-400">/</span>
|
alt={article.title_burmese}
|
||||||
<Link
|
fill
|
||||||
href={`/category/${article.category_slug}`}
|
className="object-cover"
|
||||||
className="text-primary-600 hover:text-primary-700 font-burmese"
|
priority
|
||||||
>
|
/>
|
||||||
{article.category_name_burmese}
|
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent" />
|
||||||
</Link>
|
|
||||||
<span className="mx-2 text-gray-400">/</span>
|
|
||||||
<span className="text-gray-600 font-burmese">{article.title_burmese}</span>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
{/* Article Header */}
|
<div className="absolute inset-0 flex items-end">
|
||||||
<article className="bg-white rounded-lg shadow-lg overflow-hidden">
|
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 pb-16 w-full">
|
||||||
{/* Category Badge */}
|
{/* Category */}
|
||||||
<div className="p-6 pb-0">
|
<Link
|
||||||
<Link
|
href={`/category/${article.category_slug}`}
|
||||||
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 px-3 py-1 bg-primary-100 text-primary-700 rounded-full text-sm font-medium font-burmese mb-4 hover:bg-primary-200"
|
>
|
||||||
>
|
{article.category_name_burmese}
|
||||||
{article.category_name_burmese}
|
</Link>
|
||||||
</Link>
|
|
||||||
|
{/* Title */}
|
||||||
|
<h1 className="text-5xl md:text-6xl font-bold text-white mb-6 font-burmese leading-tight">
|
||||||
|
{article.title_burmese}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{/* Meta */}
|
||||||
|
<div className="flex flex-wrap items-center gap-4 text-white/90">
|
||||||
|
<span className="font-burmese">{publishedDate}</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span className="font-burmese">{article.reading_time} မိနစ်</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span>{article.view_count} views</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Featured Image */}
|
{/* Article Content */}
|
||||||
{article.featured_image && (
|
<article className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||||
<div className="relative h-96 w-full">
|
{/* Tags */}
|
||||||
<Image
|
{article.tags_burmese && article.tags_burmese.length > 0 && (
|
||||||
src={article.featured_image}
|
<div className="flex flex-wrap gap-2 mb-8 pb-8 border-b">
|
||||||
alt={article.title_burmese}
|
{article.tags_burmese.map((tag: string, idx: number) => (
|
||||||
fill
|
<Link
|
||||||
className="object-cover"
|
key={idx}
|
||||||
priority
|
href={`/tag/${article.tag_slugs[idx]}`}
|
||||||
/>
|
className="tag tag-burmese"
|
||||||
|
>
|
||||||
|
#{tag}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Article Content */}
|
{/* Article Body */}
|
||||||
<div className="p-6 lg:p-12">
|
<div className="article-content">
|
||||||
{/* Title */}
|
<div dangerouslySetInnerHTML={{ __html: formatContent(article.content_burmese) }} />
|
||||||
<h1 className="text-4xl font-bold text-gray-900 mb-4 font-burmese leading-tight">
|
|
||||||
{article.title_burmese}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
{/* Meta Info */}
|
{/* Additional Images Gallery */}
|
||||||
<div className="flex items-center text-sm text-gray-600 mb-8 pb-8 border-b">
|
{article.images && article.images.length > 1 && (
|
||||||
<span className="font-burmese">{publishedDate}</span>
|
<div className="my-12">
|
||||||
<span className="mx-3">•</span>
|
<h3 className="text-2xl font-bold mb-6 font-burmese">ဓာတ်ပုံများ</h3>
|
||||||
<span className="font-burmese">{article.reading_time} မိနစ်</span>
|
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||||
<span className="mx-3">•</span>
|
{article.images.slice(1).map((img: string, idx: number) => (
|
||||||
<span className="font-burmese">{article.view_count} ကြည့်ရှုမှု</span>
|
<div key={idx} className="relative h-56 rounded-xl overflow-hidden image-zoom">
|
||||||
</div>
|
<Image
|
||||||
|
src={img}
|
||||||
{/* Article Body */}
|
alt={`${article.title_burmese} - ${idx + 2}`}
|
||||||
<div className="article-content prose prose-lg max-w-none">
|
fill
|
||||||
<div dangerouslySetInnerHTML={{ __html: formatContent(article.content_burmese) }} />
|
className="object-cover"
|
||||||
|
/>
|
||||||
{/* 🔥 Additional Images Gallery */}
|
</div>
|
||||||
{article.images && article.images.length > 1 && (
|
|
||||||
<div className="mt-8 mb-8">
|
|
||||||
<h3 className="text-xl font-bold mb-4 font-burmese">ဓာတ်ပုံများ</h3>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
|
||||||
{article.images.slice(1).map((img: string, idx: number) => (
|
|
||||||
<div key={idx} className="relative h-48 rounded-lg overflow-hidden">
|
|
||||||
<Image
|
|
||||||
src={img}
|
|
||||||
alt={`${article.title_burmese} - ဓာတ်ပုံ ${idx + 2}`}
|
|
||||||
fill
|
|
||||||
className="object-cover hover:scale-105 transition-transform duration-200"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 🔥 Videos */}
|
|
||||||
{article.videos && article.videos.length > 0 && (
|
|
||||||
<div className="mt-8 mb-8">
|
|
||||||
<h3 className="text-xl font-bold mb-4 font-burmese">ဗီဒီယိုများ</h3>
|
|
||||||
<div className="space-y-4">
|
|
||||||
{article.videos.map((video: string, idx: number) => (
|
|
||||||
<div key={idx} className="relative aspect-video rounded-lg overflow-hidden bg-gray-900">
|
|
||||||
{renderVideo(video)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* ⭐ SOURCE ATTRIBUTION - THIS IS THE KEY PART! */}
|
|
||||||
{article.source_articles && article.source_articles.length > 0 && (
|
|
||||||
<div className="mt-12 pt-8 border-t-2 border-gray-200 bg-gray-50 p-6 rounded-lg">
|
|
||||||
<h3 className="text-xl font-bold text-gray-900 mb-4 font-burmese flex items-center">
|
|
||||||
<svg className="w-6 h-6 mr-2 text-primary-600" 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-600 mb-4 font-burmese">
|
|
||||||
ဤဆောင်းပါးကို အောက်ပါမူရင်းသတင်းများမှ စုစည်း၍ မြန်မာဘာသာသို့ ပြန်ဆိုထားခြင်း ဖြစ်ပါသည်။ အားလုံးသော အကြွေးအရ မူရင်းစာရေးသူများနှင့် ထုတ်ပြန်သူများကို သက်ဆိုင်ပါသည်။
|
|
||||||
</p>
|
|
||||||
<ul className="space-y-3">
|
|
||||||
{article.source_articles.map((source: any, index: number) => (
|
|
||||||
<li key={index} className="bg-white p-4 rounded border border-gray-200 hover:border-primary-300 transition-colors">
|
|
||||||
<div className="flex items-start">
|
|
||||||
<span className="flex-shrink-0 w-6 h-6 bg-primary-100 text-primary-700 rounded-full flex items-center justify-center text-sm font-bold mr-3">
|
|
||||||
{index + 1}
|
|
||||||
</span>
|
|
||||||
<div className="flex-1">
|
|
||||||
<a
|
|
||||||
href={source.url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-primary-600 hover:text-primary-700 font-medium break-words"
|
|
||||||
>
|
|
||||||
{source.title}
|
|
||||||
</a>
|
|
||||||
{source.author && source.author !== 'Unknown' && (
|
|
||||||
<p className="text-sm text-gray-600 mt-1">
|
|
||||||
<span className="font-burmese">စာရေးသူ:</span> {source.author}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<p className="text-xs text-gray-500 mt-1 break-all">
|
|
||||||
{source.url}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href={source.url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="ml-2 text-primary-600 hover:text-primary-700"
|
|
||||||
>
|
|
||||||
<svg className="w-5 h-5" 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>
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</ul>
|
|
||||||
<div className="mt-4 p-4 bg-yellow-50 border border-yellow-200 rounded">
|
|
||||||
<p className="text-sm text-gray-700 font-burmese">
|
|
||||||
<strong>မှတ်ချက်:</strong> ဤဆောင်းပါးသည် သတင်းမျာ လုံးစုစည်းကာ ပြန်ဆိုထားခြင်း ဖြစ်ပါသည်။ အသေးစိတ် အချက်အလက်များနှင့် မူရင်းအကြောင်းအရာများအတွက် အထက်ပါမူရင်းသတင်းရင်းမြစ်များကို ကြည့်ရှုပါ။
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Disclaimer */}
|
{/* Videos */}
|
||||||
<div className="mt-6 p-4 bg-gray-100 rounded text-sm text-gray-600 font-burmese">
|
{article.videos && article.videos.length > 0 && (
|
||||||
<p>
|
<div className="my-12">
|
||||||
<strong>ပြန်ဆိုသူမှတ်ချက်:</strong> ဤဆောင်းပါးကို AI နည်းပညာဖြင့် အင်္ဂလိပ်ဘာသာမှ မြန်မာဘာသာသို့ ပြန်ဆိုထားပါသည်။ နည်းပညာဝေါဟာရများကို တတ်နိုင်သမျှ အင်္ဂလိပ်ဘာသာဖြင့် ထားရှိထားပါသည်။
|
<h3 className="text-2xl font-bold mb-6 font-burmese">ဗီဒီယိုများ</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">
|
||||||
|
{renderVideo(video)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 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">
|
||||||
|
<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>
|
</p>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{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">
|
||||||
|
{index + 1}
|
||||||
|
</span>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<a
|
||||||
|
href={source.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-primary hover:text-primary-dark font-medium break-words hover:underline"
|
||||||
|
>
|
||||||
|
{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>
|
||||||
|
)}
|
||||||
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
{/* Related Articles */}
|
{/* Related Articles */}
|
||||||
{relatedArticles.length > 0 && (
|
{relatedArticles.length > 0 && (
|
||||||
<div className="mt-12">
|
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 bg-gray-50">
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-6 font-burmese">
|
<h2 className="text-3xl font-bold text-gray-900 mb-10 font-burmese">
|
||||||
ဆက်စပ်ဆောင်းပါးများ
|
ဆက်စပ်ဆောင်းပါးများ
|
||||||
</h2>
|
</h2>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||||
{relatedArticles.map((related: any) => (
|
{relatedArticles.map((related: any) => (
|
||||||
<Link
|
<Link
|
||||||
key={related.id}
|
key={related.id}
|
||||||
href={`/article/${related.slug}`}
|
href={`/article/${related.slug}`}
|
||||||
className="bg-white rounded-lg shadow hover:shadow-lg transition-shadow p-4"
|
className="card card-hover"
|
||||||
>
|
>
|
||||||
{related.featured_image && (
|
{related.featured_image && (
|
||||||
<div className="relative h-32 w-full mb-3 rounded overflow-hidden">
|
<div className="relative h-48 w-full image-zoom">
|
||||||
<Image
|
<Image
|
||||||
src={related.featured_image}
|
src={related.featured_image}
|
||||||
alt={related.title_burmese}
|
alt={related.title_burmese}
|
||||||
@@ -241,24 +253,24 @@ export default async function ArticlePage({ params }: { params: { slug: string }
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<h3 className="font-semibold text-gray-900 font-burmese line-clamp-2 hover:text-primary-600">
|
<div className="p-6">
|
||||||
{related.title_burmese}
|
<h3 className="font-bold text-gray-900 font-burmese line-clamp-2 hover:text-primary transition-colors text-lg mb-3">
|
||||||
</h3>
|
{related.title_burmese}
|
||||||
<p className="text-sm text-gray-600 font-burmese mt-2 line-clamp-2">
|
</h3>
|
||||||
{related.excerpt_burmese}
|
<p className="text-sm text-gray-600 font-burmese line-clamp-2">
|
||||||
</p>
|
{related.excerpt_burmese}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatContent(content: string): string {
|
function formatContent(content: string): string {
|
||||||
// Convert markdown-like formatting to HTML
|
|
||||||
// This is a simple implementation - you might want to use a proper markdown parser
|
|
||||||
let formatted = content
|
let formatted = content
|
||||||
.replace(/\n\n/g, '</p><p>')
|
.replace(/\n\n/g, '</p><p>')
|
||||||
.replace(/## (.*?)\n/g, '<h2>$1</h2>')
|
.replace(/## (.*?)\n/g, '<h2>$1</h2>')
|
||||||
@@ -270,10 +282,8 @@ function formatContent(content: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderVideo(videoUrl: string) {
|
function renderVideo(videoUrl: string) {
|
||||||
// Extract YouTube video ID
|
|
||||||
let videoId = null
|
let videoId = null
|
||||||
|
|
||||||
// Handle different YouTube URL formats
|
|
||||||
if (videoUrl.includes('youtube.com/watch')) {
|
if (videoUrl.includes('youtube.com/watch')) {
|
||||||
const match = videoUrl.match(/v=([^&]+)/)
|
const match = videoUrl.match(/v=([^&]+)/)
|
||||||
videoId = match ? match[1] : null
|
videoId = match ? match[1] : null
|
||||||
@@ -296,7 +306,6 @@ function renderVideo(videoUrl: string) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For other video formats, try generic iframe embed
|
|
||||||
return (
|
return (
|
||||||
<iframe
|
<iframe
|
||||||
src={videoUrl}
|
src={videoUrl}
|
||||||
@@ -307,7 +316,7 @@ function renderVideo(videoUrl: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function generateMetadata({ params }: { params: { slug: string } }) {
|
export async function generateMetadata({ params }: { params: { slug: string } }) {
|
||||||
const article = await getArticle(params.slug)
|
const article = await getArticleWithTags(params.slug)
|
||||||
|
|
||||||
if (!article) {
|
if (!article) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,267 +0,0 @@
|
|||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
/* Modern Design System for Burmddit */
|
|
||||||
|
|
||||||
@layer base {
|
|
||||||
:root {
|
|
||||||
--primary: #2563eb;
|
|
||||||
--primary-dark: #1e40af;
|
|
||||||
--accent: #f59e0b;
|
|
||||||
--text: #1f2937;
|
|
||||||
--text-light: #6b7280;
|
|
||||||
--bg: #ffffff;
|
|
||||||
--bg-secondary: #f9fafb;
|
|
||||||
--border: #e5e7eb;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
@apply border-border;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
@apply bg-bg-secondary text-text antialiased;
|
|
||||||
font-feature-settings: "cv11", "ss01";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.font-burmese {
|
|
||||||
font-family: 'Pyidaungsu', 'Noto Sans Myanmar', 'Myanmar Text', sans-serif;
|
|
||||||
letter-spacing: 0.01em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Modern Card Design */
|
|
||||||
.card {
|
|
||||||
@apply bg-white rounded-xl shadow-sm hover:shadow-xl transition-all duration-300 overflow-hidden;
|
|
||||||
@apply border border-gray-100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-hover {
|
|
||||||
@apply transform hover:-translate-y-1 hover:scale-[1.02];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cover Image with Overlay */
|
|
||||||
.cover-image-container {
|
|
||||||
@apply relative overflow-hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cover-image-overlay {
|
|
||||||
@apply absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cover-image-text {
|
|
||||||
@apply absolute bottom-0 left-0 right-0 p-6 text-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tag/Hashtag Design */
|
|
||||||
.tag {
|
|
||||||
@apply inline-flex items-center px-3 py-1 rounded-full text-xs font-medium;
|
|
||||||
@apply bg-primary/10 text-primary hover:bg-primary hover:text-white;
|
|
||||||
@apply transition-all duration-200 cursor-pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-burmese {
|
|
||||||
@apply font-burmese text-sm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Article Content - Better Typography */
|
|
||||||
.article-content {
|
|
||||||
@apply font-burmese text-gray-800 leading-loose;
|
|
||||||
font-size: 1.125rem;
|
|
||||||
line-height: 1.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content h1 {
|
|
||||||
@apply text-4xl font-bold mt-10 mb-6 text-gray-900;
|
|
||||||
@apply font-burmese leading-tight;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content h2 {
|
|
||||||
@apply text-3xl font-bold mt-8 mb-5 text-gray-900;
|
|
||||||
@apply font-burmese leading-snug;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content h3 {
|
|
||||||
@apply text-2xl font-semibold mt-6 mb-4 text-gray-800;
|
|
||||||
@apply font-burmese;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content p {
|
|
||||||
@apply mb-6 text-lg leading-loose text-gray-700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content a {
|
|
||||||
@apply text-primary hover:text-primary-dark underline decoration-2 underline-offset-2;
|
|
||||||
@apply transition-colors duration-200;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content ul, .article-content ol {
|
|
||||||
@apply ml-6 mb-6 space-y-3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content li {
|
|
||||||
@apply text-lg text-gray-700 leading-relaxed;
|
|
||||||
@apply pl-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content ul li {
|
|
||||||
@apply list-disc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content ol li {
|
|
||||||
@apply list-decimal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content code {
|
|
||||||
@apply bg-gray-100 px-2 py-1 rounded text-sm font-mono text-gray-800;
|
|
||||||
@apply border border-gray-200;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content pre {
|
|
||||||
@apply bg-gray-900 text-gray-100 p-5 rounded-xl overflow-x-auto mb-6;
|
|
||||||
@apply shadow-lg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content blockquote {
|
|
||||||
@apply border-l-4 border-primary pl-6 italic my-6 text-gray-700;
|
|
||||||
@apply bg-primary/5 py-4 rounded-r-lg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hero Section */
|
|
||||||
.hero-gradient {
|
|
||||||
background: linear-gradient(135deg, #2563eb 0%, #7c3aed 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Glassmorphism Effect */
|
|
||||||
.glass {
|
|
||||||
@apply bg-white/80 backdrop-blur-lg border border-white/20;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Image Zoom on Hover */
|
|
||||||
.image-zoom {
|
|
||||||
@apply overflow-hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-zoom img {
|
|
||||||
@apply transition-transform duration-500 ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-zoom:hover img {
|
|
||||||
@apply scale-110;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Loading Skeleton */
|
|
||||||
.skeleton {
|
|
||||||
@apply animate-pulse bg-gradient-to-r from-gray-200 via-gray-300 to-gray-200;
|
|
||||||
@apply bg-[length:200%_100%];
|
|
||||||
animation: shimmer 1.5s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes shimmer {
|
|
||||||
0% { background-position: -200% 0; }
|
|
||||||
100% { background-position: 200% 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Smooth Page Transitions */
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from { opacity: 0; transform: translateY(10px); }
|
|
||||||
to { opacity: 1; transform: translateY(0); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-in {
|
|
||||||
animation: fadeIn 0.4s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Badge Design */
|
|
||||||
.badge {
|
|
||||||
@apply inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold;
|
|
||||||
@apply shadow-sm;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-primary {
|
|
||||||
@apply bg-primary text-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-accent {
|
|
||||||
@apply bg-accent text-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hover Effects */
|
|
||||||
.hover-lift {
|
|
||||||
@apply transition-all duration-300;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hover-lift:hover {
|
|
||||||
@apply transform -translate-y-2 shadow-2xl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Focus Styles */
|
|
||||||
*:focus-visible {
|
|
||||||
@apply outline-none ring-2 ring-primary ring-offset-2 rounded;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scrollbar Styling */
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
@apply bg-gray-100;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
@apply bg-gray-400 rounded-full;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
|
||||||
@apply bg-gray-500;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile Optimizations */
|
|
||||||
@media (max-width: 640px) {
|
|
||||||
.article-content {
|
|
||||||
font-size: 1rem;
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content h1 {
|
|
||||||
@apply text-3xl;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content h2 {
|
|
||||||
@apply text-2xl;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content h3 {
|
|
||||||
@apply text-xl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print Styles */
|
|
||||||
@media print {
|
|
||||||
.no-print {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content {
|
|
||||||
@apply text-black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,18 +2,28 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
|
/* Modern Design System for Burmddit */
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--primary: #2563eb;
|
||||||
|
--primary-dark: #1e40af;
|
||||||
|
--accent: #f59e0b;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-gray-50 text-gray-900;
|
@apply antialiased bg-gray-50 text-gray-900;
|
||||||
|
font-feature-settings: "cv11", "ss01";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Burmese font support */
|
/* Burmese Fonts - Better rendering */
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Pyidaungsu';
|
font-family: 'Pyidaungsu';
|
||||||
src: url('https://myanmar-tools-website.appspot.com/fonts/Pyidaungsu-2.5.3_Regular.ttf') format('truetype');
|
src: url('https://myanmar-tools-website.appspot.com/fonts/Pyidaungsu-2.5.3_Regular.ttf') format('truetype');
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
|
font-feature-settings: "liga" 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@@ -23,57 +33,193 @@
|
|||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Article content styling */
|
.font-burmese {
|
||||||
|
font-family: 'Pyidaungsu', 'Noto Sans Myanmar', 'Myanmar Text', sans-serif;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modern Card Design */
|
||||||
|
.card {
|
||||||
|
@apply bg-white rounded-xl shadow-sm hover:shadow-xl transition-all duration-300 overflow-hidden border border-gray-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-hover {
|
||||||
|
@apply transform hover:-translate-y-1 hover:scale-[1.02];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tag/Hashtag Design */
|
||||||
|
.tag {
|
||||||
|
@apply inline-flex items-center px-3 py-1 rounded-full text-xs font-medium;
|
||||||
|
@apply bg-blue-50 text-blue-600 hover:bg-blue-600 hover:text-white;
|
||||||
|
@apply transition-all duration-200 cursor-pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-burmese {
|
||||||
|
@apply font-burmese text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Article Content - Better Typography */
|
||||||
.article-content {
|
.article-content {
|
||||||
@apply font-burmese text-gray-800 leading-relaxed;
|
@apply font-burmese text-gray-800 leading-loose;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
line-height: 1.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content h1 {
|
.article-content h1 {
|
||||||
@apply text-3xl font-bold mt-8 mb-4;
|
@apply text-4xl font-bold mt-10 mb-6 text-gray-900 font-burmese leading-tight;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content h2 {
|
.article-content h2 {
|
||||||
@apply text-2xl font-bold mt-6 mb-3;
|
@apply text-3xl font-bold mt-8 mb-5 text-gray-900 font-burmese leading-snug;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content h3 {
|
.article-content h3 {
|
||||||
@apply text-xl font-semibold mt-4 mb-2;
|
@apply text-2xl font-semibold mt-6 mb-4 text-gray-800 font-burmese;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content p {
|
.article-content p {
|
||||||
@apply mb-4 text-lg leading-loose;
|
@apply mb-6 text-lg leading-loose text-gray-700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content a {
|
.article-content a {
|
||||||
@apply text-primary-600 hover:text-primary-700 underline;
|
@apply text-blue-600 hover:text-blue-800 underline decoration-2 underline-offset-2 transition-colors duration-200;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content ul, .article-content ol {
|
.article-content ul,
|
||||||
@apply ml-6 mb-4 space-y-2;
|
.article-content ol {
|
||||||
|
@apply ml-6 mb-6 space-y-3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content li {
|
.article-content li {
|
||||||
@apply text-lg;
|
@apply text-lg text-gray-700 leading-relaxed pl-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content ul li {
|
||||||
|
@apply list-disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content ol li {
|
||||||
|
@apply list-decimal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content code {
|
.article-content code {
|
||||||
@apply bg-gray-100 px-2 py-1 rounded text-sm font-mono;
|
@apply bg-gray-100 px-2 py-1 rounded text-sm font-mono text-gray-800 border border-gray-200;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content pre {
|
.article-content pre {
|
||||||
@apply bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto mb-4;
|
@apply bg-gray-900 text-gray-100 p-5 rounded-xl overflow-x-auto mb-6 shadow-lg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content blockquote {
|
.article-content blockquote {
|
||||||
@apply border-l-4 border-primary-500 pl-4 italic my-4;
|
@apply border-l-4 border-blue-600 pl-6 italic my-6 text-gray-700 bg-blue-50 py-4 rounded-r-lg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Card hover effects */
|
/* Image Zoom on Hover */
|
||||||
.article-card {
|
.image-zoom {
|
||||||
@apply transition-transform duration-200 hover:scale-105 hover:shadow-xl;
|
@apply overflow-hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loading skeleton */
|
.image-zoom img {
|
||||||
|
@apply transition-transform duration-500 ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-zoom:hover img {
|
||||||
|
@apply scale-110;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading Skeleton */
|
||||||
.skeleton {
|
.skeleton {
|
||||||
@apply animate-pulse bg-gray-200 rounded;
|
@apply animate-pulse bg-gradient-to-r from-gray-200 via-gray-300 to-gray-200;
|
||||||
|
animation: shimmer 1.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shimmer {
|
||||||
|
0% { background-position: -200% 0; }
|
||||||
|
100% { background-position: 200% 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smooth Page Transitions */
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(10px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-in {
|
||||||
|
animation: fadeIn 0.4s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Badge Design */
|
||||||
|
.badge {
|
||||||
|
@apply inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold shadow-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-primary {
|
||||||
|
@apply bg-blue-600 text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-accent {
|
||||||
|
@apply bg-orange-500 text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover Effects */
|
||||||
|
.hover-lift {
|
||||||
|
@apply transition-all duration-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover-lift:hover {
|
||||||
|
@apply transform -translate-y-2 shadow-2xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Focus Styles */
|
||||||
|
*:focus-visible {
|
||||||
|
@apply outline-none ring-2 ring-blue-600 ring-offset-2 rounded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar Styling */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
@apply bg-gray-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
@apply bg-gray-400 rounded-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
@apply bg-gray-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Optimizations */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.article-content {
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content h1 {
|
||||||
|
@apply text-3xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content h2 {
|
||||||
|
@apply text-2xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content h3 {
|
||||||
|
@apply text-xl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print Styles */
|
||||||
|
@media print {
|
||||||
|
.no-print {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content {
|
||||||
|
@apply text-black;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,235 +0,0 @@
|
|||||||
import { sql } from '@vercel/postgres'
|
|
||||||
import Image from 'next/image'
|
|
||||||
import Link from 'next/link'
|
|
||||||
|
|
||||||
async function getArticlesWithTags() {
|
|
||||||
try {
|
|
||||||
const { rows } = await sql`
|
|
||||||
SELECT * FROM articles_with_tags
|
|
||||||
ORDER BY published_at DESC
|
|
||||||
LIMIT 20
|
|
||||||
`
|
|
||||||
return rows
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching articles:', error)
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getFeaturedArticle() {
|
|
||||||
try {
|
|
||||||
const { rows } = await sql`
|
|
||||||
SELECT * FROM articles_with_tags
|
|
||||||
ORDER BY view_count DESC
|
|
||||||
LIMIT 1
|
|
||||||
`
|
|
||||||
return rows[0] || null
|
|
||||||
} catch (error) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getTrendingTags() {
|
|
||||||
try {
|
|
||||||
const { rows } = await sql`
|
|
||||||
SELECT t.name_burmese, t.slug, COUNT(at.article_id) as count
|
|
||||||
FROM tags t
|
|
||||||
JOIN article_tags at ON t.id = at.tag_id
|
|
||||||
JOIN articles a ON at.article_id = a.id
|
|
||||||
WHERE a.status = 'published'
|
|
||||||
GROUP BY t.id
|
|
||||||
ORDER BY count DESC
|
|
||||||
LIMIT 15
|
|
||||||
`
|
|
||||||
return rows
|
|
||||||
} catch (error) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function ImprovedHome() {
|
|
||||||
const [articles, featured, trendingTags] = await Promise.all([
|
|
||||||
getArticlesWithTags(),
|
|
||||||
getFeaturedArticle(),
|
|
||||||
getTrendingTags()
|
|
||||||
])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gradient-to-b from-gray-50 to-white">
|
|
||||||
{/* Hero Section with Featured Article */}
|
|
||||||
{featured && (
|
|
||||||
<section className="relative h-[600px] w-full overflow-hidden fade-in">
|
|
||||||
<Image
|
|
||||||
src={featured.featured_image || '/placeholder.jpg'}
|
|
||||||
alt={featured.title_burmese}
|
|
||||||
fill
|
|
||||||
className="object-cover"
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/60 to-transparent" />
|
|
||||||
|
|
||||||
<div className="absolute inset-0 flex items-end">
|
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-16 w-full">
|
|
||||||
<div className="max-w-3xl">
|
|
||||||
{/* Category Badge */}
|
|
||||||
<Link
|
|
||||||
href={`/category/${featured.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"
|
|
||||||
>
|
|
||||||
{featured.category_name_burmese}
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{/* Title */}
|
|
||||||
<h1 className="text-5xl md:text-6xl font-bold text-white mb-4 font-burmese leading-tight">
|
|
||||||
<Link href={`/article/${featured.slug}`} className="hover:text-gray-200 transition-colors">
|
|
||||||
{featured.title_burmese}
|
|
||||||
</Link>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
{/* Excerpt */}
|
|
||||||
<p className="text-xl text-gray-200 mb-6 font-burmese line-clamp-2">
|
|
||||||
{featured.excerpt_burmese}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{/* Tags */}
|
|
||||||
{featured.tags_burmese && featured.tags_burmese.length > 0 && (
|
|
||||||
<div className="flex flex-wrap gap-2 mb-6">
|
|
||||||
{featured.tags_burmese.slice(0, 5).map((tag: string, idx: number) => (
|
|
||||||
<Link
|
|
||||||
key={idx}
|
|
||||||
href={`/tag/${featured.tag_slugs[idx]}`}
|
|
||||||
className="px-3 py-1 bg-white/20 backdrop-blur-sm text-white rounded-full text-sm hover:bg-white/30 transition-colors"
|
|
||||||
>
|
|
||||||
#{tag}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Read More Button */}
|
|
||||||
<Link
|
|
||||||
href={`/article/${featured.slug}`}
|
|
||||||
className="inline-flex items-center px-8 py-4 bg-white text-gray-900 rounded-full font-semibold hover:bg-gray-100 transition-all hover:shadow-xl font-burmese"
|
|
||||||
>
|
|
||||||
ဖတ်ရန် →
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Main Content */}
|
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
||||||
{/* Trending Tags */}
|
|
||||||
{trendingTags.length > 0 && (
|
|
||||||
<section className="mb-12 fade-in">
|
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-6 font-burmese flex items-center">
|
|
||||||
🔥 လူကြိုက်များသော အကြောင်းအရာများ
|
|
||||||
</h2>
|
|
||||||
<div className="flex flex-wrap gap-3">
|
|
||||||
{trendingTags.map((tag: any) => (
|
|
||||||
<Link
|
|
||||||
key={tag.slug}
|
|
||||||
href={`/tag/${tag.slug}`}
|
|
||||||
className="tag tag-burmese"
|
|
||||||
>
|
|
||||||
#{tag.name_burmese}
|
|
||||||
<span className="ml-2 text-xs opacity-60">({tag.count})</span>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Article Grid */}
|
|
||||||
<section className="fade-in">
|
|
||||||
<h2 className="text-3xl font-bold text-gray-900 mb-8 font-burmese">
|
|
||||||
နောက်ဆုံးရ သတင်းများ
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
{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 fade-in">
|
|
||||||
{/* Cover Image */}
|
|
||||||
{article.featured_image && (
|
|
||||||
<Link href={`/article/${article.slug}`} className="block image-zoom">
|
|
||||||
<div className="relative h-56 w-full">
|
|
||||||
<Image
|
|
||||||
src={article.featured_image}
|
|
||||||
alt={article.title_burmese}
|
|
||||||
fill
|
|
||||||
className="object-cover"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="p-6">
|
|
||||||
{/* Category Badge */}
|
|
||||||
<Link
|
|
||||||
href={`/category/${article.category_slug}`}
|
|
||||||
className="inline-block mb-3 px-3 py-1 bg-primary/10 text-primary rounded-full text-xs font-semibold hover:bg-primary hover:text-white transition-all"
|
|
||||||
>
|
|
||||||
{article.category_name_burmese}
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{/* Title */}
|
|
||||||
<h3 className="text-xl font-bold text-gray-900 mb-3 font-burmese line-clamp-2 hover:text-primary transition-colors">
|
|
||||||
<Link href={`/article/${article.slug}`}>
|
|
||||||
{article.title_burmese}
|
|
||||||
</Link>
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
{/* Excerpt */}
|
|
||||||
<p className="text-gray-600 mb-4 font-burmese line-clamp-3 text-sm leading-relaxed">
|
|
||||||
{article.excerpt_burmese}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{/* Tags */}
|
|
||||||
{article.tags_burmese && article.tags_burmese.length > 0 && (
|
|
||||||
<div className="flex flex-wrap gap-2 mb-4">
|
|
||||||
{article.tags_burmese.slice(0, 3).map((tag: string, idx: number) => (
|
|
||||||
<Link
|
|
||||||
key={idx}
|
|
||||||
href={`/tag/${article.tag_slugs[idx]}`}
|
|
||||||
className="text-xs px-2 py-1 bg-gray-100 text-gray-700 rounded hover:bg-gray-200 transition-colors"
|
|
||||||
>
|
|
||||||
#{tag}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Meta */}
|
|
||||||
<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>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Load More Button */}
|
|
||||||
{articles.length >= 20 && (
|
|
||||||
<div className="text-center mt-12">
|
|
||||||
<button className="px-8 py-4 bg-primary text-white rounded-full font-semibold hover:bg-primary-dark transition-all hover:shadow-xl font-burmese">
|
|
||||||
နောက်ထပ် ဖတ်ရန် →
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
import { sql } from '@/lib/db'
|
import { sql } from '@vercel/postgres'
|
||||||
import ArticleCard from '@/components/ArticleCard'
|
import Image from 'next/image'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic'
|
async function getArticlesWithTags() {
|
||||||
import TrendingSection from '@/components/TrendingSection'
|
|
||||||
import CategoryNav from '@/components/CategoryNav'
|
|
||||||
|
|
||||||
async function getRecentArticles() {
|
|
||||||
try {
|
try {
|
||||||
const { rows } = await sql`
|
const { rows } = await sql`
|
||||||
SELECT * FROM published_articles
|
SELECT * FROM articles_with_tags
|
||||||
ORDER BY published_at DESC
|
ORDER BY published_at DESC
|
||||||
LIMIT 20
|
LIMIT 20
|
||||||
`
|
`
|
||||||
@@ -19,107 +16,219 @@ async function getRecentArticles() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getTrendingArticles() {
|
async function getFeaturedArticle() {
|
||||||
try {
|
try {
|
||||||
const { rows } = await sql`SELECT * FROM get_trending_articles(10)`
|
const { rows } = await sql`
|
||||||
|
SELECT * FROM articles_with_tags
|
||||||
|
ORDER BY view_count DESC
|
||||||
|
LIMIT 1
|
||||||
|
`
|
||||||
|
return rows[0] || null
|
||||||
|
} catch (error) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getTrendingTags() {
|
||||||
|
try {
|
||||||
|
const { rows } = await sql`
|
||||||
|
SELECT t.name_burmese, t.slug, COUNT(at.article_id) as count
|
||||||
|
FROM tags t
|
||||||
|
JOIN article_tags at ON t.id = at.tag_id
|
||||||
|
JOIN articles a ON at.article_id = a.id
|
||||||
|
WHERE a.status = 'published'
|
||||||
|
GROUP BY t.id
|
||||||
|
ORDER BY count DESC
|
||||||
|
LIMIT 15
|
||||||
|
`
|
||||||
return rows
|
return rows
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching trending:', error)
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Home() {
|
export default async function ImprovedHome() {
|
||||||
const [articles, trending] = await Promise.all([
|
const [articles, featured, trendingTags] = await Promise.all([
|
||||||
getRecentArticles(),
|
getArticlesWithTags(),
|
||||||
getTrendingArticles()
|
getFeaturedArticle(),
|
||||||
|
getTrendingTags()
|
||||||
])
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div className="min-h-screen bg-gradient-to-b from-gray-50 to-white">
|
||||||
{/* Hero Section */}
|
{/* Hero Section with Featured Article */}
|
||||||
<section className="mb-12 text-center">
|
{featured && (
|
||||||
<h1 className="text-5xl font-bold text-gray-900 mb-4 font-burmese">
|
<section className="relative h-[600px] w-full overflow-hidden fade-in">
|
||||||
Burmddit
|
<Image
|
||||||
</h1>
|
src={featured.featured_image || '/placeholder.jpg'}
|
||||||
<p className="text-xl text-gray-600 font-burmese">
|
alt={featured.title_burmese}
|
||||||
AI သတင်းများ၊ သင်ခန်းစာများနှင့် အကြံပြုချက်များ
|
fill
|
||||||
</p>
|
className="object-cover"
|
||||||
<p className="text-lg text-gray-500 mt-2">
|
priority
|
||||||
Daily AI News, Tutorials & Tips in Burmese
|
/>
|
||||||
</p>
|
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/60 to-transparent" />
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Category Navigation */}
|
<div className="absolute inset-0 flex items-end">
|
||||||
<CategoryNav />
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-16 w-full">
|
||||||
|
<div className="max-w-3xl">
|
||||||
|
{/* Category Badge */}
|
||||||
|
<Link
|
||||||
|
href={`/category/${featured.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"
|
||||||
|
>
|
||||||
|
{featured.category_name_burmese}
|
||||||
|
</Link>
|
||||||
|
|
||||||
{/* Main Content Grid */}
|
{/* Title */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 mt-8">
|
<h1 className="text-5xl md:text-6xl font-bold text-white mb-4 font-burmese leading-tight">
|
||||||
{/* Main Articles (Left 2/3) */}
|
<Link href={`/article/${featured.slug}`} className="hover:text-gray-200 transition-colors">
|
||||||
<div className="lg:col-span-2">
|
{featured.title_burmese}
|
||||||
<h2 className="text-2xl font-bold text-gray-900 mb-6 font-burmese">
|
</Link>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{/* Excerpt */}
|
||||||
|
<p className="text-xl text-gray-200 mb-6 font-burmese line-clamp-2">
|
||||||
|
{featured.excerpt_burmese}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Tags */}
|
||||||
|
{featured.tags_burmese && featured.tags_burmese.length > 0 && (
|
||||||
|
<div className="flex flex-wrap gap-2 mb-6">
|
||||||
|
{featured.tags_burmese.slice(0, 5).map((tag: string, idx: number) => (
|
||||||
|
<Link
|
||||||
|
key={idx}
|
||||||
|
href={`/tag/${featured.tag_slugs[idx]}`}
|
||||||
|
className="px-3 py-1 bg-white/20 backdrop-blur-sm text-white rounded-full text-sm hover:bg-white/30 transition-colors"
|
||||||
|
>
|
||||||
|
#{tag}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Read More Button */}
|
||||||
|
<Link
|
||||||
|
href={`/article/${featured.slug}`}
|
||||||
|
className="inline-flex items-center px-8 py-4 bg-white text-gray-900 rounded-full font-semibold hover:bg-gray-100 transition-all hover:shadow-xl font-burmese"
|
||||||
|
>
|
||||||
|
ဖတ်ရန် →
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Main Content */}
|
||||||
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||||
|
{/* Trending Tags */}
|
||||||
|
{trendingTags.length > 0 && (
|
||||||
|
<section className="mb-12 fade-in">
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-6 font-burmese flex items-center">
|
||||||
|
🔥 လူကြိုက်များသော အကြောင်းအရာများ
|
||||||
|
</h2>
|
||||||
|
<div className="flex flex-wrap gap-3">
|
||||||
|
{trendingTags.map((tag: any) => (
|
||||||
|
<Link
|
||||||
|
key={tag.slug}
|
||||||
|
href={`/tag/${tag.slug}`}
|
||||||
|
className="tag tag-burmese"
|
||||||
|
>
|
||||||
|
#{tag.name_burmese}
|
||||||
|
<span className="ml-2 text-xs opacity-60">({tag.count})</span>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Article Grid */}
|
||||||
|
<section className="fade-in">
|
||||||
|
<h2 className="text-3xl font-bold text-gray-900 mb-8 font-burmese">
|
||||||
နောက်ဆုံးရ သတင်းများ
|
နောက်ဆုံးရ သတင်းများ
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{articles.length === 0 ? (
|
{articles.length === 0 ? (
|
||||||
<div className="text-center py-12 bg-white rounded-lg shadow">
|
<div className="text-center py-20 bg-white rounded-2xl shadow-sm">
|
||||||
<p className="text-gray-500 font-burmese">
|
<div className="text-6xl mb-4">📰</div>
|
||||||
သတင်းမရှိသေးပါ။ မကြာမီ ထပ်စစ်ကြည့်ပါ။
|
<p className="text-xl text-gray-500 font-burmese">
|
||||||
|
သတင်းမရှိသေးပါ။ မကြာမီ ပြန်စစ်ကြည့်ပါ။
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||||
{articles.map((article) => (
|
{articles.map((article: any) => (
|
||||||
<ArticleCard key={article.id} article={article} />
|
<article key={article.id} className="card card-hover fade-in">
|
||||||
|
{/* Cover Image */}
|
||||||
|
{article.featured_image && (
|
||||||
|
<Link href={`/article/${article.slug}`} className="block image-zoom">
|
||||||
|
<div className="relative h-56 w-full">
|
||||||
|
<Image
|
||||||
|
src={article.featured_image}
|
||||||
|
alt={article.title_burmese}
|
||||||
|
fill
|
||||||
|
className="object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="p-6">
|
||||||
|
{/* Category Badge */}
|
||||||
|
<Link
|
||||||
|
href={`/category/${article.category_slug}`}
|
||||||
|
className="inline-block mb-3 px-3 py-1 bg-primary/10 text-primary rounded-full text-xs font-semibold hover:bg-primary hover:text-white transition-all"
|
||||||
|
>
|
||||||
|
{article.category_name_burmese}
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{/* Title */}
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-3 font-burmese line-clamp-2 hover:text-primary transition-colors">
|
||||||
|
<Link href={`/article/${article.slug}`}>
|
||||||
|
{article.title_burmese}
|
||||||
|
</Link>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
{/* Excerpt */}
|
||||||
|
<p className="text-gray-600 mb-4 font-burmese line-clamp-3 text-sm leading-relaxed">
|
||||||
|
{article.excerpt_burmese}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Tags */}
|
||||||
|
{article.tags_burmese && article.tags_burmese.length > 0 && (
|
||||||
|
<div className="flex flex-wrap gap-2 mb-4">
|
||||||
|
{article.tags_burmese.slice(0, 3).map((tag: string, idx: number) => (
|
||||||
|
<Link
|
||||||
|
key={idx}
|
||||||
|
href={`/tag/${article.tag_slugs[idx]}`}
|
||||||
|
className="text-xs px-2 py-1 bg-gray-100 text-gray-700 rounded hover:bg-gray-200 transition-colors"
|
||||||
|
>
|
||||||
|
#{tag}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Meta */}
|
||||||
|
<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>
|
</section>
|
||||||
|
|
||||||
{/* Sidebar (Right 1/3) */}
|
{/* Load More Button */}
|
||||||
<aside className="space-y-8">
|
{articles.length >= 20 && (
|
||||||
{/* Trending Articles */}
|
<div className="text-center mt-12">
|
||||||
<TrendingSection articles={trending} />
|
<button className="px-8 py-4 bg-primary text-white rounded-full font-semibold hover:bg-primary-dark transition-all hover:shadow-xl font-burmese">
|
||||||
|
နောက်ထပ် ဖတ်ရန် →
|
||||||
{/* Categories Card */}
|
</button>
|
||||||
<div className="bg-white rounded-lg shadow p-6">
|
|
||||||
<h3 className="text-lg font-bold text-gray-900 mb-4 font-burmese">
|
|
||||||
အမျိုးအစားများ
|
|
||||||
</h3>
|
|
||||||
<ul className="space-y-2">
|
|
||||||
<li>
|
|
||||||
<a href="/category/ai-news" className="text-primary-600 hover:text-primary-700 font-burmese">
|
|
||||||
AI သတင်းများ
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/category/tutorials" className="text-primary-600 hover:text-primary-700 font-burmese">
|
|
||||||
သင်ခန်းစာများ
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/category/tips-tricks" className="text-primary-600 hover:text-primary-700 font-burmese">
|
|
||||||
အကြံပြုချက်များ
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/category/upcoming" className="text-primary-600 hover:text-primary-700 font-burmese">
|
|
||||||
လာမည့်အရာများ
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
{/* About Card */}
|
|
||||||
<div className="bg-gradient-to-br from-primary-50 to-primary-100 rounded-lg shadow p-6">
|
|
||||||
<h3 className="text-lg font-bold text-gray-900 mb-3 font-burmese">
|
|
||||||
Burmddit အကြောင်း
|
|
||||||
</h3>
|
|
||||||
<p className="text-gray-700 text-sm leading-relaxed font-burmese">
|
|
||||||
Burmddit သည် AI နှင့် ပတ်သက်သော နောက်ဆုံးရ သတင်းများ၊ သင်ခန်းစာများနှင့် အကြံပြုချက်များကို မြန်မာဘာသာဖြင့် နေ့စဉ် ထုတ်ပြန်ပေးသော ပလက်ဖောင်း ဖြစ်ပါသည်။
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
1865
frontend/package-lock.json
generated
Normal file
1865
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -10,11 +10,12 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/pg": "^8.10.9",
|
||||||
|
"@vercel/postgres": "^0.5.1",
|
||||||
"next": "14.1.0",
|
"next": "14.1.0",
|
||||||
"react": "^18",
|
|
||||||
"react-dom": "^18",
|
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
"@types/pg": "^8.10.9"
|
"react": "^18",
|
||||||
|
"react-dom": "^18"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
|
|||||||
Reference in New Issue
Block a user