import { useCallback, useEffect, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { getCommitDetail, getRepositoryHistory, type CommitDetail, type CommitHistoryEntry, type CommitHistoryResponse } from "../api/git_repositories"; import { EmptyState, ErrorState, LoadingState } from "../components/data-states"; import { Icon } from "../components/icon"; import { useAsyncData } from "../hooks/use-async-data"; export const GitHistoryPage = () => { const { projectId, repoId } = useParams<{ projectId: string; repoId: string }>(); const navigate = useNavigate(); const [selectedCommit, setSelectedCommit] = useState(null); const [commitDetail, setCommitDetail] = useState(null); const [selectedBranch, setSelectedBranch] = useState(""); const [detailStatus, setDetailStatus] = useState<"idle" | "loading" | "ready" | "error">("idle"); const { data: historyData, status, reload } = useAsyncData( async () => { if (!projectId || !repoId) return { commits: [], branches: [], tags: [] }; return await getRepositoryHistory(projectId, repoId, selectedBranch || undefined, 10000); }, [projectId, repoId, selectedBranch] ); // Auto-select first branch when data loads useEffect(() => { if (historyData?.branches.length && !selectedBranch) { setSelectedBranch(historyData.branches[0]); } }, [historyData?.branches, selectedBranch]); const handleCommitClick = async (hash: string) => { if (!projectId || !repoId) return; setSelectedCommit(hash); setDetailStatus("loading"); try { const detail = await getCommitDetail(projectId, repoId, hash); setCommitDetail(detail); setDetailStatus("ready"); } catch { setDetailStatus("error"); } }; const handleCloseDetail = () => { setSelectedCommit(null); setCommitDetail(null); setDetailStatus("idle"); }; const formatDate = (dateStr: string) => { return new Date(dateStr).toLocaleString(); }; if (status === "loading") { return (
); } if (status === "error") { return (
); } const commits = historyData?.commits ?? []; const branches = historyData?.branches ?? []; return (

Commit History

{commits.length} commits

{branches.length > 0 && ( )}
{commits.map((commit) => (
void handleCommitClick(commit.hash)} role="button" tabIndex={0} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { void handleCommitClick(commit.hash); } }} >
{commit.graph_symbol}
{commit.short_hash} {commit.refs.length > 0 && ( {commit.refs.map((ref) => ( {ref} ))} )}

{commit.message.split("\n")[0]}

{commit.author_name} {formatDate(commit.author_date)}
))}
{selectedCommit && (

Commit Details

{detailStatus === "loading" &&

Loading details...

} {detailStatus === "error" && (

Failed to load commit details

)} {detailStatus === "ready" && commitDetail && (

{commitDetail.hash}

{commitDetail.message}

Author

{commitDetail.author_name} <{commitDetail.author_email}>

{formatDate(commitDetail.author_date)}

Committer

{commitDetail.committer_name} <{commitDetail.committer_email}>

{formatDate(commitDetail.committer_date)}

Stats

{commitDetail.stats.files_changed} Files
+{commitDetail.stats.additions} Additions
-{commitDetail.stats.deletions} Deletions
{commitDetail.parents.length > 0 && (

Parents

{commitDetail.parents.map((parent) => ( {parent.substring(0, 8)} ))}
)} {commitDetail.diff && (

Diff

{commitDetail.diff}
)}
)}
)}
); };