import { render } from 'react-dom';
import { Helmet, HelmetProvider } from 'react-helmet-async';
import { IntlShape, RawIntlProvider } from 'react-intl';
-import { BrowserRouter, Route, Routes } from 'react-router-dom';
+import {
+ Route,
+ RouterProvider,
+ createBrowserRouter,
+ createRoutesFromElements,
+} from 'react-router-dom';
import accountRoutes from '../../apps/account/routes';
import auditLogsRoutes from '../../apps/audit-logs/routes';
import backgroundTasksRoutes from '../../apps/background-tasks/routes';
);
}
+const router = createBrowserRouter(
+ createRoutesFromElements(
+ <>
+ {renderRedirects()}
+
+ <Route path="formatting/help" element={<FormattingHelp />} />
+
+ <Route element={<SimpleContainer />}>{maintenanceRoutes()}</Route>
+
+ <Route element={<MigrationContainer />}>
+ {sessionsRoutes()}
+
+ <Route path="/" element={<App />}>
+ <Route index element={<Landing />} />
+
+ <Route element={<GlobalContainer />}>
+ {accountRoutes()}
+
+ {codingRulesRoutes()}
+
+ <Route path="extension/:pluginKey/:extensionKey" element={<GlobalPageExtension />} />
+
+ {globalIssuesRoutes()}
+
+ {projectsRoutes()}
+
+ {qualityGatesRoutes()}
+ {qualityProfilesRoutes()}
+
+ <Route path="portfolios" element={<PortfoliosPage />} />
+
+ <Route path="sonarlint/auth" element={<SonarLintConnection />} />
+
+ {webAPIRoutes()}
+ {webAPIRoutesV2()}
+
+ {renderComponentRoutes()}
+
+ {renderAdminRoutes()}
+ </Route>
+ <Route
+ // We don't want this route to have any menu.
+ // That is why we can not have it under the accountRoutes
+ path="account/reset_password"
+ element={<ResetPassword />}
+ />
+
+ <Route
+ // We don't want this route to have any menu. This is why we define it here
+ // rather than under the admin routes.
+ path="admin/change_admin_password"
+ element={<ChangeAdminPasswordApp />}
+ />
+
+ <Route
+ // We don't want this route to have any menu. This is why we define it here
+ // rather than under the admin routes.
+ path="admin/plugin_risk_consent"
+ element={<PluginRiskConsent />}
+ />
+ <Route path="not_found" element={<NotFound />} />
+ <Route path="*" element={<NotFound />} />
+ </Route>
+ </Route>
+ </>,
+ ),
+ { basename: getBaseUrl() },
+);
+
const queryClient = new QueryClient();
export default function startReactApp(
<ThemeProvider theme={lightTheme}>
<QueryClientProvider client={queryClient}>
<GlobalMessagesContainer />
- <BrowserRouter basename={getBaseUrl()}>
- <Helmet titleTemplate={translate('page_title.template.default')} />
- <Routes>
- {renderRedirects()}
-
- <Route path="formatting/help" element={<FormattingHelp />} />
-
- <Route element={<SimpleContainer />}>{maintenanceRoutes()}</Route>
-
- <Route element={<MigrationContainer />}>
- {sessionsRoutes()}
-
- <Route path="/" element={<App />}>
- <Route index element={<Landing />} />
-
- <Route element={<GlobalContainer />}>
- {accountRoutes()}
-
- {codingRulesRoutes()}
-
- <Route
- path="extension/:pluginKey/:extensionKey"
- element={<GlobalPageExtension />}
- />
-
- {globalIssuesRoutes()}
-
- {projectsRoutes()}
-
- {qualityGatesRoutes()}
- {qualityProfilesRoutes()}
-
- <Route path="portfolios" element={<PortfoliosPage />} />
-
- <Route path="sonarlint/auth" element={<SonarLintConnection />} />
-
- {webAPIRoutes()}
- {webAPIRoutesV2()}
-
- {renderComponentRoutes()}
-
- {renderAdminRoutes()}
- </Route>
- <Route
- // We don't want this route to have any menu.
- // That is why we can not have it under the accountRoutes
- path="account/reset_password"
- element={<ResetPassword />}
- />
-
- <Route
- // We don't want this route to have any menu. This is why we define it here
- // rather than under the admin routes.
- path="admin/change_admin_password"
- element={<ChangeAdminPasswordApp />}
- />
-
- <Route
- // We don't want this route to have any menu. This is why we define it here
- // rather than under the admin routes.
- path="admin/plugin_risk_consent"
- element={<PluginRiskConsent />}
- />
- <Route path="not_found" element={<NotFound />} />
- <Route path="*" element={<NotFound />} />
- </Route>
- </Route>
- </Routes>
- </BrowserRouter>
+ <Helmet titleTemplate={translate('page_title.template.default')} />
+ <RouterProvider router={router} />
</QueryClientProvider>
</ThemeProvider>
</RawIntlProvider>
selectedAlmInstance?: AlmSettingsInstance;
}
-const REPOSITORY_PAGE_SIZE = 20;
+const REPOSITORY_PAGE_SIZE = 50;
export default class GitHubProjectCreate extends React.Component<Props, State> {
mounted = false;
almSetting: 'conf-github-2',
organization: 'org-1',
page: 1,
- pageSize: 20,
+ pageSize: 50,
query: 'search',
});
});
almSetting: 'conf-github-2',
organization: 'org-1',
page: 2,
- pageSize: 20,
+ pageSize: 50,
query: '',
});
expect(loadMore).not.toBeInTheDocument();
import * as React from 'react';
import { useEffect } from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
-import { useNavigate } from 'react-router-dom';
+import { useNavigate, unstable_usePrompt as usePrompt } from 'react-router-dom';
import NewCodeDefinitionSelector from '../../../../components/new-code-definition/NewCodeDefinitionSelector';
import { useDocUrl } from '../../../../helpers/docs';
import { addGlobalSuccessMessage } from '../../../../helpers/globalMessages';
import { NewCodeDefinitiondWithCompliance } from '../../../../types/new-code-definition';
import { ImportProjectParam } from '../CreateProjectPage';
+const listener = (event: BeforeUnloadEvent) => {
+ event.returnValue = true;
+};
+
interface Props {
importProjects: ImportProjectParam;
}
const intl = useIntl();
const navigate = useNavigate();
const getDocUrl = useDocUrl();
+ usePrompt({
+ when: isLoading,
+ message: translate('onboarding.create_project.please_dont_leave'),
+ });
const projectCount = importProjects.projects.length;
const isMultipleProjects = projectCount > 1;
}
}, [data, projectCount, mutateCount, reset, intl, navigate]);
+ React.useEffect(() => {
+ if (isLoading) {
+ window.addEventListener('beforeunload', listener);
+ }
+
+ return () => window.removeEventListener('beforeunload', listener);
+ }, [isLoading]);
+
const handleProjectCreation = () => {
if (selectedDefinition) {
importProjects.projects.forEach((p) => {
</FlagMessage>
)}
- <div className="sw-mt-10 sw-mb-8">
- <ButtonSecondary className="sw-mr-2" onClick={() => navigate(-1)}>
- {translate('back')}
- </ButtonSecondary>
+ <div className="sw-mt-10 sw-mb-8 sw-flex sw-gap-2 sw-items-center">
+ <ButtonSecondary onClick={() => navigate(-1)}>{translate('back')}</ButtonSecondary>
<ButtonPrimary
onClick={handleProjectCreation}
disabled={!selectedDefinition?.isCompliant || isLoading}
/>
<Spinner className="sw-ml-2" loading={isLoading} />
</ButtonPrimary>
+ {isLoading && (
+ <FlagMessage variant="warning">
+ <FormattedMessage
+ id="onboarding.create_project.import_in_progress"
+ values={{
+ count: projectCount - mutateCount,
+ total: projectCount,
+ }}
+ />
+ </FlagMessage>
+ )}
</div>
</div>
);
import * as React from 'react';
import { HelmetProvider } from 'react-helmet-async';
import { IntlProvider, ReactIntlErrorCode } from 'react-intl';
-import { MemoryRouter, Outlet, Route, Routes, parsePath } from 'react-router-dom';
+import {
+ MemoryRouter,
+ Outlet,
+ Route,
+ RouterProvider,
+ Routes,
+ createMemoryRouter,
+ createRoutesFromElements,
+ parsePath,
+} from 'react-router-dom';
import AdminContext from '../app/components/AdminContext';
import GlobalMessagesContainer from '../app/components/GlobalMessagesContainer';
import AppStateContextProvider from '../app/components/app-state/AppStateContextProvider';
}: RenderContext = {},
): RenderResult {
const path = parsePath(navigateTo);
- path.pathname = `/${path.pathname}`;
+ if (!path.pathname?.startsWith('/')) {
+ path.pathname = `/${path.pathname}`;
+ }
const queryClient = new QueryClient();
+ const router = createMemoryRouter(
+ createRoutesFromElements(
+ <>
+ {children}
+ <Route path="*" element={<CatchAll />} />
+ </>,
+ ),
+ { initialEntries: [path] },
+ );
+
return render(
<HelmetProvider context={{}}>
<IntlWrapper>
<IndexationContextProvider>
<QueryClientProvider client={queryClient}>
<GlobalMessagesContainer />
- <MemoryRouter initialEntries={[path]}>
- <Routes>
- {children}
- <Route path="*" element={<CatchAll />} />
- </Routes>
- </MemoryRouter>
+ <RouterProvider router={router} />
</QueryClientProvider>
</IndexationContextProvider>
</AppStateContextProvider>
onboarding.create_project.bitbucket.subtitle=Import projects from one of your Bitbucket server workspaces
onboarding.create_project.x_repositories_selected={count} {count, plural, one {repository} other {repositories}} selected
onboarding.create_project.x_repository_created={count} {count, plural, one {repository} other {repositories}} will be created as a project on SonarQube
+onboarding.create_project.please_dont_leave=If you leave the page the import could fail. Are you sure you want to leave?
+onboarding.create_project.import_in_progress={count} of {total} projects is imported. Please do not close this page until the import is done.
onboarding.create_project.new_code_definition.title=Set up project for Clean as You Code
onboarding.create_x_project.new_code_definition.title=Set up {count, plural, one {project} other {# projects}} for Clean as You Code