2025-06-22 13:07:02 +02:00
|
|
|
import { TProject } from "@/types/project";
|
|
|
|
|
import { create } from "zustand";
|
2025-06-30 19:58:36 +02:00
|
|
|
import { storageService } from "@/lib/storage/storage-service";
|
|
|
|
|
import { toast } from "sonner";
|
2025-06-22 13:07:02 +02:00
|
|
|
|
|
|
|
|
interface ProjectStore {
|
|
|
|
|
activeProject: TProject | null;
|
2025-06-30 19:58:36 +02:00
|
|
|
savedProjects: TProject[];
|
|
|
|
|
isLoading: boolean;
|
|
|
|
|
isInitialized: boolean;
|
2025-06-22 13:07:02 +02:00
|
|
|
|
|
|
|
|
// Actions
|
2025-06-30 19:58:36 +02:00
|
|
|
createNewProject: (name: string) => Promise<string>;
|
|
|
|
|
loadProject: (id: string) => Promise<void>;
|
|
|
|
|
saveCurrentProject: () => Promise<void>;
|
|
|
|
|
loadAllProjects: () => Promise<void>;
|
|
|
|
|
deleteProject: (id: string) => Promise<void>;
|
2025-06-22 13:07:02 +02:00
|
|
|
closeProject: () => void;
|
2025-06-30 19:58:36 +02:00
|
|
|
renameProject: (projectId: string, name: string) => Promise<void>;
|
|
|
|
|
duplicateProject: (projectId: string) => Promise<string>;
|
2025-06-22 13:07:02 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-30 19:58:36 +02:00
|
|
|
export const useProjectStore = create<ProjectStore>((set, get) => ({
|
2025-06-22 13:07:02 +02:00
|
|
|
activeProject: null,
|
2025-06-30 19:58:36 +02:00
|
|
|
savedProjects: [],
|
|
|
|
|
isLoading: true,
|
|
|
|
|
isInitialized: false,
|
2025-06-22 13:07:02 +02:00
|
|
|
|
2025-06-30 19:58:36 +02:00
|
|
|
createNewProject: async (name: string) => {
|
2025-06-22 13:07:02 +02:00
|
|
|
const newProject: TProject = {
|
|
|
|
|
id: crypto.randomUUID(),
|
|
|
|
|
name,
|
2025-06-29 21:32:51 +02:00
|
|
|
thumbnail: "",
|
2025-06-22 13:07:02 +02:00
|
|
|
createdAt: new Date(),
|
|
|
|
|
updatedAt: new Date(),
|
|
|
|
|
};
|
2025-06-30 19:58:36 +02:00
|
|
|
|
2025-06-22 13:07:02 +02:00
|
|
|
set({ activeProject: newProject });
|
2025-06-30 19:58:36 +02:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await storageService.saveProject(newProject);
|
|
|
|
|
// Reload all projects to update the list
|
|
|
|
|
await get().loadAllProjects();
|
|
|
|
|
return newProject.id;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
toast.error("Failed to save new project");
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
loadProject: async (id: string) => {
|
|
|
|
|
if (!get().isInitialized) {
|
|
|
|
|
set({ isLoading: true });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const project = await storageService.loadProject(id);
|
|
|
|
|
if (project) {
|
|
|
|
|
set({ activeProject: project });
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Failed to load project:", error);
|
|
|
|
|
} finally {
|
|
|
|
|
set({ isLoading: false });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
saveCurrentProject: async () => {
|
|
|
|
|
const { activeProject } = get();
|
|
|
|
|
if (!activeProject) return;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await storageService.saveProject(activeProject);
|
|
|
|
|
await get().loadAllProjects(); // Refresh the list
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Failed to save project:", error);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
loadAllProjects: async () => {
|
|
|
|
|
if (!get().isInitialized) {
|
|
|
|
|
set({ isLoading: true });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const projects = await storageService.loadAllProjects();
|
|
|
|
|
set({ savedProjects: projects });
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Failed to load projects:", error);
|
|
|
|
|
} finally {
|
|
|
|
|
set({ isLoading: false, isInitialized: true });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
deleteProject: async (id: string) => {
|
|
|
|
|
try {
|
|
|
|
|
await storageService.deleteProject(id);
|
|
|
|
|
await get().loadAllProjects(); // Refresh the list
|
|
|
|
|
|
|
|
|
|
// If we deleted the active project, close it
|
|
|
|
|
const { activeProject } = get();
|
|
|
|
|
if (activeProject?.id === id) {
|
|
|
|
|
set({ activeProject: null });
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Failed to delete project:", error);
|
|
|
|
|
}
|
2025-06-22 13:07:02 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
closeProject: () => {
|
|
|
|
|
set({ activeProject: null });
|
|
|
|
|
},
|
2025-06-25 10:26:29 +05:30
|
|
|
|
2025-06-30 19:58:36 +02:00
|
|
|
renameProject: async (id: string, name: string) => {
|
|
|
|
|
const { savedProjects } = get();
|
|
|
|
|
|
|
|
|
|
// Find the project to rename
|
|
|
|
|
const projectToRename = savedProjects.find((p) => p.id === id);
|
|
|
|
|
if (!projectToRename) {
|
|
|
|
|
toast.error("Project not found", {
|
|
|
|
|
description: "Please try again",
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const updatedProject = {
|
|
|
|
|
...projectToRename,
|
|
|
|
|
name,
|
|
|
|
|
updatedAt: new Date(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Save to storage
|
|
|
|
|
await storageService.saveProject(updatedProject);
|
|
|
|
|
|
|
|
|
|
await get().loadAllProjects();
|
|
|
|
|
|
|
|
|
|
// Update activeProject if it's the same project
|
|
|
|
|
const { activeProject } = get();
|
|
|
|
|
if (activeProject?.id === id) {
|
|
|
|
|
set({ activeProject: updatedProject });
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Failed to rename project:", error);
|
|
|
|
|
toast.error("Failed to rename project", {
|
|
|
|
|
description:
|
|
|
|
|
error instanceof Error ? error.message : "Please try again",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
duplicateProject: async (projectId: string) => {
|
|
|
|
|
try {
|
|
|
|
|
const project = await storageService.loadProject(projectId);
|
|
|
|
|
if (!project) {
|
|
|
|
|
toast.error("Project not found", {
|
|
|
|
|
description: "Please try again",
|
|
|
|
|
});
|
|
|
|
|
throw new Error("Project not found");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { savedProjects } = get();
|
|
|
|
|
|
|
|
|
|
// Extract the base name (remove any existing numbering)
|
|
|
|
|
const numberMatch = project.name.match(/^\((\d+)\)\s+(.+)$/);
|
|
|
|
|
const baseName = numberMatch ? numberMatch[2] : project.name;
|
|
|
|
|
const existingNumbers: number[] = [];
|
|
|
|
|
|
|
|
|
|
// Check for pattern "(number) baseName" in existing projects
|
|
|
|
|
savedProjects.forEach((p) => {
|
|
|
|
|
const match = p.name.match(/^\((\d+)\)\s+(.+)$/);
|
|
|
|
|
if (match && match[2] === baseName) {
|
|
|
|
|
existingNumbers.push(parseInt(match[1], 10));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const nextNumber =
|
|
|
|
|
existingNumbers.length > 0 ? Math.max(...existingNumbers) + 1 : 1;
|
|
|
|
|
|
|
|
|
|
const newProject: TProject = {
|
|
|
|
|
id: crypto.randomUUID(),
|
|
|
|
|
name: `(${nextNumber}) ${baseName}`,
|
|
|
|
|
thumbnail: project.thumbnail,
|
|
|
|
|
createdAt: new Date(),
|
|
|
|
|
updatedAt: new Date(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await storageService.saveProject(newProject);
|
|
|
|
|
await get().loadAllProjects();
|
|
|
|
|
return newProject.id;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Failed to duplicate project:", error);
|
|
|
|
|
toast.error("Failed to duplicate project", {
|
|
|
|
|
description:
|
|
|
|
|
error instanceof Error ? error.message : "Please try again",
|
|
|
|
|
});
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
2025-06-25 10:26:29 +05:30
|
|
|
},
|
2025-06-22 13:07:02 +02:00
|
|
|
}));
|