add languages to landing page

This commit is contained in:
KM Koushik
2025-09-09 07:13:15 +10:00
parent 3158ddc51c
commit 292048d2c9
7 changed files with 357 additions and 57 deletions

View File

@@ -7,7 +7,7 @@ import { TopNav } from "~/components/TopNav";
import { FeatureCard } from "~/components/FeatureCard";
import { FeatureCardPlain } from "~/components/FeatureCardPlain";
import { PricingCalculator } from "~/components/PricingCalculator";
import { CodeBlock } from "@usesend/ui/src/code-block";
import CodeExample from "~/components/CodeExample";
const REPO = "usesend/usesend";
const REPO_URL = `https://github.com/${REPO}`;
@@ -292,62 +292,7 @@ function Features() {
);
}
function CodeExample() {
const code = `import { UseSend } from "usesend-js";
const usesend = new UseSend("us_12345");
usesend.emails.send({
to: "hello@acme.com",
from: "hello@company.com",
subject: "useSend email",
html: "<p>useSend is the best open source product to send emails</p>",
text: "useSend is the best open source product to send emails",
});`;
return (
<section className="py-16 sm:py-20">
<div className="mx-auto max-w-6xl px-6">
<div className="text-center">
<div className="mb-2 text-sm uppercase tracking-wider text-primary">
Developers
</div>
<p className="mt-1 text-xs sm:text-sm text-muted-foreground max-w-2xl mx-auto">
Typed SDKs and simple APIs, so you can focus on product not
plumbing.
</p>
</div>
<div className="mt-8 overflow-hidden">
<div className=" py-2 text-xs text-muted-foreground">JavaScript</div>
<div className="rounded-[18px] bg-primary/20 p-1">
<div className="rounded-[14px] bg-primary/20 p-0.5 shadow-sm">
<div className="bg-background rounded-xl overflow-hidden">
<CodeBlock
lang="javascript"
children={code}
className="p-4 rounded-[10px]"
/>
</div>
</div>
</div>
</div>
<div className="mt-6 flex items-center justify-center gap-3">
<Button size="lg" className="px-6">
<a
href="https://docs.usesend.com"
target="_blank"
rel="noopener noreferrer"
>
Read the docs
</a>
</Button>
</div>
</div>
</section>
);
}
// CodeExample moved to a dedicated client component in ~/components/CodeExample
function Pricing() {
const freePerks = [

View File

@@ -0,0 +1,184 @@
import { Button } from "@usesend/ui/src/button";
import { CodeBlock } from "@usesend/ui/src/code-block";
import { LangToggle } from "./CodeLangToggle";
const TS_CODE = `import { UseSend } from "usesend-js";
const usesend = new UseSend("us_12345");
usesend.emails.send({
to: "hello@acme.com",
from: "hello@company.com",
subject: "useSend email",
html: "<p>useSend is the best open source product to send emails</p>",
text: "useSend is the best open source product to send emails",
});`;
const PY_CODE = `from usesend import UseSend
client = UseSend("us_12345")
data, err = client.emails.send({
"to": "hello@acme.com",
"from": "hello@company.com",
"subject": "useSend email",
"html": "<p>useSend is the best open source product to send emails</p>",
"text": "useSend is the best open source product to send emails",
})
print(data or err)`;
const GO_CODE = `package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
url := "https://app.usesend.com/api/v1/emails"
payload := strings.NewReader("{\n \\\"to\\\": \\\"hello@acme.com\\\",\n \\\"from\\\": \\\"hello@company.com\\\",\n \\\"subject\\\": \\\"useSend email\\\",\n \\\"html\\\": \\\"<p>useSend is the best open source product to send emails</p>\\\",\n \\\"text\\\": \\\"useSend is the best open source product to send emails\\\"\n }")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", "Bearer us_12345")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}`;
const PHP_CODE = `<?php
$ch = curl_init('https://app.usesend.com/api/v1/emails');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer us_12345',
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'to' => 'hello@acme.com',
'from' => 'hello@company.com',
'subject' => 'useSend email',
'html' => '<p>useSend is the best open source product to send emails</p>',
'text' => 'useSend is the best open source product to send emails',
]),
]);
$response = curl_exec($ch);
if ($response === false) {
echo 'cURL error: ' . curl_error($ch);
} else {
echo $response;
}
curl_close($ch);`;
export function CodeExample() {
const containerId = "code-example";
const languages = [
{
key: "ts",
label: "TypeScript",
kind: "ts",
shiki: "typescript" as const,
code: TS_CODE,
},
{
key: "py",
label: "Python",
kind: "py",
shiki: "python" as const,
code: PY_CODE,
},
{
key: "go",
label: "Go",
kind: "go",
shiki: "go" as const,
code: GO_CODE,
},
{
key: "php",
label: "PHP",
kind: "php",
shiki: "php" as const,
code: PHP_CODE,
},
];
return (
<section className="py-16 sm:py-20">
<div className="mx-auto max-w-6xl px-6">
<div className="text-center">
<div className="mb-2 text-sm uppercase tracking-wider text-primary">
Developers
</div>
<p className="mt-1 text-xs sm:text-sm text-muted-foreground max-w-2xl mx-auto">
Typed SDKs and simple APIs, so you can focus on product not
plumbing.
</p>
</div>
<div className="mt-8 overflow-hidden" id={containerId}>
<div className="flex items-center gap-2 justify-center py-2 text-xs text-muted-foreground mb-4">
<LangToggle
containerId={containerId}
defaultLang="ts"
languages={languages.map(({ key, label, kind }) => ({
key,
label,
kind,
}))}
/>
</div>
<div className="rounded-[18px] bg-primary/20 p-1">
<div className="rounded-[14px] bg-primary/20 p-0.5 shadow-sm">
<div className="bg-background rounded-xl overflow-hidden">
{languages.map((l, idx) => (
<div
key={l.key}
data-lang-slot={l.key}
className={idx === 0 ? "block" : "hidden"}
>
{/* Cast to any to align with shiki BundledLanguage without importing types here */}
<CodeBlock
lang={l.shiki as any}
className="p-4 rounded-[10px]"
>
{l.code}
</CodeBlock>
</div>
))}
</div>
</div>
</div>
<div className="sr-only" aria-live="polite">
Language example toggled
</div>
</div>
<div className="mt-6 flex items-center justify-center gap-3">
<Button size="lg" className="px-6">
<a
href="https://docs.usesend.com"
target="_blank"
rel="noopener noreferrer"
>
Read the docs
</a>
</Button>
</div>
</div>
</section>
);
}
export default CodeExample;

View File

@@ -0,0 +1,132 @@
"use client";
import { useEffect, useState } from "react";
import Image from "next/image";
import { Button } from "@usesend/ui/src/button";
type LangItem = {
key: string;
label: string;
kind: "ts" | "py" | string; // used for icon selection
};
export function LangToggle({
containerId,
languages,
defaultLang,
}: {
containerId: string;
languages: LangItem[];
defaultLang: string;
}) {
const [active, setActive] = useState(defaultLang);
useEffect(() => {
const container = document.getElementById(containerId);
if (!container) return;
const slots = Array.from(
container.querySelectorAll<HTMLElement>("[data-lang-slot]")
);
for (const el of slots) {
const key = el.getAttribute("data-lang-slot");
if (key === active) {
el.classList.remove("hidden");
el.classList.add("block");
} else {
el.classList.add("hidden");
el.classList.remove("block");
}
}
}, [active, containerId]);
return (
<div className="flex items-center gap-2 justify-center">
{languages.map((l) => (
<Button
key={l.key}
size="sm"
variant="outline"
className={
"px-3 bg-transparent hover:bg-transparent hover:text-inherit " +
(active === l.key
? "border-primary"
: "border-input")
}
aria-pressed={active === l.key}
onClick={() => setActive(l.key)}
>
<span className="inline-flex items-center">
<LangIcon kind={l.kind} className="h-4 w-4 mr-1" /> {l.label}
</span>
</Button>
))}
</div>
);
}
function LangIcon({ kind, className = "h-4 w-4" }: { kind: string; className?: string }) {
const [failed, setFailed] = useState(false);
if (failed) {
return (
<svg viewBox="0 0 24 24" aria-hidden="true" className={className} role="img">
<circle cx="12" cy="12" r="10" fill="currentColor" opacity="0.2" />
</svg>
);
}
if (kind === "ts")
return (
<Image
src="/typescript.svg"
alt="TypeScript logo"
width={16}
height={16}
className={className}
priority={false}
onError={() => setFailed(true)}
/>
);
if (kind === "py")
return (
<Image
src="/python.svg"
alt="Python logo"
width={16}
height={16}
className={className}
priority={false}
onError={() => setFailed(true)}
/>
);
if (kind === "go")
return (
<Image
src="/go.svg"
alt="Go logo"
width={16}
height={16}
className={className}
priority={false}
onError={() => setFailed(true)}
/>
);
if (kind === "php")
return (
<Image
src="/php.svg"
alt="PHP logo"
width={16}
height={16}
className={className}
priority={false}
onError={() => setFailed(true)}
/>
);
return (
<svg viewBox="0 0 24 24" aria-hidden="true" className={className} role="img">
<circle cx="12" cy="12" r="10" fill="currentColor" opacity="0.2" />
</svg>
);
}
export default LangToggle;