mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 04:15:30 +00:00
various changes to add terms and security info (#225)
* add terms and security to footer * add security card * add demo card
This commit is contained in:
parent
eab0007602
commit
a212e857c8
11 changed files with 220 additions and 71 deletions
|
|
@ -17,6 +17,7 @@ import { CodeHostIconButton } from "../../components/codeHostIconButton";
|
|||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
import { useSession } from "next-auth/react";
|
||||
import posthog from "posthog-js";
|
||||
import SecurityCard from "@/app/components/securityCard";
|
||||
|
||||
interface ConnectCodeHostProps {
|
||||
nextStep: OnboardingSteps;
|
||||
|
|
@ -48,7 +49,10 @@ export const ConnectCodeHost = ({ nextStep }: ConnectCodeHostProps) => {
|
|||
|
||||
if (!selectedCodeHost) {
|
||||
return (
|
||||
<CodeHostSelection onSelect={setSelectedCodeHost} />
|
||||
<>
|
||||
<CodeHostSelection onSelect={setSelectedCodeHost} />
|
||||
<SecurityCard />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
"use client"
|
||||
|
||||
import { ExternalLink } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent"
|
||||
|
||||
export default function DemoCard() {
|
||||
const captureEvent = useCaptureEvent();
|
||||
|
||||
return (
|
||||
<Card className="mb-6 w-full border bg-card text-card-foreground">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<h3 className="text-sm font-medium">New to Sourcebot?</h3>
|
||||
<p className="text-xs text-muted-foreground">Try our public demo before creating an account</p>
|
||||
</div>
|
||||
|
||||
<Button asChild variant="outline" size="sm" className="h-8 text-xs">
|
||||
<Link
|
||||
href="https://sourcebot.dev/search"
|
||||
target="_blank"
|
||||
className="flex items-center gap-1.5"
|
||||
onClick={() => captureEvent('wa_demo_card_click', {})}
|
||||
>
|
||||
Try demo
|
||||
<ExternalLink className="h-3.5 w-3.5" />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
|
@ -7,6 +7,8 @@ import { InviteTeam } from "./components/inviteTeam";
|
|||
import { CompleteOnboarding } from "./components/completeOnboarding";
|
||||
import { Checkout } from "./components/checkout";
|
||||
import { LogoutEscapeHatch } from "@/app/components/logoutEscapeHatch";
|
||||
import SecurityCard from "@/app/components/securityCard";
|
||||
|
||||
interface OnboardProps {
|
||||
params: {
|
||||
domain: string
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { UpgradeToast } from "./components/upgradeToast";
|
|||
import Link from "next/link";
|
||||
import { getOrgFromDomain } from "@/data/org";
|
||||
import { PageNotFound } from "./components/pageNotFound";
|
||||
import { Footer } from "./components/footer";
|
||||
import { Footer } from "@/app/components/footer";
|
||||
import { SourcebotLogo } from "../components/sourcebotLogo";
|
||||
import { RepositorySnapshot } from "./components/repositorySnapshot";
|
||||
import { KeyboardShortcutHint } from "./components/keyboardShortcutHint";
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export default async function ReposPage({ params: { domain } }: { params: { doma
|
|||
<Header>
|
||||
<h1 className="text-3xl">Repositories</h1>
|
||||
</Header>
|
||||
<div className="h-screen flex flex-col items-center">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="w-full">
|
||||
<RepositoryTable />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ export function Footer() {
|
|||
<footer className="w-full mt-auto py-4 flex flex-row justify-center items-center gap-4">
|
||||
<Link href="https://sourcebot.dev" className="text-gray-400 text-sm hover:underline">About</Link>
|
||||
<Separator orientation="vertical" className="h-4" />
|
||||
<Link href="https://github.com/sourcebot-dev/sourcebot/issues/new" className="text-gray-400 text-sm hover:underline">Support</Link>
|
||||
<Link href="https://sourcebot.dev/terms" className="text-gray-400 text-sm hover:underline">Terms</Link>
|
||||
<Separator orientation="vertical" className="h-4" />
|
||||
<Link href="https://sourcebot.dev/security" className="text-gray-400 text-sm hover:underline">Security</Link>
|
||||
<Separator orientation="vertical" className="h-4" />
|
||||
<Link href="mailto:team@sourcebot.dev" className="text-gray-400 text-sm hover:underline">Contact Us</Link>
|
||||
</footer>
|
||||
86
packages/web/src/app/components/securityCard.tsx
Normal file
86
packages/web/src/app/components/securityCard.tsx
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import { Shield, Lock, CheckCircle, ExternalLink, Mail } from "lucide-react"
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent"
|
||||
|
||||
export default function SecurityCard() {
|
||||
const captureEvent = useCaptureEvent();
|
||||
|
||||
return (
|
||||
<div className="mt-12 max-w-md mx-auto text-center">
|
||||
<div className="bg-backgroundSecondary border border-[#1E2A3A] rounded-lg p-6 shadow-lg">
|
||||
<div className="flex justify-center mb-4">
|
||||
<Shield className="h-10 w-10 text-[#9D5CFF]" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold mb-3">Multi-Layered Security</h3>
|
||||
<p className="text-[#A1A1AA] mb-6">
|
||||
We take the security and privacy of your data seriously. All code and secret tokens you provide are protected
|
||||
using multiple layers of security.
|
||||
</p>
|
||||
|
||||
<div className="space-y-4 mb-5">
|
||||
<div className="flex items-start">
|
||||
<CheckCircle className="h-5 w-5 text-[#9D5CFF] mr-3 mt-0.5 flex-shrink-0" />
|
||||
<span className="text-sm text-foregroundSecondary text-left">
|
||||
All data is stored on Google Cloud Platform in the United States (us-west-1)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start">
|
||||
<CheckCircle className="h-5 w-5 text-[#9D5CFF] mr-3 mt-0.5 flex-shrink-0" />
|
||||
<span className="text-sm text-foregroundSecondary text-left">
|
||||
All data is encrypted in transit using TLS 1.2+, and at rest using AES-256
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start">
|
||||
<CheckCircle className="h-5 w-5 text-[#9D5CFF] mr-3 mt-0.5 flex-shrink-0" />
|
||||
<div className="text-sm text-foregroundSecondary text-left">
|
||||
<div className="flex items-center">
|
||||
<span>Sourcebot is open-source and trusted by thousands of developers</span>
|
||||
<Link
|
||||
href="https://github.com/sourcebot-dev/sourcebot"
|
||||
target="_blank"
|
||||
className="inline-flex items-center ml-2 text-[#9D5CFF] hover:text-[#B47EFF] transition-colors"
|
||||
>
|
||||
<svg
|
||||
className="h-4 w-4 mr-0.5"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
|
||||
</svg>
|
||||
<ExternalLink className="h-3 w-3" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-sm text-[#A1A1AA] mb-5">
|
||||
Have questions?
|
||||
<Link
|
||||
href="mailto:team@sourcebot.dev"
|
||||
className="inline-flex items-center ml-2 text-[#9D5CFF] hover:text-[#B47EFF] transition-colors"
|
||||
>
|
||||
<Mail className="h-3.5 w-3.5 mr-1" />
|
||||
<span>Get in touch</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="https://sourcebot.dev/security"
|
||||
target="_blank"
|
||||
className="inline-flex items-center justify-center px-5 py-2.5 rounded-md bg-backgroundSecondary border border-[#1E2A3A] text-foreground hover:bg-backgroundSecondary/80 transition-colors"
|
||||
onClick={() => captureEvent('wa_security_page_click', {})}
|
||||
>
|
||||
<Lock className="h-4 w-4 mr-2" />
|
||||
<span>Learn about our security measures</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -12,7 +12,7 @@ import { CredentialsForm } from "./credentialsForm";
|
|||
import { SourcebotLogo } from "@/app/components/sourcebotLogo";
|
||||
import { TextSeparator } from "@/app/components/textSeparator";
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
|
||||
import DemoCard from "@/app/[domain]/onboard/components/demoCard";
|
||||
interface LoginFormProps {
|
||||
callbackUrl?: string;
|
||||
error?: string;
|
||||
|
|
@ -52,6 +52,9 @@ export const LoginForm = ({ callbackUrl, error, enabledMethods }: LoginFormProps
|
|||
/>
|
||||
<h2 className="text-lg font-bold text-center">Sign in to your account</h2>
|
||||
</div>
|
||||
<div className="w-full sm:w-[500px] max-w-[500px]">
|
||||
<DemoCard />
|
||||
</div>
|
||||
<Card className="flex flex-col items-center border p-6 sm:p-12 rounded-lg gap-4 sm:gap-6 w-full sm:w-[500px] max-w-[500px] bg-background">
|
||||
{error && (
|
||||
<div className="text-sm text-destructive text-center text-wrap border p-2 rounded-md border-destructive">
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { auth } from "@/auth";
|
|||
import { LoginForm } from "./components/loginForm";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getProviders } from "@/auth";
|
||||
import { Footer } from "@/app/components/footer";
|
||||
|
||||
interface LoginProps {
|
||||
searchParams: {
|
||||
callbackUrl?: string;
|
||||
|
|
@ -18,26 +20,29 @@ export default async function Login({ searchParams }: LoginProps) {
|
|||
const providers = getProviders();
|
||||
const providerMap = providers
|
||||
.map((provider) => {
|
||||
if (typeof provider === "function") {
|
||||
const providerData = provider()
|
||||
return { id: providerData.id, name: providerData.name }
|
||||
} else {
|
||||
return { id: provider.id, name: provider.name }
|
||||
}
|
||||
});
|
||||
if (typeof provider === "function") {
|
||||
const providerData = provider()
|
||||
return { id: providerData.id, name: providerData.name }
|
||||
} else {
|
||||
return { id: provider.id, name: provider.name }
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center p-4 sm:p-12 min-h-screen w-full bg-backgroundSecondary">
|
||||
<LoginForm
|
||||
callbackUrl={searchParams.callbackUrl}
|
||||
error={searchParams.error}
|
||||
enabledMethods={{
|
||||
github: providerMap.some(provider => provider.id === "github"),
|
||||
google: providerMap.some(provider => provider.id === "google"),
|
||||
magicLink: providerMap.some(provider => provider.id === "nodemailer"),
|
||||
credentials: providerMap.some(provider => provider.id === "credentials"),
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<div className="flex-1 flex flex-col items-center p-4 sm:p-12 w-full bg-backgroundSecondary">
|
||||
<LoginForm
|
||||
callbackUrl={searchParams.callbackUrl}
|
||||
error={searchParams.error}
|
||||
enabledMethods={{
|
||||
github: providerMap.some(provider => provider.id === "github"),
|
||||
google: providerMap.some(provider => provider.id === "google"),
|
||||
magicLink: providerMap.some(provider => provider.id === "nodemailer"),
|
||||
credentials: providerMap.some(provider => provider.id === "credentials"),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { useCallback, useState, Suspense } from "react"
|
|||
import VerificationFailed from "./verificationFailed"
|
||||
import { SourcebotLogo } from "@/app/components/sourcebotLogo"
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent"
|
||||
import { Footer } from "@/app/components/footer"
|
||||
|
||||
function VerifyPageContent() {
|
||||
const [value, setValue] = useState("")
|
||||
|
|
@ -41,58 +42,61 @@ function VerifyPageContent() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center p-4 sm:p-12 min-h-screen w-full bg-backgroundSecondary">
|
||||
<div className="w-full max-w-md">
|
||||
<div className="flex justify-center mb-6">
|
||||
<SourcebotLogo className="h-16" size="large" />
|
||||
</div>
|
||||
<Card className="w-full shadow-lg border-muted/40">
|
||||
<CardHeader className="space-y-1">
|
||||
<CardTitle className="text-2xl font-bold text-center">Verify your email</CardTitle>
|
||||
<CardDescription className="text-center">
|
||||
Enter the 6-digit code we sent to <span className="font-semibold text-primary">{email}</span>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<div className="flex-1 flex flex-col items-center p-4 sm:p-12 w-full bg-backgroundSecondary">
|
||||
<div className="w-full max-w-md">
|
||||
<div className="flex justify-center mb-6">
|
||||
<SourcebotLogo className="h-16" size="large" />
|
||||
</div>
|
||||
<Card className="w-full shadow-lg border-muted/40">
|
||||
<CardHeader className="space-y-1">
|
||||
<CardTitle className="text-2xl font-bold text-center">Verify your email</CardTitle>
|
||||
<CardDescription className="text-center">
|
||||
Enter the 6-digit code we sent to <span className="font-semibold text-primary">{email}</span>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
<form onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
handleSubmit()
|
||||
}} className="space-y-6">
|
||||
<div className="flex justify-center py-4">
|
||||
<InputOTP maxLength={6} value={value} onChange={setValue} onKeyDown={handleKeyDown} className="gap-2">
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} className="rounded-md border-input" />
|
||||
<InputOTPSlot index={1} className="rounded-md border-input" />
|
||||
<InputOTPSlot index={2} className="rounded-md border-input" />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} className="rounded-md border-input" />
|
||||
<InputOTPSlot index={4} className="rounded-md border-input" />
|
||||
<InputOTPSlot index={5} className="rounded-md border-input" />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<form onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
handleSubmit()
|
||||
}} className="space-y-6">
|
||||
<div className="flex justify-center py-4">
|
||||
<InputOTP maxLength={6} value={value} onChange={setValue} onKeyDown={handleKeyDown} className="gap-2">
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} className="rounded-md border-input" />
|
||||
<InputOTPSlot index={1} className="rounded-md border-input" />
|
||||
<InputOTPSlot index={2} className="rounded-md border-input" />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} className="rounded-md border-input" />
|
||||
<InputOTPSlot index={4} className="rounded-md border-input" />
|
||||
<InputOTPSlot index={5} className="rounded-md border-input" />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
|
||||
<CardFooter className="flex flex-col space-y-4 pt-0">
|
||||
<Button variant="ghost" className="w-full text-sm" size="sm" onClick={() => window.history.back()}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back to login
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<div className="mt-8 text-center text-sm text-muted-foreground">
|
||||
<p>
|
||||
Having trouble?{" "}
|
||||
<a href="mailto:team@sourcebot.dev" className="text-primary hover:underline">
|
||||
Contact support
|
||||
</a>
|
||||
</p>
|
||||
<CardFooter className="flex flex-col space-y-4 pt-0">
|
||||
<Button variant="ghost" className="w-full text-sm" size="sm" onClick={() => window.history.back()}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back to login
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<div className="mt-8 text-center text-sm text-muted-foreground">
|
||||
<p>
|
||||
Having trouble?{" "}
|
||||
<a href="mailto:team@sourcebot.dev" className="text-primary hover:underline">
|
||||
Contact support
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,6 +238,10 @@ export type PosthogEventMap = {
|
|||
wa_onboard_gitlab_selected: {},
|
||||
wa_onboard_gitea_selected: {},
|
||||
wa_onboard_gerrit_selected: {},
|
||||
//////////////////////////////////////////////////////////////////
|
||||
wa_security_page_click: {},
|
||||
//////////////////////////////////////////////////////////////////
|
||||
wa_demo_card_click: {},
|
||||
}
|
||||
|
||||
export type PosthogEvent = keyof PosthogEventMap;
|
||||
Loading…
Reference in a new issue