import { gitBranchList, gitUpdateRepo, projectExchangeName, projectExchangeVisibility, projectExchangeTitle } from "@/client"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Edit2, GitBranch, GitCommit, Globe, Loader2, Lock, } from "lucide-react"; import { useState, useEffect } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { toast } from "sonner"; import { useRepo } from "@/contexts"; import {getApiErrorMessage} from '@/lib/api-error'; export function RepoSettingsGeneral() { const repo = useRepo(); const navigate = useNavigate(); const queryClient = useQueryClient(); const { namespace, repoName } = useParams<{ namespace: string; repoName: string }>(); const ns = namespace!; const rn = repoName!; // Project name const [editingName, setEditingName] = useState(false); const [newName, setNewName] = useState(repo?.repo_name ?? ""); // Visibility const visibilityMutation = useMutation({ mutationFn: async (isPrivate: boolean) => { await projectExchangeVisibility({ path: { project_name: ns }, body: { is_public: !isPrivate }, }); }, onSuccess: () => { toast.success("Visibility updated"); queryClient.invalidateQueries({ queryKey: ["projectRepos", ns] }); }, onError: (err: unknown) => { toast.error(getApiErrorMessage(err, "Failed to update visibility")); }, }); // Default branch const [editingBranch, setEditingBranch] = useState(false); const [selectedBranch, setSelectedBranch] = useState(repo?.default_branch ?? ""); const { data: branchesData } = useQuery({ queryKey: ["repo-branches", ns, rn], queryFn: async () => { const resp = await gitBranchList({ path: { namespace: ns, repo: rn } }); return resp.data?.data ?? []; }, enabled: !!ns && !!rn && editingBranch, staleTime: 30 * 1000, }); // Description const [descText, setDescText] = useState(repo?.description ?? ""); const [descSaved, setDescSaved] = useState(true); useEffect(() => { if (repo) { setDescText(repo.description ?? ""); setDescSaved(true); } }, [repo?.description]); useEffect(() => { if (repo) setSelectedBranch(repo.default_branch ?? ""); }, [repo?.default_branch]); const nameMutation = useMutation({ mutationFn: async () => { if (!newName.trim() || newName === repo?.repo_name) return; await projectExchangeName({ path: { project_name: ns }, body: { name: newName.trim() }, }); }, onSuccess: () => { toast.success("Repository renamed"); queryClient.invalidateQueries({ queryKey: ["projectRepos", ns] }); navigate(`/repository/${newName}/${rn}/settings`); }, onError: (err: unknown) => { toast.error(getApiErrorMessage(err, "Failed to rename repository")); }, }); const branchMutation = useMutation({ mutationFn: async () => { if (!selectedBranch || selectedBranch === repo?.default_branch) return; await gitUpdateRepo({ path: { namespace: ns, repo: rn }, body: { default_branch: selectedBranch }, }); }, onSuccess: () => { toast.success("Default branch updated"); queryClient.invalidateQueries({ queryKey: ["projectRepos", ns] }); setEditingBranch(false); }, onError: (err: unknown) => { toast.error(getApiErrorMessage(err, "Failed to update default branch")); }, }); const descMutation = useMutation({ mutationFn: async () => { await projectExchangeTitle({ path: { project_name: ns }, body: { description: descText.trim() || null }, }); }, onSuccess: () => { toast.success("Description updated"); queryClient.invalidateQueries({ queryKey: ["projectRepos", ns] }); setDescSaved(true); }, onError: (err: unknown) => { toast.error(getApiErrorMessage(err, "Failed to update description")); }, }); const branches: Array<{ name: string }> = branchesData ?? []; if (!repo) return null; return (
{repo.repo_name}
{repo.namespace}
{(repo as any).branch_count ?? 0}
Project description shown on the overview page