diff --git a/apps/marketing/public/go.svg b/apps/marketing/public/go.svg new file mode 100644 index 0000000..693d2e8 --- /dev/null +++ b/apps/marketing/public/go.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/apps/marketing/public/php.svg b/apps/marketing/public/php.svg new file mode 100644 index 0000000..0877923 --- /dev/null +++ b/apps/marketing/public/php.svg @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/apps/marketing/public/python.svg b/apps/marketing/public/python.svg new file mode 100644 index 0000000..3a4e654 --- /dev/null +++ b/apps/marketing/public/python.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/marketing/public/typescript.svg b/apps/marketing/public/typescript.svg new file mode 100644 index 0000000..4bf36f8 --- /dev/null +++ b/apps/marketing/public/typescript.svg @@ -0,0 +1,2 @@ + +file_type_typescript_official \ No newline at end of file diff --git a/apps/marketing/src/app/page.tsx b/apps/marketing/src/app/page.tsx index fce0f0f..df14f54 100644 --- a/apps/marketing/src/app/page.tsx +++ b/apps/marketing/src/app/page.tsx @@ -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: "

useSend is the best open source product to send emails

", - text: "useSend is the best open source product to send emails", -});`; - - return ( -
-
-
-
- Developers -
-

- Typed SDKs and simple APIs, so you can focus on product not - plumbing. -

-
- -
-
JavaScript
-
-
-
- -
-
-
-
- -
- -
-
-
- ); -} +// CodeExample moved to a dedicated client component in ~/components/CodeExample function Pricing() { const freePerks = [ diff --git a/apps/marketing/src/components/CodeExample.tsx b/apps/marketing/src/components/CodeExample.tsx new file mode 100644 index 0000000..ef72fa9 --- /dev/null +++ b/apps/marketing/src/components/CodeExample.tsx @@ -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: "

useSend is the best open source product to send emails

", + 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": "

useSend is the best open source product to send emails

", + "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\\\": \\\"

useSend is the best open source product to send emails

\\\",\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 = ` 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' => '

useSend is the best open source product to send emails

', + '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 ( +
+
+
+
+ Developers +
+

+ Typed SDKs and simple APIs, so you can focus on product not + plumbing. +

+
+ +
+
+ ({ + key, + label, + kind, + }))} + /> +
+
+
+
+ {languages.map((l, idx) => ( +
+ {/* Cast to any to align with shiki BundledLanguage without importing types here */} + + {l.code} + +
+ ))} +
+
+
+
+ Language example toggled +
+
+ +
+ +
+
+
+ ); +} + +export default CodeExample; diff --git a/apps/marketing/src/components/CodeLangToggle.tsx b/apps/marketing/src/components/CodeLangToggle.tsx new file mode 100644 index 0000000..08d0406 --- /dev/null +++ b/apps/marketing/src/components/CodeLangToggle.tsx @@ -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("[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 ( +
+ {languages.map((l) => ( + + ))} +
+ ); +} + +function LangIcon({ kind, className = "h-4 w-4" }: { kind: string; className?: string }) { + const [failed, setFailed] = useState(false); + if (failed) { + return ( + + ); + } + if (kind === "ts") + return ( + TypeScript logo setFailed(true)} + /> + ); + if (kind === "py") + return ( + Python logo setFailed(true)} + /> + ); + if (kind === "go") + return ( + Go logo setFailed(true)} + /> + ); + if (kind === "php") + return ( + PHP logo setFailed(true)} + /> + ); + return ( + + ); +} + +export default LangToggle;