index.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import type {RouteComponentProps} from 'react-router';
  2. import {hasEveryAccess} from 'sentry/components/acl/access';
  3. import Form from 'sentry/components/forms/form';
  4. import JsonForm from 'sentry/components/forms/jsonForm';
  5. import ExternalLink from 'sentry/components/links/externalLink';
  6. import LoadingError from 'sentry/components/loadingError';
  7. import LoadingIndicator from 'sentry/components/loadingIndicator';
  8. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  9. import {fields} from 'sentry/data/forms/projectIssueGrouping';
  10. import {t, tct} from 'sentry/locale';
  11. import ProjectsStore from 'sentry/stores/projectsStore';
  12. import type {EventGroupingConfig} from 'sentry/types/event';
  13. import type {Organization} from 'sentry/types/organization';
  14. import type {Project} from 'sentry/types/project';
  15. import {useApiQuery} from 'sentry/utils/queryClient';
  16. import routeTitleGen from 'sentry/utils/routeTitle';
  17. import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
  18. import TextBlock from 'sentry/views/settings/components/text/textBlock';
  19. import PermissionAlert from 'sentry/views/settings/project/permissionAlert';
  20. type Props = RouteComponentProps<{}, {projectId: string}> & {
  21. organization: Organization;
  22. project: Project;
  23. };
  24. export default function ProjectIssueGrouping({organization, project, params}: Props) {
  25. const queryKey = `/projects/${organization.slug}/${project.slug}/grouping-configs/`;
  26. const {
  27. data: groupingConfigs,
  28. isPending,
  29. isError,
  30. refetch,
  31. } = useApiQuery<EventGroupingConfig[]>([queryKey], {staleTime: 0, cacheTime: 0});
  32. if (isPending) {
  33. return <LoadingIndicator />;
  34. }
  35. if (isError) {
  36. return (
  37. <LoadingError message={t('Failed to load grouping configs')} onRetry={refetch} />
  38. );
  39. }
  40. const handleSubmit = (response: Project) => {
  41. // This will update our project context
  42. ProjectsStore.onUpdateSuccess(response);
  43. };
  44. const endpoint = `/projects/${organization.slug}/${project.slug}/`;
  45. const access = new Set(organization.access.concat(project.access));
  46. const hasAccess = hasEveryAccess(['project:write'], {organization, project});
  47. const jsonFormProps = {
  48. additionalFieldProps: {
  49. organization,
  50. groupingConfigs,
  51. },
  52. features: new Set(organization.features),
  53. access,
  54. disabled: !hasAccess,
  55. };
  56. return (
  57. <SentryDocumentTitle
  58. title={routeTitleGen(t('Issue Grouping'), params.projectId, false)}
  59. >
  60. <SettingsPageHeader title={t('Issue Grouping')} />
  61. <TextBlock>
  62. {tct(
  63. `All events have a fingerprint. Events with the same fingerprint are grouped together into an issue. To learn more about issue grouping, [link: read the docs].`,
  64. {
  65. link: (
  66. <ExternalLink href="https://docs.sentry.io/product/data-management-settings/event-grouping/" />
  67. ),
  68. }
  69. )}
  70. </TextBlock>
  71. <PermissionAlert project={project} />
  72. <Form
  73. saveOnBlur
  74. allowUndo
  75. initialData={project}
  76. apiMethod="PUT"
  77. apiEndpoint={endpoint}
  78. onSubmitSuccess={handleSubmit}
  79. >
  80. <JsonForm
  81. {...jsonFormProps}
  82. title={t('Fingerprint Rules')}
  83. fields={[fields.fingerprintingRules]}
  84. />
  85. <JsonForm
  86. {...jsonFormProps}
  87. title={t('Stack Trace Rules')}
  88. fields={[fields.groupingEnhancements]}
  89. />
  90. </Form>
  91. </SentryDocumentTitle>
  92. );
  93. }