mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 08:33:36 +00:00
- Extract board-view into organized subfolders following new pattern: - components/: kanban-card, kanban-column - dialogs/: all dialog and modal components (8 files) - hooks/: all board-specific hooks (10 files) - shared/: reusable components between dialogs (model-selector, etc.) - Rename all files to kebab-case convention - Add barrel exports (index.ts) for clean imports - Add docs/folder-pattern.md documenting the folder structure - Reduce board-view.tsx from ~3600 lines to ~490 lines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
167 lines
4.9 KiB
TypeScript
167 lines
4.9 KiB
TypeScript
import { useEffect, useRef } from "react";
|
|
import { getElectronAPI } from "@/lib/electron";
|
|
import { useAppStore } from "@/store/app-store";
|
|
import { useAutoMode } from "@/hooks/use-auto-mode";
|
|
|
|
interface UseBoardEffectsProps {
|
|
currentProject: { path: string; id: string } | null;
|
|
specCreatingForProject: string | null;
|
|
setSpecCreatingForProject: (path: string | null) => void;
|
|
setSuggestionsCount: (count: number) => void;
|
|
setFeatureSuggestions: (suggestions: any[]) => void;
|
|
setIsGeneratingSuggestions: (generating: boolean) => void;
|
|
checkContextExists: (featureId: string) => Promise<boolean>;
|
|
features: any[];
|
|
isLoading: boolean;
|
|
setFeaturesWithContext: (set: Set<string>) => void;
|
|
}
|
|
|
|
export function useBoardEffects({
|
|
currentProject,
|
|
specCreatingForProject,
|
|
setSpecCreatingForProject,
|
|
setSuggestionsCount,
|
|
setFeatureSuggestions,
|
|
setIsGeneratingSuggestions,
|
|
checkContextExists,
|
|
features,
|
|
isLoading,
|
|
setFeaturesWithContext,
|
|
}: UseBoardEffectsProps) {
|
|
const autoMode = useAutoMode();
|
|
|
|
// Make current project available globally for modal
|
|
useEffect(() => {
|
|
if (currentProject) {
|
|
(window as any).__currentProject = currentProject;
|
|
}
|
|
return () => {
|
|
(window as any).__currentProject = null;
|
|
};
|
|
}, [currentProject]);
|
|
|
|
// Listen for suggestions events to update count (persists even when dialog is closed)
|
|
useEffect(() => {
|
|
const api = getElectronAPI();
|
|
if (!api?.suggestions) return;
|
|
|
|
const unsubscribe = api.suggestions.onEvent((event) => {
|
|
if (event.type === "suggestions_complete" && event.suggestions) {
|
|
setSuggestionsCount(event.suggestions.length);
|
|
setFeatureSuggestions(event.suggestions);
|
|
setIsGeneratingSuggestions(false);
|
|
} else if (event.type === "suggestions_error") {
|
|
setIsGeneratingSuggestions(false);
|
|
}
|
|
});
|
|
|
|
return () => {
|
|
unsubscribe();
|
|
};
|
|
}, [setSuggestionsCount, setFeatureSuggestions, setIsGeneratingSuggestions]);
|
|
|
|
// Subscribe to spec regeneration events to clear creating state on completion
|
|
useEffect(() => {
|
|
const api = getElectronAPI();
|
|
if (!api.specRegeneration) return;
|
|
|
|
const unsubscribe = api.specRegeneration.onEvent((event) => {
|
|
console.log(
|
|
"[BoardView] Spec regeneration event:",
|
|
event.type,
|
|
"for project:",
|
|
event.projectPath
|
|
);
|
|
|
|
if (event.projectPath !== specCreatingForProject) {
|
|
return;
|
|
}
|
|
|
|
if (event.type === "spec_regeneration_complete") {
|
|
setSpecCreatingForProject(null);
|
|
} else if (event.type === "spec_regeneration_error") {
|
|
setSpecCreatingForProject(null);
|
|
}
|
|
});
|
|
|
|
return () => {
|
|
unsubscribe();
|
|
};
|
|
}, [specCreatingForProject, setSpecCreatingForProject]);
|
|
|
|
// Sync running tasks from electron backend on mount
|
|
useEffect(() => {
|
|
if (!currentProject) return;
|
|
|
|
const syncRunningTasks = async () => {
|
|
try {
|
|
const api = getElectronAPI();
|
|
if (!api?.autoMode?.status) return;
|
|
|
|
const status = await api.autoMode.status(currentProject.path);
|
|
if (status.success) {
|
|
const projectId = currentProject.id;
|
|
const { clearRunningTasks, addRunningTask, setAutoModeRunning } =
|
|
useAppStore.getState();
|
|
|
|
if (status.runningFeatures) {
|
|
console.log(
|
|
"[Board] Syncing running tasks from backend:",
|
|
status.runningFeatures
|
|
);
|
|
|
|
clearRunningTasks(projectId);
|
|
|
|
status.runningFeatures.forEach((featureId: string) => {
|
|
addRunningTask(projectId, featureId);
|
|
});
|
|
}
|
|
|
|
const isAutoModeRunning =
|
|
status.autoLoopRunning ?? status.isRunning ?? false;
|
|
console.log(
|
|
"[Board] Syncing auto mode running state:",
|
|
isAutoModeRunning
|
|
);
|
|
setAutoModeRunning(projectId, isAutoModeRunning);
|
|
}
|
|
} catch (error) {
|
|
console.error("[Board] Failed to sync running tasks:", error);
|
|
}
|
|
};
|
|
|
|
syncRunningTasks();
|
|
}, [currentProject]);
|
|
|
|
// Check which features have context files
|
|
useEffect(() => {
|
|
const checkAllContexts = async () => {
|
|
const featuresWithPotentialContext = features.filter(
|
|
(f) =>
|
|
f.status === "in_progress" ||
|
|
f.status === "waiting_approval" ||
|
|
f.status === "verified"
|
|
);
|
|
const contextChecks = await Promise.all(
|
|
featuresWithPotentialContext.map(async (f) => ({
|
|
id: f.id,
|
|
hasContext: await checkContextExists(f.id),
|
|
}))
|
|
);
|
|
|
|
const newSet = new Set<string>();
|
|
contextChecks.forEach(({ id, hasContext }) => {
|
|
if (hasContext) {
|
|
newSet.add(id);
|
|
}
|
|
});
|
|
|
|
setFeaturesWithContext(newSet);
|
|
};
|
|
|
|
if (features.length > 0 && !isLoading) {
|
|
checkAllContexts();
|
|
}
|
|
}, [features, isLoading, checkContextExists, setFeaturesWithContext]);
|
|
}
|