feat(website): Next.js 16 marketing website with RTL/Farsi
Marketing website for Meezi platform: - Server-side rendered pages: home, demo, blog, pricing - RTL/Farsi layout with Vazirmatn font - SEO metadata and Open Graph tags - proxy.ts for Next.js 16 middleware convention - MEEZI_API_URL internal Docker network routing Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import matter from "gray-matter";
|
||||
import readingTime from "reading-time";
|
||||
|
||||
const BLOG_DIR = path.join(process.cwd(), "src/content/blog");
|
||||
|
||||
export interface BlogPost {
|
||||
slug: string;
|
||||
title: string;
|
||||
excerpt: string;
|
||||
date: string;
|
||||
author: string;
|
||||
category: string;
|
||||
tags: string[];
|
||||
coverImage?: string;
|
||||
readingTime: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export function getAllPosts(locale: "fa" | "en" = "fa"): BlogPost[] {
|
||||
if (!fs.existsSync(BLOG_DIR)) return [];
|
||||
const files = fs.readdirSync(BLOG_DIR).filter((f) => f.endsWith(`.${locale}.mdx`));
|
||||
return files
|
||||
.map((file) => {
|
||||
const slug = file.replace(`.${locale}.mdx`, "");
|
||||
return getPostBySlug(slug, locale);
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => new Date(b!.date).getTime() - new Date(a!.date).getTime()) as BlogPost[];
|
||||
}
|
||||
|
||||
export function getPostBySlug(slug: string, locale: "fa" | "en" = "fa"): BlogPost | null {
|
||||
const filePath = path.join(BLOG_DIR, `${slug}.${locale}.mdx`);
|
||||
if (!fs.existsSync(filePath)) return null;
|
||||
const raw = fs.readFileSync(filePath, "utf8");
|
||||
const { data, content } = matter(raw);
|
||||
const rt = readingTime(content);
|
||||
return {
|
||||
slug,
|
||||
title: data.title ?? "",
|
||||
excerpt: data.excerpt ?? "",
|
||||
date: data.date ?? "",
|
||||
author: data.author ?? "",
|
||||
category: data.category ?? "",
|
||||
tags: data.tags ?? [],
|
||||
coverImage: data.coverImage,
|
||||
readingTime: locale === "fa" ? `${Math.ceil(rt.minutes)} دقیقه مطالعه` : rt.text,
|
||||
content,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
Reference in New Issue
Block a user