"use client"; import { useState, useEffect } from "react"; import { saveResendCredentials, saveTwilioCredentials, testResendConnection, testTwilioConnection, getResendCredentials, getTwilioCredentials } from "@/actions/integrations/credentials"; // Icons const MailIcon = ({ className }: { className?: string }) => ( ); const MessageIcon = ({ className }: { className?: string }) => ( ); const ShieldIcon = ({ className }: { className?: string }) => ( ); const CheckIcon = ({ className }: { className?: string }) => ( ); const EyeIcon = ({ className }: { className?: string }) => ( ); const EyeOffIcon = ({ className }: { className?: string }) => ( ); const AlertIcon = ({ className }: { className?: string }) => ( ); type Props = { brandId: string; brands: { id: string; name: string }[]; }; export default function AdvancedIntegrations({ brandId, brands }: Props) { // The selected brand is always the current `brandId` prop — there is no // in-component brand picker, so derive it directly to avoid a stale copy // when the parent re-renders with a different brand. const selectedBrandId = brandId; const [credentials, setCredentials] = useState<{ resend: { api_key: string; from_email: string; from_name: string }; twilio: { account_sid: string; auth_token: string; phone_number: string }; }>({ resend: { api_key: "", from_email: "", from_name: "" }, twilio: { account_sid: "", auth_token: "", phone_number: "" }, }); const [showSecrets, setShowSecrets] = useState>({}); const [loading, setLoading] = useState(false); const [testResults, setTestResults] = useState>({}); const [saveStatus, setSaveStatus] = useState<{ type: "success" | "error"; message: string } | null>(null); useEffect(() => { async function fetchCredentials() { const [resendCreds, twilioCreds] = await Promise.all([ getResendCredentials(selectedBrandId), getTwilioCredentials(selectedBrandId), ]); setCredentials({ resend: { api_key: resendCreds.api_key ?? "", from_email: resendCreds.from_email ?? "", from_name: resendCreds.from_name ?? "", }, twilio: { account_sid: twilioCreds.account_sid ?? "", auth_token: twilioCreds.auth_token ?? "", phone_number: twilioCreds.phone_number ?? "", }, }); } fetchCredentials(); }, [selectedBrandId]); function toggleSecret(key: string) { setShowSecrets((prev) => ({ ...prev, [key]: !prev[key] })); } async function handleTest(service: "resend" | "twilio") { // Clear previous result for this service const newResults = { ...testResults }; delete newResults[service]; setTestResults(newResults); if (service === "resend") { if (!credentials.resend.api_key?.trim()) { setTestResults((prev) => ({ ...prev, resend: { ok: false, message: "API key is required" } })); return; } const result = await testResendConnection(credentials.resend.api_key); setTestResults((prev) => ({ ...prev, resend: result })); } else { if (!credentials.twilio.account_sid?.trim() || !credentials.twilio.auth_token?.trim()) { setTestResults((prev) => ({ ...prev, twilio: { ok: false, message: "Account SID and Auth Token are required" } })); return; } const result = await testTwilioConnection(credentials.twilio.account_sid, credentials.twilio.auth_token); setTestResults((prev) => ({ ...prev, twilio: result })); } } async function handleSave(service: "resend" | "twilio") { setLoading(true); setSaveStatus(null); // Clear test result for this service const clearedResults = { ...testResults }; delete clearedResults[service]; setTestResults(clearedResults); try { if (service === "resend") { const result = await saveResendCredentials(selectedBrandId, { api_key: credentials.resend.api_key?.trim() || null, from_email: credentials.resend.from_email?.trim() || null, from_name: credentials.resend.from_name?.trim() || null, }); if (result.success) { setSaveStatus({ type: "success", message: "Resend credentials saved" }); setTestResults((prev) => ({ ...prev, resend: { ok: true, message: "Saved successfully" } })); } else { setSaveStatus({ type: "error", message: result.error ?? "Failed to save" }); } } else { const result = await saveTwilioCredentials(selectedBrandId, { account_sid: credentials.twilio.account_sid?.trim() || null, auth_token: credentials.twilio.auth_token?.trim() || null, phone_number: credentials.twilio.phone_number?.trim() || null, }); if (result.success) { setSaveStatus({ type: "success", message: "Twilio credentials saved" }); setTestResults((prev) => ({ ...prev, twilio: { ok: true, message: "Saved successfully" } })); } else { setSaveStatus({ type: "error", message: result.error ?? "Failed to save" }); } } } catch (err) { setSaveStatus({ type: "error", message: err instanceof Error ? err.message : "Failed to save" }); } finally { setLoading(false); } } return (
{/* Security Warning Banner */}

API Key Security

  • Never share keys — Don't paste in Slack, email, or commit to GitHub.
  • SMS costs add up fast — Twilio charges $0.008-$0.05 per message.
  • Set spending caps — Use provider budget controls to avoid surprise bills.
{/* Resend Card */}

Resend

Email delivery for Harvest Reach campaigns

{/* API Key */}
setCredentials((p) => ({ ...p, resend: { ...p.resend, api_key: e.target.value }, }))} placeholder="re_..." className="w-full rounded-lg border border-[var(--admin-border)] bg-white px-3 py-2.5 text-sm text-[var(--admin-text-primary)] placeholder:text-stone-400 outline-none focus:border-emerald-500 focus:ring-1 focus:ring-emerald-500" />
{/* From Email */}
setCredentials((p) => ({ ...p, resend: { ...p.resend, from_email: e.target.value }, }))} placeholder="orders@yourbrand.com" className="w-full rounded-lg border border-[var(--admin-border)] bg-white px-3 py-2.5 text-sm text-[var(--admin-text-primary)] placeholder:text-stone-400 outline-none focus:border-emerald-500 focus:ring-1 focus:ring-emerald-500" />
{/* From Name */}
setCredentials((p) => ({ ...p, resend: { ...p.resend, from_name: e.target.value }, }))} placeholder="Your Brand Name" className="w-full rounded-lg border border-[var(--admin-border)] bg-white px-3 py-2.5 text-sm text-[var(--admin-text-primary)] placeholder:text-stone-400 outline-none focus:border-emerald-500 focus:ring-1 focus:ring-emerald-500" />
{/* Status & Actions */} {testResults.resend && (
{testResults.resend.ok ? : } {testResults.resend.message}
)}
{/* Twilio Card */}

Twilio

SMS campaigns and alerts via Harvest Reach

{/* Account SID */}
setCredentials((p) => ({ ...p, twilio: { ...p.twilio, account_sid: e.target.value }, }))} placeholder="AC..." className="w-full rounded-lg border border-[var(--admin-border)] bg-white px-3 py-2.5 text-sm text-[var(--admin-text-primary)] placeholder:text-stone-400 outline-none focus:border-emerald-500 focus:ring-1 focus:ring-emerald-500" />
{/* Auth Token */}
setCredentials((p) => ({ ...p, twilio: { ...p.twilio, auth_token: e.target.value }, }))} placeholder="Your Twilio auth token" className="w-full rounded-lg border border-[var(--admin-border)] bg-white px-3 py-2.5 text-sm pr-10 text-[var(--admin-text-primary)] placeholder:text-stone-400 outline-none focus:border-emerald-500 focus:ring-1 focus:ring-emerald-500" />
{/* Phone Number */}
setCredentials((p) => ({ ...p, twilio: { ...p.twilio, phone_number: e.target.value }, }))} placeholder="+1234567890" className="w-full rounded-lg border border-[var(--admin-border)] bg-white px-3 py-2.5 text-sm text-[var(--admin-text-primary)] placeholder:text-stone-400 outline-none focus:border-emerald-500 focus:ring-1 focus:ring-emerald-500" />
{/* Status & Actions */} {testResults.twilio && (
{testResults.twilio.ok ? : } {testResults.twilio.message}
)}
{/* Save Status */} {saveStatus && (
{saveStatus.message}
)}
); }