Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Changelog #9063

Merged
merged 27 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .envrc

This file was deleted.

20 changes: 20 additions & 0 deletions app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {PrismaAdapter} from '@auth/prisma-adapter';
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';

import {prisma} from 'sentry-docs/prisma';

const handler = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID || '',
clientSecret: process.env.GOOGLE_CLIENT_SECRET || '',
}),
],
session: {
strategy: 'jwt',
},
});

export {handler as GET, handler as POST};
92 changes: 92 additions & 0 deletions app/changelog/%5Fadmin/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {Fragment, Suspense} from 'react';
import Link from 'next/link';

import {editChangelog} from 'sentry-docs/actions/changelog';
import {FileUpload} from 'sentry-docs/components/changelog/fileUpload';
import {ForwardRefEditor} from 'sentry-docs/components/changelog/forwardRefEditor';
import {TitleSlug} from 'sentry-docs/components/changelog/titleSlug';
import {Button} from 'sentry-docs/components/changelog/ui/Button';
import {Select} from 'sentry-docs/components/changelog/ui/Select';
import {prisma} from 'sentry-docs/prisma';

export default async function ChangelogCreatePage({params}) {
const categories = await prisma.category.findMany();
const changelog = await prisma.changelog.findUnique({
where: {id: params.id},
include: {
author: true,
categories: true,
},
});

if (!changelog) {
return (
<Fragment>
<header>
<h2>Changelog not found</h2>
</header>
<footer>
<Link href="/changelogs">Return to Changelogs list</Link>
</footer>
</Fragment>
);
}

return (
<section className="overflow-x-auto col-start-3 col-span-8">
<form action={editChangelog} className="px-2 w-full">
<input type="hidden" name="id" value={changelog.id} />
<TitleSlug defaultSlug={changelog.slug} defaultTitle={changelog.title} />
<FileUpload defaultFile={changelog.image || ''} />
<div className="my-6">
<label htmlFor="summary" className="block text-xs font-medium text-gray-700">
Summary
<Fragment>
&nbsp;<span className="font-bold text-secondary">*</span>
</Fragment>
</label>
<textarea name="summary" className="w-full" required>
{changelog.summary}
</textarea>
<span className="text-xs text-gray-500 italic">
This will be shown in the list
</span>
</div>
<div>
<Select
name="categories"
className="mt-1 mb-6"
label="Category"
placeholder="Select Category"
defaultValue={changelog.categories.map(category => ({
label: category.name,
value: category.name,
}))}
options={categories.map(category => ({
label: category.name,
value: category.name,
}))}
isMulti
/>
</div>

<Suspense fallback={null}>
<ForwardRefEditor
name="content"
defaultValue={changelog.content || ''}
className="w-full"
/>
</Suspense>

<footer className="flex items-center justify-between mt-2 mb-8">
<Link href="/changelog/_admin" className="underline text-gray-500">
Return to Changelogs list
</Link>
<div>
<Button type="submit">Update</Button>
</div>
</footer>
</form>
</section>
);
}
25 changes: 25 additions & 0 deletions app/changelog/%5Fadmin/confirm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use client';

export default function Confirm({changelog, action, children}) {
return (
<form
action={action}
className="inline-block"
onSubmit={e => {
e.preventDefault();
// eslint-disable-next-line no-alert
if (confirm('Are you sure?')) {
action(new FormData(e.currentTarget));
}
}}
>
<input type="hidden" name="id" value={changelog.id} />
<button
type="submit"
className="text-indigo-600 hover:bg-indigo-100 rounded-md px-1 py-2 text-xs whitespace-nowrap"
>
{children}
</button>
</form>
);
}
61 changes: 61 additions & 0 deletions app/changelog/%5Fadmin/create/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {Fragment} from 'react';
import Link from 'next/link';

import {createChangelog} from 'sentry-docs/actions/changelog';
import {FileUpload} from 'sentry-docs/components/changelog/fileUpload';
import {ForwardRefEditor} from 'sentry-docs/components/changelog/forwardRefEditor';
import {TitleSlug} from 'sentry-docs/components/changelog/titleSlug';
import {Button} from 'sentry-docs/components/changelog/ui/Button';
import {Select} from 'sentry-docs/components/changelog/ui/Select';
import {prisma} from 'sentry-docs/prisma';

export default async function ChangelogCreatePage() {
const categories = await prisma.category.findMany();

return (
<section className="overflow-x-auto col-start-3 col-span-8">
<form action={createChangelog} className="px-2 w-full">
<TitleSlug />
<FileUpload />
<div className="my-6">
<label htmlFor="summary" className="block text-xs font-medium text-gray-700">
Summary
<Fragment>
&nbsp;<span className="font-bold text-secondary">*</span>
</Fragment>
</label>
<textarea name="summary" className="w-full" required />
<span className="text-xs text-gray-500 italic">
This will be shown in the list
</span>
</div>
<div>
<Select
name="categories"
className="mt-1 mb-6"
label="Category"
placeholder="Select Category"
options={categories.map(category => ({
label: category.name,
value: category.name,
}))}
isMulti
/>
</div>

<ForwardRefEditor name="content" className="w-full" />

<footer className="flex items-center justify-between mt-2">
<Link href="/changelog/_admin" className="underline text-gray-500">
Return to Changelogs list
</Link>
<div>
<Button type="submit">Create (not published yet)</Button>
<br />
<span className="text-xs text-gray-500 italic">You can publish it later</span>
</div>
</footer>
</form>
</section>
);
}
28 changes: 28 additions & 0 deletions app/changelog/%5Fadmin/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {type ReactNode, Suspense} from 'react';
import {GET} from 'app/api/auth/[...nextauth]/route';
import {getServerSession} from 'next-auth/next';

import LoginButton from 'sentry-docs/components/changelog/loginButton';
import NextAuthSessionProvider from 'sentry-docs/components/nextAuthSessionProvider';

export default async function Layout({children}: {children: ReactNode}) {
const session = await getServerSession(GET);
let content = (
<div className="relative min-h-[calc(100vh-8rem)] w-full mx-auto bg-gray-200 pt-16 grid grid-cols-12">
{children}
<div className="fixed top-3 right-4 z-50">
<Suspense fallback={null}>
<LoginButton />
</Suspense>
</div>
</div>
);
if (!session) {
content = (
<div className="relative min-h-[calc(100vh-8rem)] w-full mx-auto bg-gray-200 pt-16 flex items-center justify-center">
<LoginButton />
</div>
);
}
return <NextAuthSessionProvider>{content}</NextAuthSessionProvider>;
}
121 changes: 121 additions & 0 deletions app/changelog/%5Fadmin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {Fragment} from 'react';
import {PlusIcon} from '@radix-ui/react-icons';
import {Button, Text} from '@radix-ui/themes';
import Link from 'next/link';

import {
deleteChangelog,
publishChangelog,
unpublishChangelog,
} from 'sentry-docs/actions/changelog';
import {prisma} from 'sentry-docs/prisma';

import Confirm from './confirm';

export default async function ChangelogsListPage() {
const changelogs = await prisma.changelog.findMany({
include: {
categories: true,
author: true,
},
orderBy: {
createdAt: 'desc',
},
});

return (
<Fragment>
<header className="mb-4 col-start-3 col-span-2 text-left">
<Button>
<PlusIcon />
<Link href="/changelog/_admin/create">New Changelog</Link>
</Button>
</header>
<section className="overflow-x-auto col-start-3 col-span-8 shadow-md rounded-lg">
<table className="w-full text-sm text-left text-gray-500">
<thead className="text-xs text-gray-700 uppercase bg-gray-50">
<tr>
<th className="whitespace-nowrap px-4 py-2">Title</th>
<th className="whitespace-nowrap px-4 py-2">Categories</th>
<th className="whitespace-nowrap px-4 py-2">Published by</th>
<th className="px-4 py-2" />
</tr>
</thead>
<tbody className="divide-y divide-gray-200 bg-white border-b hover:bg-gray-50 dark:hover:bg-gray-600">
{changelogs.length === 0 && (
<tr>
<td
colSpan={10}
className="text-center font-medium text-gray-900 whitespace-nowrap"
>
No changelogs found
</td>
</tr>
)}

{changelogs.map(changelog => (
<tr key={changelog.id} className="bg-white border-b hover:bg-gray-50">
<td className="px-6 py-4 font-medium text-gray-900">{changelog.title}</td>

<td className="px-4 py-2">
{changelog.categories.map(category => (
<div
key={category.id}
className="inline whitespace-nowrap p-2 uppercase shadow-sm no-underline rounded-full text-red text-xs mr-1 bg-gray-100"
>
{category.name}
</div>
))}
</td>
<td className="px-4 py-2 text-center">
{changelog.published && (
<span className="text-gray-500">
<Text size="1">
{' '}
{new Date(changelog.publishedAt || '').toLocaleDateString(
undefined,
{month: 'long', day: 'numeric'}
)}
</Text>
<br />
</span>
)}
<Text size="1">{changelog.author?.name}</Text>
</td>

<td className="px-4 py-2">
<div className="flex h-full justify-end">
<Link
href={`/changelog/${changelog.slug}`}
className="text-indigo-600 hover:bg-indigo-100 rounded-md px-1 py-2 text-xs whitespace-nowrap"
>
👀 Show
</Link>
<Link
href={`/changelog/_admin/${changelog.id}/edit`}
className="text-indigo-600 hover:bg-indigo-100 rounded-md px-1 py-2 text-xs whitespace-nowrap"
>
📝 Edit
</Link>
{changelog.published ? (
<Confirm action={unpublishChangelog} changelog={changelog}>
⛔️ Unpublish?
</Confirm>
) : (
<Confirm action={publishChangelog} changelog={changelog}>
✅ Publish?
</Confirm>
)}
<Confirm action={deleteChangelog} changelog={changelog}>
💀 Delete?
</Confirm>
</div>
</td>
</tr>
))}
</tbody>
</table>
</section>
</Fragment>
);
}
Loading
Loading