Skip to content

Commit 8b1ffdf

Browse files
feat: Add generic modal for requesting access to private gaming repository
1 parent c6104ea commit 8b1ffdf

File tree

3 files changed

+208
-2
lines changed

3 files changed

+208
-2
lines changed

static/app/actionCreators/modal.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {SaveQueryModalProps} from 'sentry/components/modals/explore/saveQue
77
import type {ImportDashboardFromFileModalProps} from 'sentry/components/modals/importDashboardFromFileModal';
88
import type {InsightChartModalOptions} from 'sentry/components/modals/insightChartModal';
99
import type {InviteRow} from 'sentry/components/modals/inviteMembersModal/types';
10+
import type {PrivateGamingSdkAccessModalProps} from 'sentry/components/modals/privateGamingSdkAccessModal';
1011
import type {ReprocessEventModalOptions} from 'sentry/components/modals/reprocessEventModal';
1112
import type {AddToDashboardModalProps} from 'sentry/components/modals/widgetBuilder/addToDashboardModal';
1213
import type {OverwriteWidgetModalProps} from 'sentry/components/modals/widgetBuilder/overwriteWidgetModal';
@@ -447,3 +448,13 @@ export async function openTokenRegenerationConfirmationModal(options: ModalOptio
447448

448449
openModal(deps => <Modal {...deps} {...options} />);
449450
}
451+
452+
export async function openPrivateGamingSdkAccessModal(
453+
options: PrivateGamingSdkAccessModalProps
454+
) {
455+
const {PrivateGamingSdkAccessModal} = await import(
456+
'sentry/components/modals/privateGamingSdkAccessModal'
457+
);
458+
459+
openModal(deps => <PrivateGamingSdkAccessModal {...deps} {...options} />);
460+
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import {Fragment, useCallback, useState} from 'react';
2+
import {captureFeedback} from '@sentry/react';
3+
4+
import {addSuccessMessage} from 'sentry/actionCreators/indicator';
5+
import {type ModalRenderProps} from 'sentry/actionCreators/modal';
6+
import {Button} from 'sentry/components/core/button';
7+
import {ButtonBar} from 'sentry/components/core/button/buttonBar';
8+
import SelectField from 'sentry/components/forms/fields/selectField';
9+
import TextField from 'sentry/components/forms/fields/textField';
10+
import {t, tct} from 'sentry/locale';
11+
import type {Organization} from 'sentry/types/organization';
12+
import {useUser} from 'sentry/utils/useUser';
13+
14+
const PRIVATE_GAMING_SDK_OPTIONS = [
15+
{value: 'playstation', label: 'PlayStation'},
16+
{value: 'xbox', label: 'Xbox'},
17+
{value: 'nintendo-switch', label: 'Nintendo Switch'},
18+
] as const;
19+
20+
type GamingPlatform = (typeof PRIVATE_GAMING_SDK_OPTIONS)[number]['value'];
21+
22+
export interface PrivateGamingSdkAccessModalProps {
23+
organization: Organization;
24+
projectSlug: string;
25+
sdkName: string;
26+
gamingPlatform?: GamingPlatform;
27+
onSubmit?: () => void;
28+
}
29+
30+
export function PrivateGamingSdkAccessModal({
31+
Header,
32+
Body,
33+
Footer,
34+
closeModal,
35+
organization,
36+
projectSlug,
37+
sdkName,
38+
gamingPlatform,
39+
onSubmit,
40+
}: PrivateGamingSdkAccessModalProps & ModalRenderProps) {
41+
const user = useUser();
42+
const [isSubmitting, setIsSubmitting] = useState(false);
43+
const [githubProfile, setGithubProfile] = useState('');
44+
const [gamingPlatforms, setGamingPlatforms] = useState<string[]>(
45+
gamingPlatform ? [gamingPlatform] : []
46+
);
47+
48+
const isFormValid = !!githubProfile.trim() && gamingPlatforms.length > 0;
49+
50+
const handleSubmit = useCallback(() => {
51+
if (!isFormValid) {
52+
return;
53+
}
54+
55+
setIsSubmitting(true);
56+
57+
onSubmit?.();
58+
59+
const messageBody = [
60+
`User: ${user.name}`,
61+
`Email: ${user.email}`,
62+
gamingPlatforms.length === 1
63+
? `Platform: ${gamingPlatforms[0]}`
64+
: `Platforms: ${gamingPlatforms
65+
.map(
66+
(platform: string) =>
67+
PRIVATE_GAMING_SDK_OPTIONS.find(option => option.value === platform)
68+
?.label || platform
69+
)
70+
.join(', ')}`,
71+
`Org Slug: ${organization.slug}`,
72+
`Project: ${projectSlug}`,
73+
`GitHub Profile: ${githubProfile}`,
74+
].join('\n');
75+
76+
// Use captureFeedback with proper user context instead of tags
77+
captureFeedback(
78+
{
79+
message: messageBody,
80+
name: user.name,
81+
email: user.email,
82+
source: `${gamingPlatform}-sdk-access`,
83+
tags: {
84+
feature: `${gamingPlatform}-sdk-access`,
85+
},
86+
},
87+
{
88+
captureContext: {
89+
user: {
90+
id: user.id,
91+
email: user.email,
92+
username: user.username,
93+
name: user.name,
94+
},
95+
},
96+
}
97+
);
98+
99+
addSuccessMessage(
100+
tct('Your [sdkName] SDK access request has been submitted.', {
101+
sdkName,
102+
})
103+
);
104+
setIsSubmitting(false);
105+
closeModal();
106+
}, [
107+
isFormValid,
108+
gamingPlatform,
109+
githubProfile,
110+
organization,
111+
sdkName,
112+
user,
113+
gamingPlatforms,
114+
closeModal,
115+
onSubmit,
116+
projectSlug,
117+
]);
118+
119+
return (
120+
<Fragment>
121+
<Header closeButton>
122+
<h3>
123+
{tct('Request [sdkName] SDK Access', {
124+
sdkName,
125+
})}
126+
</h3>
127+
</Header>
128+
<Body>
129+
<p>
130+
{gamingPlatform
131+
? tct(
132+
'Request access to our [sdkName] SDK. Please provide your GitHub profile.',
133+
{
134+
sdkName,
135+
}
136+
)
137+
: tct(
138+
'Request access to our [sdkName] SDK. Please provide your GitHub profile and the gaming platforms you work with.',
139+
{
140+
sdkName,
141+
}
142+
)}
143+
</p>
144+
<TextField
145+
name="githubProfile"
146+
label={t('Link to your GitHub profile')}
147+
placeholder="https://github.com/username"
148+
value={githubProfile}
149+
onChange={setGithubProfile}
150+
required
151+
stacked
152+
inline={false}
153+
/>
154+
{!gamingPlatform && (
155+
<SelectField
156+
name="gamingPlatforms"
157+
label={t('Select Gaming Platform')}
158+
placeholder={t('Select one or more gaming platforms')}
159+
options={PRIVATE_GAMING_SDK_OPTIONS}
160+
value={gamingPlatforms}
161+
onChange={setGamingPlatforms}
162+
multiple
163+
required
164+
stacked
165+
inline={false}
166+
/>
167+
)}
168+
</Body>
169+
<Footer>
170+
<ButtonBar>
171+
<Button onClick={closeModal}>{t('Cancel')}</Button>
172+
<Button
173+
priority="primary"
174+
onClick={handleSubmit}
175+
disabled={!isFormValid || isSubmitting}
176+
>
177+
{isSubmitting ? t('Submitting\u2026') : t('Submit Request')}
178+
</Button>
179+
</ButtonBar>
180+
</Footer>
181+
</Fragment>
182+
);
183+
}

static/app/gettingStartedDocs/console/playstation.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {openPrivateGamingSdkAccessModal} from 'sentry/actionCreators/modal';
12
import {Button} from 'sentry/components/core/button';
23
import {ExternalLink} from 'sentry/components/core/link';
34
import List from 'sentry/components/list';
@@ -11,7 +12,7 @@ import {IconLock} from 'sentry/icons/iconLock';
1112
import {t, tct} from 'sentry/locale';
1213

1314
const onboarding: OnboardingConfig = {
14-
install: () => [
15+
install: params => [
1516
{
1617
type: StepType.INSTALL,
1718
content: [
@@ -42,7 +43,18 @@ const onboarding: OnboardingConfig = {
4243
),
4344
showIcon: true,
4445
trailingItems: (
45-
<Button size="sm" priority="primary">
46+
<Button
47+
size="sm"
48+
priority="primary"
49+
onClick={() => {
50+
openPrivateGamingSdkAccessModal({
51+
organization: params.organization,
52+
projectSlug: params.projectSlug,
53+
sdkName: 'PlayStation',
54+
gamingPlatform: 'playstation',
55+
});
56+
}}
57+
>
4658
{t('Request Access')}
4759
</Button>
4860
),

0 commit comments

Comments
 (0)