diff --git a/.betterer.results b/.betterer.results index 0c0f8b906bc..c8692914cbd 100644 --- a/.betterer.results +++ b/.betterer.results @@ -5697,6 +5697,18 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "9"], [0, 0, 0, "Unexpected any. Specify a different type.", "10"] ], + "public/app/features/provisioning/FilesView.tsx:5381": [ + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] + ], + "public/app/features/provisioning/FolderRepositoryList.tsx:5381": [ + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] + ], "public/app/features/provisioning/GettingStarted/EnhancedFeatures.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], @@ -5732,6 +5744,14 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] ], + "public/app/features/provisioning/HomePage.tsx:5381": [ + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], + [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] + ], "public/app/features/provisioning/JobStatus.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], @@ -5759,26 +5779,23 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] ], + "public/app/features/provisioning/RepositoryActions.tsx:5381": [ + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] + ], + "public/app/features/provisioning/RepositoryCard.tsx:5381": [ + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"] + ], "public/app/features/provisioning/RepositoryHealth.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] ], - "public/app/features/provisioning/RepositoryListPage.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "7"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "8"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "9"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "10"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "11"] - ], "public/app/features/provisioning/RepositoryOverview.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], @@ -5811,19 +5828,10 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"], [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "3"], - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "4"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "7"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "8"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "9"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "10"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "11"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "12"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "13"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "14"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "15"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "6"] ], "public/app/features/provisioning/SyncRepository.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"], diff --git a/public/app/features/provisioning/FilesView.tsx b/public/app/features/provisioning/FilesView.tsx new file mode 100644 index 00000000000..08954adbbdb --- /dev/null +++ b/public/app/features/provisioning/FilesView.tsx @@ -0,0 +1,112 @@ +import { useState } from 'react'; + +import { + Button, + CellProps, + Column, + ConfirmModal, + FilterInput, + InteractiveTable, + LinkButton, + Spinner, + Stack, +} from '@grafana/ui'; + +import { Repository, useGetRepositoryFilesQuery, useDeleteRepositoryFilesWithPathMutation } from './api'; +import { FileDetails } from './api/types'; +import { PROVISIONING_URL } from './constants'; + +interface FilesViewProps { + repo: Repository; +} + +type FileCell = CellProps; + +export function FilesView({ repo }: FilesViewProps) { + const name = repo.metadata?.name ?? ''; + const query = useGetRepositoryFilesQuery({ name }); + const [deleteFile, deleteFileStatus] = useDeleteRepositoryFilesWithPathMutation(); + + const [searchQuery, setSearchQuery] = useState(''); + const [pathToDelete, setPathToDelete] = useState(); + const data = [...(query.data?.items ?? [])].filter((file) => + file.path.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + const columns: Array> = [ + { + id: 'path', + header: 'Path', + sortType: 'string', + cell: ({ row: { original } }: FileCell<'path'>) => { + const { path } = original; + return {path}; + }, + }, + { + id: 'size', + header: 'Size (KB)', + cell: ({ row: { original } }: FileCell<'size'>) => { + const { size } = original; + return (parseInt(size, 10) / 1024).toFixed(2); + }, + sortType: 'number', + }, + { + id: 'hash', + header: 'Hash', + sortType: 'string', + }, + { + id: 'actions', + header: '', + cell: ({ row: { original } }: FileCell<'path'>) => { + const { path } = original; + return ( + + {(path.endsWith('.json') || path.endsWith('.yaml') || path.endsWith('.yml')) && ( + View + )} + History + + + ); + }, + }, + ]; + + if (query.isLoading) { + return ( + + + + ); + } + + return ( + + { + deleteFile({ + name: name, + path: pathToDelete!, + message: `Deleted from repo test UI`, + }); + setPathToDelete(''); + }} + onDismiss={() => setPathToDelete('')} + /> + + + + String(f.path)} /> + + ); +} diff --git a/public/app/features/provisioning/FolderRepositoryList.tsx b/public/app/features/provisioning/FolderRepositoryList.tsx new file mode 100644 index 00000000000..64569940c9c --- /dev/null +++ b/public/app/features/provisioning/FolderRepositoryList.tsx @@ -0,0 +1,35 @@ +import { useState } from 'react'; + +import { EmptySearchResult, FilterInput, LinkButton, Stack } from '@grafana/ui'; + +import { RepositoryCard } from './RepositoryCard'; +import { Repository, useGetFrontendSettingsQuery } from './api'; +import { CONNECT_URL } from './constants'; +import { checkSyncSettings } from './utils'; + +export function FolderRepositoryList({ items }: { items: Repository[] }) { + const [query, setQuery] = useState(''); + const filteredItems = items.filter((item) => item.metadata?.name?.includes(query)); + const settings = useGetFrontendSettingsQuery(); + const [instanceConnected] = checkSyncSettings(settings.data); + + return ( + + + + {!instanceConnected && ( + + Connect to repository + + )} + + + {!!filteredItems.length ? ( + filteredItems.map((item) => ) + ) : ( + No results matching your query + )} + + + ); +} diff --git a/public/app/features/provisioning/HomePage.tsx b/public/app/features/provisioning/HomePage.tsx new file mode 100644 index 00000000000..7d907033de9 --- /dev/null +++ b/public/app/features/provisioning/HomePage.tsx @@ -0,0 +1,172 @@ +import { useEffect, useState } from 'react'; + +import { AppEvents } from '@grafana/data'; +import { getAppEvents } from '@grafana/runtime'; +import { Alert, ConfirmModal, Modal, Stack, Tab, TabContent, TabsBar } from '@grafana/ui'; +import { Page } from 'app/core/components/Page/Page'; + +import { ExportToRepository } from './ExportToRepository'; +import { FilesView } from './FilesView'; +import { FolderRepositoryList } from './FolderRepositoryList'; +import GettingStarted from './GettingStarted/GettingStarted'; +import GettingStartedPage from './GettingStarted/GettingStartedPage'; +import { MigrateToRepository } from './MigrateToRepository'; +import { RepositoryActions } from './RepositoryActions'; +import { RepositoryOverview } from './RepositoryOverview'; +import { RepositoryResources } from './RepositoryResources'; +import { useDeletecollectionRepositoryMutation, useGetFrontendSettingsQuery } from './api'; +import { useRepositoryList } from './hooks'; +import { checkSyncSettings } from './utils'; + +const appEvents = getAppEvents(); + +enum TabSelection { + Overview = 'overview', + Resources = 'resources', + Files = 'files', + GettingStarted = 'getting-started', + Repositories = 'repositories', +} + +const connectedTabInfo = [ + { value: TabSelection.Overview, label: 'Overview', title: 'Repository overview' }, + { value: TabSelection.Resources, label: 'Resources', title: 'Resources saved in Grafana database' }, + { value: TabSelection.Files, label: 'Files', title: 'The raw file list from the repository' }, + { value: TabSelection.GettingStarted, label: 'Getting started', title: 'Getting started' }, +]; + +const disconnectedTabInfo = [ + { value: TabSelection.Repositories, label: 'Repositories', title: 'List of repositories' }, + { value: TabSelection.GettingStarted, label: 'Getting started', title: 'Getting started' }, +]; + +export default function HomePage() { + const [items, isLoading] = useRepositoryList({ watch: true }); + const settings = useGetFrontendSettingsQuery(); + const [deleteAll, deleteAllResult] = useDeletecollectionRepositoryMutation(); + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [showExportModal, setShowExportModal] = useState(false); + const [showMigrateModal, setShowMigrateModal] = useState(false); + const [instanceConnected] = checkSyncSettings(settings.data); + const [activeTab, setActiveTab] = useState( + instanceConnected ? TabSelection.Overview : TabSelection.Repositories + ); + + useEffect(() => { + if (deleteAllResult.isSuccess) { + appEvents.publish({ + type: AppEvents.alertSuccess.name, + payload: ['All configured repositories deleted'], + }); + } + }, [deleteAllResult.isSuccess]); + + useEffect(() => { + setActiveTab(instanceConnected ? TabSelection.Overview : TabSelection.Repositories); + }, [instanceConnected]); + + // Early return for onboarding + if (!items?.length && !isLoading) { + return ; + } + + const onConfirmDelete = () => { + deleteAll({}); + setShowDeleteModal(false); + }; + + const renderTabContent = () => { + if (!instanceConnected) { + switch (activeTab) { + case TabSelection.Repositories: + return ; + case TabSelection.GettingStarted: + return ; + default: + return null; + } + } + + const repo = items?.[0]; + if (!repo) { + return null; + } + + switch (activeTab) { + case TabSelection.Overview: + return ; + case TabSelection.Resources: + return ; + case TabSelection.Files: + return ; + case TabSelection.GettingStarted: + return ; + default: + return null; + } + }; + + return ( + setShowExportModal(true)} + onMigrateClick={() => setShowMigrateModal(true)} + /> + ) : undefined + } + > + + {settings.data?.legacyStorage && ( + Remove all configured repositories} + onRemove={() => { + setShowDeleteModal(true); + }} + > + Configured repositories will not work while running legacy storage. + + )} + setShowDeleteModal(false)} + /> + {showExportModal && items?.length && ( + setShowExportModal(false)}> + + + )} + {showMigrateModal && items?.length && ( + setShowMigrateModal(false)}> + + + )} + + + {(instanceConnected ? connectedTabInfo : disconnectedTabInfo).map((t) => ( + setActiveTab(t.value)} + title={t.title} + /> + ))} + + {renderTabContent()} + + + + ); +} diff --git a/public/app/features/provisioning/RepositoryActions.tsx b/public/app/features/provisioning/RepositoryActions.tsx new file mode 100644 index 00000000000..fa72f206d3d --- /dev/null +++ b/public/app/features/provisioning/RepositoryActions.tsx @@ -0,0 +1,54 @@ +import { Button, LinkButton, Stack } from '@grafana/ui'; + +import { DeleteRepositoryButton } from './DeleteRepositoryButton'; +import { StatusBadge } from './StatusBadge'; +import { SyncRepository } from './SyncRepository'; +import { Repository } from './api'; +import { PROVISIONING_URL } from './constants'; +import { getRemoteURL } from './utils/git'; + +interface RepositoryActionsProps { + repository: Repository; + showMigrateButton?: boolean; + onExportClick?: () => void; + onMigrateClick?: () => void; +} + +export function RepositoryActions({ + repository, + showMigrateButton, + onExportClick, + onMigrateClick, +}: RepositoryActionsProps) { + const remoteURL = getRemoteURL(repository); + const name = repository.metadata?.name ?? ''; + + return ( + + + {remoteURL && ( + + )} + + {showMigrateButton ? ( + + ) : ( + + )} + + Settings + + + + ); +} diff --git a/public/app/features/provisioning/RepositoryCard.tsx b/public/app/features/provisioning/RepositoryCard.tsx new file mode 100644 index 00000000000..02027afaa5d --- /dev/null +++ b/public/app/features/provisioning/RepositoryCard.tsx @@ -0,0 +1,124 @@ +import { ReactNode } from 'react'; + +import { IconName, Stack, Text, TextLink, Icon, Card, LinkButton } from '@grafana/ui'; + +import { DeleteRepositoryButton } from './DeleteRepositoryButton'; +import { StatusBadge } from './StatusBadge'; +import { SyncRepository } from './SyncRepository'; +import { Repository, ResourceCount } from './api'; +import { PROVISIONING_URL } from './constants'; + +interface Props { + repository: Repository; +} + +export function RepositoryCard({ repository }: Props) { + const { metadata, spec, status } = repository; + const name = metadata?.name ?? ''; + + const getRepositoryMeta = (): ReactNode[] => { + const meta: ReactNode[] = []; + + if (spec?.type === 'github') { + const { url = '', branch } = spec.github ?? {}; + const branchUrl = branch ? `${url}/tree/${branch}` : url; + + meta.push( + + {branchUrl} + + ); + + if (status?.webhook?.id) { + const webhookUrl = `${url}/settings/hooks/${status.webhook.id}`; + meta.push( + + + Webhook + + + + ); + } + } else if (spec?.type === 'local') { + meta.push( + + {spec.local?.path ?? ''} + + ); + } + + return meta; + }; + + const getRepositoryIcon = (): IconName => { + return spec?.type === 'github' ? 'github' : 'database'; + }; + + return ( + + + + + + + {spec?.title && {spec.title}} + + + + + + + {spec?.description && {spec.description}} + {status?.stats?.length && ( + + {status.stats.map((stat, index) => ( + + {stat.count} {stat.resource} + + ))} + + )} + + + + + + {getRepositoryMeta()} + + + + + + + View + + + + Settings + + + + + + + + ); +} + +// Helper function +function getListURL(repo: Repository, stats: ResourceCount): string { + if (stats.resource === 'playlists') { + return '/playlists'; + } + if (repo.spec?.sync.target === 'folder') { + return `/dashboards/f/${repo.metadata?.name}`; + } + return '/dashboards'; +} diff --git a/public/app/features/provisioning/RepositoryListPage.tsx b/public/app/features/provisioning/RepositoryListPage.tsx deleted file mode 100644 index 8ffda21b537..00000000000 --- a/public/app/features/provisioning/RepositoryListPage.tsx +++ /dev/null @@ -1,232 +0,0 @@ -import { ReactNode, useEffect, useState } from 'react'; - -import { AppEvents } from '@grafana/data'; -import { getAppEvents } from '@grafana/runtime'; -import { - Card, - EmptySearchResult, - FilterInput, - Icon, - IconName, - LinkButton, - Stack, - TextLink, - Text, - Alert, - ConfirmModal, - Tab, - TabsBar, - TagList, -} from '@grafana/ui'; -import { Page } from 'app/core/components/Page/Page'; - -import { DeleteRepositoryButton } from './DeleteRepositoryButton'; -import GettingStarted from './GettingStarted/GettingStarted'; -import GettingStartedPage from './GettingStarted/GettingStartedPage'; -import { StatusBadge } from './StatusBadge'; -import { SyncRepository } from './SyncRepository'; -import { Repository, ResourceCount, useDeletecollectionRepositoryMutation, useGetFrontendSettingsQuery } from './api'; -import { PROVISIONING_URL, CONNECT_URL } from './constants'; -import { useRepositoryList } from './hooks'; -import { checkSyncSettings } from './utils'; - -const appEvents = getAppEvents(); - -export default function RepositoryListPage() { - const [items, isLoading] = useRepositoryList({ watch: true }); - const settings = useGetFrontendSettingsQuery(); - const [deleteAll, deleteAllResult] = useDeletecollectionRepositoryMutation(); - const [showDeleteModal, setShowDeleteModal] = useState(false); - const [activeTab, setActiveTab] = useState('repositories'); - - useEffect(() => { - if (deleteAllResult.isSuccess) { - appEvents.publish({ - type: AppEvents.alertSuccess.name, - payload: ['All configured repositories deleted'], - }); - } - }, [deleteAllResult.isSuccess]); - - if (!items?.length && !isLoading) { - return ; - } - - const onConfirmDelete = () => { - deleteAll({}); - setShowDeleteModal(false); - }; - - const renderTabContent = () => { - switch (activeTab) { - case 'repositories': - return ; - case 'getting-started': - return ; - default: - return null; - } - }; - - return ( - - - {settings.data?.legacyStorage && ( - Remove all configured repositories} - onRemove={() => { - setShowDeleteModal(true); - }} - > - Configured repositories will not work while running legacy storage. - - )} - setShowDeleteModal(false)} - /> - - - setActiveTab('repositories')} - /> - setActiveTab('getting-started')} - /> - - {renderTabContent()} - - - - ); -} - -function RepositoryListPageContent({ items }: { items: Repository[] }) { - const [query, setQuery] = useState(''); - const filteredItems = items.filter((item) => item.metadata?.name?.includes(query)); - const settings = useGetFrontendSettingsQuery(); - const [instanceConnected] = checkSyncSettings(settings.data); - - return ( - - - - {!instanceConnected && ( - - Connect to repository - - )} - - - {!!filteredItems.length ? ( - filteredItems.map((item) => { - const name = item.metadata?.name ?? ''; - - let icon: IconName = 'database'; // based on type - let meta: ReactNode[] = []; - switch (item.spec?.type) { - case 'github': - icon = 'github'; - const spec = item.spec.github; - const url = item.spec.github?.url ?? ''; - let branch = url; - if (spec?.branch) { - branch += `/tree/` + spec?.branch; - } - meta.push( - - {branch} - - ); - - if (item.status?.webhook?.id) { - const hook = url + `/settings/hooks/${item.status?.webhook?.id}`; - meta.push( - - Webhook - - ); - } - break; - - case 'local': - meta.push( - - {item.spec.local?.path ?? ''} - - ); - break; - } - - return ( - - - - - - - {item.spec?.title}{' '} - - - - - - {item.spec?.description} - {item.status?.stats?.length && ( - - {item.status.stats.map((v, index) => ( - - {v.count} {v.resource} - - ))} - - )} - - {meta} - - - View - - - - Settings - - - - - - - ); - }) - ) : ( - No results matching your query - )} - - - ); -} - -// This should return a URL in the UI that will show the selected values -function getListURL(repo: Repository, stats: ResourceCount): string { - if (stats.resource === 'playlists') { - return '/playlists'; - } - if (repo.spec?.sync.target === 'folder') { - return `/dashboards/f/${repo.metadata?.name}`; - } - return '/dashboards'; -} diff --git a/public/app/features/provisioning/RepositoryStatusPage.tsx b/public/app/features/provisioning/RepositoryStatusPage.tsx index a556690c4b3..48a1144ee37 100644 --- a/public/app/features/provisioning/RepositoryStatusPage.tsx +++ b/public/app/features/provisioning/RepositoryStatusPage.tsx @@ -1,48 +1,22 @@ -import { useMemo, useState } from 'react'; +import { useState } from 'react'; import { useLocation } from 'react-router'; import { useParams } from 'react-router-dom-v5-compat'; import { SelectableValue, urlUtil } from '@grafana/data'; -import { - Alert, - Button, - CellProps, - Column, - ConfirmModal, - EmptyState, - FilterInput, - InteractiveTable, - LinkButton, - Spinner, - Stack, - Tab, - TabContent, - TabsBar, - Text, - TextLink, - Modal, -} from '@grafana/ui'; +import { Alert, EmptyState, Modal, Tab, TabContent, TabsBar, Text, TextLink } from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import { useQueryParams } from 'app/core/hooks/useQueryParams'; import { isNotFoundError } from '../alerting/unified/api/util'; import { ExportToRepository } from './ExportToRepository'; +import { FilesView } from './FilesView'; import { MigrateToRepository } from './MigrateToRepository'; +import { RepositoryActions } from './RepositoryActions'; import { RepositoryOverview } from './RepositoryOverview'; import { RepositoryResources } from './RepositoryResources'; -import { StatusBadge } from './StatusBadge'; -import { SyncRepository } from './SyncRepository'; -import { - useGetRepositoryFilesQuery, - Repository, - useListRepositoryQuery, - useDeleteRepositoryFilesWithPathMutation, - useGetFrontendSettingsQuery, -} from './api'; -import { FileDetails } from './api/types'; +import { useGetFrontendSettingsQuery, useListRepositoryQuery } from './api'; import { PROVISIONING_URL } from './constants'; -import { getRemoteURL } from './utils/git'; enum TabSelection { Overview = 'overview', @@ -70,9 +44,9 @@ export default function RepositoryStatusPage() { const [queryParams] = useQueryParams(); const settings = useGetFrontendSettingsQuery(); const tab = queryParams['tab'] ?? TabSelection.Overview; - const remoteURL = data ? getRemoteURL(data) : undefined; const notFound = query.isError && isNotFoundError(query.error); + return ( - - {remoteURL && ( - - )} - - {settings.data?.legacyStorage ? ( - - ) : ( - - )} - - Settings - - + setShowExportModal(true)} + onMigrateClick={() => setShowMigrateModal(true)} + /> ) } > @@ -158,99 +117,3 @@ export default function RepositoryStatusPage() { ); } -interface RepoProps { - repo: Repository; -} - -type FileCell = CellProps; - -function FilesView({ repo }: RepoProps) { - const name = repo.metadata?.name ?? ''; - const query = useGetRepositoryFilesQuery({ name }); - const [deleteFile, deleteFileStatus] = useDeleteRepositoryFilesWithPathMutation(); - - const [searchQuery, setSearchQuery] = useState(''); - const [pathToDelete, setPathToDelete] = useState(); - const data = [...(query.data?.items ?? [])].filter((file) => - file.path.toLowerCase().includes(searchQuery.toLowerCase()) - ); - const columns: Array> = useMemo( - () => [ - { - id: 'path', - header: 'Path', - sortType: 'string', - cell: ({ row: { original } }: FileCell<'path'>) => { - const { path } = original; - return {path}; - }, - }, - { - id: 'size', - header: 'Size (KB)', - cell: ({ row: { original } }: FileCell<'size'>) => { - const { size } = original; - return (parseInt(size, 10) / 1024).toFixed(2); - }, - sortType: 'number', - }, - { - id: 'hash', - header: 'Hash', - sortType: 'string', - }, - { - id: 'actions', - header: '', - cell: ({ row: { original } }: FileCell<'path'>) => { - const { path } = original; - return ( - - {(path.endsWith('.json') || path.endsWith('.yaml') || path.endsWith('.yml')) && ( - View - )} - History - - - ); - }, - }, - ], - [name] - ); - - if (query.isLoading) { - return ( - - - - ); - } - - return ( - - { - deleteFile({ - name: name, - path: pathToDelete!, - message: `Deleted from repo test UI`, - }); - setPathToDelete(''); - }} - onDismiss={() => setPathToDelete('')} - /> - - - - String(f.path)} /> - - ); -} diff --git a/public/app/features/provisioning/Wizard/ConnectionStep.tsx b/public/app/features/provisioning/Wizard/ConnectionStep.tsx index c2f4ab00e94..36203e0c283 100644 --- a/public/app/features/provisioning/Wizard/ConnectionStep.tsx +++ b/public/app/features/provisioning/Wizard/ConnectionStep.tsx @@ -29,9 +29,10 @@ const modeOptions = [ type Props = { targetSelectable?: boolean; + generateName?: boolean; }; -export function ConnectionStep({ targetSelectable = true }: Props) { +export function ConnectionStep({ targetSelectable = true, generateName = false }: Props) { const { register, control, @@ -58,7 +59,12 @@ export function ConnectionStep({ targetSelectable = true }: Props) { if (folderConnected) { setValue('repository.sync.target', folderConnected ? 'folder' : 'instance'); } - }, [folderConnected, setValue]); + if (generateName) { + // Generate a unique title with timestamp to ensure uniqueness + const timestamp = Date.now(); + setValue('repository.title', `instance-${timestamp}`); + } + }, [folderConnected, generateName, setValue]); return (
@@ -88,17 +94,20 @@ export function ConnectionStep({ targetSelectable = true }: Props) { )} /> - - - + {/* Only show title field if not instance sync */} + {!generateName && ( + + + + )} {folderConnected && ( diff --git a/public/app/features/provisioning/Wizard/ProvisioningWizard.tsx b/public/app/features/provisioning/Wizard/ProvisioningWizard.tsx index 6df1493a089..7f4b3a917e2 100644 --- a/public/app/features/provisioning/Wizard/ProvisioningWizard.tsx +++ b/public/app/features/provisioning/Wizard/ProvisioningWizard.tsx @@ -115,7 +115,9 @@ export function ProvisioningWizard({ requiresMigration }: Props) { />
- {activeStep === 'connection' && } + {activeStep === 'connection' && ( + + )} {activeStep === 'repository' && } {activeStep === 'migrate' && requiresMigration && } {activeStep === 'pull' && !requiresMigration && } diff --git a/public/app/features/provisioning/utils/routes.ts b/public/app/features/provisioning/utils/routes.ts index 396761c5d10..4efcca5d607 100644 --- a/public/app/features/provisioning/utils/routes.ts +++ b/public/app/features/provisioning/utils/routes.ts @@ -24,7 +24,7 @@ export function getProvisioningRoutes(): RouteDescriptor[] { { path: PROVISIONING_URL, component: SafeDynamicImport( - () => import(/* webpackChunkName: "RepositoryListPage"*/ 'app/features/provisioning/RepositoryListPage') + () => import(/* webpackChunkName: "RepositoryListPage"*/ 'app/features/provisioning/HomePage') ), }, {