import { useMemo } from 'react'; import { useSelector } from 'react-redux'; import { IconCheck, IconRefresh } from '@tabler/icons'; import Button from 'ui/Button'; import StatusBadge from 'ui/StatusBadge'; import ConfirmSyncModal from '../ConfirmSyncModal'; import SyncReviewPage from '../SyncReviewPage'; import useSyncFlow from '../hooks/useSyncFlow'; const SpecStatusSection = ({ collection, sourceUrl, isLoading, error, setError, fileNotFound, specDrift, storedSpec, collectionDrift, remoteDrift, onCheck, onOpenSettings }) => { const openApiSyncConfig = collection?.brunoConfig?.openapi?.[0]; const lastCheckedAt = useSelector((state) => state.openapiSync?.collectionUpdates?.[collection.uid]?.lastChecked); const { isSyncing, showConfirmModal, confirmGroups, handleSyncNow, handleApplySync, cancelConfirmModal, handleConfirmModalSync } = useSyncFlow({ collection, specDrift, remoteDrift, collectionDrift, sourceUrl, setError, checkForUpdates: onCheck }); const lastSyncedAt = openApiSyncConfig?.lastSyncDate; const bannerState = useMemo(() => { if (fileNotFound) { return { variant: 'danger', message: `Source file not found at ${sourceUrl}`, actions: ['open-settings'] }; } if (error || specDrift?.isValid === false) { return { variant: 'danger', message: error || specDrift?.error || 'Invalid OpenAPI specification', actions: [] }; } if (!specDrift) { return null; // TODO: re-enable success banner // if (!lastSyncedAt) return null; // return { // variant: 'success', message: 'Spec is up to date', actions: [], // version: storedSpec?.info?.version, // lastChecked: moment(lastCheckedAt || lastSyncedAt).fromNow() // }; } if (specDrift.storedSpecMissing) { if (!lastSyncedAt) { return { variant: 'warning', message: 'Initial sync required — your collection differs from the spec', actions: [] }; } if (specDrift.hasRemoteChanges) { return { variant: 'warning', message: 'Last synced spec not found — Restore the latest spec from the source to track future changes.', actions: [] }; } return { variant: 'warning', message: 'Last synced spec not found — Restore the latest spec from the source to track future changes.', actions: [] }; } if (specDrift.hasRemoteChanges) { const versionInfo = (specDrift.storedVersion && specDrift.newVersion && specDrift.storedVersion !== specDrift.newVersion) ? ` (v${specDrift.storedVersion} → v${specDrift.newVersion})` : ''; return { variant: 'warning', message: `OpenAPI spec has been updated${versionInfo}`, actions: [], changes: { added: specDrift.added?.length || 0, modified: specDrift.modified?.length || 0, removed: specDrift.removed?.length || 0 } }; } // return { // variant: 'success', message: 'Spec is up to date', actions: [], // version: specDrift.newVersion || storedSpec?.info?.version || specDrift.storedVersion, // lastChecked: lastCheckedAt ? moment(lastCheckedAt).fromNow() : 'just now' // }; return null; }, [isLoading, fileNotFound, error, sourceUrl, specDrift, lastSyncedAt, storedSpec, lastCheckedAt]); return ( <> {bannerState && (
{bannerState.variant === 'success' ? :
} {bannerState.message} {bannerState.version && ( <> · v{bannerState.version} )} {bannerState.lastChecked && ( · Checked {bannerState.lastChecked} )} {bannerState.changes && ( {bannerState.changes.added > 0 && {bannerState.changes.added} {bannerState.changes.added > 1 ? 'endpoints' : 'endpoint'} added} {bannerState.changes.modified > 0 && {bannerState.changes.modified} {bannerState.changes.modified > 1 ? 'endpoints' : 'endpoint'} updated} {bannerState.changes.removed > 0 && {bannerState.changes.removed} {bannerState.changes.removed > 1 ? 'endpoints' : 'endpoint'} removed} )}
{bannerState.actions.includes('quick-sync') && ( )} {bannerState.actions.includes('open-settings') && ( )}
)} {specDrift?.storedSpecMissing && openApiSyncConfig?.lastSyncDate ? (

Last Synced Spec not found in storage

The last synced spec is missing in the storage. Restore the latest spec from the source to track future changes.

) : (
)} {showConfirmModal && ( )} ); }; export default SpecStatusSection;