Skip to content
23 changes: 23 additions & 0 deletions docs/changelog.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: Documentation Changelog
sidebar_order: 100
description: Track recent updates to Sentry docs
---

import {DocsChangelog} from 'sentry-docs/components/changelog/docsChangelog';


## Recent Updates

<DocsChangelog />

## Alternative Views

- [Full Content Dashboard](https://sentry-content-dashboard.sentry.dev/) - View all Sentry content (blog, videos, docs, changelog)
- [RSS Feed](https://sentry-content-dashboard.sentry.dev/api/docs/feed) - Subscribe to doc updates in your RSS reader
- [JSON API](https://sentry-content-dashboard.sentry.dev/api/docs) - Programmatically access changelog data

<Alert level="info">
The changelog updates automatically throughout the day. Summaries are generated by AI to provide quick, user-friendly insights into each update from the [getsentry/sentry-docs](https://github.com/getsentry/sentry-docs) repository.
</Alert>

175 changes: 175 additions & 0 deletions src/components/changelog/docsChangelog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
interface ChangelogEntry {
author: string;
description: string;
id: string;
publishedAt: string;
title: string;
url: string;
filesChanged?: {
added: string[];
modified: string[];
removed: string[];
};
}

async function getChangelogEntries(): Promise<ChangelogEntry[]> {
try {
const res = await fetch('https://sentry-content-dashboard.sentry.dev/api/docs', {
next: {revalidate: 3600}, // Cache for 1 hour
});

if (!res.ok) {
throw new Error(`Failed to fetch changelog: ${res.status} ${res.statusText}`);
}

return res.json();
} catch (error) {
// eslint-disable-next-line no-console
console.error('Error fetching changelog:', error);
// Error fetching changelog - return empty array
return [];
}
}

export async function DocsChangelog() {
const entries = await getChangelogEntries();

if (entries.length === 0) {
return (
<div className="rounded-lg border border-yellow-200 bg-yellow-50 p-4 text-yellow-800 dark:border-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-200">
<p className="font-semibold">No changelog entries available</p>
<p className="text-sm">Check back later for updates.</p>
</div>
);
}

// Show only the 20 most recent entries
const recentEntries = entries.slice(0, 20);

return (
<div className="space-y-8">
{recentEntries.map(entry => {
const date = new Date(entry.publishedAt);
const totalFiles =
(entry.filesChanged?.added?.length || 0) +
(entry.filesChanged?.modified?.length || 0) +
(entry.filesChanged?.removed?.length || 0);

return (
<article key={entry.id} className="border-b border-gray-200 pb-8 last:border-0">
<header className="mb-3">
<h3 className="mb-2 text-xl font-semibold">
<a
href={entry.url}
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

Looks like the hover:underline is not being applied since it's a heading. I do think there should be an underline. But this creates a separate issue: it looks like .prose class is being applied which has some default styling for content on other docs pages. Things like text color, etc. Maybe it's worth removing all tailwind classes and seeing which ones are needed to get it to the desired look (border bottom after sections, etc) and leave a lot of it to default to the same styling as the other pages?

>
{entry.title.replace('Docs Update: ', '')}
</a>
</h3>
<div className="flex flex-wrap items-center gap-3 text-sm text-gray-600 dark:[color:rgb(210,199,218)]">
<time dateTime={entry.publishedAt}>
{date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</time>
{totalFiles > 0 && <span>•</span>}
{totalFiles > 0 && (
<span>
{totalFiles} file{totalFiles !== 1 ? 's' : ''} changed
</span>
)}
</div>
</header>

<p className="mb-4 text-gray-700 dark:[color:rgb(210,199,218)]">
{entry.description}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my concern is that description currently lists changed files where we have filesChanged below it which could result in duplicate content. Is this something we can control a bit better and pull out an informative description of the actual file changes and list the files below?

</p>

{entry.filesChanged && totalFiles > 0 && (
<details className="text-sm">
<summary className="cursor-pointer text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200">
View changed files
</summary>
<div className="mt-2 space-y-2 rounded-md bg-gray-50 p-3 dark:bg-gray-800">
{entry.filesChanged.added && entry.filesChanged.added.length > 0 && (
<div>
<span className="font-semibold text-green-700 dark:text-green-400">
Added:
</span>
<ul className="ml-4 mt-1 list-inside list-disc">
{entry.filesChanged.added.map(file => (
<li key={file} className="text-gray-700 dark:text-gray-300">
{file}
</li>
))}
</ul>
</div>
)}
{entry.filesChanged.modified &&
entry.filesChanged.modified.length > 0 && (
<div>
<span className="font-semibold text-blue-700 dark:text-blue-400">
Modified:
</span>
<ul className="ml-4 mt-1 list-inside list-disc">
{entry.filesChanged.modified.map(file => (
<li key={file} className="text-gray-700 dark:text-gray-300">
{file}
</li>
))}
</ul>
</div>
)}
{entry.filesChanged.removed &&
entry.filesChanged.removed.length > 0 && (
<div>
<span className="font-semibold text-red-700 dark:text-red-400">
Removed:
</span>
<ul className="ml-4 mt-1 list-inside list-disc">
{entry.filesChanged.removed.map(file => (
<li key={file} className="text-gray-700 dark:text-gray-300">
{file}
</li>
))}
</ul>
</div>
)}
</div>
</details>
)}
</article>
);
})}
{entries.length > 20 && (
<div className="mt-8 rounded-lg border border-gray-200 bg-gray-50 p-4 text-center dark:border-gray-700 dark:bg-gray-800">
<p className="text-sm text-gray-600 dark:[color:rgb(210,199,218)]">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we just use our Alert component here?

Showing the 20 most recent updates. View the{' '}
<a
href="https://sentry-content-dashboard.sentry.dev/"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
full content dashboard
</a>{' '}
or subscribe to the{' '}
<a
href="https://sentry-content-dashboard.sentry.dev/api/docs/feed"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
RSS feed
</a>
.
</p>
</div>
)}
</div>
);
}
4 changes: 4 additions & 0 deletions src/components/sidebar/sidebarNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ const productSidebarItems = [
title: 'Concepts & Reference',
root: 'concepts',
},
{
title: 'Documentation Changelog',
root: 'changelog',
},
];

export async function SidebarNavigation({path}: {path: string[]}) {
Expand Down
Loading