From c456b6a2c5aec563ad1555d9f55b0303c9e41405 Mon Sep 17 00:00:00 2001 From: Bereket Engida Date: Fri, 19 Sep 2025 15:33:13 -0700 Subject: [PATCH] chore(docs): misc --- docs/app/api/support/route.ts | 58 +++++ docs/app/blog/[[...slug]]/page.tsx | 209 ++++++++++-------- docs/app/blog/_components/blog-list.tsx | 8 +- docs/app/blog/_components/support.tsx | 183 +++++++++++++++ ...supabase-auth-to-planetscale-migration.mdx | 1 + docs/content/blogs/1-3.mdx | 2 +- .../blogs/authjs-joins-better-auth.mdx | 66 ++++++ docs/package.json | 1 + docs/public/{avatar => avatars}/beka.jpg | Bin docs/public/blogs/authjs-joins.png | Bin 0 -> 565024 bytes docs/source.config.ts | 2 + pnpm-lock.yaml | 3 + 12 files changed, 439 insertions(+), 94 deletions(-) create mode 100644 docs/app/api/support/route.ts create mode 100644 docs/app/blog/_components/support.tsx create mode 100644 docs/content/blogs/authjs-joins-better-auth.mdx rename docs/public/{avatar => avatars}/beka.jpg (100%) create mode 100644 docs/public/blogs/authjs-joins.png diff --git a/docs/app/api/support/route.ts b/docs/app/api/support/route.ts new file mode 100644 index 00000000..5eb55d6c --- /dev/null +++ b/docs/app/api/support/route.ts @@ -0,0 +1,58 @@ +import { NextResponse } from "next/server"; + +export async function POST(request: Request) { + try { + const body = await request.json(); + const { + name, + email, + company, + website, + userCount, + interest, + features, + additional, + } = body ?? {}; + + if (!name || !email) { + return NextResponse.json( + { error: "Missing required fields" }, + { status: 400 }, + ); + } + + const payload = { + name, + email, + company: company ?? "", + website: website ?? "", + userCount: userCount ?? "", + interest: interest ?? "", + features: features ?? "", + additional: additional ?? "", + submittedAt: new Date().toISOString(), + userAgent: request.headers.get("user-agent") ?? undefined, + referer: request.headers.get("referer") ?? undefined, + }; + + const webhook = process.env.SUPPORT_WEBHOOK_URL; + if (webhook) { + try { + await fetch(webhook, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + } catch (e) { + console.error("Support webhook failed", e); + } + } else { + console.log("[support] submission", payload); + } + + return NextResponse.json({ ok: true }); + } catch (e) { + console.error(e); + return NextResponse.json({ error: "Invalid request" }, { status: 400 }); + } +} diff --git a/docs/app/blog/[[...slug]]/page.tsx b/docs/app/blog/[[...slug]]/page.tsx index 3a0f6731..f1034e0d 100644 --- a/docs/app/blog/[[...slug]]/page.tsx +++ b/docs/app/blog/[[...slug]]/page.tsx @@ -16,13 +16,13 @@ import { File, Folder, Files } from "fumadocs-ui/components/files"; import { Accordion, Accordions } from "fumadocs-ui/components/accordion"; import { Pre } from "fumadocs-ui/components/codeblock"; import { Glow } from "../_components/default-changelog"; -import { IconLink } from "../_components/changelog-layout"; -import { BookIcon, GitHubIcon, XIcon } from "../_components/icons"; -import { DiscordLogoIcon } from "@radix-ui/react-icons"; +import { XIcon } from "../_components/icons"; import { StarField } from "../_components/stat-field"; import Image from "next/image"; import { BlogPage } from "../_components/blog-list"; import { Callout } from "@/components/ui/callout"; +import { ArrowLeftIcon, ExternalLink } from "lucide-react"; +import { Support } from "../_components/support"; const metaTitle = "Blogs"; const metaDescription = "Latest changes , fixes and updates."; @@ -42,99 +42,127 @@ export default async function Page({ notFound(); } const MDX = page.data?.body; - const toc = page.data?.toc; const { title, description, date } = page.data; return ( -
-
- +
+
+ - -
- -
-
- - - -
-

- {title}{" "} -

-
- - -

{description}

-
- By {page.data?.author.name} | {formatDate(page.data?.date)} -
-
- {title} -
-
-
- - Documentation - - - GitHub - - - Community - -
-

- - BETTER-AUTH. - -

-
-
-
-
+
+

+ {title} +

+ {description && ( +

+ {description} +

+ )} +
+ {page.data?.author?.avatar && ( + {page.data?.author?.name + )} +
+ {page.data?.author?.name && ( + + {page.data.author.name} + + )} + {page.data?.author?.twitter && ( + <> + · + + @{page.data.author.twitter} + + + )} + {date && ( + <> + · + + + )} +
+
+
+
+ + + Blogs + +
+
+
+ +
) => ( - - ), + a: ({ className, href, children, ...props }: any) => { + const isExternal = + typeof href === "string" && /^(https?:)?\/\//.test(href); + const classes = cn( + "inline-flex items-center gap-1 font-medium underline decoration-dashed", + className, + ); + if (isExternal) { + return ( + + {children} + + + ); + } + return ( + + {children} + + ); + }, + Link: ({ className, href, children, ...props }: any) => { + const isExternal = + typeof href === "string" && /^(https?:)?\/\//.test(href); + const classes = cn( + "inline-flex items-center gap-1 font-medium underline decoration-dashed", + className, + ); + if (isExternal) { + return ( + + {children} + + + ); + } + return ( + + {children} + + ); + }, Step, Steps, File, @@ -164,9 +192,10 @@ export default async function Page({ {children} ), + Support, }} /> -
+
); diff --git a/docs/app/blog/_components/blog-list.tsx b/docs/app/blog/_components/blog-list.tsx index c5d4cbf8..2f0dd49b 100644 --- a/docs/app/blog/_components/blog-list.tsx +++ b/docs/app/blog/_components/blog-list.tsx @@ -9,7 +9,9 @@ import { DiscordLogoIcon } from "@radix-ui/react-icons"; import Image from "next/image"; export async function BlogPage() { - const posts = blogs.getPages(); + const posts = blogs.getPages().sort((a, b) => { + return new Date(b.data.date).getTime() - new Date(a.data.date).getTime(); + }); return (
@@ -75,8 +77,8 @@ export async function BlogPage() { {post.data.title} )} diff --git a/docs/app/blog/_components/support.tsx b/docs/app/blog/_components/support.tsx new file mode 100644 index 00000000..84293b62 --- /dev/null +++ b/docs/app/blog/_components/support.tsx @@ -0,0 +1,183 @@ +"use client"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Textarea } from "@/components/ui/textarea"; +import { Callout } from "@/components/ui/callout"; +import * as React from "react"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; + +export function Support() { + const [open, setOpen] = React.useState(false); + const [submitting, setSubmitting] = React.useState(false); + const formRef = React.useRef(null); + + async function onSubmit(event: React.FormEvent) { + event.preventDefault(); + if (submitting) return; + setSubmitting(true); + const form = new FormData(event.currentTarget); + const payload = { + name: String(form.get("name") || ""), + email: String(form.get("email") || ""), + company: String(form.get("company") || ""), + website: String(form.get("website") || ""), + userCount: String(form.get("userCount") || ""), + interest: String(form.get("interest") || ""), + features: String(form.get("features") || ""), + additional: String(form.get("additional") || ""), + }; + try { + const res = await fetch("/api/support", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + if (!res.ok) throw new Error("Failed to submit"); + setOpen(false); + formRef.current?.reset(); + // optionally add a toast later + } catch (e) { + console.error(e); + // optionally add error toast + } finally { + setSubmitting(false); + } + } + + return ( + + + Dedicated Support + + We're now offering on demand support for Better Auth and Auth.js. + Including help out migrations, consultations, premium dedicated + support and more. If you're interested, please get in touch. + + + + +
+ + + +
+ + + Request dedicated support + + Tell us about your team and what you're looking for. + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +