feat: setup frontend project with bun and dynamic data fetching

- Create new frontend project directory with Next.js 15 + React 19
- Migrate from pnpm to bun for faster package management
- Add TanStack Query + Axios for dynamic data fetching
- Create comprehensive Makefile with development commands
- Setup API layer with query hooks and error handling
- Configure environment variables and bun settings
- Add TypeScript type checking and project documentation
- Update CLAUDE.md with bun-specific development workflow
This commit is contained in:
xujiang
2025-07-08 15:28:26 +08:00
parent d06caee35a
commit 3d197eb7e3
87 changed files with 8329 additions and 0 deletions

View File

@ -0,0 +1,81 @@
"use client"
import { Card, CardContent } from "@/components/ui/card"
import { Camera, MapPin, Calendar, Hash } from "lucide-react"
interface Photo {
id: number
src: string
title: string
description: string
category: string
tags: string[]
date: string
exif: {
camera: string
lens: string
settings: string
location: string
}
}
interface TimelineStatsProps {
photos: Photo[]
}
export function TimelineStats({ photos }: TimelineStatsProps) {
// Calculate statistics
const totalPhotos = photos.length
const uniqueLocations = new Set(photos.map((p) => p.exif.location)).size
const uniqueCameras = new Set(photos.map((p) => p.exif.camera)).size
const dateRange =
photos.length > 0
? {
start: new Date(Math.min(...photos.map((p) => new Date(p.date).getTime()))),
end: new Date(Math.max(...photos.map((p) => new Date(p.date).getTime()))),
}
: null
const stats = [
{
icon: Camera,
label: "总作品数",
value: totalPhotos,
color: "text-blue-600",
},
{
icon: MapPin,
label: "拍摄地点",
value: uniqueLocations,
color: "text-green-600",
},
{
icon: Hash,
label: "使用设备",
value: uniqueCameras,
color: "text-purple-600",
},
{
icon: Calendar,
label: "创作时间",
value: dateRange ? `${dateRange.start.getFullYear()}-${dateRange.end.getFullYear()}` : "N/A",
color: "text-orange-600",
},
]
return (
<div className="mb-12">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{stats.map((stat, index) => (
<Card key={index} className="border-0 shadow-sm">
<CardContent className="p-4 text-center">
<stat.icon className={`h-6 w-6 mx-auto mb-2 ${stat.color}`} />
<div className="text-2xl font-light text-gray-900 mb-1">{stat.value}</div>
<div className="text-sm text-gray-500">{stat.label}</div>
</CardContent>
</Card>
))}
</div>
</div>
)
}