Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/dashboard/src/start/OnaBanner.tsx
2500 views
1
/**
2
* Copyright (c) 2024 Gitpod GmbH. All rights reserved.
3
* Licensed under the GNU Affero General Public License (AGPL).
4
* See License.AGPL.txt in the project root for license information.
5
*/
6
7
import React, { useEffect, useState } from "react";
8
import { trackEvent } from "../Analytics";
9
import { useCurrentUser } from "../user-context";
10
import { getPrimaryEmail } from "@gitpod/public-api-common/lib/user-utils";
11
import { useToast } from "../components/toasts/Toasts";
12
import onaWordmark from "../images/ona-wordmark.svg";
13
14
const onaBanner = {
15
type: "Introducing",
16
title: "ONA",
17
subtitle: "Parallel SWE agents in the cloud, sandboxed for high-autonomy.",
18
ctaText: "Get early access",
19
learnMoreText: "Learn more",
20
link: "https://ona.com/stories/gitpod-classic-payg-sunset",
21
};
22
23
interface OnaBannerProps {
24
compact?: boolean;
25
}
26
27
export const OnaBanner: React.FC<OnaBannerProps> = ({ compact = false }) => {
28
const [onaClicked, setOnaClicked] = useState(false);
29
const [isDismissed, setIsDismissed] = useState(false);
30
const user = useCurrentUser();
31
const { toast } = useToast();
32
33
useEffect(() => {
34
const storedOnaData = localStorage.getItem("ona-banner-data");
35
36
if (storedOnaData) {
37
const { clicked, dismissed } = JSON.parse(storedOnaData);
38
setOnaClicked(clicked || false);
39
setIsDismissed(dismissed || false);
40
}
41
}, []);
42
43
const handleOnaBannerClick = () => {
44
// Track "Get early access" click
45
const userEmail = user ? getPrimaryEmail(user) || "" : "";
46
trackEvent("waitlist_joined", { email: userEmail, feature: "Ona" });
47
48
setOnaClicked(true);
49
localStorage.setItem("ona-banner-data", JSON.stringify({ clicked: true, dismissed: isDismissed }));
50
51
// Show success toast
52
toast(
53
<div>
54
<div className="font-medium">You're on the waitlist</div>
55
<div className="text-sm opacity-80">We'll reach out to you soon.</div>
56
</div>,
57
);
58
};
59
60
const handleDismiss = () => {
61
setIsDismissed(true);
62
localStorage.setItem("ona-banner-data", JSON.stringify({ clicked: onaClicked, dismissed: true }));
63
};
64
65
// Don't render if dismissed
66
if (isDismissed) {
67
return null;
68
}
69
70
if (compact) {
71
return (
72
<div
73
className="relative rounded-lg hidden lg:flex flex-col gap-3 text-white max-w-80 p-4 shadow-lg"
74
style={{
75
background:
76
"linear-gradient(340deg, #1F1329 0%, #333A75 20%, #556CA8 40%, #90A898 60%, #E2B15C 80%, #BEA462 100%)",
77
}}
78
>
79
{/* Close button */}
80
<button
81
onClick={handleDismiss}
82
className="absolute top-3 right-3 text-white/70 hover:text-white w-6 h-6 flex items-center justify-center rounded-full hover:bg-white/10 transition-colors"
83
aria-label="Dismiss banner"
84
>
85
86
</button>
87
88
{/* Compact layout */}
89
<div className="flex items-center gap-2 text-sm font-normal">
90
{onaBanner.type}
91
<img src={onaWordmark} alt="ONA" className="w-12" draggable="false" />
92
</div>
93
94
<p className="text-white text-base font-semibold leading-tight text-left">
95
Parallel SWE agents in the cloud, sandboxed for high-autonomy.
96
</p>
97
98
<a
99
href={onaBanner.link}
100
target="_blank"
101
rel="noopener noreferrer"
102
className="bg-white/20 backdrop-blur-sm text-white font-medium py-1 px-3 rounded-full hover:bg-white/30 transition-colors border border-white/20 inline-flex items-center gap-2 text-sm w-fit"
103
>
104
{onaBanner.learnMoreText}
105
<span className="font-bold"></span>
106
</a>
107
</div>
108
);
109
}
110
111
return (
112
<div
113
className="relative rounded-lg flex flex-col lg:flex-row gap-6 text-white max-w-5xl mx-auto p-4 lg:p-4 mt-6 mb-2"
114
style={{
115
background:
116
"linear-gradient(340deg, #1F1329 0%, #333A75 20%, #556CA8 40%, #90A898 60%, #E2B15C 80%, #BEA462 100%)",
117
}}
118
>
119
{/* Close button */}
120
<button
121
onClick={handleDismiss}
122
className="absolute top-2 right-2 text-white/70 hover:text-white w-6 h-6 flex items-center justify-center rounded-full hover:bg-white/10 transition-colors z-10"
123
aria-label="Dismiss banner"
124
>
125
126
</button>
127
128
{/* Left section - ONA branding and image */}
129
<div className="flex-1 max-w-full lg:max-w-[330px] order-1">
130
<div className="relative bg-white/10 backdrop-blur-sm rounded-lg h-full flex items-center justify-center px-4 lg:px-6 py-4 lg:py-6">
131
{/* ONA Logo prominently displayed */}
132
<div className="flex justify-center">
133
<img src={onaWordmark} alt="ONA" className="w-28 lg:w-36" draggable="false" />
134
</div>
135
</div>
136
</div>
137
138
{/* Right section - Text content and CTA */}
139
<div className="flex-1 max-w-[500px] lg:max-w-[550px] order-2 text-left">
140
<div className="max-lg:mt-2 max-w-sm space-y-3 lg:space-y-4">
141
{/* Main title */}
142
<h2 className="text-white text-lg sm:text-lg font-bold">
143
The privacy-first software engineering agent
144
</h2>
145
146
{/* CTA Button */}
147
<div className="mt-2 mb-4">
148
{!onaClicked ? (
149
<button
150
onClick={handleOnaBannerClick}
151
className="inline-flex items-center justify-center gap-2 bg-[#fdfdfd] text-[#12100C] px-3 py-1.5 rounded-[8px] text-xs transition-all duration-200 shadow-sm hover:shadow-md focus:shadow-md hover:bg-[#F5F4F3] font-medium"
152
>
153
<span>{onaBanner.ctaText}</span>
154
<span className="font-bold"></span>
155
</button>
156
) : (
157
<a
158
href={onaBanner.link}
159
target="_blank"
160
rel="noopener noreferrer"
161
className="inline-flex items-center justify-center gap-2 bg-[#fdfdfd] text-[#12100C] px-3 py-1.5 rounded-[8px] text-xs transition-all duration-200 shadow-sm hover:shadow-md focus:shadow-md hover:bg-[#F5F4F3] font-medium"
162
>
163
<span>{onaBanner.learnMoreText}</span>
164
<span className="font-bold"></span>
165
</a>
166
)}
167
</div>
168
</div>
169
</div>
170
</div>
171
);
172
};
173
174