import { useTranslation } from "react-i18next";
import { I18nKey } from "#/i18n/declaration";
import { RefreshCw } from "lucide-react";
import type { GitSyncStatus } from "#/types/git-sync";
import { formatTimeDelta } from "#/utils/format-time-delta";
import { parseGitRemoteUrl } from "#/utils/parse-git-remote-url";
import { cn } from "#/utils/utils";
import GitBranchIcon from "#/icons/git-branch.svg?react";
import GlobeIcon from "#/icons/globe.svg?react";
import FolderIcon from "#/icons/folder.svg?react";
import ClockIcon from "#/icons/clock.svg?react";
import { SectionCard } from "#/components/features/automations/detail/section-card";
import { ConfigField } from "#/components/features/automations/detail/config-field";
import { BrandButton } from "#/components/features/settings/brand-button";
import { GitSyncStatusPill } from "./git-sync-status-pill";
import {
GitSyncActivityRow,
type GitSyncActivityState,
} from "./git-sync-activity-row";
/**
* A browsable URL for the configured remote, or `null` when there isn't one.
* A bare repo on disk is a valid sync target, so a local path -- and anything
* else that doesn't parse -- stays plain text rather than becoming a dead link.
*
* An http(s) remote is already browsable, so it is reused as-is apart from the
* `.git` suffix and any embedded credentials, which keeps forge-specific paths
* (Azure's `org/project/_git/repo`) and non-default ports intact. Other schemes
* -- `ssh://`, `git://`, `git@host:owner/repo` -- carry no browsable form, so
* they are rebuilt over https from the parsed host and repository.
*/
function browseUrlFor(repoUrl: string): string | null {
const parsed = parseGitRemoteUrl(repoUrl);
if (!parsed?.host || !parsed.repository) return null;
if (/^https?:\/\//i.test(parsed.url)) {
const url = new URL(parsed.url);
url.username = "";
url.password = "";
url.pathname = url.pathname.replace(/\.git$/, "");
return url.toString();
}
return `https://${parsed.host}/${parsed.repository}`;
}
interface GitSyncOverviewSectionProps {
status: GitSyncStatus;
onSyncNow: () => void;
isSyncing: boolean;
syncActivity: GitSyncActivityState;
syncStartedAt: string | null;
canManage: boolean;
}
export function GitSyncOverviewSection({
status,
onSyncNow,
isSyncing,
syncActivity,
syncStartedAt,
canManage,
}: GitSyncOverviewSectionProps) {
const { t } = useTranslation("openhands");
const repoHref = browseUrlFor(status.repo_url);
return (