Skip to content
38 changes: 11 additions & 27 deletions static/app/views/settings/project/projectKeys/credentials/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import {Fragment, useMemo} from 'react';
import styled from '@emotion/styled';
import {parseAsBoolean, parseAsStringLiteral, useQueryState} from 'nuqs';

import {ExternalLink, Link} from 'sentry/components/core/link';
import {TabList, Tabs} from 'sentry/components/core/tabs';
import FieldGroup from 'sentry/components/forms/fieldGroup';
import TextCopyInput from 'sentry/components/textCopyInput';
import {t, tct} from 'sentry/locale';
import type {ProjectKey} from 'sentry/types/project';
import {decodeScalar} from 'sentry/utils/queryString';
import {useLocation} from 'sentry/utils/useLocation';
import {useNavigate} from 'sentry/utils/useNavigate';
import {OtlpTab} from 'sentry/views/settings/project/projectKeys/credentials/otlp';
import {VercelTab} from 'sentry/views/settings/project/projectKeys/credentials/vercel';

Expand Down Expand Up @@ -180,9 +179,7 @@ function ProjectKeyCredentials({
showUnreal = true,
}: Props) {
const location = useLocation();
const navigate = useNavigate();

// Calculate available tabs based on props
const availableTabs = useMemo<TabConfig[]>(() => {
const tabs: TabConfig[] = [
{
Expand Down Expand Up @@ -229,29 +226,17 @@ function ProjectKeyCredentials({
showProjectId,
]);

// Get showDeprecatedDsn from query params
const showDeprecatedDsn = decodeScalar(location?.query?.showDeprecated) === 'true';
const [showDeprecatedDsn] = useQueryState('showDeprecated', parseAsBoolean);

// Get current tab from query params, defaulting to first available
const getCurrentTab = (): TabValue => {
const queryTab = decodeScalar(location?.query?.tab);
const validTabs = availableTabs.map(tab => tab.key);
return validTabs.includes(queryTab as TabValue)
? (queryTab as TabValue)
: (availableTabs[0]?.key ?? 'otlp');
};

const activeTab = getCurrentTab();
const tabParser = useMemo(
() => ({
...parseAsStringLiteral(availableTabs.map(tab => tab.key)),
defaultValue: availableTabs[0]?.key ?? 'otlp',
}),
[availableTabs]
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Inconsistent Default Value Breaks Parser Behavior

The tabParser's defaultValue is 'otlp' when availableTabs is empty, but parseAsStringLiteral is configured with no valid keys. This inconsistency means the default value isn't accepted by the parser, potentially causing unexpected useQueryState behavior.

Fix in Cursor Fix in Web


const handleTabChange = (newTab: TabValue) => {
navigate({
pathname: location.pathname,
query: {
...location.query,
tab: newTab,
},
});
};
const [activeTab, setActiveTab] = useQueryState('tab', tabParser);

const renderTabContent = () => {
switch (activeTab) {
Expand Down Expand Up @@ -309,7 +294,6 @@ function ProjectKeyCredentials({
link: showDsn ? (
<Link
to={{
...location,
query: {
...location.query,
showDeprecated: showDeprecatedDsn ? undefined : 'true',
Expand Down Expand Up @@ -365,7 +349,7 @@ function ProjectKeyCredentials({

{availableTabs.length > 0 && (
<Fragment>
<Tabs value={activeTab} onChange={handleTabChange}>
<Tabs value={activeTab} onChange={setActiveTab}>
<TabList>
{availableTabs.map(tab => (
<TabList.Item key={tab.key}>{tab.label}</TabList.Item>
Expand Down
Loading