index.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  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, Organization, Project} from 'sentry/types';
  13. import {useApiQuery} from 'sentry/utils/queryClient';
  14. import routeTitleGen from 'sentry/utils/routeTitle';
  15. import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
  16. import TextBlock from 'sentry/views/settings/components/text/textBlock';
  17. import PermissionAlert from 'sentry/views/settings/project/permissionAlert';
  18. type Props = RouteComponentProps<{}, {projectId: string}> & {
  19. organization: Organization;
  20. project: Project;
  21. };
  22. export default function ProjectIssueGrouping({organization, project, params}: Props) {
  23. const queryKey = `/projects/${organization.slug}/${project.slug}/grouping-configs/`;
  24. const {
  25. data: groupingConfigs,
  26. isLoading,
  27. isError,
  28. refetch,
  29. } = useApiQuery<EventGroupingConfig[]>([queryKey], {staleTime: 0, cacheTime: 0});
  30. if (isLoading) {
  31. return <LoadingIndicator />;
  32. }
  33. if (isError) {
  34. return (
  35. <LoadingError message={t('Failed to load grouping configs')} onRetry={refetch} />
  36. );
  37. }
  38. const handleSubmit = (response: Project) => {
  39. // This will update our project context
  40. ProjectsStore.onUpdateSuccess(response);
  41. };
  42. const endpoint = `/projects/${organization.slug}/${project.slug}/`;
  43. const access = new Set(organization.access.concat(project.access));
  44. const hasAccess = hasEveryAccess(['project:write'], {organization, project});
  45. const jsonFormProps = {
  46. additionalFieldProps: {
  47. organization,
  48. groupingConfigs,
  49. },
  50. features: new Set(organization.features),
  51. access,
  52. disabled: !hasAccess,
  53. };
  54. return (
  55. <SentryDocumentTitle
  56. title={routeTitleGen(t('Issue Grouping'), params.projectId, false)}
  57. >
  58. <SettingsPageHeader title={t('Issue Grouping')} />
  59. <TextBlock>
  60. {tct(
  61. `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].`,
  62. {
  63. link: (
  64. <ExternalLink href="https://docs.sentry.io/product/data-management-settings/event-grouping/" />
  65. ),
  66. }
  67. )}
  68. </TextBlock>
  69. <PermissionAlert project={project} />
  70. <Form
  71. saveOnBlur
  72. allowUndo
  73. initialData={project}
  74. apiMethod="PUT"
  75. apiEndpoint={endpoint}
  76. onSubmitSuccess={handleSubmit}
  77. >
  78. <JsonForm
  79. {...jsonFormProps}
  80. title={t('Fingerprint Rules')}
  81. fields={[fields.fingerprintingRules]}
  82. />
  83. <JsonForm
  84. {...jsonFormProps}
  85. title={t('Stack Trace Rules')}
  86. fields={[fields.groupingEnhancements]}
  87. />
  88. </Form>
  89. </SentryDocumentTitle>
  90. );
  91. }