Skip to content
Source
Sign inSign up
Open History to see the last commit

T3ChatUserProfilePage.tsx

127 lines4.5 KB

1import { T3CHAT_PLAN_TUNNEL_LIMITS, type RelayT3ChatLink } from "@t3tools/contracts/relay";
2import { MessageSquareIcon } from "lucide-react";
3import type { ReactNode } from "react";
4​
5import { useManagedTunnelUsage } from "../../cloud/managedRelayState";
6import { T3CHAT_SUBSCRIPTION_URL, TunnelUsageMeter } from "../cloud/TunnelUsage";
7import { Button, InlineButton } from "../ui/button";
8import {
9 ClerkUserProfilePage,
10 ClerkUserProfileRefreshButton,
11 ClerkUserProfileRow,
12} from "./ClerkUserProfilePage";
13import { useT3ChatLink } from "./useT3ChatLink";
14​
15const upgradeLink = (
16 <InlineButton
17 render={<a href={T3CHAT_SUBSCRIPTION_URL} target="_blank" rel="noopener noreferrer" />}
18 >
19 Upgrade on T3 Chat
20 </InlineButton>
21);
22​
23function planLabel(link: RelayT3ChatLink): ReactNode {
24 switch (link.plan) {
25 case "premier":
26 return (
27 <>
28 <span className="block">Premier</span>
29 <span className="block">Up to {T3CHAT_PLAN_TUNNEL_LIMITS.premier} tunnels</span>
30 </>
31 );
32 case "pro":
33 return (
34 <>
35 <span className="block">Pro</span>
36 <span className="block">
37 Up to {T3CHAT_PLAN_TUNNEL_LIMITS.pro} tunnels. {upgradeLink} to Premier for{" "}
38 {T3CHAT_PLAN_TUNNEL_LIMITS.premier}.
39 </span>
40 </>
41 );
42 case "free":
43 return <>{upgradeLink} for more tunnels</>;
44 }
46​
47/** Links a T3 Chat account so paid subscribers get more tunnels. */
48export function T3ChatUserProfilePage() {
49 const { linkState, pending, linkUrl, link: handleLink, unlink: handleUnlink } = useT3ChatLink();
50 const usage = useManagedTunnelUsage();
51​
52 const data = linkState.data;
53 const link = data?.link ?? null;
54​
55 return (
56 <ClerkUserProfilePage
57 title="T3 Chat"
58 description={`Link a paid T3 Chat account for more T3 Connect tunnels: up to ${T3CHAT_PLAN_TUNNEL_LIMITS.pro} with Pro, ${T3CHAT_PLAN_TUNNEL_LIMITS.premier} with Premier.`}
59 action={
60 <ClerkUserProfileRefreshButton
61 disabled={pending !== null}
62 isPending={linkState.isPending}
63 onClick={usage.refresh}
64 />
65 }
66 >
67 {linkState.error ? (
68 <div className="border-t py-3 text-meta" role="alert">
69 <p className="text-label text-destructive-foreground">Could not load T3 Chat link</p>
70 <p className="mt-0.5 text-muted-foreground">{linkState.error}</p>
71 </div>
72 ) : data === null ? null : !data.available ? (
73 <p className="border-t py-4 text-meta text-muted-foreground">
74 This T3 Connect server does not support T3 Chat linking.
75 </p>
76 ) : (
77 <ul className="border-t">
78 <ClerkUserProfileRow icon={<MessageSquareIcon className="size-4" />}>
79 <div className="flex items-start gap-4">
80 <div className="min-w-0 flex-1">
81 <h3 className="truncate text-label leading-4.5 text-foreground">
82 {link ? link.email : "Not linked"}
83 </h3>
84 <p className="mt-1 text-meta leading-4.5 text-muted-foreground">
85 {link ? (
86 planLabel(link)
87 ) : linkUrl ? (
88 <>
89 Confirm on{" "}
90 <InlineButton
91 render={<a href={linkUrl} target="_blank" rel="noopener noreferrer" />}
92 >
93 t3.chat
94 </InlineButton>
95 , then select Refresh.
96 </>
97 ) : (
98 "You confirm the link on t3.chat."
99 )}
100 </p>
101 </div>
102 {link ? (
103 <Button
104 size="sm"
105 variant="destructive-outline"
106 disabled={pending !== null}
107 onClick={() => void handleUnlink()}
108 >
109 {pending === "unlink" ? "Unlinking…" : "Unlink"}
110 </Button>
111 ) : (
112 <Button size="sm" disabled={pending !== null} onClick={() => void handleLink()}>
113 {pending === "link" ? "Opening…" : "Link T3 Chat"}
114 </Button>
115 )}
116 </div>
117 </ClerkUserProfileRow>
118 {usage.used !== null && usage.limit !== null ? (
119 <li className="border-t py-4">
120 <TunnelUsageMeter used={usage.used} limit={usage.limit} />
121 </li>
122 ) : null}
123 </ul>
124 )}
125 </ClerkUserProfilePage>
126 );