Seo
|
After Width: | Height: | Size: 6.9 MiB |
|
After Width: | Height: | Size: 5.6 MiB |
|
After Width: | Height: | Size: 9.4 MiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 589 KiB |
|
After Width: | Height: | Size: 531 KiB |
@@ -0,0 +1,235 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 1080">
|
||||
<defs>
|
||||
<!-- Background gradient -->
|
||||
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#7C3AED"/>
|
||||
<stop offset="100%" style="stop-color:#5B21B6"/>
|
||||
</linearGradient>
|
||||
|
||||
<!-- Document gradient -->
|
||||
<linearGradient id="docGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#FFFFFF"/>
|
||||
<stop offset="100%" style="stop-color:#F3F4F6"/>
|
||||
</linearGradient>
|
||||
|
||||
<!-- Glow effect for AI -->
|
||||
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="8" result="coloredBlur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<!-- Soft shadow -->
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="8" stdDeviation="12" flood-color="#000000" flood-opacity="0.25"/>
|
||||
</filter>
|
||||
|
||||
<!-- Arrow gradient -->
|
||||
<linearGradient id="arrowGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#FBBF24"/>
|
||||
<stop offset="100%" style="stop-color:#F59E0B"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="1920" height="1080" fill="url(#bgGradient)"/>
|
||||
|
||||
<!-- Subtle pattern overlay -->
|
||||
<g opacity="0.05">
|
||||
<circle cx="200" cy="200" r="300" fill="#FFFFFF"/>
|
||||
<circle cx="1700" cy="900" r="400" fill="#FFFFFF"/>
|
||||
<circle cx="1000" cy="100" r="200" fill="#FFFFFF"/>
|
||||
</g>
|
||||
|
||||
<!-- ==================== LEFT SIDE: INPUT ==================== -->
|
||||
|
||||
<!-- Input document/card -->
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="120" y="280" width="380" height="480" rx="20" fill="url(#docGradient)"/>
|
||||
</g>
|
||||
|
||||
<!-- Input header -->
|
||||
<rect x="120" y="280" width="380" height="70" rx="20" fill="#7C3AED"/>
|
||||
<rect x="120" y="320" width="380" height="30" fill="#7C3AED"/>
|
||||
<text x="310" y="325" text-anchor="middle" fill="#FFFFFF" font-family="system-ui, -apple-system, sans-serif" font-size="24" font-weight="600">INPUT</text>
|
||||
|
||||
<!-- Keywords label -->
|
||||
<text x="160" y="400" fill="#6B7280" font-family="system-ui, sans-serif" font-size="18" font-weight="500">Keywords</text>
|
||||
|
||||
<!-- Keyword tags -->
|
||||
<rect x="160" y="415" width="100" height="32" rx="16" fill="#EDE9FE"/>
|
||||
<text x="210" y="437" text-anchor="middle" fill="#7C3AED" font-family="system-ui, sans-serif" font-size="14" font-weight="500">SEO</text>
|
||||
|
||||
<rect x="270" y="415" width="120" height="32" rx="16" fill="#EDE9FE"/>
|
||||
<text x="330" y="437" text-anchor="middle" fill="#7C3AED" font-family="system-ui, sans-serif" font-size="14" font-weight="500">Content</text>
|
||||
|
||||
<rect x="160" y="455" width="140" height="32" rx="16" fill="#EDE9FE"/>
|
||||
<text x="230" y="477" text-anchor="middle" fill="#7C3AED" font-family="system-ui, sans-serif" font-size="14" font-weight="500">Marketing</text>
|
||||
|
||||
<!-- Topic label -->
|
||||
<text x="160" y="530" fill="#6B7280" font-family="system-ui, sans-serif" font-size="18" font-weight="500">Topic</text>
|
||||
|
||||
<!-- Topic input box -->
|
||||
<rect x="160" y="545" width="300" height="45" rx="8" fill="#F3F4F6" stroke="#E5E7EB" stroke-width="2"/>
|
||||
<text x="180" y="575" fill="#374151" font-family="system-ui, sans-serif" font-size="16">"Product Launch Blog"</text>
|
||||
|
||||
<!-- Tone label -->
|
||||
<text x="160" y="630" fill="#6B7280" font-family="system-ui, sans-serif" font-size="18" font-weight="500">Tone</text>
|
||||
|
||||
<!-- Tone selector -->
|
||||
<rect x="160" y="645" width="140" height="40" rx="8" fill="#7C3AED"/>
|
||||
<text x="230" y="672" text-anchor="middle" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="15" font-weight="500">Professional</text>
|
||||
|
||||
<rect x="310" y="645" width="140" height="40" rx="8" fill="#F3F4F6" stroke="#E5E7EB" stroke-width="2"/>
|
||||
<text x="380" y="672" text-anchor="middle" fill="#6B7280" font-family="system-ui, sans-serif" font-size="15">Casual</text>
|
||||
|
||||
<!-- ==================== CENTER: AI TRANSFORMATION ==================== -->
|
||||
|
||||
<!-- Flow arrow left -->
|
||||
<path d="M540 520 L620 520" stroke="url(#arrowGradient)" stroke-width="6" stroke-linecap="round" fill="none"/>
|
||||
<polygon points="630,520 610,508 610,532" fill="#FBBF24"/>
|
||||
|
||||
<!-- AI Brain/Magic circle -->
|
||||
<g filter="url(#glow)">
|
||||
<circle cx="810" cy="520" r="140" fill="#FFFFFF" opacity="0.15"/>
|
||||
<circle cx="810" cy="520" r="110" fill="#FFFFFF" opacity="0.25"/>
|
||||
<circle cx="810" cy="520" r="80" fill="#FFFFFF"/>
|
||||
</g>
|
||||
|
||||
<!-- AI Icon - Brain with circuit pattern -->
|
||||
<g transform="translate(810, 520)">
|
||||
<!-- Brain outline -->
|
||||
<path d="M-35 -10 C-35 -35 -15 -50 5 -50 C25 -50 40 -35 40 -15 C55 -15 60 5 50 20 C60 35 50 55 30 55 C30 55 20 60 0 60 C-20 60 -30 55 -30 55 C-50 55 -60 35 -50 20 C-60 5 -55 -15 -35 -10 Z"
|
||||
fill="none" stroke="#7C3AED" stroke-width="4"/>
|
||||
|
||||
<!-- Circuit dots -->
|
||||
<circle cx="-15" cy="-25" r="6" fill="#7C3AED"/>
|
||||
<circle cx="15" cy="-20" r="6" fill="#A78BFA"/>
|
||||
<circle cx="-5" cy="5" r="8" fill="#7C3AED"/>
|
||||
<circle cx="25" cy="10" r="6" fill="#A78BFA"/>
|
||||
<circle cx="-20" cy="25" r="6" fill="#7C3AED"/>
|
||||
<circle cx="10" cy="35" r="6" fill="#A78BFA"/>
|
||||
|
||||
<!-- Circuit lines -->
|
||||
<path d="M-15 -25 L-5 5 M15 -20 L-5 5 M-5 5 L25 10 M-5 5 L-20 25 M-5 5 L10 35"
|
||||
stroke="#7C3AED" stroke-width="2" opacity="0.6"/>
|
||||
</g>
|
||||
|
||||
<!-- AI Sparkles around brain -->
|
||||
<g filter="url(#glow)">
|
||||
<!-- Large sparkle top right -->
|
||||
<g transform="translate(920, 400)">
|
||||
<path d="M0 -25 L7 -7 L25 0 L7 7 L0 25 L-7 7 L-25 0 L-7 -7 Z" fill="#FBBF24"/>
|
||||
<circle cx="0" cy="0" r="6" fill="#FFFFFF"/>
|
||||
</g>
|
||||
|
||||
<!-- Medium sparkle top left -->
|
||||
<g transform="translate(700, 420)">
|
||||
<path d="M0 -18 L5 -5 L18 0 L5 5 L0 18 L-5 5 L-18 0 L-5 -5 Z" fill="#FBBF24"/>
|
||||
<circle cx="0" cy="0" r="4" fill="#FFFFFF"/>
|
||||
</g>
|
||||
|
||||
<!-- Small sparkle bottom -->
|
||||
<g transform="translate(850, 650)">
|
||||
<path d="M0 -15 L4 -4 L15 0 L4 4 L0 15 L-4 4 L-15 0 L-4 -4 Z" fill="#FBBF24" opacity="0.9"/>
|
||||
</g>
|
||||
|
||||
<!-- Tiny sparkles -->
|
||||
<g transform="translate(740, 600) scale(0.6)">
|
||||
<path d="M0 -15 L4 -4 L15 0 L4 4 L0 15 L-4 4 L-15 0 L-4 -4 Z" fill="#FBBF24" opacity="0.7"/>
|
||||
</g>
|
||||
<g transform="translate(900, 480) scale(0.5)">
|
||||
<path d="M0 -15 L4 -4 L15 0 L4 4 L0 15 L-4 4 L-15 0 L-4 -4 Z" fill="#FBBF24" opacity="0.8"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- "AI" label -->
|
||||
<text x="810" y="720" text-anchor="middle" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="28" font-weight="700" opacity="0.9">AI-Powered</text>
|
||||
|
||||
<!-- Flow arrow right -->
|
||||
<path d="M1000 520 L1080 520" stroke="url(#arrowGradient)" stroke-width="6" stroke-linecap="round" fill="none"/>
|
||||
<polygon points="1090,520 1070,508 1070,532" fill="#FBBF24"/>
|
||||
|
||||
<!-- ==================== RIGHT SIDE: OUTPUT ==================== -->
|
||||
|
||||
<!-- Output document -->
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="1140" y="200" width="420" height="560" rx="20" fill="url(#docGradient)"/>
|
||||
</g>
|
||||
|
||||
<!-- Output header -->
|
||||
<rect x="1140" y="200" width="420" height="70" rx="20" fill="#10B981"/>
|
||||
<rect x="1140" y="240" width="420" height="30" fill="#10B981"/>
|
||||
<text x="1350" y="245" text-anchor="middle" fill="#FFFFFF" font-family="system-ui, -apple-system, sans-serif" font-size="24" font-weight="600">SEO OPTIMIZED</text>
|
||||
|
||||
<!-- Generated title -->
|
||||
<rect x="1180" y="300" width="340" height="20" rx="4" fill="#7C3AED"/>
|
||||
<rect x="1180" y="330" width="280" height="14" rx="3" fill="#9CA3AF"/>
|
||||
|
||||
<!-- Content preview lines -->
|
||||
<rect x="1180" y="370" width="340" height="10" rx="3" fill="#D1D5DB"/>
|
||||
<rect x="1180" y="390" width="320" height="10" rx="3" fill="#D1D5DB"/>
|
||||
<rect x="1180" y="410" width="340" height="10" rx="3" fill="#D1D5DB"/>
|
||||
<rect x="1180" y="430" width="280" height="10" rx="3" fill="#D1D5DB"/>
|
||||
|
||||
<!-- Subheading -->
|
||||
<rect x="1180" y="465" width="200" height="16" rx="4" fill="#7C3AED" opacity="0.7"/>
|
||||
|
||||
<!-- More content lines -->
|
||||
<rect x="1180" y="495" width="340" height="10" rx="3" fill="#D1D5DB"/>
|
||||
<rect x="1180" y="515" width="300" height="10" rx="3" fill="#D1D5DB"/>
|
||||
<rect x="1180" y="535" width="340" height="10" rx="3" fill="#D1D5DB"/>
|
||||
|
||||
<!-- SEO Score Circle -->
|
||||
<g transform="translate(1470, 670)">
|
||||
<circle cx="0" cy="0" r="70" fill="#FFFFFF" stroke="#10B981" stroke-width="8"/>
|
||||
<text x="0" y="10" text-anchor="middle" fill="#10B981" font-family="system-ui, sans-serif" font-size="42" font-weight="700">92</text>
|
||||
<text x="0" y="35" text-anchor="middle" fill="#6B7280" font-family="system-ui, sans-serif" font-size="14">SEO Score</text>
|
||||
</g>
|
||||
|
||||
<!-- Mini chart bars -->
|
||||
<g transform="translate(1200, 620)">
|
||||
<rect x="0" y="40" width="25" height="60" rx="4" fill="#7C3AED"/>
|
||||
<rect x="35" y="25" width="25" height="75" rx="4" fill="#A78BFA"/>
|
||||
<rect x="70" y="10" width="25" height="90" rx="4" fill="#10B981"/>
|
||||
<rect x="105" y="30" width="25" height="70" rx="4" fill="#7C3AED"/>
|
||||
|
||||
<!-- Trend line -->
|
||||
<path d="M12 35 L47 20 L82 5 L117 25" stroke="#FBBF24" stroke-width="3" fill="none" stroke-linecap="round"/>
|
||||
<circle cx="82" cy="5" r="5" fill="#FBBF24"/>
|
||||
</g>
|
||||
|
||||
<!-- Checkmarks for SEO items -->
|
||||
<g transform="translate(1180, 580)">
|
||||
<circle cx="10" cy="10" r="10" fill="#10B981"/>
|
||||
<path d="M5 10 L9 14 L16 6" stroke="#FFFFFF" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<text x="30" y="15" fill="#374151" font-family="system-ui, sans-serif" font-size="13">Meta optimized</text>
|
||||
</g>
|
||||
|
||||
<!-- ==================== BOTTOM: TITLE & TAGLINE ==================== -->
|
||||
|
||||
<!-- Module name -->
|
||||
<text x="960" y="920" text-anchor="middle" fill="#FFFFFF" font-family="system-ui, -apple-system, BlinkMacSystemFont, sans-serif" font-size="56" font-weight="700">SEO Content Generator</text>
|
||||
|
||||
<!-- Tagline -->
|
||||
<text x="960" y="980" text-anchor="middle" fill="#FFFFFF" font-family="system-ui, -apple-system, sans-serif" font-size="28" font-weight="400" opacity="0.9">AI-Powered Content Creation & Optimization for Odoo</text>
|
||||
|
||||
<!-- Decorative bottom sparkles -->
|
||||
<g transform="translate(550, 950) scale(0.4)">
|
||||
<path d="M0 -20 L5 -5 L20 0 L5 5 L0 20 L-5 5 L-20 0 L-5 -5 Z" fill="#FBBF24" opacity="0.6"/>
|
||||
</g>
|
||||
<g transform="translate(1370, 940) scale(0.35)">
|
||||
<path d="M0 -20 L5 -5 L20 0 L5 5 L0 20 L-5 5 L-20 0 L-5 -5 Z" fill="#FBBF24" opacity="0.6"/>
|
||||
</g>
|
||||
|
||||
<!-- Corner accent sparkles -->
|
||||
<g transform="translate(80, 100)">
|
||||
<path d="M0 -30 L8 -8 L30 0 L8 8 L0 30 L-8 8 L-30 0 L-8 -8 Z" fill="#FBBF24" opacity="0.5"/>
|
||||
</g>
|
||||
<g transform="translate(1840, 980)">
|
||||
<path d="M0 -25 L7 -7 L25 0 L7 7 L0 25 L-7 7 L-25 0 L-7 -7 Z" fill="#FBBF24" opacity="0.5"/>
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,71 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<defs>
|
||||
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#7C3AED;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#5B21B6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="docGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#FFFFFF;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#F3F4F6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background rounded square -->
|
||||
<rect x="20" y="20" width="472" height="472" rx="80" ry="80" fill="url(#bgGradient)"/>
|
||||
|
||||
<!-- Document/Paper -->
|
||||
<path d="M140 100 L300 100 L360 160 L360 400 C360 415 348 427 333 427 L167 427 C152 427 140 415 140 400 L140 100 Z"
|
||||
fill="url(#docGradient)"
|
||||
stroke="#E5E7EB"
|
||||
stroke-width="2"/>
|
||||
|
||||
<!-- Document fold -->
|
||||
<path d="M300 100 L300 160 L360 160"
|
||||
fill="#E5E7EB"
|
||||
stroke="#D1D5DB"
|
||||
stroke-width="2"/>
|
||||
|
||||
<!-- Text lines on document -->
|
||||
<rect x="170" y="185" width="160" height="12" rx="6" fill="#7C3AED" opacity="0.8"/>
|
||||
<rect x="170" y="215" width="130" height="10" rx="5" fill="#9CA3AF"/>
|
||||
<rect x="170" y="240" width="150" height="10" rx="5" fill="#9CA3AF"/>
|
||||
<rect x="170" y="265" width="120" height="10" rx="5" fill="#9CA3AF"/>
|
||||
<rect x="170" y="300" width="160" height="12" rx="6" fill="#7C3AED" opacity="0.6"/>
|
||||
<rect x="170" y="330" width="140" height="10" rx="5" fill="#9CA3AF"/>
|
||||
<rect x="170" y="355" width="155" height="10" rx="5" fill="#9CA3AF"/>
|
||||
|
||||
<!-- SEO Chart/Analytics circle -->
|
||||
<circle cx="380" cy="340" r="85" fill="#FFFFFF" stroke="#7C3AED" stroke-width="4"/>
|
||||
|
||||
<!-- Chart bars inside circle -->
|
||||
<rect x="330" y="355" width="20" height="45" rx="4" fill="#7C3AED"/>
|
||||
<rect x="360" y="325" width="20" height="75" rx="4" fill="#A78BFA"/>
|
||||
<rect x="390" y="305" width="20" height="95" rx="4" fill="#7C3AED"/>
|
||||
<rect x="420" y="340" width="20" height="60" rx="4" fill="#A78BFA"/>
|
||||
|
||||
<!-- Upward trend arrow -->
|
||||
<path d="M335 320 L405 280 L405 300 L425 300 L425 265 L390 265 L390 285 L340 315"
|
||||
fill="none"
|
||||
stroke="#10B981"
|
||||
stroke-width="6"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"/>
|
||||
<polygon points="425,265 440,280 425,280" fill="#10B981"/>
|
||||
|
||||
<!-- AI Sparkle/Star (top right) -->
|
||||
<g transform="translate(420, 120)">
|
||||
<path d="M0 -30 L8 -8 L30 0 L8 8 L0 30 L-8 8 L-30 0 L-8 -8 Z"
|
||||
fill="#FBBF24"/>
|
||||
<circle cx="0" cy="0" r="8" fill="#FFFFFF"/>
|
||||
</g>
|
||||
|
||||
<!-- Small sparkles -->
|
||||
<g transform="translate(100, 150) scale(0.5)">
|
||||
<path d="M0 -20 L5 -5 L20 0 L5 5 L0 20 L-5 5 L-20 0 L-5 -5 Z"
|
||||
fill="#FBBF24" opacity="0.8"/>
|
||||
</g>
|
||||
<g transform="translate(400, 430) scale(0.4)">
|
||||
<path d="M0 -20 L5 -5 L20 0 L5 5 L0 20 L-5 5 L-20 0 L-5 -5 Z"
|
||||
fill="#FBBF24" opacity="0.8"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,233 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
||||
</head>
|
||||
<body>
|
||||
<div class="row position-relative pt-lg-3 px-lg-5 pb-lg-3 p-md-4 p-3 mx-auto d-flex align-items-center" style="color:#333333; font-weight:500; font-size:16px; width:95%">
|
||||
<section class="oe_container">
|
||||
<!-- Header -->
|
||||
<div class="col-md-12 text-center" style="margin-top: 10px;">
|
||||
<a target="_blank" href="https://otoolkit.app">
|
||||
<h2>O'Toolkit</h2>
|
||||
</a>
|
||||
<p>
|
||||
<a href="https://otoolkit.app" target="_blank">Website: otoolkit.app</a><br>
|
||||
Email: <a href="mailto:contact@otoolkit.app">contact@otoolkit.app</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Product Title -->
|
||||
<div class="col-md-12 text-center" style="margin-top: 30px;">
|
||||
<a target="_blank" href="https://otoolkit.app/application/seo-content">
|
||||
<h2>SEO Content Generator</h2>
|
||||
</a>
|
||||
<p>The <strong>SEO Content Generator</strong> is an AI-powered content creation suite for Odoo that helps you generate, optimize, and manage SEO-friendly blog posts, product descriptions, and marketing content directly within your ERP. Powered by advanced AI models, it creates high-quality content with built-in SEO analysis, generates matching images, and seamlessly publishes to your Odoo website blog.</p>
|
||||
<p>Complete documentation available at <a href="https://otoolkit.app/application/seo-content">https://otoolkit.app/application/seo-content</a></p>
|
||||
</div>
|
||||
|
||||
<!-- How it works -->
|
||||
<div class="col-md-12 text-center" style="margin-top: 30px;text-align: left !important;">
|
||||
<h2 style="text-align: center !important">
|
||||
<b>How it works</b>
|
||||
</h2>
|
||||
<div class="row text-dark">
|
||||
<div class="col-lg-8 col-sm-12 mt-5 mb-5">
|
||||
<img src="assets/generate-wizard.webp" class="shadow" style="width: 100%; border-radius: 15px;" alt="SEO Content generation wizard in Odoo">
|
||||
</div>
|
||||
<div class="col-lg-4 col-sm-12 mt-5 mb-5" style="display: flex; align-items: center;">
|
||||
<div>
|
||||
<h3>
|
||||
<b>Generate in one click</b>
|
||||
</h3>
|
||||
<p class="mt-4">Enter your keywords or topic, select your content type and tone, and let AI generate a complete SEO-optimized article with title, content, meta tags, and teaser.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="d-lg-none">
|
||||
|
||||
<div class="col-lg-4 col-sm-12 mt-5 mb-5" style="display: flex; align-items: center;">
|
||||
<div>
|
||||
<h3>
|
||||
<b>Real-time SEO analysis</b>
|
||||
</h3>
|
||||
<p class="mt-4">Get instant feedback on your content's SEO performance with a detailed score breakdown and actionable improvement suggestions.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-8 col-sm-12 mt-5 mb-5">
|
||||
<img src="assets/seo-analysis.webp" class="shadow" style="width: 100%; border-radius: 15px;" alt="SEO analysis with score and recommendations">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI Image Generation -->
|
||||
<div class="col-md-12 text-center" style="margin-top: 30px;text-align: left !important;">
|
||||
<h2 style="text-align: center !important">
|
||||
<b>AI-Powered Image Generation</b>
|
||||
</h2>
|
||||
<div class="row text-dark">
|
||||
<div class="col-lg-8 col-sm-12 mt-5 mb-5">
|
||||
<img src="assets/image-generation.gif" class="shadow" style="width: 100%; border-radius: 15px;" alt="AI image generation with multiple styles">
|
||||
</div>
|
||||
<div class="col-lg-4 col-sm-12 mt-5 mb-5" style="display: flex; align-items: center;">
|
||||
<div>
|
||||
<h3>
|
||||
<b>Matching visuals</b>
|
||||
</h3>
|
||||
<p class="mt-4">Automatically generate images that match your content. Choose from multiple styles: photorealistic, illustration, digital art, watercolor, sketch, or 3D render.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="d-lg-none">
|
||||
|
||||
<div class="col-lg-4 col-sm-12 mt-5 mb-5" style="display: flex; align-items: center;">
|
||||
<div>
|
||||
<h3>
|
||||
<b>Content-aware prompts</b>
|
||||
</h3>
|
||||
<p class="mt-4">Images are generated based on your content's title, sections, and keywords, ensuring they perfectly complement your article.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-8 col-sm-12 mt-5 mb-5">
|
||||
<img src="assets/image-styles.gif" class="shadow" style="width: 100%; border-radius: 15px;" alt="Different image generation styles">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Features -->
|
||||
<div class="col-md-12 text-center" style="margin-top: 30px;text-align: left !important;">
|
||||
<h2 style="text-align: center !important">
|
||||
<b>Powerful Features</b>
|
||||
</h2>
|
||||
<div class="row text-dark">
|
||||
<div class="col-lg-8 col-sm-12 mt-5 mb-5">
|
||||
<img src="assets/batch-processing.gif" class="shadow" style="width: 100%; border-radius: 15px;" alt="Batch content generation">
|
||||
</div>
|
||||
<div class="col-lg-4 col-sm-12 mt-5 mb-5" style="display: flex; align-items: center;">
|
||||
<div>
|
||||
<h3>
|
||||
<b>Batch generation</b>
|
||||
</h3>
|
||||
<p class="mt-4">Generate hundreds of product descriptions or blog posts at once. Import from CSV, product categories, or keyword lists.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="d-lg-none">
|
||||
|
||||
<div class="col-lg-4 col-sm-12 mt-5 mb-5" style="display: flex; align-items: center;">
|
||||
<div>
|
||||
<h3>
|
||||
<b>Ideas Queue & Scheduling</b>
|
||||
</h3>
|
||||
<p class="mt-4">Plan your content calendar with the Ideas Queue. Schedule content for future generation and even auto-publish to your blog.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-8 col-sm-12 mt-5 mb-5">
|
||||
<img src="assets/ideas-queue.webp" class="shadow" style="width: 100%; border-radius: 15px;" alt="Ideas queue and content scheduling">
|
||||
</div>
|
||||
|
||||
<hr class="d-lg-none">
|
||||
|
||||
<div class="col-lg-8 col-sm-12 mt-5 mb-5">
|
||||
<img src="assets/blog-integration.gif" class="shadow" style="width: 100%; border-radius: 15px;" alt="Blog post creation from SEO content">
|
||||
</div>
|
||||
<div class="col-lg-4 col-sm-12 mt-5 mb-5" style="display: flex; align-items: center;">
|
||||
<div>
|
||||
<h3>
|
||||
<b>One-click blog publishing</b>
|
||||
</h3>
|
||||
<p class="mt-4">Create blog posts directly from your generated content with cover images, meta tags, and SEO-friendly URLs already configured.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="d-lg-none">
|
||||
|
||||
<div class="col-lg-4 col-sm-12 mt-5 mb-5" style="display: flex; align-items: center;">
|
||||
<div>
|
||||
<h3>
|
||||
<b>Templates & Brand Voice</b>
|
||||
</h3>
|
||||
<p class="mt-4">Create reusable templates with custom prompts and brand voice guidelines to maintain consistency across all your content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-8 col-sm-12 mt-5 mb-5">
|
||||
<img src="assets/templates.gif" class="shadow" style="width: 100%; border-radius: 15px;" alt="Content templates and brand voice">
|
||||
</div>
|
||||
|
||||
<hr class="d-lg-none">
|
||||
|
||||
<div class="col-lg-8 col-sm-12 mt-5 mb-5">
|
||||
<img src="assets/references.gif" class="shadow" style="width: 100%; border-radius: 15px;" alt="URL reference fetching">
|
||||
</div>
|
||||
<div class="col-lg-4 col-sm-12 mt-5 mb-5" style="display: flex; align-items: center;">
|
||||
<div>
|
||||
<h3>
|
||||
<b>Reference materials</b>
|
||||
</h3>
|
||||
<p class="mt-4">Add URLs as references and the AI will fetch and analyze their content to create more informed, accurate articles.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Multi-language -->
|
||||
<div class="col-md-12 text-center" style="margin-top: 30px;text-align: left !important;">
|
||||
<h2 style="text-align: center !important">
|
||||
<b>Multi-Language Support</b>
|
||||
</h2>
|
||||
<div class="row text-dark">
|
||||
<div class="col-lg-4 col-sm-12 mt-5 mb-5" style="display: flex; align-items: center;">
|
||||
<div>
|
||||
<h3>
|
||||
<b>Generate in any language</b>
|
||||
</h3>
|
||||
<p class="mt-4">Create content in any language activated in your Odoo instance. Combined with O'Toolkit Auto Translate, automatically translate to all your website languages.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-8 col-sm-12 mt-5 mb-5">
|
||||
<img src="assets/multi-language.webp" class="shadow" style="width: 100%; border-radius: 15px;" alt="Multi-language content generation">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content Types -->
|
||||
<div class="col-md-12 text-center" style="margin-top: 30px;">
|
||||
<h2><b>Content Types</b></h2>
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="p-4" style="background-color: #f8f9fa; border-radius: 15px; height: 100%;">
|
||||
<h4 style="color: rgb(124, 58, 237);">Blog Posts</h4>
|
||||
<p>Full-length articles optimized for search engines with proper heading structure, meta tags, and engaging content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="p-4" style="background-color: #f8f9fa; border-radius: 15px; height: 100%;">
|
||||
<h4 style="color: rgb(124, 58, 237);">Product Descriptions</h4>
|
||||
<p>Compelling product descriptions that highlight features, benefits, and SEO keywords. Apply directly to products.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="p-4" style="background-color: #f8f9fa; border-radius: 15px; height: 100%;">
|
||||
<h4 style="color: rgb(124, 58, 237);">Landing Pages</h4>
|
||||
<p>Conversion-focused content for landing pages, category descriptions, and marketing campaigns.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CTA Footer -->
|
||||
<div class="col-md-12 text-center mt-5 p-4" style="background-color: #f8f9fa; border-radius: 15px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);">
|
||||
<h3 style="color: rgb(124, 58, 237);">Ready to supercharge your content creation?</h3>
|
||||
<p class="mt-3" style="color: #333;">Start generating SEO-optimized content in minutes. Visit our website or contact us for more information.</p>
|
||||
<p>
|
||||
<span href="https://otoolkit.app" class="btn otoolkit-open-site" style="background-color: rgb(124, 58, 237); color: #fff; border-radius: 5px; padding: 10px 20px; text-decoration: none;">Visit Our Website</span>
|
||||
<a href="mailto:contact@otoolkit.app" class="btn" style="background-color: #fff; color: rgb(124, 58, 237); border: 2px solid rgb(124, 58, 237); border-radius: 5px; padding: 10px 20px; text-decoration: none;">Email Us</a>
|
||||
</p>
|
||||
<a href="https://otoolkit.app">https://otoolkit.app</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,608 @@
|
||||
/* O'Toolkit SEO Content Generator - Custom Styles */
|
||||
|
||||
/* ============================================
|
||||
SEO Score Badge Styling
|
||||
============================================ */
|
||||
|
||||
.o_seo_score_badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 50px;
|
||||
padding: 4px 12px;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.o_seo_score_excellent {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.o_seo_score_good {
|
||||
background-color: #5cb85c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.o_seo_score_average {
|
||||
background-color: #ffc107;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.o_seo_score_poor {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Content Generation States
|
||||
============================================ */
|
||||
|
||||
.o_seo_content_state {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.o_seo_content_state .o_state_generating {
|
||||
color: #17a2b8;
|
||||
}
|
||||
|
||||
.o_seo_content_state .o_state_generated {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.o_seo_content_state .o_state_failed {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
/* Generating animation */
|
||||
.o_seo_generating_spinner {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid #17a2b8;
|
||||
border-radius: 50%;
|
||||
border-top-color: transparent;
|
||||
animation: seo-spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes seo-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Content Preview Styling
|
||||
============================================ */
|
||||
|
||||
.o_seo_content_preview {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.o_seo_content_preview h1,
|
||||
.o_seo_content_preview h2,
|
||||
.o_seo_content_preview h3,
|
||||
.o_seo_content_preview h4 {
|
||||
color: #343a40;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.o_seo_content_preview h1:first-child,
|
||||
.o_seo_content_preview h2:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.o_seo_content_preview p {
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.o_seo_content_preview ul,
|
||||
.o_seo_content_preview ol {
|
||||
margin-bottom: 1em;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.o_seo_content_preview li {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Image Gallery Styling
|
||||
============================================ */
|
||||
|
||||
.o_seo_image_gallery {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.o_seo_image_card {
|
||||
position: relative;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
transition: box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.o_seo_image_card:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.o_seo_image_card img {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.o_seo_image_card .o_image_info {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.o_seo_image_card .o_image_state {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.o_seo_image_card .o_image_state_pending {
|
||||
background-color: #ffc107;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.o_seo_image_card .o_image_state_generating {
|
||||
background-color: #17a2b8;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.o_seo_image_card .o_image_state_completed {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.o_seo_image_card .o_image_state_failed {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Image placeholder for pending/generating */
|
||||
.o_seo_image_placeholder {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #e9ecef;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Kanban Card Styling
|
||||
============================================ */
|
||||
|
||||
.o_kanban_record .o_seo_content_kanban {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.o_seo_content_kanban .o_kanban_title {
|
||||
font-weight: 600;
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 8px;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.o_seo_content_kanban .o_kanban_meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.o_seo_content_kanban .o_kanban_tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.85em;
|
||||
background-color: #e9ecef;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.o_seo_content_kanban .o_kanban_progress {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Template Selection Styling
|
||||
============================================ */
|
||||
|
||||
.o_seo_template_selector {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.o_seo_template_card {
|
||||
border: 2px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.o_seo_template_card:hover {
|
||||
border-color: #007bff;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.o_seo_template_card.selected {
|
||||
border-color: #007bff;
|
||||
background-color: #e7f1ff;
|
||||
}
|
||||
|
||||
.o_seo_template_card .o_template_name {
|
||||
font-weight: 600;
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 8px;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.o_seo_template_card .o_template_desc {
|
||||
font-size: 0.9em;
|
||||
color: #6c757d;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.o_seo_template_card .o_template_meta {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
font-size: 0.85em;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Wizard Styling
|
||||
============================================ */
|
||||
|
||||
.o_seo_wizard_step {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.o_seo_wizard_step .o_step_header {
|
||||
font-size: 1.2em;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
color: #343a40;
|
||||
}
|
||||
|
||||
.o_seo_wizard_step .o_step_description {
|
||||
color: #6c757d;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* Keyword input styling */
|
||||
.o_seo_keyword_input {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.o_seo_keyword_tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.o_seo_keyword_tag .o_remove {
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.o_seo_keyword_tag .o_remove:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Version History Styling
|
||||
============================================ */
|
||||
|
||||
.o_seo_version_list {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.o_seo_version_item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.o_seo_version_item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.o_seo_version_item .o_version_info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.o_seo_version_item .o_version_number {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.o_seo_version_item .o_version_date {
|
||||
font-size: 0.85em;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.o_seo_version_item .o_version_actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Cost Estimation Panel
|
||||
============================================ */
|
||||
|
||||
.o_seo_cost_panel {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.o_seo_cost_panel .o_cost_header {
|
||||
font-weight: 600;
|
||||
margin-bottom: 12px;
|
||||
color: #343a40;
|
||||
}
|
||||
|
||||
.o_seo_cost_panel .o_cost_row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 4px 0;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.o_seo_cost_panel .o_cost_total {
|
||||
border-top: 1px solid #dee2e6;
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
SEO Analysis Panel
|
||||
============================================ */
|
||||
|
||||
.o_seo_analysis_panel {
|
||||
background-color: #fff;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.o_seo_analysis_item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f1f3f4;
|
||||
}
|
||||
|
||||
.o_seo_analysis_item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.o_seo_analysis_item .o_check_icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.o_seo_analysis_item .o_check_pass {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.o_seo_analysis_item .o_check_warn {
|
||||
color: #ffc107;
|
||||
}
|
||||
|
||||
.o_seo_analysis_item .o_check_fail {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.o_seo_analysis_item .o_check_text {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.o_seo_analysis_item .o_check_suggestion {
|
||||
font-size: 0.85em;
|
||||
color: #6c757d;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Wizard Step Indicator
|
||||
============================================ */
|
||||
|
||||
.otk_wizard_steps {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.otk_step_circle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
min-width: 28px;
|
||||
border-radius: 50%;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.otk_step_circle.otk_step_active {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.25);
|
||||
}
|
||||
|
||||
.otk_step_circle.otk_step_done {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.otk_step_circle.otk_step_upcoming {
|
||||
background-color: #dee2e6;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Inline SEO Panel
|
||||
============================================ */
|
||||
|
||||
.o_seo_inline_panel {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.o_seo_inline_panel .o_meta_indicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Meta Length Indicators
|
||||
============================================ */
|
||||
|
||||
.o_meta_length_optimal {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.o_meta_length_short {
|
||||
color: #ffc107;
|
||||
}
|
||||
|
||||
.o_meta_length_long {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Diff View Styling
|
||||
============================================ */
|
||||
|
||||
.o_seo_diff_view {
|
||||
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
||||
font-size: 0.85em;
|
||||
line-height: 1.5;
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.o_seo_diff_view .o_diff_header {
|
||||
color: #6c757d;
|
||||
font-weight: 600;
|
||||
padding: 2px 4px;
|
||||
background-color: #e9ecef;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.o_seo_diff_view .o_diff_section {
|
||||
color: #6f42c1;
|
||||
padding: 2px 4px;
|
||||
background-color: #f3e8ff;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.o_seo_diff_view .o_diff_add {
|
||||
color: #155724;
|
||||
background-color: #d4edda;
|
||||
padding: 1px 4px;
|
||||
border-left: 3px solid #28a745;
|
||||
}
|
||||
|
||||
.o_seo_diff_view .o_diff_remove {
|
||||
color: #721c24;
|
||||
background-color: #f8d7da;
|
||||
padding: 1px 4px;
|
||||
border-left: 3px solid #dc3545;
|
||||
}
|
||||
|
||||
.o_seo_diff_view .o_diff_context {
|
||||
color: #495057;
|
||||
padding: 1px 4px;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Responsive Adjustments
|
||||
============================================ */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.o_seo_image_gallery {
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
}
|
||||
|
||||
.o_seo_template_selector {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.o_seo_content_preview {
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.o_seo_step_indicator {
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.o_seo_step_chevron {
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
.o_seo_inline_panel .row {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.o_seo_diff_view {
|
||||
max-height: 300px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Component, onWillStart, useState } from "@odoo/owl";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
class SeoDashboard extends Component {
|
||||
static template = "otoolkit_seo_content.SeoDashboard";
|
||||
static props = ["*"];
|
||||
|
||||
setup() {
|
||||
this.orm = useService("orm");
|
||||
this.action = useService("action");
|
||||
this.state = useState({
|
||||
data: null,
|
||||
loading: true,
|
||||
});
|
||||
onWillStart(async () => {
|
||||
await this.loadDashboardData();
|
||||
});
|
||||
}
|
||||
|
||||
async loadDashboardData() {
|
||||
this.state.loading = true;
|
||||
try {
|
||||
this.state.data = await this.orm.call(
|
||||
"otk.seo.content",
|
||||
"get_dashboard_data",
|
||||
[]
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("Failed to load dashboard data:", e);
|
||||
this.state.data = null;
|
||||
}
|
||||
this.state.loading = false;
|
||||
}
|
||||
|
||||
getScoreColor(score) {
|
||||
if (score >= 70) return "success";
|
||||
if (score >= 40) return "warning";
|
||||
return "danger";
|
||||
}
|
||||
|
||||
getStateLabel(state) {
|
||||
const labels = {
|
||||
draft: "Draft",
|
||||
generating: "Generating",
|
||||
images_pending: "Images Pending",
|
||||
review: "Review",
|
||||
approved: "Approved",
|
||||
published: "Published",
|
||||
archived: "Archived",
|
||||
};
|
||||
return labels[state] || state;
|
||||
}
|
||||
|
||||
getTypeLabel(type) {
|
||||
const labels = {
|
||||
blog_post: "Blog Post",
|
||||
product_desc: "Product Desc",
|
||||
category_desc: "Category Desc",
|
||||
landing_page: "Landing Page",
|
||||
social_post: "Social Post",
|
||||
};
|
||||
return labels[type] || type;
|
||||
}
|
||||
|
||||
getStateColor(state) {
|
||||
const colors = {
|
||||
draft: "#6c757d",
|
||||
generating: "#17a2b8",
|
||||
images_pending: "#ffc107",
|
||||
review: "#007bff",
|
||||
approved: "#28a745",
|
||||
published: "#20c997",
|
||||
archived: "#adb5bd",
|
||||
};
|
||||
return colors[state] || "#6c757d";
|
||||
}
|
||||
|
||||
onClickTotalContent() {
|
||||
this.action.doAction({
|
||||
type: "ir.actions.act_window",
|
||||
name: "All Content",
|
||||
res_model: "otk.seo.content",
|
||||
view_mode: "list,form",
|
||||
views: [[false, "list"], [false, "form"]],
|
||||
});
|
||||
}
|
||||
|
||||
onClickStaleContent() {
|
||||
this.action.doAction({
|
||||
type: "ir.actions.act_window",
|
||||
name: "Stale Content",
|
||||
res_model: "otk.seo.content",
|
||||
view_mode: "list,form",
|
||||
views: [[false, "list"], [false, "form"]],
|
||||
domain: [["is_stale", "=", true]],
|
||||
});
|
||||
}
|
||||
|
||||
onClickState(state) {
|
||||
this.action.doAction({
|
||||
type: "ir.actions.act_window",
|
||||
name: this.getStateLabel(state),
|
||||
res_model: "otk.seo.content",
|
||||
view_mode: "list,form",
|
||||
views: [[false, "list"], [false, "form"]],
|
||||
domain: [["state", "=", state]],
|
||||
});
|
||||
}
|
||||
|
||||
onClickRecent(contentId) {
|
||||
this.action.doAction({
|
||||
type: "ir.actions.act_window",
|
||||
res_model: "otk.seo.content",
|
||||
res_id: contentId,
|
||||
view_mode: "form",
|
||||
views: [[false, "form"]],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registry.category("actions").add("seo_content_dashboard", SeoDashboard);
|
||||
@@ -0,0 +1,196 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="otoolkit_seo_content.SeoDashboard">
|
||||
<div class="o_action">
|
||||
<div class="container-fluid py-3">
|
||||
<!-- Header -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="mb-0">
|
||||
<i class="fa fa-line-chart me-2"/>SEO Content Dashboard
|
||||
</h2>
|
||||
<button class="btn btn-sm btn-outline-primary" t-on-click="loadDashboardData">
|
||||
<i class="fa fa-refresh me-1"/>Refresh
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div t-if="state.loading" class="text-center py-5">
|
||||
<i class="fa fa-spinner fa-spin fa-3x text-primary"/>
|
||||
<p class="mt-3 text-muted">Loading dashboard data...</p>
|
||||
</div>
|
||||
|
||||
<t t-if="!state.loading and state.data">
|
||||
<!-- Row 1: KPI Cards -->
|
||||
<div class="row g-3 mb-4">
|
||||
<!-- Total Content -->
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="card h-100 border-0 shadow-sm" style="cursor:pointer" t-on-click="onClickTotalContent">
|
||||
<div class="card-body text-center">
|
||||
<div class="text-muted small text-uppercase mb-1">Total Content</div>
|
||||
<div class="display-5 fw-bold text-primary" t-out="state.data.totalContent"/>
|
||||
<div class="text-muted small mt-1">pieces created</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Avg SEO Score -->
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-body text-center">
|
||||
<div class="text-muted small text-uppercase mb-1">Avg SEO Score</div>
|
||||
<div class="display-5 fw-bold">
|
||||
<span t-attf-class="text-#{getScoreColor(state.data.avgSeoScore)}" t-out="state.data.avgSeoScore"/>
|
||||
</div>
|
||||
<div class="progress mt-2" style="height: 6px;">
|
||||
<div class="progress-bar"
|
||||
t-attf-class="bg-#{getScoreColor(state.data.avgSeoScore)}"
|
||||
role="progressbar"
|
||||
t-attf-style="width: #{state.data.avgSeoScore}%"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Tokens Used -->
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-body text-center">
|
||||
<div class="text-muted small text-uppercase mb-1">Tokens Used</div>
|
||||
<div class="display-5 fw-bold text-info" t-out="state.data.totalTokens"/>
|
||||
<div class="text-muted small mt-1">total consumed</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Stale Content -->
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="card h-100 border-0 shadow-sm" style="cursor:pointer" t-on-click="onClickStaleContent">
|
||||
<div class="card-body text-center">
|
||||
<div class="text-muted small text-uppercase mb-1">Needs Refresh</div>
|
||||
<div t-attf-class="display-5 fw-bold #{state.data.staleCount > 0 ? 'text-warning' : 'text-success'}"
|
||||
t-out="state.data.staleCount"/>
|
||||
<div class="text-muted small mt-1">stale items</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Row 2: Charts (simple bar-like display) -->
|
||||
<div class="row g-3 mb-4">
|
||||
<!-- Content by State -->
|
||||
<div class="col-lg-6">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-transparent border-0">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fa fa-bar-chart me-1"/>Content by Status
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<t t-foreach="Object.entries(state.data.contentByState)" t-as="entry" t-key="entry[0]">
|
||||
<div class="d-flex align-items-center mb-2" style="cursor:pointer"
|
||||
t-on-click="() => this.onClickState(entry[0])">
|
||||
<span class="text-truncate me-2" style="min-width:100px;" t-out="getStateLabel(entry[0])"/>
|
||||
<div class="progress flex-grow-1 me-2" style="height: 20px;">
|
||||
<div class="progress-bar"
|
||||
t-attf-style="width: #{state.data.totalContent ? (entry[1] / state.data.totalContent * 100) : 0}%; background-color: #{getStateColor(entry[0])};"
|
||||
t-out="entry[1]"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<div t-if="!Object.keys(state.data.contentByState).length"
|
||||
class="text-muted text-center py-3">
|
||||
No content yet
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Content by Type -->
|
||||
<div class="col-lg-6">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-transparent border-0">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fa fa-pie-chart me-1"/>Content by Type
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<t t-foreach="Object.entries(state.data.contentByType)" t-as="entry" t-key="entry[0]">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="badge bg-secondary" t-out="getTypeLabel(entry[0])"/>
|
||||
<strong t-out="entry[1]"/>
|
||||
</div>
|
||||
</t>
|
||||
<div t-if="!Object.keys(state.data.contentByType).length"
|
||||
class="text-muted text-center py-3">
|
||||
No content yet
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Row 3: Recent Content -->
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-transparent border-0">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fa fa-clock-o me-1"/>Recent Content
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
<th>SEO Score</th>
|
||||
<th>Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-foreach="state.data.recentContent" t-as="item" t-key="item.id">
|
||||
<tr style="cursor:pointer" t-on-click="() => this.onClickRecent(item.id)">
|
||||
<td class="text-truncate" style="max-width:250px;" t-out="item.name"/>
|
||||
<td>
|
||||
<span class="badge bg-light text-dark" t-out="getTypeLabel(item.content_type)"/>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge"
|
||||
t-attf-style="background-color: #{getStateColor(item.state)}; color: white;"
|
||||
t-out="getStateLabel(item.state)"/>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="progress flex-grow-1 me-1" style="height:8px;width:60px;">
|
||||
<div class="progress-bar"
|
||||
t-attf-class="bg-#{getScoreColor(item.seo_score)}"
|
||||
t-attf-style="width: #{item.seo_score}%"/>
|
||||
</div>
|
||||
<small t-out="item.seo_score"/>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-muted small" t-out="item.create_date"/>
|
||||
</tr>
|
||||
</t>
|
||||
<tr t-if="!state.data.recentContent.length">
|
||||
<td colspan="5" class="text-center text-muted py-3">
|
||||
No content created yet
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Error / No Data -->
|
||||
<div t-if="!state.loading and !state.data" class="text-center py-5">
|
||||
<i class="fa fa-exclamation-triangle fa-3x text-warning"/>
|
||||
<p class="mt-3 text-muted">Failed to load dashboard data. Please try again.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||