diff --git a/static/app/components/onboarding/gettingStartedDoc/storeCrashReportsConfig.tsx b/static/app/components/onboarding/gettingStartedDoc/storeCrashReportsConfig.tsx
new file mode 100644
index 00000000000000..f044081de4b80d
--- /dev/null
+++ b/static/app/components/onboarding/gettingStartedDoc/storeCrashReportsConfig.tsx
@@ -0,0 +1,74 @@
+import styled from '@emotion/styled';
+
+import {addErrorMessage} from 'sentry/actionCreators/indicator';
+import {hasEveryAccess} from 'sentry/components/acl/access';
+import Form from 'sentry/components/forms/form';
+import JsonForm from 'sentry/components/forms/jsonForm';
+import Panel from 'sentry/components/panels/panel';
+import Placeholder from 'sentry/components/placeholder';
+import projectSecurityAndPrivacyGroups from 'sentry/data/forms/projectSecurityAndPrivacyGroups';
+import ProjectsStore from 'sentry/stores/projectsStore';
+import type {Organization} from 'sentry/types/organization';
+import type {Project} from 'sentry/types/project';
+import {useDetailedProject} from 'sentry/utils/useDetailedProject';
+
+interface StoreCrashReportsConfigProps {
+ organization: Organization;
+ projectSlug: Project['slug'];
+}
+
+export function StoreCrashReportsConfig({
+ projectSlug,
+ organization,
+}: StoreCrashReportsConfigProps) {
+ const {data: project, isPending: isPendingProject} = useDetailedProject({
+ orgSlug: organization.slug,
+ projectSlug,
+ });
+
+ if (isPendingProject) {
+ // 72px is the height of the form
+ return ;
+ }
+
+ const storeCrashReportsField = projectSecurityAndPrivacyGroups
+ .flatMap(group => group.fields)
+ .find(field => field.name === 'storeCrashReports');
+
+ if (!project || !storeCrashReportsField) {
+ return null;
+ }
+
+ return (
+
+ );
+}
+
+const StyledJsonForm = styled(JsonForm)`
+ ${Panel} {
+ margin-bottom: 0;
+ }
+`;
diff --git a/static/app/data/forms/projectSecurityAndPrivacyGroups.tsx b/static/app/data/forms/projectSecurityAndPrivacyGroups.tsx
index 452676a38f3256..087b84a70adcc6 100644
--- a/static/app/data/forms/projectSecurityAndPrivacyGroups.tsx
+++ b/static/app/data/forms/projectSecurityAndPrivacyGroups.tsx
@@ -43,9 +43,11 @@ function hasProjectWriteAndOrgOverride({
function projectWriteAndOrgOverrideDisabledReason({
organization,
name,
+ project,
}: {
name: string;
organization: Organization;
+ project: Project;
}) {
if (hasOrgOverride({organization, name})) {
return t(
@@ -53,6 +55,10 @@ function projectWriteAndOrgOverrideDisabledReason({
);
}
+ if (!hasEveryAccess(['project:write'], {organization, project})) {
+ return t("You do not have permission to modify this project's setting.");
+ }
+
return null;
}
@@ -61,6 +67,8 @@ const formGroups: JsonFormObject[] = [
title: t('Security & Privacy'),
fields: [
{
+ disabled: hasProjectWriteAndOrgOverride,
+ disabledReason: projectWriteAndOrgOverrideDisabledReason,
name: 'storeCrashReports',
type: 'select',
label: t('Store Minidumps As Attachments'),
diff --git a/static/app/gettingStartedDocs/minidump/minidump.spec.tsx b/static/app/gettingStartedDocs/minidump/minidump.spec.tsx
index 7b92a54edb0061..aeb707c47cc637 100644
--- a/static/app/gettingStartedDocs/minidump/minidump.spec.tsx
+++ b/static/app/gettingStartedDocs/minidump/minidump.spec.tsx
@@ -1,10 +1,21 @@
+import {ProjectFixture} from 'sentry-fixture/project';
+
import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout';
import {screen} from 'sentry-test/reactTestingLibrary';
import docs from './minidump';
+function renderMockRequests() {
+ MockApiClient.addMockResponse({
+ url: '/projects/org-slug/project-slug/',
+ body: [ProjectFixture()],
+ });
+}
+
describe('getting started with minidump', function () {
it('renders gradle docs correctly', function () {
+ renderMockRequests();
+
renderWithOnboardingLayout(docs);
// Renders main headings
diff --git a/static/app/gettingStartedDocs/minidump/minidump.tsx b/static/app/gettingStartedDocs/minidump/minidump.tsx
index 00140f5dc47539..c749e67adafbd1 100644
--- a/static/app/gettingStartedDocs/minidump/minidump.tsx
+++ b/static/app/gettingStartedDocs/minidump/minidump.tsx
@@ -3,6 +3,7 @@ import {Fragment} from 'react';
import ExternalLink from 'sentry/components/links/externalLink';
import List from 'sentry/components/list';
import ListItem from 'sentry/components/list/listItem';
+import {StoreCrashReportsConfig} from 'sentry/components/onboarding/gettingStartedDoc/storeCrashReportsConfig';
import type {
Docs,
DocsParams,
@@ -70,7 +71,17 @@ const onboarding: OnboardingConfig = {
},
],
configure: () => [],
- verify: () => [],
+ verify: params => [
+ {
+ title: t('Further Settings'),
+ description: (
+
+ ),
+ },
+ ],
};
const docs: Docs = {
diff --git a/static/app/gettingStartedDocs/unity/unity.spec.tsx b/static/app/gettingStartedDocs/unity/unity.spec.tsx
index 35a5d86522899d..3969e81c50077a 100644
--- a/static/app/gettingStartedDocs/unity/unity.spec.tsx
+++ b/static/app/gettingStartedDocs/unity/unity.spec.tsx
@@ -1,11 +1,22 @@
+import {ProjectFixture} from 'sentry-fixture/project';
+
import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout';
import {screen} from 'sentry-test/reactTestingLibrary';
import {textWithMarkupMatcher} from 'sentry-test/utils';
import docs from './unity';
+function renderMockRequests() {
+ MockApiClient.addMockResponse({
+ url: '/projects/org-slug/project-slug/',
+ body: [ProjectFixture()],
+ });
+}
+
describe('unity onboarding docs', function () {
it('renders docs correctly', async function () {
+ renderMockRequests();
+
renderWithOnboardingLayout(docs, {
releaseRegistry: {
'sentry.dotnet.unity': {
diff --git a/static/app/gettingStartedDocs/unity/unity.tsx b/static/app/gettingStartedDocs/unity/unity.tsx
index b0a8aa3a641b97..6751b00beaf6e3 100644
--- a/static/app/gettingStartedDocs/unity/unity.tsx
+++ b/static/app/gettingStartedDocs/unity/unity.tsx
@@ -4,6 +4,7 @@ import styled from '@emotion/styled';
import {Alert} from 'sentry/components/alert';
import ExternalLink from 'sentry/components/links/externalLink';
import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
+import {StoreCrashReportsConfig} from 'sentry/components/onboarding/gettingStartedDoc/storeCrashReportsConfig';
import type {
Docs,
OnboardingConfig,
@@ -94,7 +95,7 @@ const onboarding: OnboardingConfig = {
),
},
],
- verify: () => [
+ verify: params => [
{
type: StepType.VERIFY,
description: t(
@@ -130,6 +131,15 @@ const onboarding: OnboardingConfig = {
),
},
+ {
+ title: t('Further Settings'),
+ description: (
+
+ ),
+ },
],
};
diff --git a/static/app/gettingStartedDocs/unreal/unreal.spec.tsx b/static/app/gettingStartedDocs/unreal/unreal.spec.tsx
index 1183a45c7db3c7..46f659cb460cd7 100644
--- a/static/app/gettingStartedDocs/unreal/unreal.spec.tsx
+++ b/static/app/gettingStartedDocs/unreal/unreal.spec.tsx
@@ -1,10 +1,21 @@
+import {ProjectFixture} from 'sentry-fixture/project';
+
import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout';
import {screen} from 'sentry-test/reactTestingLibrary';
import docs from './unreal';
+function renderMockRequests() {
+ MockApiClient.addMockResponse({
+ url: '/projects/org-slug/project-slug/',
+ body: [ProjectFixture()],
+ });
+}
+
describe('getting started with unreal', function () {
it('renders docs correctly', function () {
+ renderMockRequests();
+
renderWithOnboardingLayout(docs);
// Renders main headings
diff --git a/static/app/gettingStartedDocs/unreal/unreal.tsx b/static/app/gettingStartedDocs/unreal/unreal.tsx
index bb3570ca8db814..8c5de8938dfd80 100644
--- a/static/app/gettingStartedDocs/unreal/unreal.tsx
+++ b/static/app/gettingStartedDocs/unreal/unreal.tsx
@@ -4,6 +4,7 @@ import styled from '@emotion/styled';
import {Alert} from 'sentry/components/alert';
import ExternalLink from 'sentry/components/links/externalLink';
import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
+import {StoreCrashReportsConfig} from 'sentry/components/onboarding/gettingStartedDoc/storeCrashReportsConfig';
import type {
Docs,
DocsParams,
@@ -210,6 +211,15 @@ const onboarding: OnboardingConfig = {
),
},
+ {
+ title: t('Further Settings'),
+ description: (
+
+ ),
+ },
],
};