Skip to content
47 linesCodeBlameRaw
1import { Hash, MessagesSquare, PlugZap } from "lucide-react";
2import type { ReactNode } from "react";
3import { useParams } from "react-router";
4
5import { CreateChannelButton, NewMessageButton } from "./actions";
6import { Card } from "../ui/card";
7
8/** A calm full-height panel for a conversation that cannot be shown. */
9function Panel({ icon, title, children }: { icon: ReactNode; title: string; children: ReactNode }) {
10 return (
11 <div className="flex h-(--page-h) items-center justify-center px-6">
12 <div className="max-w-sm text-center">
13 <Card className="mx-auto flex size-12 items-center justify-center text-muted">{icon}</Card>
14 <h1 className="mt-4 text-base font-semibold">{title}</h1>
15 <div className="mt-1.5 text-sm text-muted">{children}</div>
16 </div>
17 </div>
18 );
19}
20
21/** Chat did not answer: it may be deploying a moment after the site. */
22export function ChatUnavailable({ title = "Chat" }: { title?: string }) {
23 return (
24 <Panel icon={<PlugZap size={20} />} title={`${title} isn't reachable right now`}>
25 Chat didn't answer. It's usually back within a minute; reload to try again. Nothing you sent is lost.
26 </Panel>
27 );
28}
29
30/** A workspace with no conversations yet. */
31export function NoChannels() {
32 const { owner = "" } = useParams();
33 return (
34 <Panel icon={<MessagesSquare size={20} />} title="Start the first conversation">
35 <p>Channels keep a topic in one place. Direct messages are for one person, a few, or an agent.</p>
36 <div className="mt-5 flex justify-center gap-2">
37 <CreateChannelButton slug={owner.toLowerCase()} variant="button" />
38 <NewMessageButton slug={owner.toLowerCase()} variant="button" />
39 </div>
40 </Panel>
41 );
42}
43
44/** A channel's name with its mark, as the header and the sidebar show it. */
45export function ChannelGlyph({ className }: { className?: string }) {
46 return <Hash size={16} className={className} />;
47}