diff --git a/.gitignore b/.gitignore index 83d3015..f053c4a 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ pnpm-debug.log* .cache/ cloned-websites/ storage/ +data/postgres/ # Estimation Engine Data data/crawls/ diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs index fcfdc73..88342d0 100644 --- a/apps/web/next.config.mjs +++ b/apps/web/next.config.mjs @@ -13,6 +13,7 @@ const nextConfig = { '@mintel/content-engine', '@mintel/concept-engine', '@mintel/estimation-engine', + '@mintel/payload-ai', '@mintel/pdf', 'canvas', 'sharp', diff --git a/apps/web/payload.config.ts b/apps/web/payload.config.ts index a4e3aac..f997992 100644 --- a/apps/web/payload.config.ts +++ b/apps/web/payload.config.ts @@ -23,7 +23,7 @@ import { CrmInteractions } from "./src/payload/collections/CrmInteractions"; import { CrmTopics } from "./src/payload/collections/CrmTopics"; import { Projects } from "./src/payload/collections/Projects"; -import { AiSettings } from "./src/payload/globals/AiSettings"; +import { AiSettings } from "@mintel/payload-ai"; const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); @@ -48,7 +48,7 @@ export default buildConfig({ CrmInteractions, Projects, ], - globals: [AiSettings], + globals: [AiSettings as any], email: nodemailerAdapter({ defaultFromAddress: process.env.MAIL_FROM || "info@mintel.me", defaultFromName: "Mintel.me", diff --git a/apps/web/src/payload/actions/generateField.ts b/apps/web/src/payload/actions/generateField.ts deleted file mode 100644 index 41c7845..0000000 --- a/apps/web/src/payload/actions/generateField.ts +++ /dev/null @@ -1,191 +0,0 @@ -"use server"; - -import { config } from "../../../content-engine.config"; -import { getPayloadHMR } from "@payloadcms/next/utilities"; -import configPromise from "@payload-config"; -import * as fs from "node:fs/promises"; -import * as path from "node:path"; -import * as os from "node:os"; - -async function getOrchestrator() { - const OPENROUTER_KEY = - process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY; - const REPLICATE_KEY = process.env.REPLICATE_API_KEY; - - if (!OPENROUTER_KEY) { - throw new Error( - "Missing OPENROUTER_API_KEY in .env (Required for AI generation)", - ); - } - - const importDynamic = new Function("modulePath", "return import(modulePath)"); - const { AiBlogPostOrchestrator } = await importDynamic( - "@mintel/content-engine", - ); - - return new AiBlogPostOrchestrator({ - apiKey: OPENROUTER_KEY, - replicateApiKey: REPLICATE_KEY, - model: "google/gemini-3-flash-preview", - }); -} - -export async function generateSlugAction( - title: string, - draftContent: string, - oldSlug?: string, - instructions?: string, -) { - try { - const orchestrator = await getOrchestrator(); - const newSlug = await orchestrator.generateSlug( - draftContent, - title, - instructions, - ); - - if (oldSlug && oldSlug !== newSlug) { - const payload = await getPayloadHMR({ config: configPromise }); - await payload.create({ - collection: "redirects", - data: { - from: oldSlug, - to: newSlug, - }, - }); - } - - return { success: true, slug: newSlug }; - } catch (e: any) { - return { success: false, error: e.message }; - } -} - -export async function generateThumbnailAction( - draftContent: string, - title?: string, - instructions?: string, -) { - try { - const payload = await getPayloadHMR({ config: configPromise }); - const OPENROUTER_KEY = - process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY; - const REPLICATE_KEY = process.env.REPLICATE_API_KEY; - - if (!OPENROUTER_KEY) { - throw new Error("Missing OPENROUTER_API_KEY in .env"); - } - if (!REPLICATE_KEY) { - throw new Error( - "Missing REPLICATE_API_KEY in .env (Required for Thumbnails)", - ); - } - - const importDynamic = new Function( - "modulePath", - "return import(modulePath)", - ); - const { AiBlogPostOrchestrator } = await importDynamic( - "@mintel/content-engine", - ); - const { ThumbnailGenerator } = await importDynamic( - "@mintel/thumbnail-generator", - ); - - const orchestrator = new AiBlogPostOrchestrator({ - apiKey: OPENROUTER_KEY, - replicateApiKey: REPLICATE_KEY, - model: "google/gemini-3-flash-preview", - }); - - const tg = new ThumbnailGenerator({ replicateApiKey: REPLICATE_KEY }); - - const prompt = await orchestrator.generateVisualPrompt( - draftContent || title || "Technology", - instructions, - ); - - const tmpPath = path.join(os.tmpdir(), `mintel-thumb-${Date.now()}.png`); - await tg.generateImage(prompt, tmpPath); - - const fileData = await fs.readFile(tmpPath); - const stat = await fs.stat(tmpPath); - const fileName = path.basename(tmpPath); - - const newMedia = await payload.create({ - collection: "media", - data: { - alt: title ? `Thumbnail for ${title}` : "AI Generated Thumbnail", - }, - file: { - data: fileData, - name: fileName, - mimetype: "image/png", - size: stat.size, - }, - }); - - // Cleanup temp file - await fs.unlink(tmpPath).catch(() => {}); - - return { success: true, mediaId: newMedia.id }; - } catch (e: any) { - return { success: false, error: e.message }; - } -} -export async function generateSingleFieldAction( - documentTitle: string, - documentContent: string, - fieldName: string, - fieldDescription: string, - instructions?: string, -) { - try { - const OPENROUTER_KEY = - process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY; - if (!OPENROUTER_KEY) throw new Error("Missing OPENROUTER_API_KEY"); - - const payload = await getPayloadHMR({ config: configPromise }); - - // Fetch context documents from DB - const contextDocsData = await payload.find({ - collection: "context-files", - limit: 100, - }); - const projectContext = contextDocsData.docs - .map((doc) => `--- ${doc.filename} ---\n${doc.content}`) - .join("\n\n"); - - const prompt = `You are an expert AI assistant perfectly trained for generating exact data values for CMS components. -PROJECT STRATEGY & CONTEXT: -${projectContext} - -DOCUMENT TITLE: ${documentTitle} -DOCUMENT DRAFT:\n${documentContent}\n -YOUR TASK: Generate the exact value for a specific field named "${fieldName}". -${fieldDescription ? `FIELD DESCRIPTION / CONSTRAINTS: ${fieldDescription}\n` : ""} -${instructions ? `EDITOR INSTRUCTIONS for this field: ${instructions}\n` : ""} -CRITICAL RULES: -1. Respond ONLY with the requested content value. -2. NO markdown wrapping blocks (like \`\`\`mermaid or \`\`\`html) around the output! Just the raw code or text. -3. If the field implies a diagram or flow, output RAW Mermaid.js code. -4. If it's standard text, write professional B2B German. No quotes, no conversational filler.`; - - const res = await fetch("https://openrouter.ai/api/v1/chat/completions", { - method: "POST", - headers: { - Authorization: `Bearer ${OPENROUTER_KEY}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - model: "google/gemini-3-flash-preview", - messages: [{ role: "user", content: prompt }], - }), - }); - const data = await res.json(); - const text = data.choices?.[0]?.message?.content?.trim() || ""; - return { success: true, text }; - } catch (e: any) { - return { success: false, error: e.message }; - } -} diff --git a/apps/web/src/payload/actions/optimizePost.ts b/apps/web/src/payload/actions/optimizePost.ts deleted file mode 100644 index 6019883..0000000 --- a/apps/web/src/payload/actions/optimizePost.ts +++ /dev/null @@ -1,88 +0,0 @@ -"use server"; - -import { config } from "../../../content-engine.config"; -import { revalidatePath } from "next/cache"; -import { parseMarkdownToLexical } from "../utils/lexicalParser"; -import { getPayloadHMR } from "@payloadcms/next/utilities"; -import configPromise from "@payload-config"; - -export async function optimizePostText( - draftContent: string, - instructions?: string, -) { - try { - const payload = await getPayloadHMR({ config: configPromise }); - const globalAiSettings = await payload.findGlobal({ slug: "ai-settings" }); - const customSources = - globalAiSettings?.customSources?.map((s: any) => s.sourceName) || []; - - const OPENROUTER_KEY = - process.env.OPENROUTER_KEY || process.env.OPENROUTER_API_KEY; - const REPLICATE_KEY = process.env.REPLICATE_API_KEY; - - if (!OPENROUTER_KEY) { - throw new Error( - "OPENROUTER_KEY or OPENROUTER_API_KEY not found in environment.", - ); - } - - const importDynamic = new Function( - "modulePath", - "return import(modulePath)", - ); - const { AiBlogPostOrchestrator } = await importDynamic( - "@mintel/content-engine", - ); - - const orchestrator = new AiBlogPostOrchestrator({ - apiKey: OPENROUTER_KEY, - replicateApiKey: REPLICATE_KEY, - model: "google/gemini-3-flash-preview", - }); - - // Fetch context documents purely from DB - const contextDocsData = await payload.find({ - collection: "context-files", - limit: 100, - }); - const projectContext = contextDocsData.docs.map((doc) => doc.content); - - const optimizedMarkdown = await orchestrator.optimizeDocument({ - content: draftContent, - projectContext, - availableComponents: config.components, - instructions, - internalLinks: [], - customSources, - }); - - // The orchestrator currently returns Markdown + JSX tags. - // We convert this mixed string into a basic Lexical AST map. - - if (!optimizedMarkdown || typeof optimizedMarkdown !== "string") { - throw new Error("AI returned invalid markup."); - } - - const blocks = parseMarkdownToLexical(optimizedMarkdown); - - return { - success: true, - lexicalAST: { - root: { - type: "root", - format: "", - indent: 0, - version: 1, - children: blocks, - direction: "ltr", - }, - }, - }; - } catch (error: any) { - console.error("Failed to optimize post:", error); - return { - success: false, - error: error.message || "An unknown error occurred during optimization.", - }; - } -} diff --git a/apps/web/src/payload/blocks/ArticleBlockquoteBlock.ts b/apps/web/src/payload/blocks/ArticleBlockquoteBlock.ts index 20f7649..4f4e3fc 100644 --- a/apps/web/src/payload/blocks/ArticleBlockquoteBlock.ts +++ b/apps/web/src/payload/blocks/ArticleBlockquoteBlock.ts @@ -25,7 +25,7 @@ export const ArticleBlockquoteBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den mehrzeiligen Text für quote ein.", @@ -37,7 +37,7 @@ export const ArticleBlockquoteBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für author ein.", @@ -49,7 +49,7 @@ export const ArticleBlockquoteBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für role ein.", diff --git a/apps/web/src/payload/blocks/ArticleMemeBlock.ts b/apps/web/src/payload/blocks/ArticleMemeBlock.ts index c4a38ce..0a00669 100644 --- a/apps/web/src/payload/blocks/ArticleMemeBlock.ts +++ b/apps/web/src/payload/blocks/ArticleMemeBlock.ts @@ -32,7 +32,7 @@ export const ArticleMemeBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für alt ein.", @@ -44,7 +44,7 @@ export const ArticleMemeBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für caption ein.", diff --git a/apps/web/src/payload/blocks/ArticleQuoteBlock.ts b/apps/web/src/payload/blocks/ArticleQuoteBlock.ts index 099d22d..401f088 100644 --- a/apps/web/src/payload/blocks/ArticleQuoteBlock.ts +++ b/apps/web/src/payload/blocks/ArticleQuoteBlock.ts @@ -26,7 +26,7 @@ export const ArticleQuoteBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den mehrzeiligen Text für quote ein.", @@ -39,7 +39,7 @@ export const ArticleQuoteBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für author ein.", @@ -51,7 +51,7 @@ export const ArticleQuoteBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für role ein.", @@ -63,7 +63,7 @@ export const ArticleQuoteBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für source ein.", @@ -75,7 +75,7 @@ export const ArticleQuoteBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für sourceUrl ein.", diff --git a/apps/web/src/payload/blocks/BoldNumberBlock.ts b/apps/web/src/payload/blocks/BoldNumberBlock.ts index 1d98a87..d10dba9 100644 --- a/apps/web/src/payload/blocks/BoldNumberBlock.ts +++ b/apps/web/src/payload/blocks/BoldNumberBlock.ts @@ -26,7 +26,7 @@ export const BoldNumberBlock: MintelBlock = { description: "e.g. 53% or 2.5M€", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, }, @@ -38,7 +38,7 @@ export const BoldNumberBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für label ein.", @@ -50,7 +50,7 @@ export const BoldNumberBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für source ein.", @@ -62,7 +62,7 @@ export const BoldNumberBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für sourceUrl ein.", diff --git a/apps/web/src/payload/blocks/ButtonBlock.ts b/apps/web/src/payload/blocks/ButtonBlock.ts index 86ee4f9..962c405 100644 --- a/apps/web/src/payload/blocks/ButtonBlock.ts +++ b/apps/web/src/payload/blocks/ButtonBlock.ts @@ -26,7 +26,7 @@ export const ButtonBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für label ein.", diff --git a/apps/web/src/payload/blocks/CarouselBlock.ts b/apps/web/src/payload/blocks/CarouselBlock.ts index bf78daa..0269563 100644 --- a/apps/web/src/payload/blocks/CarouselBlock.ts +++ b/apps/web/src/payload/blocks/CarouselBlock.ts @@ -35,7 +35,7 @@ export const CarouselBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für caption ein.", diff --git a/apps/web/src/payload/blocks/ComparisonRowBlock.ts b/apps/web/src/payload/blocks/ComparisonRowBlock.ts index 5c1060c..1ad71a4 100644 --- a/apps/web/src/payload/blocks/ComparisonRowBlock.ts +++ b/apps/web/src/payload/blocks/ComparisonRowBlock.ts @@ -31,7 +31,7 @@ export const ComparisonRowBlock: MintelBlock = { description: "Optional overarching description for the comparison.", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, }, @@ -44,7 +44,7 @@ export const ComparisonRowBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für negativeLabel ein.", @@ -57,7 +57,7 @@ export const ComparisonRowBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für negativeText ein.", @@ -71,7 +71,7 @@ export const ComparisonRowBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für positiveLabel ein.", @@ -84,7 +84,7 @@ export const ComparisonRowBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für positiveText ein.", diff --git a/apps/web/src/payload/blocks/DiagramFlowBlock.ts b/apps/web/src/payload/blocks/DiagramFlowBlock.ts index 4b41d2c..cb427e7 100644 --- a/apps/web/src/payload/blocks/DiagramFlowBlock.ts +++ b/apps/web/src/payload/blocks/DiagramFlowBlock.ts @@ -25,7 +25,7 @@ export const DiagramFlowBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den mehrzeiligen Text für definition ein.", diff --git a/apps/web/src/payload/blocks/DiagramGanttBlock.ts b/apps/web/src/payload/blocks/DiagramGanttBlock.ts index 16c6190..b1872a1 100644 --- a/apps/web/src/payload/blocks/DiagramGanttBlock.ts +++ b/apps/web/src/payload/blocks/DiagramGanttBlock.ts @@ -25,7 +25,7 @@ export const DiagramGanttBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den mehrzeiligen Text für definition ein.", diff --git a/apps/web/src/payload/blocks/DiagramPieBlock.ts b/apps/web/src/payload/blocks/DiagramPieBlock.ts index e6b5481..af7fd5f 100644 --- a/apps/web/src/payload/blocks/DiagramPieBlock.ts +++ b/apps/web/src/payload/blocks/DiagramPieBlock.ts @@ -24,7 +24,7 @@ export const DiagramPieBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den mehrzeiligen Text für definition ein.", diff --git a/apps/web/src/payload/blocks/DiagramSequenceBlock.ts b/apps/web/src/payload/blocks/DiagramSequenceBlock.ts index c35836f..e32dc2f 100644 --- a/apps/web/src/payload/blocks/DiagramSequenceBlock.ts +++ b/apps/web/src/payload/blocks/DiagramSequenceBlock.ts @@ -26,7 +26,7 @@ export const DiagramSequenceBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den mehrzeiligen Text für definition ein.", diff --git a/apps/web/src/payload/blocks/DiagramStateBlock.ts b/apps/web/src/payload/blocks/DiagramStateBlock.ts index 4a90258..0403615 100644 --- a/apps/web/src/payload/blocks/DiagramStateBlock.ts +++ b/apps/web/src/payload/blocks/DiagramStateBlock.ts @@ -25,7 +25,7 @@ export const DiagramStateBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den mehrzeiligen Text für definition ein.", diff --git a/apps/web/src/payload/blocks/DiagramTimelineBlock.ts b/apps/web/src/payload/blocks/DiagramTimelineBlock.ts index b79d7ca..e3f477b 100644 --- a/apps/web/src/payload/blocks/DiagramTimelineBlock.ts +++ b/apps/web/src/payload/blocks/DiagramTimelineBlock.ts @@ -26,7 +26,7 @@ export const DiagramTimelineBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den mehrzeiligen Text für definition ein.", diff --git a/apps/web/src/payload/blocks/ExternalLinkBlock.ts b/apps/web/src/payload/blocks/ExternalLinkBlock.ts index 071ce4d..52eb339 100644 --- a/apps/web/src/payload/blocks/ExternalLinkBlock.ts +++ b/apps/web/src/payload/blocks/ExternalLinkBlock.ts @@ -32,7 +32,7 @@ export const ExternalLinkBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für label ein.", diff --git a/apps/web/src/payload/blocks/H2Block.ts b/apps/web/src/payload/blocks/H2Block.ts index 3b4bc70..20d3f93 100644 --- a/apps/web/src/payload/blocks/H2Block.ts +++ b/apps/web/src/payload/blocks/H2Block.ts @@ -15,7 +15,7 @@ export const H2Block: MintelBlock = { description: "Geben Sie den Text für die H2-Überschrift ein.", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, }, diff --git a/apps/web/src/payload/blocks/H3Block.ts b/apps/web/src/payload/blocks/H3Block.ts index 4e85bcb..f759e8c 100644 --- a/apps/web/src/payload/blocks/H3Block.ts +++ b/apps/web/src/payload/blocks/H3Block.ts @@ -15,7 +15,7 @@ export const H3Block: MintelBlock = { description: "Geben Sie den Text für die H3-Überschrift ein.", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, }, diff --git a/apps/web/src/payload/blocks/HeadingBlock.ts b/apps/web/src/payload/blocks/HeadingBlock.ts index a2d4105..e7a4f72 100644 --- a/apps/web/src/payload/blocks/HeadingBlock.ts +++ b/apps/web/src/payload/blocks/HeadingBlock.ts @@ -25,7 +25,7 @@ export const HeadingBlock: MintelBlock = { description: "Der Text der Überschrift.", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, }, diff --git a/apps/web/src/payload/blocks/IconListBlock.ts b/apps/web/src/payload/blocks/IconListBlock.ts index b60b990..db697b8 100644 --- a/apps/web/src/payload/blocks/IconListBlock.ts +++ b/apps/web/src/payload/blocks/IconListBlock.ts @@ -44,7 +44,7 @@ export const IconListBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für title ein.", @@ -56,7 +56,7 @@ export const IconListBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den mehrzeiligen Text für description ein.", diff --git a/apps/web/src/payload/blocks/ImageTextBlock.ts b/apps/web/src/payload/blocks/ImageTextBlock.ts index 5a996bf..aaa8c85 100644 --- a/apps/web/src/payload/blocks/ImageTextBlock.ts +++ b/apps/web/src/payload/blocks/ImageTextBlock.ts @@ -32,7 +32,7 @@ export const ImageTextBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den mehrzeiligen Text für text ein.", diff --git a/apps/web/src/payload/blocks/LeadMagnetBlock.ts b/apps/web/src/payload/blocks/LeadMagnetBlock.ts index e748b2d..6a266ca 100644 --- a/apps/web/src/payload/blocks/LeadMagnetBlock.ts +++ b/apps/web/src/payload/blocks/LeadMagnetBlock.ts @@ -27,7 +27,7 @@ export const LeadMagnetBlock: MintelBlock = { description: "The strong headline for the Call-to-Action", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, }, @@ -40,7 +40,7 @@ export const LeadMagnetBlock: MintelBlock = { description: "The value proposition text.", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, }, @@ -53,7 +53,7 @@ export const LeadMagnetBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für buttonText ein.", diff --git a/apps/web/src/payload/blocks/LeadParagraphBlock.ts b/apps/web/src/payload/blocks/LeadParagraphBlock.ts index ecb9e6d..c7d08d1 100644 --- a/apps/web/src/payload/blocks/LeadParagraphBlock.ts +++ b/apps/web/src/payload/blocks/LeadParagraphBlock.ts @@ -26,7 +26,7 @@ export const LeadParagraphBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den mehrzeiligen Text für text ein.", diff --git a/apps/web/src/payload/blocks/MarkerBlock.ts b/apps/web/src/payload/blocks/MarkerBlock.ts index f5f2ab5..7b3a6e3 100644 --- a/apps/web/src/payload/blocks/MarkerBlock.ts +++ b/apps/web/src/payload/blocks/MarkerBlock.ts @@ -25,7 +25,7 @@ export const MarkerBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für text ein.", diff --git a/apps/web/src/payload/blocks/MemeCardBlock.ts b/apps/web/src/payload/blocks/MemeCardBlock.ts index c194a89..8ae1052 100644 --- a/apps/web/src/payload/blocks/MemeCardBlock.ts +++ b/apps/web/src/payload/blocks/MemeCardBlock.ts @@ -28,7 +28,7 @@ export const MemeCardBlock: MintelBlock = { "The template ID from memegen.link (e.g. 'drake', 'disastergirl')", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, }, @@ -42,7 +42,7 @@ export const MemeCardBlock: MintelBlock = { "Pipe-separated captions for the meme (e.g. 'Legacy Code|Mintel Stack'). Maximum 6 words per line.", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, }, diff --git a/apps/web/src/payload/blocks/MermaidBlock.ts b/apps/web/src/payload/blocks/MermaidBlock.ts index 9590548..8e20477 100644 --- a/apps/web/src/payload/blocks/MermaidBlock.ts +++ b/apps/web/src/payload/blocks/MermaidBlock.ts @@ -36,7 +36,7 @@ export const MermaidBlock: MintelBlock = { description: "Optional title displayed above the diagram.", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, }, @@ -59,7 +59,7 @@ export const MermaidBlock: MintelBlock = { "The raw Mermaid.js syntax (e.g. graph TD... shadowing, loops, etc.).", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, }, diff --git a/apps/web/src/payload/blocks/MetricBarBlock.ts b/apps/web/src/payload/blocks/MetricBarBlock.ts index 4def336..bf7306a 100644 --- a/apps/web/src/payload/blocks/MetricBarBlock.ts +++ b/apps/web/src/payload/blocks/MetricBarBlock.ts @@ -25,7 +25,7 @@ export const MetricBarBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für label ein.", @@ -50,7 +50,7 @@ export const MetricBarBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für unit ein.", diff --git a/apps/web/src/payload/blocks/ParagraphBlock.ts b/apps/web/src/payload/blocks/ParagraphBlock.ts index 256214d..a5d7250 100644 --- a/apps/web/src/payload/blocks/ParagraphBlock.ts +++ b/apps/web/src/payload/blocks/ParagraphBlock.ts @@ -26,7 +26,7 @@ export const ParagraphBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den mehrzeiligen Text für text ein.", diff --git a/apps/web/src/payload/blocks/PerformanceChartBlock.ts b/apps/web/src/payload/blocks/PerformanceChartBlock.ts index 3310a69..116e162 100644 --- a/apps/web/src/payload/blocks/PerformanceChartBlock.ts +++ b/apps/web/src/payload/blocks/PerformanceChartBlock.ts @@ -26,7 +26,7 @@ export const PerformanceChartBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für title ein.", diff --git a/apps/web/src/payload/blocks/PremiumComparisonChartBlock.ts b/apps/web/src/payload/blocks/PremiumComparisonChartBlock.ts index b3be1b1..79a8234 100644 --- a/apps/web/src/payload/blocks/PremiumComparisonChartBlock.ts +++ b/apps/web/src/payload/blocks/PremiumComparisonChartBlock.ts @@ -25,7 +25,7 @@ export const PremiumComparisonChartBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für title ein.", @@ -37,7 +37,7 @@ export const PremiumComparisonChartBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für subtitle ein.", @@ -54,7 +54,7 @@ export const PremiumComparisonChartBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für label ein.", @@ -82,7 +82,7 @@ export const PremiumComparisonChartBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für unit ein.", @@ -102,7 +102,7 @@ export const PremiumComparisonChartBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für description ein.", diff --git a/apps/web/src/payload/blocks/RevenueLossCalculatorBlock.ts b/apps/web/src/payload/blocks/RevenueLossCalculatorBlock.ts index 35d7838..89eaee1 100644 --- a/apps/web/src/payload/blocks/RevenueLossCalculatorBlock.ts +++ b/apps/web/src/payload/blocks/RevenueLossCalculatorBlock.ts @@ -25,7 +25,7 @@ export const RevenueLossCalculatorBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für title ein.", diff --git a/apps/web/src/payload/blocks/SectionBlock.ts b/apps/web/src/payload/blocks/SectionBlock.ts index 849f546..2b93833 100644 --- a/apps/web/src/payload/blocks/SectionBlock.ts +++ b/apps/web/src/payload/blocks/SectionBlock.ts @@ -25,7 +25,7 @@ export const SectionBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für title ein.", diff --git a/apps/web/src/payload/blocks/StatsDisplayBlock.ts b/apps/web/src/payload/blocks/StatsDisplayBlock.ts index 0b93c54..0585a33 100644 --- a/apps/web/src/payload/blocks/StatsDisplayBlock.ts +++ b/apps/web/src/payload/blocks/StatsDisplayBlock.ts @@ -25,7 +25,7 @@ export const StatsDisplayBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für label ein.", @@ -38,7 +38,7 @@ export const StatsDisplayBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für value ein.", @@ -50,7 +50,7 @@ export const StatsDisplayBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für subtext ein.", diff --git a/apps/web/src/payload/blocks/StatsGridBlock.ts b/apps/web/src/payload/blocks/StatsGridBlock.ts index d132110..0f72974 100644 --- a/apps/web/src/payload/blocks/StatsGridBlock.ts +++ b/apps/web/src/payload/blocks/StatsGridBlock.ts @@ -30,7 +30,7 @@ export const StatsGridBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für label ein.", @@ -43,7 +43,7 @@ export const StatsGridBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für value ein.", diff --git a/apps/web/src/payload/blocks/TLDRBlock.ts b/apps/web/src/payload/blocks/TLDRBlock.ts index 43350d9..c8343ba 100644 --- a/apps/web/src/payload/blocks/TLDRBlock.ts +++ b/apps/web/src/payload/blocks/TLDRBlock.ts @@ -26,7 +26,7 @@ export const TLDRBlock: MintelBlock = { description: "The summary content for the TLDR box.", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, }, diff --git a/apps/web/src/payload/blocks/TrackedLinkBlock.ts b/apps/web/src/payload/blocks/TrackedLinkBlock.ts index d43ebc2..322e71e 100644 --- a/apps/web/src/payload/blocks/TrackedLinkBlock.ts +++ b/apps/web/src/payload/blocks/TrackedLinkBlock.ts @@ -32,7 +32,7 @@ export const TrackedLinkBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für label ein.", diff --git a/apps/web/src/payload/blocks/WaterfallChartBlock.ts b/apps/web/src/payload/blocks/WaterfallChartBlock.ts index d182235..9bb08e4 100644 --- a/apps/web/src/payload/blocks/WaterfallChartBlock.ts +++ b/apps/web/src/payload/blocks/WaterfallChartBlock.ts @@ -27,7 +27,7 @@ export const WaterfallChartBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für title ein.", @@ -44,7 +44,7 @@ export const WaterfallChartBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für label ein.", diff --git a/apps/web/src/payload/blocks/WebVitalsScoreBlock.ts b/apps/web/src/payload/blocks/WebVitalsScoreBlock.ts index f95f465..6d12646 100644 --- a/apps/web/src/payload/blocks/WebVitalsScoreBlock.ts +++ b/apps/web/src/payload/blocks/WebVitalsScoreBlock.ts @@ -42,7 +42,7 @@ export const WebVitalsScoreBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für description ein.", diff --git a/apps/web/src/payload/blocks/YouTubeEmbedBlock.ts b/apps/web/src/payload/blocks/YouTubeEmbedBlock.ts index 47da6fd..878b0ec 100644 --- a/apps/web/src/payload/blocks/YouTubeEmbedBlock.ts +++ b/apps/web/src/payload/blocks/YouTubeEmbedBlock.ts @@ -31,7 +31,7 @@ export const YouTubeEmbedBlock: MintelBlock = { admin: { components: { afterInput: [ - "@/src/payload/components/FieldGenerators/AiFieldButton#AiFieldButton", + "@mintel/payload-ai/components/AiFieldButton#AiFieldButton", ], }, description: "Geben Sie den Text für title ein.", diff --git a/apps/web/src/payload/collections/Media.ts b/apps/web/src/payload/collections/Media.ts index 90f5111..9ea1587 100644 --- a/apps/web/src/payload/collections/Media.ts +++ b/apps/web/src/payload/collections/Media.ts @@ -1,6 +1,7 @@ import type { CollectionConfig } from "payload"; import path from "path"; import { fileURLToPath } from "url"; +import { replicateMediaHandler } from "@mintel/payload-ai"; const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); @@ -14,6 +15,13 @@ export const Media: CollectionConfig = { access: { read: () => true, // Publicly readable }, + endpoints: [ + { + path: "/:id/ai-process", + method: "post", + handler: replicateMediaHandler as any, + }, + ], upload: { staticDir: path.resolve(dirname, "../../../../public/media"), adminThumbnail: "thumbnail", @@ -39,6 +47,15 @@ export const Media: CollectionConfig = { ], }, fields: [ + { + name: "aiProcessButtons", + type: "ui", + admin: { + components: { + Field: "@mintel/payload-ai/components/AiMediaButtons#AiMediaButtons", + }, + }, + }, { name: "alt", type: "text", diff --git a/apps/web/src/payload/collections/Posts.ts b/apps/web/src/payload/collections/Posts.ts index a02a699..3c6120d 100644 --- a/apps/web/src/payload/collections/Posts.ts +++ b/apps/web/src/payload/collections/Posts.ts @@ -21,7 +21,7 @@ export const Posts: CollectionConfig = { admin: { position: "sidebar", components: { - Field: "@/src/payload/components/OptimizeButton#OptimizeButton", + Field: "@mintel/payload-ai/components/OptimizeButton#OptimizeButton", }, }, }, @@ -39,7 +39,7 @@ export const Posts: CollectionConfig = { position: "sidebar", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/GenerateSlugButton#GenerateSlugButton", + "@mintel/payload-ai/components/GenerateSlugButton#GenerateSlugButton", ], }, }, @@ -100,7 +100,7 @@ export const Posts: CollectionConfig = { position: "sidebar", components: { afterInput: [ - "@/src/payload/components/FieldGenerators/GenerateThumbnailButton#GenerateThumbnailButton", + "@mintel/payload-ai/components/GenerateThumbnailButton#GenerateThumbnailButton", ], }, }, diff --git a/apps/web/src/payload/components/FieldGenerators/AiFieldButton.tsx b/apps/web/src/payload/components/FieldGenerators/AiFieldButton.tsx deleted file mode 100644 index 292c341..0000000 --- a/apps/web/src/payload/components/FieldGenerators/AiFieldButton.tsx +++ /dev/null @@ -1,136 +0,0 @@ -"use client"; - -import React, { useState } from "react"; -import { useField, useDocumentInfo, useForm } from "@payloadcms/ui"; -import { generateSingleFieldAction } from "../../actions/generateField"; - -export function AiFieldButton({ path, field }: { path: string; field: any }) { - const [isGenerating, setIsGenerating] = useState(false); - const [instructions, setInstructions] = useState(""); - const [showInstructions, setShowInstructions] = useState(false); - - // Payload hooks - const { value, setValue } = useField({ path }); - const { title } = useDocumentInfo(); - const { fields } = useForm(); - - 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; - }; - - const handleGenerate = async (e: React.MouseEvent) => { - e.preventDefault(); - - const lexicalValue = fields?.content?.value as any; - const legacyValue = fields?.legacyMdx?.value as string; - let draftContent = legacyValue || ""; - if (!draftContent && lexicalValue?.root) { - draftContent = extractText(lexicalValue.root); - } - - setIsGenerating(true); - try { - // Field name is passed as a label usually, fallback to path - const fieldName = typeof field?.label === "string" ? field.label : path; - const fieldDescription = - typeof field?.admin?.description === "string" - ? field.admin.description - : ""; - - const res = await generateSingleFieldAction( - (title as string) || "", - draftContent, - fieldName, - fieldDescription, - instructions, - ); - if (res.success && res.text) { - setValue(res.text); - } else { - alert("Fehler: " + res.error); - } - } catch (e) { - alert("Fehler bei der Generierung."); - } finally { - setIsGenerating(false); - setShowInstructions(false); - } - }; - - return ( -
-
- - -
- {showInstructions && ( -