add empty screen and external links

This commit is contained in:
Shu Ding 2023-05-23 16:49:07 +02:00
parent f14e73ac8f
commit 398b671300
No known key found for this signature in database
GPG key ID: B84C6E25F5FEA16B
5 changed files with 136 additions and 10 deletions

View file

@ -5,6 +5,7 @@ import { type Message } from "@prisma/client";
import { ChatMessage } from "./chat-message"; import { ChatMessage } from "./chat-message";
import { NextChatLogo } from "@/components/ui/nextchat-logo"; import { NextChatLogo } from "@/components/ui/nextchat-logo";
import { Plus } from "lucide-react"; import { Plus } from "lucide-react";
import { EmptyScreen } from "./empty";
export interface ChatList { export interface ChatList {
messages: Message[]; messages: Message[];
@ -36,7 +37,9 @@ export function ChatList({ messages }: ChatList) {
/> />
))} ))}
</div> </div>
) : null} ) : (
<EmptyScreen />
)}
</div> </div>
</div> </div>
); );

56
app/empty.tsx Normal file
View file

@ -0,0 +1,56 @@
import { cn } from "@/lib/utils";
import { ExternalLink } from "./external-link";
import { fontMessage } from "@/lib/fonts";
function ExampleBubble({ children }: { children?: React.ReactNode }) {
return (
<div
className={cn(
"flex-1 flex items-start justify-between p-4 rounded-lg relative bg-zinc-100 text-sm font-medium group hover:bg-zinc-200 hover transition-colors cursor-pointer select-none",
fontMessage.className
)}
style={{}}
>
<div
className="bg-zinc-100 group-hover:bg-zinc-200 w-9 h-6 absolute left-0 top-full -mt-[1px] scale-75 origin-top-left transition-colors"
style={{
clipPath:
'path("M31.1838 0C24.9593 10.7604 13.3251 18 0 18C9.94113 18 18 9.94113 18 0H31.1838Z")',
}}
></div>
<p>{children}</p>
</div>
);
}
export function EmptyScreen() {
return (
<div
className={cn(
"w-full h-full flex items-center justify-center pt-4 pb-8 pr-0 lg:pr-[260px] lg:pl-16"
)}
>
<div className="p-8 rounded-lg flex flex-col items-center justify-center gap-8 max-w-2xl">
<div className="flex items-center justify-center gap-6">
<div className="text-zinc-500 font-medium">
<p>Welcome to Next.js Chatbot!</p>
<p className="mt-2">
This is an open source AI chatbot app built with{" "}
<ExternalLink href="https://nextjs.org">Next.js</ExternalLink> and{" "}
<ExternalLink href="https://vercel.com/storage/postgres">
Vercel Postgres
</ExternalLink>
. You can start a conversation here or try the following examples:
</p>
</div>
</div>
<div className="grid gap-x-4 gap-y-6 grid-cols-2 w-full">
<ExampleBubble>Explain technical concepts</ExampleBubble>
<ExampleBubble>Summarize article</ExampleBubble>
<ExampleBubble>Get assistance</ExampleBubble>
<ExampleBubble>Draft an email</ExampleBubble>
</div>
</div>
</div>
);
}

29
app/external-link.tsx Normal file
View file

@ -0,0 +1,29 @@
export function ExternalLink({
href,
children,
}: {
href: string;
children: React.ReactNode;
}) {
return (
<a
href={href}
target="_blank"
className="inline-flex gap-1 hover:underline flex-1 justify-center leading-4"
>
<span>{children}</span>
<svg
aria-hidden="true"
height="7"
viewBox="0 0 6 6"
width="7"
className="opacity-70"
>
<path
d="M1.25215 5.54731L0.622742 4.9179L3.78169 1.75597H1.3834L1.38936 0.890915H5.27615V4.78069H4.40513L4.41109 2.38538L1.25215 5.54731Z"
fill="currentColor"
></path>
</svg>
</a>
);
}

View file

@ -8,6 +8,7 @@ import { Plus } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { unstable_cache } from "next/cache"; import { unstable_cache } from "next/cache";
import { SidebarItem } from "./sidebar-item"; import { SidebarItem } from "./sidebar-item";
import { ExternalLink } from "./external-link";
export interface SidebarProps { export interface SidebarProps {
session?: Session; session?: Session;
@ -48,6 +49,33 @@ export function Sidebar({ session, newChat }: SidebarProps) {
<SidebarList session={session} /> <SidebarList session={session} />
</div> </div>
</div> </div>
<div>
<div className="flex gap-4 items-center justify-center text-sm font-medium leading-4 whitespace-nowrap">
<a
href="https://github.com/vercel/nextjs-ai-chatbot/"
target="_blank"
className="inline-flex gap-2 px-3 py-2 items-center border border-zinc-300 rounded-md hover:border-zinc-600 transition-colors"
>
<svg
aria-label="Vercel logomark"
height="13"
role="img"
className="w-auto overflow-hidden"
viewBox="0 0 74 64"
>
<path
d="M37.5896 0.25L74.5396 64.25H0.639648L37.5896 0.25Z"
fill="var(--geist-foreground)"
></path>
</svg>
<span>Deploy</span>
</a>
<ExternalLink href="https://github.com/vercel/nextjs-ai-chatbot/">
View on GitHub
</ExternalLink>
</div>
</div>
</aside> </aside>
</div> </div>
</div> </div>

View file

@ -1,6 +1,8 @@
import { useId } from "react"; import { useId } from "react";
export function NextChatLogo(props: JSX.IntrinsicElements["svg"]) { export function NextChatLogo(
props: JSX.IntrinsicElements["svg"] & { inverted?: boolean }
) {
const id = useId(); const id = useId();
return ( return (
<svg <svg
@ -20,8 +22,12 @@ export function NextChatLogo(props: JSX.IntrinsicElements["svg"]) {
y2="14.2667" y2="14.2667"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
> >
<stop stopColor="black" /> <stop stopColor={props.inverted ? "white" : "black"} />
<stop offset={1} stopColor="black" stopOpacity={0} /> <stop
offset={1}
stopColor={props.inverted ? "white" : "black"}
stopOpacity={0}
/>
</linearGradient> </linearGradient>
<linearGradient <linearGradient
id={`gradient-${id}-2`} id={`gradient-${id}-2`}
@ -31,14 +37,18 @@ export function NextChatLogo(props: JSX.IntrinsicElements["svg"]) {
y2="9.50002" y2="9.50002"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
> >
<stop stopColor="black" /> <stop stopColor={props.inverted ? "white" : "black"} />
<stop offset={1} stopColor="black" stopOpacity={0} /> <stop
offset={1}
stopColor={props.inverted ? "white" : "black"}
stopOpacity={0}
/>
</linearGradient> </linearGradient>
</defs> </defs>
<path <path
d="M1 16L2.58314 11.2506C1.83084 9.74642 1.63835 8.02363 2.04013 6.39052C2.4419 4.75741 3.41171 3.32057 4.776 2.33712C6.1403 1.35367 7.81003 0.887808 9.4864 1.02289C11.1628 1.15798 12.7364 1.8852 13.9256 3.07442C15.1148 4.26363 15.842 5.83723 15.9771 7.5136C16.1122 9.18997 15.6463 10.8597 14.6629 12.224C13.6794 13.5883 12.2426 14.5581 10.6095 14.9599C8.97637 15.3616 7.25358 15.1692 5.74942 14.4169L1 16Z" d="M1 16L2.58314 11.2506C1.83084 9.74642 1.63835 8.02363 2.04013 6.39052C2.4419 4.75741 3.41171 3.32057 4.776 2.33712C6.1403 1.35367 7.81003 0.887808 9.4864 1.02289C11.1628 1.15798 12.7364 1.8852 13.9256 3.07442C15.1148 4.26363 15.842 5.83723 15.9771 7.5136C16.1122 9.18997 15.6463 10.8597 14.6629 12.224C13.6794 13.5883 12.2426 14.5581 10.6095 14.9599C8.97637 15.3616 7.25358 15.1692 5.74942 14.4169L1 16Z"
fill="white" fill={props.inverted ? "black" : "white"}
stroke="white" stroke={props.inverted ? "black" : "white"}
strokeWidth={2} strokeWidth={2}
strokeLinecap="round" strokeLinecap="round"
strokeLinejoin="round" strokeLinejoin="round"
@ -52,10 +62,10 @@ export function NextChatLogo(props: JSX.IntrinsicElements["svg"]) {
width={16} width={16}
height={16} height={16}
> >
<circle cx={9} cy={8} r={8} fill="white" /> <circle cx={9} cy={8} r={8} fill={props.inverted ? "black" : "white"} />
</mask> </mask>
<g mask="url(#mask0_91_2047)"> <g mask="url(#mask0_91_2047)">
<circle cx={9} cy={8} r={8} fill="white" /> <circle cx={9} cy={8} r={8} fill={props.inverted ? "black" : "white"} />
<path <path
d="M14.2896 14.0018L7.146 4.8H5.80005V11.1973H6.87681V6.16743L13.4444 14.6529C13.7407 14.4545 14.0231 14.2369 14.2896 14.0018Z" d="M14.2896 14.0018L7.146 4.8H5.80005V11.1973H6.87681V6.16743L13.4444 14.6529C13.7407 14.4545 14.0231 14.2369 14.2896 14.0018Z"
fill={`url(#gradient-${id}-1)`} fill={`url(#gradient-${id}-1)`}