diff --git a/frontend/src/components/NavBarItem.tsx b/frontend/src/components/NavBarItem.tsx index eb155e3..fb0fca2 100644 --- a/frontend/src/components/NavBarItem.tsx +++ b/frontend/src/components/NavBarItem.tsx @@ -1,6 +1,5 @@ -import React, {useEffect, useRef} from 'react' +import React, { useEffect, useRef, useState } from 'react' import Link from 'next/link' -import { useState } from 'react' import { mdiChevronUp, mdiChevronDown } from '@mdi/js' import BaseDivider from './BaseDivider' import BaseIcon from './BaseIcon' diff --git a/frontend/src/layouts/Authenticated.tsx b/frontend/src/layouts/Authenticated.tsx index 1b9907d..73d8391 100644 --- a/frontend/src/layouts/Authenticated.tsx +++ b/frontend/src/layouts/Authenticated.tsx @@ -1,5 +1,4 @@ -import React, { ReactNode, useEffect } from 'react' -import { useState } from 'react' +import React, { ReactNode, useEffect, useState } from 'react' import jwt from 'jsonwebtoken'; import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js' import menuAside from '../menuAside' diff --git a/frontend/src/menuAside.ts b/frontend/src/menuAside.ts index 5900c30..bd18e16 100644 --- a/frontend/src/menuAside.ts +++ b/frontend/src/menuAside.ts @@ -8,6 +8,12 @@ const menuAside: MenuAsideItem[] = [ label: 'Dashboard', }, + { + href: '/tools-studio', + icon: icon.mdiRobotExcitedOutline, + label: 'DarkFactory Studio', + }, + { href: '/users/users-list', label: 'Users', diff --git a/frontend/src/pages/dashboard.tsx b/frontend/src/pages/dashboard.tsx index 8e6b92c..22400a4 100644 --- a/frontend/src/pages/dashboard.tsx +++ b/frontend/src/pages/dashboard.tsx @@ -105,6 +105,19 @@ const Dashboard = () => { main> {''} + +
+
+
+

DarkFactory.ai member cockpit

+

Crea paquetes dark y revisa tu historial de producción.

+

Accede rápido a generadores de ideas, guiones, títulos, CTAs, thumbnails, prompts MGX y voces IA. Los resultados se guardan en Tool runs cuando tu rol lo permite.

+
+ + Abrir Tools Studio + +
+
{hasPermission(currentUser, 'CREATE_ROLES') && state.style.linkColor); - - const title = 'DarkFactory.ai' - - // Fetch Pexels image/video - useEffect(() => { - async function fetchData() { - const image = await getPexelsImage(); - const video = await getPexelsVideo(); - setIllustrationImage(image); - setIllustrationVideo(video); - } - fetchData(); - }, []); - - const imageBlock = (image) => ( -
-
- - Photo by {image?.photographer} on Pexels - -
-
- ); - - const videoBlock = (video) => { - if (video?.video_files?.length > 0) { - return ( -
- -
- - Video by {video.user.name} on Pexels - -
-
) - } - }; - return ( -
+
- {getPageTitle('Starter Page')} + {getPageTitle('DarkFactory.ai')} + - -
- {contentType === 'image' && contentPosition !== 'background' - ? imageBlock(illustrationImage) - : null} - {contentType === 'video' && contentPosition !== 'background' - ? videoBlock(illustrationVideo) - : null} -
- - - -
-

This is a React.js/Node.js app generated by the Flatlogic Web App Generator

-

For guides and documentation please check - your local README.md and the Flatlogic documentation

+
+ +
+ +
+
+
+
+
+
+
+ 🤖 SaaS de contenido dark para monetizar sin aparecer +
+

+ Crea videos que pueden generar dinero{' '} + + {}sin mostrar tu rostro. + +

+

+ DarkFactory.ai usa inteligencia artificial para generar ideas, guiones, títulos, + CTAs, prompts MGX y paquetes de publicación para YouTube, TikTok y Reels en minutos. +

+
+ + Comenzar ahora + + + Probar gratis + +
+

+ Plan Pro desde R$29/mes · login incluido · historial de creaciones. +

- - - - - +
+
+
+
+

Factory Output

+

Pack viral listo

+
+ Activo +
+
+ {scriptExamples.map((item) => ( +
+

{item.label}

+

{item.title}

+

{item.copy}

+
+ ))} +
+
+
+
+
+ +
+
+
+

Suite de herramientas

+

De un tema a un paquete de publicación.

+

+ Ideas, guiones cortos y largos, títulos, CTAs, prompts para MGX, thumbnails y voz IA en una sola experiencia. +

+
+
+ {['🧠 Ideas de videos', '✍️ Guiones cortos/largos', '🎯 Títulos + CTAs', '🎬 Shorts automáticos', '🖼️ Thumbnails', '💬 Prompts MGX', '🔊 Voz IA', '📦 Packs semanales'].map((tool) => ( +
+ {tool} +
+ ))} +
+ + Abrir página de herramientas + +
+
+ +
+
+ {creatorResults.map(([value, label]) => ( +
+

{value}

+

{label}

+
+ ))} +
+
+ +
+
+
+

Monetización por suscripción

+

Elige tu plan y empieza a producir.

+
+
+ {plans.map((plan) => ( +
+
+

{plan.name}

+ {plan.badge} +
+

{plan.price}

+
    + {plan.benefits.map((benefit) => ( +
  • + + {benefit} +
  • + ))} +
+ + {plan.name === 'Free' ? 'Crear cuenta gratis' : 'Suscribirme ahora'} + +
+ ))} +
+
+
+ +
+
+

Sobre el proyecto

+

DarkFactory.ai convierte constancia en sistema.

+

+ Creamos una fábrica digital para creadores que quieren publicar más, probar más nichos y monetizar más rápido sin depender de cámara, edición pesada o bloqueo creativo. +

+
+
+
+ +
+
+

© 2026 DarkFactory.ai. Todos los derechos reservados.

+
+ Admin / Login + Política de privacidad + Términos + Soporte + YouTube + TikTok +
-
- -
-

© 2026 {title}. All rights reserved

- - Privacy Policy - -
- +
); } @@ -163,4 +255,3 @@ export default function Starter() { Starter.getLayout = function getLayout(page: ReactElement) { return {page}; }; - diff --git a/frontend/src/pages/tools-studio.tsx b/frontend/src/pages/tools-studio.tsx new file mode 100644 index 0000000..7fa7b5c --- /dev/null +++ b/frontend/src/pages/tools-studio.tsx @@ -0,0 +1,420 @@ +import * as icon from '@mdi/js'; +import Head from 'next/head'; +import React, { ReactElement } from 'react'; +import axios from 'axios'; +import BaseButton from '../components/BaseButton'; +import CardBox from '../components/CardBox'; +import LayoutAuthenticated from '../layouts/Authenticated'; +import SectionMain from '../components/SectionMain'; +import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'; +import { getPageTitle } from '../config'; +import { useAppDispatch, useAppSelector } from '../stores/hooks'; +import { aiResponse } from '../stores/openAiSlice'; + +type ToolId = 'ideas' | 'scripts' | 'titles' | 'shorts' | 'thumbnails' | 'mgx' | 'voice'; + +type GeneratedRun = { + id: string; + toolId: ToolId; + toolName: string; + topic: string; + platform: string; + style: string; + language: string; + result: string; + createdAt: string; + savedToApi: boolean; +}; + +const tools: Array<{ id: ToolId; icon: string; name: string; description: string; instruction: string }> = [ + { + id: 'ideas', + icon: '🧠', + name: 'Gerador de Ideias de Vídeos', + description: 'Ángulos virales y series de contenido para canales dark.', + instruction: 'Genera 10 ideas de videos dark con gancho, promesa, público objetivo y formato recomendado.', + }, + { + id: 'scripts', + icon: '✍️', + name: 'Gerador de Roteiros', + description: 'Guiones cortos y largos con hook, desarrollo, CTA y retención.', + instruction: 'Crea un guion completo para video dark con hook de 3 segundos, narrativa, escenas sugeridas y CTA final.', + }, + { + id: 'titles', + icon: '🎯', + name: 'Gerador de Títulos + CTAs', + description: 'Títulos magnéticos, CTAs y descripciones listas para publicar.', + instruction: 'Genera 12 títulos virales, 5 CTAs y 3 descripciones cortas optimizadas para conversión.', + }, + { + id: 'shorts', + icon: '🎬', + name: 'Gerador de Shorts Automáticos', + description: 'Estructuras rápidas para TikTok, Reels y YouTube Shorts.', + instruction: 'Diseña un short de 45 segundos con escenas, texto en pantalla, narración y ritmo de edición.', + }, + { + id: 'thumbnails', + icon: '🖼️', + name: 'Gerador de Thumbnails', + description: 'Conceptos visuales con copy, composición y contraste.', + instruction: 'Propón 6 ideas de thumbnails con texto corto, colores, foco visual y emoción dominante.', + }, + { + id: 'mgx', + icon: '💬', + name: 'Gerador de Prompts para MGX', + description: 'Prompts visuales para assets, escenas y B-roll generativo.', + instruction: 'Escribe prompts MGX detallados para generar escenas visuales coherentes con un canal dark.', + }, + { + id: 'voice', + icon: '🔊', + name: 'Sugestões de Voz IA', + description: 'Dirección de voz, ritmo, emoción y estilo de narración.', + instruction: 'Recomienda estilos de voz IA, ritmo, emoción, pausas y ejemplo de narración para el tema.', + }, +]; + +const styles = [ + { value: 'storytelling', label: 'Storytelling' }, + { value: 'listicle', label: 'Lista viral' }, + { value: 'tutorial', label: 'Tutorial' }, + { value: 'facts', label: 'Curiosidades' }, + { value: 'finance', label: 'Finanzas' }, + { value: 'motivational', label: 'Motivacional' }, + { value: 'horror', label: 'Horror / misterio' }, +]; + +const extractResponseText = (payload: any) => { + const output = payload?.output; + if (Array.isArray(output)) { + const message = output.find((item) => item?.type === 'message'); + const content = message?.content; + if (Array.isArray(content)) { + const textItem = content.find((item) => item?.type === 'output_text'); + if (textItem?.text) return textItem.text; + } + } + if (typeof payload?.text === 'string') return payload.text; + if (typeof payload?.data === 'string') return payload.data; + return JSON.stringify(payload, null, 2); +}; + +const buildPrompt = (toolName: string, instruction: string, topic: string, platform: string, style: string, language: string) => ` +Herramienta: ${toolName} +Tema: ${topic} +Plataforma: ${platform} +Estilo de contenido: ${style} +Idioma de salida: ${language} + +Instrucciones: +${instruction} + +Devuelve el resultado en formato práctico, con secciones claras, bullets accionables y texto listo para copiar/pegar. Mantén un tono directo, persuasivo y orientado a creadores que quieren monetizar canales dark sin mostrar el rostro. +`; + +const localStorageKey = 'darkfactory_tool_runs'; + +const ToolsStudio = () => { + const dispatch = useAppDispatch(); + const { isAskingResponse, errorMessage } = useAppSelector((state) => state.openAi); + const [activeToolId, setActiveToolId] = React.useState('ideas'); + const [topic, setTopic] = React.useState('Finanzas personales para jóvenes que quieren ganar dinero con IA'); + const [platform, setPlatform] = React.useState('multi'); + const [style, setStyle] = React.useState('storytelling'); + const [language, setLanguage] = React.useState('es'); + const [validationError, setValidationError] = React.useState(''); + const [saveNotice, setSaveNotice] = React.useState(''); + const [runs, setRuns] = React.useState([]); + const [selectedRunId, setSelectedRunId] = React.useState(''); + + const activeTool = tools.find((tool) => tool.id === activeToolId) || tools[0]; + const selectedRun = runs.find((run) => run.id === selectedRunId) || runs[0]; + + React.useEffect(() => { + try { + const storedRuns = window.localStorage.getItem(localStorageKey); + if (storedRuns) { + const parsedRuns = JSON.parse(storedRuns); + if (Array.isArray(parsedRuns)) { + setRuns(parsedRuns); + setSelectedRunId(parsedRuns[0]?.id || ''); + } + } + } catch (error) { + console.error('Failed to load DarkFactory local history', error); + setSaveNotice('No se pudo cargar el historial local. Revisa la consola para más detalles.'); + } + }, []); + + const persistRuns = (nextRuns: GeneratedRun[]) => { + window.localStorage.setItem(localStorageKey, JSON.stringify(nextRuns.slice(0, 12))); + setRuns(nextRuns.slice(0, 12)); + }; + + const generate = async () => { + const cleanTopic = topic.trim(); + setValidationError(''); + setSaveNotice(''); + + if (cleanTopic.length < 5) { + setValidationError('Escribe un tema con al menos 5 caracteres para generar un resultado útil.'); + return; + } + + const prompt = buildPrompt(activeTool.name, activeTool.instruction, cleanTopic, platform, style, language); + const response = await dispatch( + aiResponse({ + input: [ + { + role: 'system', + content: 'Eres DarkFactory.ai, un asistente experto en contenido dark, retención, monetización y guiones para YouTube, TikTok y Reels.', + }, + { role: 'user', content: prompt }, + ], + options: { poll_interval: 5, poll_timeout: 300 }, + }), + ).unwrap(); + + const resultText = extractResponseText(response); + let savedToApi = false; + + try { + await axios.post('/tool_runs', { + data: { + platform, + content_style: style, + language, + topic: cleanTopic, + audience: 'Creadores de canales dark que quieren monetizar con contenido sin rostro', + tone: 'Directo, persuasivo y accionable', + desired_length_seconds: activeToolId === 'shorts' ? 45 : 120, + prompt_text: prompt, + result_text: resultText, + status: 'succeeded', + requested_at: new Date().toISOString(), + completed_at: new Date().toISOString(), + estimated_cost: 0, + }, + }); + savedToApi = true; + setSaveNotice('Resultado generado y guardado en Tool runs.'); + } catch (error) { + console.error('DarkFactory result generated but API history save failed', error); + setSaveNotice('Resultado generado. No se pudo guardar en la base de datos; quedó guardado en este navegador.'); + } + + const nextRun: GeneratedRun = { + id: `${Date.now()}`, + toolId: activeTool.id, + toolName: activeTool.name, + topic: cleanTopic, + platform, + style, + language, + result: resultText, + createdAt: new Date().toISOString(), + savedToApi, + }; + const nextRuns = [nextRun, ...runs]; + persistRuns(nextRuns); + setSelectedRunId(nextRun.id); + }; + + const downloadSelected = () => { + if (!selectedRun) return; + const blob = new Blob([selectedRun.result], { type: 'text/plain;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const anchor = document.createElement('a'); + anchor.href = url; + anchor.download = `darkfactory-${selectedRun.toolId}-${selectedRun.id}.txt`; + anchor.click(); + URL.revokeObjectURL(url); + }; + + return ( + <> + + {getPageTitle('DarkFactory Tools Studio')} + + + + + + +
+
+
+
+
+
+

Generación con IA

+

+ Escribe un tema. Recibe un paquete listo para publicar. +

+

+ Elige una herramienta, define plataforma y estilo, genera contenido y guarda el historial para descargar paquetes después. +

+ +
+ {tools.map((tool) => ( + + ))} +
+
+
+ +
+ +
+
+ {activeTool.icon} +
+

{activeTool.name}

+

{activeTool.description}

+
+
+ + +