diff --git a/static/app/views/issueList/pages/dynamicGrouping.tsx b/static/app/views/issueList/pages/dynamicGrouping.tsx index d2465e729ad593..1d76d6da793705 100644 --- a/static/app/views/issueList/pages/dynamicGrouping.tsx +++ b/static/app/views/issueList/pages/dynamicGrouping.tsx @@ -19,14 +19,7 @@ import ProjectBadge from 'sentry/components/idBadge/projectBadge'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import Redirect from 'sentry/components/redirect'; import TimeSince from 'sentry/components/timeSince'; -import { - IconCalendar, - IconClock, - IconClose, - IconFire, - IconFix, - IconUpload, -} from 'sentry/icons'; +import {IconClose, IconFire, IconUpload} from 'sentry/icons'; import {t, tn} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Group} from 'sentry/types/group'; @@ -278,112 +271,112 @@ function ClusterCard({ return ( - - - - {cluster.title} - - {cluster.description && ( - - {showDescription ? ( - {cluster.description} - ) : ( - setShowDescription(true)}> - {t('View summary')} - - )} - - )} - - - - {issueCount} - - {tn('issue', 'issues', issueCount)} - - - - - - {cluster.fixability_score && ( - - - - - {Math.round(cluster.fixability_score * 100)}% - {' '} - {t('confidence')} - - + {/* Zone 1: Title + Description (Primary Focus) */} + + {cluster.title} + {cluster.description && ( + + {showDescription ? ( + {cluster.description} + ) : ( + setShowDescription(true)}> + {t('View summary')} + + )} + )} - - - {clusterStats.isPending ? ( - - – - - ) : ( - - - {clusterStats.totalEvents.toLocaleString()} - {' '} + + + + {/* Zone 2: Stats (Secondary Context) */} + + + + + {clusterStats.isPending ? ( + + – + + ) : ( + {clusterStats.totalEvents.toLocaleString()} + )} + {tn('event', 'events', clusterStats.totalEvents)} + + {cluster.fixability_score && ( + + + {Math.round(cluster.fixability_score * 100)}% + + + {t('fixable')} + + )} - - {!clusterStats.isPending && clusterStats.lastSeen && ( - - - - - )} - {!clusterStats.isPending && clusterStats.firstSeen && ( - - - - - )} - - - - - - {cluster.group_ids.length > 3 && ( - - {t('+ %s more similar issues', cluster.group_ids.length - 3)} + + + {!clusterStats.isPending && clusterStats.lastSeen && ( + + + {t('Last seen')} + + + + )} + {!clusterStats.isPending && clusterStats.firstSeen && ( + + + {t('Age')} + + + + )} + + + + {/* Zone 3: Nested Issues (Detail Content) */} + + + + {tn('%s Issue', '%s Issues', issueCount)} - )} - + + + + {cluster.group_ids.length > 3 && ( + + {t('+ %s more similar issues', cluster.group_ids.length - 3)} + + )} + + - + {/* Zone 4: Actions (Tertiary) */} + - + ); } @@ -802,63 +795,148 @@ const CardsGrid = styled('div')` } `; -// Card with hover effect +// Card with subtle hover effect const CardContainer = styled('div')` background: ${p => p.theme.background}; border: 1px solid ${p => p.theme.border}; border-radius: ${p => p.theme.borderRadius}; - padding: ${space(3)}; display: flex; flex-direction: column; min-width: 0; + overflow: hidden; transition: border-color 0.2s ease, box-shadow 0.2s ease; &:hover { - border-color: ${p => p.theme.purple300}; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); + border-color: ${p => p.theme.purple200}; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); } `; -// Issue count badge - compact version -const IssueCountBadge = styled('div')` +// Zone 1: Title area - clean and prominent +const CardHeader = styled('div')` + padding: ${space(3)} ${space(3)} ${space(2)}; display: flex; flex-direction: column; - align-items: center; - padding: ${space(1)} ${space(1.5)}; - background: ${p => p.theme.purple100}; - border-radius: ${p => p.theme.borderRadius}; - flex-shrink: 0; + gap: ${space(1)}; `; -const IssueCountNumber = styled('div')` - font-size: 24px; +const ClusterTitle = styled('h3')` + margin: 0; + font-size: ${p => p.theme.fontSize.xl}; font-weight: 600; - color: ${p => p.theme.purple400}; - line-height: 1; + color: ${p => p.theme.textColor}; + line-height: 1.3; + word-break: break-word; `; -// Horizontal stats bar below header -const ClusterStatsBar = styled('div')` +// Zone 2: Stats section with visual hierarchy +const StatsSection = styled('div')` + padding: ${space(2)} ${space(3)}; + background: ${p => p.theme.backgroundSecondary}; + border-top: 1px solid ${p => p.theme.innerBorder}; + border-bottom: 1px solid ${p => p.theme.innerBorder}; display: flex; - flex-wrap: wrap; + justify-content: space-between; align-items: center; gap: ${space(2)}; - padding: ${space(1.5)} 0; - margin-top: ${space(1.5)}; - border-top: 1px solid ${p => p.theme.innerBorder}; + flex-wrap: wrap; +`; + +const PrimaryStats = styled('div')` + display: flex; + align-items: center; + gap: ${space(3)}; +`; + +const EventsMetric = styled('div')` + display: flex; + align-items: center; + gap: ${space(1)}; + color: ${p => p.theme.red300}; +`; + +const EventsCount = styled('span')` + font-size: ${p => p.theme.fontSize.xl}; + font-weight: 700; + color: ${p => p.theme.textColor}; + font-variant-numeric: tabular-nums; +`; + +const FixabilityIndicator = styled('div')<{score: number}>` + display: flex; + flex-direction: column; + align-items: center; + padding: ${space(0.75)} ${space(1.5)}; + background: ${p => + p.score >= 0.7 + ? p.theme.green100 + : p.score >= 0.4 + ? p.theme.yellow100 + : p.theme.gray100}; + border-radius: ${p => p.theme.borderRadius}; + color: ${p => + p.score >= 0.7 + ? p.theme.green400 + : p.score >= 0.4 + ? p.theme.yellow400 + : p.theme.gray400}; + line-height: 1.2; +`; + +const SecondaryStats = styled('div')` + display: flex; + gap: ${space(3)}; +`; + +const SecondaryStatItem = styled('div')` + display: flex; + flex-direction: column; + gap: ${space(0.25)}; font-size: ${p => p.theme.fontSize.sm}; + color: ${p => p.theme.textColor}; +`; + +// Zone 3: Issues list with clear containment +const IssuesSection = styled('div')` + padding: ${space(2)} ${space(3)}; + flex: 1; + display: flex; + flex-direction: column; +`; + +const IssuesSectionHeader = styled('div')` + margin-bottom: ${space(1.5)}; color: ${p => p.theme.subText}; + letter-spacing: 0.5px; `; -const StatItem = styled('div')` +const IssuesList = styled('div')` display: flex; - align-items: center; - gap: ${space(0.5)}; + flex-direction: column; + gap: ${space(1.5)}; +`; + +const MoreIssuesIndicator = styled('div')` + font-size: ${p => p.theme.fontSize.sm}; + color: ${p => p.theme.subText}; + text-align: center; + font-style: italic; + padding-top: ${space(1)}; +`; + +// Zone 4: Footer with actions +const CardFooter = styled('div')` + padding: ${space(2)} ${space(3)}; + border-top: 1px solid ${p => p.theme.innerBorder}; + display: flex; + justify-content: flex-end; + gap: ${space(1)}; + background: ${p => p.theme.backgroundSecondary}; `; -// Issue preview link with hover effect +// Issue preview link with hover effect - consistent with issue feed cards const IssuePreviewLink = styled(Link)` display: block; padding: ${space(1.5)} ${space(2)}; @@ -878,7 +956,7 @@ const IssuePreviewLink = styled(Link)` // Issue title with ellipsis and nested em styling for EventOrGroupTitle const IssueTitle = styled('div')` font-size: ${p => p.theme.fontSize.md}; - font-weight: ${p => p.theme.fontWeight.bold}; + font-weight: 600; color: ${p => p.theme.textColor}; line-height: 1.4; ${p => p.theme.overflowEllipsis}; @@ -896,6 +974,7 @@ const IssueMessage = styled(EventMessage)` margin: 0; font-size: ${p => p.theme.fontSize.sm}; color: ${p => p.theme.subText}; + opacity: 0.9; `; // Meta separator line