diff --git a/apps/web/src/app/auth/components/particles/index.ts b/apps/web/src/app/auth/components/particles/index.ts new file mode 100644 index 00000000..cfcb4d89 --- /dev/null +++ b/apps/web/src/app/auth/components/particles/index.ts @@ -0,0 +1 @@ +export { Particles } from "./particles"; diff --git a/apps/web/src/app/auth/login/mouse-position.tsx b/apps/web/src/app/auth/components/particles/mouse-position.tsx similarity index 100% rename from apps/web/src/app/auth/login/mouse-position.tsx rename to apps/web/src/app/auth/components/particles/mouse-position.tsx diff --git a/apps/web/src/app/auth/login/particles.tsx b/apps/web/src/app/auth/components/particles/particles.tsx similarity index 99% rename from apps/web/src/app/auth/login/particles.tsx rename to apps/web/src/app/auth/components/particles/particles.tsx index aaba919e..6fabeae6 100644 --- a/apps/web/src/app/auth/login/particles.tsx +++ b/apps/web/src/app/auth/components/particles/particles.tsx @@ -12,7 +12,7 @@ interface ParticlesProps { rgb?: string; } -export default function Particles({ +export function Particles({ className = "", quantity = 30, staticity = 50, diff --git a/apps/web/src/app/auth/login/page.tsx b/apps/web/src/app/auth/login/page.tsx index d86f7268..bf98d559 100644 --- a/apps/web/src/app/auth/login/page.tsx +++ b/apps/web/src/app/auth/login/page.tsx @@ -1,52 +1,11 @@ -import { - Box, - Button, - Flex, - Paper, - Stack, - Text, - ThemeIcon, - Title, - useMantineColorScheme, -} from "@mantine/core"; +import { Button, Flex, Paper, Stack, Text, Title } from "@mantine/core"; import { AuthProvider } from "@sweetr/graphql-types/frontend/graphql"; -import { - IconBrandGithub, - IconClock, - IconFireExtinguisher, - IconFlame, - IconQuote, -} from "@tabler/icons-react"; import { useAuthProviderQuery } from "../../../api/auth.api"; import { Logo } from "../../../components/logo"; -import { IconDeployment } from "../../../providers/icon.provider"; -import { useSearchParams } from "react-router"; -import Particles from "./particles"; +import { Link, useSearchParams } from "react-router"; +import { Particles } from "../components/particles"; import classes from "./page.module.css"; -const ELITE_BENCHMARKS = [ - { - icon: IconDeployment, - metric: "Deployment Frequency", - benchmark: "On-demand, multiple deploys per day", - }, - { - icon: IconClock, - metric: "Lead Time for Changes", - benchmark: "Less than one hour from commit to production", - }, - { - icon: IconFireExtinguisher, - metric: "Time to Restore Service", - benchmark: "Less than one hour to recover from incidents", - }, - { - icon: IconFlame, - metric: "Change Failure Rate", - benchmark: "0–15% of changes cause failures", - }, -]; - export const LoginPage = () => { const [searchParams] = useSearchParams({ redirectTo: "", @@ -59,8 +18,6 @@ export const LoginPage = () => { }, }); - const { colorScheme } = useMantineColorScheme(); - document.body.style.backgroundColor = "#141517"; return ( @@ -76,7 +33,6 @@ export const LoginPage = () => { radius="lg" p={48} maw={960} - w="100%" pos="relative" style={{ zIndex: 1 }} > @@ -89,29 +45,39 @@ export const LoginPage = () => { > - Get started for free + Sweetr - No credit card required + Login or signup with GitHub - - - Works for login and signup. New teams will be guided through - setup. - + + + + + + { td="underline" style={{ textUnderlineOffset: 3 }} > - Explore the sandbox instead. + Explore the sandbox instead - - - - Is your engineering team elite? - - - According to the DORA research program, elite teams hit all four - of these benchmarks. Most teams don't know where they stand. - - - - {ELITE_BENCHMARKS.map((item) => ( - - - - - - - {item.metric} - - - {item.benchmark} - - - - ))} - - - - Sweetr tracks these metrics automatically from your Git data. - - - - - - Source: 2021 Accelerate State of DevOps Report (DORA / Google - Cloud) - - - diff --git a/apps/web/src/app/auth/sign-up/page.module.css b/apps/web/src/app/auth/sign-up/page.module.css new file mode 100644 index 00000000..25774738 --- /dev/null +++ b/apps/web/src/app/auth/sign-up/page.module.css @@ -0,0 +1,32 @@ +.page { + min-height: 100vh; + position: relative; + overflow: hidden; + background: + radial-gradient( + ellipse 600px 400px at 15% 10%, + rgba(64, 192, 87, 0.12) 0%, + transparent 100% + ), + radial-gradient( + ellipse 500px 500px at 85% 80%, + rgba(64, 192, 87, 0.08) 0%, + transparent 100% + ), + radial-gradient( + ellipse 300px 600px at 50% 50%, + rgba(64, 192, 87, 0.04) 0%, + transparent 100% + ), + radial-gradient( + circle 800px at 70% 20%, + rgba(34, 139, 230, 0.04) 0%, + transparent 100% + ); +} + +.particles { + position: absolute; + inset: 0; + z-index: 0; +} diff --git a/apps/web/src/app/auth/sign-up/page.tsx b/apps/web/src/app/auth/sign-up/page.tsx new file mode 100644 index 00000000..e9268e1a --- /dev/null +++ b/apps/web/src/app/auth/sign-up/page.tsx @@ -0,0 +1,197 @@ +import { + Accordion, + Anchor, + Box, + Button, + Divider, + Flex, + Paper, + Stack, + Text, + ThemeIcon, + Title, +} from "@mantine/core"; +import { + IconBrandGithub, + IconCode, + IconLock, + IconLogout, + IconShieldCheck, +} from "@tabler/icons-react"; +import { Logo } from "../../../components/logo"; +import { Link } from "react-router"; +import { Particles } from "../components/particles"; +import classes from "./page.module.css"; +import { installGithubAppUrl } from "../../../providers/github.provider"; + +const FAQ_ITEMS = [ + { + value: "source-code", + icon: IconCode, + color: "green", + question: "Does the GitHub app have access to my source code?", + answer: ( + <> + No. Sweetr only asks for access to metadata about organization members + and their pull requests. You can check the{" "} + + documentation + {" "} + for more details. + + ), + }, + { + value: "permissions", + icon: IconLock, + color: "blue", + question: "Who has permissions to see the data?", + answer: + "Sweetr inherits the exact permissions your team already has on GitHub. If a teammate can't see a private repository on GitHub, they won't see any of its data on Sweetr either.", + }, + { + value: "security", + icon: IconShieldCheck, + color: "violet", + question: "Is my data secure?", + answer: ( + <> + We follow stringent security best practices to protect our servers and + your data.{" "} + + Learn more + + . + + ), + }, + { + value: "uninstall", + icon: IconLogout, + color: "red", + question: "Can I uninstall it at any time?", + answer: + "Yes. You can uninstall the GitHub app from your organization settings at any time and revoke access in a single click to remove all data from our servers. No questions asked.", + }, +]; + +export const SignUpPage = () => { + document.body.style.backgroundColor = "#141517"; + + return ( + + + + + + + + Start 14-day free trial + + No credit card required + + + + + Back to login + + + + + + How it works + + + Sweetr is a GitHub app you install on your organization. Once + installed, we sync your data automatically. You'll see your team's + metrics within minutes, with zero configuration. + + + + + + Common questions + + + + {FAQ_ITEMS.map((item) => ( + + + + + } + > + + {item.question} + + + + + {item.answer} + + + + ))} + + + + + + ); +}; diff --git a/apps/web/src/routes.tsx b/apps/web/src/routes.tsx index 4865c519..10d88086 100644 --- a/apps/web/src/routes.tsx +++ b/apps/web/src/routes.tsx @@ -1,7 +1,7 @@ import { createBrowserRouter, redirect } from "react-router"; import { PageNotFound } from "./app/404"; import { ErrorPage } from "./app/500"; -import { LoginPage } from "./app/auth/login/page"; +import { SignUpPage } from "./app/auth/sign-up/page"; import { AutomationsPage } from "./app/automations/page"; import { AutomationPrSizeLabelerPage } from "./app/automations/settings/pr-size-labeler/page"; import { AutomationIncidentDetectionPage } from "./app/automations/settings/incident-detection/page"; @@ -70,6 +70,7 @@ import { SystemsPullRequestsPage } from "./app/systems/pull-requests/page"; import { MetricsAndInsightsPage } from "./app/metrics-and-insights/page"; import { PrFlowPage } from "./app/metrics-and-insights/pr-flow/page"; import { CodeReviewEfficiencyPage } from "./app/metrics-and-insights/code-review-efficiency/page"; +import { LoginPage } from "./app/auth/login/page"; export const router = createBrowserRouter([ { @@ -101,6 +102,10 @@ export const router = createBrowserRouter([ path: "/login", element: , }, + { + path: "/sign-up", + element: , + }, ], }, // Sandbox entry point — starts MSW, sets fake auth, redirects to / diff --git a/package-lock.json b/package-lock.json index f9653572..35bc1f44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,8 +16,8 @@ }, "devDependencies": { "dotenv-cli": "^7.4.2", - "prettier": "*", - "turbo": "*" + "prettier": "latest", + "turbo": "latest" }, "engines": { "node": ">=22.12.0" @@ -15083,7 +15083,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" }