應用 APP 功能實作

This commit is contained in:
2025-09-09 18:18:02 +08:00
parent 22bbe64349
commit 900e33aefa
22 changed files with 2745 additions and 242 deletions

View File

@@ -57,6 +57,23 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp
const { user, addToRecentApps, getAppLikes, incrementViewCount, getViewCount, getAppRating } = useAuth()
const [currentRating, setCurrentRating] = useState(getAppRating(app.id.toString()))
const [reviewCount, setReviewCount] = useState(0)
const [appStats, setAppStats] = useState({
basic: {
views: 0,
likes: 0,
rating: 0,
reviewCount: 0
},
usage: {
dailyUsers: 0,
weeklyUsers: 0,
monthlyUsers: 0,
totalSessions: 0,
topDepartments: [],
trendData: []
}
})
const [isLoadingStats, setIsLoadingStats] = useState(false)
// Date range for usage trends
const [startDate, setStartDate] = useState(() => {
@@ -88,6 +105,34 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp
setReviewCount(newReviewCount)
}
// 載入應用統計數據
const loadAppStats = async () => {
if (!app.id) return
setIsLoadingStats(true)
try {
const response = await fetch(`/api/apps/${app.id}/stats`)
const data = await response.json()
if (data.success) {
setAppStats(data.data)
setCurrentRating(data.data.basic.rating)
setReviewCount(data.data.basic.reviewCount)
}
} catch (error) {
console.error('載入應用統計數據錯誤:', error)
} finally {
setIsLoadingStats(false)
}
}
// 當對話框打開時載入統計數據
React.useEffect(() => {
if (open && app.id) {
loadAppStats()
}
}, [open, app.id])
const handleTryApp = () => {
if (user) {
addToRecentApps(app.id.toString())
@@ -245,19 +290,74 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp
</CardContent>
</Card>
<div className="flex items-center space-x-4">
<div className="flex items-center justify-between">
<FavoriteButton appId={app.id.toString()} size="default" showText={true} className="px-6" />
<Button
onClick={handleTryApp}
className="flex-1 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700"
className="bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700"
>
</Button>
<FavoriteButton appId={app.id.toString()} size="default" showText={true} className="px-6" />
</div>
</TabsContent>
<TabsContent value="statistics" className="space-y-6">
{/* Usage Overview */}
{/* 基本統計數據 */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
<Eye className="h-4 w-4 text-blue-500" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-blue-600">
{isLoadingStats ? '...' : appStats.basic.views}
</div>
<p className="text-xs text-muted-foreground"></p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
<Heart className="h-4 w-4 text-red-500" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-red-600">
{isLoadingStats ? '...' : appStats.basic.likes}
</div>
<p className="text-xs text-muted-foreground"></p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
<Star className="h-4 w-4 text-yellow-500" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-yellow-600">
{isLoadingStats ? '...' : appStats.basic.rating.toFixed(1)}
</div>
<p className="text-xs text-muted-foreground"></p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
<MessageSquare className="h-4 w-4 text-green-500" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-green-600">
{isLoadingStats ? '...' : appStats.basic.reviewCount}
</div>
<p className="text-xs text-muted-foreground"></p>
</CardContent>
</Card>
</div>
{/* 使用趨勢 */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
@@ -265,7 +365,9 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp
<Users className="h-4 w-4 text-blue-500" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{usageStats.dailyUsers}</div>
<div className="text-2xl font-bold">
{isLoadingStats ? '...' : appStats.usage.dailyUsers}
</div>
<p className="text-xs text-muted-foreground"></p>
</CardContent>
</Card>
@@ -276,7 +378,9 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp
<TrendingUp className="h-4 w-4 text-green-500" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{usageStats.weeklyUsers}</div>
<div className="text-2xl font-bold">
{isLoadingStats ? '...' : appStats.usage.weeklyUsers}
</div>
<p className="text-xs text-muted-foreground"></p>
</CardContent>
</Card>
@@ -287,7 +391,9 @@ export function AppDetailDialog({ open, onOpenChange, app }: AppDetailDialogProp
<BarChart3 className="h-4 w-4 text-purple-500" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{usageStats.totalSessions.toLocaleString()}</div>
<div className="text-2xl font-bold">
{isLoadingStats ? '...' : appStats.usage.totalSessions.toLocaleString()}
</div>
<p className="text-xs text-muted-foreground">使</p>
</CardContent>
</Card>