mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
feat(ui): add React Query mutation hooks
- Add feature mutations (create, update, delete with optimistic updates) - Add auto-mode mutations (start, stop, approve plan) - Add worktree mutations (create, delete, checkout, switch branch) - Add settings mutations (update global/project, validate API keys) - Add GitHub mutations (create PR, validate PR) - Add cursor permissions mutations (apply profile, copy config) - Add spec mutations (generate, update, save) - Add pipeline mutations (toggle, update config) - Add session mutations with cache invalidation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
179
apps/ui/src/hooks/mutations/use-spec-mutations.ts
Normal file
179
apps/ui/src/hooks/mutations/use-spec-mutations.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Spec Mutation Hooks
|
||||
*
|
||||
* React Query mutations for spec operations like creating, regenerating, and saving.
|
||||
*/
|
||||
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { queryKeys } from '@/lib/query-keys';
|
||||
import { toast } from 'sonner';
|
||||
import type { FeatureCount } from '@/components/views/spec-view/types';
|
||||
|
||||
/**
|
||||
* Input for creating a spec
|
||||
*/
|
||||
interface CreateSpecInput {
|
||||
projectOverview: string;
|
||||
generateFeatures: boolean;
|
||||
analyzeProject: boolean;
|
||||
featureCount?: FeatureCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input for regenerating a spec
|
||||
*/
|
||||
interface RegenerateSpecInput {
|
||||
projectDefinition: string;
|
||||
generateFeatures: boolean;
|
||||
analyzeProject: boolean;
|
||||
featureCount?: FeatureCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new spec for a project
|
||||
*
|
||||
* This mutation triggers an async spec creation process. Progress and completion
|
||||
* are delivered via WebSocket events (spec_regeneration_progress, spec_regeneration_complete).
|
||||
*
|
||||
* @param projectPath - Path to the project
|
||||
* @returns Mutation for creating specs
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const createMutation = useCreateSpec(projectPath);
|
||||
*
|
||||
* createMutation.mutate({
|
||||
* projectOverview: 'A todo app with...',
|
||||
* generateFeatures: true,
|
||||
* analyzeProject: true,
|
||||
* featureCount: 50,
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function useCreateSpec(projectPath: string) {
|
||||
return useMutation({
|
||||
mutationFn: async (input: CreateSpecInput) => {
|
||||
const { projectOverview, generateFeatures, analyzeProject, featureCount } = input;
|
||||
|
||||
const api = getElectronAPI();
|
||||
if (!api.specRegeneration) {
|
||||
throw new Error('Spec regeneration API not available');
|
||||
}
|
||||
|
||||
const result = await api.specRegeneration.create(
|
||||
projectPath,
|
||||
projectOverview.trim(),
|
||||
generateFeatures,
|
||||
analyzeProject,
|
||||
generateFeatures ? featureCount : undefined
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to start spec creation');
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
// Toast/state updates are handled by the component since it tracks WebSocket events
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate an existing spec
|
||||
*
|
||||
* @param projectPath - Path to the project
|
||||
* @returns Mutation for regenerating specs
|
||||
*/
|
||||
export function useRegenerateSpec(projectPath: string) {
|
||||
return useMutation({
|
||||
mutationFn: async (input: RegenerateSpecInput) => {
|
||||
const { projectDefinition, generateFeatures, analyzeProject, featureCount } = input;
|
||||
|
||||
const api = getElectronAPI();
|
||||
if (!api.specRegeneration) {
|
||||
throw new Error('Spec regeneration API not available');
|
||||
}
|
||||
|
||||
const result = await api.specRegeneration.generate(
|
||||
projectPath,
|
||||
projectDefinition.trim(),
|
||||
generateFeatures,
|
||||
analyzeProject,
|
||||
generateFeatures ? featureCount : undefined
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to start spec regeneration');
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate features from existing spec
|
||||
*
|
||||
* @param projectPath - Path to the project
|
||||
* @returns Mutation for generating features
|
||||
*/
|
||||
export function useGenerateFeatures(projectPath: string) {
|
||||
return useMutation({
|
||||
mutationFn: async () => {
|
||||
const api = getElectronAPI();
|
||||
if (!api.specRegeneration) {
|
||||
throw new Error('Spec regeneration API not available');
|
||||
}
|
||||
|
||||
const result = await api.specRegeneration.generateFeatures(projectPath);
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || 'Failed to start feature generation');
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Save spec file content
|
||||
*
|
||||
* @param projectPath - Path to the project
|
||||
* @returns Mutation for saving spec
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const saveMutation = useSaveSpec(projectPath);
|
||||
*
|
||||
* saveMutation.mutate(specContent, {
|
||||
* onSuccess: () => setHasChanges(false),
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function useSaveSpec(projectPath: string) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (content: string) => {
|
||||
const api = getElectronAPI();
|
||||
|
||||
await api.writeFile(`${projectPath}/.automaker/app_spec.txt`, content);
|
||||
|
||||
return { content };
|
||||
},
|
||||
onSuccess: () => {
|
||||
// Invalidate spec file cache
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.spec.file(projectPath),
|
||||
});
|
||||
toast.success('Spec saved');
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error('Failed to save spec', {
|
||||
description: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user