Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -48,7 +49,10 @@ export const ConnectCodeHost = ({ nextStep }: ConnectCodeHostProps) => {

if (!selectedCodeHost) {
return (
<CodeHostSelection onSelect={setSelectedCodeHost} />
<>
<CodeHostSelection onSelect={setSelectedCodeHost} />
<SecurityCard />
</>
)
}

Expand Down
39 changes: 39 additions & 0 deletions packages/web/src/app/[domain]/onboard/components/demoCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}
2 changes: 2 additions & 0 deletions packages/web/src/app/[domain]/onboard/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/repos/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
86 changes: 86 additions & 0 deletions packages/web/src/app/components/securityCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}

5 changes: 4 additions & 1 deletion packages/web/src/app/login/components/loginForm.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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">
Expand Down
41 changes: 23 additions & 18 deletions packages/web/src/app/login/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand All@@ -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>
)
}
100 changes: 52 additions & 48 deletions packages/web/src/app/login/verify/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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("")
Expand DownExpand Up@@ -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>
)
}
Expand Down
4 changes: 4 additions & 0 deletions packages/web/src/lib/posthogEvents.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -48,7 +49,10 @@ export const ConnectCodeHost = ({ nextStep }: ConnectCodeHostProps) => {

if (!selectedCodeHost) {
return (
<CodeHostSelection onSelect={setSelectedCodeHost} />
<>
<CodeHostSelection onSelect={setSelectedCodeHost} />
<SecurityCard />
</>
)
}

Expand Down
39 changes: 39 additions & 0 deletions packages/web/src/app/[domain]/onboard/components/demoCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}
2 changes: 2 additions & 0 deletions packages/web/src/app/[domain]/onboard/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/repos/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
86 changes: 86 additions & 0 deletions packages/web/src/app/components/securityCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}

5 changes: 4 additions & 1 deletion packages/web/src/app/login/components/loginForm.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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">
Expand Down
41 changes: 23 additions & 18 deletions packages/web/src/app/login/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand All@@ -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>
)
}
100 changes: 52 additions & 48 deletions packages/web/src/app/login/verify/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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("")
Expand DownExpand Up@@ -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>
)
}
Expand Down
4 changes: 4 additions & 0 deletions packages/web/src/lib/posthogEvents.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -48,7 +49,10 @@ export const ConnectCodeHost = ({ nextStep }: ConnectCodeHostProps) => {

if (!selectedCodeHost) {
return (
<CodeHostSelection onSelect={setSelectedCodeHost} />
<>
<CodeHostSelection onSelect={setSelectedCodeHost} />
<SecurityCard />
</>
)
}

Expand Down
39 changes: 39 additions & 0 deletions packages/web/src/app/[domain]/onboard/components/demoCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}
2 changes: 2 additions & 0 deletions packages/web/src/app/[domain]/onboard/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/repos/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
86 changes: 86 additions & 0 deletions packages/web/src/app/components/securityCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}

5 changes: 4 additions & 1 deletion packages/web/src/app/login/components/loginForm.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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">
Expand Down
41 changes: 23 additions & 18 deletions packages/web/src/app/login/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand All@@ -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>
)
}
100 changes: 52 additions & 48 deletions packages/web/src/app/login/verify/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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("")
Expand DownExpand Up@@ -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>
)
}
Expand Down
4 changes: 4 additions & 0 deletions packages/web/src/lib/posthogEvents.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -48,7 +49,10 @@ export const ConnectCodeHost = ({ nextStep }: ConnectCodeHostProps) => {

if (!selectedCodeHost) {
return (
<CodeHostSelection onSelect={setSelectedCodeHost} />
<>
<CodeHostSelection onSelect={setSelectedCodeHost} />
<SecurityCard />
</>
)
}

Expand Down
39 changes: 39 additions & 0 deletions packages/web/src/app/[domain]/onboard/components/demoCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}
2 changes: 2 additions & 0 deletions packages/web/src/app/[domain]/onboard/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/repos/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
86 changes: 86 additions & 0 deletions packages/web/src/app/components/securityCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}

5 changes: 4 additions & 1 deletion packages/web/src/app/login/components/loginForm.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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">
Expand Down
41 changes: 23 additions & 18 deletions packages/web/src/app/login/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand All@@ -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>
)
}
100 changes: 52 additions & 48 deletions packages/web/src/app/login/verify/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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("")
Expand DownExpand Up@@ -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>
)
}
Expand Down
4 changes: 4 additions & 0 deletions packages/web/src/lib/posthogEvents.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -48,7 +49,10 @@ export const ConnectCodeHost = ({ nextStep }: ConnectCodeHostProps) => {

if (!selectedCodeHost) {
return (
<CodeHostSelection onSelect={setSelectedCodeHost} />
<>
<CodeHostSelection onSelect={setSelectedCodeHost} />
<SecurityCard />
</>
)
}

Expand Down
39 changes: 39 additions & 0 deletions packages/web/src/app/[domain]/onboard/components/demoCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}
2 changes: 2 additions & 0 deletions packages/web/src/app/[domain]/onboard/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/repos/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
86 changes: 86 additions & 0 deletions packages/web/src/app/components/securityCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}

5 changes: 4 additions & 1 deletion packages/web/src/app/login/components/loginForm.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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">
Expand Down
41 changes: 23 additions & 18 deletions packages/web/src/app/login/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand All@@ -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>
)
}
100 changes: 52 additions & 48 deletions packages/web/src/app/login/verify/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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("")
Expand DownExpand Up@@ -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>
)
}
Expand Down
4 changes: 4 additions & 0 deletions packages/web/src/lib/posthogEvents.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -48,7 +49,10 @@ export const ConnectCodeHost = ({ nextStep }: ConnectCodeHostProps) => {

if (!selectedCodeHost) {
return (
<CodeHostSelection onSelect={setSelectedCodeHost} />
<>
<CodeHostSelection onSelect={setSelectedCodeHost} />
<SecurityCard />
</>
)
}

Expand Down
39 changes: 39 additions & 0 deletions packages/web/src/app/[domain]/onboard/components/demoCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}
2 changes: 2 additions & 0 deletions packages/web/src/app/[domain]/onboard/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/repos/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
86 changes: 86 additions & 0 deletions packages/web/src/app/components/securityCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}

5 changes: 4 additions & 1 deletion packages/web/src/app/login/components/loginForm.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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">
Expand Down
41 changes: 23 additions & 18 deletions packages/web/src/app/login/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand All@@ -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>
)
}
100 changes: 52 additions & 48 deletions packages/web/src/app/login/verify/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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("")
Expand DownExpand Up@@ -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>
)
}
Expand Down
4 changes: 4 additions & 0 deletions packages/web/src/lib/posthogEvents.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -48,7 +49,10 @@ export const ConnectCodeHost = ({ nextStep }: ConnectCodeHostProps) => {

if (!selectedCodeHost) {
return (
<CodeHostSelection onSelect={setSelectedCodeHost} />
<>
<CodeHostSelection onSelect={setSelectedCodeHost} />
<SecurityCard />
</>
)
}

Expand Down
39 changes: 39 additions & 0 deletions packages/web/src/app/[domain]/onboard/components/demoCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}
2 changes: 2 additions & 0 deletions packages/web/src/app/[domain]/onboard/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/repos/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
86 changes: 86 additions & 0 deletions packages/web/src/app/components/securityCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}

5 changes: 4 additions & 1 deletion packages/web/src/app/login/components/loginForm.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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">
Expand Down
41 changes: 23 additions & 18 deletions packages/web/src/app/login/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand All@@ -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>
)
}
100 changes: 52 additions & 48 deletions packages/web/src/app/login/verify/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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("")
Expand DownExpand Up@@ -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>
)
}
Expand Down
4 changes: 4 additions & 0 deletions packages/web/src/lib/posthogEvents.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -48,7 +49,10 @@ export const ConnectCodeHost = ({ nextStep }: ConnectCodeHostProps) => {

if (!selectedCodeHost) {
return (
<CodeHostSelection onSelect={setSelectedCodeHost} />
<>
<CodeHostSelection onSelect={setSelectedCodeHost} />
<SecurityCard />
</>
)
}

Expand Down
39 changes: 39 additions & 0 deletions packages/web/src/app/[domain]/onboard/components/demoCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}
2 changes: 2 additions & 0 deletions packages/web/src/app/[domain]/onboard/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/[domain]/repos/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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>
Expand Down
86 changes: 86 additions & 0 deletions packages/web/src/app/components/securityCard.tsx
Original file line numberDiff line numberDiff line change
@@ -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>
)
}

5 changes: 4 additions & 1 deletion packages/web/src/app/login/components/loginForm.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -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">
Expand Down
41 changes: 23 additions & 18 deletions packages/web/src/app/login/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand All@@ -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>
)
}
100 changes: 52 additions & 48 deletions packages/web/src/app/login/verify/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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("")
Expand DownExpand Up@@ -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>
)
}
Expand Down
4 changes: 4 additions & 0 deletions packages/web/src/lib/posthogEvents.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;