import ExternalLink from 'sentry/components/links/externalLink';
import {DEFAULT_QUERY, NEW_DEFAULT_QUERY} from 'sentry/constants';
import {t, tct} from 'sentry/locale';
import type {Organization} from 'sentry/types/organization';
export enum Query {
FOR_REVIEW = 'is:unresolved is:for_review assigned_or_suggested:[me, my_teams, none]',
PRIORITIZED = NEW_DEFAULT_QUERY,
UNRESOLVED = 'is:unresolved',
IGNORED = 'is:ignored',
NEW = 'is:new',
ARCHIVED = 'is:archived',
ESCALATING = 'is:escalating',
REGRESSED = 'is:regressed',
REPROCESSING = 'is:reprocessing',
}
export const CUSTOM_TAB_VALUE = '__custom__';
type OverviewTab = {
/**
* Emitted analytics event tab name
*/
analyticsName: string;
/**
* Will fetch a count to display on this tab
*/
count: boolean;
/**
* Tabs can be disabled via flag
*/
enabled: boolean;
name: string;
hidden?: boolean;
/**
* Tooltip text to be hoverable when text has links
*/
tooltipHoverable?: boolean;
/**
* Tooltip text for each tab
*/
tooltipTitle?: React.ReactNode;
};
/**
* Get a list of currently active tabs
*/
export function getTabs(organization: Organization) {
const hasIssuePriority = organization.features.includes('issue-priority-ui');
const tabs: Array<[string, OverviewTab]> = [
[
Query.PRIORITIZED,
{
name: t('Prioritized'),
analyticsName: 'prioritized',
count: true,
enabled: hasIssuePriority,
},
],
[
Query.UNRESOLVED,
{
name: t('Unresolved'),
analyticsName: 'unresolved',
count: true,
enabled: !hasIssuePriority,
},
],
[
Query.FOR_REVIEW,
{
name: t('For Review'),
analyticsName: 'needs_review',
count: true,
enabled: true,
tooltipTitle: t(
'Issues are marked for review if they are new or escalating, and have not been resolved or archived. Issues are automatically marked reviewed in 7 days.'
),
},
],
[
Query.REGRESSED,
{
name: t('Regressed'),
analyticsName: 'regressed',
count: true,
enabled: true,
},
],
[
Query.ESCALATING,
{
name: t('Escalating'),
analyticsName: 'escalating',
count: true,
enabled: true,
},
],
[
Query.ARCHIVED,
{
name: t('Archived'),
analyticsName: 'archived',
count: true,
enabled: true,
},
],
[
Query.IGNORED,
{
name: t('Ignored'),
analyticsName: 'ignored',
count: true,
enabled: false,
tooltipTitle: t(`Ignored issues don’t trigger alerts. When their ignore
conditions are met they become Unresolved and are flagged for review.`),
},
],
[
Query.REPROCESSING,
{
name: t('Reprocessing'),
analyticsName: 'reprocessing',
count: true,
enabled: organization.features.includes('reprocessing-v2'),
tooltipTitle: tct(
`These [link:reprocessing issues] will take some time to complete.
Any new issues that are created during reprocessing will be flagged for review.`,
{
link: (
),
}
),
tooltipHoverable: true,
},
],
[
// Hidden tab to account for custom queries that don't match any of the queries
// above. It's necessary because if Tabs's value doesn't match that of any tab item
// then Tabs will fall back to a default value, causing unexpected behaviors.
CUSTOM_TAB_VALUE,
{
name: t('Custom'),
analyticsName: 'custom',
hidden: true,
count: false,
enabled: true,
},
],
];
return tabs.filter(([_query, tab]) => tab.enabled);
}
/**
* @returns queries that should have counts fetched
*/
export function getTabsWithCounts(organization: Organization) {
const tabs = getTabs(organization);
return tabs.filter(([_query, tab]) => tab.count).map(([query]) => query);
}
export function isForReviewQuery(query: string | undefined) {
return !!query && /\bis:for_review\b/.test(query);
}
// the tab counts will look like 99+
export const TAB_MAX_COUNT = 99;
type QueryCount = {
count: number;
hasMore: boolean;
};
export type QueryCounts = Partial>;
export enum IssueSortOptions {
DATE = 'date',
NEW = 'new',
TRENDS = 'trends',
FREQ = 'freq',
USER = 'user',
INBOX = 'inbox',
}
export const DEFAULT_ISSUE_STREAM_SORT = IssueSortOptions.DATE;
export function isDefaultIssueStreamSearch(
{query, sort}: {query: string; sort: string},
{organization}: {organization: Organization}
) {
const defaultQuery = organization.features.includes('issue-priority-ui')
? NEW_DEFAULT_QUERY
: DEFAULT_QUERY;
return query === defaultQuery && sort === DEFAULT_ISSUE_STREAM_SORT;
}
export function getSortLabel(key: string) {
switch (key) {
case IssueSortOptions.NEW:
return t('First Seen');
case IssueSortOptions.TRENDS:
return t('Trends');
case IssueSortOptions.FREQ:
return t('Events');
case IssueSortOptions.USER:
return t('Users');
case IssueSortOptions.INBOX:
return t('Date Added');
case IssueSortOptions.DATE:
default:
return t('Last Seen');
}
}
export const DISCOVER_EXCLUSION_FIELDS: string[] = [
'query',
'status',
'bookmarked_by',
'assigned',
'assigned_to',
'unassigned',
'subscribed_by',
'active_at',
'first_release',
'first_seen',
'is',
'__text',
];
export const FOR_REVIEW_QUERIES: string[] = [Query.FOR_REVIEW];
export const SAVED_SEARCHES_SIDEBAR_OPEN_LOCALSTORAGE_KEY =
'issue-stream-saved-searches-sidebar-open';