diff --git a/actions/settings.ts b/actions/settings.ts index 993eb7e4..262ae4ce 100644 --- a/actions/settings.ts +++ b/actions/settings.ts @@ -3,67 +3,20 @@ import { revalidatePath } from "next/cache"; import { z } from "zod"; import PocketBase from "pocketbase"; -import { Resend } from "resend"; -import { pbCreate, pbList, pbUpdate } from "@/lib/pocketbase"; +import { sendEmail } from "@/lib/email"; import { getAuth } from "@/lib/auth"; -import { pbId } from "@/lib/pb"; import { PB_URL } from "@/lib/pocketbase"; export type ActionResult = { ok: true } | { ok: false; error: string }; -const emailSettingsSchema = z.object({ - statement_from_email: z.string().email("Valid email").or(z.literal("")).optional(), - statement_cc_email: z.string().email("Valid email").or(z.literal("")).optional(), - statement_app_email: z.string().email("Valid email").or(z.literal("")).optional(), - statement_email_password: z.string().optional(), -}); - -function clean(obj: any) { - const out: any = {}; - for (const [k, v] of Object.entries(obj)) { - if (v === "" || v === null || v === undefined) continue; - out[k] = v; - } - return out; -} - -export async function updateEmailSettings(_: unknown, formData: FormData): Promise { - const { token } = await getAuth(); - if (!token) return { ok: false, error: "Not signed in" }; - const parsed = emailSettingsSchema.safeParse({ - statement_from_email: formData.get("statement_from_email") || "", - statement_cc_email: formData.get("statement_cc_email") || "", - statement_app_email: formData.get("statement_app_email") || "", - statement_email_password: formData.get("statement_email_password") || "", - }); - if (!parsed.success) return { ok: false, error: parsed.error.issues[0]?.message ?? "Invalid input" }; - try { - const settings = await pbList("fuel_settings", { perPage: 1 }, token); - if (settings.items.length === 0) return { ok: false, error: "Settings not configured" }; - await pbUpdate("fuel_settings", settings.items[0].id, clean(parsed.data), token); - } catch (err: any) { - return { ok: false, error: err?.message || "Failed to update" }; - } - revalidatePath("/settings"); - return { ok: true }; -} - export async function sendTestEmail(to: string): Promise { if (!to) return { ok: false, error: "Recipient email required" }; - const apiKey = process.env.RESEND_API_KEY; - if (!apiKey) return { ok: false, error: "RESEND_API_KEY not configured" }; - const fromAddr = process.env.RESEND_FROM || "statements@rjbulk.co.za"; try { - const resend = new Resend(apiKey); - const result = await resend.emails.send({ - from: fromAddr, + await sendEmail({ to, subject: "RJ Bulk test email", html: "

This is a test email from your RJ Bulk back-office. If you got this, email is working correctly.

", }); - if (result.error) { - return { ok: false, error: result.error.message || "Send failed" }; - } } catch (err: any) { return { ok: false, error: err?.message || "Send failed" }; } diff --git a/actions/statements.ts b/actions/statements.ts index ee669691..a165b82a 100644 --- a/actions/statements.ts +++ b/actions/statements.ts @@ -2,7 +2,7 @@ import { revalidatePath } from "next/cache"; import { z } from "zod"; -import { Resend } from "resend"; +import { sendEmail } from "@/lib/email"; import { pbCreate, pbList, pbUpdate } from "@/lib/pocketbase"; import { getAuth } from "@/lib/auth"; import { pbId } from "@/lib/pb"; @@ -105,20 +105,12 @@ export async function sendStatement(_: unknown, formData: FormData): Promise a + n.qty_litres_supplied, 0); const totalValue = notes.items.reduce((a, n) => a + (n.total_value || 0), 0); - const result = await resend.emails.send({ - from: fromAddr, + await sendEmail({ to: parsed.data.recipient_email, - cc, subject: `Delivery Statement — ${cust.name} (${start} to ${end})`, html: `

Hi ${cust.name},

Please find your delivery statement attached for the period ${start} to ${end}.

@@ -134,10 +126,6 @@ Total value: R ${totalValue.toFixed(2)}

], }); - if (result.error) { - return { ok: false, error: result.error.message || "Email send failed" }; - } - // Notes stay "Pending" — the customer pays separately (EFT / CASH / CARD). // We don't auto-flip the status here; the owner marks notes as paid manually.