pluginDetailedView.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import styled from '@emotion/styled';
  2. import * as modal from 'app/actionCreators/modal';
  3. import Button from 'app/components/button';
  4. import ContextPickerModal from 'app/components/contextPickerModal';
  5. import {t} from 'app/locale';
  6. import space from 'app/styles/space';
  7. import {PluginProjectItem, PluginWithProjectList} from 'app/types';
  8. import withOrganization from 'app/utils/withOrganization';
  9. import AbstractIntegrationDetailedView from './abstractIntegrationDetailedView';
  10. import InstalledPlugin from './installedPlugin';
  11. type State = {
  12. plugins: PluginWithProjectList[];
  13. };
  14. type Tab = AbstractIntegrationDetailedView['state']['tab'];
  15. class PluginDetailedView extends AbstractIntegrationDetailedView<
  16. AbstractIntegrationDetailedView['props'],
  17. State & AbstractIntegrationDetailedView['state']
  18. > {
  19. getEndpoints(): ([string, string, any] | [string, string])[] {
  20. const {orgId, integrationSlug} = this.props.params;
  21. return [
  22. ['plugins', `/organizations/${orgId}/plugins/configs/?plugins=${integrationSlug}`],
  23. ];
  24. }
  25. get integrationType() {
  26. return 'plugin' as const;
  27. }
  28. get plugin() {
  29. return this.state.plugins[0];
  30. }
  31. get description() {
  32. return this.plugin.description || '';
  33. }
  34. get author() {
  35. return this.plugin.author?.name;
  36. }
  37. get resourceLinks() {
  38. return this.plugin.resourceLinks || [];
  39. }
  40. get installationStatus() {
  41. return this.plugin.projectList.length > 0 ? 'Installed' : 'Not Installed';
  42. }
  43. get integrationName() {
  44. return `${this.plugin.name}${this.plugin.isHidden ? ' (Legacy)' : ''}`;
  45. }
  46. get featureData() {
  47. return this.plugin.featureDescriptions;
  48. }
  49. handleResetConfiguration = (projectId: string) => {
  50. // make a copy of our project list
  51. const projectList = this.plugin.projectList.slice();
  52. // find the index of the project
  53. const index = projectList.findIndex(item => item.projectId === projectId);
  54. // should match but quit if it doesn't
  55. if (index < 0) {
  56. return;
  57. }
  58. // remove from array
  59. projectList.splice(index, 1);
  60. // update state
  61. this.setState({
  62. plugins: [{...this.state.plugins[0], projectList}],
  63. });
  64. };
  65. handlePluginEnableStatus = (projectId: string, enable: boolean = true) => {
  66. // make a copy of our project list
  67. const projectList = this.plugin.projectList.slice();
  68. // find the index of the project
  69. const index = projectList.findIndex(item => item.projectId === projectId);
  70. // should match but quit if it doesn't
  71. if (index < 0) {
  72. return;
  73. }
  74. // update item in array
  75. projectList[index] = {
  76. ...projectList[index],
  77. enabled: enable,
  78. };
  79. // update state
  80. this.setState({
  81. plugins: [{...this.state.plugins[0], projectList}],
  82. });
  83. };
  84. handleAddToProject = () => {
  85. const plugin = this.plugin;
  86. const {organization, router} = this.props;
  87. this.trackIntegrationEvent('integrations.plugin_add_to_project_clicked');
  88. modal.openModal(
  89. modalProps => (
  90. <ContextPickerModal
  91. {...modalProps}
  92. nextPath={`/settings/${organization.slug}/projects/:projectId/plugins/${plugin.id}/`}
  93. needProject
  94. needOrg={false}
  95. onFinish={path => {
  96. modalProps.closeModal();
  97. router.push(path);
  98. }}
  99. />
  100. ),
  101. {}
  102. );
  103. };
  104. getTabDisplay(tab: Tab) {
  105. // we want to show project configurations to make it more clear
  106. if (tab === 'configurations') {
  107. return 'project configurations';
  108. }
  109. return 'overview';
  110. }
  111. renderTopButton(disabledFromFeatures: boolean, userHasAccess: boolean) {
  112. if (userHasAccess) {
  113. return (
  114. <AddButton
  115. data-test-id="install-button"
  116. disabled={disabledFromFeatures}
  117. onClick={this.handleAddToProject}
  118. size="small"
  119. priority="primary"
  120. >
  121. {t('Add to Project')}
  122. </AddButton>
  123. );
  124. }
  125. return this.renderRequestIntegrationButton();
  126. }
  127. renderConfigurations() {
  128. const plugin = this.plugin;
  129. const {organization} = this.props;
  130. if (plugin.projectList.length) {
  131. return (
  132. <div>
  133. {plugin.projectList.map((projectItem: PluginProjectItem) => (
  134. <InstalledPlugin
  135. key={projectItem.projectId}
  136. organization={organization}
  137. plugin={plugin}
  138. projectItem={projectItem}
  139. onResetConfiguration={this.handleResetConfiguration}
  140. onPluginEnableStatusChange={this.handlePluginEnableStatus}
  141. trackIntegrationEvent={this.trackIntegrationEvent}
  142. />
  143. ))}
  144. </div>
  145. );
  146. }
  147. return this.renderEmptyConfigurations();
  148. }
  149. }
  150. const AddButton = styled(Button)`
  151. margin-bottom: ${space(1)};
  152. `;
  153. export default withOrganization(PluginDetailedView);