117 lines
3.1 KiB
TypeScript
117 lines
3.1 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>
|
||
)
|
||
}
|