mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
fix(ui): bulk update cache invalidation and model dropdown display (#633)
Fix two related issues with bulk model updates in Kanban view: 1. Bulk update now properly invalidates React Query cache - Changed handleBulkUpdate and bulk verify handler to call loadFeatures() - This ensures UI immediately reflects bulk changes 2. Custom provider models (GLM, MiniMax, etc.) now display correctly - Added fallback lookup in PhaseModelSelector by model ID - Updated mass-edit-dialog to track providerId after selection
This commit is contained in:
committed by
GitHub
parent
47a6033b43
commit
4f584f9a89
@@ -636,10 +636,8 @@ export function BoardView() {
|
|||||||
const result = await api.features.bulkUpdate(currentProject.path, featureIds, finalUpdates);
|
const result = await api.features.bulkUpdate(currentProject.path, featureIds, finalUpdates);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
// Update local state
|
// Invalidate React Query cache to refetch features with server-updated values
|
||||||
featureIds.forEach((featureId) => {
|
loadFeatures();
|
||||||
updateFeature(featureId, finalUpdates);
|
|
||||||
});
|
|
||||||
toast.success(`Updated ${result.updatedCount} features`);
|
toast.success(`Updated ${result.updatedCount} features`);
|
||||||
exitSelectionMode();
|
exitSelectionMode();
|
||||||
} else {
|
} else {
|
||||||
@@ -655,7 +653,7 @@ export function BoardView() {
|
|||||||
[
|
[
|
||||||
currentProject,
|
currentProject,
|
||||||
selectedFeatureIds,
|
selectedFeatureIds,
|
||||||
updateFeature,
|
loadFeatures,
|
||||||
exitSelectionMode,
|
exitSelectionMode,
|
||||||
getPrimaryWorktreeBranch,
|
getPrimaryWorktreeBranch,
|
||||||
addAndSelectWorktree,
|
addAndSelectWorktree,
|
||||||
@@ -783,10 +781,8 @@ export function BoardView() {
|
|||||||
const result = await api.features.bulkUpdate(currentProject.path, featureIds, updates);
|
const result = await api.features.bulkUpdate(currentProject.path, featureIds, updates);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
// Update local state for all features
|
// Invalidate React Query cache to refetch features with server-updated values
|
||||||
featureIds.forEach((featureId) => {
|
loadFeatures();
|
||||||
updateFeature(featureId, updates);
|
|
||||||
});
|
|
||||||
toast.success(`Verified ${result.updatedCount} features`);
|
toast.success(`Verified ${result.updatedCount} features`);
|
||||||
exitSelectionMode();
|
exitSelectionMode();
|
||||||
} else {
|
} else {
|
||||||
@@ -798,7 +794,7 @@ export function BoardView() {
|
|||||||
logger.error('Bulk verify failed:', error);
|
logger.error('Bulk verify failed:', error);
|
||||||
toast.error('Failed to verify features');
|
toast.error('Failed to verify features');
|
||||||
}
|
}
|
||||||
}, [currentProject, selectedFeatureIds, updateFeature, exitSelectionMode]);
|
}, [currentProject, selectedFeatureIds, loadFeatures, exitSelectionMode]);
|
||||||
|
|
||||||
// Handler for addressing PR comments - creates a feature and starts it automatically
|
// Handler for addressing PR comments - creates a feature and starts it automatically
|
||||||
const handleAddressPRComments = useCallback(
|
const handleAddressPRComments = useCallback(
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ export function MassEditDialog({
|
|||||||
// Field values
|
// Field values
|
||||||
const [model, setModel] = useState<ModelAlias>('claude-sonnet');
|
const [model, setModel] = useState<ModelAlias>('claude-sonnet');
|
||||||
const [thinkingLevel, setThinkingLevel] = useState<ThinkingLevel>('none');
|
const [thinkingLevel, setThinkingLevel] = useState<ThinkingLevel>('none');
|
||||||
|
const [providerId, setProviderId] = useState<string | undefined>(undefined);
|
||||||
const [planningMode, setPlanningMode] = useState<PlanningMode>('skip');
|
const [planningMode, setPlanningMode] = useState<PlanningMode>('skip');
|
||||||
const [requirePlanApproval, setRequirePlanApproval] = useState(false);
|
const [requirePlanApproval, setRequirePlanApproval] = useState(false);
|
||||||
const [priority, setPriority] = useState(2);
|
const [priority, setPriority] = useState(2);
|
||||||
@@ -162,6 +163,7 @@ export function MassEditDialog({
|
|||||||
});
|
});
|
||||||
setModel(getInitialValue(selectedFeatures, 'model', 'claude-sonnet') as ModelAlias);
|
setModel(getInitialValue(selectedFeatures, 'model', 'claude-sonnet') as ModelAlias);
|
||||||
setThinkingLevel(getInitialValue(selectedFeatures, 'thinkingLevel', 'none') as ThinkingLevel);
|
setThinkingLevel(getInitialValue(selectedFeatures, 'thinkingLevel', 'none') as ThinkingLevel);
|
||||||
|
setProviderId(undefined); // Features don't store providerId, but we track it after selection
|
||||||
setPlanningMode(getInitialValue(selectedFeatures, 'planningMode', 'skip') as PlanningMode);
|
setPlanningMode(getInitialValue(selectedFeatures, 'planningMode', 'skip') as PlanningMode);
|
||||||
setRequirePlanApproval(getInitialValue(selectedFeatures, 'requirePlanApproval', false));
|
setRequirePlanApproval(getInitialValue(selectedFeatures, 'requirePlanApproval', false));
|
||||||
setPriority(getInitialValue(selectedFeatures, 'priority', 2));
|
setPriority(getInitialValue(selectedFeatures, 'priority', 2));
|
||||||
@@ -226,10 +228,11 @@ export function MassEditDialog({
|
|||||||
Select a specific model configuration
|
Select a specific model configuration
|
||||||
</p>
|
</p>
|
||||||
<PhaseModelSelector
|
<PhaseModelSelector
|
||||||
value={{ model, thinkingLevel }}
|
value={{ model, thinkingLevel, providerId }}
|
||||||
onChange={(entry: PhaseModelEntry) => {
|
onChange={(entry: PhaseModelEntry) => {
|
||||||
setModel(entry.model as ModelAlias);
|
setModel(entry.model as ModelAlias);
|
||||||
setThinkingLevel(entry.thinkingLevel || 'none');
|
setThinkingLevel(entry.thinkingLevel || 'none');
|
||||||
|
setProviderId(entry.providerId);
|
||||||
// Auto-enable model and thinking level for apply state
|
// Auto-enable model and thinking level for apply state
|
||||||
setApplyState((prev) => ({
|
setApplyState((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
|
|||||||
@@ -415,6 +415,44 @@ export function PhaseModelSelector({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback: Check ClaudeCompatibleProvider models by model ID only (when providerId is not set)
|
||||||
|
// This handles cases where features store model ID but not providerId
|
||||||
|
for (const provider of enabledProviders) {
|
||||||
|
const providerModel = provider.models?.find((m) => m.id === selectedModel);
|
||||||
|
if (providerModel) {
|
||||||
|
// Count providers of same type to determine if we need provider name suffix
|
||||||
|
const sameTypeCount = enabledProviders.filter(
|
||||||
|
(p) => p.providerType === provider.providerType
|
||||||
|
).length;
|
||||||
|
const suffix = sameTypeCount > 1 ? ` (${provider.name})` : '';
|
||||||
|
// Add thinking level to label if not 'none'
|
||||||
|
const thinkingLabel =
|
||||||
|
selectedThinkingLevel !== 'none'
|
||||||
|
? ` (${THINKING_LEVEL_LABELS[selectedThinkingLevel]} Thinking)`
|
||||||
|
: '';
|
||||||
|
// Get icon based on provider type
|
||||||
|
const getIconForProviderType = () => {
|
||||||
|
switch (provider.providerType) {
|
||||||
|
case 'glm':
|
||||||
|
return GlmIcon;
|
||||||
|
case 'minimax':
|
||||||
|
return MiniMaxIcon;
|
||||||
|
case 'openrouter':
|
||||||
|
return OpenRouterIcon;
|
||||||
|
default:
|
||||||
|
return getProviderIconForModel(providerModel.id) || OpenRouterIcon;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
id: selectedModel,
|
||||||
|
label: `${providerModel.displayName}${suffix}${thinkingLabel}`,
|
||||||
|
description: provider.name,
|
||||||
|
provider: 'claude-compatible' as const,
|
||||||
|
icon: getIconForProviderType(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}, [
|
}, [
|
||||||
selectedModel,
|
selectedModel,
|
||||||
|
|||||||
Reference in New Issue
Block a user