|
@@ -8,7 +8,6 @@ import {fetchTagValues} from 'sentry/actionCreators/tags';
|
|
|
import Alert from 'sentry/components/alert';
|
|
|
import GuideAnchor from 'sentry/components/assistant/guideAnchor';
|
|
|
import DatePageFilter from 'sentry/components/datePageFilter';
|
|
|
-import EmptyStateWarning from 'sentry/components/emptyStateWarning';
|
|
|
import EnvironmentPageFilter from 'sentry/components/environmentPageFilter';
|
|
|
import ExternalLink from 'sentry/components/links/externalLink';
|
|
|
import LoadingIndicator from 'sentry/components/loadingIndicator';
|
|
@@ -18,12 +17,14 @@ import PageFiltersContainer from 'sentry/components/organizations/pageFilters/co
|
|
|
import {getRelativeSummary} from 'sentry/components/organizations/timeRangeSelector/utils';
|
|
|
import PageHeading from 'sentry/components/pageHeading';
|
|
|
import Pagination from 'sentry/components/pagination';
|
|
|
+import {Panel} from 'sentry/components/panels';
|
|
|
import ProjectPageFilter from 'sentry/components/projectPageFilter';
|
|
|
import SmartSearchBar from 'sentry/components/smartSearchBar';
|
|
|
import {ItemType} from 'sentry/components/smartSearchBar/types';
|
|
|
import {DEFAULT_STATS_PERIOD} from 'sentry/constants';
|
|
|
import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters';
|
|
|
import {releaseHealth} from 'sentry/data/platformCategories';
|
|
|
+import {IconSearch} from 'sentry/icons';
|
|
|
import {t} from 'sentry/locale';
|
|
|
import ProjectsStore from 'sentry/stores/projectsStore';
|
|
|
import {PageContent, PageHeader} from 'sentry/styles/organization';
|
|
@@ -44,6 +45,7 @@ import withOrganization from 'sentry/utils/withOrganization';
|
|
|
import withPageFilters from 'sentry/utils/withPageFilters';
|
|
|
import withProjects from 'sentry/utils/withProjects';
|
|
|
import AsyncView from 'sentry/views/asyncView';
|
|
|
+import EmptyMessage from 'sentry/views/settings/components/emptyMessage';
|
|
|
|
|
|
import ReleaseArchivedNotice from '../detail/overview/releaseArchivedNotice';
|
|
|
import {isMobileRelease} from '../utils';
|
|
@@ -292,35 +294,48 @@ class ReleasesList extends AsyncView<Props, State> {
|
|
|
}
|
|
|
|
|
|
renderEmptyMessage() {
|
|
|
- const {location, organization, selection} = this.props;
|
|
|
- const {statsPeriod} = location.query;
|
|
|
+ const {location} = this.props;
|
|
|
+ const {statsPeriod, start, end} = location.query;
|
|
|
const searchQuery = this.getQuery();
|
|
|
const activeSort = this.getSort();
|
|
|
const activeStatus = this.getStatus();
|
|
|
|
|
|
+ const selectedPeriod =
|
|
|
+ !!start && !!end
|
|
|
+ ? t('time range')
|
|
|
+ : getRelativeSummary(statsPeriod || DEFAULT_STATS_PERIOD).toLowerCase();
|
|
|
+
|
|
|
if (searchQuery && searchQuery.length) {
|
|
|
return (
|
|
|
- <EmptyStateWarning small>{`${t(
|
|
|
- 'There are no releases that match'
|
|
|
- )}: '${searchQuery}'.`}</EmptyStateWarning>
|
|
|
+ <Panel>
|
|
|
+ <EmptyMessage icon={<IconSearch size="xl" />} size="large">{`${t(
|
|
|
+ 'There are no releases that match'
|
|
|
+ )}: '${searchQuery}'.`}</EmptyMessage>
|
|
|
+ </Panel>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
if (activeSort === ReleasesSortOption.USERS_24_HOURS) {
|
|
|
return (
|
|
|
- <EmptyStateWarning small>
|
|
|
- {t('There are no releases with active user data (users in the last 24 hours).')}
|
|
|
- </EmptyStateWarning>
|
|
|
+ <Panel>
|
|
|
+ <EmptyMessage icon={<IconSearch size="xl" />} size="large">
|
|
|
+ {t(
|
|
|
+ 'There are no releases with active user data (users in the last 24 hours).'
|
|
|
+ )}
|
|
|
+ </EmptyMessage>
|
|
|
+ </Panel>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
if (activeSort === ReleasesSortOption.SESSIONS_24_HOURS) {
|
|
|
return (
|
|
|
- <EmptyStateWarning small>
|
|
|
- {t(
|
|
|
- 'There are no releases with active session data (sessions in the last 24 hours).'
|
|
|
- )}
|
|
|
- </EmptyStateWarning>
|
|
|
+ <Panel>
|
|
|
+ <EmptyMessage icon={<IconSearch size="xl" />} size="large">
|
|
|
+ {t(
|
|
|
+ 'There are no releases with active session data (sessions in the last 24 hours).'
|
|
|
+ )}
|
|
|
+ </EmptyMessage>
|
|
|
+ </Panel>
|
|
|
);
|
|
|
}
|
|
|
|
|
@@ -329,37 +344,40 @@ class ReleasesList extends AsyncView<Props, State> {
|
|
|
activeSort === ReleasesSortOption.SEMVER
|
|
|
) {
|
|
|
return (
|
|
|
- <EmptyStateWarning small>
|
|
|
- {t('There are no releases with semantic versioning.')}
|
|
|
- </EmptyStateWarning>
|
|
|
+ <Panel>
|
|
|
+ <EmptyMessage icon={<IconSearch size="xl" />} size="large">
|
|
|
+ {t('There are no releases with semantic versioning.')}
|
|
|
+ </EmptyMessage>
|
|
|
+ </Panel>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
if (activeSort !== ReleasesSortOption.DATE) {
|
|
|
- const relativePeriod = getRelativeSummary(
|
|
|
- statsPeriod || DEFAULT_STATS_PERIOD
|
|
|
- ).toLowerCase();
|
|
|
-
|
|
|
return (
|
|
|
- <EmptyStateWarning small>
|
|
|
- {`${t('There are no releases with data in the')} ${relativePeriod}.`}
|
|
|
- </EmptyStateWarning>
|
|
|
+ <Panel>
|
|
|
+ <EmptyMessage icon={<IconSearch size="xl" />} size="large">
|
|
|
+ {`${t('There are no releases with data in the')} ${selectedPeriod}.`}
|
|
|
+ </EmptyMessage>
|
|
|
+ </Panel>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
if (activeStatus === ReleasesStatusOption.ARCHIVED) {
|
|
|
return (
|
|
|
- <EmptyStateWarning small>
|
|
|
- {t('There are no archived releases.')}
|
|
|
- </EmptyStateWarning>
|
|
|
+ <Panel>
|
|
|
+ <EmptyMessage icon={<IconSearch size="xl" />} size="large">
|
|
|
+ {t('There are no archived releases.')}
|
|
|
+ </EmptyMessage>
|
|
|
+ </Panel>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
return (
|
|
|
- <ReleasesPromo
|
|
|
- organization={organization}
|
|
|
- projectId={selection.projects.filter(p => p !== ALL_ACCESS_PROJECTS)[0]}
|
|
|
- />
|
|
|
+ <Panel>
|
|
|
+ <EmptyMessage icon={<IconSearch size="xl" />} size="large">
|
|
|
+ {`${t('There are no releases with data in the')} ${selectedPeriod}.`}
|
|
|
+ </EmptyMessage>
|
|
|
+ </Panel>
|
|
|
);
|
|
|
}
|
|
|
|
|
@@ -413,14 +431,26 @@ class ReleasesList extends AsyncView<Props, State> {
|
|
|
const {location, selection, organization, router} = this.props;
|
|
|
const {releases, reloading, releasesPageLinks} = this.state;
|
|
|
|
|
|
+ const selectedProject = this.getSelectedProject();
|
|
|
+ const hasReleasesSetup = selectedProject?.features.includes('releases');
|
|
|
+
|
|
|
if (this.shouldShowLoadingIndicator()) {
|
|
|
return <LoadingIndicator />;
|
|
|
}
|
|
|
|
|
|
- if (!releases?.length) {
|
|
|
+ if (!releases?.length && hasReleasesSetup) {
|
|
|
return this.renderEmptyMessage();
|
|
|
}
|
|
|
|
|
|
+ if (!releases?.length && !hasReleasesSetup) {
|
|
|
+ return (
|
|
|
+ <ReleasesPromo
|
|
|
+ organization={organization}
|
|
|
+ projectId={selection.projects.filter(p => p !== ALL_ACCESS_PROJECTS)[0]}
|
|
|
+ />
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
return (
|
|
|
<ReleasesRequest
|
|
|
releases={releases.map(({version}) => version)}
|
|
@@ -435,7 +465,6 @@ class ReleasesList extends AsyncView<Props, State> {
|
|
|
const singleProjectSelected =
|
|
|
selection.projects?.length === 1 &&
|
|
|
selection.projects[0] !== ALL_ACCESS_PROJECTS;
|
|
|
- const selectedProject = this.getSelectedProject();
|
|
|
const isMobileProject =
|
|
|
selectedProject?.platform && isMobileRelease(selectedProject.platform);
|
|
|
|