diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bc039a..0afcce5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# [1.13.0](https://github.com/unitystation/unitystation-web/compare/v1.12.0...v1.13.0) (2025-12-10) + + +### Bug Fixes + +* update all deps ([f307964](https://github.com/unitystation/unitystation-web/commit/f307964ce27f43656b783ece0db9e1d4f967eab0)) +* updated vulnerable deps ([5826ba7](https://github.com/unitystation/unitystation-web/commit/5826ba71cfb41b1b5023b46f971b797119de6877)) + + +### Features + +* changes "Weekly Update" pill's text to "Progress update" ([6845ea6](https://github.com/unitystation/unitystation-web/commit/6845ea6b17fe2701e8fbdc4cecf64d1a1319ec97)) +* first iteration of the new ledger page ([1cae21d](https://github.com/unitystation/unitystation-web/commit/1cae21d0368529be3fba38b3e8990ff6c61639a4)) +* first iteration of the new ledger page ([cd5c42f](https://github.com/unitystation/unitystation-web/commit/cd5c42f3b569956608fcc80886f1b962d1ed1319)) + # [1.13.0](https://github.com/unitystation/unitystation-web/compare/v1.12.0...v1.13.0) (2025-04-21) diff --git a/app/changelog/buildStatsComponent.tsx b/app/changelog/buildStatsComponent.tsx new file mode 100644 index 0000000..87aa8c6 --- /dev/null +++ b/app/changelog/buildStatsComponent.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import Build from '../../types/build'; +import Panel from '../common/uiLibrary/panel'; +import { FaArrowUp, FaWrench } from 'react-icons/fa'; +import { FaCirclePlus } from "react-icons/fa6"; +import Card from '../../components/mocules/Card'; +import SmallNoteText from '../../components/mocules/SmallNoteText'; + +interface BuildStatsProps { + builds: Build[]; +} + +const BuildStatsComponent: React.FC = ({ builds }) => { + const recentBuilds = builds.slice(0, 150); + + const stats = recentBuilds.reduce((acc, build) => { + build.changes.forEach(change => { + switch (change.category) { + case 'NEW': + acc.additions++; + break; + case 'IMPROVEMENT': + acc.improvements++; + break; + case 'FIX': + acc.fixes++; + break; + default: + break; + } + }); + return acc; + }, { additions: 0, improvements: 0, fixes: 0 }); + + return ( + +

Recent Build Statistics

+
+ + +
+

Additions

+

{stats.additions}

+
+
+ + + +
+

Improvements

+

{stats.improvements}

+
+
+ + + +
+

Bug Fixes

+

{stats.fixes}

+
+
+
+ +
+ ); +}; + +export default BuildStatsComponent; \ No newline at end of file diff --git a/app/changelog/latestReleaseComponent.tsx b/app/changelog/latestReleaseComponent.tsx new file mode 100644 index 0000000..b63bcc6 --- /dev/null +++ b/app/changelog/latestReleaseComponent.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import Panel from '../common/uiLibrary/panel'; +import { FaTag } from 'react-icons/fa'; +import Card from '../../components/mocules/Card'; +import SmallNoteText from '../../components/mocules/SmallNoteText'; + +interface ReleaseInfo { + version: string; + commitSha: string; +} + +interface LatestReleaseComponentProps { + releaseInfo: ReleaseInfo; +} + +const LatestReleaseComponent: React.FC = ({ releaseInfo }) => { + return ( + +

Latest Code-Scan Release

+ + +
+

{releaseInfo.version}

+

Commit: {releaseInfo.commitSha.substring(0, 7)}

+
+
+ +
+ ); +}; + +export default LatestReleaseComponent; \ No newline at end of file diff --git a/app/changelog/page.tsx b/app/changelog/page.tsx index d8faea6..ba1dc18 100644 --- a/app/changelog/page.tsx +++ b/app/changelog/page.tsx @@ -7,26 +7,97 @@ import Container from "../common/uiLibrary/container"; import PageHeading from "../common/uiLibrary/PageHeading"; import BuildComponent from "./buildComponent"; import LoadingBuild from "./loading"; +import BuildStatsComponent from "./buildStatsComponent"; +import LatestReleaseComponent from "./latestReleaseComponent"; + +interface ReleaseInfo { + version: string; + commitSha: string; +} + +interface GitHubTag { + name: string; + commit: { + url: string; + sha: string; + }; +} const fetchChangelog = async (url: string): Promise => { const response = await fetch(url); return await response.json(); } +const fetchLatestRelease = async (): Promise => { + try { + const response = await fetch('https://api.github.com/repos/unitystation/unitystation/tags'); + const data = await response.json(); + + if (data && data.length > 0) { + const goodFileTags = data + .filter((tag: GitHubTag) => tag.name.startsWith('good-file-')) + .sort((a: GitHubTag, b: GitHubTag) => { + const versionA = a.name.replace('good-file-', '').split('.').map(Number); + const versionB = b.name.replace('good-file-', '').split('.').map(Number); + for (let i = 0; i < Math.max(versionA.length, versionB.length); i++) { + const numA = versionA[i] || 0; + const numB = versionB[i] || 0; + if (numA !== numB) { + return numB - numA; // Higher version first + } + } + return 0; + }); + + if (goodFileTags.length > 0) { + const latestTag = goodFileTags[0]; + const commitResponse = await fetch(latestTag.commit.url); + const commitData = await commitResponse.json(); + + return { + version: latestTag.name, + commitSha: latestTag.commit.sha, + }; + } + } + return null; + } catch (error) { + console.error('Error fetching latest release:', error); + return null; + } +}; + const ChangelogPage = () => { const [buildsResponse, setBuildsResponse] = useState(); const [builds, setBuilds] = useState([]); const [isLoading, setIsLoading] = useState(true); - const INITIAL_PAGE = "https://changelog.unitystation.org/all-changes?limit=5"; + const [releaseInfo, setReleaseInfo] = useState(null); + const [isLoadingRelease, setIsLoadingRelease] = useState(true); + const INITIAL_PAGE = "https://changelog.unitystation.org/all-changes?limit=10"; useEffect(() => { - fetchChangelog(INITIAL_PAGE).then((response) => { + const fetchData = async () => { setIsLoading(true); - setBuildsResponse(response); - setBuilds(response.results); - }).finally( - () => setIsLoading(false) - ) + setIsLoadingRelease(true); + + try { + const [changelogResponse, releaseData] = await Promise.all([ + fetchChangelog(INITIAL_PAGE), + fetchLatestRelease() + ]); + + setBuildsResponse(changelogResponse); + setBuilds(changelogResponse.results); + setReleaseInfo(releaseData); + } catch (error) { + console.error('Error fetching data:', error); + } finally { + setIsLoading(false); + setIsLoadingRelease(false); + } + }; + + fetchData(); }, []); const handleScroll = useCallback(async () => { @@ -46,8 +117,20 @@ const ChangelogPage = () => { return ( - Changelog
+ {!!builds && builds.length > 0 && ( +
+ + {isLoadingRelease ? ( +
+

Loading latest code-scan release info...

+
+ ) : releaseInfo ? ( + + ) : null} +
+ )} + Changelog {!!builds && builds.map((build, index) => { return })} diff --git a/components/mocules/Card.tsx b/components/mocules/Card.tsx new file mode 100644 index 0000000..4aa126e --- /dev/null +++ b/components/mocules/Card.tsx @@ -0,0 +1,15 @@ +import React, { ReactNode } from 'react'; + +interface CardProps { + children: ReactNode; +} + +const Card: React.FC = ({ children }) => { + return ( +
+ {children} +
+ ); +}; + +export default Card; diff --git a/components/mocules/SmallNoteText.tsx b/components/mocules/SmallNoteText.tsx new file mode 100644 index 0000000..1c6909e --- /dev/null +++ b/components/mocules/SmallNoteText.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +interface SmallNoteTextProps { + text: string; +} + +const SmallNoteText: React.FC = ({ text }) => { + return ( +

{text}

+ ); +}; + +export default SmallNoteText; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f46c6fe..0517abf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11789,4 +11789,4 @@ } } } -} +} \ No newline at end of file