feat: rediseño UI minimalista con tema oscuro
Migración completa del frontend: nuevo sistema de tokens semánticos (canvas, surface, ink, accent), tipografía Bricolage Grotesque + Outfit, paleta oscura coherente con badges de plataforma adaptados. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@ -1,13 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es" class="dark">
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Guiones IA</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,300;12..96,400;12..96,500;12..96,600;12..96,700;12..96,800&family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
||||
</head>
|
||||
<body class="text-on-surface antialiased overflow-x-hidden font-body bg-surface selection:bg-primary-container selection:text-on-primary-container">
|
||||
<body class="text-ink antialiased overflow-x-hidden font-body bg-canvas selection:bg-accent/20 selection:text-accent">
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
<template>
|
||||
<div class="bg-surface min-h-screen text-on-surface selection:bg-primary/30">
|
||||
<div class="bg-canvas min-h-screen text-ink selection:bg-accent/20 selection:text-accent">
|
||||
<SideNavBar />
|
||||
<TopAppBar />
|
||||
|
||||
<!-- Main Content Canvas -->
|
||||
<main class="ml-64 pt-24 pb-12 px-8 min-h-screen relative">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="fade" mode="out-in">
|
||||
<component :is="Component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
<!-- Área principal -->
|
||||
<main class="ml-60 pt-16 pb-12 px-8 min-h-screen">
|
||||
<div class="max-w-7xl mx-auto pt-8">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="fade" mode="out-in">
|
||||
<component :is="Component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
@ -22,7 +24,7 @@ import TopAppBar from './components/TopAppBar.vue'
|
||||
<style>
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.2s ease;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
|
||||
@ -1,9 +1,20 @@
|
||||
<template>
|
||||
<div class="flex items-center gap-3 p-2.5 rounded-xl border transition-colors" :class="active ? 'bg-indigo-500/10 border-indigo-500/30' : 'bg-surface-container-lowest border-white/5'">
|
||||
<div class="w-4 h-4 rounded-full flex items-center justify-center shrink-0" :class="active ? 'bg-indigo-500/20 text-indigo-400' : 'bg-white/5 text-transparent'">
|
||||
<span v-if="active" class="material-symbols-outlined text-[12px] font-bold">check</span>
|
||||
<div
|
||||
class="flex items-center gap-2.5 p-2.5 rounded-lg border transition-colors"
|
||||
:class="active
|
||||
? 'bg-accent-subtle border-accent-border'
|
||||
: 'bg-surface-muted border-border'"
|
||||
>
|
||||
<div
|
||||
class="w-4 h-4 rounded-full flex items-center justify-center shrink-0 transition-colors"
|
||||
:class="active ? 'bg-accent text-white' : 'bg-border text-transparent'"
|
||||
>
|
||||
<span v-if="active" class="material-symbols-outlined text-[11px]" style="font-variation-settings:'FILL' 1;">check</span>
|
||||
</div>
|
||||
<span class="text-xs font-bold tracking-wide" :class="active ? 'text-white' : 'text-outline'">{{ label }}</span>
|
||||
<span
|
||||
class="text-xs font-medium tracking-wide transition-colors"
|
||||
:class="active ? 'text-accent' : 'text-ink-3'"
|
||||
>{{ label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,18 +1,21 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-between border-b border-white/5 pb-2 last:border-0 last:pb-0">
|
||||
<span class="text-[10px] font-bold text-outline uppercase tracking-wider">{{ label }}</span>
|
||||
<div class="flex items-center justify-between py-2 border-b border-border last:border-0">
|
||||
<span class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider">{{ label }}</span>
|
||||
|
||||
<template v-if="type === 'boolean'">
|
||||
<span :class="value ? 'text-primary' : 'text-outline/40'" class="text-xs font-bold uppercase tracking-widest">
|
||||
{{ value ? 'SÍ' : 'NO' }}
|
||||
</span>
|
||||
<span
|
||||
class="text-xs font-semibold uppercase tracking-wider"
|
||||
:class="value ? 'text-success' : 'text-ink-3'"
|
||||
>{{ value ? 'Sí' : 'No' }}</span>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<span v-if="value" class="text-xs font-medium text-right max-w-[150px] truncate" :class="highlight ? 'text-white' : 'text-on-surface-variant'">
|
||||
{{ value }}
|
||||
</span>
|
||||
<span v-else class="text-gray-600 text-xs">—</span>
|
||||
<span
|
||||
v-if="value"
|
||||
class="text-xs font-medium text-right max-w-[160px] truncate"
|
||||
:class="highlight ? 'text-ink font-semibold' : 'text-ink-2'"
|
||||
>{{ value }}</span>
|
||||
<span v-else class="text-xs text-ink-3">—</span>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@ -23,7 +26,7 @@ defineProps({
|
||||
value: [String, Number, Boolean],
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text' // o 'boolean'
|
||||
default: 'text'
|
||||
},
|
||||
highlight: Boolean
|
||||
})
|
||||
|
||||
@ -1,40 +1,65 @@
|
||||
<template>
|
||||
<aside class="fixed left-0 top-0 h-full z-40 flex flex-col p-4 w-64 border-r border-white/5 bg-[#13131a] font-['Manrope'] antialiased shadow-2xl shadow-indigo-500/10">
|
||||
<!-- Branding -->
|
||||
<div class="flex items-center gap-3 mb-10 px-2">
|
||||
<div class="w-8 h-8 rounded bg-gradient-to-br from-primary-container to-primary flex items-center justify-center">
|
||||
<span class="material-symbols-outlined text-on-primary-container text-lg" style="font-variation-settings: 'FILL' 1;">psychology</span>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-xl font-bold tracking-tight text-white">Guiones IA</h1>
|
||||
<p class="text-[10px] uppercase tracking-widest text-primary/60 font-bold">Marketing Pro</p>
|
||||
<aside class="fixed left-0 top-0 h-full z-40 flex flex-col w-60 bg-surface border-r border-border font-body">
|
||||
|
||||
<!-- Logo -->
|
||||
<div class="px-5 py-5 border-b border-border">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<div class="w-8 h-8 rounded-lg bg-accent flex items-center justify-center shrink-0">
|
||||
<span class="material-symbols-outlined text-white text-[18px]" style="font-variation-settings:'FILL' 1;">psychology</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-[15px] font-semibold text-ink leading-none font-headline">Guiones IA</p>
|
||||
<p class="text-[10px] text-ink-3 mt-0.5">Marketing Pro</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navegación -->
|
||||
<nav class="flex-1 space-y-1">
|
||||
<router-link to="/" class="flex items-center gap-3 px-3 py-2.5 text-[#c7c4d7] hover:text-white hover:bg-white/5 transition-all duration-200 group relative" active-class="bg-white/10 !text-white rounded-lg font-semibold">
|
||||
<nav class="flex-1 px-3 py-4 space-y-0.5">
|
||||
<router-link
|
||||
to="/"
|
||||
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-ink-2 hover:bg-surface-muted hover:text-ink transition-all text-sm font-medium"
|
||||
active-class="bg-accent-subtle !text-accent font-semibold"
|
||||
>
|
||||
<span class="material-symbols-outlined text-[20px]">dashboard</span>
|
||||
<span class="text-sm font-medium">Dashboard</span>
|
||||
<span>Dashboard</span>
|
||||
</router-link>
|
||||
<router-link to="/analysis" class="flex items-center gap-3 px-3 py-2.5 text-[#c7c4d7] hover:text-white hover:bg-white/5 transition-all duration-200 group relative" active-class="bg-white/10 !text-white rounded-lg font-semibold">
|
||||
|
||||
<router-link
|
||||
to="/analysis"
|
||||
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-ink-2 hover:bg-surface-muted hover:text-ink transition-all text-sm font-medium"
|
||||
active-class="bg-accent-subtle !text-accent font-semibold"
|
||||
>
|
||||
<span class="material-symbols-outlined text-[20px]">analytics</span>
|
||||
<span class="text-sm font-medium">Análisis</span>
|
||||
<span>Análisis</span>
|
||||
</router-link>
|
||||
<router-link to="/scripts" class="flex items-center gap-3 px-3 py-2.5 text-[#c7c4d7] hover:text-white hover:bg-white/5 transition-all duration-200 group relative" active-class="bg-white/10 !text-white rounded-lg font-semibold">
|
||||
|
||||
<router-link
|
||||
to="/scripts"
|
||||
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-ink-2 hover:bg-surface-muted hover:text-ink transition-all text-sm font-medium"
|
||||
active-class="bg-accent-subtle !text-accent font-semibold"
|
||||
>
|
||||
<span class="material-symbols-outlined text-[20px]">description</span>
|
||||
<span class="text-sm font-medium">Guiones</span>
|
||||
<span>Guiones</span>
|
||||
</router-link>
|
||||
<router-link to="/generate" class="flex items-center gap-3 px-3 py-2.5 text-[#c7c4d7] hover:text-white hover:bg-white/5 transition-all duration-200 group relative" active-class="bg-white/10 !text-white rounded-lg font-semibold">
|
||||
|
||||
<router-link
|
||||
to="/generate"
|
||||
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-ink-2 hover:bg-surface-muted hover:text-ink transition-all text-sm font-medium"
|
||||
active-class="bg-accent-subtle !text-accent font-semibold"
|
||||
>
|
||||
<span class="material-symbols-outlined text-[20px]">auto_fix_high</span>
|
||||
<span class="text-sm font-medium">Generar</span>
|
||||
<span>Generar</span>
|
||||
</router-link>
|
||||
</nav>
|
||||
|
||||
<!-- Acción principal -->
|
||||
<div class="mt-auto pt-6 border-t border-white/5">
|
||||
<router-link to="/new-analysis" class="w-full flex items-center justify-center gap-2 py-3 px-4 bg-primary-container text-on-primary-container rounded-lg font-bold text-sm shadow-lg shadow-primary/20 scale-95 active:scale-90 transition-transform hover:brightness-110">
|
||||
<span class="material-symbols-outlined text-sm">add</span>
|
||||
<!-- CTA Nuevo Análisis -->
|
||||
<div class="px-3 pb-5">
|
||||
<router-link
|
||||
to="/new-analysis"
|
||||
class="w-full flex items-center justify-center gap-2 py-2.5 px-4 bg-accent hover:bg-accent-hover text-white rounded-lg text-sm font-semibold transition-colors shadow-sm"
|
||||
>
|
||||
<span class="material-symbols-outlined text-[18px]">add</span>
|
||||
Nuevo Análisis
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
@ -1,21 +1,30 @@
|
||||
<template>
|
||||
<header class="fixed top-0 right-0 left-64 h-16 flex items-center justify-between px-8 z-30 bg-[#13131a]/80 backdrop-blur-xl font-['Manrope'] text-sm">
|
||||
<div class="relative w-96 group">
|
||||
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-outline text-lg group-focus-within:text-primary transition-colors">search</span>
|
||||
<input class="w-full bg-surface-container-lowest border-none rounded-full pl-10 pr-4 py-2 text-sm focus:ring-2 focus:ring-primary/40 text-on-surface placeholder:text-outline/50 transition-all font-medium" placeholder="Buscar guiones analizados..." type="text"/>
|
||||
<header class="fixed top-0 right-0 left-60 h-16 flex items-center justify-between px-8 z-30 bg-surface border-b border-border">
|
||||
|
||||
<!-- Búsqueda -->
|
||||
<div class="relative w-80">
|
||||
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-ink-3 text-[18px]">search</span>
|
||||
<input
|
||||
class="w-full bg-canvas border border-border rounded-lg pl-9 pr-4 py-2 text-sm text-ink placeholder:text-ink-3 focus:outline-none focus:ring-2 focus:ring-accent/30 focus:border-accent/40 transition-all"
|
||||
placeholder="Buscar guiones..."
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-6">
|
||||
<button class="relative text-[#c7c4d7] hover:text-white transition-opacity">
|
||||
<span class="material-symbols-outlined">notifications</span>
|
||||
<span class="absolute top-0 right-0 w-2 h-2 bg-secondary rounded-full border-2 border-[#13131a]"></span>
|
||||
|
||||
<!-- Acciones -->
|
||||
<div class="flex items-center gap-4">
|
||||
<button class="relative text-ink-3 hover:text-ink-2 transition-colors p-1.5 rounded-lg hover:bg-surface-muted">
|
||||
<span class="material-symbols-outlined text-[22px]">notifications</span>
|
||||
<span class="absolute top-1 right-1 w-1.5 h-1.5 bg-accent rounded-full"></span>
|
||||
</button>
|
||||
<div class="flex items-center gap-3 pl-6 border-l border-white/5">
|
||||
<div class="text-right">
|
||||
<p class="text-xs font-bold text-white leading-none">Marketing Pro</p>
|
||||
<p class="text-[10px] text-outline font-medium">Administrador</p>
|
||||
|
||||
<div class="flex items-center gap-3 pl-4 border-l border-border">
|
||||
<div class="w-8 h-8 rounded-full bg-accent-subtle border border-accent-border flex items-center justify-center">
|
||||
<span class="material-symbols-outlined text-accent text-[18px]" style="font-variation-settings:'FILL' 1;">account_circle</span>
|
||||
</div>
|
||||
<div class="w-8 h-8 rounded-full overflow-hidden bg-surface-container-highest flex items-center justify-center">
|
||||
<span class="material-symbols-outlined text-outline">account_circle</span>
|
||||
<div class="text-right">
|
||||
<p class="text-xs font-semibold text-ink leading-none">Marketing Pro</p>
|
||||
<p class="text-[10px] text-ink-3 mt-0.5">Administrador</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -3,33 +3,36 @@
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
::-webkit-scrollbar { width: 6px; height: 6px; }
|
||||
::-webkit-scrollbar-track { @apply bg-surface; }
|
||||
::-webkit-scrollbar-thumb { @apply bg-surface-container-highest rounded-full; }
|
||||
::-webkit-scrollbar { width: 5px; height: 5px; }
|
||||
::-webkit-scrollbar-track { @apply bg-canvas; }
|
||||
::-webkit-scrollbar-thumb { @apply bg-border-strong rounded-full; }
|
||||
::-webkit-scrollbar-thumb:hover { @apply bg-ink-3; }
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.material-symbols-outlined {
|
||||
font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 24;
|
||||
}
|
||||
.glass-panel {
|
||||
background: rgba(31, 31, 38, 0.6);
|
||||
backdrop-filter: blur(20px);
|
||||
|
||||
/* Tarjeta base con sombra sutil */
|
||||
.card {
|
||||
@apply bg-surface rounded-xl border border-border shadow-sm;
|
||||
}
|
||||
.neon-glow {
|
||||
text-shadow: 0 0 10px rgba(78, 222, 163, 0.4);
|
||||
}
|
||||
.radial-gradient-score {
|
||||
background: conic-gradient(from 0deg, #4edea3 88%, #1f1f26 0%);
|
||||
|
||||
/* Badge de plataforma — base */
|
||||
.platform-badge {
|
||||
@apply text-[9px] font-semibold px-2 py-0.5 rounded-md uppercase tracking-wider;
|
||||
}
|
||||
|
||||
/* Animación del paso activo */
|
||||
.step-pulse {
|
||||
box-shadow: 0 0 0 0 rgba(192, 193, 255, 0.4);
|
||||
animation: pulse 2s infinite;
|
||||
box-shadow: 0 0 0 0 rgba(37, 99, 235, 0.3);
|
||||
animation: step-pulse-anim 2s infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { box-shadow: 0 0 0 0 rgba(192, 193, 255, 0.4); }
|
||||
70% { box-shadow: 0 0 0 10px rgba(192, 193, 255, 0); }
|
||||
100% { box-shadow: 0 0 0 0 rgba(192, 193, 255, 0); }
|
||||
@keyframes step-pulse-anim {
|
||||
0% { box-shadow: 0 0 0 0 rgba(37, 99, 235, 0.3); }
|
||||
70% { box-shadow: 0 0 0 8px rgba(37, 99, 235, 0); }
|
||||
100% { box-shadow: 0 0 0 0 rgba(37, 99, 235, 0); }
|
||||
}
|
||||
|
||||
@ -1,174 +1,233 @@
|
||||
<template>
|
||||
<div class="max-w-7xl mx-auto flex flex-col gap-10">
|
||||
<div class="flex flex-col gap-8">
|
||||
|
||||
<!-- Encabezado -->
|
||||
<header class="flex flex-col md:flex-row md:items-end justify-between gap-6 border-b border-white/5 pb-8">
|
||||
<header class="flex flex-col md:flex-row md:items-end justify-between gap-4 pb-6 border-b border-border">
|
||||
<div>
|
||||
<h1 class="text-5xl font-extrabold font-headline tracking-tighter text-white mb-2 leading-tight">Nuevo Análisis de Video</h1>
|
||||
<p class="text-primary text-sm font-bold flex items-center gap-2 tracking-widest uppercase">
|
||||
<span class="material-symbols-outlined text-sm">rocket_launch</span>
|
||||
Motor de Pipeline GPT-4o + Whisper
|
||||
</p>
|
||||
<h1 class="text-3xl font-bold font-headline text-ink mb-1">Nuevo Análisis</h1>
|
||||
<p class="text-sm text-ink-3">Extrae patrones de neuromarketing de cualquier video viral</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<button class="px-6 py-3 bg-surface-container border border-white/5 text-outline font-bold rounded-xl text-sm hover:text-white transition-colors">Guardar Borrador</button>
|
||||
<button class="px-8 py-3 bg-gradient-to-br from-primary-container to-primary text-on-primary-container font-headline font-bold rounded-xl shadow-lg shadow-primary/20 hover:scale-105 active:scale-95 transition-all text-sm h-12 flex items-center gap-2" :disabled="analizando" @click="iniciarAnalisis">
|
||||
<span class="material-symbols-outlined text-sm">{{ analizando ? 'hourglass_top' : 'auto_fix_high' }}</span>
|
||||
{{ analizando ? 'Analizando...' : 'Iniciar Pipeline' }}
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
class="px-5 py-2 bg-accent hover:bg-accent-hover text-white font-semibold rounded-lg text-sm flex items-center gap-2 transition-colors shadow-sm disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
:disabled="analizando"
|
||||
@click="iniciarAnalisis"
|
||||
>
|
||||
<span class="material-symbols-outlined text-[18px]" :class="analizando ? 'animate-spin' : ''">
|
||||
{{ analizando ? 'hourglass_top' : 'play_arrow' }}
|
||||
</span>
|
||||
{{ analizando ? 'Analizando…' : 'Iniciar Análisis' }}
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-12 gap-12">
|
||||
<!-- Columna del Formulario -->
|
||||
<div class="xl:col-span-7 flex flex-col gap-10">
|
||||
<!-- Paso 1: Fuente -->
|
||||
<section class="space-y-6">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="w-8 h-8 rounded-lg bg-primary/10 text-primary flex items-center justify-center font-black text-sm border border-primary/20">01</span>
|
||||
<h2 class="text-xl font-headline font-extrabold text-white tracking-tight">Identificación de Fuente</h2>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 xl:grid-cols-12 gap-8">
|
||||
|
||||
<div class="glass-panel p-8 rounded-3xl border border-white/5 shadow-2xl space-y-8">
|
||||
<div class="space-y-3">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline ml-1">URL del Video</label>
|
||||
<div class="relative group">
|
||||
<span class="material-symbols-outlined absolute left-4 top-1/2 -translate-y-1/2 text-primary text-lg group-focus-within:animate-pulse">link</span>
|
||||
<input v-model="form.url" type="url" placeholder="https://www.tiktok.com/@usuario/video/..." class="w-full bg-surface-container-low border border-white/10 rounded-2xl pl-12 pr-4 py-4 text-sm focus:ring-2 focus:ring-primary/40 focus:border-primary/40 text-white placeholder:text-outline/40 transition-all font-medium font-serif" :disabled="analizando"/>
|
||||
<!-- Columna del Formulario -->
|
||||
<div class="xl:col-span-7 flex flex-col gap-6">
|
||||
|
||||
<!-- Paso 1: Fuente del Video -->
|
||||
<section class="bg-surface rounded-xl border border-border shadow-sm overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-border bg-surface-muted/50 flex items-center gap-3">
|
||||
<span class="w-6 h-6 rounded-md bg-accent text-white text-xs font-bold flex items-center justify-center">1</span>
|
||||
<h2 class="text-sm font-semibold text-ink">Fuente del Video</h2>
|
||||
</div>
|
||||
<div class="p-6 space-y-5">
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">URL del Video</label>
|
||||
<div class="relative">
|
||||
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-ink-3 text-[18px]">link</span>
|
||||
<input
|
||||
v-model="form.url"
|
||||
type="url"
|
||||
placeholder="https://www.tiktok.com/@usuario/video/..."
|
||||
class="w-full bg-canvas border border-border rounded-lg pl-10 pr-4 py-2.5 text-sm text-ink placeholder:text-ink-3 focus:outline-none focus:ring-2 focus:ring-accent/30 focus:border-accent/40 transition-all"
|
||||
:disabled="analizando"
|
||||
/>
|
||||
</div>
|
||||
<p class="text-[10px] text-outline/60 italic px-1">Compatible con TikTok, Instagram Reels y YouTube Shorts.</p>
|
||||
<p class="text-[11px] text-ink-3">Compatible con TikTok, Instagram Reels y YouTube Shorts.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-6">
|
||||
<div class="space-y-3">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline ml-1">Organización / Cliente</label>
|
||||
<select v-model="form.cliente_id" class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm focus:ring-2 focus:ring-primary/40 text-on-surface font-medium appearance-none transition-all shadow-inner" :disabled="analizando">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Cliente</label>
|
||||
<select
|
||||
v-model="form.cliente_id"
|
||||
class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-accent/30 appearance-none transition-all"
|
||||
:disabled="analizando"
|
||||
>
|
||||
<option :value="null">Interno / Sin cliente</option>
|
||||
<option v-for="c in clientes" :key="c.id" :value="c.id">{{ c.nombre }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline ml-1">Nombre del Proyecto (opcional)</label>
|
||||
<input v-model="form.proyecto_nombre" type="text" placeholder="Ej. Campaña Q1" class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm focus:ring-2 focus:ring-primary/40 text-on-surface font-medium transition-all shadow-inner" :disabled="analizando"/>
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Proyecto (opcional)</label>
|
||||
<input
|
||||
v-model="form.proyecto_nombre"
|
||||
type="text"
|
||||
placeholder="Ej. Campaña Q1"
|
||||
class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink placeholder:text-ink-3 focus:outline-none focus:ring-2 focus:ring-accent/30 transition-all"
|
||||
:disabled="analizando"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Paso 2: Métricas de Mercado -->
|
||||
<section class="space-y-6">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="w-8 h-8 rounded-lg bg-secondary/10 text-secondary flex items-center justify-center font-black text-sm border border-secondary/20">02</span>
|
||||
<h2 class="text-xl font-headline font-extrabold text-white tracking-tight">Inteligencia de Mercado</h2>
|
||||
<!-- Paso 2: Inteligencia de Mercado -->
|
||||
<section class="bg-surface rounded-xl border border-border shadow-sm overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-border bg-surface-muted/50 flex items-center gap-3">
|
||||
<span class="w-6 h-6 rounded-md bg-success text-white text-xs font-bold flex items-center justify-center">2</span>
|
||||
<h2 class="text-sm font-semibold text-ink">Inteligencia de Mercado</h2>
|
||||
</div>
|
||||
|
||||
<div class="glass-panel p-8 rounded-3xl border border-white/5 shadow-2xl space-y-8">
|
||||
<div class="grid grid-cols-2 gap-6">
|
||||
<div class="space-y-3">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline ml-1">Nicho Principal</label>
|
||||
<div class="relative">
|
||||
<input v-model="form.niche" list="nichos-list" placeholder="Seleccionar o escribir..." class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm focus:ring-2 focus:ring-primary/40 text-on-surface font-black uppercase tracking-widest shadow-inner" :disabled="analizando"/>
|
||||
<datalist id="nichos-list">
|
||||
<option v-for="n in nichos" :key="n" :value="n">{{ n }}</option>
|
||||
</datalist>
|
||||
</div>
|
||||
<div class="p-6 space-y-5">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Nicho Principal</label>
|
||||
<input
|
||||
v-model="form.niche"
|
||||
list="nichos-list"
|
||||
placeholder="Seleccionar o escribir…"
|
||||
class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink placeholder:text-ink-3 focus:outline-none focus:ring-2 focus:ring-accent/30 transition-all"
|
||||
:disabled="analizando"
|
||||
/>
|
||||
<datalist id="nichos-list">
|
||||
<option v-for="n in nichos" :key="n" :value="n">{{ n }}</option>
|
||||
</datalist>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline ml-1">Audiencia Objetivo</label>
|
||||
<input v-model="form.mercado_objetivo" type="text" placeholder="Ej. Emprendedoras 25-35 años" class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm focus:ring-2 focus:ring-primary/40 text-on-surface font-medium shadow-inner" :disabled="analizando"/>
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Audiencia Objetivo</label>
|
||||
<input
|
||||
v-model="form.mercado_objetivo"
|
||||
type="text"
|
||||
placeholder="Ej. Emprendedoras 25-35 años"
|
||||
class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink placeholder:text-ink-3 focus:outline-none focus:ring-2 focus:ring-accent/30 transition-all"
|
||||
:disabled="analizando"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-6">
|
||||
<div class="space-y-3">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline ml-1">Vistas</label>
|
||||
<input v-model.number="form.vistas" type="number" placeholder="0" class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm focus:ring-2 focus:ring-primary/40 text-on-surface font-bold text-center" :disabled="analizando"/>
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Vistas</label>
|
||||
<input v-model.number="form.vistas" type="number" placeholder="0" class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink text-center focus:outline-none focus:ring-2 focus:ring-accent/30" :disabled="analizando"/>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline ml-1">Likes</label>
|
||||
<input v-model.number="form.likes" type="number" placeholder="0" class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm focus:ring-2 focus:ring-primary/40 text-on-surface font-bold text-center" :disabled="analizando"/>
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Likes</label>
|
||||
<input v-model.number="form.likes" type="number" placeholder="0" class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink text-center focus:outline-none focus:ring-2 focus:ring-accent/30" :disabled="analizando"/>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline ml-1">Compartidos / Guardados</label>
|
||||
<input v-model.number="form.compartidos" type="number" placeholder="0" class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm focus:ring-2 focus:ring-primary/40 text-on-surface font-bold text-center" :disabled="analizando"/>
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Compartidos</label>
|
||||
<input v-model.number="form.compartidos" type="number" placeholder="0" class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink text-center focus:outline-none focus:ring-2 focus:ring-accent/30" :disabled="analizando"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 p-4 rounded-2xl bg-surface-container-lowest border border-white/5">
|
||||
<input v-model="form.competidor_referente" type="checkbox" id="check-comp" class="w-5 h-5 rounded border-white/10 bg-surface text-primary focus:ring-primary focus:ring-offset-surface-container ring-offset-2 transition-all cursor-pointer" :disabled="analizando"/>
|
||||
<label for="check-comp" class="text-xs font-bold text-on-surface cursor-pointer select-none">Marcar como Referencia de Competidor Estratégico</label>
|
||||
</div>
|
||||
<label class="flex items-center gap-3 cursor-pointer group">
|
||||
<input
|
||||
v-model="form.competidor_referente"
|
||||
type="checkbox"
|
||||
class="w-4 h-4 rounded border-border text-accent focus:ring-accent/30 focus:ring-offset-0 transition-all cursor-pointer"
|
||||
:disabled="analizando"
|
||||
/>
|
||||
<span class="text-sm text-ink-2 group-hover:text-ink transition-colors select-none">
|
||||
Marcar como referencia de competidor estratégico
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Paso 3: Contexto del Video -->
|
||||
<section class="space-y-6">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="w-8 h-8 rounded-lg bg-tertiary/10 text-tertiary flex items-center justify-center font-black text-sm border border-tertiary/20">03</span>
|
||||
<h2 class="text-xl font-headline font-extrabold text-white tracking-tight">Contexto del Video <span class="text-outline font-normal text-base">(opcional)</span></h2>
|
||||
<section class="bg-surface rounded-xl border border-border shadow-sm overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-border bg-surface-muted/50 flex items-center gap-3">
|
||||
<span class="w-6 h-6 rounded-md bg-warn text-white text-xs font-bold flex items-center justify-center">3</span>
|
||||
<h2 class="text-sm font-semibold text-ink">
|
||||
Contexto del Video
|
||||
<span class="text-ink-3 font-normal ml-1">(opcional)</span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="glass-panel p-8 rounded-3xl border border-white/5 shadow-2xl space-y-4">
|
||||
<p class="text-xs text-outline/70 leading-relaxed">
|
||||
Describe de qué trata el video, cuál era la intención del creador, o cualquier detalle que la transcripción sola no captura (ej. tono irónico, contexto de tendencia, referencia cultural). GPT-4o usará esto para enriquecer el análisis.
|
||||
<div class="p-6">
|
||||
<p class="text-xs text-ink-3 leading-relaxed mb-3">
|
||||
Describe la intención del creador o cualquier contexto que la transcripción no capture. GPT-4o lo usará para enriquecer el análisis.
|
||||
</p>
|
||||
<div class="relative">
|
||||
<span class="material-symbols-outlined absolute left-4 top-4 text-tertiary text-lg">lightbulb</span>
|
||||
<textarea
|
||||
v-model="form.contexto_video"
|
||||
rows="4"
|
||||
placeholder="Ej. Este video es una respuesta a una tendencia viral donde los creadores muestran su rutina matutina. El creador usa humor sarcástico y habla a emprendedores que trabajan desde casa..."
|
||||
class="w-full bg-surface-container-low border border-white/10 rounded-2xl pl-12 pr-4 py-4 text-sm focus:ring-2 focus:ring-tertiary/40 focus:border-tertiary/40 text-white placeholder:text-outline/40 transition-all resize-none leading-relaxed"
|
||||
:disabled="analizando"
|
||||
></textarea>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="form.contexto_video"
|
||||
rows="3"
|
||||
placeholder="Ej. Este video responde a una tendencia viral usando humor sarcástico dirigido a emprendedores…"
|
||||
class="w-full bg-canvas border border-border rounded-lg px-4 py-3 text-sm text-ink placeholder:text-ink-3 focus:outline-none focus:ring-2 focus:ring-accent/30 resize-none leading-relaxed transition-all"
|
||||
:disabled="analizando"
|
||||
></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Columna de Estado del Pipeline -->
|
||||
<div class="xl:col-span-5 flex flex-col gap-6">
|
||||
<div class="sticky top-24">
|
||||
<div class="bg-surface-container p-8 rounded-[40px] border border-outline-variant/10 shadow-3xl relative overflow-hidden flex flex-col gap-8">
|
||||
<div class="absolute -top-12 -right-12 w-32 h-32 bg-secondary/10 blur-3xl rounded-full"></div>
|
||||
|
||||
<div class="flex items-center justify-between border-b border-white/5 pb-6">
|
||||
<div>
|
||||
<h3 class="text-sm font-headline font-black text-white uppercase tracking-widest mb-1">Motor de Pipeline</h3>
|
||||
<p class="text-[10px] text-outline uppercase font-bold tracking-tight">Estado del análisis en tiempo real</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 px-3 py-1 bg-surface-container-low rounded-full border border-white/5">
|
||||
<span class="w-1.5 h-1.5 rounded-full" :class="analizando ? 'bg-secondary animate-pulse' : 'bg-outline/30'"></span>
|
||||
<span class="text-[10px] font-black tracking-widest uppercase" :class="analizando ? 'text-secondary' : 'text-outline'">{{ analizando ? 'Activo' : 'En espera' }}</span>
|
||||
</div>
|
||||
<!-- Panel de Estado del Pipeline -->
|
||||
<div class="xl:col-span-5">
|
||||
<div class="sticky top-24 bg-surface rounded-xl border border-border shadow-sm overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-border flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-sm font-semibold text-ink">Estado del Pipeline</h3>
|
||||
<p class="text-[11px] text-ink-3 mt-0.5">Proceso de análisis en tiempo real</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-2 h-2 rounded-full" :class="analizando ? 'bg-success animate-pulse' : 'bg-border-strong'"></span>
|
||||
<span class="text-[11px] font-medium" :class="analizando ? 'text-success' : 'text-ink-3'">
|
||||
{{ analizando ? 'Activo' : 'En espera' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-8 relative">
|
||||
<div class="absolute left-4 top-2 bottom-2 w-px bg-white/5 z-0"></div>
|
||||
<div class="p-6">
|
||||
<div class="space-y-5 relative">
|
||||
<!-- Línea vertical conectora -->
|
||||
<div class="absolute left-[14px] top-4 bottom-4 w-px bg-border z-0"></div>
|
||||
|
||||
<div v-for="(s, idx) in pasosVisibles" :key="s.id" class="flex gap-4 relative z-10" :class="idx > currentStepIdx ? 'opacity-30' : 'opacity-100'">
|
||||
<div class="w-8 h-8 rounded-full flex items-center justify-center shrink-0 transition-all duration-300" :class="idx === currentStepIdx ? 'bg-secondary text-on-secondary shadow-lg shadow-secondary/40 scale-110' : (idx < currentStepIdx ? 'bg-primary/20 text-primary' : 'bg-surface-container-highest text-outline')">
|
||||
<span class="material-symbols-outlined text-sm font-black">{{ idx < currentStepIdx ? 'check' : s.icon }}</span>
|
||||
<div
|
||||
v-for="(s, idx) in pasosVisibles"
|
||||
:key="s.id"
|
||||
class="flex gap-4 relative z-10"
|
||||
:class="idx > currentStepIdx && analizando ? 'opacity-40' : 'opacity-100'"
|
||||
>
|
||||
<div
|
||||
class="w-7 h-7 rounded-full flex items-center justify-center shrink-0 transition-all duration-300 bg-surface"
|
||||
:class="
|
||||
idx < currentStepIdx
|
||||
? 'bg-success border-2 border-success text-white step-pulse'
|
||||
: idx === currentStepIdx && analizando
|
||||
? 'bg-accent border-2 border-accent text-white step-pulse'
|
||||
: 'border-2 border-border text-ink-3'
|
||||
"
|
||||
>
|
||||
<span class="material-symbols-outlined text-[14px]">{{ idx < currentStepIdx ? 'check' : s.icon }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs font-black uppercase tracking-widest mb-1" :class="idx === currentStepIdx ? 'text-white' : 'text-outline'">{{ s.label }}</p>
|
||||
<p class="text-[10px] font-bold tracking-tight leading-none" :class="idx === currentStepIdx ? 'text-secondary' : 'text-outline/40'">{{ s.desc }}</p>
|
||||
<div class="pt-0.5">
|
||||
<p class="text-sm font-medium" :class="idx === currentStepIdx && analizando ? 'text-ink' : 'text-ink-2'">{{ s.label }}</p>
|
||||
<p class="text-[11px] text-ink-3 mt-0.5">{{ s.desc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="p-4 rounded-xl bg-red-500/10 border border-red-500/30 text-red-500 text-[11px] font-bold leading-relaxed shadow-lg">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<span class="material-symbols-outlined text-sm">report</span>
|
||||
<span class="uppercase tracking-widest">Error en el Pipeline</span>
|
||||
<!-- Progreso -->
|
||||
<div v-if="analizando" class="mt-6 pt-5 border-t border-border space-y-2">
|
||||
<div class="w-full bg-surface-subtle h-1.5 rounded-full overflow-hidden">
|
||||
<div
|
||||
class="bg-accent h-full rounded-full transition-all duration-500"
|
||||
:style="{ width: ((currentStepIdx / 4) * 100) + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
{{ error }}
|
||||
<p class="text-[11px] text-ink-3 text-center">Tiempo estimado: ~15 segundos · GPT-4o + Whisper</p>
|
||||
</div>
|
||||
|
||||
<div v-if="analizando" class="mt-4 pt-4 border-t border-white/5 flex flex-col gap-3">
|
||||
<div class="w-full bg-surface-container-low h-1 rounded-full overflow-hidden">
|
||||
<div class="bg-secondary h-full transition-all duration-500" :style="{ width: ((currentStepIdx / 4) * 100) + '%' }"></div>
|
||||
</div>
|
||||
<p class="text-[10px] text-outline italic text-center animate-pulse">Procesando embeddings vectoriales y solicitud a OpenAI. Tiempo estimado: 15s</p>
|
||||
<!-- Error -->
|
||||
<div v-if="error" class="mt-5 p-4 rounded-lg bg-error-subtle border border-error-border text-error text-sm">
|
||||
<p class="font-semibold mb-1">Error en el pipeline</p>
|
||||
<p class="text-[12px] leading-relaxed">{{ error }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Estado inicial -->
|
||||
<div v-if="!analizando && !error" class="mt-6 pt-5 border-t border-border">
|
||||
<p class="text-[11px] text-ink-3 leading-relaxed">
|
||||
Completa la URL y el nicho para iniciar. El pipeline extrae audio, transcribe y analiza los patrones de neuromarketing automáticamente.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -205,10 +264,10 @@ const form = ref({
|
||||
})
|
||||
|
||||
const pasosVisibles = [
|
||||
{ id: 'extraccion', label: 'Extracción de Audio', icon: 'downloading', desc: 'Descargando el video fuente y extrayendo el audio.' },
|
||||
{ id: 'transcripcion', label: 'Transcripción Whisper', icon: 'mic', desc: 'Generando transcripción con precisión de milisegundos.' },
|
||||
{ id: 'analisis', label: 'Análisis con GPT-4o', icon: 'psychology', desc: 'Analizando patrones de neuromarketing y narrativa.' },
|
||||
{ id: 'embedding', label: 'Codificación Vectorial', icon: 'hub', desc: 'Generando embedding e inyectando en la base de datos.' },
|
||||
{ id: 'extraccion', label: 'Extracción de Audio', icon: 'downloading', desc: 'Descargando el video y extrayendo el audio.' },
|
||||
{ id: 'transcripcion', label: 'Transcripción Whisper', icon: 'mic', desc: 'Convirtiendo audio a texto con precisión.' },
|
||||
{ id: 'analisis', label: 'Análisis con GPT-4o', icon: 'psychology', desc: 'Identificando patrones de neuromarketing.' },
|
||||
{ id: 'embedding', label: 'Codificación Vectorial', icon: 'hub', desc: 'Guardando en la base de datos.' },
|
||||
]
|
||||
|
||||
const currentStepIdx = computed(() => {
|
||||
|
||||
@ -1,171 +1,190 @@
|
||||
<template>
|
||||
<div v-if="cargando" class="flex flex-col items-center justify-center py-24 gap-4 min-h-[50vh]">
|
||||
<div class="w-10 h-10 border-2 border-primary border-t-transparent rounded-full animate-spin"></div>
|
||||
<p class="text-outline text-sm animate-pulse">Cargando datos neuro-semánticos...</p>
|
||||
<!-- Loading -->
|
||||
<div v-if="cargando" class="flex flex-col items-center justify-center py-24 gap-4">
|
||||
<div class="w-8 h-8 border-2 border-accent border-t-transparent rounded-full animate-spin"></div>
|
||||
<p class="text-sm text-ink-3">Cargando análisis…</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="guion" class="max-w-6xl mx-auto relative flex flex-col gap-8 pb-12">
|
||||
<div v-else-if="guion" class="flex flex-col gap-8 pb-12">
|
||||
|
||||
<!-- Encabezado -->
|
||||
<header class="relative z-10 flex flex-col md:flex-row md:items-end justify-between gap-6">
|
||||
<div>
|
||||
<router-link to="/analysis" class="flex items-center gap-2 text-outline hover:text-white transition-colors text-sm font-bold uppercase tracking-widest mb-6 w-fit">
|
||||
<span class="material-symbols-outlined text-lg">west</span> Volver al Historial
|
||||
<header class="flex flex-col md:flex-row md:items-start justify-between gap-6 pb-6 border-b border-border">
|
||||
<div class="flex-1">
|
||||
<router-link to="/analysis" class="inline-flex items-center gap-1.5 text-xs font-medium text-ink-3 hover:text-accent transition-colors mb-4 uppercase tracking-wider">
|
||||
<span class="material-symbols-outlined text-base">west</span> Volver al historial
|
||||
</router-link>
|
||||
<div class="flex items-center gap-3 mb-3 flex-wrap">
|
||||
<span class="px-3 py-1 bg-surface-container-highest text-on-surface-variant text-[11px] font-black rounded uppercase tracking-widest">{{ guion.niche }}</span>
|
||||
<span v-if="guion.sub_niche" class="px-3 py-1 bg-surface-container-low border border-outline-variant/20 text-outline text-[11px] font-bold rounded">{{ guion.sub_niche }}</span>
|
||||
<span :class="plataformaBadge(guion.plataforma)" class="px-3 py-1 text-[11px] font-black rounded uppercase tracking-widest">{{ guion.plataforma }}</span>
|
||||
<span v-if="guion.replicabilidad" :class="replicabilidadBadge(guion.replicabilidad)" class="px-3 py-1 text-[11px] font-black rounded uppercase tracking-widest">
|
||||
|
||||
<div class="flex items-center gap-2 flex-wrap mb-3">
|
||||
<span class="text-[11px] font-semibold px-2.5 py-1 bg-surface-muted border border-border rounded-md text-ink-2 uppercase tracking-wide">{{ guion.niche }}</span>
|
||||
<span v-if="guion.sub_niche" class="text-[11px] px-2.5 py-1 bg-canvas border border-border rounded-md text-ink-3">{{ guion.sub_niche }}</span>
|
||||
<span :class="plataformaBadge(guion.plataforma)" class="platform-badge">{{ guion.plataforma }}</span>
|
||||
<span v-if="guion.replicabilidad" :class="replicabilidadBadge(guion.replicabilidad)" class="text-[11px] font-semibold px-2.5 py-1 rounded-md">
|
||||
Replicabilidad {{ guion.replicabilidad }}
|
||||
</span>
|
||||
</div>
|
||||
<h1 class="text-4xl md:text-5xl font-extrabold font-headline tracking-tighter text-white mb-2 leading-tight max-w-3xl">
|
||||
|
||||
<h1 class="text-3xl font-bold font-headline text-ink mb-2 leading-tight max-w-2xl">
|
||||
{{ guion.tema_principal || 'Análisis sin título' }}
|
||||
</h1>
|
||||
<p class="text-primary text-sm font-bold flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-sm">visibility</span>
|
||||
<p class="text-sm text-ink-2 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-[16px] text-ink-3">visibility</span>
|
||||
{{ guion.angulo_unico || 'Ángulo único no especificado' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 shrink-0">
|
||||
<button v-if="guion.url_origen" class="h-12 w-12 rounded-xl bg-surface-container-low border border-outline-variant/20 flex items-center justify-center text-on-surface hover:bg-surface-container transition-colors shadow-lg" title="Ver video original" @click="openUrl(guion.url_origen)">
|
||||
<span class="material-symbols-outlined">link</span>
|
||||
</button>
|
||||
<button class="px-6 py-3 bg-gradient-to-br from-primary-container to-primary text-on-primary-container font-headline font-bold rounded-xl shadow-lg shadow-primary/20 hover:scale-105 active:scale-95 transition-all text-sm h-12 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-sm">auto_fix_high</span> Generar Guion
|
||||
<div class="flex items-center gap-3 shrink-0">
|
||||
<button
|
||||
v-if="guion.url_origen"
|
||||
class="h-9 w-9 rounded-lg bg-canvas border border-border flex items-center justify-center text-ink-2 hover:bg-surface-muted hover:text-ink transition-colors"
|
||||
title="Ver video original"
|
||||
@click="openUrl(guion.url_origen)"
|
||||
>
|
||||
<span class="material-symbols-outlined text-[18px]">open_in_new</span>
|
||||
</button>
|
||||
<router-link to="/generate" class="px-4 py-2 bg-accent hover:bg-accent-hover text-white font-semibold rounded-lg text-sm flex items-center gap-1.5 transition-colors shadow-sm">
|
||||
<span class="material-symbols-outlined text-[16px]">auto_fix_high</span>
|
||||
Generar Guion
|
||||
</router-link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Cuadrícula principal -->
|
||||
<div class="grid grid-cols-1 xl:grid-cols-12 gap-8 relative z-10">
|
||||
<!-- Columna izquierda -->
|
||||
<div class="xl:col-span-4 flex flex-col gap-6">
|
||||
<div class="grid grid-cols-1 xl:grid-cols-12 gap-6">
|
||||
|
||||
<!-- Columna izquierda (métricas) -->
|
||||
<div class="xl:col-span-4 flex flex-col gap-4">
|
||||
|
||||
<!-- Métricas Sociales -->
|
||||
<div v-if="guion.vistas || guion.likes || guion.compartidos" class="bg-surface-container p-5 rounded-3xl border border-outline-variant/10 shadow-xl">
|
||||
<h3 class="text-xs font-headline font-bold text-white mb-4 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-outline text-base">bar_chart</span> Métricas del Video
|
||||
<div v-if="guion.vistas || guion.likes || guion.compartidos" class="bg-surface rounded-xl border border-border shadow-sm p-5">
|
||||
<h3 class="text-xs font-semibold text-ink-2 uppercase tracking-wider mb-4 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-ink-3 text-[16px]">bar_chart</span>
|
||||
Métricas del Video
|
||||
</h3>
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div class="text-center p-3 rounded-2xl bg-surface-container-low border border-white/5">
|
||||
<span class="material-symbols-outlined text-blue-400 text-lg block mb-1">visibility</span>
|
||||
<p class="text-[9px] text-outline font-black uppercase tracking-widest mb-1">Vistas</p>
|
||||
<p class="text-base font-black text-white font-headline">{{ formatNum(guion.vistas) }}</p>
|
||||
<div class="text-center p-3 rounded-lg bg-surface-muted border border-border">
|
||||
<span class="material-symbols-outlined text-blue-500 text-lg block mb-1">visibility</span>
|
||||
<p class="text-[9px] text-ink-3 font-semibold uppercase tracking-wider mb-1">Vistas</p>
|
||||
<p class="text-base font-bold text-ink">{{ formatNum(guion.vistas) }}</p>
|
||||
</div>
|
||||
<div class="text-center p-3 rounded-2xl bg-surface-container-low border border-white/5">
|
||||
<span class="material-symbols-outlined text-red-400 text-lg block mb-1" style="font-variation-settings:'FILL' 1;">favorite</span>
|
||||
<p class="text-[9px] text-outline font-black uppercase tracking-widest mb-1">Likes</p>
|
||||
<p class="text-base font-black text-white font-headline">{{ formatNum(guion.likes) }}</p>
|
||||
<div class="text-center p-3 rounded-lg bg-surface-muted border border-border">
|
||||
<span class="material-symbols-outlined text-red-500 text-lg block mb-1" style="font-variation-settings:'FILL' 1;">favorite</span>
|
||||
<p class="text-[9px] text-ink-3 font-semibold uppercase tracking-wider mb-1">Likes</p>
|
||||
<p class="text-base font-bold text-ink">{{ formatNum(guion.likes) }}</p>
|
||||
</div>
|
||||
<div class="text-center p-3 rounded-2xl bg-surface-container-low border border-white/5">
|
||||
<span class="material-symbols-outlined text-secondary text-lg block mb-1">share</span>
|
||||
<p class="text-[9px] text-outline font-black uppercase tracking-widest mb-1">Compartidos</p>
|
||||
<p class="text-base font-black text-white font-headline">{{ formatNum(guion.compartidos) }}</p>
|
||||
<div class="text-center p-3 rounded-lg bg-surface-muted border border-border">
|
||||
<span class="material-symbols-outlined text-success text-lg block mb-1">share</span>
|
||||
<p class="text-[9px] text-ink-3 font-semibold uppercase tracking-wider mb-1">Compartidos</p>
|
||||
<p class="text-base font-bold text-ink">{{ formatNum(guion.compartidos) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="guion.score_engagement" class="mt-3 flex items-center justify-between px-3 py-2 rounded-xl bg-surface-container-lowest border border-white/5">
|
||||
<span class="text-[10px] text-outline font-black uppercase tracking-widest">Engagement Rate</span>
|
||||
<span class="text-sm font-black text-secondary">{{ (guion.score_engagement * 1).toFixed(2) }}%</span>
|
||||
<div v-if="guion.score_engagement" class="mt-3 flex items-center justify-between px-3 py-2 rounded-lg bg-success-subtle border border-success-border">
|
||||
<span class="text-[11px] text-success font-semibold">Engagement Rate</span>
|
||||
<span class="text-sm font-bold text-success">{{ (guion.score_engagement * 1).toFixed(2) }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Puntaje -->
|
||||
<div class="bg-surface-container p-6 rounded-3xl border border-outline-variant/10 shadow-2xl relative overflow-hidden group">
|
||||
<div class="absolute -top-24 -right-24 w-48 h-48 bg-primary/20 blur-3xl rounded-full group-hover:bg-primary/30 transition-colors pointer-events-none"></div>
|
||||
<h3 class="text-sm font-headline font-bold text-white mb-8 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary">analytics</span> Puntaje Neuro-Engagement
|
||||
<!-- Puntaje Circular -->
|
||||
<div class="bg-surface rounded-xl border border-border shadow-sm p-6">
|
||||
<h3 class="text-xs font-semibold text-ink-2 uppercase tracking-wider mb-5 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-accent text-[16px]">analytics</span>
|
||||
Puntaje de Viralidad
|
||||
</h3>
|
||||
<div class="flex justify-center mb-6 relative">
|
||||
<svg class="w-48 h-48 transform -rotate-90" viewBox="0 0 100 100">
|
||||
<circle cx="50" cy="50" r="45" fill="none" stroke="rgba(255,255,255,0.05)" stroke-width="8" />
|
||||
<circle cx="50" cy="50" r="45" fill="none" class="stroke-primary transition-all duration-1000 ease-out" stroke-width="8" :stroke-dasharray="`${(guion.score_virabilidad || 0)/100 * 283} 283`" stroke-linecap="round" />
|
||||
<div class="flex justify-center mb-5">
|
||||
<svg class="w-40 h-40 transform -rotate-90" viewBox="0 0 100 100">
|
||||
<circle cx="50" cy="50" r="42" fill="none" stroke="#e5e3de" stroke-width="7"/>
|
||||
<circle
|
||||
cx="50" cy="50" r="42" fill="none"
|
||||
class="stroke-accent transition-all duration-1000 ease-out"
|
||||
stroke-width="7"
|
||||
:stroke-dasharray="`${(guion.score_virabilidad || 0)/100 * 264} 264`"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
<div class="absolute inset-0 flex flex-col items-center justify-center">
|
||||
<span class="text-6xl font-black font-headline text-white tracking-tighter">{{ guion.score_virabilidad || 0 }}</span>
|
||||
<span class="text-xs font-bold text-primary uppercase tracking-widest mt-1">/ 100</span>
|
||||
<div class="absolute flex flex-col items-center justify-center" style="margin-top: 30px;">
|
||||
<span class="text-5xl font-bold font-headline text-ink">{{ guion.score_virabilidad || 0 }}</span>
|
||||
<span class="text-xs text-ink-3 font-medium">/ 100</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4 pt-6 border-t border-white/5">
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-outline uppercase tracking-widest font-bold mb-1">Cialdini</p>
|
||||
<p class="text-xl font-bold text-white">{{ guion.score_cialdini ?? 0 }}<span class="text-sm text-outline">/7</span></p>
|
||||
<div class="grid grid-cols-2 gap-3 pt-4 border-t border-border">
|
||||
<div class="text-center p-3 rounded-lg bg-surface-muted">
|
||||
<p class="text-[10px] text-ink-3 font-medium mb-1">Cialdini</p>
|
||||
<p class="text-xl font-bold text-ink">{{ guion.score_cialdini ?? 0 }}<span class="text-sm text-ink-3">/7</span></p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-outline uppercase tracking-widest font-bold mb-1">Intensidad</p>
|
||||
<p class="text-xl font-bold text-orange-400">{{ guion.intensidad_emocional || 0 }}<span class="text-sm text-outline">/10</span></p>
|
||||
<div class="text-center p-3 rounded-lg bg-surface-muted">
|
||||
<p class="text-[10px] text-ink-3 font-medium mb-1">Intensidad</p>
|
||||
<p class="text-xl font-bold text-warn">{{ guion.intensidad_emocional || 0 }}<span class="text-sm text-ink-3">/10</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ganchos semánticos -->
|
||||
<div class="bg-surface-container p-6 rounded-3xl border border-outline-variant/10 shadow-xl">
|
||||
<h3 class="text-sm font-headline font-bold text-white mb-6 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-secondary">psychology_alt</span> Ganchos Semánticos
|
||||
<!-- Ganchos Semánticos -->
|
||||
<div class="bg-surface rounded-xl border border-border shadow-sm p-5">
|
||||
<h3 class="text-xs font-semibold text-ink-2 uppercase tracking-wider mb-4 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-success text-[16px]">psychology_alt</span>
|
||||
Ganchos Semánticos
|
||||
</h3>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<p class="text-[10px] text-outline uppercase tracking-widest font-bold mb-2">Estructura Narrativa</p>
|
||||
<div class="p-3 rounded-lg border border-white/5 bg-surface-container-low">
|
||||
<span class="text-sm font-bold text-on-surface">{{ guion.estructura_narrativa || 'No detectada' }}</span>
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-2">Estructura Narrativa</p>
|
||||
<div class="px-3 py-2 rounded-lg bg-surface-muted border border-border">
|
||||
<span class="text-sm font-medium text-ink">{{ guion.estructura_narrativa || 'No detectada' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-[10px] text-outline uppercase tracking-widest font-bold mb-2 flex justify-between">
|
||||
<span>Gancho Principal</span>
|
||||
<span class="text-secondary">{{ guion.gancho_duracion_seg ? guion.gancho_duracion_seg + 's' : '' }}</span>
|
||||
</p>
|
||||
<div class="p-4 rounded-xl border border-secondary/20 bg-surface-container-low relative">
|
||||
<div class="absolute top-0 right-0 p-2"><span class="w-1.5 h-1.5 rounded-full bg-secondary block"></span></div>
|
||||
<p class="text-xs text-secondary font-bold uppercase tracking-wider mb-2">{{ guion.gancho_tipo || 'Gancho Estándar' }}</p>
|
||||
<p class="text-sm text-white font-medium leading-relaxed italic">"{{ guion.gancho_texto || '—' }}"</p>
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider">Gancho Principal</p>
|
||||
<span v-if="guion.gancho_duracion_seg" class="text-[10px] font-medium text-success">{{ guion.gancho_duracion_seg }}s</span>
|
||||
</div>
|
||||
<div class="p-3 rounded-lg border border-success-border bg-success-subtle">
|
||||
<p class="text-[10px] font-semibold text-success uppercase tracking-wider mb-1.5">{{ guion.gancho_tipo || 'Gancho Estándar' }}</p>
|
||||
<p class="text-xs text-ink-2 leading-relaxed italic">"{{ guion.gancho_texto || '—' }}"</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-[10px] text-outline uppercase tracking-widest font-bold mb-2">Técnica de Retención</p>
|
||||
<div class="p-3 rounded-lg border border-primary/20 bg-primary/5 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary text-base">repeat</span>
|
||||
<span class="text-sm font-bold text-primary">{{ guion.tecnica_retencion || '—' }}</span>
|
||||
<span v-if="guion.momento_pico_seg" class="ml-auto text-[10px] text-outline font-bold">Pico: {{ guion.momento_pico_seg }}s</span>
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-2">Técnica de Retención</p>
|
||||
<div class="px-3 py-2 rounded-lg border border-accent-border bg-accent-subtle flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-accent text-[16px]">repeat</span>
|
||||
<span class="text-sm font-medium text-accent">{{ guion.tecnica_retencion || '—' }}</span>
|
||||
<span v-if="guion.momento_pico_seg" class="ml-auto text-[10px] text-ink-3">Pico: {{ guion.momento_pico_seg }}s</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Avatar y Consciencia -->
|
||||
<div class="bg-surface-container p-6 rounded-3xl border border-outline-variant/10 shadow-xl">
|
||||
<h3 class="text-sm font-headline font-bold text-white mb-6 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-tertiary">person_search</span> Avatar & Copywriting
|
||||
<div class="bg-surface rounded-xl border border-border shadow-sm p-5">
|
||||
<h3 class="text-xs font-semibold text-ink-2 uppercase tracking-wider mb-4 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-warn text-[16px]">person_search</span>
|
||||
Avatar & Copywriting
|
||||
</h3>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<p class="text-[10px] text-outline uppercase tracking-widest font-bold mb-2">Nivel de Consciencia</p>
|
||||
<div class="relative">
|
||||
<div class="flex gap-1">
|
||||
<div v-for="(nivel, i) in nivelesConciencia" :key="nivel.key"
|
||||
class="flex-1 h-2 rounded-full transition-all"
|
||||
:class="nivelConcienciaIndex >= i ? 'bg-primary' : 'bg-surface-container-highest'"
|
||||
></div>
|
||||
</div>
|
||||
<p class="text-xs font-bold text-primary mt-2">{{ guion.nivel_consciencia?.replace(/_/g,' ') || '—' }}</p>
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-2">Nivel de Consciencia</p>
|
||||
<div class="flex gap-1 mb-1.5">
|
||||
<div
|
||||
v-for="(nivel, i) in nivelesConciencia"
|
||||
:key="nivel.key"
|
||||
class="flex-1 h-1.5 rounded-full transition-all"
|
||||
:class="nivelConcienciaIndex >= i ? 'bg-accent' : 'bg-border'"
|
||||
></div>
|
||||
</div>
|
||||
<p class="text-xs font-medium text-accent">{{ guion.nivel_consciencia?.replace(/_/g,' ') || '—' }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-[10px] text-outline uppercase tracking-widest font-bold mb-2">Avatar Objetivo</p>
|
||||
<p class="text-xs text-on-surface-variant leading-relaxed">{{ guion.avatar_descripcion || '—' }}</p>
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-1.5">Avatar Objetivo</p>
|
||||
<p class="text-xs text-ink-2 leading-relaxed">{{ guion.avatar_descripcion || '—' }}</p>
|
||||
</div>
|
||||
<div v-if="guion.objecion_principal">
|
||||
<p class="text-[10px] text-outline uppercase tracking-widest font-bold mb-2">Objeción Principal</p>
|
||||
<div class="p-3 rounded-xl border border-red-500/20 bg-red-500/5">
|
||||
<p class="text-xs text-red-400 leading-relaxed italic">"{{ guion.objecion_principal }}"</p>
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-1.5">Objeción Principal</p>
|
||||
<div class="p-3 rounded-lg border border-error-border bg-error-subtle">
|
||||
<p class="text-xs text-error leading-relaxed italic">"{{ guion.objecion_principal }}"</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-[10px] text-outline uppercase tracking-widest font-bold mb-2">Balance Emoción / Lógica</p>
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-1.5">Balance Emoción / Lógica</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-base" :class="ratioColor">{{ ratioIcon }}</span>
|
||||
<span class="text-sm font-bold" :class="ratioColor">{{ guion.ratio_emocion_logica || '—' }}</span>
|
||||
<span class="text-sm font-medium" :class="ratioColor">{{ guion.ratio_emocion_logica || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -173,61 +192,62 @@
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Columna derecha -->
|
||||
<div class="xl:col-span-8 flex flex-col gap-6">
|
||||
<!-- Columna derecha (contenido) -->
|
||||
<div class="xl:col-span-8 flex flex-col gap-5">
|
||||
|
||||
<!-- Patrón ganador -->
|
||||
<div class="bg-surface-container p-8 rounded-3xl border border-primary/20 shadow-2xl relative overflow-hidden">
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-primary/5 to-transparent pointer-events-none"></div>
|
||||
<p class="text-xs text-primary font-bold uppercase tracking-widest mb-4">Síntesis del Patrón Ganador</p>
|
||||
<p class="text-lg md:text-xl text-white font-medium leading-relaxed max-w-3xl relative z-10">{{ guion.resumen_patron }}</p>
|
||||
<!-- Patrón Ganador -->
|
||||
<div class="bg-surface rounded-xl border border-accent-border shadow-sm p-6 bg-accent-subtle/30">
|
||||
<p class="text-[10px] font-semibold text-accent uppercase tracking-wider mb-3">Síntesis del Patrón Ganador</p>
|
||||
<p class="text-base text-ink leading-relaxed max-w-2xl">{{ guion.resumen_patron }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Apertura y Cierre -->
|
||||
<div v-if="guion.apertura_exacta || guion.cierre_exacto" class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="bg-surface-container p-5 rounded-2xl border border-secondary/20">
|
||||
<p class="text-[10px] text-secondary font-black uppercase tracking-widest mb-3 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-sm">play_arrow</span> Apertura Exacta
|
||||
<div class="bg-surface rounded-xl border border-success-border p-5">
|
||||
<p class="text-[10px] font-semibold text-success uppercase tracking-wider mb-2 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-[14px]">play_arrow</span> Apertura Exacta
|
||||
</p>
|
||||
<p class="text-sm text-white font-medium leading-relaxed italic">"{{ guion.apertura_exacta }}"</p>
|
||||
<p class="text-sm text-ink-2 leading-relaxed italic">"{{ guion.apertura_exacta }}"</p>
|
||||
</div>
|
||||
<div class="bg-surface-container p-5 rounded-2xl border border-tertiary/20">
|
||||
<p class="text-[10px] text-tertiary font-black uppercase tracking-widest mb-3 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-sm">stop</span> Cierre Exacto
|
||||
<div class="bg-surface rounded-xl border border-warn-border p-5">
|
||||
<p class="text-[10px] font-semibold text-warn uppercase tracking-wider mb-2 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-[14px]">stop</span> Cierre Exacto
|
||||
</p>
|
||||
<p class="text-sm text-white font-medium leading-relaxed italic">"{{ guion.cierre_exacto }}"</p>
|
||||
<p class="text-sm text-ink-2 leading-relaxed italic">"{{ guion.cierre_exacto }}"</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ingredientes clave -->
|
||||
<div v-if="guion.ingredientes_clave?.length" class="bg-surface-container p-6 rounded-3xl border border-outline-variant/10 shadow-xl">
|
||||
<h3 class="text-sm font-headline font-bold text-white mb-5 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-yellow-400">key</span> Ingredientes Clave para Replicar
|
||||
<!-- Ingredientes Clave -->
|
||||
<div v-if="guion.ingredientes_clave?.length" class="bg-surface rounded-xl border border-border shadow-sm p-6">
|
||||
<h3 class="text-xs font-semibold text-ink-2 uppercase tracking-wider mb-4 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-warn text-[16px]">key</span>
|
||||
Ingredientes Clave para Replicar
|
||||
</h3>
|
||||
<div class="space-y-2">
|
||||
<div v-for="(ing, i) in guion.ingredientes_clave" :key="i" class="flex items-start gap-3 p-3 rounded-xl bg-surface-container-low border border-white/5">
|
||||
<span class="w-5 h-5 rounded-full bg-yellow-400/10 border border-yellow-400/30 text-yellow-400 text-[10px] font-black flex items-center justify-center shrink-0 mt-0.5">{{ i + 1 }}</span>
|
||||
<p class="text-sm text-on-surface-variant leading-relaxed">{{ ing }}</p>
|
||||
<div v-for="(ing, i) in guion.ingredientes_clave" :key="i" class="flex items-start gap-3 py-2 border-b border-border last:border-0">
|
||||
<span class="w-5 h-5 rounded-full bg-warn-subtle border border-warn-border text-warn text-[10px] font-bold flex items-center justify-center shrink-0 mt-0.5">{{ i + 1 }}</span>
|
||||
<p class="text-sm text-ink-2 leading-relaxed">{{ ing }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Emocional + Cialdini -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="bg-surface-container p-6 rounded-3xl border border-outline-variant/10 shadow-xl">
|
||||
<h3 class="text-sm font-headline font-bold text-white mb-6 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-orange-400">local_fire_department</span> Resonancia Emocional
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="bg-surface rounded-xl border border-border shadow-sm p-5">
|
||||
<h3 class="text-xs font-semibold text-ink-2 uppercase tracking-wider mb-4 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-orange-500 text-[16px]">local_fire_department</span>
|
||||
Resonancia Emocional
|
||||
</h3>
|
||||
<div class="mb-5">
|
||||
<div class="flex justify-between text-xs font-bold uppercase tracking-widest mb-2">
|
||||
<span class="text-outline">Intensidad</span>
|
||||
<span class="text-orange-400">{{ guion.intensidad_emocional || 0 }}/10</span>
|
||||
<div class="mb-4">
|
||||
<div class="flex justify-between text-xs font-medium mb-1.5">
|
||||
<span class="text-ink-3">Intensidad</span>
|
||||
<span class="text-orange-500 font-semibold">{{ guion.intensidad_emocional || 0 }}/10</span>
|
||||
</div>
|
||||
<div class="w-full bg-surface-container-highest h-1.5 rounded-full overflow-hidden">
|
||||
<div class="bg-gradient-to-r from-orange-500/50 to-orange-400 h-full" :style="{ width: ((guion.intensidad_emocional||0)*10) + '%' }"></div>
|
||||
<div class="w-full bg-surface-subtle h-1.5 rounded-full overflow-hidden">
|
||||
<div class="bg-orange-400 h-full rounded-full" :style="{ width: ((guion.intensidad_emocional||0)*10) + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<div class="space-y-2">
|
||||
<DataRow label="Trigger Principal" :value="guion.trigger_emocional" highlight />
|
||||
<DataRow label="Arco Emocional" :value="guion.arco_emocional" />
|
||||
<DataRow label="Sesgo Cognitivo" :value="guion.sesgo_cognitivo" />
|
||||
@ -235,11 +255,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-surface-container p-6 rounded-3xl border border-outline-variant/10 shadow-xl flex flex-col">
|
||||
<h3 class="text-sm font-headline font-bold text-white mb-6 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-indigo-400">group_work</span> Principios de Cialdini
|
||||
<div class="bg-surface rounded-xl border border-border shadow-sm p-5 flex flex-col">
|
||||
<h3 class="text-xs font-semibold text-ink-2 uppercase tracking-wider mb-4 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-indigo-500 text-[16px]">group_work</span>
|
||||
Principios de Cialdini
|
||||
</h3>
|
||||
<div class="grid grid-cols-2 gap-3 flex-1">
|
||||
<div class="grid grid-cols-2 gap-2 flex-1">
|
||||
<CialdiniItem label="Reciprocidad" :active="!!guion.cialdini_reciprocidad" />
|
||||
<CialdiniItem label="Escasez" :active="!!guion.cialdini_escasez" />
|
||||
<CialdiniItem label="Autoridad" :active="!!guion.cialdini_autoridad" />
|
||||
@ -252,12 +273,13 @@
|
||||
</div>
|
||||
|
||||
<!-- Neuromarketing + Entrega -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="bg-surface-container p-6 rounded-3xl border border-outline-variant/10 shadow-xl">
|
||||
<h3 class="text-sm font-headline font-bold text-white mb-6 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-fuchsia-400">biotech</span> Neuromarketing
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="bg-surface rounded-xl border border-border shadow-sm p-5">
|
||||
<h3 class="text-xs font-semibold text-ink-2 uppercase tracking-wider mb-4 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-fuchsia-500 text-[16px]">biotech</span>
|
||||
Neuromarketing
|
||||
</h3>
|
||||
<div class="space-y-3">
|
||||
<div class="space-y-2">
|
||||
<DataRow label="Atención Visual" :value="guion.atencion_visual" highlight />
|
||||
<DataRow label="Carga Cognitiva" :value="guion.carga_cognitiva" highlight />
|
||||
<DataRow label="Ritmo / Pacing" :value="guion.pacing_ritmo" highlight />
|
||||
@ -268,25 +290,26 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-surface-container p-6 rounded-3xl border border-outline-variant/10 shadow-xl flex flex-col">
|
||||
<h3 class="text-sm font-headline font-bold text-white mb-6 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-cyan-400">record_voice_over</span> Entrega y Alcance
|
||||
<div class="bg-surface rounded-xl border border-border shadow-sm p-5 flex flex-col">
|
||||
<h3 class="text-xs font-semibold text-ink-2 uppercase tracking-wider mb-4 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-cyan-600 text-[16px]">record_voice_over</span>
|
||||
Entrega y Alcance
|
||||
</h3>
|
||||
<div class="space-y-3 mb-5">
|
||||
<div class="space-y-2 mb-4">
|
||||
<DataRow label="Tono" :value="guion.tono" highlight />
|
||||
<DataRow label="Perspectiva" :value="guion.persona_narradora" highlight />
|
||||
<DataRow label="Especificidad" :value="guion.nivel_especificidad" highlight />
|
||||
<DataRow label="Velocidad" :value="guion.velocidad_locucion" />
|
||||
<DataRow label="CTA" :value="guion.cta_tipo" highlight />
|
||||
</div>
|
||||
<div v-if="guion.cta_texto" class="p-3 rounded-xl bg-surface-container-low border border-white/5">
|
||||
<p class="text-[9px] text-outline font-black uppercase tracking-widest mb-1">Texto del CTA</p>
|
||||
<p class="text-xs text-on-surface-variant italic">"{{ guion.cta_texto }}"</p>
|
||||
<div v-if="guion.cta_texto" class="p-3 rounded-lg bg-surface-muted border border-border">
|
||||
<p class="text-[9px] text-ink-3 font-semibold uppercase tracking-wider mb-1">Texto del CTA</p>
|
||||
<p class="text-xs text-ink-2 italic">"{{ guion.cta_texto }}"</p>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<p class="text-[10px] text-outline uppercase tracking-widest font-bold mb-2">Palabras Clave</p>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span v-for="kw in guion.palabras_clave" :key="kw" class="px-2 py-1 bg-surface-container-lowest border border-white/5 rounded text-[10px] font-bold text-on-surface-variant">{{ kw }}</span>
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-2">Palabras Clave</p>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<span v-for="kw in guion.palabras_clave" :key="kw" class="px-2 py-0.5 bg-surface-muted border border-border rounded text-[10px] font-medium text-ink-2">{{ kw }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -294,89 +317,89 @@
|
||||
|
||||
<!-- Promesa + Conflicto -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="bg-surface-container p-5 rounded-2xl border border-outline-variant/10">
|
||||
<p class="text-[10px] text-outline uppercase tracking-widest font-bold mb-2">Promesa Explícita</p>
|
||||
<p class="text-sm text-white leading-relaxed">{{ guion.promesa_explicita || '—' }}</p>
|
||||
<div class="bg-surface rounded-xl border border-border p-5">
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-2">Promesa Explícita</p>
|
||||
<p class="text-sm text-ink leading-relaxed">{{ guion.promesa_explicita || '—' }}</p>
|
||||
</div>
|
||||
<div class="bg-surface-container p-5 rounded-2xl border border-outline-variant/10">
|
||||
<p class="text-[10px] text-outline uppercase tracking-widest font-bold mb-2">Conflicto → Resolución</p>
|
||||
<p class="text-xs text-on-surface-variant leading-relaxed">{{ guion.conflicto_central || '—' }}</p>
|
||||
<p v-if="guion.resolucion" class="text-xs text-secondary mt-2 leading-relaxed">→ {{ guion.resolucion }}</p>
|
||||
<div class="bg-surface rounded-xl border border-border p-5">
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-2">Conflicto → Resolución</p>
|
||||
<p class="text-xs text-ink-2 leading-relaxed">{{ guion.conflicto_central || '—' }}</p>
|
||||
<p v-if="guion.resolucion" class="text-xs text-success mt-2 leading-relaxed font-medium">→ {{ guion.resolucion }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Diagnóstico: Fortalezas / Debilidades / Sugerencias -->
|
||||
<div v-if="guion.fortalezas?.length || guion.debilidades?.length" class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="bg-surface-container p-6 rounded-3xl border border-secondary/10 shadow-xl">
|
||||
<h3 class="text-sm font-headline font-bold text-white mb-5 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-secondary text-base" style="font-variation-settings:'FILL' 1;">thumb_up</span> Fortalezas
|
||||
<!-- Fortalezas / Debilidades -->
|
||||
<div v-if="guion.fortalezas?.length || guion.debilidades?.length" class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="bg-surface rounded-xl border border-success-border p-5">
|
||||
<h3 class="text-xs font-semibold text-success uppercase tracking-wider mb-4 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-[16px]" style="font-variation-settings:'FILL' 1;">thumb_up</span> Fortalezas
|
||||
</h3>
|
||||
<div class="space-y-2">
|
||||
<div v-for="(f, i) in guion.fortalezas" :key="i" class="flex items-start gap-2">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-secondary mt-2 shrink-0"></span>
|
||||
<p class="text-xs text-on-surface-variant leading-relaxed">{{ f }}</p>
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-success mt-1.5 shrink-0"></span>
|
||||
<p class="text-xs text-ink-2 leading-relaxed">{{ f }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-surface-container p-6 rounded-3xl border border-red-500/10 shadow-xl">
|
||||
<h3 class="text-sm font-headline font-bold text-white mb-5 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-red-400 text-base">build</span> Áreas de Mejora
|
||||
<div class="bg-surface rounded-xl border border-error-border p-5">
|
||||
<h3 class="text-xs font-semibold text-error uppercase tracking-wider mb-4 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-[16px]">build</span> Áreas de Mejora
|
||||
</h3>
|
||||
<div class="space-y-2 mb-5">
|
||||
<div class="space-y-2 mb-4">
|
||||
<div v-for="(d, i) in guion.debilidades" :key="i" class="flex items-start gap-2">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-red-400 mt-2 shrink-0"></span>
|
||||
<p class="text-xs text-on-surface-variant leading-relaxed">{{ d }}</p>
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-error mt-1.5 shrink-0"></span>
|
||||
<p class="text-xs text-ink-2 leading-relaxed">{{ d }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="guion.sugerencias_mejora?.length">
|
||||
<p class="text-[9px] text-outline font-black uppercase tracking-widest mb-2">Sugerencias</p>
|
||||
<p class="text-[9px] text-ink-3 font-semibold uppercase tracking-wider mb-2">Sugerencias</p>
|
||||
<div class="space-y-1.5">
|
||||
<div v-for="(s, i) in guion.sugerencias_mejora" :key="i" class="flex items-start gap-2">
|
||||
<span class="text-[9px] font-black text-tertiary shrink-0 mt-0.5">{{ i + 1 }}.</span>
|
||||
<p class="text-xs text-on-surface-variant leading-relaxed">{{ s }}</p>
|
||||
<span class="text-[10px] font-bold text-warn shrink-0 mt-0.5">{{ i + 1 }}.</span>
|
||||
<p class="text-xs text-ink-2 leading-relaxed">{{ s }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hashtags sugeridos -->
|
||||
<div v-if="guion.hashtags_sugeridos?.length" class="bg-surface-container p-6 rounded-3xl border border-outline-variant/10 shadow-xl">
|
||||
<h3 class="text-sm font-headline font-bold text-white mb-4 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-outline text-base">tag</span> Hashtags Sugeridos
|
||||
<!-- Hashtags -->
|
||||
<div v-if="guion.hashtags_sugeridos?.length" class="bg-surface rounded-xl border border-border shadow-sm p-5">
|
||||
<h3 class="text-xs font-semibold text-ink-2 uppercase tracking-wider mb-3 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-ink-3 text-[16px]">tag</span> Hashtags Sugeridos
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span
|
||||
v-for="tag in guion.hashtags_sugeridos"
|
||||
:key="tag"
|
||||
class="px-3 py-1.5 bg-primary/10 border border-primary/20 rounded-full text-xs font-bold text-primary cursor-pointer hover:bg-primary/20 transition-colors"
|
||||
class="px-3 py-1.5 bg-accent-subtle border border-accent-border rounded-full text-xs font-medium text-accent cursor-pointer hover:bg-accent hover:text-white transition-colors"
|
||||
@click="copiarTag(tag)"
|
||||
>#{{ tag.replace(/^#/, '') }}</span>
|
||||
</div>
|
||||
<p class="text-[9px] text-outline/50 mt-3 italic">Haz clic en un hashtag para copiarlo</p>
|
||||
<p class="text-[10px] text-ink-3 mt-2">Haz clic en un hashtag para copiarlo</p>
|
||||
</div>
|
||||
|
||||
<!-- Contexto original del usuario -->
|
||||
<div v-if="guion.contexto_video" class="bg-surface-container p-5 rounded-2xl border border-tertiary/10">
|
||||
<p class="text-[10px] text-tertiary font-black uppercase tracking-widest mb-2 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-sm">lightbulb</span> Contexto ingresado
|
||||
<!-- Contexto ingresado -->
|
||||
<div v-if="guion.contexto_video" class="bg-surface rounded-xl border border-warn-border p-5">
|
||||
<p class="text-[10px] font-semibold text-warn uppercase tracking-wider mb-2 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-[14px]">lightbulb</span> Contexto ingresado
|
||||
</p>
|
||||
<p class="text-sm text-on-surface-variant leading-relaxed italic">{{ guion.contexto_video }}</p>
|
||||
<p class="text-sm text-ink-2 leading-relaxed italic">{{ guion.contexto_video }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Transcripción -->
|
||||
<div class="bg-surface-container p-6 rounded-3xl border border-outline-variant/10 shadow-xl">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-sm font-headline font-bold text-white flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-outline">notes</span> Transcripción Completa
|
||||
<div class="bg-surface rounded-xl border border-border shadow-sm p-5">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h3 class="text-xs font-semibold text-ink-2 uppercase tracking-wider flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-ink-3 text-[16px]">notes</span> Transcripción Completa
|
||||
</h3>
|
||||
<button @click="showTranscript = !showTranscript" class="text-xs font-bold uppercase tracking-widest text-primary hover:text-white transition-colors">
|
||||
<button @click="showTranscript = !showTranscript" class="text-xs font-medium text-accent hover:text-accent-hover transition-colors">
|
||||
{{ showTranscript ? 'Colapsar' : 'Expandir' }}
|
||||
</button>
|
||||
</div>
|
||||
<div :class="showTranscript ? 'max-h-[800px]' : 'max-h-24'" class="overflow-hidden relative transition-all duration-500 ease-in-out">
|
||||
<div v-if="!showTranscript" class="absolute inset-0 bg-gradient-to-t from-surface-container to-transparent z-10"></div>
|
||||
<p class="text-sm text-on-surface-variant leading-relaxed whitespace-pre-wrap font-serif pt-4">
|
||||
<div :class="showTranscript ? 'max-h-[600px]' : 'max-h-20'" class="overflow-hidden relative transition-all duration-400 ease-in-out">
|
||||
<div v-if="!showTranscript" class="absolute inset-0 bg-gradient-to-t from-surface to-transparent z-10 pointer-events-none"></div>
|
||||
<p class="text-sm text-ink-2 leading-relaxed whitespace-pre-wrap">
|
||||
{{ guion.transcript || 'Video sin transcripción disponible.' }}
|
||||
</p>
|
||||
</div>
|
||||
@ -413,8 +436,8 @@ const nivelConcienciaIndex = computed(() => {
|
||||
})
|
||||
|
||||
const ratioColor = computed(() => {
|
||||
const map = { emocional: 'text-orange-400', logico: 'text-blue-400', equilibrado: 'text-secondary' }
|
||||
return map[guion.value?.ratio_emocion_logica] || 'text-outline'
|
||||
const map = { emocional: 'text-orange-500', logico: 'text-blue-500', equilibrado: 'text-success' }
|
||||
return map[guion.value?.ratio_emocion_logica] || 'text-ink-3'
|
||||
})
|
||||
|
||||
const ratioIcon = computed(() => {
|
||||
@ -439,18 +462,18 @@ function copiarTag(tag) {
|
||||
|
||||
function plataformaBadge(p) {
|
||||
return {
|
||||
tiktok: 'bg-red-500/20 text-red-400',
|
||||
reels: 'bg-fuchsia-500/20 text-fuchsia-400',
|
||||
shorts: 'bg-red-600/20 text-red-500',
|
||||
}[p] ?? 'bg-white/5 text-on-surface-variant'
|
||||
tiktok: 'bg-red-950 text-red-400 border border-red-800',
|
||||
reels: 'bg-fuchsia-950 text-fuchsia-400 border border-fuchsia-800',
|
||||
shorts: 'bg-orange-950 text-orange-400 border border-orange-800',
|
||||
}[p] ?? 'bg-surface-muted text-ink-2 border border-border'
|
||||
}
|
||||
|
||||
function replicabilidadBadge(r) {
|
||||
return {
|
||||
alta: 'bg-secondary/10 text-secondary border border-secondary/20',
|
||||
media: 'bg-yellow-500/10 text-yellow-400 border border-yellow-500/20',
|
||||
baja: 'bg-red-500/10 text-red-400 border border-red-500/20',
|
||||
}[r] ?? 'bg-white/5 text-outline'
|
||||
alta: 'bg-success-subtle text-success border border-success-border',
|
||||
media: 'bg-warn-subtle text-warn border border-warn-border',
|
||||
baja: 'bg-error-subtle text-error border border-error-border',
|
||||
}[r] ?? 'bg-surface-muted text-ink-3 border border-border'
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
@ -1,151 +1,171 @@
|
||||
<template>
|
||||
<div class="max-w-7xl mx-auto flex flex-col gap-10">
|
||||
<div class="flex flex-col gap-8">
|
||||
|
||||
<!-- Encabezado -->
|
||||
<header class="flex flex-col md:flex-row md:items-end justify-between gap-6 border-b border-white/5 pb-8">
|
||||
<header class="flex flex-col md:flex-row md:items-end justify-between gap-4 pb-6 border-b border-border">
|
||||
<div>
|
||||
<h1 class="text-5xl font-extrabold font-headline tracking-tighter text-white mb-2 leading-tight">Historial de Análisis</h1>
|
||||
<p class="text-primary text-sm font-bold flex items-center gap-2 tracking-widest uppercase">
|
||||
<span class="material-symbols-outlined text-sm">analytics</span>
|
||||
{{ totalOk }} exitosos · {{ totalFallidos }} fallidos
|
||||
<h1 class="text-3xl font-bold font-headline text-ink mb-1">Historial de Análisis</h1>
|
||||
<p class="text-sm text-ink-3">
|
||||
<span class="text-success font-medium">{{ totalOk }} exitosos</span>
|
||||
<span class="mx-1 text-border-strong">·</span>
|
||||
<span class="text-error font-medium">{{ totalFallidos }} fallidos</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<button class="px-6 py-3 bg-surface-container border border-white/5 text-outline font-bold rounded-xl text-sm hover:text-white transition-colors" @click="cargarDatos">Actualizar</button>
|
||||
<router-link to="/new-analysis" class="px-8 py-3 bg-gradient-to-br from-primary-container to-primary text-on-primary-container font-headline font-bold rounded-xl shadow-lg shadow-primary/20 hover:scale-105 active:scale-95 transition-all text-sm h-12 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-sm">add</span> Nuevo Análisis
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
class="px-4 py-2 bg-surface border border-border text-ink-2 font-medium rounded-lg text-sm hover:bg-surface-muted transition-colors"
|
||||
@click="cargarDatos"
|
||||
>
|
||||
Actualizar
|
||||
</button>
|
||||
<router-link
|
||||
to="/new-analysis"
|
||||
class="px-5 py-2 bg-accent hover:bg-accent-hover text-white font-semibold rounded-lg text-sm flex items-center gap-2 transition-colors shadow-sm"
|
||||
>
|
||||
<span class="material-symbols-outlined text-[18px]">add</span>
|
||||
Nuevo Análisis
|
||||
</router-link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Filtros rápidos -->
|
||||
<div class="flex flex-wrap gap-3 items-center">
|
||||
<button
|
||||
v-for="f in filtrosEstado"
|
||||
:key="f.valor"
|
||||
@click="filtroActivo = f.valor; filtros.page = 1; cargarDatos()"
|
||||
class="px-4 py-2 rounded-xl text-xs font-black uppercase tracking-widest border transition-all"
|
||||
:class="filtroActivo === f.valor
|
||||
? 'bg-primary/10 border-primary/30 text-primary'
|
||||
: 'bg-surface-container border-white/5 text-outline hover:text-white hover:border-white/10'"
|
||||
>
|
||||
{{ f.label }}
|
||||
</button>
|
||||
<!-- Filtros -->
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<div class="flex items-center gap-1 bg-surface border border-border rounded-lg p-1">
|
||||
<button
|
||||
v-for="f in filtrosEstado"
|
||||
:key="f.valor"
|
||||
@click="filtroActivo = f.valor; filtros.page = 1; cargarDatos()"
|
||||
class="px-3 py-1.5 rounded-md text-xs font-medium transition-all"
|
||||
:class="filtroActivo === f.valor
|
||||
? 'bg-accent text-white shadow-sm'
|
||||
: 'text-ink-2 hover:bg-surface-muted'"
|
||||
>
|
||||
{{ f.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="ml-auto flex items-center gap-3">
|
||||
<select v-model="filtros.niche" @change="filtros.page = 1; cargarDatos()" class="bg-surface-container border border-white/10 rounded-xl px-4 py-2 text-xs text-on-surface-variant focus:ring-2 focus:ring-primary/40 appearance-none">
|
||||
<div class="ml-auto">
|
||||
<select
|
||||
v-model="filtros.niche"
|
||||
@change="filtros.page = 1; cargarDatos()"
|
||||
class="bg-surface border border-border rounded-lg px-3 py-2 text-xs text-ink-2 focus:outline-none focus:ring-2 focus:ring-accent/30 appearance-none"
|
||||
>
|
||||
<option value="">Todos los nichos</option>
|
||||
<option v-for="n in nichos" :key="n" :value="n">{{ n }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabla de historial -->
|
||||
<div class="bg-surface-container rounded-3xl border border-outline-variant/10 shadow-2xl overflow-hidden">
|
||||
<div class="px-8 py-5 border-b border-white/5 bg-surface-container-high/50 flex items-center justify-between">
|
||||
<h3 class="text-xs font-black text-white uppercase tracking-widest flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-outline text-base">history</span> Registro completo
|
||||
</h3>
|
||||
<div class="flex gap-2">
|
||||
<button class="px-4 py-1.5 bg-surface-container-low text-[10px] font-black uppercase text-outline rounded-lg hover:text-white transition-colors disabled:opacity-30" :disabled="filtros.page <= 1" @click="cambiarPagina(filtros.page - 1)">Anterior</button>
|
||||
<span class="px-3 py-1.5 text-[10px] font-black text-outline">Pág. {{ filtros.page }}</span>
|
||||
<button class="px-4 py-1.5 bg-surface-container-low text-[10px] font-black uppercase text-outline rounded-lg hover:text-white transition-colors disabled:opacity-30" :disabled="guiones.length < filtros.limit" @click="cambiarPagina(filtros.page + 1)">Siguiente</button>
|
||||
<!-- Tabla -->
|
||||
<div class="bg-surface rounded-xl border border-border shadow-sm overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-border flex items-center justify-between">
|
||||
<h2 class="text-sm font-semibold text-ink">Registro completo</h2>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
class="px-3 py-1.5 bg-canvas border border-border text-[11px] font-medium text-ink-2 rounded-md hover:bg-surface-muted transition-colors disabled:opacity-40"
|
||||
:disabled="filtros.page <= 1"
|
||||
@click="cambiarPagina(filtros.page - 1)"
|
||||
>Anterior</button>
|
||||
<span class="text-[11px] text-ink-3 font-medium px-2">Pág. {{ filtros.page }}</span>
|
||||
<button
|
||||
class="px-3 py-1.5 bg-canvas border border-border text-[11px] font-medium text-ink-2 rounded-md hover:bg-surface-muted transition-colors disabled:opacity-40"
|
||||
:disabled="guiones.length < filtros.limit"
|
||||
@click="cambiarPagina(filtros.page + 1)"
|
||||
>Siguiente</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading -->
|
||||
<div v-if="cargando" class="py-16 flex items-center justify-center gap-3">
|
||||
<div class="w-6 h-6 border-2 border-primary border-t-transparent rounded-full animate-spin"></div>
|
||||
<span class="text-outline text-sm animate-pulse">Cargando historial...</span>
|
||||
<div class="w-5 h-5 border-2 border-accent border-t-transparent rounded-full animate-spin"></div>
|
||||
<span class="text-sm text-ink-3">Cargando historial…</span>
|
||||
</div>
|
||||
|
||||
<!-- Vacío -->
|
||||
<div v-else-if="guiones.length === 0" class="py-20 flex flex-col items-center justify-center gap-3 opacity-40">
|
||||
<span class="material-symbols-outlined text-5xl">manage_search</span>
|
||||
<p class="text-sm font-bold uppercase tracking-widest text-outline">Sin registros para este filtro</p>
|
||||
<div v-else-if="guiones.length === 0" class="py-20 flex flex-col items-center gap-3 text-ink-3">
|
||||
<span class="material-symbols-outlined text-4xl">manage_search</span>
|
||||
<p class="text-sm font-medium">Sin registros para este filtro</p>
|
||||
</div>
|
||||
|
||||
<!-- Tabla -->
|
||||
<!-- Tabla con datos -->
|
||||
<div v-else class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr class="text-[10px] font-black text-outline uppercase tracking-widest bg-surface-container-high/30 border-b border-white/5">
|
||||
<th class="px-8 py-4">Estado</th>
|
||||
<th class="px-6 py-4">Fuente</th>
|
||||
<th class="px-6 py-4">Niche</th>
|
||||
<th class="px-6 py-4">Puntajes</th>
|
||||
<th class="px-6 py-4">Fecha</th>
|
||||
<th class="px-8 py-4 text-right">Acción</th>
|
||||
<tr class="border-b border-border bg-surface-muted/60">
|
||||
<th class="px-6 py-3 text-[11px] font-semibold text-ink-3 uppercase tracking-wider">Estado</th>
|
||||
<th class="px-4 py-3 text-[11px] font-semibold text-ink-3 uppercase tracking-wider">Video / Fuente</th>
|
||||
<th class="px-4 py-3 text-[11px] font-semibold text-ink-3 uppercase tracking-wider">Niche</th>
|
||||
<th class="px-4 py-3 text-[11px] font-semibold text-ink-3 uppercase tracking-wider">Puntajes</th>
|
||||
<th class="px-4 py-3 text-[11px] font-semibold text-ink-3 uppercase tracking-wider">Fecha</th>
|
||||
<th class="px-6 py-3"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-white/5">
|
||||
<tbody class="divide-y divide-border">
|
||||
<tr
|
||||
v-for="g in guiones"
|
||||
:key="g.id"
|
||||
class="group hover:bg-white/[0.02] transition-colors"
|
||||
class="group hover:bg-surface-muted/60 transition-colors"
|
||||
:class="g.procesado_ok ? 'cursor-pointer' : 'opacity-60'"
|
||||
@click="g.procesado_ok && verDetalle(g.id)"
|
||||
>
|
||||
<!-- Estado -->
|
||||
<td class="px-8 py-5">
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span
|
||||
class="w-2 h-2 rounded-full shrink-0"
|
||||
:class="g.procesado_ok ? 'bg-secondary' : 'bg-red-500'"
|
||||
></span>
|
||||
<span class="text-[10px] font-black uppercase tracking-widest" :class="g.procesado_ok ? 'text-secondary' : 'text-red-400'">
|
||||
<span class="w-2 h-2 rounded-full shrink-0" :class="g.procesado_ok ? 'bg-success' : 'bg-error'"></span>
|
||||
<span class="text-xs font-medium" :class="g.procesado_ok ? 'text-success' : 'text-error'">
|
||||
{{ g.procesado_ok ? 'Completado' : 'Fallido' }}
|
||||
</span>
|
||||
</div>
|
||||
<p v-if="!g.procesado_ok && g.error_detalle" class="text-[9px] text-red-400/60 mt-1 max-w-[160px] truncate">{{ g.error_detalle }}</p>
|
||||
<p v-if="!g.procesado_ok && g.error_detalle" class="text-[10px] text-error/70 mt-0.5 max-w-[140px] truncate">{{ g.error_detalle }}</p>
|
||||
</td>
|
||||
|
||||
<!-- Fuente -->
|
||||
<td class="px-6 py-5">
|
||||
<td class="px-4 py-4">
|
||||
<div class="flex flex-col gap-1">
|
||||
<span :class="plataformaBadge(g.plataforma)" class="text-[8px] font-black px-1.5 py-0.5 rounded uppercase tracking-widest w-fit">{{ g.plataforma || '—' }}</span>
|
||||
<p class="text-xs font-bold text-white leading-tight line-clamp-1 max-w-[220px]">{{ g.tema_principal || g.url_origen }}</p>
|
||||
<p class="text-[9px] text-outline/50 truncate max-w-[220px] font-medium">{{ g.url_origen }}</p>
|
||||
<span :class="plataformaBadge(g.plataforma)" class="platform-badge w-fit">{{ g.plataforma || '—' }}</span>
|
||||
<p class="text-sm font-medium text-ink line-clamp-1 max-w-52">{{ g.tema_principal || g.url_origen }}</p>
|
||||
<p class="text-[10px] text-ink-3 truncate max-w-52">{{ g.url_origen }}</p>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- Niche -->
|
||||
<td class="px-6 py-5">
|
||||
<span class="text-xs font-black text-on-surface-variant uppercase tracking-wider">{{ g.niche || '—' }}</span>
|
||||
<p v-if="g.sub_niche" class="text-[9px] text-outline/60 mt-0.5">{{ g.sub_niche }}</p>
|
||||
<td class="px-4 py-4">
|
||||
<p class="text-sm font-medium text-ink-2">{{ g.niche || '—' }}</p>
|
||||
<p v-if="g.sub_niche" class="text-[10px] text-ink-3 mt-0.5">{{ g.sub_niche }}</p>
|
||||
</td>
|
||||
|
||||
<!-- Puntajes -->
|
||||
<td class="px-6 py-5">
|
||||
<div v-if="g.procesado_ok" class="flex items-center gap-3 font-headline">
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-outline font-black uppercase">Viral</p>
|
||||
<p class="text-sm font-black text-white">{{ g.score_virabilidad || 0 }}</p>
|
||||
<td class="px-4 py-4">
|
||||
<div v-if="g.procesado_ok" class="flex items-center gap-4">
|
||||
<div>
|
||||
<p class="text-[10px] text-ink-3 font-medium">Viral</p>
|
||||
<p class="text-sm font-bold text-ink">{{ g.score_virabilidad || 0 }}</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-outline font-black uppercase">Cialdini</p>
|
||||
<p class="text-sm font-black text-white">{{ g.score_cialdini || 0 }}/7</p>
|
||||
<div>
|
||||
<p class="text-[10px] text-ink-3 font-medium">Cialdini</p>
|
||||
<p class="text-sm font-bold text-ink">{{ g.score_cialdini || 0 }}/7</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-outline font-black uppercase">ENG</p>
|
||||
<p class="text-sm font-black text-secondary">{{ (g.score_engagement || 0).toFixed(1) }}%</p>
|
||||
<div>
|
||||
<p class="text-[10px] text-ink-3 font-medium">Eng.</p>
|
||||
<p class="text-sm font-bold text-success">{{ (g.score_engagement || 0).toFixed(1) }}%</p>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else class="text-[10px] text-outline/40 italic">—</span>
|
||||
<span v-else class="text-[11px] text-ink-3">—</span>
|
||||
</td>
|
||||
|
||||
<!-- Fecha -->
|
||||
<td class="px-6 py-5">
|
||||
<p class="text-xs font-bold text-on-surface-variant">{{ formatFecha(g.fecha_analisis) }}</p>
|
||||
<td class="px-4 py-4">
|
||||
<p class="text-xs text-ink-2">{{ formatFecha(g.fecha_analisis) }}</p>
|
||||
</td>
|
||||
|
||||
<!-- Acción -->
|
||||
<td class="px-8 py-5 text-right">
|
||||
<td class="px-6 py-4 text-right">
|
||||
<button
|
||||
v-if="g.procesado_ok"
|
||||
@click.stop="verDetalle(g.id)"
|
||||
class="p-2 rounded-xl bg-surface-container-low border border-white/5 text-outline hover:text-white hover:border-primary/20 transition-all opacity-0 group-hover:opacity-100 scale-90 group-hover:scale-100"
|
||||
class="p-1.5 rounded-lg text-ink-3 hover:text-accent hover:bg-accent-subtle transition-all opacity-0 group-hover:opacity-100"
|
||||
>
|
||||
<span class="material-symbols-outlined text-base">open_in_new</span>
|
||||
<span class="material-symbols-outlined text-[18px]">open_in_new</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@ -170,9 +190,9 @@ const filtroActivo = ref('todos')
|
||||
const filtros = ref({ page: 1, limit: 20, niche: '' })
|
||||
|
||||
const filtrosEstado = [
|
||||
{ valor: 'todos', label: 'Todos' },
|
||||
{ valor: 'exitosos', label: 'Exitosos' },
|
||||
{ valor: 'fallidos', label: 'Fallidos' },
|
||||
{ valor: 'todos', label: 'Todos' },
|
||||
{ valor: 'exitosos', label: 'Exitosos' },
|
||||
{ valor: 'fallidos', label: 'Fallidos' },
|
||||
]
|
||||
|
||||
const totalOk = computed(() => guiones.value.filter(g => g.procesado_ok).length)
|
||||
@ -219,11 +239,11 @@ function formatFecha(fecha) {
|
||||
|
||||
function plataformaBadge(p) {
|
||||
const map = {
|
||||
tiktok: 'bg-red-500/10 text-red-500 border border-red-500/20',
|
||||
reels: 'bg-fuchsia-500/10 text-fuchsia-500 border border-fuchsia-500/20',
|
||||
shorts: 'bg-red-600/10 text-red-600 border border-red-600/20',
|
||||
tiktok: 'bg-red-950 text-red-400 border border-red-800',
|
||||
reels: 'bg-fuchsia-950 text-fuchsia-400 border border-fuchsia-800',
|
||||
shorts: 'bg-orange-950 text-orange-400 border border-orange-800',
|
||||
}
|
||||
return map[p] || 'bg-white/5 text-outline'
|
||||
return map[p] || 'bg-surface-muted text-ink-3 border border-border'
|
||||
}
|
||||
|
||||
onMounted(cargarDatos)
|
||||
|
||||
@ -1,110 +1,145 @@
|
||||
<template>
|
||||
<div class="max-w-7xl mx-auto flex flex-col gap-10">
|
||||
<div class="flex flex-col gap-8">
|
||||
|
||||
<!-- Encabezado -->
|
||||
<header class="flex flex-col md:flex-row md:items-end justify-between gap-6 border-b border-white/5 pb-8">
|
||||
<header class="flex flex-col md:flex-row md:items-end justify-between gap-4 pb-6 border-b border-border">
|
||||
<div>
|
||||
<h1 class="text-5xl font-extrabold font-headline tracking-tighter text-white mb-2 leading-tight">Panel Principal</h1>
|
||||
<p class="text-primary text-sm font-bold flex items-center gap-2 tracking-widest uppercase">
|
||||
<span class="material-symbols-outlined text-sm">terminal</span>
|
||||
Resumen del sistema y métricas de rendimiento
|
||||
</p>
|
||||
<h1 class="text-3xl font-bold font-headline text-ink mb-1">Panel Principal</h1>
|
||||
<p class="text-sm text-ink-3">Resumen de métricas y guiones analizados</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<button class="px-6 py-3 bg-surface-container border border-white/5 text-outline font-bold rounded-xl text-sm hover:text-white transition-colors" @click="cargarDatos">Actualizar</button>
|
||||
<router-link to="/new-analysis" class="px-8 py-3 bg-gradient-to-br from-primary-container to-primary text-on-primary-container font-headline font-bold rounded-xl shadow-lg shadow-primary/20 hover:scale-105 active:scale-95 transition-all text-sm h-12 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-sm">add</span> Nuevo Análisis
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
class="px-4 py-2 bg-surface border border-border text-ink-2 font-medium rounded-lg text-sm hover:bg-surface-muted transition-colors"
|
||||
@click="cargarDatos"
|
||||
>
|
||||
Actualizar
|
||||
</button>
|
||||
<router-link
|
||||
to="/new-analysis"
|
||||
class="px-5 py-2 bg-accent hover:bg-accent-hover text-white font-semibold rounded-lg text-sm flex items-center gap-2 transition-colors shadow-sm"
|
||||
>
|
||||
<span class="material-symbols-outlined text-[18px]">add</span>
|
||||
Nuevo Análisis
|
||||
</router-link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- KPI por Niche -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div v-for="stat in stats" :key="stat.niche" class="bg-surface-container p-6 rounded-3xl border border-outline-variant/10 shadow-xl relative overflow-hidden group">
|
||||
<div class="absolute -bottom-6 -right-6 w-24 h-24 bg-primary/5 blur-2xl rounded-full group-hover:bg-primary/10 transition-colors"></div>
|
||||
<p class="text-[10px] text-outline uppercase tracking-widest font-black mb-1">{{ stat.niche }}</p>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div
|
||||
v-for="stat in stats"
|
||||
:key="stat.niche"
|
||||
class="bg-surface rounded-xl border border-border p-5 shadow-sm"
|
||||
>
|
||||
<p class="text-[11px] font-semibold text-ink-3 uppercase tracking-wider mb-3">{{ stat.niche }}</p>
|
||||
<div class="flex items-end justify-between">
|
||||
<h2 class="text-3xl font-black font-headline text-white tracking-tighter">{{ stat.total_guiones }}</h2>
|
||||
<span class="text-3xl font-bold font-headline text-ink">{{ stat.total_guiones }}</span>
|
||||
<div class="text-right">
|
||||
<p class="text-[10px] text-primary font-bold uppercase tracking-tighter">Puntaje Prom.</p>
|
||||
<p class="text-lg font-black text-primary leading-none">{{ (stat.avg_score || 0).toFixed(1) }}</p>
|
||||
<p class="text-[10px] text-ink-3 mb-0.5">Puntaje prom.</p>
|
||||
<p class="text-lg font-bold text-accent">{{ (stat.avg_score || 0).toFixed(1) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 w-full bg-surface-container-highest h-1 rounded-full overflow-hidden">
|
||||
<div class="bg-primary h-full transition-all duration-1000" :style="{ width: (stat.avg_score || 0) + '%' }"></div>
|
||||
<div class="mt-3 w-full bg-surface-subtle h-1 rounded-full overflow-hidden">
|
||||
<div
|
||||
class="bg-accent h-full transition-all duration-700 rounded-full"
|
||||
:style="{ width: (stat.avg_score || 0) + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Placeholder sin datos -->
|
||||
<div v-if="stats.length === 0" class="lg:col-span-4 bg-surface-container/50 border border-dashed border-white/5 p-8 rounded-3xl flex items-center justify-center italic text-outline text-sm">
|
||||
<div
|
||||
v-if="stats.length === 0"
|
||||
class="lg:col-span-4 bg-surface border border-dashed border-border rounded-xl p-8 flex items-center justify-center text-sm text-ink-3 italic"
|
||||
>
|
||||
Conecta la base de datos para ver el rendimiento por niche.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contenido Principal -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-10">
|
||||
<!-- Tabla de Guiones -->
|
||||
<div class="lg:col-span-12 xl:col-span-8 bg-surface-container rounded-3xl border border-outline-variant/10 shadow-2xl overflow-hidden flex flex-col">
|
||||
<div class="px-8 py-6 border-b border-white/5 bg-surface-container-high/50 flex items-center justify-between">
|
||||
<h3 class="text-sm font-headline font-black text-white uppercase tracking-widest flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-outline text-lg">database</span> Guiones Analizados
|
||||
</h3>
|
||||
<!-- Contenido principal -->
|
||||
<div class="grid grid-cols-1 xl:grid-cols-12 gap-6">
|
||||
|
||||
<!-- Tabla de guiones -->
|
||||
<div class="xl:col-span-8 bg-surface rounded-xl border border-border shadow-sm overflow-hidden flex flex-col">
|
||||
<div class="px-6 py-4 border-b border-border flex items-center justify-between">
|
||||
<h2 class="text-sm font-semibold text-ink">Guiones Analizados</h2>
|
||||
<div class="flex gap-2">
|
||||
<button class="px-4 py-2 bg-surface-container-low text-[10px] font-black uppercase text-outline rounded hover:text-white transition-colors" @click="cambiarPagina(filtros.page - 1)" :disabled="filtros.page <= 1">Anterior</button>
|
||||
<button class="px-4 py-2 bg-surface-container-low text-[10px] font-black uppercase text-outline rounded hover:text-white transition-colors" @click="cambiarPagina(filtros.page + 1)" :disabled="guiones.length < filtros.limit">Siguiente</button>
|
||||
<button
|
||||
class="px-3 py-1.5 bg-canvas border border-border text-[11px] font-medium text-ink-2 rounded-md hover:bg-surface-muted transition-colors disabled:opacity-40"
|
||||
@click="cambiarPagina(filtros.page - 1)"
|
||||
:disabled="filtros.page <= 1"
|
||||
>Anterior</button>
|
||||
<button
|
||||
class="px-3 py-1.5 bg-canvas border border-border text-[11px] font-medium text-ink-2 rounded-md hover:bg-surface-muted transition-colors disabled:opacity-40"
|
||||
@click="cambiarPagina(filtros.page + 1)"
|
||||
:disabled="guiones.length < filtros.limit"
|
||||
>Siguiente</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto flex-1 max-h-[600px] scrollbar-custom">
|
||||
<div class="overflow-x-auto max-h-[520px]">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead class="sticky top-0 bg-surface-container z-10 border-b border-white/5">
|
||||
<tr class="text-[10px] font-black text-outline uppercase tracking-widest bg-surface-container-high/30">
|
||||
<th class="px-8 py-4">Contexto</th>
|
||||
<th class="px-6 py-4">Puntaje Viral</th>
|
||||
<th class="px-6 py-4">Patrón Neurométrico</th>
|
||||
<th class="px-8 py-4 text-right">Acciones</th>
|
||||
<thead class="sticky top-0 bg-surface-muted z-10">
|
||||
<tr class="border-b border-border">
|
||||
<th class="px-6 py-3 text-[11px] font-semibold text-ink-3 uppercase tracking-wider">Video</th>
|
||||
<th class="px-4 py-3 text-[11px] font-semibold text-ink-3 uppercase tracking-wider">Viralidad</th>
|
||||
<th class="px-4 py-3 text-[11px] font-semibold text-ink-3 uppercase tracking-wider">Gancho</th>
|
||||
<th class="px-6 py-3"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-white/5">
|
||||
<tr v-for="g in guiones" :key="g.id" class="group hover:bg-white/[0.02] transition-colors cursor-pointer" @click="verDetalle(g.id)">
|
||||
<td class="px-8 py-6">
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<tbody class="divide-y divide-border">
|
||||
<tr
|
||||
v-for="g in guiones"
|
||||
:key="g.id"
|
||||
class="group hover:bg-surface-muted/60 transition-colors cursor-pointer"
|
||||
@click="verDetalle(g.id)"
|
||||
>
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<span :class="plataformaBadge(g.plataforma)" class="text-[8px] font-black px-1.5 py-0.5 rounded uppercase tracking-widest">{{ g.plataforma }}</span>
|
||||
<span class="text-[10px] text-outline font-bold uppercase tracking-wider">{{ g.niche }}</span>
|
||||
<span :class="plataformaBadge(g.plataforma)" class="platform-badge">{{ g.plataforma }}</span>
|
||||
<span class="text-[11px] font-medium text-ink-3 uppercase tracking-wide">{{ g.niche }}</span>
|
||||
</div>
|
||||
<p class="text-sm font-bold text-white leading-tight group-hover:text-primary transition-colors line-clamp-2 max-w-sm">{{ g.tema_principal || 'Sin título detectado' }}</p>
|
||||
<p class="text-[10px] text-outline/60 italic font-medium truncate max-w-xs">{{ g.url_origen }}</p>
|
||||
<p class="text-sm font-medium text-ink leading-tight group-hover:text-accent transition-colors line-clamp-1 max-w-xs">
|
||||
{{ g.tema_principal || 'Sin título detectado' }}
|
||||
</p>
|
||||
<p class="text-[10px] text-ink-3 truncate max-w-xs">{{ g.url_origen }}</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-6 font-headline">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex items-center justify-between text-[11px] font-black tracking-tighter">
|
||||
<span class="text-white">{{ g.score_virabilidad || 0 }}/100</span>
|
||||
<span class="text-primary">{{ (g.score_engagement || 0).toFixed(1) }}% ENG</span>
|
||||
|
||||
<td class="px-4 py-4">
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<div class="flex items-center gap-2 text-[11px]">
|
||||
<span class="font-bold text-ink">{{ g.score_virabilidad || 0 }}/100</span>
|
||||
<span class="text-success text-[10px]">{{ (g.score_engagement || 0).toFixed(1) }}%</span>
|
||||
</div>
|
||||
<div class="w-32 bg-surface-container-highest h-1.5 rounded-full overflow-hidden">
|
||||
<div class="bg-gradient-to-r from-primary/50 to-primary h-full" :style="{ width: (g.score_virabilidad || 0) + '%' }"></div>
|
||||
<div class="w-24 bg-surface-subtle h-1 rounded-full overflow-hidden">
|
||||
<div
|
||||
class="bg-accent h-full rounded-full"
|
||||
:style="{ width: (g.score_virabilidad || 0) + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-6">
|
||||
<div class="max-w-xs">
|
||||
<p class="text-[11px] text-outline font-bold uppercase tracking-widest mb-1">{{ g.gancho_tipo || 'Gancho' }}</p>
|
||||
<p class="text-xs text-on-surface-variant line-clamp-2 leading-relaxed italic">"{{ g.gancho_texto }}"</p>
|
||||
|
||||
<td class="px-4 py-4">
|
||||
<div class="max-w-56">
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-0.5">{{ g.gancho_tipo || 'Gancho' }}</p>
|
||||
<p class="text-xs text-ink-2 line-clamp-2 leading-relaxed italic">"{{ g.gancho_texto }}"</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-8 py-6 text-right">
|
||||
<button class="p-2.5 rounded-xl bg-surface-container-low border border-white/5 text-outline hover:text-white hover:border-primary/20 transition-all opacity-0 group-hover:opacity-100 scale-90 group-hover:scale-100">
|
||||
<span class="material-symbols-outlined text-xl">open_in_new</span>
|
||||
|
||||
<td class="px-6 py-4 text-right">
|
||||
<button class="p-1.5 rounded-lg text-ink-3 hover:text-accent hover:bg-accent-subtle transition-all opacity-0 group-hover:opacity-100">
|
||||
<span class="material-symbols-outlined text-[18px]">open_in_new</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr v-if="guiones.length === 0 && !cargando">
|
||||
<td colspan="4" class="py-24 text-center">
|
||||
<div class="flex flex-col items-center gap-2 opacity-30">
|
||||
<span class="material-symbols-outlined text-5xl">inventory_2</span>
|
||||
<p class="text-sm font-bold uppercase tracking-widest">Repositorio vacío</p>
|
||||
<td colspan="4" class="py-20 text-center">
|
||||
<div class="flex flex-col items-center gap-2 text-ink-3">
|
||||
<span class="material-symbols-outlined text-4xl">inbox</span>
|
||||
<p class="text-sm font-medium">Sin guiones analizados</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -113,48 +148,51 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Columna derecha: Mejor Guión -->
|
||||
<div class="lg:col-span-12 xl:col-span-4 flex flex-col gap-8">
|
||||
<div class="bg-surface-container p-8 rounded-[40px] border border-secondary/20 shadow-3xl relative overflow-hidden flex-1 flex flex-col gap-8 group">
|
||||
<div class="absolute -top-12 -right-12 w-48 h-48 bg-secondary/5 blur-3xl rounded-full group-hover:bg-secondary/10 transition-colors"></div>
|
||||
<!-- Mejor Guión -->
|
||||
<div class="xl:col-span-4 bg-surface rounded-xl border border-border shadow-sm p-6 flex flex-col gap-5">
|
||||
<div>
|
||||
<div class="flex items-center gap-1.5 text-success text-[11px] font-semibold uppercase tracking-wider mb-2">
|
||||
<span class="material-symbols-outlined text-sm">star</span>
|
||||
Mejor Rendimiento
|
||||
</div>
|
||||
<h2 class="text-lg font-bold font-headline text-ink">Guión Destacado</h2>
|
||||
<p class="text-xs text-ink-3 mt-0.5">Mayor puntaje de viralidad en tu biblioteca</p>
|
||||
</div>
|
||||
|
||||
<div v-if="guionTop" class="flex flex-col gap-4 flex-1">
|
||||
<div class="p-4 rounded-lg bg-surface-muted border border-border">
|
||||
<p class="text-[10px] font-semibold text-success uppercase tracking-wider mb-1">
|
||||
{{ guionTop.niche }}{{ guionTop.sub_niche ? ` · ${guionTop.sub_niche}` : '' }}
|
||||
</p>
|
||||
<p class="text-sm font-semibold text-ink leading-snug">{{ guionTop.tema_principal }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-center gap-2 text-secondary font-black text-[10px] uppercase tracking-[0.2em] mb-3">
|
||||
<span class="material-symbols-outlined text-sm animate-pulse">new_releases</span> Selección del Editor
|
||||
</div>
|
||||
<h3 class="text-3xl font-headline font-black text-white tracking-widest leading-none mb-2">GUIÓN MAESTRO</h3>
|
||||
<p class="text-xs text-outline font-bold">Patrón de neuromarketing con mayor rendimiento</p>
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-1.5">Patrón narrativo</p>
|
||||
<p class="text-xs text-ink-2 leading-relaxed line-clamp-4">{{ guionTop.resumen_patron }}</p>
|
||||
</div>
|
||||
|
||||
<div v-if="guionTop" class="space-y-6 flex-1 flex flex-col">
|
||||
<div class="p-4 rounded-2xl bg-surface-container-low border border-white/5 flex flex-col gap-2">
|
||||
<p class="text-[10px] text-secondary font-bold uppercase tracking-widest">{{ guionTop.niche }} • {{ guionTop.sub_niche }}</p>
|
||||
<p class="text-lg font-black text-white leading-tight font-headline">{{ guionTop.tema_principal }}</p>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div class="p-3 rounded-lg bg-surface-muted border border-border text-center">
|
||||
<p class="text-[10px] text-ink-3 font-medium mb-1">Cialdini</p>
|
||||
<p class="text-xl font-bold text-ink">{{ guionTop.score_cialdini }}<span class="text-xs text-ink-3">/7</span></p>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 space-y-4">
|
||||
<div>
|
||||
<p class="text-[10px] text-outline font-black uppercase tracking-widest mb-2">Núcleo Narrativo</p>
|
||||
<p class="text-sm text-on-surface-variant leading-relaxed line-clamp-4">{{ guionTop.resumen_patron }}</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="p-3 rounded-xl bg-surface-container-low border border-white/5">
|
||||
<p class="text-[10px] text-outline font-bold uppercase mb-1">Cialdini</p>
|
||||
<p class="text-xl font-black text-white">{{ guionTop.score_cialdini }}/7</p>
|
||||
</div>
|
||||
<div class="p-3 rounded-xl bg-surface-container-low border border-white/5">
|
||||
<p class="text-[10px] text-outline font-bold uppercase mb-1">Viralidad</p>
|
||||
<p class="text-xl font-black text-white">{{ guionTop.score_virabilidad }}%</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-3 rounded-lg bg-accent-subtle border border-accent-border text-center">
|
||||
<p class="text-[10px] text-accent font-medium mb-1">Viralidad</p>
|
||||
<p class="text-xl font-bold text-accent">{{ guionTop.score_virabilidad }}<span class="text-xs">%</span></p>
|
||||
</div>
|
||||
|
||||
<button @click="verDetalle(guionTop.id)" class="w-full py-4 bg-white text-surface rounded-2xl font-black text-sm uppercase tracking-widest hover:bg-secondary hover:text-white transition-all transform active:scale-95 shadow-xl shadow-white/5">Ver Análisis Completo</button>
|
||||
</div>
|
||||
|
||||
<div v-else class="flex flex-col items-center justify-center flex-1 italic text-outline text-xs opacity-50">
|
||||
Aún no hay patrones analizados.
|
||||
</div>
|
||||
<button
|
||||
@click="verDetalle(guionTop.id)"
|
||||
class="w-full py-2.5 bg-ink hover:bg-ink-2 text-surface rounded-lg font-semibold text-sm transition-colors mt-auto"
|
||||
>
|
||||
Ver análisis completo
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-else class="flex flex-col items-center justify-center flex-1 py-8 text-ink-3 text-sm italic">
|
||||
Aún no hay guiones analizados.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -209,11 +247,11 @@ function verDetalle(id) {
|
||||
|
||||
function plataformaBadge(p) {
|
||||
const map = {
|
||||
tiktok: 'bg-red-500/10 text-red-500 border border-red-500/20',
|
||||
reels: 'bg-fuchsia-500/10 text-fuchsia-500 border border-fuchsia-500/20',
|
||||
shorts: 'bg-red-600/10 text-red-600 border border-red-600/20'
|
||||
tiktok: 'bg-red-950 text-red-400 border border-red-800',
|
||||
reels: 'bg-fuchsia-950 text-fuchsia-400 border border-fuchsia-800',
|
||||
shorts: 'bg-orange-950 text-orange-400 border border-orange-800',
|
||||
}
|
||||
return map[p] || 'bg-white/5 text-outline'
|
||||
return map[p] || 'bg-surface-muted text-ink-3 border border-border'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@ -1,76 +1,97 @@
|
||||
<template>
|
||||
<div class="max-w-7xl mx-auto flex flex-col gap-10">
|
||||
<div class="flex flex-col gap-8">
|
||||
|
||||
<!-- Encabezado -->
|
||||
<header class="flex flex-col md:flex-row md:items-end justify-between gap-6 border-b border-white/5 pb-8">
|
||||
<div>
|
||||
<h1 class="text-5xl font-extrabold font-headline tracking-tighter text-white mb-2 leading-tight">Generador de Guiones</h1>
|
||||
<p class="text-secondary text-sm font-bold flex items-center gap-2 tracking-widest uppercase">
|
||||
<span class="material-symbols-outlined text-sm">auto_fix_high</span>
|
||||
IA que aprende de tus patrones de alto rendimiento
|
||||
</p>
|
||||
</div>
|
||||
<header class="pb-6 border-b border-border">
|
||||
<h1 class="text-3xl font-bold font-headline text-ink mb-1">Generar Guion</h1>
|
||||
<p class="text-sm text-ink-3">IA que aprende de tus patrones de mayor rendimiento</p>
|
||||
</header>
|
||||
|
||||
<div class="grid grid-cols-1 xl:grid-cols-12 gap-10">
|
||||
<!-- Formulario -->
|
||||
<div class="xl:col-span-7 flex flex-col gap-8">
|
||||
<div class="grid grid-cols-1 xl:grid-cols-12 gap-8">
|
||||
|
||||
<!-- Formulario (izquierda) -->
|
||||
<div class="xl:col-span-7 flex flex-col gap-6">
|
||||
|
||||
<!-- Paso 1: Contexto -->
|
||||
<section class="space-y-6">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="w-8 h-8 rounded-lg bg-secondary/10 text-secondary flex items-center justify-center font-black text-sm border border-secondary/20">01</span>
|
||||
<h2 class="text-xl font-headline font-extrabold text-white tracking-tight">Contexto del Guion</h2>
|
||||
<section class="bg-surface rounded-xl border border-border shadow-sm overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-border bg-surface-muted/50 flex items-center gap-3">
|
||||
<span class="w-6 h-6 rounded-md bg-accent text-white text-xs font-bold flex items-center justify-center">1</span>
|
||||
<h2 class="text-sm font-semibold text-ink">Contexto del Guion</h2>
|
||||
</div>
|
||||
<div class="bg-surface-container p-8 rounded-3xl border border-white/5 shadow-2xl space-y-6">
|
||||
<div class="grid grid-cols-2 gap-6">
|
||||
<div class="space-y-2">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline">Nicho</label>
|
||||
<input v-model="form.niche" list="nichos-gen" placeholder="ej. fitness, finanzas..." class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm text-white placeholder:text-outline/40 focus:ring-2 focus:ring-secondary/40 font-black uppercase tracking-widest" :disabled="generando" />
|
||||
<div class="p-6 space-y-5">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Nicho</label>
|
||||
<input
|
||||
v-model="form.niche"
|
||||
list="nichos-gen"
|
||||
placeholder="ej. fitness, finanzas…"
|
||||
class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink placeholder:text-ink-3 focus:outline-none focus:ring-2 focus:ring-accent/30 focus:border-accent/40 transition-all"
|
||||
:disabled="generando"
|
||||
/>
|
||||
<datalist id="nichos-gen">
|
||||
<option v-for="n in nichos" :key="n" :value="n" />
|
||||
</datalist>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline">Plataforma</label>
|
||||
<select v-model="form.plataforma" class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm text-on-surface appearance-none focus:ring-2 focus:ring-secondary/40" :disabled="generando">
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Plataforma</label>
|
||||
<select
|
||||
v-model="form.plataforma"
|
||||
class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-accent/30 appearance-none transition-all"
|
||||
:disabled="generando"
|
||||
>
|
||||
<option value="tiktok">TikTok</option>
|
||||
<option value="reels">Instagram Reels</option>
|
||||
<option value="shorts">YouTube Shorts</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline">Tema del Video</label>
|
||||
<input v-model="form.tema" type="text" placeholder="ej. Cómo perder 5kg en 30 días sin pasar hambre" class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm text-white placeholder:text-outline/40 focus:ring-2 focus:ring-secondary/40" :disabled="generando" />
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Tema del Video</label>
|
||||
<input
|
||||
v-model="form.tema"
|
||||
type="text"
|
||||
placeholder="ej. Cómo perder 5kg en 30 días sin pasar hambre"
|
||||
class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink placeholder:text-ink-3 focus:outline-none focus:ring-2 focus:ring-accent/30 transition-all"
|
||||
:disabled="generando"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline">Audiencia Objetivo</label>
|
||||
<input v-model="form.audiencia" type="text" placeholder="ej. Mujeres de 25-40 años con poco tiempo para el gimnasio" class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm text-white placeholder:text-outline/40 focus:ring-2 focus:ring-secondary/40" :disabled="generando" />
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Audiencia Objetivo</label>
|
||||
<input
|
||||
v-model="form.audiencia"
|
||||
type="text"
|
||||
placeholder="ej. Mujeres de 25-40 años con poco tiempo para el gimnasio"
|
||||
class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink placeholder:text-ink-3 focus:outline-none focus:ring-2 focus:ring-accent/30 transition-all"
|
||||
:disabled="generando"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Paso 2: Parámetros -->
|
||||
<section class="space-y-6">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="w-8 h-8 rounded-lg bg-primary/10 text-primary flex items-center justify-center font-black text-sm border border-primary/20">02</span>
|
||||
<h2 class="text-xl font-headline font-extrabold text-white tracking-tight">Parámetros de Generación</h2>
|
||||
<section class="bg-surface rounded-xl border border-border shadow-sm overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-border bg-surface-muted/50 flex items-center gap-3">
|
||||
<span class="w-6 h-6 rounded-md bg-success text-white text-xs font-bold flex items-center justify-center">2</span>
|
||||
<h2 class="text-sm font-semibold text-ink">Parámetros de Generación</h2>
|
||||
</div>
|
||||
<div class="bg-surface-container p-8 rounded-3xl border border-white/5 shadow-2xl space-y-6">
|
||||
<div class="grid grid-cols-2 gap-6">
|
||||
<div class="space-y-2">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline">Estructura Narrativa</label>
|
||||
<select v-model="form.estructura" class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm text-on-surface appearance-none focus:ring-2 focus:ring-primary/40" :disabled="generando">
|
||||
<option value="AIDA">AIDA (Atención → Interés → Deseo → Acción)</option>
|
||||
<option value="PAS">PAS (Problema → Agitación → Solución)</option>
|
||||
<div class="p-6 space-y-5">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Estructura Narrativa</label>
|
||||
<select v-model="form.estructura" class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-accent/30 appearance-none transition-all" :disabled="generando">
|
||||
<option value="AIDA">AIDA — Atención · Interés · Deseo · Acción</option>
|
||||
<option value="PAS">PAS — Problema · Agitación · Solución</option>
|
||||
<option value="hero_journey">Hero's Journey</option>
|
||||
<option value="storybrand">StoryBrand</option>
|
||||
<option value="antes_despues">Antes / Después</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline">Objetivo</label>
|
||||
<select v-model="form.objetivo" class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm text-on-surface appearance-none focus:ring-2 focus:ring-primary/40" :disabled="generando">
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Objetivo</label>
|
||||
<select v-model="form.objetivo" class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-accent/30 appearance-none transition-all" :disabled="generando">
|
||||
<option value="engagement">Engagement (likes, comentarios)</option>
|
||||
<option value="awareness">Awareness (alcance)</option>
|
||||
<option value="conversion">Conversión (ventas, leads)</option>
|
||||
@ -79,10 +100,11 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-6">
|
||||
<div class="space-y-2">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline">Tono</label>
|
||||
<select v-model="form.tono" class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm text-on-surface appearance-none focus:ring-2 focus:ring-primary/40" :disabled="generando">
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Tono</label>
|
||||
<select v-model="form.tono" class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink focus:outline-none focus:ring-2 focus:ring-accent/30 appearance-none transition-all" :disabled="generando">
|
||||
<option value="educativo">Educativo</option>
|
||||
<option value="entretenimiento">Entretenimiento</option>
|
||||
<option value="inspiracional">Inspiracional</option>
|
||||
@ -91,127 +113,151 @@
|
||||
<option value="humoristico">Humorístico</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline">Duración Objetivo (seg)</label>
|
||||
<input v-model.number="form.duracion_objetivo" type="number" min="15" max="180" class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm text-on-surface font-bold text-center focus:ring-2 focus:ring-primary/40" :disabled="generando" />
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Duración objetivo (seg)</label>
|
||||
<input
|
||||
v-model.number="form.duracion_objetivo"
|
||||
type="number" min="15" max="180"
|
||||
class="w-full bg-canvas border border-border rounded-lg px-3 py-2.5 text-sm text-ink font-medium text-center focus:outline-none focus:ring-2 focus:ring-accent/30 transition-all"
|
||||
:disabled="generando"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<label class="text-[10px] font-black uppercase tracking-widest text-outline">Instrucciones adicionales (opcional)</label>
|
||||
<textarea v-model="form.instrucciones_extra" rows="3" placeholder="ej. Incluir una estadística de estudio, no mencionar competidores, usar lenguaje informal..." class="w-full bg-surface-container-low border border-white/10 rounded-2xl px-4 py-4 text-sm text-white placeholder:text-outline/40 focus:ring-2 focus:ring-primary/40 resize-none" :disabled="generando"></textarea>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<label class="text-xs font-semibold text-ink-2 uppercase tracking-wide">Instrucciones adicionales <span class="font-normal text-ink-3">(opcional)</span></label>
|
||||
<textarea
|
||||
v-model="form.instrucciones_extra"
|
||||
rows="3"
|
||||
placeholder="ej. Incluir una estadística de estudio, no mencionar competidores, usar lenguaje informal…"
|
||||
class="w-full bg-canvas border border-border rounded-lg px-3 py-3 text-sm text-ink placeholder:text-ink-3 focus:outline-none focus:ring-2 focus:ring-accent/30 resize-none leading-relaxed transition-all"
|
||||
:disabled="generando"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Botón -->
|
||||
<!-- Botón Generar -->
|
||||
<button
|
||||
@click="generar"
|
||||
:disabled="generando || !form.niche || !form.tema || !form.audiencia"
|
||||
class="w-full py-5 bg-gradient-to-br from-secondary/80 to-secondary text-on-secondary font-headline font-black rounded-2xl shadow-xl shadow-secondary/20 hover:scale-[1.02] active:scale-95 transition-all text-base uppercase tracking-widest flex items-center justify-center gap-3 disabled:opacity-40 disabled:scale-100"
|
||||
class="w-full py-3 bg-accent hover:bg-accent-hover text-white font-semibold rounded-lg text-sm flex items-center justify-center gap-2 transition-colors shadow-sm disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<span class="material-symbols-outlined text-xl" :class="generando ? 'animate-spin' : ''">{{ generando ? 'hourglass_top' : 'auto_fix_high' }}</span>
|
||||
{{ generando ? 'Generando con GPT-4o...' : 'Generar Guion' }}
|
||||
<span class="material-symbols-outlined text-[18px]" :class="generando ? 'animate-spin' : ''">
|
||||
{{ generando ? 'hourglass_top' : 'auto_fix_high' }}
|
||||
</span>
|
||||
{{ generando ? 'Generando con GPT-4o…' : 'Generar Guion' }}
|
||||
</button>
|
||||
|
||||
<p v-if="error" class="p-4 rounded-xl bg-red-500/10 border border-red-500/30 text-red-400 text-sm">{{ error }}</p>
|
||||
<div v-if="error" class="p-4 rounded-lg bg-error-subtle border border-error-border text-error text-sm">
|
||||
{{ error }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Panel derecho: Preview + Info -->
|
||||
<div class="xl:col-span-5 flex flex-col gap-6">
|
||||
<!-- Panel de resultados (derecha) -->
|
||||
<div class="xl:col-span-5 flex flex-col gap-5">
|
||||
|
||||
<!-- Resultado -->
|
||||
<div v-if="resultado" class="flex flex-col gap-4">
|
||||
<div v-if="resultado" class="flex flex-col gap-5">
|
||||
|
||||
<!-- Score -->
|
||||
<div class="bg-surface-container p-6 rounded-3xl border border-secondary/20 shadow-xl relative overflow-hidden">
|
||||
<div class="absolute -top-8 -right-8 w-32 h-32 bg-secondary/10 blur-3xl rounded-full"></div>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-sm font-headline font-black text-white uppercase tracking-widest">{{ resultado.guion.titulo_sugerido }}</h3>
|
||||
<div class="flex items-center gap-2 px-3 py-1.5 bg-secondary/10 rounded-full border border-secondary/20">
|
||||
<span class="material-symbols-outlined text-secondary text-sm" style="font-variation-settings:'FILL' 1;">bolt</span>
|
||||
<span class="text-sm font-black text-secondary">{{ resultado.guion.score_estimado }}/100</span>
|
||||
<div class="bg-surface rounded-xl border border-success-border shadow-sm p-5">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<h3 class="text-sm font-semibold text-ink leading-snug">{{ resultado.guion.titulo_sugerido }}</h3>
|
||||
<div class="flex items-center gap-1.5 px-2.5 py-1 bg-success-subtle border border-success-border rounded-full ml-3 shrink-0">
|
||||
<span class="material-symbols-outlined text-success text-[14px]" style="font-variation-settings:'FILL' 1;">bolt</span>
|
||||
<span class="text-xs font-bold text-success">{{ resultado.guion.score_estimado }}/100</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
<span v-for="t in resultado.guion.tecnicas_aplicadas" :key="t" class="text-[9px] font-black uppercase tracking-widest px-2 py-1 bg-surface-container-low border border-white/5 rounded text-outline">{{ t }}</span>
|
||||
<div class="flex flex-wrap gap-1.5 mb-3">
|
||||
<span
|
||||
v-for="t in resultado.guion.tecnicas_aplicadas"
|
||||
:key="t"
|
||||
class="text-[9px] font-semibold uppercase tracking-wider px-2 py-0.5 bg-surface-muted border border-border rounded text-ink-3"
|
||||
>{{ t }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<p class="text-[10px] text-ink-3 font-medium uppercase tracking-wide">Duración estimada</p>
|
||||
<p class="text-base font-bold text-ink">{{ resultado.guion.duracion_estimada_seg }}s</p>
|
||||
</div>
|
||||
<p class="text-[10px] text-secondary font-black uppercase tracking-widest mb-1">Duración estimada</p>
|
||||
<p class="text-2xl font-black text-white font-headline">{{ resultado.guion.duracion_estimada_seg }}s</p>
|
||||
</div>
|
||||
|
||||
<!-- Guion completo -->
|
||||
<div class="bg-surface-container rounded-3xl border border-outline-variant/10 shadow-xl overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-white/5 bg-surface-container-high/50 flex items-center justify-between">
|
||||
<h3 class="text-xs font-black text-white uppercase tracking-widest">Guion Completo</h3>
|
||||
<button @click="copiarGuion" class="text-xs font-bold text-primary hover:text-white transition-colors flex items-center gap-1">
|
||||
<span class="material-symbols-outlined text-sm">{{ copiado ? 'check' : 'content_copy' }}</span>
|
||||
<div class="bg-surface rounded-xl border border-border shadow-sm overflow-hidden">
|
||||
<div class="px-5 py-3.5 border-b border-border flex items-center justify-between">
|
||||
<h3 class="text-xs font-semibold text-ink uppercase tracking-wider">Guion Completo</h3>
|
||||
<button @click="copiarGuion" class="text-xs font-medium text-accent hover:text-accent-hover transition-colors flex items-center gap-1">
|
||||
<span class="material-symbols-outlined text-[14px]">{{ copiado ? 'check' : 'content_copy' }}</span>
|
||||
{{ copiado ? 'Copiado' : 'Copiar' }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-6 space-y-4">
|
||||
<div class="p-5 space-y-4">
|
||||
<div>
|
||||
<p class="text-[9px] text-secondary font-black uppercase tracking-widest mb-2">Gancho</p>
|
||||
<p class="text-sm text-white leading-relaxed font-medium">{{ resultado.guion.gancho }}</p>
|
||||
<p class="text-[10px] font-semibold text-success uppercase tracking-wider mb-2">Gancho</p>
|
||||
<p class="text-sm text-ink leading-relaxed">{{ resultado.guion.gancho }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-[9px] text-primary font-black uppercase tracking-widest mb-2">Desarrollo</p>
|
||||
<p class="text-sm text-on-surface-variant leading-relaxed whitespace-pre-wrap">{{ resultado.guion.desarrollo }}</p>
|
||||
<p class="text-[10px] font-semibold text-accent uppercase tracking-wider mb-2">Desarrollo</p>
|
||||
<p class="text-sm text-ink-2 leading-relaxed whitespace-pre-wrap">{{ resultado.guion.desarrollo }}</p>
|
||||
</div>
|
||||
<div v-if="resultado.guion.cta">
|
||||
<p class="text-[9px] text-tertiary font-black uppercase tracking-widest mb-2">Call to Action</p>
|
||||
<p class="text-sm text-white leading-relaxed font-medium">{{ resultado.guion.cta }}</p>
|
||||
<p class="text-[10px] font-semibold text-warn uppercase tracking-wider mb-2">Call to Action</p>
|
||||
<p class="text-sm text-ink leading-relaxed">{{ resultado.guion.cta }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Variantes del gancho -->
|
||||
<div v-if="resultado.guion.variantes_gancho?.length" class="bg-surface-container p-6 rounded-3xl border border-outline-variant/10">
|
||||
<h3 class="text-xs font-black text-white uppercase tracking-widest mb-4 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-outline text-base">shuffle</span> Variantes del Gancho
|
||||
<!-- Variantes del Gancho -->
|
||||
<div v-if="resultado.guion.variantes_gancho?.length" class="bg-surface rounded-xl border border-border shadow-sm p-5">
|
||||
<h3 class="text-xs font-semibold text-ink-2 uppercase tracking-wider mb-3 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-ink-3 text-[16px]">shuffle</span> Variantes del Gancho
|
||||
</h3>
|
||||
<div class="space-y-3">
|
||||
<div v-for="(v, i) in resultado.guion.variantes_gancho" :key="i" class="p-3 rounded-xl bg-surface-container-low border border-white/5">
|
||||
<span class="text-[9px] text-outline font-black uppercase tracking-widest mr-2">V{{ i + 1 }}</span>
|
||||
<span class="text-sm text-on-surface-variant italic">"{{ v }}"</span>
|
||||
<div class="space-y-2">
|
||||
<div v-for="(v, i) in resultado.guion.variantes_gancho" :key="i" class="flex items-start gap-2.5 p-3 rounded-lg bg-surface-muted border border-border">
|
||||
<span class="text-[9px] font-bold text-ink-3 uppercase tracking-wider pt-0.5 shrink-0">V{{ i + 1 }}</span>
|
||||
<span class="text-sm text-ink-2 italic leading-relaxed">"{{ v }}"</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notas de producción -->
|
||||
<div v-if="resultado.guion.notas_produccion" class="bg-surface-container p-6 rounded-3xl border border-yellow-500/20">
|
||||
<h3 class="text-xs font-black text-yellow-400 uppercase tracking-widest mb-3 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-base">videocam</span> Notas de Producción
|
||||
<!-- Notas de Producción -->
|
||||
<div v-if="resultado.guion.notas_produccion" class="bg-surface rounded-xl border border-warn-border p-5">
|
||||
<h3 class="text-xs font-semibold text-warn uppercase tracking-wider mb-2 flex items-center gap-1.5">
|
||||
<span class="material-symbols-outlined text-[16px]">videocam</span> Notas de Producción
|
||||
</h3>
|
||||
<p class="text-sm text-on-surface-variant leading-relaxed">{{ resultado.guion.notas_produccion }}</p>
|
||||
<p class="text-sm text-ink-2 leading-relaxed">{{ resultado.guion.notas_produccion }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Nuevo guion -->
|
||||
<button @click="resultado = null; form.tema = ''; form.instrucciones_extra = ''" class="w-full py-3 bg-surface-container border border-white/5 text-outline font-bold rounded-xl text-sm hover:text-white hover:border-white/10 transition-colors">
|
||||
<button
|
||||
@click="resultado = null; form.tema = ''; form.instrucciones_extra = ''"
|
||||
class="w-full py-2.5 bg-canvas border border-border text-ink-2 font-medium rounded-lg text-sm hover:bg-surface-muted transition-colors"
|
||||
>
|
||||
Generar otro guion
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Estado vacío / Info -->
|
||||
<div v-else class="bg-surface-container rounded-3xl border border-outline-variant/10 p-8 flex flex-col gap-6 sticky top-24">
|
||||
<div>
|
||||
<h3 class="text-sm font-headline font-black text-white uppercase tracking-widest mb-2">Cómo funciona</h3>
|
||||
<p class="text-xs text-outline/70 leading-relaxed">El generador analiza los guiones de mayor rendimiento de tu base de datos y aplica sus patrones estructurales, técnicas de retención y triggers emocionales a tu nuevo contenido.</p>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<div v-else class="bg-surface rounded-xl border border-border shadow-sm p-6 sticky top-24">
|
||||
<h3 class="text-sm font-semibold text-ink mb-3">¿Cómo funciona?</h3>
|
||||
<p class="text-xs text-ink-3 leading-relaxed mb-5">
|
||||
El generador analiza los guiones de mayor rendimiento de tu base de datos y aplica sus patrones estructurales, técnicas de retención y triggers emocionales al nuevo contenido.
|
||||
</p>
|
||||
<div class="space-y-4">
|
||||
<div v-for="paso in pasoInfo" :key="paso.label" class="flex items-start gap-3">
|
||||
<div class="w-7 h-7 rounded-full bg-surface-container-low border border-white/5 flex items-center justify-center shrink-0">
|
||||
<span class="material-symbols-outlined text-sm text-primary">{{ paso.icon }}</span>
|
||||
<div class="w-7 h-7 rounded-full bg-accent-subtle border border-accent-border flex items-center justify-center shrink-0">
|
||||
<span class="material-symbols-outlined text-accent text-[14px]">{{ paso.icon }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs font-black text-white">{{ paso.label }}</p>
|
||||
<p class="text-[10px] text-outline/60">{{ paso.desc }}</p>
|
||||
<p class="text-xs font-semibold text-ink">{{ paso.label }}</p>
|
||||
<p class="text-[11px] text-ink-3 mt-0.5 leading-snug">{{ paso.desc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pt-4 border-t border-white/5">
|
||||
<p class="text-[10px] text-outline/50 italic">Tiempo estimado: 5-10 segundos · Modelo: GPT-4o</p>
|
||||
<div class="mt-5 pt-4 border-t border-border">
|
||||
<p class="text-[11px] text-ink-3">Tiempo estimado: 5–10 segundos · Modelo: GPT-4o</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,182 +1,185 @@
|
||||
<template>
|
||||
<div class="max-w-7xl mx-auto flex flex-col gap-10">
|
||||
<div class="flex flex-col gap-8">
|
||||
|
||||
<!-- Encabezado -->
|
||||
<header class="flex flex-col md:flex-row md:items-end justify-between gap-6 border-b border-white/5 pb-8">
|
||||
<header class="flex flex-col md:flex-row md:items-end justify-between gap-4 pb-6 border-b border-border">
|
||||
<div>
|
||||
<h1 class="text-5xl font-extrabold font-headline tracking-tighter text-white mb-2 leading-tight">Biblioteca de Guiones</h1>
|
||||
<p class="text-sm font-bold flex items-center gap-2 tracking-widest uppercase" :class="filtros.tipo === 'analizados' ? 'text-primary' : 'text-secondary'">
|
||||
<span class="material-symbols-outlined text-sm">{{ filtros.tipo === 'analizados' ? 'description' : 'auto_fix_high' }}</span>
|
||||
{{ totalGuiones }} guiones {{ filtros.tipo }} en el repositorio
|
||||
<h1 class="text-3xl font-bold font-headline text-ink mb-1">Biblioteca de Guiones</h1>
|
||||
<p class="text-sm text-ink-3">
|
||||
{{ totalGuiones }} guiones
|
||||
<span class="font-medium" :class="filtros.tipo === 'analizados' ? 'text-accent' : 'text-success'">
|
||||
{{ filtros.tipo }}
|
||||
</span>
|
||||
en el repositorio
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<button class="px-6 py-3 bg-surface-container border border-white/5 text-outline font-bold rounded-xl text-sm hover:text-white transition-colors" @click="cargarDatos">Actualizar</button>
|
||||
<router-link to="/new-analysis" class="px-8 py-3 bg-gradient-to-br from-primary-container to-primary text-on-primary-container font-headline font-bold rounded-xl shadow-lg shadow-primary/20 hover:scale-105 active:scale-95 transition-all text-sm h-12 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-sm">add</span> Nuevo Análisis
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
class="px-4 py-2 bg-surface border border-border text-ink-2 font-medium rounded-lg text-sm hover:bg-surface-muted transition-colors"
|
||||
@click="cargarDatos"
|
||||
>
|
||||
Actualizar
|
||||
</button>
|
||||
<router-link
|
||||
to="/new-analysis"
|
||||
class="px-5 py-2 bg-accent hover:bg-accent-hover text-white font-semibold rounded-lg text-sm flex items-center gap-2 transition-colors shadow-sm"
|
||||
>
|
||||
<span class="material-symbols-outlined text-[18px]">add</span>
|
||||
Nuevo Análisis
|
||||
</router-link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Filtros -->
|
||||
<div class="flex flex-wrap gap-4 items-center">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
|
||||
<!-- Toggle Analizados / Generados -->
|
||||
<div class="flex items-center bg-surface-container border border-white/10 rounded-xl p-1 gap-1">
|
||||
<div class="flex items-center bg-surface border border-border rounded-lg p-1 gap-1">
|
||||
<button
|
||||
@click="cambiarTipo('analizados')"
|
||||
:class="filtros.tipo === 'analizados'
|
||||
? 'bg-primary/20 text-primary border-primary/30'
|
||||
: 'text-outline hover:text-white border-transparent'"
|
||||
class="px-4 py-2 rounded-lg text-xs font-black uppercase tracking-widest border transition-all flex items-center gap-1.5"
|
||||
:class="filtros.tipo === 'analizados' ? 'bg-accent text-white shadow-sm' : 'text-ink-2 hover:bg-surface-muted'"
|
||||
class="px-3 py-1.5 rounded-md text-xs font-semibold transition-all flex items-center gap-1.5"
|
||||
>
|
||||
<span class="material-symbols-outlined text-sm">analytics</span>
|
||||
<span class="material-symbols-outlined text-[14px]">analytics</span>
|
||||
Analizados
|
||||
</button>
|
||||
<button
|
||||
@click="cambiarTipo('generados')"
|
||||
:class="filtros.tipo === 'generados'
|
||||
? 'bg-secondary/20 text-secondary border-secondary/30'
|
||||
: 'text-outline hover:text-white border-transparent'"
|
||||
class="px-4 py-2 rounded-lg text-xs font-black uppercase tracking-widest border transition-all flex items-center gap-1.5"
|
||||
:class="filtros.tipo === 'generados' ? 'bg-success text-white shadow-sm' : 'text-ink-2 hover:bg-surface-muted'"
|
||||
class="px-3 py-1.5 rounded-md text-xs font-semibold transition-all flex items-center gap-1.5"
|
||||
>
|
||||
<span class="material-symbols-outlined text-sm">auto_fix_high</span>
|
||||
<span class="material-symbols-outlined text-[14px]">auto_fix_high</span>
|
||||
Generados
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="relative flex-1 min-w-[200px] max-w-sm">
|
||||
<span class="material-symbols-outlined absolute left-4 top-1/2 -translate-y-1/2 text-outline text-lg">search</span>
|
||||
<!-- Búsqueda -->
|
||||
<div class="relative flex-1 min-w-[200px] max-w-xs">
|
||||
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-ink-3 text-[18px]">search</span>
|
||||
<input
|
||||
v-model="filtros.busqueda"
|
||||
type="text"
|
||||
placeholder="Buscar por tema, gancho..."
|
||||
class="w-full bg-surface-container border border-white/10 rounded-xl pl-12 pr-4 py-3 text-sm text-white placeholder:text-outline/40 focus:ring-2 focus:ring-primary/40 focus:border-primary/40 transition-all"
|
||||
placeholder="Buscar por tema, gancho…"
|
||||
class="w-full bg-surface border border-border rounded-lg pl-9 pr-4 py-2 text-sm text-ink placeholder:text-ink-3 focus:outline-none focus:ring-2 focus:ring-accent/30 focus:border-accent/40 transition-all"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<select v-model="filtros.niche" class="bg-surface-container border border-white/10 rounded-xl px-4 py-3 text-sm text-on-surface-variant focus:ring-2 focus:ring-primary/40 appearance-none min-w-[150px]">
|
||||
<select v-model="filtros.niche" class="bg-surface border border-border rounded-lg px-3 py-2 text-sm text-ink-2 focus:outline-none focus:ring-2 focus:ring-accent/30 appearance-none min-w-[140px]">
|
||||
<option value="">Todos los nichos</option>
|
||||
<option v-for="n in nichos" :key="n" :value="n">{{ n }}</option>
|
||||
</select>
|
||||
|
||||
<select v-model="filtros.plataforma" class="bg-surface-container border border-white/10 rounded-xl px-4 py-3 text-sm text-on-surface-variant focus:ring-2 focus:ring-primary/40 appearance-none min-w-[150px]">
|
||||
<select v-model="filtros.plataforma" class="bg-surface border border-border rounded-lg px-3 py-2 text-sm text-ink-2 focus:outline-none focus:ring-2 focus:ring-accent/30 appearance-none min-w-[150px]">
|
||||
<option value="">Todas las plataformas</option>
|
||||
<option value="tiktok">TikTok</option>
|
||||
<option value="reels">Instagram Reels</option>
|
||||
<option value="shorts">YouTube Shorts</option>
|
||||
</select>
|
||||
|
||||
<select v-if="filtros.tipo === 'analizados'" v-model="filtros.orden" class="bg-surface-container border border-white/10 rounded-xl px-4 py-3 text-sm text-on-surface-variant focus:ring-2 focus:ring-primary/40 appearance-none min-w-[180px]">
|
||||
<select v-if="filtros.tipo === 'analizados'" v-model="filtros.orden" class="bg-surface border border-border rounded-lg px-3 py-2 text-sm text-ink-2 focus:outline-none focus:ring-2 focus:ring-accent/30 appearance-none min-w-[160px]">
|
||||
<option value="fecha_desc">Más recientes</option>
|
||||
<option value="viralidad_desc">Mayor viralidad</option>
|
||||
<option value="engagement_desc">Mayor engagement</option>
|
||||
<option value="cialdini_desc">Mayor score Cialdini</option>
|
||||
<option value="cialdini_desc">Mayor Cialdini</option>
|
||||
</select>
|
||||
<select v-else v-model="filtros.orden" class="bg-surface-container border border-white/10 rounded-xl px-4 py-3 text-sm text-on-surface-variant focus:ring-2 focus:ring-primary/40 appearance-none min-w-[180px]">
|
||||
<select v-else v-model="filtros.orden" class="bg-surface border border-border rounded-lg px-3 py-2 text-sm text-ink-2 focus:outline-none focus:ring-2 focus:ring-accent/30 appearance-none min-w-[160px]">
|
||||
<option value="fecha_desc">Más recientes</option>
|
||||
<option value="score_desc">Mayor score</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Skeleton loading -->
|
||||
<div v-if="cargando" class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-5">
|
||||
<div v-for="i in 6" :key="i" class="bg-surface rounded-xl border border-border h-56 animate-pulse"></div>
|
||||
</div>
|
||||
|
||||
<!-- Vacío -->
|
||||
<div v-else-if="guionesFiltrados.length === 0" class="flex flex-col items-center justify-center py-24 gap-3 text-ink-3">
|
||||
<span class="material-symbols-outlined text-4xl">inventory_2</span>
|
||||
<p class="text-sm font-medium">No hay guiones que coincidan con los filtros</p>
|
||||
</div>
|
||||
|
||||
<!-- Grid de Guiones -->
|
||||
<div v-if="cargando" class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||
<div v-for="i in 6" :key="i" class="bg-surface-container rounded-3xl border border-white/5 p-6 animate-pulse h-64"></div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="guionesFiltrados.length === 0" class="flex flex-col items-center justify-center py-24 gap-4 opacity-40">
|
||||
<span class="material-symbols-outlined text-6xl">inventory_2</span>
|
||||
<p class="text-sm font-bold uppercase tracking-widest text-outline">No hay guiones que coincidan con los filtros</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||
<div v-else class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-5">
|
||||
<div
|
||||
v-for="g in guionesFiltrados"
|
||||
:key="g.id"
|
||||
@click="filtros.tipo === 'analizados' ? verDetalle(g.id) : verGenerado(g.id)"
|
||||
class="bg-surface-container rounded-3xl border border-outline-variant/10 shadow-xl p-6 flex flex-col gap-4 cursor-pointer transition-all group relative overflow-hidden"
|
||||
:class="filtros.tipo === 'analizados'
|
||||
? 'hover:border-primary/20 hover:shadow-primary/10'
|
||||
: 'hover:border-secondary/20 hover:shadow-secondary/10'"
|
||||
class="bg-surface rounded-xl border border-border shadow-sm p-5 flex flex-col gap-4 cursor-pointer transition-all group hover:shadow-md"
|
||||
:class="filtros.tipo === 'analizados' ? 'hover:border-accent/30' : 'hover:border-success/30'"
|
||||
>
|
||||
<div
|
||||
class="absolute -bottom-8 -right-8 w-28 h-28 blur-2xl rounded-full transition-colors"
|
||||
:class="filtros.tipo === 'analizados' ? 'bg-primary/5 group-hover:bg-primary/10' : 'bg-secondary/5 group-hover:bg-secondary/10'"
|
||||
></div>
|
||||
|
||||
<!-- Badges -->
|
||||
<!-- Badges + Score -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<span :class="plataformaBadge(g.plataforma)" class="text-[8px] font-black px-2 py-1 rounded-md uppercase tracking-widest">{{ g.plataforma }}</span>
|
||||
<span class="text-[10px] font-black text-outline uppercase tracking-wider">{{ g.niche }}</span>
|
||||
<span :class="plataformaBadge(g.plataforma)" class="platform-badge">{{ g.plataforma }}</span>
|
||||
<span class="text-[10px] font-semibold text-ink-3 uppercase tracking-wide">{{ g.niche }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 px-2.5 py-1 bg-surface-container-low rounded-full border border-white/5">
|
||||
<span
|
||||
class="material-symbols-outlined text-sm"
|
||||
:class="filtros.tipo === 'analizados' ? 'text-primary' : 'text-secondary'"
|
||||
style="font-variation-settings: 'FILL' 1;"
|
||||
>{{ filtros.tipo === 'analizados' ? 'bolt' : 'auto_fix_high' }}</span>
|
||||
<span class="text-[11px] font-black text-white">
|
||||
{{ filtros.tipo === 'analizados' ? (g.score_virabilidad || 0) : (g.score_estimado || 0) }}
|
||||
</span>
|
||||
<div
|
||||
class="flex items-center gap-1 px-2 py-1 rounded-full text-[11px] font-bold border"
|
||||
:class="filtros.tipo === 'analizados'
|
||||
? 'bg-accent-subtle border-accent-border text-accent'
|
||||
: 'bg-success-subtle border-success-border text-success'"
|
||||
>
|
||||
<span class="material-symbols-outlined text-[12px]" style="font-variation-settings:'FILL' 1;">bolt</span>
|
||||
{{ filtros.tipo === 'analizados' ? (g.score_virabilidad || 0) : (g.score_estimado || 0) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tema -->
|
||||
<div>
|
||||
<p
|
||||
class="text-sm font-bold text-white leading-snug line-clamp-2 mb-1 transition-colors"
|
||||
:class="filtros.tipo === 'analizados' ? 'group-hover:text-primary' : 'group-hover:text-secondary'"
|
||||
class="text-sm font-semibold text-ink leading-snug line-clamp-2 mb-1 transition-colors"
|
||||
:class="filtros.tipo === 'analizados' ? 'group-hover:text-accent' : 'group-hover:text-success'"
|
||||
>
|
||||
{{ filtros.tipo === 'analizados' ? (g.tema_principal || 'Sin título detectado') : (g.titulo_sugerido || g.tema || 'Sin título') }}
|
||||
</p>
|
||||
<p v-if="filtros.tipo === 'analizados'" class="text-[10px] text-outline/60 italic truncate">{{ g.url_origen }}</p>
|
||||
<p v-else class="text-[10px] text-outline/60 italic truncate">{{ g.tono }} · {{ g.objetivo }}</p>
|
||||
<p v-if="filtros.tipo === 'analizados'" class="text-[10px] text-ink-3 truncate">{{ g.url_origen }}</p>
|
||||
<p v-else class="text-[10px] text-ink-3 truncate">{{ g.tono }} · {{ g.objetivo }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Gancho -->
|
||||
<div class="flex-1">
|
||||
<p class="text-[10px] text-outline font-black uppercase tracking-widest mb-1">Gancho</p>
|
||||
<p class="text-xs text-on-surface-variant leading-relaxed italic line-clamp-3">
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-1">Gancho</p>
|
||||
<p class="text-xs text-ink-2 leading-relaxed italic line-clamp-3">
|
||||
"{{ (filtros.tipo === 'analizados' ? g.gancho_texto : g.gancho) || '—' }}"
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Métricas analizados -->
|
||||
<div v-if="filtros.tipo === 'analizados'" class="flex items-center justify-between pt-4 border-t border-white/5">
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-outline font-black uppercase tracking-widest">Engagement</p>
|
||||
<p class="text-sm font-black text-secondary">{{ (g.score_engagement || 0).toFixed(1) }}%</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-outline font-black uppercase tracking-widest">Cialdini</p>
|
||||
<p class="text-sm font-black text-white">{{ g.score_cialdini || 0 }}/7</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-outline font-black uppercase tracking-widest">Viralidad</p>
|
||||
<div class="w-16 bg-surface-container-highest h-1.5 rounded-full overflow-hidden mt-1">
|
||||
<div class="bg-primary h-full transition-all duration-1000" :style="{ width: (g.score_virabilidad || 0) + '%' }"></div>
|
||||
<!-- Footer con métricas -->
|
||||
<div class="flex items-center justify-between pt-3 border-t border-border">
|
||||
<template v-if="filtros.tipo === 'analizados'">
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-ink-3 font-medium mb-0.5">Engagement</p>
|
||||
<p class="text-xs font-bold text-success">{{ (g.score_engagement || 0).toFixed(1) }}%</p>
|
||||
</div>
|
||||
</div>
|
||||
<button class="p-2 rounded-xl bg-surface-container-low border border-white/5 text-outline hover:text-white hover:border-primary/20 transition-all opacity-0 group-hover:opacity-100 scale-90 group-hover:scale-100">
|
||||
<span class="material-symbols-outlined text-base">open_in_new</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-ink-3 font-medium mb-0.5">Cialdini</p>
|
||||
<p class="text-xs font-bold text-ink">{{ g.score_cialdini || 0 }}/7</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-ink-3 font-medium mb-1">Viralidad</p>
|
||||
<div class="w-16 bg-surface-subtle h-1 rounded-full overflow-hidden">
|
||||
<div class="bg-accent h-full rounded-full transition-all" :style="{ width: (g.score_virabilidad || 0) + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-ink-3 font-medium mb-0.5">Score</p>
|
||||
<p class="text-xs font-bold text-success">{{ g.score_estimado || 0 }}/100</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-ink-3 font-medium mb-0.5">Duración</p>
|
||||
<p class="text-xs font-bold text-ink">{{ g.duracion_estimada_seg || '—' }}s</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-ink-3 font-medium mb-0.5">Estructura</p>
|
||||
<p class="text-[10px] font-semibold text-ink-2">{{ g.estructura_usada || '—' }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Métricas generados -->
|
||||
<div v-else class="flex items-center justify-between pt-4 border-t border-white/5">
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-outline font-black uppercase tracking-widest">Score</p>
|
||||
<p class="text-sm font-black text-secondary">{{ g.score_estimado || 0 }}/100</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-outline font-black uppercase tracking-widest">Duración</p>
|
||||
<p class="text-sm font-black text-white">{{ g.duracion_estimada_seg || '—' }}s</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p class="text-[9px] text-outline font-black uppercase tracking-widest">Estructura</p>
|
||||
<p class="text-xs font-black text-outline">{{ g.estructura_usada || '—' }}</p>
|
||||
</div>
|
||||
<button class="p-2 rounded-xl bg-surface-container-low border border-white/5 text-outline hover:text-white hover:border-secondary/20 transition-all opacity-0 group-hover:opacity-100 scale-90 group-hover:scale-100">
|
||||
<span class="material-symbols-outlined text-base">open_in_new</span>
|
||||
<button class="p-1.5 rounded-lg text-ink-3 hover:text-accent hover:bg-accent-subtle transition-all opacity-0 group-hover:opacity-100">
|
||||
<span class="material-symbols-outlined text-[16px]">open_in_new</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -189,101 +192,99 @@
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4"
|
||||
@click.self="modalGenerado = null"
|
||||
>
|
||||
<div class="absolute inset-0 bg-black/70 backdrop-blur-sm"></div>
|
||||
<div class="relative z-10 w-full max-w-2xl max-h-[90vh] overflow-y-auto bg-surface rounded-3xl border border-secondary/20 shadow-2xl shadow-secondary/10 flex flex-col">
|
||||
<!-- Header -->
|
||||
<div class="sticky top-0 bg-surface px-8 py-6 border-b border-white/5 flex items-start justify-between gap-4 z-10">
|
||||
<div class="absolute inset-0 bg-black/30 backdrop-blur-sm"></div>
|
||||
<div class="relative z-10 w-full max-w-2xl max-h-[90vh] overflow-y-auto bg-surface rounded-xl border border-border shadow-xl flex flex-col">
|
||||
|
||||
<!-- Header del modal -->
|
||||
<div class="sticky top-0 bg-surface px-6 py-5 border-b border-border flex items-start justify-between gap-4 z-10">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span :class="plataformaBadge(modalGenerado.plataforma)" class="text-[8px] font-black px-2 py-1 rounded-md uppercase tracking-widest">{{ modalGenerado.plataforma }}</span>
|
||||
<span class="text-[10px] font-black text-outline uppercase tracking-wider">{{ modalGenerado.niche }}</span>
|
||||
<div class="flex items-center gap-1 px-2 py-0.5 bg-secondary/10 rounded-full border border-secondary/20">
|
||||
<span class="material-symbols-outlined text-secondary text-xs" style="font-variation-settings:'FILL' 1;">auto_fix_high</span>
|
||||
<span class="text-[10px] font-black text-secondary">{{ modalGenerado.score_estimado }}/100</span>
|
||||
<div class="flex items-center gap-2 mb-2 flex-wrap">
|
||||
<span :class="plataformaBadge(modalGenerado.plataforma)" class="platform-badge">{{ modalGenerado.plataforma }}</span>
|
||||
<span class="text-[10px] font-semibold text-ink-3 uppercase tracking-wide">{{ modalGenerado.niche }}</span>
|
||||
<div class="flex items-center gap-1 px-2 py-0.5 bg-success-subtle border border-success-border rounded-full">
|
||||
<span class="material-symbols-outlined text-success text-[12px]" style="font-variation-settings:'FILL' 1;">auto_fix_high</span>
|
||||
<span class="text-[10px] font-bold text-success">{{ modalGenerado.score_estimado }}/100</span>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="text-lg font-headline font-black text-white leading-tight">{{ modalGenerado.titulo_sugerido }}</h2>
|
||||
<p class="text-[10px] text-outline/60 mt-1">{{ modalGenerado.tono }} · {{ modalGenerado.objetivo }} · {{ modalGenerado.duracion_estimada_seg }}s estimados</p>
|
||||
<h2 class="text-base font-bold font-headline text-ink leading-tight">{{ modalGenerado.titulo_sugerido }}</h2>
|
||||
<p class="text-[11px] text-ink-3 mt-0.5">{{ modalGenerado.tono }} · {{ modalGenerado.objetivo }} · {{ modalGenerado.duracion_estimada_seg }}s estimados</p>
|
||||
</div>
|
||||
<button @click="modalGenerado = null" class="p-2 rounded-xl bg-surface-container border border-white/5 text-outline hover:text-white transition-colors shrink-0">
|
||||
<span class="material-symbols-outlined text-base">close</span>
|
||||
<button @click="modalGenerado = null" class="p-1.5 rounded-lg text-ink-3 hover:text-ink hover:bg-surface-muted transition-colors shrink-0">
|
||||
<span class="material-symbols-outlined text-[20px]">close</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Contenido -->
|
||||
<div class="p-8 space-y-6" v-if="!cargandoModal">
|
||||
<!-- Técnicas -->
|
||||
<div v-if="modalGenerado.tecnicas_aplicadas?.length" class="flex flex-wrap gap-2">
|
||||
<span v-for="t in modalGenerado.tecnicas_aplicadas" :key="t" class="text-[9px] font-black uppercase tracking-widest px-2 py-1 bg-secondary/10 border border-secondary/20 rounded text-secondary">{{ t }}</span>
|
||||
<!-- Contenido del modal -->
|
||||
<div class="p-6 space-y-5" v-if="!cargandoModal">
|
||||
<div v-if="modalGenerado.tecnicas_aplicadas?.length" class="flex flex-wrap gap-1.5">
|
||||
<span v-for="t in modalGenerado.tecnicas_aplicadas" :key="t" class="text-[9px] font-semibold uppercase tracking-wider px-2 py-0.5 bg-success-subtle border border-success-border rounded text-success">{{ t }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Gancho -->
|
||||
<div>
|
||||
<p class="text-[10px] text-secondary font-black uppercase tracking-widest mb-2">Gancho</p>
|
||||
<p class="text-sm text-white leading-relaxed font-medium">{{ modalGenerado.gancho }}</p>
|
||||
<p class="text-[10px] font-semibold text-success uppercase tracking-wider mb-2">Gancho</p>
|
||||
<p class="text-sm text-ink leading-relaxed">{{ modalGenerado.gancho }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Desarrollo -->
|
||||
<div>
|
||||
<p class="text-[10px] text-primary font-black uppercase tracking-widest mb-2">Desarrollo</p>
|
||||
<p class="text-sm text-on-surface-variant leading-relaxed whitespace-pre-wrap">{{ modalGenerado.desarrollo }}</p>
|
||||
<p class="text-[10px] font-semibold text-accent uppercase tracking-wider mb-2">Desarrollo</p>
|
||||
<p class="text-sm text-ink-2 leading-relaxed whitespace-pre-wrap">{{ modalGenerado.desarrollo }}</p>
|
||||
</div>
|
||||
|
||||
<!-- CTA -->
|
||||
<div v-if="modalGenerado.cta">
|
||||
<p class="text-[10px] text-outline font-black uppercase tracking-widest mb-2">Call to Action</p>
|
||||
<p class="text-sm text-white leading-relaxed font-medium">{{ modalGenerado.cta }}</p>
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-2">Call to Action</p>
|
||||
<p class="text-sm text-ink leading-relaxed">{{ modalGenerado.cta }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Variantes del gancho -->
|
||||
<div v-if="modalGenerado.variantes_gancho?.length">
|
||||
<p class="text-[10px] text-outline font-black uppercase tracking-widest mb-3 flex items-center gap-1">
|
||||
<span class="material-symbols-outlined text-sm">shuffle</span> Variantes del Gancho
|
||||
<p class="text-[10px] font-semibold text-ink-3 uppercase tracking-wider mb-2 flex items-center gap-1">
|
||||
<span class="material-symbols-outlined text-[14px]">shuffle</span> Variantes del Gancho
|
||||
</p>
|
||||
<div class="space-y-2">
|
||||
<div v-for="(v, i) in modalGenerado.variantes_gancho" :key="i" class="p-3 rounded-xl bg-surface-container border border-white/5">
|
||||
<span class="text-[9px] text-outline font-black uppercase tracking-widest mr-2">V{{ i + 1 }}</span>
|
||||
<span class="text-xs text-on-surface-variant italic">"{{ v }}"</span>
|
||||
<div v-for="(v, i) in modalGenerado.variantes_gancho" :key="i" class="flex items-start gap-2 p-3 rounded-lg bg-surface-muted border border-border">
|
||||
<span class="text-[9px] font-bold text-ink-3 uppercase tracking-wider pt-0.5 shrink-0">V{{ i + 1 }}</span>
|
||||
<span class="text-xs text-ink-2 italic leading-relaxed">"{{ v }}"</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notas de producción -->
|
||||
<div v-if="modalGenerado.notas_produccion" class="p-4 rounded-xl bg-yellow-500/5 border border-yellow-500/20">
|
||||
<p class="text-[10px] text-yellow-400 font-black uppercase tracking-widest mb-2 flex items-center gap-1">
|
||||
<span class="material-symbols-outlined text-sm">videocam</span> Notas de Producción
|
||||
<div v-if="modalGenerado.notas_produccion" class="p-4 rounded-lg bg-warn-subtle border border-warn-border">
|
||||
<p class="text-[10px] font-semibold text-warn uppercase tracking-wider mb-2 flex items-center gap-1">
|
||||
<span class="material-symbols-outlined text-[14px]">videocam</span> Notas de Producción
|
||||
</p>
|
||||
<p class="text-xs text-on-surface-variant leading-relaxed">{{ modalGenerado.notas_produccion }}</p>
|
||||
<p class="text-sm text-ink-2 leading-relaxed">{{ modalGenerado.notas_produccion }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Botón copiar -->
|
||||
<button @click="copiarGuionModal" class="w-full py-3 bg-secondary/10 border border-secondary/20 text-secondary font-bold rounded-xl text-sm hover:bg-secondary/20 transition-colors flex items-center justify-center gap-2">
|
||||
<span class="material-symbols-outlined text-sm">{{ copiadoModal ? 'check' : 'content_copy' }}</span>
|
||||
<button
|
||||
@click="copiarGuionModal"
|
||||
class="w-full py-2.5 bg-success-subtle border border-success-border text-success font-semibold rounded-lg text-sm hover:bg-success hover:text-white transition-colors flex items-center justify-center gap-2"
|
||||
>
|
||||
<span class="material-symbols-outlined text-[16px]">{{ copiadoModal ? 'check' : 'content_copy' }}</span>
|
||||
{{ copiadoModal ? 'Copiado' : 'Copiar guion completo' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-else class="p-8 flex items-center justify-center">
|
||||
<span class="material-symbols-outlined animate-spin text-secondary text-2xl">hourglass_top</span>
|
||||
<div class="w-6 h-6 border-2 border-success border-t-transparent rounded-full animate-spin"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
|
||||
<!-- Paginación -->
|
||||
<div v-if="!cargando && guionesFiltrados.length > 0" class="flex items-center justify-center gap-4 pt-4">
|
||||
<div v-if="!cargando && guionesFiltrados.length > 0" class="flex items-center justify-center gap-3 pt-2">
|
||||
<button
|
||||
class="px-6 py-2.5 bg-surface-container border border-white/5 text-outline font-bold rounded-xl text-sm hover:text-white hover:border-primary/20 transition-colors disabled:opacity-30"
|
||||
class="px-5 py-2 bg-surface border border-border text-ink-2 font-medium rounded-lg text-sm hover:bg-surface-muted transition-colors disabled:opacity-40"
|
||||
:disabled="filtros.page <= 1"
|
||||
@click="cambiarPagina(filtros.page - 1)"
|
||||
>Anterior</button>
|
||||
<span class="text-[11px] font-black text-outline uppercase tracking-widest">Página {{ filtros.page }}</span>
|
||||
<span class="text-sm font-medium text-ink-3">Página {{ filtros.page }}</span>
|
||||
<button
|
||||
class="px-6 py-2.5 bg-surface-container border border-white/5 text-outline font-bold rounded-xl text-sm hover:text-white hover:border-primary/20 transition-colors disabled:opacity-30"
|
||||
class="px-5 py-2 bg-surface border border-border text-ink-2 font-medium rounded-lg text-sm hover:bg-surface-muted transition-colors disabled:opacity-40"
|
||||
:disabled="guiones.length < filtros.limit"
|
||||
@click="cambiarPagina(filtros.page + 1)"
|
||||
>Siguiente</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -349,18 +350,12 @@ async function cargarDatos() {
|
||||
if (filtros.value.plataforma) params.plataforma = filtros.value.plataforma
|
||||
|
||||
if (filtros.value.tipo === 'analizados') {
|
||||
const [dg, dn] = await Promise.all([
|
||||
api.guiones.listar(params),
|
||||
api.nichos(),
|
||||
])
|
||||
const [dg, dn] = await Promise.all([api.guiones.listar(params), api.nichos()])
|
||||
guiones.value = dg.guiones
|
||||
totalGuiones.value = dg.total || dg.guiones.length
|
||||
nichos.value = dn
|
||||
} else {
|
||||
const [dg, dn] = await Promise.all([
|
||||
api.generados.listar(params),
|
||||
api.nichos(),
|
||||
])
|
||||
const [dg, dn] = await Promise.all([api.generados.listar(params), api.nichos()])
|
||||
guiones.value = dg.generados
|
||||
totalGuiones.value = dg.total || dg.generados.length
|
||||
nichos.value = dn
|
||||
@ -417,11 +412,11 @@ async function copiarGuionModal() {
|
||||
|
||||
function plataformaBadge(p) {
|
||||
const map = {
|
||||
tiktok: 'bg-red-500/10 text-red-500 border border-red-500/20',
|
||||
reels: 'bg-fuchsia-500/10 text-fuchsia-500 border border-fuchsia-500/20',
|
||||
shorts: 'bg-red-600/10 text-red-600 border border-red-600/20',
|
||||
tiktok: 'bg-red-950 text-red-400 border border-red-800',
|
||||
reels: 'bg-fuchsia-950 text-fuchsia-400 border border-fuchsia-800',
|
||||
shorts: 'bg-orange-950 text-orange-400 border border-orange-800',
|
||||
}
|
||||
return map[p] || 'bg-white/5 text-outline'
|
||||
return map[p] || 'bg-surface-muted text-ink-3 border border-border'
|
||||
}
|
||||
|
||||
onMounted(cargarDatos)
|
||||
|
||||
@ -1,62 +1,97 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
darkMode: "class",
|
||||
content: ['./index.html', './src/**/*.{vue,js}'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
"on-tertiary-fixed": "#301400",
|
||||
"tertiary-fixed": "#ffdcc5",
|
||||
"primary-container": "#8083ff",
|
||||
"on-tertiary-fixed-variant": "#703700",
|
||||
"surface-container-low": "#1b1b22",
|
||||
"on-primary-container": "#0d0096",
|
||||
"on-error-container": "#ffdad6",
|
||||
"secondary": "#4edea3",
|
||||
"surface-container-high": "#2a2931",
|
||||
"surface": "#13131a",
|
||||
"error-container": "#93000a",
|
||||
"on-secondary-fixed-variant": "#005236",
|
||||
"surface-container-lowest": "#0e0e15",
|
||||
"surface-container": "#1f1f26",
|
||||
"primary": "#c0c1ff",
|
||||
"on-secondary-container": "#00311f",
|
||||
"tertiary-fixed-dim": "#ffb783",
|
||||
"on-surface": "#e4e1ec",
|
||||
"surface-dim": "#13131a",
|
||||
"outline": "#908fa0",
|
||||
"on-error": "#690005",
|
||||
"on-primary-fixed-variant": "#2f2ebe",
|
||||
"inverse-on-surface": "#303038",
|
||||
"surface-container-highest": "#34343c",
|
||||
"surface-bright": "#393840",
|
||||
"tertiary-container": "#d97721",
|
||||
"background": "#13131a",
|
||||
"secondary-container": "#00a572",
|
||||
"secondary-fixed-dim": "#4edea3",
|
||||
"on-tertiary": "#4f2500",
|
||||
"primary-fixed-dim": "#c0c1ff",
|
||||
"on-primary-fixed": "#07006c",
|
||||
"on-primary": "#1000a9",
|
||||
"on-surface-variant": "#c7c4d7",
|
||||
"surface-variant": "#34343c",
|
||||
"secondary-fixed": "#6ffbbe",
|
||||
"outline-variant": "#464554",
|
||||
"error": "#ffb4ab",
|
||||
"inverse-surface": "#e4e1ec",
|
||||
"on-tertiary-container": "#452000",
|
||||
"inverse-primary": "#494bd6",
|
||||
"on-background": "#e4e1ec",
|
||||
"on-secondary": "#003824",
|
||||
"surface-tint": "#c0c1ff",
|
||||
"tertiary": "#ffb783",
|
||||
"on-secondary-fixed": "#002113",
|
||||
"primary-fixed": "#e1e0ff"
|
||||
// ── Fondos y superficies ──────────────────────────
|
||||
"canvas": "#0c0c0f", // página principal
|
||||
"surface": "#141416", // tarjetas / paneles
|
||||
"surface-muted": "#1c1c1f", // áreas sutiles
|
||||
"surface-subtle": "#242428", // más profundo
|
||||
|
||||
// ── Bordes ───────────────────────────────────────
|
||||
"border": "#2e2e33",
|
||||
"border-strong": "#3f3f46",
|
||||
|
||||
// ── Texto ────────────────────────────────────────
|
||||
"ink": "#f4f4f5", // texto primario
|
||||
"ink-2": "#a1a1aa", // texto secundario
|
||||
"ink-3": "#71717a", // texto atenuado / placeholder
|
||||
|
||||
// ── Acento principal (azul) ───────────────────────
|
||||
"accent": "#3b82f6",
|
||||
"accent-hover": "#60a5fa",
|
||||
"accent-subtle": "#1e2d4e",
|
||||
"accent-border": "#1e3a8a",
|
||||
|
||||
// ── Éxito / secundario (esmeralda) ────────────────
|
||||
"success": "#10b981",
|
||||
"success-hover": "#34d399",
|
||||
"success-subtle": "#0d2e22",
|
||||
"success-border": "#065f46",
|
||||
|
||||
// ── Advertencia (ámbar) ───────────────────────────
|
||||
"warn": "#f59e0b",
|
||||
"warn-subtle": "#2d1f00",
|
||||
"warn-border": "#92400e",
|
||||
|
||||
// ── Error ─────────────────────────────────────────
|
||||
"error": "#f87171",
|
||||
"error-subtle": "#2d0b0b",
|
||||
"error-border": "#991b1b",
|
||||
|
||||
// ── Alias para compatibilidad con plantillas ──────
|
||||
"background": "#0c0c0f",
|
||||
"surface-container": "#141416",
|
||||
"surface-container-low": "#1c1c1f",
|
||||
"surface-container-lowest": "#0c0c0f",
|
||||
"surface-container-high": "#242428",
|
||||
"surface-container-highest":"#2e2e33",
|
||||
"surface-dim": "#0c0c0f",
|
||||
"surface-bright": "#2e2e33",
|
||||
"surface-variant": "#1c1c1f",
|
||||
"on-surface": "#f4f4f5",
|
||||
"on-surface-variant": "#a1a1aa",
|
||||
"outline": "#71717a",
|
||||
"outline-variant": "#2e2e33",
|
||||
"primary": "#3b82f6",
|
||||
"primary-container": "#1e3a8a",
|
||||
"primary-fixed": "#1e2d4e",
|
||||
"primary-fixed-dim": "#1e3a8a",
|
||||
"on-primary": "#ffffff",
|
||||
"on-primary-container": "#bfdbfe",
|
||||
"on-primary-fixed": "#93c5fd",
|
||||
"on-primary-fixed-variant": "#60a5fa",
|
||||
"secondary": "#10b981",
|
||||
"secondary-container": "#065f46",
|
||||
"secondary-fixed": "#0d2e22",
|
||||
"secondary-fixed-dim": "#065f46",
|
||||
"on-secondary": "#ffffff",
|
||||
"on-secondary-container": "#6ee7b7",
|
||||
"on-secondary-fixed": "#a7f3d0",
|
||||
"on-secondary-fixed-variant":"#34d399",
|
||||
"tertiary": "#f59e0b",
|
||||
"tertiary-container": "#92400e",
|
||||
"tertiary-fixed": "#2d1f00",
|
||||
"tertiary-fixed-dim": "#92400e",
|
||||
"on-tertiary": "#ffffff",
|
||||
"on-tertiary-container": "#fcd34d",
|
||||
"on-tertiary-fixed": "#fde68a",
|
||||
"on-tertiary-fixed-variant":"#fbbf24",
|
||||
"inverse-surface": "#f4f4f5",
|
||||
"inverse-on-surface": "#141416",
|
||||
"inverse-primary": "#1d4ed8",
|
||||
"surface-tint": "#3b82f6",
|
||||
"on-background": "#f4f4f5",
|
||||
"on-error": "#ffffff",
|
||||
"on-error-container": "#fca5a5",
|
||||
"error-container": "#2d0b0b",
|
||||
},
|
||||
fontFamily: {
|
||||
"headline": ["Manrope", "sans-serif"],
|
||||
"body": ["Inter", "sans-serif"],
|
||||
"label": ["Inter", "sans-serif"]
|
||||
"headline": ["Bricolage Grotesque", "sans-serif"],
|
||||
"body": ["Outfit", "sans-serif"],
|
||||
"label": ["Outfit", "sans-serif"],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user