dashboardCard.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import styled from '@emotion/styled';
  2. import ActivityAvatar from 'sentry/components/activity/item/avatar';
  3. import Card from 'sentry/components/card';
  4. import Link, {LinkProps} from 'sentry/components/links/link';
  5. import {t} from 'sentry/locale';
  6. import space from 'sentry/styles/space';
  7. import {User} from 'sentry/types';
  8. interface Props {
  9. detail: React.ReactNode;
  10. renderWidgets: () => React.ReactNode;
  11. title: string;
  12. to: LinkProps['to'];
  13. createdBy?: User;
  14. dateStatus?: React.ReactNode;
  15. onEventClick?: () => void;
  16. renderContextMenu?: () => void;
  17. }
  18. function DashboardCard({
  19. title,
  20. detail,
  21. createdBy,
  22. renderWidgets,
  23. dateStatus,
  24. to,
  25. onEventClick,
  26. renderContextMenu,
  27. }: Props) {
  28. function onClick() {
  29. onEventClick?.();
  30. }
  31. return (
  32. <Link data-test-id={`card-${title}`} onClick={onClick} to={to}>
  33. <StyledDashboardCard interactive>
  34. <CardHeader>
  35. <CardContent>
  36. <Title>{title}</Title>
  37. <Detail>{detail}</Detail>
  38. </CardContent>
  39. <AvatarWrapper>
  40. {createdBy ? (
  41. <ActivityAvatar type="user" user={createdBy} size={34} />
  42. ) : (
  43. <ActivityAvatar type="system" size={34} />
  44. )}
  45. </AvatarWrapper>
  46. </CardHeader>
  47. <CardBody>{renderWidgets()}</CardBody>
  48. <CardFooter>
  49. <DateSelected>
  50. {dateStatus ? (
  51. <DateStatus>
  52. {t('Created')} {dateStatus}
  53. </DateStatus>
  54. ) : (
  55. <DateStatus />
  56. )}
  57. </DateSelected>
  58. {renderContextMenu && renderContextMenu()}
  59. </CardFooter>
  60. </StyledDashboardCard>
  61. </Link>
  62. );
  63. }
  64. const AvatarWrapper = styled('span')`
  65. border: 3px solid ${p => p.theme.border};
  66. border-radius: 50%;
  67. height: min-content;
  68. `;
  69. const CardContent = styled('div')`
  70. flex-grow: 1;
  71. overflow: hidden;
  72. margin-right: ${space(1)};
  73. `;
  74. const StyledDashboardCard = styled(Card)`
  75. justify-content: space-between;
  76. height: 100%;
  77. &:focus,
  78. &:hover {
  79. top: -1px;
  80. }
  81. `;
  82. const CardHeader = styled('div')`
  83. display: flex;
  84. padding: ${space(1.5)} ${space(2)};
  85. `;
  86. const Title = styled('div')`
  87. ${p => p.theme.text.cardTitle};
  88. color: ${p => p.theme.headingColor};
  89. ${p => p.theme.overflowEllipsis};
  90. font-weight: normal;
  91. `;
  92. const Detail = styled('div')`
  93. font-family: ${p => p.theme.text.familyMono};
  94. font-size: ${p => p.theme.fontSizeSmall};
  95. color: ${p => p.theme.gray300};
  96. ${p => p.theme.overflowEllipsis};
  97. line-height: 1.5;
  98. `;
  99. const CardBody = styled('div')`
  100. background: ${p => p.theme.gray100};
  101. padding: ${space(1.5)} ${space(2)};
  102. max-height: 150px;
  103. min-height: 150px;
  104. overflow: hidden;
  105. border-bottom: 1px solid ${p => p.theme.gray100};
  106. `;
  107. const CardFooter = styled('div')`
  108. display: flex;
  109. justify-content: space-between;
  110. align-items: center;
  111. padding: ${space(1)} ${space(2)};
  112. `;
  113. const DateSelected = styled('div')`
  114. font-size: ${p => p.theme.fontSizeSmall};
  115. display: grid;
  116. grid-column-gap: ${space(1)};
  117. color: ${p => p.theme.textColor};
  118. ${p => p.theme.overflowEllipsis};
  119. `;
  120. const DateStatus = styled('span')`
  121. color: ${p => p.theme.subText};
  122. padding-left: ${space(1)};
  123. `;
  124. export default DashboardCard;