loaderScript.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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 type {Organization, Project, ProjectKey} 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. export function ProjectLoaderScript({project}: {project: Project}) {
  20. const organization = useOrganization();
  21. const apiEndpoint = `/projects/${organization.slug}/${project.slug}/keys/`;
  22. const [updatedProjectKeys, setUpdatedProjectKeys] = useState<ProjectKey[]>([]);
  23. const {
  24. data: projectKeys,
  25. isLoading,
  26. error,
  27. refetch: refetchProjectKeys,
  28. } = useApiQuery<ProjectKey[]>([apiEndpoint], {
  29. staleTime: 0,
  30. });
  31. const handleUpdateProjectKey = useCallback(
  32. (projectKey: ProjectKey) => {
  33. const existingProjectIndex = updatedProjectKeys.findIndex(
  34. key => key.id === projectKey.id
  35. );
  36. const newUpdatedProjectKeys =
  37. existingProjectIndex > -1
  38. ? [...updatedProjectKeys].map((updatedProjectKey, index) => {
  39. return index === existingProjectIndex ? projectKey : updatedProjectKey;
  40. })
  41. : [...updatedProjectKeys, projectKey];
  42. setUpdatedProjectKeys(newUpdatedProjectKeys);
  43. },
  44. [updatedProjectKeys]
  45. );
  46. return (
  47. <Fragment>
  48. <SettingsPageHeader title={t('Loader Script')} />
  49. <TextBlock>
  50. {tct(
  51. '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].',
  52. {
  53. docsLink: (
  54. <ExternalLink href="https://docs.sentry.io/platforms/javascript/install/loader/" />
  55. ),
  56. clientKeysLink: (
  57. <Link
  58. to={`/settings/${organization.slug}/projects/${project.slug}/keys/`}
  59. />
  60. ),
  61. }
  62. )}
  63. </TextBlock>
  64. {isLoading && <LoadingIndicator />}
  65. {!!error && (
  66. <LoadingError
  67. message={t('Failed to load project keys.')}
  68. onRetry={refetchProjectKeys}
  69. />
  70. )}
  71. {!isLoading && !error && !projectKeys?.length && (
  72. <EmptyMessage title={t('There are no keys active for this project.')} />
  73. )}
  74. {projectKeys?.map(key => {
  75. const actualKey =
  76. updatedProjectKeys.find(updatedKey => updatedKey.id === key.id) ?? key;
  77. return (
  78. <LoaderItem
  79. key={actualKey.id}
  80. organization={organization}
  81. project={project}
  82. projectKey={actualKey}
  83. onUpdateProjectKey={handleUpdateProjectKey}
  84. />
  85. );
  86. })}
  87. </Fragment>
  88. );
  89. }
  90. function LoaderItem({
  91. organization,
  92. project,
  93. projectKey,
  94. onUpdateProjectKey,
  95. }: {
  96. onUpdateProjectKey: (projectKey: ProjectKey) => void;
  97. organization: Organization;
  98. project: Project;
  99. projectKey: ProjectKey;
  100. }) {
  101. return (
  102. <Panel>
  103. <PanelHeader hasButtons>
  104. {tct('Client Key: [name]', {name: projectKey.name})}
  105. <LinkButton
  106. size="xs"
  107. to={`/settings/${organization.slug}/projects/${project.slug}/keys/${projectKey.id}/`}
  108. >
  109. {t('View Key Details')}
  110. </LinkButton>
  111. </PanelHeader>
  112. <PanelBody>
  113. <PanelAlert type="info" showIcon>
  114. {t('Note that it can take a few minutes until changed options are live.')}
  115. </PanelAlert>
  116. <LoaderSettings
  117. orgSlug={organization.slug}
  118. keyId={projectKey.id}
  119. project={project}
  120. data={projectKey}
  121. updateData={onUpdateProjectKey}
  122. />
  123. </PanelBody>
  124. </Panel>
  125. );
  126. }
  127. export default ProjectLoaderScript;