userMisery.tsx 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. import ScoreBar from 'sentry/components/scoreBar';
  2. import Tooltip from 'sentry/components/tooltip';
  3. import CHART_PALETTE from 'sentry/constants/chartPalette';
  4. import {tct} from 'sentry/locale';
  5. import {defined} from 'sentry/utils';
  6. type Props = {
  7. barHeight: number;
  8. bars: number;
  9. miserableUsers: number | undefined;
  10. miseryLimit: number | undefined;
  11. totalUsers: number | undefined;
  12. userMisery: number;
  13. };
  14. function UserMisery(props: Props) {
  15. const {bars, barHeight, userMisery, miseryLimit, totalUsers, miserableUsers} = props;
  16. // User Misery will always be > 0 because of the maximum a posteriori estimate
  17. // and below 5% will always be an overestimation of the actual proportion
  18. // of miserable to total unique users. We are going to visualize it as
  19. // 0 User Misery while still preserving the actual value for sorting purposes.
  20. const adjustedMisery = userMisery > 0.05 ? userMisery : 0;
  21. const palette = new Array(bars).fill([CHART_PALETTE[0][0]]);
  22. const score = Math.round(adjustedMisery * palette.length);
  23. let title: React.ReactNode;
  24. if (defined(miserableUsers) && defined(totalUsers) && defined(miseryLimit)) {
  25. title = tct(
  26. '[miserableUsers] out of [totalUsers] unique users waited more than [duration]ms (4x the response time threshold)',
  27. {
  28. miserableUsers,
  29. totalUsers,
  30. duration: 4 * miseryLimit,
  31. }
  32. );
  33. } else if (defined(miseryLimit)) {
  34. title = tct(
  35. 'User Misery score is [userMisery], representing users who waited more than [duration]ms (4x the response time threshold)',
  36. {
  37. duration: 4 * miseryLimit,
  38. userMisery: userMisery.toFixed(3),
  39. }
  40. );
  41. } else if (defined(miserableUsers) && defined(totalUsers)) {
  42. title = tct(
  43. 'User Misery score is [userMisery], because [miserableUsers] out of [totalUsers] unique users had a miserable experience.',
  44. {
  45. miserableUsers,
  46. totalUsers,
  47. userMisery: userMisery.toFixed(3),
  48. }
  49. );
  50. } else {
  51. title = tct('User Misery score is [userMisery].', {
  52. userMisery: userMisery.toFixed(3),
  53. });
  54. }
  55. return (
  56. <Tooltip title={title} containerDisplayMode="block">
  57. <ScoreBar
  58. size={barHeight}
  59. score={score}
  60. palette={palette}
  61. radius={0}
  62. data-test-id={`score-bar-${score}`}
  63. />
  64. </Tooltip>
  65. );
  66. }
  67. export default UserMisery;