From 662f8542031c904322a71390fa79908899657b88 Mon Sep 17 00:00:00 2001 From: Shirone Date: Wed, 21 Jan 2026 17:43:33 +0100 Subject: [PATCH] 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 --- apps/ui/src/components/views/board-view.tsx | 32 ----- .../views/board-view/board-controls.tsx | 44 +------ .../views/board-view/board-header.tsx | 11 +- .../config/navigation.ts | 3 +- .../data-management-section.tsx | 110 ++++++++++++++++++ .../hooks/use-project-settings-view.ts | 8 +- .../project-settings-view.tsx | 3 + 7 files changed, 125 insertions(+), 86 deletions(-) create mode 100644 apps/ui/src/components/views/project-settings-view/data-management-section.tsx diff --git a/apps/ui/src/components/views/board-view.tsx b/apps/ui/src/components/views/board-view.tsx index b6702087..2624514a 100644 --- a/apps/ui/src/components/views/board-view.tsx +++ b/apps/ui/src/components/views/board-view.tsx @@ -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(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 */} - - - {/* Import Features Dialog */} - { - loadFeatures(); - }} - /> - {/* Init Script Indicator - floating overlay for worktree init script status */} {getShowInitScriptIndicator(currentProject.path) && ( diff --git a/apps/ui/src/components/views/board-view/board-controls.tsx b/apps/ui/src/components/views/board-view/board-controls.tsx index 49a47140..8584bbdb 100644 --- a/apps/ui/src/components/views/board-view/board-controls.tsx +++ b/apps/ui/src/components/views/board-view/board-controls.tsx @@ -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({

Board Background Settings

- - {/* More Options Menu */} - - - - - - - - -

More Options

-
-
- - - - Export Features - - - - Import Features - - -
); diff --git a/apps/ui/src/components/views/board-view/board-header.tsx b/apps/ui/src/components/views/board-view/board-header.tsx index 42604d9c..77a272c9 100644 --- a/apps/ui/src/components/views/board-view/board-header.tsx +++ b/apps/ui/src/components/views/board-view/board-header.tsx @@ -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 && } - +
{/* Usage Popover - show if either provider is authenticated, only on desktop */} diff --git a/apps/ui/src/components/views/project-settings-view/config/navigation.ts b/apps/ui/src/components/views/project-settings-view/config/navigation.ts index e29564d1..18447458 100644 --- a/apps/ui/src/components/views/project-settings-view/config/navigation.ts +++ b/apps/ui/src/components/views/project-settings-view/config/navigation.ts @@ -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 }, ]; diff --git a/apps/ui/src/components/views/project-settings-view/data-management-section.tsx b/apps/ui/src/components/views/project-settings-view/data-management-section.tsx new file mode 100644 index 00000000..f6c6ceec --- /dev/null +++ b/apps/ui/src/components/views/project-settings-view/data-management-section.tsx @@ -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 ( + <> +
+
+
+
+ +
+

+ Data Management +

+
+

+ Export and import features to backup your data or share with other projects. +

+
+
+ {/* Export Section */} +
+
+

Export Features

+

+ Download all features as a JSON or YAML file for backup or sharing. +

+
+ +
+ + {/* Separator */} +
+ + {/* Import Section */} +
+
+

Import Features

+

+ Import features from a previously exported JSON or YAML file. +

+
+ +
+
+
+ + {/* Export Dialog */} + + + {/* Import Dialog */} + { + loadFeatures(); + }} + /> + + ); +} diff --git a/apps/ui/src/components/views/project-settings-view/hooks/use-project-settings-view.ts b/apps/ui/src/components/views/project-settings-view/hooks/use-project-settings-view.ts index 89cb87bc..82533940 100644 --- a/apps/ui/src/components/views/project-settings-view/hooks/use-project-settings-view.ts +++ b/apps/ui/src/components/views/project-settings-view/hooks/use-project-settings-view.ts @@ -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; diff --git a/apps/ui/src/components/views/project-settings-view/project-settings-view.tsx b/apps/ui/src/components/views/project-settings-view/project-settings-view.tsx index 75548f66..5c7b1138 100644 --- a/apps/ui/src/components/views/project-settings-view/project-settings-view.tsx +++ b/apps/ui/src/components/views/project-settings-view/project-settings-view.tsx @@ -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 ; case 'claude': return ; + case 'data': + return ; case 'danger': return (