"use client"; import { useCallback, useEffect, useState } from "react"; import { IconGitBranch, IconRefresh, IconArrowUp, IconArrowDown, IconCheck, IconAlertTriangle, } from "@tabler/icons-react"; import { Input } from "@kandev/ui/input"; import { Button } from "@kandev/ui/button"; import { Badge } from "@kandev/ui/badge"; import { toast } from "sonner"; import { useAppStore } from "@/components/state-provider"; import * as officeApi from "@/lib/api/domains/office-api"; import type { GitStatusData } from "@/lib/api/domains/office-api"; function useGitOperations(activeWorkspaceId: string) { const [gitStatus, setGitStatus] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const fetchStatus = useCallback(async () => { if (!activeWorkspaceId) return; try { const status = await officeApi.getGitStatus(activeWorkspaceId); setGitStatus(status); setError(null); } catch { setGitStatus(null); } }, [activeWorkspaceId]); useEffect(() => { fetchStatus(); }, [fetchStatus]); const runOp = useCallback( async (op: () => Promise, successMsg: string, failPrefix: string) => { if (!activeWorkspaceId) return; setLoading(true); setError(null); try { await op(); toast.success(successMsg); await fetchStatus(); } catch (err) { const msg = err instanceof Error ? err.message : `${failPrefix} failed`; setError(msg); toast.error(msg); } finally { setLoading(false); } }, [activeWorkspaceId, fetchStatus], ); return { gitStatus, loading, error, fetchStatus, runOp }; } export function GitSection() { const activeWorkspaceId = useAppStore((s) => s.workspaces?.activeId ?? ""); const [repoUrl, setRepoUrl] = useState(""); const [branch, setBranch] = useState("main"); const [commitMessage, setCommitMessage] = useState(""); const { gitStatus, loading, error, fetchStatus, runOp } = useGitOperations(activeWorkspaceId); const handleClone = useCallback( () => runOp( async () => { await officeApi.gitClone(activeWorkspaceId, { repoUrl, branch, workspaceName: activeWorkspaceId, }); }, "Repository cloned", "Clone", ), [activeWorkspaceId, repoUrl, branch, runOp], ); const handlePull = useCallback( () => runOp(() => officeApi.gitPull(activeWorkspaceId), "Pulled latest changes", "Pull"), [activeWorkspaceId, runOp], ); const handlePush = useCallback( () => runOp( async () => { await officeApi.gitPush(activeWorkspaceId, { message: commitMessage || "Update workspace configuration", }); setCommitMessage(""); }, "Changes pushed", "Push", ), [activeWorkspaceId, commitMessage, runOp], ); const isGit = gitStatus?.is_git ?? false; return (
{error && } {!isGit && ( )} {isGit && gitStatus && ( )}
); } function ErrorBanner({ message }: { message: string }) { return (

{message}

); } function CloneForm({ repoUrl, branch, loading, onRepoUrlChange, onBranchChange, onClone, }: { repoUrl: string; branch: string; loading: boolean; onRepoUrlChange: (v: string) => void; onBranchChange: (v: string) => void; onClone: () => void; }) { return (

Connect a git repository to version-control this workspace configuration.

onRepoUrlChange(e.target.value)} placeholder="https://github.com/org/config.git" className="mt-1" />
onBranchChange(e.target.value)} placeholder="main" className="mt-1" />
); } function GitStatusDisplay({ status, commitMessage, loading, onCommitMessageChange, onPull, onPush, onRefresh, }: { status: GitStatusData; commitMessage: string; loading: boolean; onCommitMessageChange: (v: string) => void; onPull: () => void; onPush: () => void; onRefresh: () => void; }) { return (
{status.branch} {status.is_dirty ? ( dirty ) : ( clean )}
{status.has_remote && (status.ahead > 0 || status.behind > 0) && (
{status.ahead > 0 && ( {status.ahead} ahead )} {status.behind > 0 && ( {status.behind} behind )}
)}
onCommitMessageChange(e.target.value)} placeholder="Update workspace configuration" className="mt-1" />
); }