loaderScript.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import {Fragment, useCallback, useState} from 'react';
  2. import {LinkButton} from 'sentry/components/button';
  3. import EmptyMessage from 'sentry/components/emptyMessage';
  4. import ExternalLink from 'sentry/components/links/externalLink';
  5. import Link from 'sentry/components/links/link';
  6. import LoadingError from 'sentry/components/loadingError';
  7. import LoadingIndicator from 'sentry/components/loadingIndicator';
  8. import Panel from 'sentry/components/panels/panel';
  9. import PanelAlert from 'sentry/components/panels/panelAlert';
  10. import PanelBody from 'sentry/components/panels/panelBody';
  11. import PanelHeader from 'sentry/components/panels/panelHeader';
  12. import {t, tct} from 'sentry/locale';
  13. import {Organization, Project} from 'sentry/types';
  14. import {useApiQuery} from 'sentry/utils/queryClient';
  15. import useOrganization from 'sentry/utils/useOrganization';
  16. import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
  17. import TextBlock from 'sentry/views/settings/components/text/textBlock';
  18. import {LoaderSettings} from 'sentry/views/settings/project/projectKeys/details/loaderSettings';
  19. import {ProjectKey} from 'sentry/views/settings/project/projectKeys/types';
  20. export function ProjectLoaderScript({project}: {project: Project}) {
  21. const organization = useOrganization();
  22. const apiEndpoint = `/projects/${organization.slug}/${project.slug}/keys/`;
  23. const [updatedProjectKeys, setUpdatedProjectKeys] = useState<ProjectKey[]>([]);
  24. const {
  25. data: projectKeys,
  26. isLoading,
  27. error,
  28. refetch: refetchProjectKeys,
  29. } = useApiQuery<ProjectKey[]>([apiEndpoint], {
  30. staleTime: 0,
  31. });
  32. const handleUpdateProjectKey = useCallback(
  33. (projectKey: ProjectKey) => {
  34. const existingProjectIndex = updatedProjectKeys.findIndex(
  35. key => key.id === projectKey.id
  36. );
  37. const newUpdatedProjectKeys =
  38. existingProjectIndex > -1
  39. ? [...updatedProjectKeys].map((updatedProjectKey, index) => {
  40. return index === existingProjectIndex ? projectKey : updatedProjectKey;
  41. })
  42. : [...updatedProjectKeys, projectKey];
  43. setUpdatedProjectKeys(newUpdatedProjectKeys);
  44. },
  45. [updatedProjectKeys]
  46. );
  47. return (
  48. <Fragment>
  49. <SettingsPageHeader title={t('Loader Script')} />
  50. <TextBlock>
  51. {tct(
  52. 'The Loader Script is the easiest way to initialize the Sentry SDK. The Loader Script automatically keeps your Sentry SDK up to date and offers configuration for different Sentry features. [docsLink:Learn more about the Loader Script]. Note: The Loader Script is bound to a Client Key (DSN), to create a new Script, go to the [clientKeysLink:Client Keys page].',
  53. {
  54. docsLink: (
  55. <ExternalLink href="https://docs.sentry.io/platforms/javascript/install/loader/" />
  56. ),
  57. clientKeysLink: (
  58. <Link
  59. to={`/settings/${organization.slug}/projects/${project.slug}/keys/`}
  60. />
  61. ),
  62. }
  63. )}
  64. </TextBlock>
  65. {isLoading && <LoadingIndicator />}
  66. {!!error && (
  67. <LoadingError
  68. message={t('Failed to load project keys.')}
  69. onRetry={refetchProjectKeys}
  70. />
  71. )}
  72. {!isLoading && !error && !projectKeys?.length && (
  73. <EmptyMessage title={t('There are no keys active for this project.')} />
  74. )}
  75. {projectKeys?.map(key => {
  76. const actualKey =
  77. updatedProjectKeys.find(updatedKey => updatedKey.id === key.id) ?? key;
  78. return (
  79. <LoaderItem
  80. key={actualKey.id}
  81. organization={organization}
  82. project={project}
  83. projectKey={actualKey}
  84. onUpdateProjectKey={handleUpdateProjectKey}
  85. />
  86. );
  87. })}
  88. </Fragment>
  89. );
  90. }
  91. function LoaderItem({
  92. organization,
  93. project,
  94. projectKey,
  95. onUpdateProjectKey,
  96. }: {
  97. onUpdateProjectKey: (projectKey: ProjectKey) => void;
  98. organization: Organization;
  99. project: Project;
  100. projectKey: ProjectKey;
  101. }) {
  102. return (
  103. <Panel>
  104. <PanelHeader hasButtons>
  105. {tct('Client Key: [name]', {name: projectKey.name})}
  106. <LinkButton
  107. size="xs"
  108. to={`/settings/${organization.slug}/projects/${project.slug}/keys/${projectKey.id}/`}
  109. >
  110. {t('View Key Details')}
  111. </LinkButton>
  112. </PanelHeader>
  113. <PanelBody>
  114. <PanelAlert type="info" showIcon>
  115. {t('Note that it can take a few minutes until changed options are live.')}
  116. </PanelAlert>
  117. <LoaderSettings
  118. orgSlug={organization.slug}
  119. keyId={projectKey.id}
  120. project={project}
  121. data={projectKey}
  122. updateData={onUpdateProjectKey}
  123. />
  124. </PanelBody>
  125. </Panel>
  126. );
  127. }
  128. export default ProjectLoaderScript;