import {Button} from '@plunk/ui'; import {motion} from 'framer-motion'; import {ArrowLeft, ArrowRight, KeyRound, Mail, ShieldCheck} from 'lucide-react'; import {NextSeo} from 'next-seo'; import Link from 'next/link'; import {useRouter} from 'next/router'; import {useEffect, useMemo} from 'react'; import {ApiKeyDisplay} from '../../components/ApiKeyDisplay'; import {CodeTabs, type CodeSnippet} from '../../components/onboarding/CodeTabs'; import {OnboardingLayout} from '../../components/onboarding/OnboardingLayout'; import {API_URI} from '../../lib/constants'; import {useActiveProject} from '../../lib/contexts/ActiveProjectProvider'; import {useOnboardingGate} from '../../lib/hooks/useOnboardingGate'; import {useOnboardingPath} from '../../lib/hooks/useOnboardingPath'; interface SetupStep { id: string; icon: React.ElementType; title: string; description: string; cta?: string; href?: string; required?: boolean; } const steps: SetupStep[] = [ { id: 'domain', icon: ShieldCheck, title: 'Verify your sender domain', description: 'Sending requires a verified domain. Two DNS records and you\'re set.', cta: 'Verify domain', href: '/settings?tab=domains', required: true, }, { id: 'key', icon: KeyRound, title: 'Copy your secret key', description: 'Add it to your server environment. Never expose it in client-side code.', }, { id: 'send', icon: Mail, title: 'Send your first email', description: 'One API call. Pick your language and drop it into your app.', }, ]; function buildSnippets(apiUrl: string, secret: string): CodeSnippet[] { const body = { to: 'you@example.com', subject: 'Hello from Plunk', body: '

Your first email is live.

', from: 'sender@yourdomain.com', }; const curl = `curl -X POST ${apiUrl}/v1/send \\ -H "Authorization: Bearer ${secret}" \\ -H "Content-Type: application/json" \\ -d '${JSON.stringify(body, null, 2).replace(/\n/g, '\n ')}'`; const node = `await fetch("${apiUrl}/v1/send", { method: "POST", headers: { Authorization: "Bearer ${secret}", "Content-Type": "application/json", }, body: JSON.stringify(${JSON.stringify(body, null, 2).replace(/\n/g, '\n ')}), });`; const python = `import requests requests.post( "${apiUrl}/v1/send", headers={"Authorization": "Bearer ${secret}"}, json=${JSON.stringify(body, null, 4).replace(/\n/g, '\n ')}, )`; const php = `$ch = curl_init("${apiUrl}/v1/send"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer ${secret}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(${JSON.stringify(body, null, 4).replace(/\n/g, '\n ')}), ]); curl_exec($ch);`; return [ {id: 'curl', label: 'cURL', code: curl}, {id: 'node', label: 'Node.js', code: node}, {id: 'python', label: 'Python', code: python}, {id: 'php', label: 'PHP', code: php}, ]; } export default function OnboardingDeveloper() { const router = useRouter(); const {activeProject} = useActiveProject(); const {state} = useOnboardingGate(); const {path, setPath} = useOnboardingPath(activeProject?.id); const snippets = useMemo( () => buildSnippets(API_URI, activeProject?.secret ?? 'sk_your_secret_key'), [activeProject?.secret], ); useEffect(() => { if (activeProject && !path) setPath('developer'); }, [activeProject, path, setPath]); if (state !== 'show') return null; const handleContinue = () => { void router.push('/'); }; const handleStepClick = (href: string) => { void router.push(href); }; return ( <>

Send your first email.

Three steps to your first send. Start with your domain so emails actually reach the inbox.

{steps.map((step, index) => { const Icon = step.icon; return (
{index + 1}

{step.title}

{step.required && ( Required )}

{step.description}

{step.cta && step.href && ( )}
{step.id === 'key' && (
{activeProject ? ( ) : (
)}
)} {step.id === 'send' && (
)} ); })}
Back
); }