Skip to content
This repository was archived by the owner on Jun 19, 2025. It is now read-only.

Commit 6cf35b1

Browse files
chore: release 2.44.0 (#3713)
2 parents b94c5ac + 505556b commit 6cf35b1

File tree

20 files changed

+642
-102
lines changed

20 files changed

+642
-102
lines changed

CHANGELOG.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,53 @@
55

66
> All notable changes to this project will be documented in this file
77

8+
## [2.44.0-beta.6](https://github.com/open-sauced/app/compare/v2.44.0-beta.5...v2.44.0-beta.6) (2024-07-11)
9+
10+
11+
### 🍕 Features
12+
13+
* filter `RossChart` by contributor type ([#3698](https://github.com/open-sauced/app/issues/3698)) ([d48e8ac](https://github.com/open-sauced/app/commit/d48e8ac55e34136540277c1f7bb9dac2881d984c))
14+
15+
16+
### 🐛 Bug Fixes
17+
18+
* allow repos and contributors to be added to workspaces from explore page ([#3700](https://github.com/open-sauced/app/issues/3700)) ([3271dd4](https://github.com/open-sauced/app/commit/3271dd4f086df90b79a8ad5a704ea49a1f528602))
19+
20+
## [2.44.0-beta.5](https://github.com/open-sauced/app/compare/v2.44.0-beta.4...v2.44.0-beta.5) (2024-07-11)
21+
22+
23+
### 🍕 Features
24+
25+
* add Contributor Distribution to repo page ([#3712](https://github.com/open-sauced/app/issues/3712)) ([a53c7cd](https://github.com/open-sauced/app/commit/a53c7cd57652c75ecc52a9650a8aed9dbc9ac9c7))
26+
27+
## [2.44.0-beta.4](https://github.com/open-sauced/app/compare/v2.44.0-beta.3...v2.44.0-beta.4) (2024-07-11)
28+
29+
30+
### 🍕 Features
31+
32+
* allow input and display of GitHub releases as highlights ([#3705](https://github.com/open-sauced/app/issues/3705)) ([8aa0177](https://github.com/open-sauced/app/commit/8aa01775eb4f93491459bbcd571293bf207e66e4))
33+
34+
## [2.44.0-beta.3](https://github.com/open-sauced/app/compare/v2.44.0-beta.2...v2.44.0-beta.3) (2024-07-10)
35+
36+
37+
### 🐛 Bug Fixes
38+
39+
* now a contributor insight can be deleted if one was deleted in the same page session ([#3246](https://github.com/open-sauced/app/issues/3246)) ([8dd1ee2](https://github.com/open-sauced/app/commit/8dd1ee25d4bfbee7d78191609e205e0cb62f7213))
40+
41+
## [2.44.0-beta.2](https://github.com/open-sauced/app/compare/v2.44.0-beta.1...v2.44.0-beta.2) (2024-07-10)
42+
43+
44+
### 🐛 Bug Fixes
45+
46+
* now the contributions tab is the first tab on the user profile page ([#3709](https://github.com/open-sauced/app/issues/3709)) ([87d0ee9](https://github.com/open-sauced/app/commit/87d0ee9f72a002de2cffb0df1d335add01303330))
47+
48+
## [2.44.0-beta.1](https://github.com/open-sauced/app/compare/v2.43.0...v2.44.0-beta.1) (2024-07-10)
49+
50+
51+
### 🍕 Features
52+
53+
* implemented the Radar chart component ([#3704](https://github.com/open-sauced/app/issues/3704)) ([8579f41](https://github.com/open-sauced/app/commit/8579f416f3077108631d04d88833a47d43896e17))
54+
855
## [2.43.0](https://github.com/open-sauced/app/compare/v2.42.0...v2.43.0) (2024-07-09)
956

1057

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Meta, StoryObj } from "@storybook/react";
2+
import { ChartTooltipContent } from "components/primitives/chart-primitives";
3+
import { RadarChart } from "./RadarChart";
4+
5+
type Story = StoryObj<typeof RadarChart>;
6+
7+
const meta: Meta<typeof RadarChart> = {
8+
title: "Components/Graphs/RadarChart",
9+
component: RadarChart,
10+
args: {
11+
radarDataKey: "percentage",
12+
polarAngleAxisDataKey: "type",
13+
data: [
14+
{ type: "Code review", percentage: 30 },
15+
{ type: "Issues", percentage: 11 },
16+
{ type: "Pull requests", percentage: 11 },
17+
{ type: "Commits", percentage: 48 },
18+
],
19+
fill: "#ff5100",
20+
},
21+
};
22+
23+
export default meta;
24+
25+
export const Default: Story = {};
26+
export const WithDot: Story = {
27+
args: {
28+
dot: {
29+
r: 4,
30+
fillOpacity: 1,
31+
},
32+
},
33+
};
34+
export const WithCustomTooltip: Story = {
35+
args: {
36+
chartTooltipContent: (
37+
<ChartTooltipContent
38+
className="bg-white"
39+
formatter={(value, name, item, index, payload) => {
40+
return `${value}%`;
41+
}}
42+
/>
43+
),
44+
},
45+
};

components/Graphs/RadarChart.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"use client";
2+
3+
// If you want to make improvements to the chart or extend it, see the examples at https://ui.shadcn.com/charts#radar-chart
4+
5+
import { PolarAngleAxis, PolarGrid, Radar, RadarChart as RawRadarChart } from "recharts";
6+
import { ComponentProps } from "react";
7+
import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from "components/primitives/chart-primitives";
8+
9+
const chartConfig = {
10+
desktop: {
11+
label: "Desktop",
12+
color: "hsl(var(--chart-1))",
13+
},
14+
} satisfies ChartConfig;
15+
16+
interface RadarChartProps {
17+
data: ComponentProps<typeof RawRadarChart>["data"];
18+
cursor?: boolean;
19+
radarDataKey: ComponentProps<typeof Radar>["dataKey"];
20+
polarAngleAxisDataKey: ComponentProps<typeof PolarAngleAxis>["dataKey"];
21+
children?: React.ReactNode;
22+
opacity?: number;
23+
// create an optional prop for the type that is the type of the RawRadarChart dot prop, but infer it from it's existing type
24+
dot?: ComponentProps<typeof Radar>["dot"];
25+
fill: ComponentProps<typeof Radar>["fill"];
26+
// If you need a diffent unit, you can add it here
27+
maxHeight?: `${number}${"px" | "rem"}` | "auto";
28+
labelFormatter?: ComponentProps<typeof ChartTooltipContent>["labelFormatter"];
29+
formatter?: ComponentProps<typeof ChartTooltipContent>["formatter"];
30+
chartTooltipContent?: ComponentProps<typeof ChartTooltip>["content"];
31+
}
32+
33+
export function RadarChart({
34+
data,
35+
radarDataKey,
36+
polarAngleAxisDataKey,
37+
dot,
38+
fill,
39+
cursor = false,
40+
opacity = 0.6,
41+
maxHeight = "250px",
42+
chartTooltipContent,
43+
}: RadarChartProps) {
44+
return (
45+
<ChartContainer config={chartConfig} className={`mx-auto aspect-square max-h-[${maxHeight}]`}>
46+
<RawRadarChart data={data}>
47+
<ChartTooltip
48+
cursor={cursor}
49+
content={chartTooltipContent ? chartTooltipContent : <ChartTooltipContent className="bg-white" />}
50+
/>
51+
<PolarAngleAxis dataKey={polarAngleAxisDataKey} />
52+
<PolarGrid />
53+
<Radar dataKey={radarDataKey} fill={fill} fillOpacity={opacity} dot={dot} />
54+
</RawRadarChart>
55+
</ChartContainer>
56+
);
57+
}

components/Repositories/RossChart.tsx

Lines changed: 98 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { FaUsers } from "react-icons/fa6";
22
import { Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, TooltipProps, XAxis, YAxis } from "recharts";
33
import { NameType, ValueType } from "recharts/types/component/DefaultTooltipContent";
4-
import { useMemo } from "react";
4+
import { useMemo, useState } from "react";
55
import Card from "components/atoms/Card/card";
66
import SkeletonWrapper from "components/atoms/SkeletonLoader/skeleton-wrapper";
77
import humanizeNumber from "lib/utils/humanizeNumber";
@@ -11,34 +11,82 @@ type RossChartProps = {
1111
isLoading: boolean;
1212
error: Error | undefined;
1313
range: number;
14-
rangedTotal: number;
1514
className?: string;
1615
};
1716

18-
export default function RossChart({ stats, rangedTotal, isLoading, error, range, className }: RossChartProps) {
19-
const rangedAverage = useMemo(() => (rangedTotal / range).toPrecision(2), [rangedTotal, range]);
17+
export default function RossChart({ stats, isLoading, error, range, className }: RossChartProps) {
18+
const [filterOutside, setFilterOutside] = useState(true);
19+
const [filterRecurring, setFilterRecurring] = useState(true);
20+
const [filterInternal, setFilterInternal] = useState(true);
21+
22+
const filteredTotal = useMemo(() => {
23+
return (
24+
stats?.contributors.reduce((prev, curr) => {
25+
return (prev +=
26+
(filterOutside ? curr.new : 0) +
27+
(filterRecurring ? curr.recurring : 0) +
28+
(filterInternal ? curr.internal : 0));
29+
}, 0) || 0
30+
);
31+
}, [stats, filterOutside, filterRecurring, filterInternal]);
32+
33+
const rangedAverage = useMemo(
34+
() => (filteredTotal / (stats ? stats.contributors.length : 1)).toPrecision(2),
35+
[filteredTotal, stats]
36+
);
2037

2138
const weeklyData = useMemo(() => {
22-
const result = stats?.contributors.reverse().map((week) => {
39+
return stats?.contributors.reverse().map((week) => {
2340
return {
24-
...week,
41+
new: filterOutside ? week.new : 0,
42+
recurring: filterRecurring ? week.recurring : 0,
43+
internal: filterInternal ? week.internal : 0,
2544
bucket: new Date(week.bucket).toLocaleDateString(undefined, { month: "numeric", day: "numeric" }),
2645
};
2746
});
28-
29-
return result;
30-
}, [stats]);
47+
}, [stats, filterOutside, filterRecurring, filterInternal]);
3148

3249
const bucketTicks = useMemo(() => {
33-
const result = stats?.contributors.reverse().map((week) => {
50+
return stats?.contributors.reverse().map((week) => {
3451
return new Date(week.bucket).toLocaleDateString(undefined, { month: "numeric", day: "numeric" });
3552
});
36-
37-
return result;
3853
}, [stats]);
3954

55+
const CONTRIBUTOR_COLORS: Record<string, string> = {
56+
internal: "#1E3A8A",
57+
recurring: "#2563EB",
58+
new: "#60A5FA",
59+
};
60+
61+
function CustomTooltip({ active, payload }: TooltipProps<ValueType, NameType>) {
62+
if (active && payload) {
63+
const legend = payload.reverse();
64+
return (
65+
<figcaption className="flex flex-col gap-1 text-sm bg-white px-4 py-3 rounded-lg border w-fit">
66+
<section className="flex gap-2 font-medium text-slate-500 items-center text-xs w-fit">
67+
<FaUsers className="fill-slate-500" />
68+
<p>Contributors</p>
69+
<p>{payload[0]?.payload.bucket}</p>
70+
</section>
71+
72+
{legend.map((data) => (
73+
<section key={`${data.payload.bucket}_${data.name}`} className="flex justify-between">
74+
<p className="flex gap-2 items-center px-1 text-slate-500 capitalize">
75+
<span
76+
className={`w-2 h-2 rounded-full bg-[${CONTRIBUTOR_COLORS[data.name || "new"]}] inline-block`}
77+
></span>
78+
{data.name === "new" ? "Outside" : data.name}:
79+
</p>
80+
<p className="font-medium pl-2">{data.value}</p>
81+
</section>
82+
))}
83+
</figcaption>
84+
);
85+
}
86+
}
87+
4088
return (
41-
<Card className={`${className ?? ""} flex flex-col gap-8 w-full h-full items-center !px-6 !py-8`}>
89+
<Card className={`${className ?? ""} flex flex-col gap-6 w-full h-full items-center !px-6 !py-8`}>
4290
<section className="flex flex-col lg:flex-row w-full items-start lg:items-start gap-4 lg:justify-between px-2">
4391
{isLoading ? (
4492
<SkeletonWrapper width={100} height={24} />
@@ -54,10 +102,10 @@ export default function RossChart({ stats, rangedTotal, isLoading, error, range,
54102
<aside className="flex gap-8">
55103
<div>
56104
<h3 className="text-xs xl:text-sm text-slate-500">Total {range} days</h3>
57-
<p className="font-semibold text-xl xl:text-3xl">{rangedTotal}</p>
105+
<p className="font-semibold text-xl xl:text-3xl">{filteredTotal}</p>
58106
</div>
59107
<div>
60-
<h3 className="text-xs xl:text-sm text-slate-500">Average per day</h3>
108+
<h3 className="text-xs xl:text-sm text-slate-500">Average per week</h3>
61109
<p className="font-semibold text-xl xl:text-3xl">{humanizeNumber(rangedAverage)}</p>
62110
</div>
63111
</aside>
@@ -79,57 +127,48 @@ export default function RossChart({ stats, rangedTotal, isLoading, error, range,
79127
/>
80128
<Tooltip content={CustomTooltip} filterNull={false} />
81129
<CartesianGrid vertical={false} strokeDasharray="4" stroke="#E2E8F0" />
82-
<Bar dataKey="internal" stackId="a" fill="#1E3A8A" />
83-
<Bar dataKey="recurring" stackId="a" fill="#2563EB" />
84-
<Bar dataKey="new" stackId="a" fill="#60A5FA" />
130+
{filterInternal && <Bar dataKey="internal" stackId="a" fill={CONTRIBUTOR_COLORS["internal"]} />}
131+
{filterRecurring && <Bar dataKey="recurring" stackId="a" fill={CONTRIBUTOR_COLORS["recurring"]} />}
132+
{filterOutside && <Bar dataKey="new" stackId="a" fill={CONTRIBUTOR_COLORS["new"]} />}
85133
</BarChart>
86134
)}
87135
</ResponsiveContainer>
136+
137+
<fieldset className="flex flex-row gap-4 w-fit text-sm mx-auto p-0">
138+
<button
139+
onClick={() => setFilterOutside(!filterOutside)}
140+
className={`flex gap-2 h-full items-center text-slate-700 ${
141+
!filterOutside && "opacity-60"
142+
} transition-all duration-300 hover:bg-slate-100 rounded-lg px-2 py-1`}
143+
>
144+
<span className={`w-4 h-4 rounded-sm bg-[#60A5FA] inline-block`} />
145+
Outside
146+
</button>
147+
148+
<button
149+
onClick={() => setFilterRecurring(!filterRecurring)}
150+
className={`flex gap-2 h-full items-center text-slate-700 ${
151+
!filterRecurring && "opacity-60"
152+
} transition-all duration-300 hover:bg-slate-100 rounded-lg px-2 py-1`}
153+
>
154+
<span className={`w-4 h-4 rounded-sm bg-[#2563EB] inline-block`} />
155+
Recurring
156+
</button>
157+
158+
<button
159+
onClick={() => setFilterInternal(!filterInternal)}
160+
className={`flex gap-2 h-full items-center text-slate-700 ${
161+
!filterInternal && "opacity-60"
162+
} transition-all duration-300 hover:bg-slate-100 rounded-lg px-2 py-1`}
163+
>
164+
<span className={`w-4 h-4 rounded-sm bg-[#1E3A8A] inline-block`} />
165+
Internal
166+
</button>
167+
</fieldset>
88168
</Card>
89169
);
90170
}
91171

92-
function CustomTooltip({ active, payload }: TooltipProps<ValueType, NameType>) {
93-
if (active && payload) {
94-
return (
95-
<figcaption className="flex flex-col gap-1 text-sm bg-white px-4 py-3 rounded-lg border w-fit">
96-
<section className="flex gap-2 font-medium text-slate-500 items-center text-xs w-fit">
97-
<FaUsers className="fill-slate-500" />
98-
<p>Contributors</p>
99-
<p>{payload[0]?.payload.bucket}</p>
100-
</section>
101-
{payload[2]?.value && (
102-
<section className="flex justify-between">
103-
<p className="flex gap-2 items-center px-1 text-slate-500">
104-
<span className={`w-2 h-2 rounded-full bg-[#60A5FA] inline-block`}></span>
105-
New:
106-
</p>
107-
<p className="font-medium pl-2">{payload[2]?.value}</p>
108-
</section>
109-
)}
110-
{payload[1]?.value && (
111-
<section className="flex justify-between">
112-
<p className="flex gap-2 items-center px-1 text-slate-500">
113-
<span className={`w-2 h-2 rounded-full bg-[#2563EB] inline-block`}></span>
114-
Recurring:
115-
</p>
116-
<p className="font-medium pl-2">{payload[1]?.value}</p>
117-
</section>
118-
)}
119-
{payload[0]?.value && (
120-
<section className="flex justify-between">
121-
<p className="flex gap-2 items-center px-1 text-slate-500">
122-
<span className={`w-2 h-2 rounded-full bg-[#1E3A8A] inline-block`}></span>
123-
Internal:
124-
</p>
125-
<p className="font-medium pl-2">{payload[0]?.value}</p>
126-
</section>
127-
)}
128-
</figcaption>
129-
);
130-
}
131-
}
132-
133172
function CustomTick({ x, y, payload }: { x: number; y: number; payload: { value: string } }) {
134173
return (
135174
<g transform={`translate(${x},${y})`}>

components/atoms/TextInput/text-input.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ const TextInput = ({
3232

3333
const handleResetInput = () => {
3434
handleChange?.("");
35-
if (fieldRef) {
35+
if (fieldRef?.current) {
3636
fieldRef.current!.value = "";
37+
} else if (inputRef.current) {
38+
inputRef.current.value = "";
3739
}
3840
};
3941

0 commit comments

Comments
 (0)