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:
parent
8da4488106
commit
bc4b5527eb
@ -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]
|
||||
});
|
30
content/en/release/v1-0-0.md
Normal file
30
content/en/release/v1-0-0.md
Normal 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
|
30
content/en/release/v1-1-0.md
Normal file
30
content/en/release/v1-1-0.md
Normal 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
|
37
content/en/release/v1-2-0.md
Normal file
37
content/en/release/v1-2-0.md
Normal 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
|
30
content/zh/release/v1-0-0.md
Normal file
30
content/zh/release/v1-0-0.md
Normal 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进行类型安全的数据库操作
|
||||
- 添加全面的错误处理和日志记录
|
||||
- 针对移动和桌面体验进行优化
|
||||
|
||||
### 错误修复
|
||||
|
||||
- 修复了用户注册流程中的问题
|
||||
- 解决了身份验证令牌过期处理
|
||||
- 改进了表单验证和错误消息
|
30
content/zh/release/v1-1-0.md
Normal file
30
content/zh/release/v1-1-0.md
Normal 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请求的错误处理
|
37
content/zh/release/v1-2-0.md
Normal file
37
content/zh/release/v1-2-0.md
Normal 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使用
|
||||
|
||||
### 错误修复
|
||||
|
||||
- 修复了某些浏览器上文件上传的问题
|
||||
- 解决了设备之间的同步问题
|
||||
- 改进了第三方集成的错误处理
|
||||
- 修复了仪表板中的可访问性问题
|
@ -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",
|
||||
|
@ -17,6 +17,10 @@
|
||||
"tryAgain": "重试",
|
||||
"backToHome": "返回首页"
|
||||
},
|
||||
"ChangelogPage": {
|
||||
"title": "更新日志",
|
||||
"subtitle": "查看我们产品的最新动态"
|
||||
},
|
||||
"AuthPage": {
|
||||
"login": {
|
||||
"title": "登录",
|
||||
|
75
src/app/[locale]/(marketing)/(pages)/changelog/page.tsx
Normal file
75
src/app/[locale]/(marketing)/(pages)/changelog/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
@ -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>
|
||||
);
|
||||
}
|
43
src/components/release/release-card.tsx
Normal file
43
src/components/release/release-card.tsx
Normal 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>
|
||||
);
|
||||
}
|
32
src/lib/release/get-releases.ts
Normal file
32
src/lib/release/get-releases.ts
Normal 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()
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user