117 lines
3.2 KiB
TypeScript
117 lines
3.2 KiB
TypeScript
"use client";
|
||
|
||
import PageHeading from "../ui/PageHeading";
|
||
|
||
import { useEffect, useMemo, useState } from "react";
|
||
import { useRouter } from "next/navigation";
|
||
import { authFetch, clearToken, getApiBase, getToken } from "../lib/auth";
|
||
|
||
type SiteInfo = {
|
||
changelog?: string;
|
||
};
|
||
|
||
type ChangelogGroup = {
|
||
date: string;
|
||
entries: string[];
|
||
};
|
||
|
||
const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
||
|
||
const parseChangelog = (raw: string): ChangelogGroup[] => {
|
||
const groups: ChangelogGroup[] = [];
|
||
for (const rawLine of raw.split("\n")) {
|
||
const line = rawLine.trim();
|
||
if (!line) continue;
|
||
const [candidateDate, ...messageParts] = line.split("|");
|
||
if (DATE_PATTERN.test(candidateDate) && messageParts.length > 0) {
|
||
const message = messageParts.join("|").trim();
|
||
if (!message) continue;
|
||
const currentGroup = groups[groups.length - 1];
|
||
if (currentGroup?.date === candidateDate) {
|
||
currentGroup.entries.push(message);
|
||
} else {
|
||
groups.push({ date: candidateDate, entries: [message] });
|
||
}
|
||
continue;
|
||
}
|
||
if (groups.length === 0) {
|
||
groups.push({ date: "Updates", entries: [line] });
|
||
} else {
|
||
groups[groups.length - 1].entries.push(line);
|
||
}
|
||
}
|
||
return groups;
|
||
};
|
||
|
||
export default function ChangelogPage() {
|
||
const router = useRouter();
|
||
const [groups, setGroups] = useState<ChangelogGroup[]>([]);
|
||
const [loading, setLoading] = useState(true);
|
||
|
||
useEffect(() => {
|
||
const token = getToken();
|
||
if (!token) {
|
||
router.push("/login");
|
||
return;
|
||
}
|
||
let active = true;
|
||
const load = async () => {
|
||
try {
|
||
const baseUrl = getApiBase();
|
||
const response = await authFetch(`${baseUrl}/site/info`);
|
||
if (!response.ok) {
|
||
if (response.status === 401) {
|
||
clearToken();
|
||
router.push("/login");
|
||
return;
|
||
}
|
||
throw new Error("Failed to load changelog");
|
||
}
|
||
const data: SiteInfo = await response.json();
|
||
if (!active) return;
|
||
setGroups(parseChangelog(data?.changelog ?? ""));
|
||
} catch (err) {
|
||
console.error(err);
|
||
if (!active) return;
|
||
setGroups([]);
|
||
} finally {
|
||
if (active) setLoading(false);
|
||
}
|
||
};
|
||
void load();
|
||
return () => {
|
||
active = false;
|
||
};
|
||
}, [router]);
|
||
|
||
const content = useMemo(() => {
|
||
if (loading) {
|
||
return <div className="loading-text">Loading changelog...</div>;
|
||
}
|
||
if (groups.length === 0) {
|
||
return <div className="meta">No updates posted yet.</div>;
|
||
}
|
||
return (
|
||
<div className="changelog-groups">
|
||
{groups.map((group) => (
|
||
<section key={group.date} className="changelog-group">
|
||
<h2>{group.date}</h2>
|
||
<ul className="changelog-list">
|
||
{group.entries.map((entry, index) => (
|
||
<li key={`${group.date}-${entry}-${index}`}>{entry}</li>
|
||
))}
|
||
</ul>
|
||
</section>
|
||
))}
|
||
</div>
|
||
);
|
||
}, [groups, loading]);
|
||
|
||
return (
|
||
<main className="card changelog-page">
|
||
<PageHeading title="Changelog" description="What’s new and improved in Magent." />
|
||
{content}
|
||
</main>
|
||
);
|
||
}
|