From d8d2fec166894d0f61e77bfda25a9952634f49a7 Mon Sep 17 00:00:00 2001 From: naman-bruno Date: Thu, 25 Jun 2026 16:53:31 +0530 Subject: [PATCH] refactor(migration): extract migration modal (#8359) --- .../MigrateToYmlModal/StyledWrapper.js | 40 ++++++++ .../Migration/MigrateToYmlModal/index.js | 92 +++++++++++++++++++ .../Overview/Migration/StyledWrapper.js | 35 ------- .../Overview/Migration/index.js | 81 +--------------- .../RequestTabs/CollectionHeader/index.js | 23 ++--- .../migrate-to-yml-pill.spec.ts | 11 ++- 6 files changed, 154 insertions(+), 128 deletions(-) create mode 100644 packages/bruno-app/src/components/CollectionSettings/Overview/Migration/MigrateToYmlModal/StyledWrapper.js create mode 100644 packages/bruno-app/src/components/CollectionSettings/Overview/Migration/MigrateToYmlModal/index.js diff --git a/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/MigrateToYmlModal/StyledWrapper.js b/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/MigrateToYmlModal/StyledWrapper.js new file mode 100644 index 000000000..7cdd36491 --- /dev/null +++ b/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/MigrateToYmlModal/StyledWrapper.js @@ -0,0 +1,40 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + .backup-section { + border: 1px solid ${(props) => props.theme.border.border2}; + border-radius: ${(props) => props.theme.border.radius.base}; + background-color: ${(props) => props.theme.background.mantle}; + padding: 12px 14px; + } + + .backup-section-head { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 6px; + color: ${(props) => props.theme.text}; + } + + .backup-section-title { + font-size: ${(props) => props.theme.font.size.sm}; + font-weight: 500; + color: ${(props) => props.theme.colors.text.muted}; + text-transform: uppercase; + letter-spacing: 0.04em; + } + + .backup-section-help { + font-size: ${(props) => props.theme.font.size.base}; + color: ${(props) => props.theme.colors.text.muted}; + line-height: 1.45; + margin: 0 0 10px 0; + } + + .backup-section-action { + display: flex; + justify-content: flex-start; + } +`; + +export default StyledWrapper; diff --git a/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/MigrateToYmlModal/index.js b/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/MigrateToYmlModal/index.js new file mode 100644 index 000000000..35fab5466 --- /dev/null +++ b/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/MigrateToYmlModal/index.js @@ -0,0 +1,92 @@ +import React, { useState } from 'react'; +import { useDispatch } from 'react-redux'; +import toast from 'react-hot-toast'; +import { migrateCollectionToYml } from 'providers/ReduxStore/slices/collections/actions'; +import Modal from 'components/Modal'; +import Portal from 'components/Portal'; +import Button from 'ui/Button'; +import StyledWrapper from './StyledWrapper'; + +const MigrateToYmlModal = ({ collection, onClose }) => { + const dispatch = useDispatch(); + const [isMigrating, setIsMigrating] = useState(false); + const [isExporting, setIsExporting] = useState(false); + + const handleMigrate = () => { + setIsMigrating(true); + dispatch(migrateCollectionToYml(collection.uid)) + .catch(() => {}) + .finally(() => { + setIsMigrating(false); + onClose(); + }); + }; + + const handleExportBackup = async () => { + if (isExporting) return; + setIsExporting(true); + try { + const { ipcRenderer } = window; + const result = await ipcRenderer.invoke('renderer:export-collection-zip', collection.pathname, collection.name); + if (result?.success) { + toast.success('Collection backup exported'); + } + } catch (error) { + toast.error('Failed to export backup: ' + error.message); + } finally { + setIsExporting(false); + } + }; + + return ( + + + +
+

+ This will convert all files in {collection.name} from .bru format to .yml format. +

+
+

What will happen:

+
    +
  • All .bru request files will be converted to .yml
  • +
  • Environment files will be converted to YML format
  • +
  • bruno.json will be replaced with opencollection.yml
  • +
  • The collection will be reloaded after migration
  • +
+
+
+
+ Backup +
+

+ Export this collection as a ZIP archive before migrating, in case you want to restore it later. +

+
+ +
+
+
+
+
+
+ ); +}; + +export default MigrateToYmlModal; diff --git a/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/StyledWrapper.js b/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/StyledWrapper.js index e6151a309..8f8330d03 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/StyledWrapper.js +++ b/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/StyledWrapper.js @@ -15,41 +15,6 @@ const StyledWrapper = styled.div` color: ${(props) => props.theme.colors.text.yellow}; } } - - .backup-section { - border: 1px solid ${(props) => props.theme.border.border2}; - border-radius: ${(props) => props.theme.border.radius.base}; - background-color: ${(props) => props.theme.background.mantle}; - padding: 12px 14px; - } - - .backup-section-head { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 6px; - color: ${(props) => props.theme.text}; - } - - .backup-section-title { - font-size: ${(props) => props.theme.font.size.sm}; - font-weight: 500; - color: ${(props) => props.theme.colors.text.muted}; - text-transform: uppercase; - letter-spacing: 0.04em; - } - - .backup-section-help { - font-size: ${(props) => props.theme.font.size.base}; - color: ${(props) => props.theme.colors.text.muted}; - line-height: 1.45; - margin: 0 0 10px 0; - } - - .backup-section-action { - display: flex; - justify-content: flex-start; - } `; export default StyledWrapper; diff --git a/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/index.js b/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/index.js index 0665edfb6..fd0a615c5 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Overview/Migration/index.js @@ -1,47 +1,17 @@ import React, { useState } from 'react'; -import { useDispatch } from 'react-redux'; import { IconFileCode, IconTransform } from '@tabler/icons'; -import toast from 'react-hot-toast'; -import { migrateCollectionToYml } from 'providers/ReduxStore/slices/collections/actions'; -import Modal from 'components/Modal'; import Button from 'ui/Button'; +import MigrateToYmlModal from './MigrateToYmlModal'; import StyledWrapper from './StyledWrapper'; const Migration = ({ collection }) => { - const dispatch = useDispatch(); const [showConfirmModal, setShowConfirmModal] = useState(false); - const [isMigrating, setIsMigrating] = useState(false); - const [isExporting, setIsExporting] = useState(false); // Only show for bru format collections if (collection.format !== 'bru') { return null; } - const handleMigrate = () => { - setIsMigrating(true); - setShowConfirmModal(false); - dispatch(migrateCollectionToYml(collection.uid)) - .catch(() => { }) - .finally(() => setIsMigrating(false)); - }; - - const handleExportBackup = async () => { - if (isExporting) return; - setIsExporting(true); - try { - const { ipcRenderer } = window; - const result = await ipcRenderer.invoke('renderer:export-collection-zip', collection.pathname, collection.name); - if (result?.success) { - toast.success('Collection backup exported'); - } - } catch (error) { - toast.error('Failed to export backup: ' + error.message); - } finally { - setIsExporting(false); - } - }; - return (
@@ -74,8 +44,6 @@ const Migration = ({ collection }) => { color="primary" className="mt-2" onClick={() => setShowConfirmModal(true)} - disabled={isMigrating} - loading={isMigrating} > Convert to YML @@ -84,49 +52,10 @@ const Migration = ({ collection }) => {
{showConfirmModal && ( - setShowConfirmModal(false)} - > -
-

- This will convert all files in {collection.name} from .bru format to .yml format. -

-
-

What will happen:

-
    -
  • All .bru request files will be converted to .yml
  • -
  • Environment files will be converted to YML format
  • -
  • bruno.json will be replaced with opencollection.yml
  • -
  • The collection will be reloaded after migration
  • -
-
-
-
- Backup -
-

- Export this collection as a ZIP archive before migrating, in case you want to restore it later. -

-
- -
-
-
-
+ setShowConfirmModal(false)} + /> )}
); diff --git a/packages/bruno-app/src/components/RequestTabs/CollectionHeader/index.js b/packages/bruno-app/src/components/RequestTabs/CollectionHeader/index.js index 0b28f5586..6746e77ee 100644 --- a/packages/bruno-app/src/components/RequestTabs/CollectionHeader/index.js +++ b/packages/bruno-app/src/components/RequestTabs/CollectionHeader/index.js @@ -23,7 +23,8 @@ import OpenAPISyncIcon from 'components/Icons/OpenAPISync'; import { switchWorkspace, renameWorkspaceAction, exportWorkspaceAction, confirmWorkspaceCreation, cancelWorkspaceCreation } from 'providers/ReduxStore/slices/workspaces/actions'; import { updateWorkspace } from 'providers/ReduxStore/slices/workspaces'; import { showInFolder } from 'providers/ReduxStore/slices/collections/actions'; -import { toggleCollectionFileMode, toggleAppMode, updateSettingsSelectedTab } from 'providers/ReduxStore/slices/collections'; +import { toggleCollectionFileMode, toggleAppMode } from 'providers/ReduxStore/slices/collections'; +import MigrateToYmlModal from 'components/CollectionSettings/Overview/Migration/MigrateToYmlModal'; import { findItemInCollection, findItemInCollectionByPathname } from 'utils/collections'; import find from 'lodash/find'; import get from 'lodash/get'; @@ -92,6 +93,7 @@ const CollectionHeader = ({ collection, isScratchCollection }) => { const [workspaceNameError, setWorkspaceNameError] = useState(''); const [closeWorkspaceModalOpen, setCloseWorkspaceModalOpen] = useState(false); const [createWorkspaceModalOpen, setCreateWorkspaceModalOpen] = useState(false); + const [showMigrateModal, setShowMigrateModal] = useState(false); // Migrate-to-YML pill dismissal state (persisted by collection pathname) const [migratePillDismissed, setMigratePillDismissed] = useState(true); @@ -270,17 +272,6 @@ const CollectionHeader = ({ collection, isScratchCollection }) => { ); }; - const viewMigrationSettings = () => { - dispatch( - addTab({ - uid: collection.uid, - collectionUid: collection.uid, - type: 'collection-settings' - }) - ); - dispatch(updateSettingsSelectedTab({ collectionUid: collection.uid, tab: 'overview' })); - }; - const viewOpenApiSync = () => { dispatch(addTab({ uid: uuid(), @@ -708,7 +699,7 @@ const CollectionHeader = ({ collection, isScratchCollection }) => {