Skip to content

Commit 3b6c95d

Browse files
MaxIsJoecorp-0
authored andcommitted
feat: Build stats
1 parent 6105932 commit 3b6c95d

5 files changed

Lines changed: 219 additions & 8 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React from 'react';
2+
import Build from '../../types/build';
3+
import Panel from '../common/uiLibrary/panel';
4+
import { FaArrowUp, FaWrench } from 'react-icons/fa';
5+
import { FaCirclePlus } from "react-icons/fa6";
6+
import Card from '../../components/mocules/Card';
7+
import SmallNoteText from '../../components/mocules/SmallNoteText';
8+
9+
interface BuildStatsProps {
10+
builds: Build[];
11+
}
12+
13+
const BuildStatsComponent: React.FC<BuildStatsProps> = ({ builds }) => {
14+
const recentBuilds = builds.slice(0, 150);
15+
16+
const stats = recentBuilds.reduce((acc, build) => {
17+
build.changes.forEach(change => {
18+
switch (change.category) {
19+
case 'NEW':
20+
acc.additions++;
21+
break;
22+
case 'IMPROVEMENT':
23+
acc.improvements++;
24+
break;
25+
case 'FIX':
26+
acc.fixes++;
27+
break;
28+
default:
29+
break;
30+
}
31+
});
32+
return acc;
33+
}, { additions: 0, improvements: 0, fixes: 0 });
34+
35+
return (
36+
<Panel>
37+
<h3 className="text-xl font-bold mb-4 text-white">Recent Build Statistics</h3>
38+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
39+
<Card>
40+
<FaCirclePlus className="text-green-500 text-2xl mr-3" />
41+
<div>
42+
<p className="text-gray-300 text-sm">Additions</p>
43+
<p className="text-white text-xl font-bold">{stats.additions}</p>
44+
</div>
45+
</Card>
46+
47+
<Card>
48+
<FaArrowUp className="text-blue-600 text-2xl mr-3" />
49+
<div>
50+
<p className="text-gray-300 text-sm">Improvements</p>
51+
<p className="text-white text-xl font-bold">{stats.improvements}</p>
52+
</div>
53+
</Card>
54+
55+
<Card>
56+
<FaWrench className="text-orange-600 text-2xl mr-3" />
57+
<div>
58+
<p className="text-gray-300 text-sm">Bug Fixes</p>
59+
<p className="text-white text-xl font-bold">{stats.fixes}</p>
60+
</div>
61+
</Card>
62+
</div>
63+
<SmallNoteText text={`Stats are based on the past ${recentBuilds.length} builds that were pushed onto our staging server from upstream.`} />
64+
</Panel>
65+
);
66+
};
67+
68+
export default BuildStatsComponent;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
import Panel from '../common/uiLibrary/panel';
3+
import { FaTag } from 'react-icons/fa';
4+
import Card from '../../components/mocules/Card';
5+
import SmallNoteText from '../../components/mocules/SmallNoteText';
6+
7+
interface ReleaseInfo {
8+
version: string;
9+
commitSha: string;
10+
}
11+
12+
interface LatestReleaseComponentProps {
13+
releaseInfo: ReleaseInfo;
14+
}
15+
16+
const LatestReleaseComponent: React.FC<LatestReleaseComponentProps> = ({ releaseInfo }) => {
17+
return (
18+
<Panel>
19+
<h3 className="text-xl font-bold mb-4 text-white">Latest Code-Scan Release</h3>
20+
<Card>
21+
<FaTag className="text-purple-500 text-2xl mr-3" />
22+
<div>
23+
<p className="text-white text-xl font-bold">{releaseInfo.version}</p>
24+
<p className="text-gray-300 text-sm">Commit: {releaseInfo.commitSha.substring(0, 7)}</p>
25+
</div>
26+
</Card>
27+
<SmallNoteText text="To keep our players safe, we only allow whitelisted code to be run on clients that are scanned and opened via stationhub." />
28+
</Panel>
29+
);
30+
};
31+
32+
export default LatestReleaseComponent;

app/changelog/page.tsx

Lines changed: 91 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,97 @@ import Container from "../common/uiLibrary/container";
77
import PageHeading from "../common/uiLibrary/PageHeading";
88
import BuildComponent from "./buildComponent";
99
import LoadingBuild from "./loading";
10+
import BuildStatsComponent from "./buildStatsComponent";
11+
import LatestReleaseComponent from "./latestReleaseComponent";
12+
13+
interface ReleaseInfo {
14+
version: string;
15+
commitSha: string;
16+
}
17+
18+
interface GitHubTag {
19+
name: string;
20+
commit: {
21+
url: string;
22+
sha: string;
23+
};
24+
}
1025

1126
const fetchChangelog = async (url: string): Promise<AllChangesResponse> => {
1227
const response = await fetch(url);
1328
return await response.json();
1429
}
1530

31+
const fetchLatestRelease = async (): Promise<ReleaseInfo | null> => {
32+
try {
33+
const response = await fetch('https://api.github.com/repos/unitystation/unitystation/tags');
34+
const data = await response.json();
35+
36+
if (data && data.length > 0) {
37+
const goodFileTags = data
38+
.filter((tag: GitHubTag) => tag.name.startsWith('good-file-'))
39+
.sort((a: GitHubTag, b: GitHubTag) => {
40+
const versionA = a.name.replace('good-file-', '').split('.').map(Number);
41+
const versionB = b.name.replace('good-file-', '').split('.').map(Number);
42+
for (let i = 0; i < Math.max(versionA.length, versionB.length); i++) {
43+
const numA = versionA[i] || 0;
44+
const numB = versionB[i] || 0;
45+
if (numA !== numB) {
46+
return numB - numA; // Higher version first
47+
}
48+
}
49+
return 0;
50+
});
51+
52+
if (goodFileTags.length > 0) {
53+
const latestTag = goodFileTags[0];
54+
const commitResponse = await fetch(latestTag.commit.url);
55+
const commitData = await commitResponse.json();
56+
57+
return {
58+
version: latestTag.name,
59+
commitSha: latestTag.commit.sha,
60+
};
61+
}
62+
}
63+
return null;
64+
} catch (error) {
65+
console.error('Error fetching latest release:', error);
66+
return null;
67+
}
68+
};
69+
1670
const ChangelogPage = () => {
1771
const [buildsResponse, setBuildsResponse] = useState<AllChangesResponse>();
1872
const [builds, setBuilds] = useState<Build[]>([]);
1973
const [isLoading, setIsLoading] = useState<boolean>(true);
20-
const INITIAL_PAGE = "https://changelog.unitystation.org/all-changes?limit=5";
74+
const [releaseInfo, setReleaseInfo] = useState<ReleaseInfo | null>(null);
75+
const [isLoadingRelease, setIsLoadingRelease] = useState<boolean>(true);
76+
const INITIAL_PAGE = "https://changelog.unitystation.org/all-changes?limit=10";
2177

2278
useEffect(() => {
23-
fetchChangelog(INITIAL_PAGE).then((response) => {
79+
const fetchData = async () => {
2480
setIsLoading(true);
25-
setBuildsResponse(response);
26-
setBuilds(response.results);
27-
}).finally(
28-
() => setIsLoading(false)
29-
)
81+
setIsLoadingRelease(true);
82+
83+
try {
84+
const [changelogResponse, releaseData] = await Promise.all([
85+
fetchChangelog(INITIAL_PAGE),
86+
fetchLatestRelease()
87+
]);
88+
89+
setBuildsResponse(changelogResponse);
90+
setBuilds(changelogResponse.results);
91+
setReleaseInfo(releaseData);
92+
} catch (error) {
93+
console.error('Error fetching data:', error);
94+
} finally {
95+
setIsLoading(false);
96+
setIsLoadingRelease(false);
97+
}
98+
};
99+
100+
fetchData();
30101
}, []);
31102

32103
const handleScroll = useCallback(async () => {
@@ -46,8 +117,20 @@ const ChangelogPage = () => {
46117

47118
return (
48119
<Container>
49-
<PageHeading isCentered>Changelog</PageHeading>
50120
<div className="flex flex-col gap-4">
121+
{!!builds && builds.length > 0 && (
122+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
123+
<BuildStatsComponent builds={builds} />
124+
{isLoadingRelease ? (
125+
<div className="flex items-center justify-center h-32 bg-gray-800 rounded-lg">
126+
<p className="text-gray-400">Loading latest code-scan release info...</p>
127+
</div>
128+
) : releaseInfo ? (
129+
<LatestReleaseComponent releaseInfo={releaseInfo} />
130+
) : null}
131+
</div>
132+
)}
133+
<PageHeading isCentered>Changelog</PageHeading>
51134
{!!builds && builds.map((build, index) => {
52135
return <BuildComponent build={build} key={index}/>
53136
})}

components/mocules/Card.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React, { ReactNode } from 'react';
2+
3+
interface CardProps {
4+
children: ReactNode;
5+
}
6+
7+
const Card: React.FC<CardProps> = ({ children }) => {
8+
return (
9+
<div className="flex items-center p-4 bg-gray-800 rounded-lg">
10+
{children}
11+
</div>
12+
);
13+
};
14+
15+
export default Card;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react';
2+
3+
interface SmallNoteTextProps {
4+
text: string;
5+
}
6+
7+
const SmallNoteText: React.FC<SmallNoteTextProps> = ({ text }) => {
8+
return (
9+
<p className="text-gray-400 text-sm mt-4">{text}</p>
10+
);
11+
};
12+
13+
export default SmallNoteText;

0 commit comments

Comments
 (0)