From 5a26343ff25f9e4729f4eff2f086beae8b647003 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sat, 11 Jul 2026 13:49:16 +0000
Subject: [PATCH 1/4] Changes
Co-authored-by: wolfoftyreso-debug <250630591+wolfoftyreso-debug@users.noreply.github.com>
---
.../functions/send-outreach-email/index.ts | 87 +++++++++++++++++++
1 file changed, 87 insertions(+)
create mode 100644 supabase/functions/send-outreach-email/index.ts
diff --git a/supabase/functions/send-outreach-email/index.ts b/supabase/functions/send-outreach-email/index.ts
new file mode 100644
index 0000000..fd843d5
--- /dev/null
+++ b/supabase/functions/send-outreach-email/index.ts
@@ -0,0 +1,87 @@
+import { z } from "npm:zod@3.23.8";
+
+const corsHeaders = {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Headers":
+ "authorization, x-client-info, apikey, content-type",
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
+};
+
+const GATEWAY_URL = "https://connector-gateway.lovable.dev/brevo";
+
+const BodySchema = z.object({
+ to_email: z.string().email(),
+ to_name: z.string().min(1).max(200),
+ subject: z.string().min(1).max(300),
+ body: z.string().min(1).max(10000),
+});
+
+Deno.serve(async (req) => {
+ if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });
+
+ try {
+ const LOVABLE_API_KEY = Deno.env.get("LOVABLE_API_KEY");
+ const BREVO_API_KEY = Deno.env.get("BREVO_API_KEY");
+ if (!LOVABLE_API_KEY || !BREVO_API_KEY) {
+ throw new Error("Email service not configured");
+ }
+
+ const parsed = BodySchema.safeParse(await req.json());
+ if (!parsed.success) {
+ return new Response(
+ JSON.stringify({ error: "Ogiltiga fält", details: parsed.error.flatten().fieldErrors }),
+ { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } },
+ );
+ }
+ const { to_email, to_name, subject, body } = parsed.data;
+
+ const senderEmail = Deno.env.get("BREVO_SENDER_EMAIL") ?? "konditorivaror@gmail.com";
+ const senderName = "Tiffany – Lennart Svenssons Konditorivaror";
+
+ // Konvertera radbrytningar till
för HTML
+ const htmlBody = `
+
+ ${body
+ .split(/\n{2,}/)
+ .map((p) => `
${p.replace(/\n/g, "
")}
`)
+ .join("")}
+
`;
+
+ const res = await fetch(`${GATEWAY_URL}/v3/smtp/email`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${LOVABLE_API_KEY}`,
+ "X-Connection-Api-Key": BREVO_API_KEY,
+ },
+ body: JSON.stringify({
+ sender: { name: senderName, email: senderEmail },
+ to: [{ email: to_email, name: to_name }],
+ replyTo: { email: senderEmail, name: senderName },
+ subject,
+ htmlContent: htmlBody,
+ textContent: body,
+ }),
+ });
+
+ if (!res.ok) {
+ const errBody = await res.text();
+ console.error(`Brevo send failed [${res.status}]:`, errBody);
+ return new Response(
+ JSON.stringify({ error: "Brevo avvisade mejlet", status: res.status, details: errBody }),
+ { status: res.status, headers: { ...corsHeaders, "Content-Type": "application/json" } },
+ );
+ }
+
+ const data = await res.json();
+ return new Response(JSON.stringify({ success: true, messageId: data.messageId }), {
+ headers: { ...corsHeaders, "Content-Type": "application/json" },
+ });
+ } catch (err: any) {
+ console.error("send-outreach-email error:", err);
+ return new Response(
+ JSON.stringify({ error: err?.message ?? "Kunde inte skicka mejl" }),
+ { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } },
+ );
+ }
+});
From c30f4fa767a4c95e3539b9674c4f153d05bda18b Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sat, 11 Jul 2026 13:49:25 +0000
Subject: [PATCH 2/4] Changes
Co-authored-by: wolfoftyreso-debug <250630591+wolfoftyreso-debug@users.noreply.github.com>
---
src/components/admin/LeadsGenerator.tsx | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/components/admin/LeadsGenerator.tsx b/src/components/admin/LeadsGenerator.tsx
index e115ca1..de54081 100644
--- a/src/components/admin/LeadsGenerator.tsx
+++ b/src/components/admin/LeadsGenerator.tsx
@@ -1,10 +1,11 @@
-import { useState } from "react";
+import { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Badge } from "@/components/ui/badge";
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";
-import { Sparkles, RefreshCw, Building2, Users, MapPin, Mail, Search, Copy, Check } from "lucide-react";
+import { Input } from "@/components/ui/input";
+import { Sparkles, RefreshCw, Building2, Users, MapPin, Mail, Search, Copy, Check, Send } from "lucide-react";
import { useToast } from "@/hooks/use-toast";
import { Skeleton } from "@/components/ui/skeleton";
From 341739bd14e5f1af391fba73d88a9ebca1b21881 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sat, 11 Jul 2026 13:49:34 +0000
Subject: [PATCH 3/4] Changes
Co-authored-by: wolfoftyreso-debug <250630591+wolfoftyreso-debug@users.noreply.github.com>
---
src/components/admin/LeadsGenerator.tsx | 44 +++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/src/components/admin/LeadsGenerator.tsx b/src/components/admin/LeadsGenerator.tsx
index de54081..8c88419 100644
--- a/src/components/admin/LeadsGenerator.tsx
+++ b/src/components/admin/LeadsGenerator.tsx
@@ -35,8 +35,52 @@ export function LeadsGenerator() {
const [researchingLead, setResearchingLead] = useState(null);
const [emailDialog, setEmailDialog] = useState(null);
const [copiedField, setCopiedField] = useState(null);
+ const [recipient, setRecipient] = useState("");
+ const [sending, setSending] = useState(false);
const { toast } = useToast();
+ useEffect(() => {
+ setRecipient("");
+ }, [emailDialog?.company_name]);
+
+ const sendEmail = async () => {
+ if (!emailDialog || !recipient) return;
+ setSending(true);
+ try {
+ const res = await fetch(
+ `${import.meta.env.VITE_SUPABASE_URL}/functions/v1/send-outreach-email`,
+ {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY}`,
+ },
+ body: JSON.stringify({
+ to_email: recipient,
+ to_name: emailDialog.company_name,
+ subject: emailDialog.email.subject,
+ body: emailDialog.email.body,
+ }),
+ },
+ );
+ const data = await res.json();
+ if (!res.ok) throw new Error(data.error || "Kunde inte skicka mejl");
+ toast({
+ title: "Mejl skickat!",
+ description: `Skickat från konditorivaror@gmail.com till ${recipient}`,
+ });
+ setEmailDialog(null);
+ } catch (err) {
+ toast({
+ title: "Fel vid utskick",
+ description: err instanceof Error ? err.message : "Okänt fel",
+ variant: "destructive",
+ });
+ } finally {
+ setSending(false);
+ }
+ };
+
const generateLeads = async () => {
setIsLoading(true);
try {
From 2340592f282ca103458da6073df765b125ac02f2 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sat, 11 Jul 2026 13:49:50 +0000
Subject: [PATCH 4/4] Changes
Co-authored-by: wolfoftyreso-debug <250630591+wolfoftyreso-debug@users.noreply.github.com>
---
src/components/admin/LeadsGenerator.tsx | 26 +++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/src/components/admin/LeadsGenerator.tsx b/src/components/admin/LeadsGenerator.tsx
index 8c88419..e4ade47 100644
--- a/src/components/admin/LeadsGenerator.tsx
+++ b/src/components/admin/LeadsGenerator.tsx
@@ -363,6 +363,32 @@ export function LeadsGenerator() {
)}
+
+
+
+
+ setRecipient(e.target.value)}
+ disabled={sending}
+ />
+
+
+
+ Skickas från konditorivaror@gmail.com som Tiffany.
+
+
)}