You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ProjectActivityAnalysis.tsx 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import * as React from 'react';
  21. import * as classNames from 'classnames';
  22. import Events from './Events';
  23. import AddEventForm from './forms/AddEventForm';
  24. import RemoveAnalysisForm from './forms/RemoveAnalysisForm';
  25. import TimeFormatter from '../../../components/intl/TimeFormatter';
  26. import Tooltip from '../../../components/controls/Tooltip';
  27. import ActionsDropdown, {
  28. ActionsDropdownDivider,
  29. ActionsDropdownItem
  30. } from '../../../components/controls/ActionsDropdown';
  31. import { translate, translateWithParameters } from '../../../helpers/l10n';
  32. import { parseDate } from '../../../helpers/dates';
  33. import { ParsedAnalysis } from '../utils';
  34. interface Props {
  35. addCustomEvent: (analysis: string, name: string, category?: string) => Promise<void>;
  36. addVersion: (analysis: string, version: string) => Promise<void>;
  37. analysis: ParsedAnalysis;
  38. canAdmin?: boolean;
  39. canDeleteAnalyses?: boolean;
  40. canCreateVersion: boolean;
  41. changeEvent: (event: string, name: string) => Promise<void>;
  42. deleteAnalysis: (analysis: string) => Promise<void>;
  43. deleteEvent: (analysis: string, event: string) => Promise<void>;
  44. isFirst: boolean;
  45. selected: boolean;
  46. updateSelectedDate: (date: Date) => void;
  47. }
  48. interface State {
  49. addEventForm: boolean;
  50. addVersionForm: boolean;
  51. removeAnalysisForm: boolean;
  52. suppressVersionTooltip?: boolean;
  53. }
  54. export default class ProjectActivityAnalysis extends React.PureComponent<Props, State> {
  55. mounted = false;
  56. state: State = {
  57. addEventForm: false,
  58. addVersionForm: false,
  59. removeAnalysisForm: false
  60. };
  61. componentDidMount() {
  62. this.mounted = true;
  63. }
  64. componentWillUnmount() {
  65. this.mounted = false;
  66. }
  67. handleClick = () => {
  68. this.props.updateSelectedDate(this.props.analysis.date);
  69. };
  70. stopPropagation = (event: React.SyntheticEvent) => {
  71. event.stopPropagation();
  72. };
  73. handleRemoveAnalysisClick = () => {
  74. this.setState({ removeAnalysisForm: true });
  75. };
  76. closeRemoveAnalysisForm = () => {
  77. if (this.mounted) {
  78. this.setState({ removeAnalysisForm: false });
  79. }
  80. };
  81. handleAddEventClick = () => {
  82. this.setState({ addEventForm: true });
  83. };
  84. closeAddEventForm = () => {
  85. if (this.mounted) {
  86. this.setState({ addEventForm: false });
  87. }
  88. };
  89. handleAddVersionClick = () => {
  90. this.setState({ addVersionForm: true });
  91. };
  92. handleTimeTooltipHide = () => {
  93. this.setState({ suppressVersionTooltip: false });
  94. };
  95. handleTimeTooltipShow = () => {
  96. this.setState({ suppressVersionTooltip: true });
  97. };
  98. closeAddVersionForm = () => {
  99. if (this.mounted) {
  100. this.setState({ addVersionForm: false });
  101. }
  102. };
  103. render() {
  104. const { analysis, isFirst, canAdmin, canCreateVersion } = this.props;
  105. const { date, events } = analysis;
  106. const parsedDate = parseDate(date);
  107. const hasVersion = events.find(event => event.category === 'VERSION') != null;
  108. const canAddVersion = canAdmin && !hasVersion && canCreateVersion;
  109. const canAddEvent = canAdmin;
  110. const canDeleteAnalyses =
  111. this.props.canDeleteAnalyses && !isFirst && !analysis.manualNewCodePeriodBaseline;
  112. let tooltipContent = <TimeFormatter date={parsedDate} long={true} />;
  113. if (analysis.buildString) {
  114. tooltipContent = (
  115. <>
  116. {tooltipContent}
  117. <br />
  118. {translateWithParameters(
  119. 'project_activity.analysis_build_string_X',
  120. analysis.buildString
  121. )}
  122. </>
  123. );
  124. }
  125. return (
  126. <Tooltip mouseEnterDelay={0.5} overlay={tooltipContent} placement="left">
  127. <li
  128. className={classNames('project-activity-analysis', { selected: this.props.selected })}
  129. data-date={parsedDate.valueOf()}
  130. onClick={this.handleClick}
  131. tabIndex={0}>
  132. <div className="project-activity-time spacer-right">
  133. <TimeFormatter date={parsedDate} long={false}>
  134. {formattedTime => (
  135. <time className="text-middle" dateTime={parsedDate.toISOString()}>
  136. {formattedTime}
  137. </time>
  138. )}
  139. </TimeFormatter>
  140. </div>
  141. {(canAddVersion || canAddEvent || canDeleteAnalyses) && (
  142. <div className="project-activity-analysis-actions big-spacer-right">
  143. <ActionsDropdown small={true} toggleClassName="js-analysis-actions">
  144. {canAddVersion && (
  145. <ActionsDropdownItem
  146. className="js-add-event"
  147. onClick={this.handleAddVersionClick}>
  148. {translate('project_activity.add_version')}
  149. </ActionsDropdownItem>
  150. )}
  151. {canAddEvent && (
  152. <ActionsDropdownItem className="js-add-event" onClick={this.handleAddEventClick}>
  153. {translate('project_activity.add_custom_event')}
  154. </ActionsDropdownItem>
  155. )}
  156. {(canAddVersion || canAddEvent) && canDeleteAnalyses && <ActionsDropdownDivider />}
  157. {canDeleteAnalyses && (
  158. <ActionsDropdownItem
  159. className="js-delete-analysis"
  160. destructive={true}
  161. onClick={this.handleRemoveAnalysisClick}>
  162. {translate('project_activity.delete_analysis')}
  163. </ActionsDropdownItem>
  164. )}
  165. </ActionsDropdown>
  166. {this.state.addVersionForm && (
  167. <AddEventForm
  168. addEvent={this.props.addVersion}
  169. addEventButtonText="project_activity.add_version"
  170. analysis={analysis}
  171. onClose={this.closeAddVersionForm}
  172. />
  173. )}
  174. {this.state.addEventForm && (
  175. <AddEventForm
  176. addEvent={this.props.addCustomEvent}
  177. addEventButtonText="project_activity.add_custom_event"
  178. analysis={analysis}
  179. onClose={this.closeAddEventForm}
  180. />
  181. )}
  182. {this.state.removeAnalysisForm && (
  183. <RemoveAnalysisForm
  184. analysis={analysis}
  185. deleteAnalysis={this.props.deleteAnalysis}
  186. onClose={this.closeRemoveAnalysisForm}
  187. />
  188. )}
  189. </div>
  190. )}
  191. {events.length > 0 && (
  192. <Events
  193. analysis={analysis.key}
  194. canAdmin={canAdmin}
  195. changeEvent={this.props.changeEvent}
  196. deleteEvent={this.props.deleteEvent}
  197. events={events}
  198. isFirst={this.props.isFirst}
  199. />
  200. )}
  201. </li>
  202. </Tooltip>
  203. );
  204. }
  205. }