mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
feat(ui): move export/import features from board header to project settings
Relocate the export and import features functionality from the board header dropdown menu to a new "Data" section in project settings for better UX. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -55,8 +55,6 @@ import {
|
||||
FollowUpDialog,
|
||||
PlanApprovalDialog,
|
||||
PullResolveConflictsDialog,
|
||||
ExportFeaturesDialog,
|
||||
ImportFeaturesDialog,
|
||||
} from './board-view/dialogs';
|
||||
import type { DependencyLinkType } from './board-view/dialogs';
|
||||
import { PipelineSettingsDialog } from './board-view/dialogs/pipeline-settings-dialog';
|
||||
@@ -236,11 +234,6 @@ export function BoardView() {
|
||||
} = useSelectionMode();
|
||||
const [showMassEditDialog, setShowMassEditDialog] = useState(false);
|
||||
|
||||
// Export/Import dialog states
|
||||
const [showExportDialog, setShowExportDialog] = useState(false);
|
||||
const [showImportDialog, setShowImportDialog] = useState(false);
|
||||
const [exportFeatureIds, setExportFeatureIds] = useState<string[] | undefined>(undefined);
|
||||
|
||||
// View mode state (kanban vs list)
|
||||
const { viewMode, setViewMode, isListView, sortConfig, setSortColumn } = useListViewState();
|
||||
|
||||
@@ -1316,11 +1309,6 @@ export function BoardView() {
|
||||
isCreatingSpec={isCreatingSpec}
|
||||
creatingSpecProjectPath={creatingSpecProjectPath}
|
||||
onShowBoardBackground={() => setShowBoardBackgroundModal(true)}
|
||||
onExportFeatures={() => {
|
||||
setExportFeatureIds(undefined); // Export all features
|
||||
setShowExportDialog(true);
|
||||
}}
|
||||
onImportFeatures={() => setShowImportDialog(true)}
|
||||
viewMode={viewMode}
|
||||
onViewModeChange={setViewMode}
|
||||
/>
|
||||
@@ -1798,26 +1786,6 @@ export function BoardView() {
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Export Features Dialog */}
|
||||
<ExportFeaturesDialog
|
||||
open={showExportDialog}
|
||||
onOpenChange={setShowExportDialog}
|
||||
projectPath={currentProject.path}
|
||||
features={hookFeatures}
|
||||
selectedFeatureIds={exportFeatureIds}
|
||||
/>
|
||||
|
||||
{/* Import Features Dialog */}
|
||||
<ImportFeaturesDialog
|
||||
open={showImportDialog}
|
||||
onOpenChange={setShowImportDialog}
|
||||
projectPath={currentProject.path}
|
||||
categorySuggestions={persistedCategories}
|
||||
onImportComplete={() => {
|
||||
loadFeatures();
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Init Script Indicator - floating overlay for worktree init script status */}
|
||||
{getShowInitScriptIndicator(currentProject.path) && (
|
||||
<InitScriptIndicator projectPath={currentProject.path} />
|
||||
|
||||
@@ -1,27 +1,13 @@
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuSeparator,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { ImageIcon, MoreHorizontal, Download, Upload } from 'lucide-react';
|
||||
import { ImageIcon } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface BoardControlsProps {
|
||||
isMounted: boolean;
|
||||
onShowBoardBackground: () => void;
|
||||
onExportFeatures?: () => void;
|
||||
onImportFeatures?: () => void;
|
||||
}
|
||||
|
||||
export function BoardControls({
|
||||
isMounted,
|
||||
onShowBoardBackground,
|
||||
onExportFeatures,
|
||||
onImportFeatures,
|
||||
}: BoardControlsProps) {
|
||||
export function BoardControls({ isMounted, onShowBoardBackground }: BoardControlsProps) {
|
||||
if (!isMounted) return null;
|
||||
|
||||
const buttonClass = cn(
|
||||
@@ -49,32 +35,6 @@ export function BoardControls({
|
||||
<p>Board Background Settings</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
{/* More Options Menu */}
|
||||
<DropdownMenu>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button className={buttonClass} data-testid="board-more-options-button">
|
||||
<MoreHorizontal className="w-4 h-4" />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>More Options</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<DropdownMenuContent align="end" className="w-48">
|
||||
<DropdownMenuItem onClick={onExportFeatures} data-testid="export-features-menu-item">
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
Export Features
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={onImportFeatures} data-testid="import-features-menu-item">
|
||||
<Upload className="w-4 h-4 mr-2" />
|
||||
Import Features
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
);
|
||||
|
||||
@@ -35,8 +35,6 @@ interface BoardHeaderProps {
|
||||
creatingSpecProjectPath?: string;
|
||||
// Board controls props
|
||||
onShowBoardBackground: () => void;
|
||||
onExportFeatures?: () => void;
|
||||
onImportFeatures?: () => void;
|
||||
// View toggle props
|
||||
viewMode: ViewMode;
|
||||
onViewModeChange: (mode: ViewMode) => void;
|
||||
@@ -62,8 +60,6 @@ export function BoardHeader({
|
||||
isCreatingSpec,
|
||||
creatingSpecProjectPath,
|
||||
onShowBoardBackground,
|
||||
onExportFeatures,
|
||||
onImportFeatures,
|
||||
viewMode,
|
||||
onViewModeChange,
|
||||
}: BoardHeaderProps) {
|
||||
@@ -128,12 +124,7 @@ export function BoardHeader({
|
||||
currentProjectPath={projectPath}
|
||||
/>
|
||||
{isMounted && <ViewToggle viewMode={viewMode} onViewModeChange={onViewModeChange} />}
|
||||
<BoardControls
|
||||
isMounted={isMounted}
|
||||
onShowBoardBackground={onShowBoardBackground}
|
||||
onExportFeatures={onExportFeatures}
|
||||
onImportFeatures={onImportFeatures}
|
||||
/>
|
||||
<BoardControls isMounted={isMounted} onShowBoardBackground={onShowBoardBackground} />
|
||||
</div>
|
||||
<div className="flex gap-4 items-center">
|
||||
{/* Usage Popover - show if either provider is authenticated, only on desktop */}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
import { User, GitBranch, Palette, AlertTriangle, Workflow } from 'lucide-react';
|
||||
import { User, GitBranch, Palette, AlertTriangle, Workflow, Database } from 'lucide-react';
|
||||
import type { ProjectSettingsViewId } from '../hooks/use-project-settings-view';
|
||||
|
||||
export interface ProjectNavigationItem {
|
||||
@@ -13,5 +13,6 @@ export const PROJECT_SETTINGS_NAV_ITEMS: ProjectNavigationItem[] = [
|
||||
{ id: 'worktrees', label: 'Worktrees', icon: GitBranch },
|
||||
{ id: 'theme', label: 'Theme', icon: Palette },
|
||||
{ id: 'claude', label: 'Models', icon: Workflow },
|
||||
{ id: 'data', label: 'Data', icon: Database },
|
||||
{ id: 'danger', label: 'Danger Zone', icon: AlertTriangle },
|
||||
];
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import { useState } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Database, Download, Upload } from 'lucide-react';
|
||||
import { ExportFeaturesDialog } from '../board-view/dialogs/export-features-dialog';
|
||||
import { ImportFeaturesDialog } from '../board-view/dialogs/import-features-dialog';
|
||||
import { useBoardFeatures } from '../board-view/hooks';
|
||||
import type { Project } from '@/lib/electron';
|
||||
|
||||
interface DataManagementSectionProps {
|
||||
project: Project;
|
||||
}
|
||||
|
||||
export function DataManagementSection({ project }: DataManagementSectionProps) {
|
||||
const [showExportDialog, setShowExportDialog] = useState(false);
|
||||
const [showImportDialog, setShowImportDialog] = useState(false);
|
||||
|
||||
// Fetch features and persisted categories using the existing hook
|
||||
const { features, persistedCategories, loadFeatures } = useBoardFeatures({
|
||||
currentProject: project,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-2xl overflow-hidden',
|
||||
'border border-border/50',
|
||||
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
|
||||
'shadow-sm shadow-black/5'
|
||||
)}
|
||||
>
|
||||
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-brand-500/20 to-brand-600/10 flex items-center justify-center border border-brand-500/20">
|
||||
<Database className="w-5 h-5 text-brand-500" />
|
||||
</div>
|
||||
<h2 className="text-lg font-semibold text-foreground tracking-tight">
|
||||
Data Management
|
||||
</h2>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground/80 ml-12">
|
||||
Export and import features to backup your data or share with other projects.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 space-y-6">
|
||||
{/* Export Section */}
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-foreground">Export Features</h3>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Download all features as a JSON or YAML file for backup or sharing.
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setShowExportDialog(true)}
|
||||
className="gap-2"
|
||||
data-testid="export-features-button"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
Export Features
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Separator */}
|
||||
<div className="border-t border-border/50" />
|
||||
|
||||
{/* Import Section */}
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-foreground">Import Features</h3>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Import features from a previously exported JSON or YAML file.
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setShowImportDialog(true)}
|
||||
className="gap-2"
|
||||
data-testid="import-features-button"
|
||||
>
|
||||
<Upload className="w-4 h-4" />
|
||||
Import Features
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Export Dialog */}
|
||||
<ExportFeaturesDialog
|
||||
open={showExportDialog}
|
||||
onOpenChange={setShowExportDialog}
|
||||
projectPath={project.path}
|
||||
features={features}
|
||||
/>
|
||||
|
||||
{/* Import Dialog */}
|
||||
<ImportFeaturesDialog
|
||||
open={showImportDialog}
|
||||
onOpenChange={setShowImportDialog}
|
||||
projectPath={project.path}
|
||||
categorySuggestions={persistedCategories}
|
||||
onImportComplete={() => {
|
||||
loadFeatures();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,12 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
|
||||
export type ProjectSettingsViewId = 'identity' | 'theme' | 'worktrees' | 'claude' | 'danger';
|
||||
export type ProjectSettingsViewId =
|
||||
| 'identity'
|
||||
| 'theme'
|
||||
| 'worktrees'
|
||||
| 'claude'
|
||||
| 'data'
|
||||
| 'danger';
|
||||
|
||||
interface UseProjectSettingsViewOptions {
|
||||
initialView?: ProjectSettingsViewId;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ProjectIdentitySection } from './project-identity-section';
|
||||
import { ProjectThemeSection } from './project-theme-section';
|
||||
import { WorktreePreferencesSection } from './worktree-preferences-section';
|
||||
import { ProjectModelsSection } from './project-models-section';
|
||||
import { DataManagementSection } from './data-management-section';
|
||||
import { DangerZoneSection } from '../settings-view/danger-zone/danger-zone-section';
|
||||
import { DeleteProjectDialog } from '../settings-view/components/delete-project-dialog';
|
||||
import { ProjectSettingsNavigation } from './components/project-settings-navigation';
|
||||
@@ -87,6 +88,8 @@ export function ProjectSettingsView() {
|
||||
return <WorktreePreferencesSection project={currentProject} />;
|
||||
case 'claude':
|
||||
return <ProjectModelsSection project={currentProject} />;
|
||||
case 'data':
|
||||
return <DataManagementSection project={currentProject} />;
|
||||
case 'danger':
|
||||
return (
|
||||
<DangerZoneSection
|
||||
|
||||
Reference in New Issue
Block a user