diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-03-02 00:55:08 +0100 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-03-02 00:55:08 +0100 |
| commit | 77a3708213606d520833944141c3aa9169954e07 (patch) | |
| tree | a9d9edf5abc209a1bb02a109ceed6fc5e5c445fb /apps/web/src/app/changelog/page.tsx | |
| parent | 9a00e8ac5b77e8d82102457aaffd19945a2f52cb (diff) | |
| download | opencut-77a3708213606d520833944141c3aa9169954e07.tar.gz | |
web: refactor changelog to support a dedicated page for each version + links for each release
Diffstat (limited to 'apps/web/src/app/changelog/page.tsx')
| -rw-r--r-- | apps/web/src/app/changelog/page.tsx | 118 |
1 files changed, 21 insertions, 97 deletions
diff --git a/apps/web/src/app/changelog/page.tsx b/apps/web/src/app/changelog/page.tsx index a9072485..8cb585b6 100644 --- a/apps/web/src/app/changelog/page.tsx +++ b/apps/web/src/app/changelog/page.tsx @@ -1,8 +1,14 @@ import type { Metadata } from "next"; import { BasePage } from "@/app/base-page"; import { Separator } from "@/components/ui/separator"; -import { cn } from "@/utils/ui"; -import { allChangelogs } from "content-collections"; +import { type Release as ReleaseType, getSortedReleases } from "./utils"; +import { + ReleaseArticle, + ReleaseMeta, + ReleaseTitle, + ReleaseDescription, + ReleaseChanges, +} from "./components/release"; export const metadata: Metadata = { title: "Changelog - OpenCut", @@ -14,25 +20,8 @@ export const metadata: Metadata = { }, }; -const knownSectionOrder = ["new", "improved", "fixed", "breaking"]; - -const knownSectionTitles: Record<string, string> = { - new: "Features", - improved: "Improvements", - fixed: "Fixes", - breaking: "Breaking Changes", -}; - -function getSectionTitle(type: string): string { - return ( - knownSectionTitles[type] ?? type.charAt(0).toUpperCase() + type.slice(1) - ); -} - export default function ChangelogPage() { - const releases = [...allChangelogs].sort((a, b) => - b.version.localeCompare(a.version, undefined, { numeric: true }), - ); + const releases = getSortedReleases(); return ( <BasePage title="Changelog" description="See what's new in OpenCut"> @@ -48,7 +37,6 @@ export default function ChangelogPage() { <div key={release.version} className="flex flex-col"> <ReleaseEntry release={release} /> {releaseIndex < releases.length - 1 && ( - // ml-1.5 aligns with the center of the 11px timeline dot <Separator className="my-10 sm:ml-1.5" /> )} </div> @@ -60,83 +48,19 @@ export default function ChangelogPage() { ); } -type Change = { type: string; text: string }; -type Release = (typeof allChangelogs)[number]; - -function groupAndOrderChanges({ changes }: { changes: Change[] }) { - const grouped = changes.reduce<Record<string, Change[]>>((acc, change) => { - if (!acc[change.type]) { - acc[change.type] = []; - } - acc[change.type].push(change); - return acc; - }, {}); - - const customTypes = Object.keys(grouped).filter( - (type) => !knownSectionOrder.includes(type), - ); - const orderedTypes = [ - ...knownSectionOrder.filter((type) => grouped[type]?.length > 0), - ...customTypes, - ]; - - return { grouped, orderedTypes }; -} - -function ReleaseEntry({ release }: { release: Release }) { - const { grouped: groupedChanges, orderedTypes } = groupAndOrderChanges({ - changes: release.changes, - }); - +function ReleaseEntry({ release }: { release: ReleaseType }) { return ( - <article className="relative sm:pl-10"> - <div aria-hidden className="absolute left-0 top-[3px] hidden sm:block"> - <div - className={cn( - "size-[11px] rounded-full border-[1.5px]", - release.isLatest - ? "border-foreground bg-foreground" - : "border-muted-foreground/30 bg-background", - )} - /> - </div> - - <div className="flex flex-col gap-5"> - <div className="flex items-center gap-2.5 flex-wrap"> - <span className="text-sm font-medium tracking-widest text-muted-foreground"> - {release.version} - {release.date} - </span> - </div> - - <div className="flex flex-col gap-4"> - <h2 className="text-2xl font-bold tracking-tight">{release.title}</h2> - {release.description && ( - <p className="text-base text-foreground leading-relaxed max-w-xl"> - {release.description} - </p> - )} - </div> - - <div className="flex flex-col gap-4"> - {orderedTypes.map((type) => ( - <div key={type} className="flex flex-col gap-1.5"> - <h3 className="text-base font-semibold text-foreground"> - {getSectionTitle(type)}: - </h3> - <ul className="list-disc pl-5 space-y-1.5"> - {groupedChanges[type].map((change) => ( - <li - key={change.text} - className="text-base text-foreground leading-relaxed" - > - {change.text} - </li> - ))} - </ul> - </div> - ))} - </div> + <ReleaseArticle variant="list" isLatest={release.isLatest}> + <ReleaseMeta release={release} /> + <div className="flex flex-col gap-4"> + <ReleaseTitle as="h2" href={`/changelog/${release.version}`}> + {release.title} + </ReleaseTitle> + {release.description && ( + <ReleaseDescription>{release.description}</ReleaseDescription> + )} </div> - </article> + <ReleaseChanges release={release} /> + </ReleaseArticle> ); } |
