import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { createFileRoute } from '@tanstack/react-router'
import { GitBranch, RotateCcw, ShieldCheck, Tag } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { EmptyState } from '@/components/shared/empty-state'
import { ErrorState } from '@/components/shared/error-state'
import { LoadingCards } from '@/components/shared/loading'
import { RefreshButton } from '@/components/shared/refresh-button'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { api } from '@/lib/api-client'
import type { GitCommit } from '@/types'
export const Route = createFileRoute('/git')({ component: GitPage })
function GitPage() {
const { t } = useTranslation()
const queryClient = useQueryClient()
const [tagName, setTagName] = useState('')
const [restoreHash, setRestoreHash] = useState('')
const {
data: commits,
isLoading,
isError,
refetch,
isFetching,
} = useQuery({
queryKey: ['git-log'],
queryFn: async () => {
const res = await api.get<{ entries: GitCommit[] }>('/api/git/log')
return Array.isArray(res?.entries) ? res.entries : []
},
refetchInterval: 15000,
})
const { data: tags } = useQuery({
queryKey: ['git-tags'],
queryFn: async () => {
const res = await api.get<{ tags: string[] }>('/api/git/tags')
return Array.isArray(res?.tags) ? res.tags : []
},
refetchInterval: 15000,
})
const tagMutation = useMutation({
mutationFn: (tag: string) => api.post('/api/git/tags', { tag }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['git-tags'] })
setTagName('')
},
})
const restoreMutation = useMutation({
mutationFn: (hash: string) => api.post('/api/git/restore', { hash }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['git-log'] })
setRestoreHash('')
},
})
const verifyMutation = useMutation({
mutationFn: () => api.post('/api/git/verify'),
})
if (isLoading) return <LoadingCards count={4} />
if (isError) return <ErrorState onRetry={() => refetch()} />
const commitList = Array.isArray(commits) ? commits : []
const tagList = Array.isArray(tags) ? tags : []
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold">{t('git.title')}</h1>
<p className="text-muted-foreground">{t('git.subtitle')}</p>
</div>
<div className="flex gap-2">
<RefreshButton onClick={() => refetch()} isFetching={isFetching} />
<Button
variant="outline"
size="sm"
onClick={() => verifyMutation.mutate()}
disabled={verifyMutation.isPending}
>
<ShieldCheck className="h-4 w-4 mr-1" /> {t('git.verify')}
</Button>
</div>
</div>
{/* Tags */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Tag className="h-4 w-4" /> {t('git.tags')}
</CardTitle>
</CardHeader>
<CardContent>
<div className="flex gap-2 mb-3">
<Input
value={tagName}
onChange={(e) => setTagName(e.target.value)}
placeholder={t('git.tagNamePlaceholder')}
className="max-w-xs"
/>
<Button
size="sm"
onClick={() => tagMutation.mutate(tagName)}
disabled={!tagName.trim() || tagMutation.isPending}
>
{t('git.createTag')}
</Button>
</div>
{tagList.length > 0 ? (
<div className="flex gap-2 flex-wrap">
{tagList.map((t) => (
<Badge key={t} variant="outline">
{t}
</Badge>
))}
</div>
) : (
<p className="text-sm text-muted-foreground">{t('git.noTags')}</p>
)}
</CardContent>
</Card>
{/* Restore */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<RotateCcw className="h-4 w-4" /> {t('git.restore')}
</CardTitle>
</CardHeader>
<CardContent>
<div className="flex gap-2">
<Input
value={restoreHash}
onChange={(e) => setRestoreHash(e.target.value)}
placeholder={t('git.restorePlaceholder')}
className="max-w-xs font-mono"
/>
<Button
size="sm"
variant="destructive"
onClick={() => restoreMutation.mutate(restoreHash)}
disabled={!restoreHash.trim() || restoreMutation.isPending}
>
{t('git.restore')}
</Button>
</div>
</CardContent>
</Card>
{/* Log */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<GitBranch className="h-4 w-4" /> {t('git.commitLog')}
</CardTitle>
</CardHeader>
<CardContent>
{commitList.length === 0 ? (
<EmptyState
icon={<GitBranch className="h-8 w-8" />}
title={t('git.noCommits')}
description={t('git.noCommitsDescription')}
className="py-6"
/>
) : (
<div className="space-y-2">
{commitList.map((commit) => (
<div key={commit.hash} className="flex items-start gap-3 rounded-lg border p-3">
<code className="text-xs bg-muted px-1.5 py-0.5 rounded shrink-0">
{commit.hash.slice(0, 7)}
</code>
<div className="flex-1 min-w-0">
<p className="text-sm truncate">{commit.message}</p>
<p className="text-xs text-muted-foreground">
{commit.author} • {new Date(commit.timestamp).toLocaleString()}
</p>
</div>
</div>
))}
</div>
)}
</CardContent>
</Card>
</div>
)
}