Skip to content

Commit 9970495

Browse files
fix(billing): Let backend handle dynamic validation (#103977)
Follow up to getsentry/getsentry#18924 Rather than duplicating validation for dynamically required fields on both the frontend and backend, we will only handle validation for fields required for all cases on both, and then handle validation for fields conditionally required on the backend (displaying those errors on the frontend on failed submission). <img width="795" height="746" alt="Screenshot 2025-11-25 at 12 06 13 PM" src="https://github.com/user-attachments/assets/bfac32fb-f78c-4b43-a0b4-7fb403c7734e" />
1 parent 742439f commit 9970495

File tree

2 files changed

+18
-21
lines changed

2 files changed

+18
-21
lines changed

static/gsApp/components/billingDetails/form.tsx

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -190,15 +190,10 @@ function BillingDetailsForm({
190190
form.setValue('countryCode', data.value.address.country);
191191
form.setValue('postalCode', data.value.address.postal_code);
192192
updateCountryCodeState(data.value.address.country ?? '');
193-
194-
// XXX(isabella): temp fix specific to UAE, remove this when we have a proper fix
195-
if (data.value.address.country === 'AE') {
196-
form.setValue('city', data.value.address.state);
197-
}
198193
};
199194

200195
useEffect(() => {
201-
const requiredFields = ['addressLine1', 'city', 'countryCode'];
196+
const requiredFields = ['addressLine1', 'countryCode'];
202197
requiredFields.forEach(field => {
203198
form.setFieldDescriptor(field, {
204199
required: true,
@@ -212,18 +207,6 @@ function BillingDetailsForm({
212207
};
213208
}, [form]);
214209

215-
useEffect(() => {
216-
if (countryHasRegionChoices(state.countryCode)) {
217-
form.setFieldDescriptor('region', {required: true});
218-
} else {
219-
form.setFieldDescriptor('region', {required: false});
220-
}
221-
222-
return () => {
223-
form.removeField('region');
224-
};
225-
}, [state.countryCode, form]);
226-
227210
if (!organization.access.includes('org:billing')) {
228211
return null;
229212
}
@@ -276,7 +259,7 @@ function BillingDetailsForm({
276259
submitLabel={submitLabel}
277260
onPreSubmit={onPreSubmit}
278261
onSubmitSuccess={handleSubmit}
279-
onSubmitError={onSubmitError}
262+
onSubmitError={err => onSubmitError?.(err)}
280263
initialData={transformedInitialData}
281264
footerStyle={footerStyle}
282265
extraButton={extraButton}

static/gsApp/components/billingDetails/panel.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {Fragment, useEffect, useState} from 'react';
22
import * as Sentry from '@sentry/react';
33

4+
import {Alert} from '@sentry/scraps/alert';
5+
46
import {Button} from 'sentry/components/core/button';
57
import {Flex} from 'sentry/components/core/layout';
68
import {Heading, Text} from 'sentry/components/core/text';
@@ -58,6 +60,7 @@ function BillingDetailsPanel({
5860
}) {
5961
const [isEditing, setIsEditing] = useState(false);
6062
const [expandInitially, setExpandInitially] = useState(shouldExpandInitially);
63+
const [formError, setFormError] = useState<string | null>(null);
6164
const {
6265
data: billingDetails,
6366
isLoading,
@@ -179,6 +182,7 @@ function BillingDetailsPanel({
179182
<Heading as="h2" size="lg">
180183
{t('Business address')}
181184
</Heading>
185+
{formError && <Alert type="error">{formError}</Alert>}
182186
{!isEditing && !!subscription.accountBalance && (
183187
<Text>
184188
{tct('Account balance: [balance]', {
@@ -193,12 +197,22 @@ function BillingDetailsPanel({
193197
onSubmitSuccess={() => {
194198
fetchBillingDetails();
195199
setIsEditing(false);
200+
setFormError(null);
201+
}}
202+
onSubmitError={error => {
203+
setFormError(
204+
Object.values(error.responseJSON || {}).join(' ') ||
205+
t('An unknown error occurred.')
206+
);
196207
}}
197208
extraButton={
198209
<Button
199210
priority="default"
200211
size="sm"
201-
onClick={() => setIsEditing(false)}
212+
onClick={() => {
213+
setIsEditing(false);
214+
setFormError(null);
215+
}}
202216
aria-label={t('Cancel editing business address')}
203217
>
204218
{t('Cancel')}
@@ -219,7 +233,7 @@ function BillingDetailsPanel({
219233
billingDetails.region ||
220234
billingDetails.postalCode) && (
221235
<Text>
222-
{`${billingDetails.city}${billingDetails.region ? `, ${billingDetails.region}` : ''}${billingDetails.postalCode ? ` ${billingDetails.postalCode}` : ''}`}
236+
{`${billingDetails.city || ''}${billingDetails.region ? `${billingDetails.city ? ', ' : ''}${billingDetails.region}` : ''}${billingDetails.postalCode ? ` ${billingDetails.postalCode}` : ''}`}
223237
</Text>
224238
)}
225239
{billingDetails.countryCode && (

0 commit comments

Comments
 (0)