From e35ac5cace57f95aa558c8285afd2ad8b80c5a7f Mon Sep 17 00:00:00 2001 From: Iskander Date: Sun, 2 Feb 2025 22:11:20 +0100 Subject: [PATCH] Edit and delete commands and projects by id --- app/command.go | 14 ++- app/project.go | 64 +++++++++- core/command.go | 28 +++++ core/project.go | 163 ++++++++++++++++++++++++- database/queries/commands.sql | 9 ++ database/queries/projects.sql | 9 ++ database/sqlc/commands.sql.go | 27 ++++ database/sqlc/projects.sql.go | 33 +++++ frontend/src/routes/command-form.tsx | 9 +- frontend/src/routes/index.tsx | 14 +-- frontend/src/routes/project-form.tsx | 44 +++++-- frontend/src/sections/command-info.tsx | 4 +- frontend/src/sections/project-info.tsx | 35 +++--- frontend/src/stores/commandsStore.ts | 16 +-- frontend/src/stores/projectsStore.ts | 44 ++++--- 15 files changed, 438 insertions(+), 75 deletions(-) diff --git a/app/command.go b/app/command.go index 605a63a..a7a3121 100644 --- a/app/command.go +++ b/app/command.go @@ -2,6 +2,7 @@ package app import ( "fmt" + "sort" "github.com/iskandervdh/spinup/common" "github.com/iskandervdh/spinup/core" @@ -24,6 +25,11 @@ func (a *App) GetCommands() []core.Command { return nil } + // Sort commands by name + sort.Slice(commands, func(i, j int) bool { + return commands[i].Name < commands[j].Name + }) + return commands } @@ -44,14 +50,14 @@ func (a *App) AddCommand(name string, command string) error { return nil } -func (a *App) UpdateCommand(name string, command string) error { +func (a *App) UpdateCommand(id int64, name string, command string) error { err := a.core.FetchCommands() if err != nil { return fmt.Errorf("error getting commands config: %s", err) } - msg := a.core.UpdateCommand(name, command) + msg := a.core.UpdateCommandById(id, name, command) if _, ok := msg.(*common.ErrMsg); ok { fmt.Println(msg.GetText()) @@ -61,14 +67,14 @@ func (a *App) UpdateCommand(name string, command string) error { return nil } -func (a *App) RemoveCommand(name string) error { +func (a *App) RemoveCommand(id int64) error { err := a.core.FetchCommands() if err != nil { return fmt.Errorf("error getting commands config: %s", err) } - msg := a.core.RemoveCommand(name) + msg := a.core.RemoveCommandById(id) if _, ok := msg.(*common.ErrMsg); ok { fmt.Println(msg.GetText()) diff --git a/app/project.go b/app/project.go index 1c8c6ae..f00c4a1 100644 --- a/app/project.go +++ b/app/project.go @@ -3,6 +3,7 @@ package app import ( "fmt" "os" + "sort" "github.com/iskandervdh/spinup/common" "github.com/iskandervdh/spinup/core" @@ -30,10 +31,15 @@ func (a *App) GetProjects() []core.Project { return nil } + // Sort projects by name + sort.Slice(projects, func(i, j int) bool { + return projects[i].Name < projects[j].Name + }) + return projects } -func (a *App) AddProject(name string, port int64, commandNames []string) error { +func (a *App) AddProject(name string, port int64, commandNames []string, projectDir string) error { err := a.core.FetchCommands() if err != nil { @@ -53,10 +59,19 @@ func (a *App) AddProject(name string, port int64, commandNames []string) error { return fmt.Errorf("%s", msg.GetText()) } + if projectDir != "" { + msg = a.core.SetProjectDir(name, &projectDir) + + if _, ok := msg.(*common.ErrMsg); ok { + fmt.Println(msg.GetText()) + return fmt.Errorf("%s", msg.GetText()) + } + } + return nil } -func (a *App) UpdateProject(name string, port int64, commandNames []string) error { +func (a *App) UpdateProject(id int64, name string, port int64, commandNames []string, projectDir string) error { err := a.core.FetchCommands() if err != nil { @@ -69,17 +84,26 @@ func (a *App) UpdateProject(name string, port int64, commandNames []string) erro return fmt.Errorf("error getting projects config: %s", err) } - msg := a.core.UpdateProject(name, port, commandNames) + msg := a.core.UpdateProjectByID(id, name, port, commandNames) if _, ok := msg.(*common.ErrMsg); ok { fmt.Println(msg.GetText()) return fmt.Errorf("%s", msg.GetText()) } + if projectDir != "" { + msg = a.core.SetProjectDir(name, &projectDir) + + if _, ok := msg.(*common.ErrMsg); ok { + fmt.Println(msg.GetText()) + return fmt.Errorf("%s", msg.GetText()) + } + } + return nil } -func (a *App) RemoveProject(name string) error { +func (a *App) RemoveProject(id int64) error { err := a.core.FetchCommands() if err != nil { @@ -92,7 +116,7 @@ func (a *App) RemoveProject(name string) error { return fmt.Errorf("error getting projects config: %s", err) } - msg := a.core.RemoveProject(name) + msg := a.core.RemoveProjectById(id) if _, ok := msg.(*common.ErrMsg); ok { fmt.Println(msg.GetText()) @@ -102,7 +126,7 @@ func (a *App) RemoveProject(name string) error { return nil } -func (a *App) SelectProjectDirectory(projectName string, defaultDir string) error { +func (a *App) UpdateProjectDirectory(projectName string, defaultDir string) error { if defaultDir == "" { d, err := os.UserHomeDir() @@ -136,3 +160,31 @@ func (a *App) SelectProjectDirectory(projectName string, defaultDir string) erro return nil } + +func (a *App) SelectProjectDirectory(projectName string, defaultDir string) (string, error) { + if defaultDir == "" { + d, err := os.UserHomeDir() + + if err != nil { + defaultDir = "" + } else { + defaultDir = d + } + } + + dir, err := runtime.OpenDirectoryDialog(a.ctx, runtime.OpenDialogOptions{ + Title: "Select a directory for project " + projectName, + DefaultDirectory: defaultDir, + }) + + if err != nil { + fmt.Println("Error selecting directory:", err) + return "", err + } + + if dir == "" { + return "", fmt.Errorf("no directory selected") + } + + return dir, nil +} diff --git a/core/command.go b/core/command.go index cb9bf22..009da32 100644 --- a/core/command.go +++ b/core/command.go @@ -91,6 +91,20 @@ func (c *Core) RemoveCommand(name string) common.Msg { return common.NewSuccessMsg("Removed command '%s'", name) } +func (c *Core) RemoveCommandById(id int64) common.Msg { + if c.commands == nil { + return common.NewErrMsg("No commands found") + } + + err := c.dbQueries.DeleteCommandById(c.dbContext, id) + + if err != nil { + return common.NewErrMsg("Error deleting command: %s", err) + } + + return common.NewSuccessMsg("Removed command") +} + // Update the command with the given name to the given command string. func (c *Core) UpdateCommand(name string, command string) common.Msg { if c.commands == nil { @@ -109,6 +123,20 @@ func (c *Core) UpdateCommand(name string, command string) common.Msg { return common.NewSuccessMsg("Updated command '%s': %s", name, command) } +func (c *Core) UpdateCommandById(id int64, name string, command string) common.Msg { + err := c.dbQueries.UpdateCommandById(c.dbContext, sqlc.UpdateCommandByIdParams{ + ID: id, + Name: name, + Command: command, + }) + + if err != nil { + return common.NewErrMsg("Error updating command: %s", err) + } + + return common.NewSuccessMsg("Updated command '%s': %s", name, command) +} + // Rename the command with the given old name to the given new name. func (c *Core) RenameCommand(oldName string, newName string) common.Msg { if c.commands == nil { diff --git a/core/project.go b/core/project.go index f255819..21ca6d5 100644 --- a/core/project.go +++ b/core/project.go @@ -103,6 +103,22 @@ func (c *Core) ProjectExists(name string) (bool, Project) { return true, c.projects[index] } +func (c *Core) GetProjectById(id int64) (bool, Project) { + if c.projects == nil { + return false, Project{} + } + + index := slices.IndexFunc(c.projects, func(project Project) bool { + return project.ID == id + }) + + if index == -1 { + return false, Project{} + } + + return true, c.projects[index] +} + // Add a project with the given name, port and command names. func (c *Core) AddProject(name string, port int64, commandNames []string) common.Msg { // Check if commands exist @@ -177,6 +193,85 @@ func (c *Core) RemoveProject(name string) common.Msg { return common.NewSuccessMsg(fmt.Sprintf("Removed project '%s'", name)) } +func (c *Core) RemoveProjectById(projectID int64) common.Msg { + exists, project := c.GetProjectById(projectID) + + if !exists { + return common.NewErrMsg("Project with id %d does not exist, nothing to remove", projectID) + } + + err := c.config.RemoveNginxConfig(project.Name) + + if err != nil { + return common.NewErrMsg("Could not remove nginx config file: %s", err) + } + + err = c.dbQueries.DeleteProjectById(c.dbContext, projectID) + + if err != nil { + return common.NewErrMsg("Error removing project from database: %s", err) + } + + return common.NewSuccessMsg("Removed project '%s'", project.Name) +} + +func (c *Core) updateProjectCommands(projectID int64, commandNames []string) error { + currentProjectCommands, err := c.dbQueries.GetProjectCommands(c.dbContext, projectID) + + if err != nil { + return fmt.Errorf("error getting project commands: %s", err) + } + + // Remove commands that are not in the new list + for _, projectCommand := range currentProjectCommands { + if !slices.Contains(commandNames, projectCommand.Name) { + err := c.dbQueries.DeleteCommandsProjects(c.dbContext, sqlc.DeleteCommandsProjectsParams{ + CommandID: projectCommand.ID, + ProjectID: projectID, + }) + + if err != nil { + return fmt.Errorf("error removing command from project: %s", err) + } + } + } + + existingCommandNames := make([]string, len(currentProjectCommands)) + + for i, projectCommand := range currentProjectCommands { + existingCommandNames[i] = projectCommand.Name + } + + commands := make([]Command, 0, len(commandNames)) + + for _, commandName := range commandNames { + if slices.Contains(existingCommandNames, commandName) { + continue + } + + exists, command := c.CommandExists(commandName) + + if !exists { + return fmt.Errorf("command '%s' does not exist", commandName) + } + + commands = append(commands, command) + } + + for _, command := range commands { + err := c.dbQueries.CreateProjectCommand(c.dbContext, sqlc.CreateProjectCommandParams{ + ProjectID: projectID, + CommandID: command.ID, + }) + + if err != nil { + return fmt.Errorf("error adding command to project in database: %s", err) + } + } + + return nil +} + // Update the project with the given name to the given port and command names. func (c *Core) UpdateProject(name string, port int64, commandNames []string) common.Msg { exists, project := c.ProjectExists(name) @@ -211,7 +306,63 @@ func (c *Core) UpdateProject(name string, port int64, commandNames []string) com return common.NewErrMsg("Error trying to update nginx config file: %s", err) } - c.dbQueries.UpdateProject(c.dbContext, sqlc.UpdateProjectParams{ + err = c.dbQueries.UpdateProject(c.dbContext, sqlc.UpdateProjectParams{ + Name: name, + Port: port, + Dir: sql.NullString{ + String: project.Dir.String, + Valid: project.Dir.Valid, + }, + }) + + if err != nil { + return common.NewErrMsg("Error updating project in database: %s", err) + } + + err = c.updateProjectCommands(project.ID, commandNames) + + if err != nil { + return common.NewErrMsg("Error updating project commands: %s", err) + } + + return common.NewSuccessMsg("Updated project '%s' with domain '%s', port %d and commands %s", name, port, commandNames) +} + +func (c *Core) UpdateProjectByID(projectID int64, name string, port int64, commandNames []string) common.Msg { + exists, project := c.GetProjectById(projectID) + + if !exists { + return common.NewErrMsg("Project with id %d does not exist", projectID) + } + + // Check if commands exist + for _, commandName := range commandNames { + exists, _ := c.CommandExists(commandName) + + if !exists { + return common.NewErrMsg("Command '%s' does not exist", commandName) + } + } + + // Check if port is already in use by another project + for _, project := range c.projects { + if project.ID == projectID { + continue + } + + if project.Port == port { + return common.NewErrMsg("Project with port %d already exists: %s", port, project.Name) + } + } + + err := c.config.RenameNginxConfig(project.Name, name) + + if err != nil { + return common.NewErrMsg("Error trying to update nginx config file: %s", err) + } + + err = c.dbQueries.UpdateProjectById(c.dbContext, sqlc.UpdateProjectByIdParams{ + ID: projectID, Name: name, Port: port, Dir: sql.NullString{ @@ -220,6 +371,16 @@ func (c *Core) UpdateProject(name string, port int64, commandNames []string) com }, }) + if err != nil { + return common.NewErrMsg("Error updating project in database: %s", err) + } + + err = c.updateProjectCommands(projectID, commandNames) + + if err != nil { + return common.NewErrMsg("Error updating project commands: %s", err) + } + return common.NewSuccessMsg("Updated project '%s' with domain '%s', port %d and commands %s", name, port, commandNames) } diff --git a/database/queries/commands.sql b/database/queries/commands.sql index f5a712d..a89db0f 100644 --- a/database/queries/commands.sql +++ b/database/queries/commands.sql @@ -19,6 +19,11 @@ UPDATE commands SET command = ? WHERE name = ?; +-- name: UpdateCommandById :exec +UPDATE commands +SET name = ?, command = ? +WHERE id = ?; + -- name: RenameCommand :exec UPDATE commands SET name = ? @@ -27,3 +32,7 @@ WHERE name = ?; -- name: DeleteCommand :exec DELETE FROM commands WHERE name = ?; + +-- name: DeleteCommandById :exec +DELETE FROM commands +WHERE id = ?; diff --git a/database/queries/projects.sql b/database/queries/projects.sql index e626e43..5aeb259 100644 --- a/database/queries/projects.sql +++ b/database/queries/projects.sql @@ -60,6 +60,11 @@ UPDATE projects SET port = ?, dir = ? WHERE name = ?; +-- name: UpdateProjectById :exec +UPDATE projects +SET name = ?, port = ?, dir = ? +WHERE id = ?; + -- name: RenameProject :exec UPDATE projects SET name = ? @@ -68,3 +73,7 @@ WHERE name = ?; -- name: DeleteProject :exec DELETE FROM projects WHERE name = ?; + +-- name: DeleteProjectById :exec +DELETE FROM projects +WHERE id = ?; diff --git a/database/sqlc/commands.sql.go b/database/sqlc/commands.sql.go index 38b853a..ea8b0de 100644 --- a/database/sqlc/commands.sql.go +++ b/database/sqlc/commands.sql.go @@ -37,6 +37,16 @@ func (q *Queries) DeleteCommand(ctx context.Context, name string) error { return err } +const deleteCommandById = `-- name: DeleteCommandById :exec +DELETE FROM commands +WHERE id = ? +` + +func (q *Queries) DeleteCommandById(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, deleteCommandById, id) + return err +} + const getCommand = `-- name: GetCommand :one SELECT id, name, command FROM commands @@ -109,3 +119,20 @@ func (q *Queries) UpdateCommand(ctx context.Context, arg UpdateCommandParams) er _, err := q.db.ExecContext(ctx, updateCommand, arg.Command, arg.Name) return err } + +const updateCommandById = `-- name: UpdateCommandById :exec +UPDATE commands +SET name = ?, command = ? +WHERE id = ? +` + +type UpdateCommandByIdParams struct { + Name string + Command string + ID int64 +} + +func (q *Queries) UpdateCommandById(ctx context.Context, arg UpdateCommandByIdParams) error { + _, err := q.db.ExecContext(ctx, updateCommandById, arg.Name, arg.Command, arg.ID) + return err +} diff --git a/database/sqlc/projects.sql.go b/database/sqlc/projects.sql.go index 81c51df..f8e6b6d 100644 --- a/database/sqlc/projects.sql.go +++ b/database/sqlc/projects.sql.go @@ -91,6 +91,16 @@ func (q *Queries) DeleteProject(ctx context.Context, name string) error { return err } +const deleteProjectById = `-- name: DeleteProjectById :exec +DELETE FROM projects +WHERE id = ? +` + +func (q *Queries) DeleteProjectById(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, deleteProjectById, id) + return err +} + const getProject = `-- name: GetProject :one SELECT id, name, port, dir FROM projects @@ -283,3 +293,26 @@ func (q *Queries) UpdateProject(ctx context.Context, arg UpdateProjectParams) er _, err := q.db.ExecContext(ctx, updateProject, arg.Port, arg.Dir, arg.Name) return err } + +const updateProjectById = `-- name: UpdateProjectById :exec +UPDATE projects +SET name = ?, port = ?, dir = ? +WHERE id = ? +` + +type UpdateProjectByIdParams struct { + Name string + Port int64 + Dir sql.NullString + ID int64 +} + +func (q *Queries) UpdateProjectById(ctx context.Context, arg UpdateProjectByIdParams) error { + _, err := q.db.ExecContext(ctx, updateProjectById, + arg.Name, + arg.Port, + arg.Dir, + arg.ID, + ) + return err +} diff --git a/frontend/src/routes/command-form.tsx b/frontend/src/routes/command-form.tsx index 2c00f92..1aa3fce 100644 --- a/frontend/src/routes/command-form.tsx +++ b/frontend/src/routes/command-form.tsx @@ -13,13 +13,14 @@ export const Route = createFileRoute('/command-form')({ function CommandForm() { const navigate = useNavigate(); - const { commands, commandFormSubmit, editingCommand, setEditingCommand } = useCommandsStore(); + const { commands, commandFormSubmit, editingCommand } = useCommandsStore(); const [name, setName] = useState(''); const [command, setCommand] = useState(''); const pageTitle = useMemo( - () => (editingCommand ? `Edit Command "${editingCommand}"` : 'Add Command'), + () => + editingCommand ? `Edit Command "${commands?.find((c) => c.ID === editingCommand)?.Name || ''}"` : 'Add Command', [editingCommand] ); @@ -57,10 +58,10 @@ function CommandForm() { useEffect(() => { if (editingCommand) { - const command = commands?.find((c) => c.Name === editingCommand); + const command = commands?.find((c) => c.ID === editingCommand); if (command) { - setName(editingCommand); + setName(command.Name); setCommand(command.Command); } } diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx index 9635082..f2a5d91 100644 --- a/frontend/src/routes/index.tsx +++ b/frontend/src/routes/index.tsx @@ -1,16 +1,16 @@ -import { createFileRoute, useNavigate } from '@tanstack/react-router' -import { useEffect } from 'react' +import { createFileRoute, useNavigate } from '@tanstack/react-router'; +import { useEffect } from 'react'; export const Route = createFileRoute('/')({ component: Index, -}) +}); function Index() { - const navigate = useNavigate() + const navigate = useNavigate(); useEffect(() => { - navigate({ to: '/projects' }) - }, [navigate]) + navigate({ to: '/projects' }); + }, [navigate]); - return null + return null; } diff --git a/frontend/src/routes/project-form.tsx b/frontend/src/routes/project-form.tsx index 878ed9c..c17c6b0 100644 --- a/frontend/src/routes/project-form.tsx +++ b/frontend/src/routes/project-form.tsx @@ -8,6 +8,7 @@ import { Button } from '~/components/button'; import { SelectMultiple } from '~/components/select-multiple'; import toast from 'react-hot-toast'; import { createFileRoute, useNavigate } from '@tanstack/react-router'; +import { PencilSquareIcon } from '@heroicons/react/20/solid'; export const Route = createFileRoute('/project-form')({ component: ProjectFormPage, @@ -17,15 +18,17 @@ export function ProjectFormPage() { const navigate = useNavigate(); const { commands, setCommands } = useCommandsStore(); - const { projects, projectFormSubmit, editingProject } = useProjectsStore(); + const { projects, projectFormSubmit, editingProject, selectProjectDir } = useProjectsStore(); const [name, setName] = useState(''); const [port, setPort] = useState(3000); const [commandNames, setCommandNames] = useState([]); + const [projectDir, setProjectDir] = useState(null); const pageTitle = useMemo( - () => (editingProject ? `Edit Project "${editingProject}"` : 'Add Project'), - [editingProject] + () => + editingProject ? `Edit Project "${projects?.find((p) => p.ID === editingProject)?.Name || ''}"` : 'Add Project', + [projects, editingProject] ); const submitText = useMemo(() => (editingProject ? 'Save Project' : 'Add Project'), [editingProject]); @@ -35,21 +38,21 @@ export function ProjectFormPage() { e.preventDefault(); await toast - .promise(projectFormSubmit(name, port, commandNames), { + .promise(projectFormSubmit(name, port, commandNames, projectDir), { loading: editingProject ? 'Saving project...' : 'Creating project...', success: editingProject ? Project saved : Project created, - error: (err: any) => + error: (err: Error) => editingProject ? ( Failed to save project:
- {err} + {err.message}
) : ( Failed to create project:
- {err} + {err.message}
), }) @@ -57,21 +60,28 @@ export function ProjectFormPage() { navigate({ to: '/projects' }); }); }, - [name, port, commandNames, editingProject, projectFormSubmit] + [name, port, commandNames, projectDir, editingProject, projectFormSubmit] ); + const openSelectProjectDir = useCallback(() => { + selectProjectDir(name, projectDir).then(setProjectDir); + }, [name, projectDir]); + useEffect(() => { GetCommands().then(setCommands); }, []); useEffect(() => { if (editingProject) { - const project = projects?.find((p) => p.Name === editingProject); + const project = projects?.find((p) => p.ID === editingProject); if (project) { - setName(editingProject); + setName(project.Name); setPort(project.Port); - setCommandNames(project.Commands.map((c) => c.Name)); + setCommandNames( + project.Commands !== null ? project.Commands.map((c) => c.Name).sort((a, b) => a.localeCompare(b)) : [] + ); + setProjectDir(project.Dir.Valid ? project.Dir.String : null); } } }, [editingProject, setName, setPort, setCommandNames]); @@ -117,6 +127,18 @@ export function ProjectFormPage() { /> +
+ + +
+
{projectDir ?? 'Not set'}
+ + +
+
+ diff --git a/frontend/src/sections/command-info.tsx b/frontend/src/sections/command-info.tsx index a18d8f2..9d467ca 100644 --- a/frontend/src/sections/command-info.tsx +++ b/frontend/src/sections/command-info.tsx @@ -12,13 +12,13 @@ export function CommandInfo({ command }: { command: sqlc.Command }) { const { removeCommand, setEditingCommand } = useCommandsStore(); const edit = useCallback(() => { - setEditingCommand(command.Name); + setEditingCommand(command.ID); navigate({ to: '/command-form' }); }, [command.Name, setEditingCommand]); const remove = useCallback(async () => { if (confirm(`Are you sure you want to remove command "${command.Name}"?`)) { - await removeCommand(command.Name); + await removeCommand(command.ID); toast.success(Removed command "{command.Name}"); } diff --git a/frontend/src/sections/project-info.tsx b/frontend/src/sections/project-info.tsx index 8a88500..1286121 100644 --- a/frontend/src/sections/project-info.tsx +++ b/frontend/src/sections/project-info.tsx @@ -22,7 +22,7 @@ export function ProjectInfo({ project }: { project: core.Project }) { runningProjects, runProject, stopProject, - selectProjectDir, + updateProjectDir, removeProject, setCurrentProject, setEditingProject, @@ -37,12 +37,12 @@ export function ProjectInfo({ project }: { project: core.Project }) { const domainAliases = useMemo(() => project.DomainAliases?.map((da) => da.Value).join(', '), [project.DomainAliases]); const canRunProject = useMemo( - () => project.Dir.Valid && project.Commands.length > 0, + () => project.Dir.Valid && project.Commands !== null && project.Commands.length > 0, [project.Dir, project.Commands] ); const cannotRunProjectReason = useMemo(() => { if (!project.Dir.Valid) return 'Project directory is not set'; - if (project.Commands.length === 0) return 'No commands set for this project'; + if (project.Commands === null || project.Commands.length === 0) return 'No commands set for this project'; return ''; }, [project.Dir, project.Commands]); @@ -54,8 +54,8 @@ export function ProjectInfo({ project }: { project: core.Project }) { const openProjectDir = useCallback(() => project.Dir.Valid && BrowserOpenURL(project.Dir.String), [project.Dir]); - const openSelectProjectDir = useCallback( - () => selectProjectDir(project.Name, project.Dir.String), + const openUpdateProjectDir = useCallback( + () => updateProjectDir(project.Name, project.Dir.String), [project.Name, project.Dir.String] ); @@ -76,13 +76,13 @@ export function ProjectInfo({ project }: { project: core.Project }) { }, [project.Name, isRunning]); const edit = useCallback(() => { - setEditingProject(project.Name); + setEditingProject(project.ID); navigate({ to: '/project-form' }); }, [project.Name, setEditingProject]); const remove = useCallback(async () => { if (confirm(`Are you sure you want to remove project "${project.Name}"?`)) { - await removeProject(project.Name); + await removeProject(project.ID); toast.success(Removed project "{project.Name}"); } @@ -112,18 +112,19 @@ export function ProjectInfo({ project }: { project: core.Project }) {

{project.Name}

- - {isRunning ? ( ) : ( - + <> + + + )}
@@ -158,14 +159,10 @@ export function ProjectInfo({ project }: { project: core.Project }) { - - ) : (
-
diff --git a/frontend/src/stores/commandsStore.ts b/frontend/src/stores/commandsStore.ts index 236fd41..6273c2f 100644 --- a/frontend/src/stores/commandsStore.ts +++ b/frontend/src/stores/commandsStore.ts @@ -6,11 +6,11 @@ interface CommandsState { commands: Commands | null; setCommands: (commands: Commands) => void; - editingCommand: string | null; - setEditingCommand: (commandName: string | null) => void; + editingCommand: number | null; + setEditingCommand: (commandName: number | null) => void; commandFormSubmit: (commandName: string, command: string) => Promise; - removeCommand: (commandName: string) => Promise; + removeCommand: (commandID: number) => Promise; } export const useCommandsStore = create((set, get) => ({ @@ -21,8 +21,10 @@ export const useCommandsStore = create((set, get) => ({ setEditingCommand: (commandName) => set(() => ({ editingCommand: commandName })), commandFormSubmit: async (commandName, command) => { - if (get().editingCommand === commandName) { - await UpdateCommand(commandName, command); + const commandID = get().editingCommand; + + if (commandID !== null) { + await UpdateCommand(commandID, commandName, command); set(() => ({ editingCommand: null })); } else { await AddCommand(commandName, command); @@ -31,8 +33,8 @@ export const useCommandsStore = create((set, get) => ({ const commands = await GetCommands(); set(() => ({ commands })); }, - removeCommand: async (commandName) => { - await RemoveCommand(commandName); + removeCommand: async (commandID) => { + await RemoveCommand(commandID); const commands = await GetCommands(); set(() => ({ commands })); diff --git a/frontend/src/stores/projectsStore.ts b/frontend/src/stores/projectsStore.ts index 97c9078..887454a 100644 --- a/frontend/src/stores/projectsStore.ts +++ b/frontend/src/stores/projectsStore.ts @@ -3,26 +3,33 @@ import { Projects } from '~/types'; import { GetProjects, RunProject, - SelectProjectDirectory, + UpdateProjectDirectory, StopProject, AddProject, RemoveProject, UpdateProject, + SelectProjectDirectory, } from 'wjs/go/app/App'; interface ProjectsState { projects: Projects | null; setProjects: (projects: Projects) => void; - editingProject: string | null; - setEditingProject: (projectName: string | null) => void; + editingProject: number | null; + setEditingProject: (projectName: number | null) => void; runningProjects: string[]; runProject: (projectName: string) => Promise; stopProject: (projectName: string) => Promise; - selectProjectDir: (projectName: string, defaultDir: string | undefined) => Promise; - projectFormSubmit: (projectName: string, port: number, commandNames: string[]) => Promise; - removeProject: (projectName: string) => Promise; + updateProjectDir: (projectName: string, defaultDir: string | undefined) => Promise; + selectProjectDir: (projectName: string, defaultDir: string | null) => Promise; + projectFormSubmit: ( + projectName: string, + port: number, + commandNames: string[], + projectDir: string | null + ) => Promise; + removeProject: (projectID: number) => Promise; currentProject: string | null; setCurrentProject: (projectName: string | null) => void; @@ -46,25 +53,34 @@ export const useProjectsStore = create((set, get) => ({ await StopProject(projectName); }, - async selectProjectDir(projectName, defaultDir) { - await SelectProjectDirectory(projectName, defaultDir ?? ''); + async updateProjectDir(projectName, defaultDir) { + await UpdateProjectDirectory(projectName, defaultDir ?? ''); const projects = await GetProjects(); set(() => ({ projects })); }, - async projectFormSubmit(projectName, port, commandNames) { - if (get().editingProject === projectName) { - await UpdateProject(projectName, port, commandNames); + async selectProjectDir(projectName, defaultDir) { + return await SelectProjectDirectory(projectName, defaultDir ?? ''); + }, + async projectFormSubmit(projectName, port, commandNames, projectDir) { + if (projectName.includes(' ')) { + throw new Error('Project name can not include a space'); + } + + const projectID = get().editingProject; + + if (projectID !== null) { + await UpdateProject(projectID, projectName, port, commandNames, projectDir ?? ''); set(() => ({ editingProject: null })); } else { - await AddProject(projectName, port, commandNames); + await AddProject(projectName, port, commandNames, projectDir ?? ''); } const projects = await GetProjects(); set(() => ({ projects })); }, - async removeProject(projectName) { - await RemoveProject(projectName); + async removeProject(projectID) { + await RemoveProject(projectID); const projects = await GetProjects(); set(() => ({ projects }));