supermemory/apps/web/hooks/use-project-mutations.ts
Prasanna721 15613c2421 feat: change "All Spaces" to "Nova Spaces" with multi-select support (#731)
### Implemented nova spaces multi-select

  ##### Summary
  - Renamed "All Spaces" to "Nova Spaces" - now filters to only nova content (sm_project_*)
  - Added multi-select spaces support via "Select Spaces" modal
  - Projects API now returns `{ nova, developer }` instead of `{ projects }`

  ##### Changes
  - `selectedProject` → `selectedProjects[]` (array-based selection)
  - New `SelectSpacesModal` component for picking multiple spaces
  - Selected spaces appear at top in modal
  - Search works by containerTag
  - Graphs filter by selected spaces
2026-02-13 21:34:58 +00:00

100 lines
2.6 KiB
TypeScript

"use client"
import { $fetch } from "@lib/api"
import { useMutation, useQueryClient } from "@tanstack/react-query"
import { toast } from "sonner"
import { useProject } from "@/stores"
import type { Project } from "@repo/lib/types"
export function useProjectMutations() {
const queryClient = useQueryClient()
const { selectedProjects, setSelectedProjects, setSelectedProject } =
useProject()
const createProjectMutation = useMutation({
mutationFn: async (input: string | { name: string; emoji?: string }) => {
const { name, emoji } =
typeof input === "string" ? { name: input, emoji: undefined } : input
const response = await $fetch("@post/projects", {
body: { name, emoji },
})
if (response.error) {
throw new Error(response.error?.message || "Failed to create project")
}
return response.data
},
onSuccess: (data) => {
toast.success("Project created successfully!")
queryClient.invalidateQueries({ queryKey: ["projects"] })
if (data?.containerTag) {
setSelectedProjects([data.containerTag])
}
},
onError: (error) => {
toast.error("Failed to create project", {
description: error instanceof Error ? error.message : "Unknown error",
})
},
})
const deleteProjectMutation = useMutation({
mutationFn: async ({
projectId,
action,
targetProjectId,
}: {
projectId: string
action: "move" | "delete"
targetProjectId?: string
}) => {
const response = await $fetch(`@delete/projects/${projectId}`, {
body: { action, targetProjectId },
})
if (response.error) {
throw new Error(response.error?.message || "Failed to delete project")
}
return response.data
},
onSuccess: (_, variables) => {
toast.success("Project deleted successfully")
queryClient.invalidateQueries({ queryKey: ["projects"] })
queryClient.invalidateQueries({ queryKey: ["container-tags"] })
const allProjects =
queryClient.getQueryData<Project[]>(["projects"]) || []
const deletedProject = allProjects.find(
(p) => p.id === variables.projectId,
)
if (
deletedProject?.containerTag &&
selectedProjects.includes(deletedProject.containerTag)
) {
setSelectedProjects(
selectedProjects.filter((tag) => tag !== deletedProject.containerTag),
)
}
},
onError: (error) => {
toast.error("Failed to delete project", {
description: error instanceof Error ? error.message : "Unknown error",
})
},
})
const switchProject = (containerTag: string) => {
setSelectedProject(containerTag)
toast.success("Project switched successfully")
}
return {
createProjectMutation,
deleteProjectMutation,
switchProject,
}
}