addIntegrationRow.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import {useCallback, useEffect, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import {Button} from 'sentry/components/button';
  4. import PluginIcon from 'sentry/plugins/components/pluginIcon';
  5. import {space} from 'sentry/styles/space';
  6. import type {IntegrationProvider} from 'sentry/types/integrations';
  7. import type {Organization} from 'sentry/types/organization';
  8. import type {Project} from 'sentry/types/project';
  9. import useApi from 'sentry/utils/useApi';
  10. import {AddIntegrationButton} from 'sentry/views/settings/organizationIntegrations/addIntegrationButton';
  11. type Props = {
  12. onClickHandler: () => void;
  13. organization: Organization;
  14. project: Project;
  15. providerKey: string;
  16. setHasError: (boolean) => void;
  17. };
  18. function AddIntegrationRow({
  19. providerKey,
  20. organization,
  21. project,
  22. onClickHandler,
  23. setHasError,
  24. }: Props) {
  25. const [provider, setProvider] = useState<IntegrationProvider | null>(null);
  26. const api = useApi();
  27. const fetchData = useCallback(() => {
  28. if (!providerKey) {
  29. return Promise.resolve();
  30. }
  31. const endpoint = `/organizations/${organization.slug}/config/integrations/?provider_key=${providerKey}`;
  32. return api
  33. .requestPromise(endpoint)
  34. .then(integrations => {
  35. setProvider(integrations.providers[0]);
  36. })
  37. .catch(() => {
  38. setHasError(true);
  39. });
  40. }, [providerKey, api, organization.slug, setHasError]);
  41. useEffect(() => {
  42. fetchData();
  43. }, [fetchData]);
  44. if (!provider) {
  45. return null;
  46. }
  47. const {metadata} = provider;
  48. const buttonProps = {
  49. size: 'sm' as const,
  50. priority: 'primary' as const,
  51. 'data-test-id': 'install-button',
  52. organization,
  53. };
  54. const close = () => onClickHandler;
  55. // TODO(Mia): show request installation button if user does not have necessary permissions
  56. const integrationButton = metadata.aspects.externalInstall ? (
  57. <ExternalButton
  58. href={metadata.aspects.externalInstall.url}
  59. onClick={() => close}
  60. external
  61. {...buttonProps}
  62. >
  63. Add Installation
  64. </ExternalButton>
  65. ) : (
  66. <InternalButton
  67. provider={provider}
  68. onAddIntegration={close}
  69. analyticsParams={{view: 'onboarding', already_installed: false}}
  70. modalParams={{projectId: project.id}}
  71. {...buttonProps}
  72. />
  73. );
  74. return (
  75. <RowWrapper>
  76. <IconTextWrapper>
  77. <PluginIcon pluginId={providerKey} size={40} />
  78. <NameHeader>Connect {provider.name}</NameHeader>
  79. </IconTextWrapper>
  80. {integrationButton}
  81. </RowWrapper>
  82. );
  83. }
  84. const RowWrapper = styled('div')`
  85. display: flex;
  86. border-radius: 4px;
  87. border: 1px solid ${p => p.theme.gray200};
  88. justify-content: space-between;
  89. align-items: center;
  90. padding: ${space(3)} ${space(4)};
  91. `;
  92. const IconTextWrapper = styled('div')`
  93. display: flex;
  94. align-items: center;
  95. gap: ${space(3)};
  96. `;
  97. const NameHeader = styled('h6')`
  98. margin: 0;
  99. `;
  100. const ExternalButton = styled(Button)`
  101. margin: 0;
  102. `;
  103. const InternalButton = styled(AddIntegrationButton)`
  104. margin: 0;
  105. `;
  106. export default AddIntegrationRow;