|
@@ -1,21 +1,21 @@
|
|
|
+import {useState} from 'react';
|
|
|
import {browserHistory} from 'react-router';
|
|
|
import styled from '@emotion/styled';
|
|
|
+import debounce from 'lodash/debounce';
|
|
|
|
|
|
-import {CompactSelect} from 'sentry/components/compactSelect';
|
|
|
+import {CompactSelect, SelectOption} from 'sentry/components/compactSelect';
|
|
|
import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
|
|
|
-import {t} from 'sentry/locale';
|
|
|
+import {DEFAULT_DEBOUNCE_DURATION} from 'sentry/constants';
|
|
|
+import {t, tn} from 'sentry/locale';
|
|
|
+import {space} from 'sentry/styles/space';
|
|
|
import {defined} from 'sentry/utils';
|
|
|
import {useLocation} from 'sentry/utils/useLocation';
|
|
|
import {
|
|
|
useReleases,
|
|
|
useReleaseSelection,
|
|
|
+ useReleaseStats,
|
|
|
} from 'sentry/views/starfish/queries/useReleases';
|
|
|
|
|
|
-const ALL_RELEASES = {
|
|
|
- value: '',
|
|
|
- label: t('All Releases'),
|
|
|
-};
|
|
|
-
|
|
|
type Props = {
|
|
|
selectorKey: string;
|
|
|
selectorName?: string;
|
|
@@ -23,25 +23,51 @@ type Props = {
|
|
|
};
|
|
|
|
|
|
export function ReleaseSelector({selectorName, selectorKey, selectorValue}: Props) {
|
|
|
- const {data, isLoading} = useReleases();
|
|
|
+ const [searchTerm, setSearchTerm] = useState<string | undefined>(undefined);
|
|
|
+ const {data, isLoading} = useReleases(searchTerm);
|
|
|
+ const {data: releaseStats} = useReleaseStats();
|
|
|
const location = useLocation();
|
|
|
- let value = selectorValue;
|
|
|
|
|
|
- if (!isLoading && !defined(value)) {
|
|
|
- value = ALL_RELEASES.value;
|
|
|
+ const options: SelectOption<string>[] = [];
|
|
|
+ if (defined(selectorValue)) {
|
|
|
+ options.push({
|
|
|
+ value: selectorValue,
|
|
|
+ label: selectorValue,
|
|
|
+ });
|
|
|
}
|
|
|
+ data
|
|
|
+ ?.filter(({version}) => selectorValue !== version)
|
|
|
+ .forEach(release => {
|
|
|
+ const option = {
|
|
|
+ value: release.version,
|
|
|
+ label: release.version,
|
|
|
+ details: (
|
|
|
+ <LabelDetails sessionCount={releaseStats[release.version]?.['sum(session)']} />
|
|
|
+ ),
|
|
|
+ };
|
|
|
+
|
|
|
+ options.push(option);
|
|
|
+ });
|
|
|
+
|
|
|
return (
|
|
|
<StyledCompactSelect
|
|
|
triggerProps={{
|
|
|
prefix: selectorName,
|
|
|
+ title: selectorValue,
|
|
|
}}
|
|
|
+ loading={isLoading}
|
|
|
+ searchable
|
|
|
value={selectorValue}
|
|
|
options={[
|
|
|
- ...(data ?? [ALL_RELEASES]).map(release => ({
|
|
|
- value: release.version,
|
|
|
- label: release.shortVersion ?? release.version,
|
|
|
- })),
|
|
|
+ {
|
|
|
+ value: '_releases',
|
|
|
+ label: t('Sorted by date created'),
|
|
|
+ options,
|
|
|
+ },
|
|
|
]}
|
|
|
+ onSearch={debounce(val => {
|
|
|
+ setSearchTerm(val);
|
|
|
+ }, DEFAULT_DEBOUNCE_DURATION)}
|
|
|
onChange={newValue => {
|
|
|
browserHistory.push({
|
|
|
...location,
|
|
@@ -51,10 +77,29 @@ export function ReleaseSelector({selectorName, selectorKey, selectorValue}: Prop
|
|
|
},
|
|
|
});
|
|
|
}}
|
|
|
+ onClose={() => {
|
|
|
+ setSearchTerm(undefined);
|
|
|
+ }}
|
|
|
/>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
+type LabelDetailsProps = {
|
|
|
+ sessionCount?: number;
|
|
|
+};
|
|
|
+
|
|
|
+function LabelDetails(props: LabelDetailsProps) {
|
|
|
+ return (
|
|
|
+ <DetailsContainer>
|
|
|
+ <div>
|
|
|
+ {defined(props.sessionCount)
|
|
|
+ ? tn('%s session', '%s sessions', props.sessionCount)
|
|
|
+ : '-'}
|
|
|
+ </div>
|
|
|
+ </DetailsContainer>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
export function ReleaseComparisonSelector() {
|
|
|
const {primaryRelease, secondaryRelease} = useReleaseSelection();
|
|
|
return (
|
|
@@ -74,3 +119,10 @@ const StyledCompactSelect = styled(CompactSelect)`
|
|
|
max-width: 275px;
|
|
|
}
|
|
|
`;
|
|
|
+
|
|
|
+const DetailsContainer = styled('div')`
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ justify-content: space-between;
|
|
|
+ gap: ${space(1)};
|
|
|
+`;
|