diff --git a/packages/view/src/components/FolderActivityFlow/FolderActivityFlow.scss b/packages/view/src/components/FolderActivityFlow/FolderActivityFlow.scss index 802ce91c..b69cf34e 100644 --- a/packages/view/src/components/FolderActivityFlow/FolderActivityFlow.scss +++ b/packages/view/src/components/FolderActivityFlow/FolderActivityFlow.scss @@ -14,6 +14,10 @@ --chart-color-9: #3a4776; // mono-sec --chart-color-10: #aa4b72; // mono-fai + --title-left-offset: 30px; + --title-icon-size: 34px; + --title-gap: 4px; + padding: 1rem; margin-bottom: 2rem; @@ -21,41 +25,54 @@ border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - &__header { + &__head { + margin-top: 40px; + margin-left: var(--title-left-offset); display: flex; - justify-content: space-between; + flex-direction: column; align-items: flex-start; - margin-bottom: 1rem; + gap: 0.25rem; } &__title { - margin: 0 0 0.5rem 0; - font-size: 1.2rem; - font-weight: 600; + display: flex; + align-items: center; + column-gap: var(--title-gap); color: $color-white; + font-size: 34px; + letter-spacing: 0.25px; + line-height: 42px; + font-weight: $font-weight-regular; + white-space: nowrap; } - &__subtitle { - margin: 0 0 0.5rem 0; - font-size: 0.875rem; - color: $color-light-gray; - } - - &__mode-toggle { - transition: all 0.2s ease; + &__title-icon { + font-size: var(--title-icon-size); + width: var(--title-icon-size); + height: var(--title-icon-size); + transform: translateY(-2px); + color: $color-white; - &:hover { - transform: translateY(-1px); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + &.MuiSvgIcon-root { + font-size: var(--title-icon-size); } + } - &:active { - transform: translateY(0); - } + &__title-text { + display: flex; + align-items: center; + } + + &__subtitle { + color: $color-white; + font-size: 20px; + font-weight: $font-weight-regular; } &__breadcrumb { - padding-left: 50px; + display: flex; + align-items: center; + margin-bottom: 0.5rem; .MuiBreadcrumbs-separator { color: $color-white; @@ -72,6 +89,7 @@ font-size: $font-size-body; font-weight: $font-weight-regular; text-decoration: none; + cursor: pointer; &:hover { color: $color-white; @@ -196,4 +214,4 @@ font-size: 11px; } } -} \ No newline at end of file +} diff --git a/packages/view/src/components/FolderActivityFlow/FolderActivityFlow.tsx b/packages/view/src/components/FolderActivityFlow/FolderActivityFlow.tsx index 79d3bbaf..ae1c3830 100644 --- a/packages/view/src/components/FolderActivityFlow/FolderActivityFlow.tsx +++ b/packages/view/src/components/FolderActivityFlow/FolderActivityFlow.tsx @@ -5,6 +5,7 @@ import Breadcrumbs from "@mui/material/Breadcrumbs"; import Link from "@mui/material/Link"; import Typography from "@mui/material/Typography"; import NavigateNextIcon from "@mui/icons-material/NavigateNext"; +import WorkspacePremiumRoundedIcon from "@mui/icons-material/WorkspacePremiumRounded"; import { useDataStore } from "store"; @@ -37,6 +38,68 @@ const FolderActivityFlow = () => { const breadcrumbs = useMemo(() => getBreadcrumbs(), [getBreadcrumbs]); + const { topContributorName, releaseRangeLabel } = useMemo(() => { + if (!totalData || totalData.length === 0 || releaseTopFolderPaths.length === 0) { + return { + topContributorName: null, + releaseRangeLabel: "...", + }; + } + + const currentDepth = currentPath === "" ? 1 : currentPath.split("/").length + 1; + const releaseContributorActivities = extractReleaseBasedContributorActivities( + totalData, + releaseTopFolderPaths, + currentDepth + ); + + if (releaseContributorActivities.length === 0) { + return { + topContributorName: null, + releaseRangeLabel: "...", + }; + } + + const contributorClocs = new Map(); + releaseContributorActivities.forEach((activity) => { + const current = contributorClocs.get(activity.contributorName) || 0; + contributorClocs.set(activity.contributorName, current + activity.changes); + }); + + let maxCloc = 0; + let mostActiveContributor = ""; + contributorClocs.forEach((cloc, name) => { + if (cloc > maxCloc) { + maxCloc = cloc; + mostActiveContributor = name; + } + }); + + const releaseIndices = Array.from( + new Set(releaseContributorActivities.map((activity) => activity.releaseIndex)) + ).sort((a, b) => a - b); + const releaseTagByIndex = new Map(); + releaseContributorActivities.forEach((activity) => { + if (!releaseTagByIndex.has(activity.releaseIndex)) { + releaseTagByIndex.set(activity.releaseIndex, activity.releaseTag); + } + }); + + const resolvedTags = releaseIndices.map((index) => { + const tag = releaseTagByIndex.get(index); + return tag && tag.trim().length > 0 ? tag : `Release ${index}`; + }); + + const firstReleaseLabel = resolvedTags[0] || "..."; + const lastReleaseLabel = resolvedTags[resolvedTags.length - 1] || firstReleaseLabel; + const rangeLabel = resolvedTags.length <= 1 ? firstReleaseLabel : `${firstReleaseLabel} to ${lastReleaseLabel}`; + + return { + topContributorName: mostActiveContributor || null, + releaseRangeLabel: rangeLabel || "...", + }; + }, [totalData, releaseTopFolderPaths, currentPath]); + useEffect(() => { if (!totalData || totalData.length === 0) { return; @@ -84,43 +147,47 @@ const FolderActivityFlow = () => { }); }, [totalData, releaseGroups, releaseTopFolderPaths, navigateToFolder, currentPath]); + const topContributorLabel = topContributorName || "..."; + return (
-
-
-

Contributors Folder Activity Flow

-
Contributors moving between folders across releases
+
+ } + aria-label="breadcrumb" + className="folder-activity-flow__breadcrumb" + > + {breadcrumbs.map((crumb, index) => { + const isLast = index === breadcrumbs.length - 1; + + if (isLast) { + return {crumb}; + } + + return ( + navigateToBreadcrumb(index, breadcrumbs.length)} + > + {crumb} + + ); + })} + + +
+ + Top contributor is {topContributorLabel}
+ +
{releaseRangeLabel}
- } - aria-label="breadcrumb" - className="folder-activity-flow__breadcrumb" - > - {breadcrumbs.map((crumb, index) => { - const isLast = index === breadcrumbs.length - 1; - - if (isLast) { - return {crumb}; - } - - return ( - navigateToBreadcrumb(index, breadcrumbs.length)} - sx={{ cursor: "pointer" }} - > - {crumb} - - ); - })} -