All checks were successful
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧪 Test (push) Successful in 1m20s
Monorepo Pipeline / 🏗️ Build (push) Successful in 3m22s
Monorepo Pipeline / 🧹 Lint (push) Successful in 3m33s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
141 lines
5.5 KiB
TypeScript
141 lines
5.5 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect } from "react";
|
|
import { useForm, useDocumentInfo } from "@payloadcms/ui";
|
|
import { Button } from "@payloadcms/ui";
|
|
|
|
export function OptimizeButton() {
|
|
const [isOptimizing, setIsOptimizing] = useState(false);
|
|
const [instructions, setInstructions] = useState("");
|
|
|
|
useEffect(() => {
|
|
if (!isOptimizing) return;
|
|
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
|
e.preventDefault();
|
|
e.returnValue =
|
|
"Lexical Block-Optimierung läuft noch (dies dauert bis zu 45 Sekunden). Wenn Sie neu laden, bricht der Vorgang ab!";
|
|
};
|
|
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
return () => window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
}, [isOptimizing]);
|
|
|
|
const { fields, setModified, replaceState } = useForm();
|
|
const { title } = useDocumentInfo();
|
|
|
|
const handleOptimize = async () => {
|
|
// ... gathering draftContent logic
|
|
const lexicalValue = fields?.content?.value as any;
|
|
const legacyValue = fields?.legacyMdx?.value as string;
|
|
|
|
let draftContent = legacyValue || "";
|
|
|
|
const extractText = (lexicalRoot: any): string => {
|
|
if (!lexicalRoot) return "";
|
|
let text = "";
|
|
const iterate = (node: any) => {
|
|
if (node.text) text += node.text + " ";
|
|
if (node.children) node.children.forEach(iterate);
|
|
};
|
|
iterate(lexicalRoot);
|
|
return text;
|
|
};
|
|
|
|
if (!draftContent && lexicalValue?.root) {
|
|
draftContent = extractText(lexicalValue.root);
|
|
}
|
|
|
|
if (!draftContent || draftContent.trim().length < 50) {
|
|
alert(
|
|
"Der Entwurf ist zu kurz. Bitte tippe zuerst ein paar Stichpunkte oder einen Rohling ein.",
|
|
);
|
|
return;
|
|
}
|
|
|
|
setIsOptimizing(true);
|
|
try {
|
|
// 2. We inject the title so the AI knows what it's writing about
|
|
const payloadText = `---\ntitle: "${title}"\n---\n\n${draftContent}`;
|
|
|
|
const res = await fetch("/api/api/mintel-ai/optimize", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ draftContent: payloadText, instructions }),
|
|
});
|
|
const response = await res.json();
|
|
|
|
if (response.success && response.lexicalAST) {
|
|
// 3. Inject the new Lexical AST directly into the field form state
|
|
// We use Payload's useForm hook replacing the value of the 'content' field.
|
|
|
|
replaceState({
|
|
...fields,
|
|
content: {
|
|
...fields.content,
|
|
value: response.lexicalAST,
|
|
initialValue: response.lexicalAST,
|
|
},
|
|
});
|
|
|
|
setModified(true);
|
|
alert(
|
|
"🎉 Artikel wurde erfolgreich von der AI optimiert und mit Lexical Components angereichert.",
|
|
);
|
|
} else {
|
|
alert("❌ Fehler: " + response.error);
|
|
}
|
|
} catch (error) {
|
|
console.error("Optimization failed:", error);
|
|
alert("Ein unerwarteter Fehler ist aufgetreten.");
|
|
} finally {
|
|
setIsOptimizing(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="mb-8 p-4 bg-slate-50 border border-slate-200 rounded-md">
|
|
<h3 className="text-sm font-semibold mb-2">AI Post Optimizer</h3>
|
|
<p className="text-xs text-slate-500 mb-4">
|
|
Lass Mintel AI deinen Text-Rohentwurf analysieren und automatisch in
|
|
einen voll formatierten Lexical Artikel mit passenden B2B Komponenten
|
|
(MemeCards, Mermaids) umwandeln.
|
|
</p>
|
|
<textarea
|
|
value={instructions}
|
|
onChange={(e) => setInstructions(e.target.value)}
|
|
placeholder="Optionale Anweisungen an die AI (z.B. 'schreibe etwas lockerer' oder 'fokussiere dich auf SEO')..."
|
|
disabled={isOptimizing}
|
|
style={{
|
|
width: "100%",
|
|
minHeight: "60px",
|
|
padding: "8px 12px",
|
|
fontSize: "14px",
|
|
borderRadius: "4px",
|
|
border: "1px solid var(--theme-elevation-200)",
|
|
background: "var(--theme-elevation-50)",
|
|
color: "var(--theme-text)",
|
|
marginBottom: "16px",
|
|
}}
|
|
/>
|
|
<button
|
|
type="button"
|
|
onClick={handleOptimize}
|
|
disabled={isOptimizing}
|
|
className="btn btn--icon-style-none btn--size-medium mt-4"
|
|
style={{
|
|
background: "var(--theme-elevation-150)",
|
|
border: "1px solid var(--theme-elevation-200)",
|
|
color: "var(--theme-text)",
|
|
boxShadow: "0 2px 4px rgba(0,0,0,0.05)",
|
|
transition: "all 0.2s ease",
|
|
opacity: isOptimizing ? 0.7 : 1,
|
|
cursor: isOptimizing ? "not-allowed" : "pointer",
|
|
}}
|
|
>
|
|
<span className="btn__content" style={{ fontWeight: 600 }}>
|
|
{isOptimizing ? "✨ AI arbeitet (ca 30s)..." : "✨ Jetzt optimieren"}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|