fix: resolve hydration mismatch error and improve project setup

- Fix React hydration mismatch in ThemeProvider with mounted state check
- Update layout.tsx to use light theme by default instead of system
- Optimize photo filtering with useMemo in page.tsx
- Add Express mock API for development
- Update CLAUDE.md with comprehensive project documentation
- Create backend/ and admin/ directories for future development
This commit is contained in:
xujiang
2025-07-08 17:34:16 +08:00
parent 3d197eb7e3
commit 8c5c9a5f8e
6 changed files with 285 additions and 12 deletions

View File

@ -24,8 +24,8 @@ export default function RootLayout({
<body className={inter.className}>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
defaultTheme="light"
enableSystem={false}
disableTransitionOnChange
>
<QueryProvider>

View File

@ -15,7 +15,6 @@ import { useToast } from "@/components/ui/use-toast"
export default function HomePage() {
const { data: photos = [], isLoading, error } = usePhotos()
const { toast } = useToast()
const [filteredPhotos, setFilteredPhotos] = useState<Photo[]>([])
const [selectedPhoto, setSelectedPhoto] = useState<Photo | null>(null)
const [activeCategory, setActiveCategory] = useState("all")
const [activeTab, setActiveTab] = useState("gallery")
@ -30,17 +29,15 @@ export default function HomePage() {
}
}, [error, toast])
useEffect(() => {
setFilteredPhotos(photos)
}, [photos])
const filteredPhotos = useMemo(() => {
if (activeCategory === "all") {
return photos
}
return photos.filter((photo) => photo.category === activeCategory)
}, [photos, activeCategory])
const handleFilter = (category: string) => {
setActiveCategory(category)
if (category === "all") {
setFilteredPhotos(photos)
} else {
setFilteredPhotos(photos.filter((photo) => photo.category === category))
}
}
const handlePhotoClick = (photo: any) => {
@ -72,7 +69,6 @@ export default function HomePage() {
// Reset filters when switching tabs
if (tab === "timeline") {
setActiveCategory("all")
setFilteredPhotos(photos)
}
}

View File

@ -7,5 +7,15 @@ import {
} from 'next-themes'
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
const [mounted, setMounted] = React.useState(false)
React.useEffect(() => {
setMounted(true)
}, [])
if (!mounted) {
return <>{children}</>
}
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}

81
frontend/mock-api.js Normal file
View File

@ -0,0 +1,81 @@
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.use(express.json());
// 模拟照片数据
const photos = [
{
id: 1,
src: '/placeholder.jpg',
title: '城市夜景',
description: '繁华都市中的宁静夜晚',
category: 'city',
tags: ['夜景', '城市', '建筑'],
date: '2024-01-15',
exif: {
camera: 'Canon EOS R5',
lens: '24-70mm f/2.8',
settings: 'f/4, 1/60s, ISO 800',
location: '上海外滩'
}
},
{
id: 2,
src: '/placeholder.jpg',
title: '自然风光',
description: '山川河流间的诗意景色',
category: 'nature',
tags: ['风景', '自然', '山水'],
date: '2024-01-10',
exif: {
camera: 'Sony A7R IV',
lens: '16-35mm f/2.8',
settings: 'f/8, 1/125s, ISO 200',
location: '张家界'
}
},
{
id: 3,
src: '/placeholder.jpg',
title: '人像摄影',
description: '捕捉真实的情感瞬间',
category: 'portrait',
tags: ['人像', '情感', '生活'],
date: '2024-01-05',
exif: {
camera: 'Nikon D850',
lens: '85mm f/1.4',
settings: 'f/2.8, 1/200s, ISO 400',
location: '工作室'
}
}
];
// 获取所有照片
app.get('/api/photos', (req, res) => {
res.json(photos);
});
// 获取单张照片
app.get('/api/photos/:id', (req, res) => {
const photo = photos.find(p => p.id === parseInt(req.params.id));
if (photo) {
res.json(photo);
} else {
res.status(404).json({ error: 'Photo not found' });
}
});
// 获取分类
app.get('/api/categories', (req, res) => {
const categories = [...new Set(photos.map(p => p.category))];
res.json(categories);
});
const PORT = 3001;
app.listen(PORT, () => {
console.log(`Mock API server running on http://localhost:${PORT}`);
});

View File

@ -45,8 +45,10 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "1.0.4",
"cors": "^2.8.5",
"date-fns": "4.1.0",
"embla-carousel-react": "8.5.1",
"express": "^5.1.0",
"input-otp": "1.4.1",
"lucide-react": "^0.454.0",
"next": "15.2.4",