feat: update AppRouter with layout routes, add Docs and Blog pages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
15
src/pages/BlogDetailPage.tsx
Normal file
15
src/pages/BlogDetailPage.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import SEOHead from '../components/seo/SEOHead';
|
||||
|
||||
export default function BlogDetailPage() {
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
return (
|
||||
<>
|
||||
<SEOHead title={slug ?? 'Blog'} description={`Blog post: ${slug}`} path={`/blog/${slug}`} type="article" />
|
||||
<div className="max-w-4xl mx-auto py-16 px-6">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-4 capitalize">{slug?.replace(/-/g, ' ')}</h1>
|
||||
<p className="text-gray-600">Content coming soon.</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
35
src/pages/BlogListPage.tsx
Normal file
35
src/pages/BlogListPage.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import SEOHead from '../components/seo/SEOHead';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
|
||||
const posts = [
|
||||
{
|
||||
slug: 'introducing-texpixel',
|
||||
title: 'Introducing TexPixel',
|
||||
titleZh: 'TexPixel 介绍',
|
||||
description: 'Meet TexPixel — your AI-powered formula recognition tool',
|
||||
descriptionZh: '认识 TexPixel — 你的 AI 公式识别工具',
|
||||
date: '2026-03-25',
|
||||
},
|
||||
];
|
||||
|
||||
export default function BlogListPage() {
|
||||
const { language } = useLanguage();
|
||||
return (
|
||||
<>
|
||||
<SEOHead title="Blog" description="TexPixel blog — updates, tutorials, and insights" path="/blog" />
|
||||
<div className="max-w-4xl mx-auto py-16 px-6">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-8">{language === 'en' ? 'Blog' : '博客'}</h1>
|
||||
<div className="space-y-6">
|
||||
{posts.map((post) => (
|
||||
<Link key={post.slug} to={`/blog/${post.slug}`} className="block p-6 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors">
|
||||
<div className="text-xs text-gray-400 mb-2">{post.date}</div>
|
||||
<h2 className="text-lg font-semibold text-gray-900">{language === 'en' ? post.title : post.titleZh}</h2>
|
||||
<p className="text-gray-600 mt-1 text-sm">{language === 'en' ? post.description : post.descriptionZh}</p>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
15
src/pages/DocDetailPage.tsx
Normal file
15
src/pages/DocDetailPage.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import SEOHead from '../components/seo/SEOHead';
|
||||
|
||||
export default function DocDetailPage() {
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
return (
|
||||
<>
|
||||
<SEOHead title={slug ?? 'Doc'} description={`Documentation: ${slug}`} path={`/docs/${slug}`} />
|
||||
<div className="max-w-4xl mx-auto py-16 px-6">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-4 capitalize">{slug?.replace(/-/g, ' ')}</h1>
|
||||
<p className="text-gray-600">Content coming soon.</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
27
src/pages/DocsListPage.tsx
Normal file
27
src/pages/DocsListPage.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import SEOHead from '../components/seo/SEOHead';
|
||||
import { useLanguage } from '../contexts/LanguageContext';
|
||||
|
||||
const docs = [
|
||||
{ slug: 'getting-started', title: 'Getting Started', titleZh: '快速开始', description: 'Learn how to use TexPixel for formula recognition', descriptionZh: '了解如何使用 TexPixel 进行公式识别' },
|
||||
];
|
||||
|
||||
export default function DocsListPage() {
|
||||
const { language } = useLanguage();
|
||||
return (
|
||||
<>
|
||||
<SEOHead title="Documentation" description="TexPixel documentation and guides" path="/docs" />
|
||||
<div className="max-w-4xl mx-auto py-16 px-6">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-8">{language === 'en' ? 'Documentation' : '文档'}</h1>
|
||||
<div className="space-y-4">
|
||||
{docs.map((doc) => (
|
||||
<Link key={doc.slug} to={`/docs/${doc.slug}`} className="block p-6 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors">
|
||||
<h2 className="text-lg font-semibold text-gray-900">{language === 'en' ? doc.title : doc.titleZh}</h2>
|
||||
<p className="text-gray-600 mt-1 text-sm">{language === 'en' ? doc.description : doc.descriptionZh}</p>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,12 +1,40 @@
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { Routes, Route } from 'react-router-dom';
|
||||
import App from '../App';
|
||||
import MarketingLayout from '../components/layout/MarketingLayout';
|
||||
import AppLayout from '../components/layout/AppLayout';
|
||||
import AuthCallbackPage from '../pages/AuthCallbackPage';
|
||||
|
||||
const HomePage = lazy(() => import('../pages/HomePage'));
|
||||
const WorkspacePage = lazy(() => import('../pages/WorkspacePage'));
|
||||
const DocsListPage = lazy(() => import('../pages/DocsListPage'));
|
||||
const DocDetailPage = lazy(() => import('../pages/DocDetailPage'));
|
||||
const BlogListPage = lazy(() => import('../pages/BlogListPage'));
|
||||
const BlogDetailPage = lazy(() => import('../pages/BlogDetailPage'));
|
||||
|
||||
function LoadingFallback() {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center">
|
||||
<div className="w-12 h-12 border-4 border-blue-600 border-t-transparent rounded-full animate-spin" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AppRouter() {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<App />} />
|
||||
<Route path="/auth/google/callback" element={<AuthCallbackPage />} />
|
||||
</Routes>
|
||||
<Suspense fallback={<LoadingFallback />}>
|
||||
<Routes>
|
||||
<Route element={<MarketingLayout />}>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/docs" element={<DocsListPage />} />
|
||||
<Route path="/docs/:slug" element={<DocDetailPage />} />
|
||||
<Route path="/blog" element={<BlogListPage />} />
|
||||
<Route path="/blog/:slug" element={<BlogDetailPage />} />
|
||||
</Route>
|
||||
<Route element={<AppLayout />}>
|
||||
<Route path="/app" element={<WorkspacePage />} />
|
||||
</Route>
|
||||
<Route path="/auth/google/callback" element={<AuthCallbackPage />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user