Files
automaker/apps/ui/src/components/views/spec-view.tsx
webdevcody 832d10e133 refactor: replace Loader2 with Spinner component across the application
This update standardizes the loading indicators by replacing all instances of Loader2 with the new Spinner component. The Spinner component provides a consistent look and feel for loading states throughout the UI, enhancing the user experience.

Changes include:
- Updated loading indicators in various components such as popovers, modals, and views.
- Ensured that the Spinner component is used with appropriate sizes for different contexts.

No functional changes were made; this is purely a visual and structural improvement.
2026-01-17 17:58:16 -05:00

170 lines
5.3 KiB
TypeScript

import { useState } from 'react';
import { useAppStore } from '@/store/app-store';
import { Spinner } from '@/components/ui/spinner';
// Extracted hooks
import { useSpecLoading, useSpecSave, useSpecGeneration } from './spec-view/hooks';
// Extracted components
import { SpecHeader, SpecEditor, SpecEmptyState } from './spec-view/components';
// Extracted dialogs
import { CreateSpecDialog, RegenerateSpecDialog } from './spec-view/dialogs';
export function SpecView() {
const { currentProject, appSpec } = useAppStore();
// Actions panel state (for tablet/mobile)
const [showActionsPanel, setShowActionsPanel] = useState(false);
// Loading state
const { isLoading, specExists, isGenerationRunning, loadSpec } = useSpecLoading();
// Save state
const { isSaving, hasChanges, saveSpec, handleChange, setHasChanges } = useSpecSave();
// Generation state and handlers
const {
// Dialog visibility
showCreateDialog,
setShowCreateDialog,
showRegenerateDialog,
setShowRegenerateDialog,
// Create state
projectOverview,
setProjectOverview,
isCreating,
generateFeatures,
setGenerateFeatures,
analyzeProjectOnCreate,
setAnalyzeProjectOnCreate,
featureCountOnCreate,
setFeatureCountOnCreate,
// Regenerate state
projectDefinition,
setProjectDefinition,
isRegenerating,
generateFeaturesOnRegenerate,
setGenerateFeaturesOnRegenerate,
analyzeProjectOnRegenerate,
setAnalyzeProjectOnRegenerate,
featureCountOnRegenerate,
setFeatureCountOnRegenerate,
// Feature generation
isGeneratingFeatures,
// Sync
isSyncing,
// Status
currentPhase,
errorMessage,
// Handlers
handleCreateSpec,
handleRegenerate,
handleGenerateFeatures,
handleSync,
} = useSpecGeneration({ loadSpec });
// Reset hasChanges when spec is reloaded
// (This is needed because loadSpec updates appSpec in the store)
// No project selected
if (!currentProject) {
return (
<div className="flex-1 flex items-center justify-center" data-testid="spec-view-no-project">
<p className="text-muted-foreground">No project selected</p>
</div>
);
}
// Loading state
if (isLoading) {
return (
<div className="flex-1 flex items-center justify-center" data-testid="spec-view-loading">
<Spinner size="lg" />
</div>
);
}
// Empty state - only show when spec doesn't exist AND no generation is running
// If generation is running but no spec exists, show the generating UI
if (!specExists) {
// If generation is running (from loading hook check), ensure we show the generating UI
const showAsGenerating = isCreating || isGenerationRunning;
return (
<>
<SpecEmptyState
projectPath={currentProject.path}
isCreating={showAsGenerating}
isRegenerating={isRegenerating || isGenerationRunning}
currentPhase={currentPhase || (isGenerationRunning ? 'initialization' : '')}
errorMessage={errorMessage}
onCreateClick={() => setShowCreateDialog(true)}
/>
<CreateSpecDialog
open={showCreateDialog}
onOpenChange={setShowCreateDialog}
projectOverview={projectOverview}
onProjectOverviewChange={setProjectOverview}
generateFeatures={generateFeatures}
onGenerateFeaturesChange={setGenerateFeatures}
analyzeProject={analyzeProjectOnCreate}
onAnalyzeProjectChange={setAnalyzeProjectOnCreate}
featureCount={featureCountOnCreate}
onFeatureCountChange={setFeatureCountOnCreate}
onCreateSpec={handleCreateSpec}
isCreatingSpec={isCreating}
/>
</>
);
}
// Main view - spec exists
return (
<div className="flex-1 flex flex-col overflow-hidden content-bg" data-testid="spec-view">
<SpecHeader
projectPath={currentProject.path}
isRegenerating={isRegenerating || isGenerationRunning}
isCreating={isCreating}
isGeneratingFeatures={isGeneratingFeatures}
isSyncing={isSyncing}
isSaving={isSaving}
hasChanges={hasChanges}
currentPhase={currentPhase || (isGenerationRunning ? 'working' : '')}
errorMessage={errorMessage}
onRegenerateClick={() => setShowRegenerateDialog(true)}
onGenerateFeaturesClick={handleGenerateFeatures}
onSyncClick={handleSync}
onSaveClick={saveSpec}
showActionsPanel={showActionsPanel}
onToggleActionsPanel={() => setShowActionsPanel(!showActionsPanel)}
/>
<SpecEditor value={appSpec} onChange={handleChange} />
<RegenerateSpecDialog
open={showRegenerateDialog}
onOpenChange={setShowRegenerateDialog}
projectDefinition={projectDefinition}
onProjectDefinitionChange={setProjectDefinition}
generateFeatures={generateFeaturesOnRegenerate}
onGenerateFeaturesChange={setGenerateFeaturesOnRegenerate}
analyzeProject={analyzeProjectOnRegenerate}
onAnalyzeProjectChange={setAnalyzeProjectOnRegenerate}
featureCount={featureCountOnRegenerate}
onFeatureCountChange={setFeatureCountOnRegenerate}
onRegenerate={handleRegenerate}
isRegenerating={isRegenerating}
isGeneratingFeatures={isGeneratingFeatures}
/>
</div>
);
}