forked from minzeyaphyo/burmddit
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
964afce761 | ||
|
|
f0146c311c |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -41,3 +41,4 @@ coverage/
|
|||||||
# Misc
|
# Misc
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.zip
|
*.zip
|
||||||
|
.playwright-mcp/
|
||||||
|
|||||||
113
CLAUDE.md
Normal file
113
CLAUDE.md
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
Burmddit is an automated AI news aggregator that scrapes English AI content, compiles related articles, translates them to Burmese using Claude API, and publishes them daily. It has two independent sub-systems:
|
||||||
|
|
||||||
|
- **Backend** (`/backend`): Python pipeline — scrape → compile → translate → publish
|
||||||
|
- **Frontend** (`/frontend`): Next.js 14 App Router site that reads from PostgreSQL
|
||||||
|
|
||||||
|
Both connect to the same PostgreSQL database hosted on Railway.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
npm install
|
||||||
|
npm run dev # Start dev server (localhost:3000)
|
||||||
|
npm run build # Production build
|
||||||
|
npm run lint # ESLint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Run full pipeline (scrape + compile + translate + publish)
|
||||||
|
python run_pipeline.py
|
||||||
|
|
||||||
|
# Run individual stages
|
||||||
|
python scraper.py
|
||||||
|
python compiler.py
|
||||||
|
python translator.py
|
||||||
|
|
||||||
|
# Database management
|
||||||
|
python init_db.py # Initialize schema
|
||||||
|
python init_db.py stats # Show article/view counts
|
||||||
|
python init_db.py --reset # Drop and recreate (destructive)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Required Environment Variables
|
||||||
|
|
||||||
|
**Frontend** (`.env.local`):
|
||||||
|
```
|
||||||
|
DATABASE_URL=postgresql://...
|
||||||
|
NEXT_PUBLIC_SITE_URL=https://burmddit.vercel.app
|
||||||
|
```
|
||||||
|
|
||||||
|
**Backend** (`.env`):
|
||||||
|
```
|
||||||
|
DATABASE_URL=postgresql://...
|
||||||
|
ANTHROPIC_API_KEY=sk-ant-...
|
||||||
|
ADMIN_PASSWORD=...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Data Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
[Scraper] → raw_articles table
|
||||||
|
↓
|
||||||
|
[Compiler] → clusters related raw articles, generates compiled English articles
|
||||||
|
↓
|
||||||
|
[Translator] → calls Claude API (claude-3-5-sonnet-20241022) to produce Burmese content
|
||||||
|
↓
|
||||||
|
[Publisher] → inserts into articles table with status='published'
|
||||||
|
↓
|
||||||
|
[Frontend] → queries published_articles view via @vercel/postgres
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Schema Key Points
|
||||||
|
|
||||||
|
- `raw_articles` — scraped source content, flagged `processed=TRUE` once compiled
|
||||||
|
- `articles` — final bilingual articles with both English and Burmese fields (`title`/`title_burmese`, `content`/`content_burmese`, etc.)
|
||||||
|
- `published_articles` — PostgreSQL view joining `articles` + `categories`, used by frontend queries
|
||||||
|
- `pipeline_logs` — tracks each stage execution for monitoring
|
||||||
|
|
||||||
|
### Frontend Architecture
|
||||||
|
|
||||||
|
Next.js 14 App Router with server components querying the database directly via `@vercel/postgres` (sql template tag). No API routes — all DB access happens in server components/actions.
|
||||||
|
|
||||||
|
Key pages: homepage (`app/page.tsx`), article detail (`app/[slug]/`), category listing (`app/category/`).
|
||||||
|
|
||||||
|
Burmese font: Noto Sans Myanmar loaded from Google Fonts. Apply `font-burmese` Tailwind class for Burmese text.
|
||||||
|
|
||||||
|
### Backend Pipeline (`run_pipeline.py`)
|
||||||
|
|
||||||
|
Orchestrates four stages sequentially. Each stage is a standalone module with a `run_*()` function. Pipeline exits early with a warning if a stage produces zero results. Logs go to both stderr and `burmddit_pipeline.log` (7-day rotation).
|
||||||
|
|
||||||
|
### Configuration (`backend/config.py`)
|
||||||
|
|
||||||
|
All tunable parameters live here:
|
||||||
|
- `SOURCES` — which RSS/scrape sources are enabled and their limits
|
||||||
|
- `PIPELINE` — articles per day, length limits, clustering threshold
|
||||||
|
- `TRANSLATION` — Claude model, temperature, technical terms to preserve in English
|
||||||
|
- `PUBLISHING` — default status (`'published'` or `'draft'`), image/video extraction settings
|
||||||
|
- `CATEGORY_KEYWORDS` — keyword lists for auto-detecting one of 4 categories
|
||||||
|
|
||||||
|
### Automation
|
||||||
|
|
||||||
|
Daily pipeline is triggered via GitHub Actions (`.github/workflows/daily-publish.yml`) at 6 AM UTC, using `DATABASE_URL` and `ANTHROPIC_API_KEY` repository secrets. Can also be triggered manually via `workflow_dispatch`.
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
- **Frontend**: Vercel (root directory: `frontend`, auto-detects Next.js)
|
||||||
|
- **Backend + DB**: Railway (root directory: `backend`, start command: `python run_pipeline.py`)
|
||||||
|
- **Database init**: Run `python init_db.py init` once from Railway console after first deploy
|
||||||
@@ -3,11 +3,12 @@ export const dynamic = "force-dynamic"
|
|||||||
import { notFound } from 'next/navigation'
|
import { notFound } from 'next/navigation'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
|
import ShareButtons from '@/components/ShareButtons'
|
||||||
|
|
||||||
async function getArticleWithTags(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,
|
||||||
@@ -27,12 +28,9 @@ async function getArticleWithTags(slug: string) {
|
|||||||
WHERE a.slug = ${slug} AND a.status = 'published'
|
WHERE a.slug = ${slug} AND a.status = 'published'
|
||||||
GROUP BY a.id, c.id
|
GROUP BY a.id, c.id
|
||||||
`
|
`
|
||||||
|
|
||||||
if (rows.length === 0) return null
|
if (rows.length === 0) return null
|
||||||
|
// Increment view count only here (not in generateMetadata)
|
||||||
// Increment view count
|
|
||||||
await sql`SELECT increment_view_count(${slug})`
|
await sql`SELECT increment_view_count(${slug})`
|
||||||
|
|
||||||
return rows[0]
|
return rows[0]
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching article:', 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) {
|
async function getRelatedArticles(articleId: number) {
|
||||||
try {
|
try {
|
||||||
const { rows } = await sql`SELECT * FROM get_related_articles(${articleId}, 6)`
|
const { rows } = await sql`SELECT * FROM get_related_articles(${articleId}, 6)`
|
||||||
return rows
|
return rows
|
||||||
} catch (error) {
|
} catch {
|
||||||
return []
|
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)
|
const article = await getArticleWithTags(params.slug)
|
||||||
|
if (!article) notFound()
|
||||||
if (!article) {
|
|
||||||
notFound()
|
|
||||||
}
|
|
||||||
|
|
||||||
const relatedArticles = await getRelatedArticles(article.id)
|
const relatedArticles = await getRelatedArticles(article.id)
|
||||||
const publishedDate = new Date(article.published_at).toLocaleDateString('my-MM', {
|
const publishedDate = new Date(article.published_at).toLocaleDateString('my-MM', {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
day: 'numeric'
|
day: 'numeric',
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-white">
|
<div className="min-h-screen bg-white">
|
||||||
{/* Hero Cover Image */}
|
{/* Hero Cover Image */}
|
||||||
{article.featured_image && (
|
{article.featured_image && (
|
||||||
<div className="relative h-[70vh] w-full overflow-hidden">
|
<div className="relative h-[55vh] w-full overflow-hidden">
|
||||||
<Image
|
<Image
|
||||||
src={article.featured_image}
|
src={article.featured_image}
|
||||||
alt={article.title_burmese}
|
alt={article.title_burmese}
|
||||||
@@ -75,25 +85,20 @@ export default async function ImprovedArticlePage({ params }: { params: { slug:
|
|||||||
className="object-cover"
|
className="object-cover"
|
||||||
priority
|
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="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">
|
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 pb-10 w-full">
|
||||||
{/* Category */}
|
|
||||||
<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 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}
|
{article.category_name_burmese}
|
||||||
</Link>
|
</Link>
|
||||||
|
<h1 className="text-2xl md:text-3xl lg:text-4xl font-bold text-white mb-4 font-burmese leading-snug line-clamp-3">
|
||||||
{/* Title */}
|
|
||||||
<h1 className="text-5xl md:text-6xl font-bold text-white mb-6 font-burmese leading-tight">
|
|
||||||
{article.title_burmese}
|
{article.title_burmese}
|
||||||
</h1>
|
</h1>
|
||||||
|
<div className="flex flex-wrap items-center gap-3 text-white/80 text-sm">
|
||||||
{/* Meta */}
|
|
||||||
<div className="flex flex-wrap items-center gap-4 text-white/90">
|
|
||||||
<span className="font-burmese">{publishedDate}</span>
|
<span className="font-burmese">{publishedDate}</span>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
<span className="font-burmese">{article.reading_time} မိနစ်</span>
|
<span className="font-burmese">{article.reading_time} မိနစ်</span>
|
||||||
@@ -106,10 +111,10 @@ export default async function ImprovedArticlePage({ params }: { params: { slug:
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Article Content */}
|
{/* 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 */}
|
{/* Tags */}
|
||||||
{article.tags_burmese && article.tags_burmese.length > 0 && (
|
{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) => (
|
{article.tags_burmese.map((tag: string, idx: number) => (
|
||||||
<Link
|
<Link
|
||||||
key={idx}
|
key={idx}
|
||||||
@@ -122,33 +127,28 @@ export default async function ImprovedArticlePage({ params }: { params: { slug:
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Article Body */}
|
{/* Body */}
|
||||||
<div className="article-content">
|
<div className="article-content">
|
||||||
<div dangerouslySetInnerHTML={{ __html: formatContent(article.content_burmese) }} />
|
<div dangerouslySetInnerHTML={{ __html: formatContent(article.content_burmese) }} />
|
||||||
|
|
||||||
{/* Additional Images Gallery */}
|
{/* Image Gallery */}
|
||||||
{article.images && article.images.length > 1 && (
|
{article.images && article.images.length > 1 && (
|
||||||
<div className="my-12">
|
<div className="my-10 not-prose">
|
||||||
<h3 className="text-2xl font-bold mb-6 font-burmese">ဓာတ်ပုံများ</h3>
|
<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-4">
|
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||||
{article.images.slice(1).map((img: string, idx: number) => (
|
{article.images.slice(1).map((img: string, idx: number) => (
|
||||||
<div key={idx} className="relative h-56 rounded-xl overflow-hidden image-zoom">
|
<div key={idx} className="relative h-48 rounded-xl overflow-hidden image-zoom">
|
||||||
<Image
|
<Image src={img} alt={`${article.title_burmese} ${idx + 2}`} fill className="object-cover" />
|
||||||
src={img}
|
|
||||||
alt={`${article.title_burmese} - ${idx + 2}`}
|
|
||||||
fill
|
|
||||||
className="object-cover"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Videos */}
|
{/* Videos */}
|
||||||
{article.videos && article.videos.length > 0 && (
|
{article.videos && article.videos.length > 0 && (
|
||||||
<div className="my-12">
|
<div className="my-10 not-prose">
|
||||||
<h3 className="text-2xl font-bold mb-6 font-burmese">ဗီဒီယိုများ</h3>
|
<h3 className="text-xl font-bold mb-4 font-burmese text-gray-900">ဗီဒီယိုများ</h3>
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{article.videos.map((video: string, idx: number) => (
|
{article.videos.map((video: string, idx: number) => (
|
||||||
<div key={idx} className="relative aspect-video rounded-xl overflow-hidden bg-gray-900 shadow-xl">
|
<div key={idx} className="relative aspect-video rounded-xl overflow-hidden bg-gray-900 shadow-xl">
|
||||||
@@ -162,49 +162,36 @@ export default async function ImprovedArticlePage({ params }: { params: { slug:
|
|||||||
|
|
||||||
{/* Source Attribution */}
|
{/* Source Attribution */}
|
||||||
{article.source_articles && article.source_articles.length > 0 && (
|
{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">
|
<div className="mt-12 p-6 bg-blue-50 rounded-2xl border border-blue-100">
|
||||||
<h3 className="text-2xl font-bold text-gray-900 mb-4 font-burmese flex items-center">
|
<h3 className="text-lg font-bold text-gray-900 mb-3 font-burmese flex items-center gap-2">
|
||||||
<svg className="w-7 h-7 mr-3 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<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" />
|
<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>
|
</svg>
|
||||||
မူရင်းသတင်းရင်းမြစ်များ
|
မူရင်းသတင်းရင်းမြစ်များ
|
||||||
</h3>
|
</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>
|
</p>
|
||||||
<div className="space-y-4">
|
<div className="space-y-3">
|
||||||
{article.source_articles.map((source: any, index: number) => (
|
{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 key={index} className="bg-white p-4 rounded-xl border border-gray-100 flex items-start gap-3">
|
||||||
<div className="flex items-start gap-4">
|
<span className="flex-shrink-0 w-6 h-6 bg-primary text-white rounded-full flex items-center justify-center text-xs font-bold">
|
||||||
<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}
|
||||||
{index + 1}
|
</span>
|
||||||
</span>
|
<div className="flex-1 min-w-0">
|
||||||
<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
|
<a
|
||||||
href={source.url}
|
href={source.url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex-shrink-0 text-primary hover:text-primary-dark"
|
className="text-primary hover:underline text-sm font-medium break-words"
|
||||||
title="Open source"
|
|
||||||
>
|
>
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
{source.title}
|
||||||
<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>
|
</a>
|
||||||
|
{source.author && source.author !== 'Unknown' && (
|
||||||
|
<p className="text-xs text-gray-500 mt-1 font-burmese">
|
||||||
|
စာရေးသူ: {source.author}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -212,58 +199,39 @@ export default async function ImprovedArticlePage({ params }: { params: { slug:
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Share Section */}
|
{/* Share */}
|
||||||
<div className="mt-12 py-8 border-y border-gray-200">
|
<div className="mt-10 py-8 border-t border-gray-100">
|
||||||
<div className="flex items-center justify-between">
|
<p className="font-burmese text-gray-700 font-semibold mb-4">မျှဝေပါ:</p>
|
||||||
<p className="font-burmese text-gray-700 font-semibold">မျှဝေပါ:</p>
|
<ShareButtons title={article.title_burmese} />
|
||||||
<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>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
{/* Related Articles */}
|
{/* Related Articles */}
|
||||||
{relatedArticles.length > 0 && (
|
{relatedArticles.length > 0 && (
|
||||||
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 bg-gray-50">
|
<section className="bg-gray-50 py-14">
|
||||||
<h2 className="text-3xl font-bold text-gray-900 mb-10 font-burmese">
|
<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">
|
</h2>
|
||||||
{relatedArticles.map((related: any) => (
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
<Link
|
{relatedArticles.map((related: any) => (
|
||||||
key={related.id}
|
<Link key={related.id} href={`/article/${related.slug}`} className="card card-hover">
|
||||||
href={`/article/${related.slug}`}
|
{related.featured_image && (
|
||||||
className="card card-hover"
|
<div className="relative h-44 w-full image-zoom">
|
||||||
>
|
<Image src={related.featured_image} alt={related.title_burmese} fill className="object-cover" />
|
||||||
{related.featured_image && (
|
</div>
|
||||||
<div className="relative h-48 w-full image-zoom">
|
)}
|
||||||
<Image
|
<div className="p-5">
|
||||||
src={related.featured_image}
|
<h3 className="font-bold text-gray-900 font-burmese line-clamp-3 hover:text-primary transition-colors leading-snug mb-2">
|
||||||
alt={related.title_burmese}
|
{related.title_burmese}
|
||||||
fill
|
</h3>
|
||||||
className="object-cover"
|
<p className="text-sm text-gray-500 font-burmese line-clamp-2 leading-relaxed">
|
||||||
/>
|
{related.excerpt_burmese}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</Link>
|
||||||
<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>
|
||||||
{related.title_burmese}
|
|
||||||
</h3>
|
|
||||||
<p className="text-sm text-gray-600 font-burmese line-clamp-2">
|
|
||||||
{related.excerpt_burmese}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
@@ -272,30 +240,30 @@ export default async function ImprovedArticlePage({ params }: { params: { slug:
|
|||||||
}
|
}
|
||||||
|
|
||||||
function formatContent(content: string): string {
|
function formatContent(content: string): string {
|
||||||
let formatted = content
|
const formatted = content
|
||||||
.replace(/\n\n/g, '</p><p>')
|
.replace(/### (.*?)(\n|$)/g, '<h3>$1</h3>')
|
||||||
.replace(/## (.*?)\n/g, '<h2>$1</h2>')
|
.replace(/## (.*?)(\n|$)/g, '<h2>$1</h2>')
|
||||||
.replace(/### (.*?)\n/g, '<h3>$1</h3>')
|
.replace(/# (.*?)(\n|$)/g, '<h1>$1</h1>')
|
||||||
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
||||||
.replace(/\*(.*?)\*/g, '<em>$1</em>')
|
.replace(/\*(.*?)\*/g, '<em>$1</em>')
|
||||||
|
.replace(/\n\n+/g, '</p><p>')
|
||||||
|
.replace(/\n/g, '<br/>')
|
||||||
return `<p>${formatted}</p>`
|
return `<p>${formatted}</p>`
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderVideo(videoUrl: string) {
|
function renderVideo(videoUrl: string) {
|
||||||
let videoId = null
|
let videoId: string | null = null
|
||||||
|
|
||||||
if (videoUrl.includes('youtube.com/watch')) {
|
if (videoUrl.includes('youtube.com/watch')) {
|
||||||
const match = videoUrl.match(/v=([^&]+)/)
|
const m = videoUrl.match(/v=([^&]+)/)
|
||||||
videoId = match ? match[1] : null
|
videoId = m ? m[1] : null
|
||||||
} else if (videoUrl.includes('youtu.be/')) {
|
} else if (videoUrl.includes('youtu.be/')) {
|
||||||
const match = videoUrl.match(/youtu\.be\/([^?]+)/)
|
const m = videoUrl.match(/youtu\.be\/([^?]+)/)
|
||||||
videoId = match ? match[1] : null
|
videoId = m ? m[1] : null
|
||||||
} else if (videoUrl.includes('youtube.com/embed/')) {
|
} else if (videoUrl.includes('youtube.com/embed/')) {
|
||||||
const match = videoUrl.match(/embed\/([^?]+)/)
|
const m = videoUrl.match(/embed\/([^?]+)/)
|
||||||
videoId = match ? match[1] : null
|
videoId = m ? m[1] : null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (videoId) {
|
if (videoId) {
|
||||||
return (
|
return (
|
||||||
<iframe
|
<iframe
|
||||||
@@ -306,25 +274,12 @@ function renderVideo(videoUrl: string) {
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
return (
|
|
||||||
<iframe
|
|
||||||
src={videoUrl}
|
|
||||||
className="w-full h-full"
|
|
||||||
allowFullScreen
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateMetadata({ params }: { params: { slug: string } }) {
|
export async function generateMetadata({ params }: { params: { slug: string } }) {
|
||||||
const article = await getArticleWithTags(params.slug)
|
const article = await getArticleMeta(params.slug)
|
||||||
|
if (!article) return { title: 'Article Not Found' }
|
||||||
if (!article) {
|
|
||||||
return {
|
|
||||||
title: 'Article Not Found',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: `${article.title_burmese} - Burmddit`,
|
title: `${article.title_burmese} - Burmddit`,
|
||||||
description: article.excerpt_burmese,
|
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 */
|
/* Burmese Fonts */
|
||||||
@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-burmese {
|
||||||
font-family: 'Pyidaungsu', 'Noto Sans Myanmar', 'Myanmar Text', sans-serif;
|
font-family: 'Noto Sans Myanmar', 'Myanmar Text', sans-serif;
|
||||||
letter-spacing: 0.01em;
|
letter-spacing: 0.01em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
import { Inter } from 'next/font/google'
|
import { Inter, Noto_Sans_Myanmar } from 'next/font/google'
|
||||||
import './globals.css'
|
import './globals.css'
|
||||||
import Header from '@/components/Header'
|
import Header from '@/components/Header'
|
||||||
import Footer from '@/components/Footer'
|
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 = {
|
export const metadata: Metadata = {
|
||||||
title: 'Burmddit - Myanmar AI News & Tutorials',
|
title: 'Burmddit - Myanmar AI သတင်းများ',
|
||||||
description: 'Daily AI news, tutorials, and tips in Burmese. Stay updated with the latest in artificial intelligence.',
|
description: 'AI နှင့် နည်းပညာဆိုင်ရာ သတင်းများ၊ သင်ခန်းစာများနှင့် အကြံပြုချက်များကို မြန်မာဘာသာဖြင့် နေ့စဉ် ဖတ်ရှုနိုင်သော ပလက်ဖောင်း',
|
||||||
keywords: 'AI, Myanmar, Burmese, AI news, AI tutorials, machine learning, ChatGPT',
|
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({
|
export default function RootLayout({
|
||||||
@@ -18,13 +27,8 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<html lang="my" className="font-burmese">
|
<html lang="my" className={`${inter.variable} ${notoSansMyanmar.variable}`}>
|
||||||
<head>
|
<body className={`${inter.className} bg-gray-50 antialiased`}>
|
||||||
<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`}>
|
|
||||||
<Header />
|
<Header />
|
||||||
<main className="min-h-screen">
|
<main className="min-h-screen">
|
||||||
{children}
|
{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>
|
</Link>
|
||||||
|
|
||||||
{/* Title */}
|
{/* 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">
|
<Link href={`/article/${featured.slug}`} className="hover:text-gray-200 transition-colors">
|
||||||
{featured.title_burmese}
|
{featured.title_burmese}
|
||||||
</Link>
|
</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() {
|
export default function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="bg-gray-900 text-white mt-16">
|
<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="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">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||||
{/* About */}
|
{/* Brand */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-bold mb-4 font-burmese">Burmddit အကြောင်း</h3>
|
<Link href="/" className="flex items-center space-x-2 mb-4">
|
||||||
<p className="text-gray-400 text-sm font-burmese">
|
<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 နှင့် နည်းပညာဆိုင်ရာ သတင်းများကို မြန်မာဘာသာဖြင့် နေ့စဉ် ထုတ်ပြန်ပေးသော ပလက်ဖောင်း
|
AI နှင့် နည်းပညာဆိုင်ရာ သတင်းများကို မြန်မာဘာသာဖြင့် နေ့စဉ် ထုတ်ပြန်ပေးသော ပလက်ဖောင်း
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Links */}
|
{/* Categories */}
|
||||||
<div>
|
<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">
|
<ul className="space-y-2 text-sm">
|
||||||
<li>
|
{categories.map((c) => (
|
||||||
<a href="/category/ai-news" className="text-gray-400 hover:text-white font-burmese">
|
<li key={c.href}>
|
||||||
AI သတင်းများ
|
<Link href={c.href} className="text-gray-400 hover:text-white font-burmese transition-colors">
|
||||||
</a>
|
{c.label}
|
||||||
</li>
|
</Link>
|
||||||
<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>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Contact */}
|
{/* Quick Links */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-bold mb-4">Contact</h3>
|
<h3 className="text-base font-bold mb-4 font-burmese">အမြန်လင့်များ</h3>
|
||||||
<p className="text-gray-400 text-sm">
|
<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
|
Built with ❤️ for Myanmar tech community
|
||||||
</p>
|
</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>
|
</div>
|
||||||
|
|
||||||
<div className="mt-8 pt-8 border-t border-gray-800 text-center">
|
<div className="mt-8 pt-8 border-t border-gray-800 text-center">
|
||||||
<p className="text-gray-400 text-sm">
|
<p className="text-gray-500 text-sm font-burmese">
|
||||||
© {new Date().getFullYear()} Burmddit. All rights reserved.
|
© {new Date().getFullYear()} Burmddit. မူပိုင်ခွင့် အာမခံသည်။
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,53 +1,104 @@
|
|||||||
|
'use client'
|
||||||
|
import { useState } from 'react'
|
||||||
import Link from 'next/link'
|
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() {
|
export default function Header() {
|
||||||
|
const [mobileOpen, setMobileOpen] = useState(false)
|
||||||
|
const pathname = usePathname()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="bg-white shadow-sm sticky top-0 z-50">
|
<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">
|
<nav className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div className="flex justify-between items-center h-16">
|
<div className="flex justify-between items-center h-16">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Link href="/" className="flex items-center space-x-2">
|
<Link href="/" className="flex items-center space-x-2 flex-shrink-0">
|
||||||
<span className="text-2xl font-bold text-primary-600">B</span>
|
<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">
|
<span className="text-xl font-bold text-gray-900 font-burmese">Burmddit</span>
|
||||||
Burmddit
|
|
||||||
</span>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Navigation */}
|
{/* Desktop Nav */}
|
||||||
<div className="hidden md:flex space-x-8">
|
<div className="hidden md:flex items-center space-x-1">
|
||||||
<Link
|
{navLinks.map((link) => (
|
||||||
href="/"
|
<Link
|
||||||
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 ${
|
||||||
</Link>
|
pathname === link.href
|
||||||
<Link
|
? 'bg-primary/10 text-primary'
|
||||||
href="/category/ai-news"
|
: 'text-gray-700 hover:text-primary hover:bg-gray-100'
|
||||||
className="text-gray-700 hover:text-primary-600 font-medium font-burmese"
|
}`}
|
||||||
>
|
>
|
||||||
AI သတင်းများ
|
{link.label}
|
||||||
</Link>
|
</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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Search Icon */}
|
{/* Right: Search + Mobile Hamburger */}
|
||||||
<button className="p-2 text-gray-600 hover:text-primary-600">
|
<div className="flex items-center space-x-1">
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<Link
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
href="/search"
|
||||||
</svg>
|
className="p-2 text-gray-600 hover:text-primary hover:bg-gray-100 rounded-lg transition-colors"
|
||||||
</button>
|
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>
|
</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>
|
</nav>
|
||||||
</header>
|
</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: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
'burmese': ['Pyidaungsu', 'Noto Sans Myanmar', 'Myanmar Text', 'sans-serif'],
|
'burmese': ['Noto Sans Myanmar', 'Myanmar Text', 'sans-serif'],
|
||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
primary: {
|
primary: {
|
||||||
50: '#f0f9ff',
|
DEFAULT: '#2563eb',
|
||||||
100: '#e0f2fe',
|
dark: '#1e40af',
|
||||||
200: '#bae6fd',
|
50: '#eff6ff',
|
||||||
300: '#7dd3fc',
|
100: '#dbeafe',
|
||||||
400: '#38bdf8',
|
200: '#bfdbfe',
|
||||||
500: '#0ea5e9',
|
300: '#93c5fd',
|
||||||
600: '#0284c7',
|
400: '#60a5fa',
|
||||||
700: '#0369a1',
|
500: '#3b82f6',
|
||||||
800: '#075985',
|
600: '#2563eb',
|
||||||
900: '#0c4a6e',
|
700: '#1d4ed8',
|
||||||
|
800: '#1e40af',
|
||||||
|
900: '#1e3a8a',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user