Path: blob/main/components/dashboard/src/workspaces/BlogBanners.tsx
2500 views
/**1* Copyright (c) 2024 Gitpod GmbH. All rights reserved.2* Licensed under the GNU Affero General Public License (AGPL).3* See License.AGPL.txt in the project root for license information.4*/56import React, { useEffect, useState } from "react";7import { trackEvent } from "../Analytics";8import { useCurrentUser } from "../user-context";9import { getPrimaryEmail } from "@gitpod/public-api-common/lib/user-utils";10import { useToast } from "../components/toasts/Toasts";11import onaWordmark from "../images/ona-wordmark.svg";1213const onaBanner = {14type: "Introducing",15title: "ONA",16subtitle: "Parallel SWE agents in the cloud, sandboxed for high-autonomy.",17ctaText: "Get early access",18learnMoreText: "Learn more",19link: "https://ona.com/stories/gitpod-classic-payg-sunset",20};2122export const OnaBanner: React.FC = () => {23const [onaClicked, setOnaClicked] = useState(false);24const [isDismissed, setIsDismissed] = useState(false);25const user = useCurrentUser();26const { toast } = useToast();2728useEffect(() => {29const storedOnaData = localStorage.getItem("workspaces-ona-banner-data");3031// Check Ona banner state32if (storedOnaData) {33const { clicked, dismissed } = JSON.parse(storedOnaData);34setOnaClicked(clicked || false);35setIsDismissed(dismissed || false);36}3738// Clean up old blog banner data39localStorage.removeItem("blog-banner-data");40}, []);4142const handleOnaBannerClick = () => {43if (!onaClicked) {44// Track "Get early access" click45const userEmail = user ? getPrimaryEmail(user) || "" : "";46trackEvent("waitlist_joined", { email: userEmail, feature: "Ona" });4748setOnaClicked(true);49localStorage.setItem(50"workspaces-ona-banner-data",51JSON.stringify({ clicked: true, dismissed: isDismissed }),52);5354// Also set the global ona-banner-data clicked state (preserve existing dismissed state)55const existingOnaData = localStorage.getItem("ona-banner-data");56const existingDismissed = existingOnaData ? JSON.parse(existingOnaData).dismissed || false : false;57localStorage.setItem("ona-banner-data", JSON.stringify({ clicked: true, dismissed: existingDismissed }));5859// Show success toast60toast(61<div>62<div className="font-medium">You're on the waitlist</div>63<div className="text-sm opacity-80">We'll reach out to you soon.</div>64</div>,65);66} else {67// "Learn more" click - open link68window.open(onaBanner.link, "_blank", "noopener,noreferrer");69}70};7172const handleDismiss = () => {73setIsDismissed(true);74localStorage.setItem("workspaces-ona-banner-data", JSON.stringify({ clicked: onaClicked, dismissed: true }));75};7677// Don't render if dismissed78if (isDismissed) {79return null;80}8182return (83<div className="flex flex-col gap-4">84<div85className="relative rounded-lg overflow-hidden flex flex-col gap-4 text-white max-w-[320px] p-6"86style={{87background:88"linear-gradient(340deg, #1F1329 0%, #333A75 20%, #556CA8 40%, #90A898 60%, #E2B15C 80%, #BEA462 100%)",89}}90>91{/* Close button */}92<button93onClick={handleDismiss}94className="absolute top-4 right-4 text-white/70 hover:text-white w-6 h-6 flex items-center justify-center rounded-full hover:bg-white/10 transition-colors"95aria-label="Dismiss banner"96>97✕98</button>99100{/* Content */}101<div className="flex flex-col gap-4">102<div className="flex items-center gap-2 text-lg font-normal">103{onaBanner.type}104<img src={onaWordmark} alt="ONA" className="w-16" draggable="false" />105</div>106<div className="text-base font-normal opacity-90">{onaBanner.subtitle}</div>107</div>108109{/* CTA Button */}110<button111onClick={handleOnaBannerClick}112className="bg-white/20 backdrop-blur-sm text-white font-medium py-1 px-6 rounded-full hover:bg-white/30 transition-colors border border-white/20 max-w-[180px]"113>114{onaClicked ? onaBanner.learnMoreText : onaBanner.ctaText}115</button>116</div>117</div>118);119};120121// Export with old name for backward compatibility122export const BlogBanners = OnaBanner;123124125