feat: add changelog page with localized release notes

- Implement releases collection in content-collections.ts for tracking version updates
- Create changelog page with dynamic release rendering for multiple locales
- Add ReleaseCard component to display version details and changes
- Implement getReleases utility function to fetch and sort release notes
- Update internationalization messages for changelog page
- Add English and Chinese release notes for v1.0.0, v1.1.0, and v1.2.0
This commit is contained in:
javayhu 2025-03-09 14:42:04 +08:00
parent 8da4488106
commit bc4b5527eb
13 changed files with 438 additions and 16 deletions

View File

@ -232,6 +232,74 @@ export const pages = defineCollection({
}
});
/**
* Releases collection for changelog
*
* 1. For a release at content/en/release/v1-0-0.md:
* locale: en
* slug: /release/v1-0-0
* slugAsParams: v1-0-0
*
* 2. For a release at content/zh/release/v1-0-0.md:
* locale: zh
* slug: /release/v1-0-0
* slugAsParams: v1-0-0
*/
export const releases = defineCollection({
name: 'release',
directory: 'content',
include: '**/release/**/*.{md,mdx}',
schema: (z) => ({
title: z.string(),
description: z.string(),
date: z.string().datetime(),
version: z.string(),
published: z.boolean().default(true),
locale: z.enum(LOCALES as [string, ...string[]]).optional()
}),
transform: async (data, context) => {
const body = await compileMDX(context, data, {
remarkPlugins: [
remarkGfm,
codeImport
],
rehypePlugins: [
rehypeSlug,
rehypeAutolinkHeadings,
[rehypePrettyCode, prettyCodeOptions]
]
});
// Determine the locale from the file path or use the provided locale
const pathParts = data._meta.path.split(path.sep);
const localeFromPath = LOCALES.includes(pathParts[0]) ? pathParts[0] : null;
const locale = data.locale || localeFromPath || DEFAULT_LOCALE;
// Create a slug without the locale in the path
let slugPath = data._meta.path;
if (localeFromPath) {
// Remove the locale from the path for the slug
const pathWithoutLocale = pathParts.slice(1).join(path.sep);
slugPath = pathWithoutLocale;
}
// Create slugAsParams without the locale
const slugParamsParts = slugPath.split(path.sep).slice(1);
const slugAsParams = slugParamsParts.join('/');
return {
...data,
locale,
slug: `/${slugPath}`,
slugAsParams,
body: {
raw: data.content,
code: body
}
};
}
});
const prettyCodeOptions: Options = {
theme: 'github-dark',
getHighlighter: (options) =>
@ -260,5 +328,5 @@ const prettyCodeOptions: Options = {
};
export default defineConfig({
collections: [authors, categories, posts, pages]
collections: [authors, categories, posts, pages, releases]
});

View File

@ -0,0 +1,30 @@
---
title: "Initial Release"
description: "Our first official release with core features and functionality"
date: "2024-03-01T00:00:00Z"
version: "v1.0.0"
published: true
---
### Core Features
We're excited to announce the initial release of our platform with the following core features:
- **User Authentication**: Secure login and registration with email verification
- **Dashboard**: Intuitive dashboard for managing your projects and resources
- **Project Management**: Create, edit, and organize your projects with ease
- **Team Collaboration**: Invite team members and collaborate on projects
- **API Integration**: Connect with third-party services through our API
### Technical Improvements
- Built with Next.js 14 and React Server Components for optimal performance
- Implemented Drizzle ORM for type-safe database operations
- Added comprehensive error handling and logging
- Optimized for mobile and desktop experiences
### Bug Fixes
- Fixed issues with user registration flow
- Resolved authentication token expiration handling
- Improved form validation and error messages

View File

@ -0,0 +1,30 @@
---
title: "Feature Update"
description: "New features and improvements to enhance your experience"
date: "2024-03-15T00:00:00Z"
version: "v1.1.0"
published: true
---
### New Features
We've added several new features to improve your experience:
- **Dark Mode**: Toggle between light and dark themes based on your preference
- **Export Options**: Export your data in multiple formats (CSV, JSON, PDF)
- **Advanced Search**: Find what you need faster with our improved search functionality
- **Custom Templates**: Create and save templates for recurring projects
### Enhancements
- **Performance Optimization**: Reduced page load times by 40%
- **UI Improvements**: Refreshed design with better accessibility
- **Mobile Experience**: Enhanced responsive design for all device sizes
- **Notification System**: Improved notification delivery and management
### Bug Fixes
- Fixed issue with project duplication
- Resolved calendar sync problems
- Fixed data import validation errors
- Improved error handling for API requests

View File

@ -0,0 +1,37 @@
---
title: "AI Integration"
description: "Introducing AI-powered features to boost productivity"
date: "2024-03-30T00:00:00Z"
version: "v1.2.0"
published: true
---
### AI-Powered Features
We're thrilled to introduce our new AI capabilities:
- **Smart Suggestions**: Get intelligent recommendations based on your usage patterns
- **Content Generation**: Generate high-quality content with our AI assistant
- **Automated Tagging**: Let AI organize your content with smart tagging
- **Predictive Analytics**: Forecast trends and outcomes with AI-powered insights
### Platform Improvements
- **Webhooks**: Set up custom webhooks to integrate with your workflow
- **Enhanced Security**: Added two-factor authentication and improved password policies
- **Custom Domains**: Use your own domain for your workspace
- **Audit Logs**: Track all activities with comprehensive audit logging
### Developer Tools
- **Expanded API**: New endpoints for advanced integrations
- **SDK Updates**: Updated SDKs for all major programming languages
- **Developer Console**: Improved developer experience with better documentation
- **Rate Limiting Controls**: Manage API usage with flexible rate limiting
### Bug Fixes
- Fixed issues with file uploads on certain browsers
- Resolved synchronization issues between devices
- Improved error handling for third-party integrations
- Fixed accessibility issues in the dashboard

View File

@ -0,0 +1,30 @@
---
title: "初始版本"
description: "我们的第一个正式版本,包含核心功能"
date: "2024-03-01T00:00:00Z"
version: "v1.0.0"
published: true
---
### 核心功能
我们很高兴宣布平台的初始版本,包含以下核心功能:
- **用户认证**:安全的登录和注册,带有电子邮件验证
- **仪表盘**:直观的仪表盘,用于管理您的项目和资源
- **项目管理**:轻松创建、编辑和组织您的项目
- **团队协作**:邀请团队成员并在项目上协作
- **API集成**通过我们的API连接第三方服务
### 技术改进
- 使用Next.js 14和React服务器组件构建以获得最佳性能
- 实现Drizzle ORM进行类型安全的数据库操作
- 添加全面的错误处理和日志记录
- 针对移动和桌面体验进行优化
### 错误修复
- 修复了用户注册流程中的问题
- 解决了身份验证令牌过期处理
- 改进了表单验证和错误消息

View File

@ -0,0 +1,30 @@
---
title: "功能更新"
description: "新功能和改进,提升您的使用体验"
date: "2024-03-15T00:00:00Z"
version: "v1.1.0"
published: true
---
### 新功能
我们添加了几个新功能来改善您的体验:
- **深色模式**:根据您的偏好在浅色和深色主题之间切换
- **导出选项**以多种格式CSV、JSON、PDF导出您的数据
- **高级搜索**:通过我们改进的搜索功能更快地找到您需要的内容
- **自定义模板**:为重复项目创建和保存模板
### 增强功能
- **性能优化**页面加载时间减少40%
- **UI改进**:更新设计,提高可访问性
- **移动体验**:为所有设备尺寸增强响应式设计
- **通知系统**:改进通知传递和管理
### 错误修复
- 修复了项目复制问题
- 解决了日历同步问题
- 修复了数据导入验证错误
- 改进了API请求的错误处理

View File

@ -0,0 +1,37 @@
---
title: "AI集成"
description: "引入AI驱动的功能提高生产力"
date: "2024-03-30T00:00:00Z"
version: "v1.2.0"
published: true
---
### AI驱动功能
我们很高兴介绍我们的新AI功能
- **智能建议**:根据您的使用模式获取智能推荐
- **内容生成**使用我们的AI助手生成高质量内容
- **自动标记**让AI通过智能标记组织您的内容
- **预测分析**使用AI驱动的洞察力预测趋势和结果
### 平台改进
- **Webhooks**设置自定义webhooks以集成到您的工作流程中
- **增强安全性**:添加双因素认证和改进的密码策略
- **自定义域名**:为您的工作空间使用自己的域名
- **审计日志**:通过全面的审计日志跟踪所有活动
### 开发者工具
- **扩展API**:用于高级集成的新端点
- **SDK更新**更新了所有主要编程语言的SDK
- **开发者控制台**:通过更好的文档改善开发者体验
- **速率限制控制**通过灵活的速率限制管理API使用
### 错误修复
- 修复了某些浏览器上文件上传的问题
- 解决了设备之间的同步问题
- 改进了第三方集成的错误处理
- 修复了仪表板中的可访问性问题

View File

@ -20,6 +20,10 @@
"tryAgain": "Try again",
"backToHome": "Back to home"
},
"ChangelogPage": {
"title": "Changelog",
"subtitle": "Stay up to date with the latest changes in our product."
},
"AuthPage": {
"login": {
"title": "Login",

View File

@ -17,6 +17,10 @@
"tryAgain": "重试",
"backToHome": "返回首页"
},
"ChangelogPage": {
"title": "更新日志",
"subtitle": "查看我们产品的最新动态"
},
"AuthPage": {
"login": {
"title": "登录",

View File

@ -0,0 +1,75 @@
import { ReleaseCard } from '@/components/release/release-card';
import { getReleases } from '@/lib/release/get-releases';
import { getBaseUrl } from '@/lib/urls/get-base-url';
import type { NextPageProps } from '@/types/next-page-props';
import type { Metadata } from 'next';
import { getTranslations } from 'next-intl/server';
import { notFound } from 'next/navigation';
import '@/styles/mdx.css';
export async function generateMetadata(
props: NextPageProps
): Promise<Metadata> {
const params = await props.params;
if (!params) {
return {};
}
const locale = params.locale as string;
return {
title: 'Changelog',
description: 'Track all updates and improvements to our platform',
openGraph: {
title: 'Changelog',
description: 'Track all updates and improvements to our platform',
type: 'article',
url: `${getBaseUrl()}/changelog`
}
};
}
export default async function ChangelogPage(props: NextPageProps) {
const params = await props.params;
if (!params) {
notFound();
}
const locale = params.locale as string;
const releases = await getReleases(locale);
if (!releases || releases.length === 0) {
notFound();
}
const t = await getTranslations('ChangelogPage');
return (
<div className="max-w-4xl mx-auto space-y-8">
{/* Header */}
<div className="space-y-4">
<h1 className="text-center text-3xl font-bold tracking-tight">
{t('title')}
</h1>
<p className="text-center text-lg text-muted-foreground">
{t('subtitle')}
</p>
</div>
{/* Releases */}
<div className="mt-8">
{releases.map((release) => (
<ReleaseCard
key={release.slug}
title={release.title}
description={release.description}
date={release.date}
version={release.version}
content={release.body.code}
/>
))}
</div>
</div>
);
}

View File

@ -13,23 +13,25 @@ export function CustomPage({ title, description, date, content }: CustomPageProp
const formattedDate = getLocaleDate(date);
return (
<div className="py-8">
<div className="space-y-8">
{/* Header */}
<div className="space-y-4">
<h1 className="text-3xl font-bold tracking-tight">{title}</h1>
<p className="text-lg text-muted-foreground">{description}</p>
<div className="flex items-center gap-2">
<CalendarIcon className="size-4 text-muted-foreground" />
<p className="text-sm text-muted-foreground">{formattedDate}</p>
</div>
</div>
{/* Content */}
<div className="prose prose-gray dark:prose-invert max-w-none">
<Mdx code={content} />
<div className="max-w-4xl mx-auto space-y-8">
{/* Header */}
<div className="space-y-4">
<h1 className="text-center text-3xl font-bold tracking-tight">
{title}
</h1>
<p className="text-center text-lg text-muted-foreground">
{description}
</p>
<div className="flex items-center justify-center gap-2">
<CalendarIcon className="size-4 text-muted-foreground" />
<p className="text-sm text-muted-foreground">{formattedDate}</p>
</div>
</div>
{/* Content */}
<div className="prose prose-gray dark:prose-invert max-w-none">
<Mdx code={content} />
</div>
</div>
);
}

View File

@ -0,0 +1,43 @@
import { Mdx } from '@/components/shared/mdx-component';
import { getLocaleDate } from '@/lib/utils';
import { CalendarIcon, TagIcon } from 'lucide-react';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { Separator } from '@/components/ui/separator';
interface ReleaseCardProps {
title: string;
description: string;
date: string;
version: string;
content: any; // MDX content
}
export function ReleaseCard({ title, description, date, version, content }: ReleaseCardProps) {
const formattedDate = getLocaleDate(date);
return (
<Card className="mb-8">
<CardHeader className="space-y-4">
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<h2 className="text-2xl font-bold tracking-tight">{title}</h2>
<Badge variant="default" className="w-fit">
<TagIcon className="mr-1 size-3" />
{version}
</Badge>
</div>
<p className="text-muted-foreground">{description}</p>
<div className="flex items-center gap-2">
<CalendarIcon className="size-4 text-muted-foreground" />
<p className="text-sm text-muted-foreground">{formattedDate}</p>
</div>
<Separator />
</CardHeader>
<CardContent>
<div className="prose prose-gray dark:prose-invert max-w-none">
<Mdx code={content} />
</div>
</CardContent>
</Card>
);
}

View File

@ -0,0 +1,32 @@
import { allReleases } from 'content-collections';
/**
* Gets all releases for the changelog page
* @param locale The locale to get releases for
* @returns An array of releases sorted by date (newest first)
*/
export async function getReleases(locale: string) {
// Find all published releases with matching locale
const releases = allReleases.filter(
(release) =>
release.published &&
release.locale === locale
);
// If no releases found with the current locale, try to find ones with any locale
if (releases.length === 0) {
const defaultReleases = allReleases.filter(
(release) => release.published
);
// Sort by date (newest first)
return defaultReleases.sort(
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
);
}
// Sort by date (newest first)
return releases.sort(
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
);
}