import * as React from "react";
import { Link, useNavigate } from "@tanstack/react-router";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { ChevronLeft } from "lucide-react";
import { toast } from "sonner";
import { SearchConsoleConnectionCard } from "@/client/features/gsc/SearchConsoleConnectionCard";
import { getStandardErrorMessage } from "@/client/lib/error-messages";
import {
clearLastProjectId,
getLastProjectId,
} from "@/client/lib/active-project";
import {
deleteProject,
getProjects,
updateProject,
} from "@/serverFunctions/projects";
import type { ProjectSummary } from "./types";
export function ProjectSettings({ projectId }: { projectId: string }) {
const projectsQuery = useQuery({
queryKey: ["projects"],
queryFn: () => getProjects(),
});
const projects = projectsQuery.data ?? [];
const project = projects.find((entry) => entry.id === projectId) ?? null;
if (!project) {
return (
);
}
return (
Projects
Project settings
{project.name}
{/* key resets the form's local state when switching between projects */}
1} />
);
}
function GeneralSection({ project }: { project: ProjectSummary }) {
const queryClient = useQueryClient();
const [name, setName] = React.useState(project.name);
const [domain, setDomain] = React.useState(project.domain ?? "");
const updateMutation = useMutation({
mutationFn: () =>
updateProject({
data: {
projectId: project.id,
name: name.trim(),
domain: domain.trim() || undefined,
},
}),
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ["projects"] });
toast.success("Project updated");
},
onError: (error) =>
toast.error(getStandardErrorMessage(error, "Failed to update project")),
});
const isDirty =
name.trim() !== project.name ||
(domain.trim() || "") !== (project.domain ?? "");
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
if (updateMutation.isPending) return;
if (!name.trim()) {
toast.error("Project name is required");
return;
}
updateMutation.mutate();
};
return (
);
}
function DangerSection({
project,
canDelete,
}: {
project: ProjectSummary;
canDelete: boolean;
}) {
const navigate = useNavigate();
const queryClient = useQueryClient();
const [confirming, setConfirming] = React.useState(false);
const deleteMutation = useMutation({
mutationFn: () => deleteProject({ data: { projectId: project.id } }),
onSuccess: async () => {
if (getLastProjectId() === project.id) clearLastProjectId();
await queryClient.invalidateQueries({ queryKey: ["projects"] });
toast.success("Project deleted");
// Re-resolve to a remaining project via the landing redirect.
void navigate({ to: "/" });
},
onError: (error) =>
toast.error(getStandardErrorMessage(error, "Failed to delete project")),
});
return (
Delete project
{confirming ? (
Deleting{" "}
{project.name}
{" "}
permanently removes its Search Console connection, rank tracking,
audits, and saved keywords. This can't be undone.
) : (
{canDelete
? "Permanently delete this project and all of its data."
: "You can't delete your only project."}
)}
);
}