Files
mintel.me/apps/web/src/payload/components/BulkMailButton.tsx
Marc Mintel 3f6fa36f9b
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 7s
Build & Deploy / 🏗️ Build (push) Failing after 6m25s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 QA (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
feat(crm): implement bulk mail action for contacts
- Add BulkMailButton component for Payload list view
- Add bulkMailEndpoint handler for processing prompts
- Integrate bulk mail action into CrmContacts collection
- Update dev:clean script to skip interactive prompts
2026-03-30 19:27:56 +02:00

148 lines
4.1 KiB
TypeScript

"use client";
import React, { useState } from "react";
import { useSelection, Button, toast } from "@payloadcms/ui";
export const BulkMailButton: React.FC = () => {
const { selected } = useSelection();
const [isOpen, setIsOpen] = useState(false);
const [prompt, setPrompt] = useState("");
const [isTest, setIsTest] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const selectedCount = Object.keys(selected).length;
if (selectedCount === 0) {
return null;
}
const handleBulkMail = async () => {
setIsLoading(true);
try {
const contactIds = Object.keys(selected);
const response = await fetch("/api/crm-contacts/bulk-mail", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
contactIds,
instructions: prompt,
isTest,
}),
});
const data = await response.json();
if (data.success) {
toast.success(
`Successfully sent emails to ${contactIds.length} contacts.`,
);
setIsOpen(false);
} else {
toast.error(`Failed: ${data.error}`);
}
} catch (e: any) {
toast.error(`An error occurred: ${e.message}`);
} finally {
setIsLoading(false);
}
};
return (
<div style={{ display: "inline-block", marginLeft: "1rem" }}>
<Button
onClick={() => setIsOpen(true)}
buttonStyle="primary"
size="small"
>
🤖 AI Bulk Mail ({selectedCount})
</Button>
{isOpen && (
<div
style={{
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(0,0,0,0.5)",
zIndex: 9999,
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<div
style={{
background: "var(--theme-elevation-100, #fff)",
color: "var(--theme-text, #000)",
padding: "2rem",
borderRadius: "8px",
width: "500px",
maxWidth: "90vw",
}}
>
<h2 style={{ marginTop: 0 }}>AI Bulk Mail</h2>
<p>
Generate and send personalized emails to {selectedCount} contacts.
</p>
<div style={{ marginBottom: "1rem" }}>
<label style={{ display: "block", marginBottom: "0.5rem" }}>
<strong>Prompt / Instructions for AI</strong>
</label>
<textarea
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="e.g. Mache ein Angebot für ein neues Messe-Design"
style={{
width: "100%",
height: "100px",
padding: "0.5rem",
borderRadius: "4px",
border: "1px solid var(--theme-elevation-400, #ccc)",
}}
/>
</div>
<div style={{ marginBottom: "1.5rem" }}>
<label
style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}
>
<input
type="checkbox"
checked={isTest}
onChange={(e) => setIsTest(e.target.checked)}
/>
<strong>Test Mode</strong> (Send all emails to yourself)
</label>
</div>
<div
style={{
display: "flex",
gap: "1rem",
justifyContent: "flex-end",
}}
>
<Button
buttonStyle="secondary"
onClick={() => setIsOpen(false)}
disabled={isLoading}
>
Cancel
</Button>
<Button onClick={handleBulkMail} disabled={isLoading || !prompt}>
{isLoading ? "Sending..." : "Send Emails"}
</Button>
</div>
</div>
</div>
)}
</div>
);
};