Ctrl K
ComunidadGuías…

webapps/console/components/SignInOrUp/SignupMarketingPanel.tsx

Rama newjitsu · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import React, { ReactNode, useEffect, useState } from "react";
import { branding } from "../../lib/branding";

const panelFeatures = [
  {
    title: "Jitsu Functions",
    href: "https://jitsu.com/features/functions",
    description: "Transform, enrich, and route events in-flight with TypeScript — call APIs, persist state.",
  },
  {
    title: "Real-time streaming",
    href: "https://jitsu.com/features/event-streaming",
    description: "Land events in your warehouse the moment they happen.",
  },
  {
    title: "Drop-in for Segment",
    href: "https://jitsu.com/features/segment-compatibility",
    description: "Same SDK shape — keep your existing instrumentation.",
  },
];

const customers: { name: string; href?: string }[] = [
  { name: "Investing.com", href: "https://jitsu.com/customers/investing" },
  { name: "PandaDoc" },
  { name: "Rarible" },
  { name: "Census" },
  { name: "Embeddables" },
];

const snippets = [
  {
    file: "enrich.ts",
    badge: "JITSU FUNCTION",
    code: `export default async function (event, { store }) {
  const geo = await store.get(event.ip);
  event.country = geo?.country ?? "unknown";
  return event;
}`,
  },
  {
    file: "track.ts",
    badge: "BROWSER SDK",
    code: `import { jitsuAnalytics } from "@jitsu/js";

const jitsu = jitsuAnalytics({ writeKey: "KEY" });
jitsu.identify("user_42", { email });
jitsu.track("Signup Completed", { plan: "pro" });`,
  },
];

// Tiny token highlighter for the (fixed, trusted) code samples — keeps the
// snippets as plain strings instead of hand-written colored JSX.
const CODE_TOKEN =
  /(`[^`]*`|"[^"]*"|'[^']*'|\b(?:import|from|export|default|async|function|const|await|return|new)\b)/g;
const KEYWORD = /^(?:import|from|export|default|async|function|const|await|return|new)$/;

function highlightLine(line: string): ReactNode {
  if (line === "") {
    return " ";
  }
  return line.split(CODE_TOKEN).map((part, i) => {
    if (!part) {
      return null;
    }
    if (part[0] === "`" || part[0] === '"' || part[0] === "'") {
      return (
        <span key={i} className="text-[#c3e88d]">
          {part}
        </span>
      );
    }
    if (KEYWORD.test(part)) {
      return (
        <span key={i} className="text-[#c792ea]">
          {part}
        </span>
      );
    }
    return <span key={i}>{part}</span>;
  });
}

/** Code card that auto-cycles through the snippets; a manual click pins it. */
const CodeShowcase: React.FC = () => {
  const [active, setActive] = useState(0);
  const [autoSwitch, setAutoSwitch] = useState(true);

  useEffect(() => {
    if (!autoSwitch) {
      return;
    }
    const t = setInterval(() => setActive(a => (a + 1) % snippets.length), 4500);
    return () => clearInterval(t);
  }, [autoSwitch]);

  // A manual pick stops the auto-cycle for good.
  const selectTab = (i: number) => {
    setActive(i);
    setAutoSwitch(false);
  };

  const snippet = snippets[active];
  return (
    <div className="rounded-xl border border-white/10 overflow-hidden" style={{ background: "#0e0d18" }}>
      <div className="flex items-center justify-between border-b border-white/10 px-4 py-2.5">
        <div className="flex items-center gap-3 font-mono text-xs">
          <span className="h-2 w-2 rounded-full bg-green-400" />
          {snippets.map((s, i) => (
            <button
              key={s.file}
              type="button"
              onClick={() => selectTab(i)}
              className={`cursor-pointer p-0 transition-colors ${
                i === active ? "text-white" : "text-white/30 hover:text-white/70"
              }`}
            >
              {s.file}
            </button>
          ))}
        </div>
        <span className="rounded border border-white/15 px-1.5 py-0.5 text-[10px] tracking-[0.15em] text-white/40">
          {snippet.badge}
        </span>
      </div>
      <div className="whitespace-pre overflow-x-auto p-4 font-mono text-[12px] leading-[1.7] text-slate-200">
        {snippet.code.split("\n").map((line, i) => (
          <div key={i}>{highlightLine(line)}</div>
        ))}
      </div>
    </div>
  );
};

/** Left-hand testimonial / proof panel of the signup page. Hidden below `lg`. */
export const SignupMarketingPanel: React.FC = () => (
  <div
    className="hidden lg:flex lg:w-1/2 p-10 xl:p-14 text-white overflow-y-auto"
    style={{ background: "linear-gradient(155deg, #1c1640 0%, #2a1b53 55%, #211a48 100%)" }}
  >
    {/* one column — every section shares the same width */}
    <div className="flex w-full max-w-2xl flex-col justify-between gap-9">
      {/* header */}
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-2">
          <div className="h-7 w-7">{branding.logo}</div>
          {/* fill-white overrides the one hardcoded fill="black" path in the wordmark */}
          <div className="h-5 w-[84px] [&_path]:fill-white">{branding.wordmark}</div>
        </div>
        <a
          href="https://jitsu.com"
          className="text-sm text-white/55 hover:text-white hover:underline transition-colors"
        >
          ‹ Back to jitsu.com
        </a>
      </div>

      {/* testimonial */}
      <div>
        <div className="font-serif text-[80px] leading-[0.6] text-white/25">“</div>
        <p className="mt-5 text-2xl xl:text-3xl font-medium leading-snug">
          Jitsu Functions let us enrich events server-side with external APIs and a persistent KV store —{" "}
          <span className="text-white/45">killing the client-side code duplication across web, iOS, and Android.</span>
        </p>
        <div className="mt-8 flex items-center justify-between">
          <a
            href="https://jitsu.com/customers/investing"
            target="_blank"
            rel="noreferrer"
            className="font-semibold hover:underline"
          >
            Investing.com
          </a>
          <div className="text-right">
            <div className="text-3xl font-bold leading-none">5B</div>
            <div className="mt-1 text-[10px] tracking-[0.2em] text-white/50">EVENTS / MONTH</div>
          </div>
        </div>
      </div>

      {/* code showcase */}
      <CodeShowcase />

      {/* features */}
      <div className="grid grid-cols-3 gap-6 border-t border-white/10 pt-6">
        {panelFeatures.map(f => (
          <div key={f.title}>
            <a
              href={f.href}
              target="_blank"
              rel="noreferrer"
              className="flex items-center gap-1.5 font-semibold hover:underline"
            >
              <span className="text-[#c3e88d]">✓</span>
              {f.title}
            </a>
            <p className="mt-1.5 text-sm text-white/55">{f.description}</p>
          </div>
        ))}
      </div>

      {/* social proof */}
      <div className="border-t border-white/10 pt-6">
        <a
          href="https://jitsu.com/customers"
          target="_blank"
          rel="noreferrer"
          className="text-[10px] tracking-[0.18em] text-white/40 hover:underline"
        >
          IN PRODUCTION AT
        </a>
        <div className="mt-3 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm font-medium text-white/80">
          {customers.map((c, i) => (
            <React.Fragment key={c.name}>
              {i > 0 && <span className="text-white/25">·</span>}
              {c.href ? (
                <a href={c.href} target="_blank" rel="noreferrer" className="hover:underline">
                  {c.name}
                </a>
              ) : (
                <span>{c.name}</span>
              )}
            </React.Fragment>
          ))}
        </div>
      </div>
    </div>
  </div>
);