diff --git a/apps/ui/src/components/views/board-view.tsx b/apps/ui/src/components/views/board-view.tsx index 86e300d4..a1e1fcec 100644 --- a/apps/ui/src/components/views/board-view.tsx +++ b/apps/ui/src/components/views/board-view.tsx @@ -882,7 +882,15 @@ export function BoardView() { // Capture existing feature IDs before adding const featuresBeforeIds = new Set(useAppStore.getState().features.map((f) => f.id)); - await handleAddFeature(featureData); + try { + await handleAddFeature(featureData); + } catch (error) { + logger.error('Failed to create PR comments feature:', error); + toast.error('Failed to create feature', { + description: error instanceof Error ? error.message : 'An error occurred', + }); + return; + } // Find the newly created feature by looking for an ID that wasn't in the original set const latestFeatures = useAppStore.getState().features; @@ -913,7 +921,7 @@ export function BoardView() { // Create the feature const featureData = { - title: `Resolve Merge Conflicts`, + title: `Resolve Merge Conflicts: ${remoteBranch} → ${worktree.branch}`, category: 'Maintenance', description, images: [], @@ -930,7 +938,15 @@ export function BoardView() { // Capture existing feature IDs before adding const featuresBeforeIds = new Set(useAppStore.getState().features.map((f) => f.id)); - await handleAddFeature(featureData); + try { + await handleAddFeature(featureData); + } catch (error) { + logger.error('Failed to create resolve conflicts feature:', error); + toast.error('Failed to create feature', { + description: error instanceof Error ? error.message : 'An error occurred', + }); + return; + } // Find the newly created feature by looking for an ID that wasn't in the original set const latestFeatures = useAppStore.getState().features; @@ -972,7 +988,15 @@ export function BoardView() { // Capture existing feature IDs before adding const featuresBeforeIds = new Set(useAppStore.getState().features.map((f) => f.id)); - await handleAddFeature(featureData); + try { + await handleAddFeature(featureData); + } catch (error) { + logger.error('Failed to create merge conflict resolution feature:', error); + toast.error('Failed to create feature', { + description: error instanceof Error ? error.message : 'An error occurred', + }); + return; + } // Find the newly created feature by looking for an ID that wasn't in the original set const latestFeatures = useAppStore.getState().features; @@ -995,7 +1019,15 @@ export function BoardView() { async (featureData: Parameters[0]) => { // Capture existing feature IDs before adding const featuresBeforeIds = new Set(useAppStore.getState().features.map((f) => f.id)); - await handleAddFeature(featureData); + try { + await handleAddFeature(featureData); + } catch (error) { + logger.error('Failed to create feature:', error); + toast.error('Failed to create feature', { + description: error instanceof Error ? error.message : 'An error occurred', + }); + return; + } // Find the newly created feature by looking for an ID that wasn't in the original set const latestFeatures = useAppStore.getState().features; diff --git a/apps/ui/src/components/views/board-view/hooks/use-board-actions.ts b/apps/ui/src/components/views/board-view/hooks/use-board-actions.ts index ebd80591..aedeebae 100644 --- a/apps/ui/src/components/views/board-view/hooks/use-board-actions.ts +++ b/apps/ui/src/components/views/board-view/hooks/use-board-actions.ts @@ -225,7 +225,13 @@ export function useBoardActions({ }; const createdFeature = addFeature(newFeatureData); // Must await to ensure feature exists on server before user can drag it - await persistFeatureCreate(createdFeature); + try { + await persistFeatureCreate(createdFeature); + } catch (error) { + // Remove the feature from state if server creation failed (e.g., duplicate title) + removeFeature(createdFeature.id); + throw error; + } saveCategory(featureData.category); // Handle child dependencies - update other features to depend on this new feature @@ -276,6 +282,7 @@ export function useBoardActions({ }, [ addFeature, + removeFeature, persistFeatureCreate, persistFeatureUpdate, updateFeature, diff --git a/apps/ui/src/components/views/board-view/hooks/use-board-persistence.ts b/apps/ui/src/components/views/board-view/hooks/use-board-persistence.ts index 6e5d23f5..48793f93 100644 --- a/apps/ui/src/components/views/board-view/hooks/use-board-persistence.ts +++ b/apps/ui/src/components/views/board-view/hooks/use-board-persistence.ts @@ -75,27 +75,25 @@ export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps ); // Persist feature creation to API + // Throws on failure so callers can handle it (e.g., remove the feature from state) const persistFeatureCreate = useCallback( async (feature: Feature) => { if (!currentProject) return; - try { - const api = getElectronAPI(); - if (!api.features) { - logger.error('Features API not available'); - return; - } + const api = getElectronAPI(); + if (!api.features) { + throw new Error('Features API not available'); + } - const result = await api.features.create(currentProject.path, feature as ApiFeature); - if (result.success && result.feature) { - updateFeature(result.feature.id, result.feature as Partial); - // Invalidate React Query cache to sync UI - queryClient.invalidateQueries({ - queryKey: queryKeys.features.all(currentProject.path), - }); - } - } catch (error) { - logger.error('Failed to persist feature creation:', error); + const result = await api.features.create(currentProject.path, feature as ApiFeature); + if (result.success && result.feature) { + updateFeature(result.feature.id, result.feature as Partial); + // Invalidate React Query cache to sync UI + queryClient.invalidateQueries({ + queryKey: queryKeys.features.all(currentProject.path), + }); + } else if (!result.success) { + throw new Error(result.error || 'Failed to create feature on server'); } }, [currentProject, updateFeature, queryClient] diff --git a/package-lock.json b/package-lock.json index 0c8786ba..c0218b2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "automaker", "version": "0.13.0", "hasInstallScript": true, + "license": "MIT", "workspaces": [ "apps/*", "libs/*" @@ -56,6 +57,7 @@ "yaml": "2.7.0" }, "devDependencies": { + "@playwright/test": "1.57.0", "@types/cookie": "0.6.0", "@types/cookie-parser": "1.4.10", "@types/cors": "2.8.19", @@ -11475,7 +11477,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11497,7 +11498,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11519,7 +11519,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11541,7 +11540,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11563,7 +11561,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11585,7 +11582,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11607,7 +11603,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11629,7 +11624,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11651,7 +11645,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11673,7 +11666,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11695,7 +11687,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" },