070e960c05
- Extract use-projects hook for state management - Extract ProjectCard and ProjectDialog components - Slim ProjectsPage from 433 to ~100 lines Quality gates: tsc --noEmit passes, npm run build passes
156 lines
3.8 KiB
TypeScript
156 lines
3.8 KiB
TypeScript
import { useState } from "react";
|
|
import {
|
|
createProject,
|
|
deleteProject,
|
|
listProjects,
|
|
updateProject,
|
|
type ProjectCreateInput,
|
|
type ProjectUpdateInput,
|
|
} from "../api/projects";
|
|
import { deleteWorkspace, syncWorkspace } from "../api/workspaces";
|
|
import { useAsyncData } from "./use-async-data";
|
|
import type { ProjectWithRepos, WorkspaceSummary } from "../types";
|
|
|
|
type DialogMode = "none" | "create" | "edit";
|
|
|
|
export const useProjects = () => {
|
|
const {
|
|
data: projects,
|
|
status,
|
|
reload,
|
|
} = useAsyncData<ProjectWithRepos[]>(listProjects, []);
|
|
const [dialogMode, setDialogMode] = useState<DialogMode>("none");
|
|
const [editingProject, setEditingProject] = useState<ProjectWithRepos | null>(
|
|
null,
|
|
);
|
|
const [formName, setFormName] = useState("");
|
|
const [formDescription, setFormDescription] = useState("");
|
|
const [formError, setFormError] = useState<string | null>(null);
|
|
const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(null);
|
|
const [expandedProject, setExpandedProject] = useState<string | null>(null);
|
|
const [creatingWorkspace, setCreatingWorkspace] = useState<{
|
|
projectId: string;
|
|
repoId: string;
|
|
} | null>(null);
|
|
const [workspaceLoading, setWorkspaceLoading] = useState<string | null>(null);
|
|
|
|
const safeProjects = projects ?? [];
|
|
|
|
const openCreate = () => {
|
|
setFormName("");
|
|
setFormDescription("");
|
|
setFormError(null);
|
|
setEditingProject(null);
|
|
setDialogMode("create");
|
|
};
|
|
|
|
const openEdit = (project: ProjectWithRepos) => {
|
|
setFormName(project.name);
|
|
setFormDescription(project.description ?? "");
|
|
setFormError(null);
|
|
setEditingProject(project);
|
|
setDialogMode("edit");
|
|
};
|
|
|
|
const closeDialog = () => {
|
|
setDialogMode("none");
|
|
setEditingProject(null);
|
|
setFormError(null);
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setFormError(null);
|
|
|
|
if (!formName.trim()) {
|
|
setFormError("Project name is required");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (dialogMode === "create") {
|
|
const input: ProjectCreateInput = {
|
|
name: formName.trim(),
|
|
description: formDescription.trim() || null,
|
|
};
|
|
await createProject(input);
|
|
} else if (dialogMode === "edit" && editingProject) {
|
|
const input: ProjectUpdateInput = {
|
|
name: formName.trim(),
|
|
description: formDescription.trim() || null,
|
|
};
|
|
await updateProject(editingProject.id, input);
|
|
}
|
|
closeDialog();
|
|
reload();
|
|
} catch {
|
|
setFormError("Failed to save project");
|
|
}
|
|
};
|
|
|
|
const handleDelete = async (projectId: string) => {
|
|
try {
|
|
await deleteProject(projectId);
|
|
setDeleteConfirmId(null);
|
|
reload();
|
|
} catch {
|
|
setDeleteConfirmId(null);
|
|
}
|
|
};
|
|
|
|
const handleSyncWorkspace = async (
|
|
projectId: string,
|
|
repoId: string,
|
|
workspace: WorkspaceSummary,
|
|
) => {
|
|
setWorkspaceLoading(workspace.id);
|
|
try {
|
|
await syncWorkspace(projectId, repoId, workspace.id);
|
|
reload();
|
|
} catch (err) {
|
|
alert(err instanceof Error ? err.message : "Failed to sync workspace");
|
|
} finally {
|
|
setWorkspaceLoading(null);
|
|
}
|
|
};
|
|
|
|
const handleDeleteWorkspace = async (workspace: WorkspaceSummary) => {
|
|
if (!confirm(`Delete workspace "${workspace.name}"?`)) return;
|
|
setWorkspaceLoading(workspace.id);
|
|
try {
|
|
await deleteWorkspace(workspace.id);
|
|
reload();
|
|
} catch (err) {
|
|
alert(err instanceof Error ? err.message : "Failed to delete workspace");
|
|
} finally {
|
|
setWorkspaceLoading(null);
|
|
}
|
|
};
|
|
|
|
return {
|
|
projects: safeProjects,
|
|
status,
|
|
reload,
|
|
dialogMode,
|
|
formName,
|
|
setFormName,
|
|
formDescription,
|
|
setFormDescription,
|
|
formError,
|
|
deleteConfirmId,
|
|
setDeleteConfirmId,
|
|
expandedProject,
|
|
setExpandedProject,
|
|
creatingWorkspace,
|
|
setCreatingWorkspace,
|
|
workspaceLoading,
|
|
openCreate,
|
|
openEdit,
|
|
closeDialog,
|
|
handleSubmit,
|
|
handleDelete,
|
|
handleSyncWorkspace,
|
|
handleDeleteWorkspace,
|
|
};
|
|
};
|