Skip to content

Commit

Permalink
blog: init (#605)
Browse files Browse the repository at this point in the history
  • Loading branch information
Yonom authored Jul 29, 2024
1 parent 8f0bfdc commit 15f3272
Show file tree
Hide file tree
Showing 12 changed files with 320 additions and 73 deletions.
85 changes: 85 additions & 0 deletions apps/docs/app/(home)/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { notFound } from "next/navigation";
import Link from "next/link";
import { blog, BlogPage } from "@/app/source";
import { buttonVariants } from "@/components/ui/button";
import Image from "next/image";

import profilePic from "../../../../components/testimonials/profiles/Mc0m3zkD_400x400.jpg";

interface Param {
slug: string;
}

export default function Page({
params,
}: {
params: Param;
}): React.ReactElement {
const page = blog.getPage([params.slug]) as BlogPage;

if (!page) notFound();

const MDX = (page.data as any).exports.default;

return (
<main className="px-4">
<div className="mx-auto flex w-full max-w-screen-xl items-center justify-between py-4">
<Link
href="/blog"
className={buttonVariants({ size: "sm", variant: "ghost" })}
>
Back
</Link>
<p className="text-xs text-gray-500">
{(page.data.date as Date).toLocaleString("en-US", {
year: "numeric",
month: "2-digit",
day: "2-digit",
})}
</p>
</div>
<div
className="mx-auto w-full max-w-screen-xl rounded-xl border py-12 md:px-8"
style={{
backgroundColor: "black",
backgroundImage: [
"linear-gradient(140deg, hsla(274,94%,54%,0.3), transparent 50%)",
"linear-gradient(to left top, hsla(260,90%,50%,0.8), transparent 50%)",
"radial-gradient(circle at 100% 100%, hsla(240,100%,82%,1), hsla(240,40%,40%,1) 17%, hsla(240,40%,40%,0.5) 20%, transparent)",
].join(", "),
backgroundBlendMode: "difference, difference, normal",
}}
>
<div className="mx-auto flex w-full max-w-screen-sm flex-col items-center justify-center px-4">
<h1 className="text-center text-4xl font-bold text-white">
{page.data.title}
</h1>
<p className="mt-4 text-balance text-center text-lg text-white/80">
{page.data.description}
</p>
</div>
</div>
<article className="prose lg:prose-lg mx-auto w-full max-w-screen-sm py-8">
<MDX />
</article>
<div className="mx-auto mb-20 flex w-full max-w-screen-sm items-start gap-3">
<Image
src={profilePic}
alt="Simon Farshid"
width={32}
height={32}
className="size-8 rounded-full"
/>
<div className="mt-1.5 flex flex-col">
<span className="text-sm font-medium">Simon Farshid</span>
</div>
</div>
</main>
);
}

export function generateStaticParams(): Param[] {
return blog.getPages().map((page) => ({
slug: page.slugs[0]!,
}));
}
30 changes: 30 additions & 0 deletions apps/docs/app/(home)/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Link from "next/link";
import { blog, BlogPage } from "@/app/source";

export default function Page(): React.ReactElement {
const posts = [...blog.getPages()].sort(
(a: BlogPage, b: BlogPage) =>
new Date(b.data.date ?? b.file.name).getTime() -
new Date(a.data.date ?? a.file.name).getTime(),
);

return (
<main className="mx-auto w-full max-w-screen-sm p-4 py-12">
<h1 className="mb-4 px-4 pb-2 text-4xl font-bold">assistant-ui Blog</h1>
<div className="flex flex-col">
{posts.map((post: BlogPage) => (
<Link
key={post.url}
href={post.url}
className="bg-card hover:bg-accent hover:text-accent-foreground flex flex-col rounded-lg p-4 transition-colors"
>
<p className="font-medium">{post.data.title}</p>
<p className="text-muted-foreground mt-auto pt-2 text-xs">
{new Date(post.data.date ?? post.file.name).toDateString()}
</p>
</Link>
))}
</div>
</main>
);
}
3 changes: 2 additions & 1 deletion apps/docs/app/(home)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function Layout({
function Footer(): React.ReactElement {
return (
<footer className="relative border-t px-8 pb-32 pt-20">
<div className="mx-auto flex max-w-screen-xl flex-col items-start justify-between sm:flex-row">
<div className="mx-auto flex w-full max-w-screen-xl flex-col items-start justify-between sm:flex-row">
<div className="mb-4 mr-4 sm:flex">
<a
className="mr-4 flex items-center gap-2 text-sm font-normal text-black"
Expand All @@ -41,6 +41,7 @@ function Footer(): React.ReactElement {
<p className="text-sm">Product</p>
<FooterLink href="/docs">Documentation</FooterLink>
<FooterLink href="/examples">Examples</FooterLink>
<FooterLink href="/blog">Blog</FooterLink>
</div>
<div className="flex flex-col justify-center gap-4">
<p className="text-sm">Support</p>
Expand Down
63 changes: 9 additions & 54 deletions apps/docs/app/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,14 @@ import { Button, buttonVariants } from "@/components/ui/button";
import { useChat } from "ai/react";
import { AssistantRuntimeProvider, useEdgeRuntime } from "@assistant-ui/react";
import Link from "next/link";
import { FC, useState } from "react";
import { useState } from "react";
import { ChatGPT } from "../../components/chatgpt/ChatGPT";
import { GenUI } from "../../components/genui/GenUI";
import { Artifacts } from "../../components/artifacts/Artifacts";
import { ModalChat } from "../../components/modal/ModalChat";
import {
Testimonial,
TESTIMONIALS,
} from "@/components/testimonials/testimonials";
import Image from "next/image";
import { TESTIMONIALS } from "@/components/testimonials/testimonials";
import { ChatBubbleIcon } from "@radix-ui/react-icons";
import { TestimonialContainer } from "../../components/testimonials/TestimonialContainer";

const supportedModels = [
{
Expand Down Expand Up @@ -51,7 +48,7 @@ export default function HomePage() {
const ChatComponent = selectedModel.component;

return (
<main className="mx-auto p-4 flex flex-col gap-6 self-stretch py-4">
<main className="mx-auto flex flex-col gap-6 self-stretch p-4 py-4">
<div className="mt-12 flex flex-col gap-4 self-center">
<h1 className="text-center text-4xl font-extrabold">
Build in-app AI chatbots
Expand Down Expand Up @@ -116,60 +113,18 @@ export default function HomePage() {
</div>
</div>

<div className="relative mx-auto max-h-[500px] max-w-screen-xl overflow-hidden">
<div className="relative columns-1 gap-4 overflow-hidden sm:columns-2 lg:columns-3 xl:columns-4">
{TESTIMONIALS.map((testimonial, idx) => (
<TestimonialView key={idx} {...testimonial} />
))}
</div>
<div className="relative mx-auto max-h-[500px] w-full max-w-screen-xl overflow-hidden">
<TestimonialContainer
testimonials={TESTIMONIALS}
className="sm:columns-2 lg:columns-3 xl:columns-4"
/>
<div className="from-background via-background pointer-events-none absolute -bottom-8 left-0 z-10 h-[120px] w-full bg-gradient-to-t" />
</div>
</div>
</main>
);
}

const TestimonialView: FC<Testimonial> = (testimonial) => {
return (
<div className="mb-4 break-inside-avoid-column">
<a target="_blank" href={testimonial.url}>
<div className="bg-card hover:bg-border flex flex-col gap-3 rounded-lg border p-6 shadow transition-colors">
<div className="relative flex items-center gap-2">
<Image
alt={"@" + testimonial.username + "'s twitter image"}
loading="lazy"
width="64"
height="64"
className="h-10 w-10 rounded-full border"
src={testimonial.avatar}
/>
<p className="text-sm font-medium">{testimonial.username}</p>
<div className="bg-background absolute -left-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full">
<XLogo />
</div>
</div>
<p className="text-muted-foreground whitespace-pre-line">
{testimonial.message}
</p>
</div>
</a>
</div>
);
};

const XLogo: FC = () => {
return (
<svg
className="h-[12px] w-[12px]"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"></path>
</svg>
);
};

export type AssistantProps = {
chat: ReturnType<typeof useChat>;
};
Expand Down
9 changes: 4 additions & 5 deletions apps/docs/app/docs/layout.config.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type BaseLayoutProps, type DocsLayoutProps } from "fumadocs-ui/layout";
import { pageTree } from "@/app/source";
import { RootToggle } from "fumadocs-ui/components/layout/root-toggle";
import { BookIcon, LayoutTemplateIcon, PhoneCallIcon } from "lucide-react";
import { BookIcon, LayoutTemplateIcon, NewspaperIcon } from "lucide-react";
import { LibraryIcon, type LucideIcon } from "lucide-react";
import icon from "@/public/favicon/icon.svg";
import Image from "next/image";
Expand Down Expand Up @@ -81,10 +81,9 @@ export const baseOptions: BaseLayoutProps = {
icon: <LayoutTemplateIcon />,
},
{
text: "Enterprise Inquiry",
url: "https://cal.com/simon-farshid/assistant-ui",
icon: <PhoneCallIcon />,
external: true,
text: "Blog",
url: "/blog",
icon: <NewspaperIcon />,
},
{
type: "secondary",
Expand Down
19 changes: 18 additions & 1 deletion apps/docs/app/source.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { map } from "@/.map";
import { createMDXSource } from "fumadocs-mdx";
import { loader } from "fumadocs-core/source";
import { loader, Page } from "fumadocs-core/source";
import { z } from "zod";

const frontmatterSchema = z.object({
Expand All @@ -10,6 +10,11 @@ const frontmatterSchema = z.object({
full: z.boolean().optional(),
});

export const blogFrontmatterSchema = frontmatterSchema.extend({
author: z.string(),
date: z.string().date().or(z.date()).optional(),
});

export const { getPages, getPage, pageTree } = loader({
baseUrl: "/",
rootDir: "docs",
Expand All @@ -19,3 +24,15 @@ export const { getPages, getPage, pageTree } = loader({
},
}),
});

export const blog = loader({
baseUrl: "/blog",
rootDir: "blog",
source: createMDXSource(map, {
schema: {
frontmatter: blogFrontmatterSchema,
},
}),
});

export type BlogPage = Page<z.infer<typeof blogFrontmatterSchema>>;
58 changes: 58 additions & 0 deletions apps/docs/components/testimonials/TestimonialContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"use client";

import { Testimonial } from "@/components/testimonials/testimonials";
import { cn } from "@/lib/utils";
import Image from "next/image";
import { FC } from "react";

export const TestimonialContainer: FC<{
testimonials: Testimonial[];
className?: string;
}> = ({ testimonials, className }) => {
return (
<div className={cn("relative columns-1 gap-4 overflow-hidden", className)}>
{testimonials.map((testimonial, idx) => (
<TestimonialView key={idx} {...testimonial} />
))}
</div>
);
};
const TestimonialView: FC<Testimonial> = (testimonial) => {
return (
<div className="mb-4 break-inside-avoid-column">
<a target="_blank" href={testimonial.url}>
<div className="bg-card hover:bg-border flex flex-col gap-3 rounded-lg border p-6 shadow transition-colors">
<div className="relative flex items-center gap-2">
<Image
alt={"@" + testimonial.username + "'s twitter image"}
loading="lazy"
width="64"
height="64"
className="h-10 w-10 rounded-full border"
src={testimonial.avatar}
/>
<p className="text-sm font-medium">{testimonial.username}</p>
<div className="bg-background absolute -left-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full">
<XLogo />
</div>
</div>
<p className="text-muted-foreground whitespace-pre-line">
{testimonial.message}
</p>
</div>
</a>
</div>
);
};
const XLogo: FC = () => {
return (
<svg
className="h-[12px] w-[12px]"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"></path>
</svg>
);
};
Loading

0 comments on commit 15f3272

Please sign in to comment.