feat: Enhance AI image generation prompts for natural, photorealistic advertising creatives by enriching context and refining modification instructions.
0fbc3f9
| "use client"; | |
| import React from "react"; | |
| import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card"; | |
| import type { CreativeAnalysisData } from "@/types/api"; | |
| interface CreativeAnalysisProps { | |
| analysis: CreativeAnalysisData; | |
| onImprovementClick?: (improvement: string) => void; | |
| onMultipleImprovementsApply?: (improvements: string[]) => void; | |
| } | |
| export const CreativeAnalysis: React.FC<CreativeAnalysisProps> = ({ | |
| analysis, | |
| onImprovementClick, | |
| onMultipleImprovementsApply, | |
| }) => { | |
| const [selectedImprovements, setSelectedImprovements] = React.useState<Set<number>>(new Set()); | |
| const toggleImprovement = (index: number) => { | |
| setSelectedImprovements(prev => { | |
| const newSet = new Set(prev); | |
| if (newSet.has(index)) { | |
| newSet.delete(index); | |
| } else { | |
| newSet.add(index); | |
| } | |
| return newSet; | |
| }); | |
| }; | |
| const handleApplySelected = () => { | |
| const selected = Array.from(selectedImprovements).map(idx => analysis.areas_for_improvement[idx]); | |
| if (selected.length > 0 && onMultipleImprovementsApply) { | |
| onMultipleImprovementsApply(selected); | |
| setSelectedImprovements(new Set()); | |
| } | |
| }; | |
| return ( | |
| <Card variant="glass"> | |
| <CardHeader> | |
| <CardTitle>Creative Analysis</CardTitle> | |
| </CardHeader> | |
| <CardContent className="space-y-6"> | |
| {/* Visual Style & Mood */} | |
| <div className="grid grid-cols-2 gap-4"> | |
| <div className="bg-gradient-to-br from-purple-50 to-blue-50 rounded-xl p-4"> | |
| <h4 className="text-sm font-semibold text-gray-700 mb-1">Visual Style</h4> | |
| <p className="text-gray-900 font-medium">{analysis.visual_style}</p> | |
| </div> | |
| <div className="bg-gradient-to-br from-blue-50 to-cyan-50 rounded-xl p-4"> | |
| <h4 className="text-sm font-semibold text-gray-700 mb-1">Mood</h4> | |
| <p className="text-gray-900 font-medium">{analysis.mood}</p> | |
| </div> | |
| </div> | |
| {/* Color Palette */} | |
| <div> | |
| <h4 className="text-sm font-semibold text-gray-700 mb-2">Color Palette</h4> | |
| <div className="flex flex-wrap gap-2"> | |
| {analysis.color_palette.map((color, index) => ( | |
| <span | |
| key={index} | |
| className="px-3 py-1 bg-white rounded-full text-sm text-gray-700 border border-gray-200" | |
| > | |
| {color} | |
| </span> | |
| ))} | |
| </div> | |
| </div> | |
| {/* Composition & Subject */} | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <h4 className="text-sm font-semibold text-gray-700 mb-1">Composition</h4> | |
| <p className="text-gray-600 text-sm">{analysis.composition}</p> | |
| </div> | |
| <div> | |
| <h4 className="text-sm font-semibold text-gray-700 mb-1">Subject Matter</h4> | |
| <p className="text-gray-600 text-sm">{analysis.subject_matter}</p> | |
| </div> | |
| </div> | |
| {/* Text Content (if any) */} | |
| {analysis.text_content && ( | |
| <div> | |
| <h4 className="text-sm font-semibold text-gray-700 mb-1">Text Content</h4> | |
| <p className="text-gray-600 text-sm bg-gray-50 p-3 rounded-lg">"{analysis.text_content}"</p> | |
| </div> | |
| )} | |
| {/* Current Angle & Concept */} | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| {analysis.current_angle && ( | |
| <div className="bg-orange-50 rounded-xl p-4"> | |
| <h4 className="text-sm font-semibold text-orange-700 mb-1">Current Angle</h4> | |
| <p className="text-orange-900 font-medium">{analysis.current_angle}</p> | |
| </div> | |
| )} | |
| {analysis.current_concept && ( | |
| <div className="bg-green-50 rounded-xl p-4"> | |
| <h4 className="text-sm font-semibold text-green-700 mb-1">Current Concept</h4> | |
| <p className="text-green-900 font-medium">{analysis.current_concept}</p> | |
| </div> | |
| )} | |
| </div> | |
| {/* Target Audience */} | |
| {analysis.target_audience && ( | |
| <div> | |
| <h4 className="text-sm font-semibold text-gray-700 mb-1">Target Audience</h4> | |
| <p className="text-gray-600 text-sm">{analysis.target_audience}</p> | |
| </div> | |
| )} | |
| {/* Strengths */} | |
| <div> | |
| <h4 className="text-sm font-semibold text-gray-700 mb-2">Strengths</h4> | |
| <ul className="space-y-1"> | |
| {analysis.strengths.map((strength, index) => ( | |
| <li key={index} className="flex items-start gap-2 text-sm text-gray-600"> | |
| <svg className="w-4 h-4 text-green-500 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"> | |
| <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" /> | |
| </svg> | |
| {strength} | |
| </li> | |
| ))} | |
| </ul> | |
| </div> | |
| {/* Areas for Improvement */} | |
| <div> | |
| <div className="flex items-center justify-between mb-2"> | |
| <h4 className="text-sm font-semibold text-gray-700"> | |
| Areas for Improvement | |
| {onMultipleImprovementsApply && ( | |
| <span className="ml-2 text-xs text-gray-500 font-normal"> | |
| (Select multiple and apply together) | |
| </span> | |
| )} | |
| </h4> | |
| {onMultipleImprovementsApply && selectedImprovements.size > 0 && ( | |
| <button | |
| type="button" | |
| onClick={handleApplySelected} | |
| className="px-3 py-1 bg-amber-500 text-white text-xs font-medium rounded-lg hover:bg-amber-600 transition-colors" | |
| > | |
| Apply {selectedImprovements.size} Selected | |
| </button> | |
| )} | |
| </div> | |
| <ul className="space-y-2"> | |
| {analysis.areas_for_improvement.map((area, index) => ( | |
| <li key={index}> | |
| <div | |
| className={`flex items-start gap-2 text-sm p-3 rounded-lg transition-all border ${ | |
| selectedImprovements.has(index) | |
| ? "bg-amber-50 border-amber-300" | |
| : onMultipleImprovementsApply | |
| ? "hover:bg-amber-50 hover:border-amber-200 border-transparent cursor-pointer" | |
| : onImprovementClick | |
| ? "hover:bg-amber-50 hover:border-amber-300 border-transparent cursor-pointer" | |
| : "border-transparent cursor-default" | |
| }`} | |
| > | |
| {onMultipleImprovementsApply && ( | |
| <input | |
| type="checkbox" | |
| checked={selectedImprovements.has(index)} | |
| onChange={() => toggleImprovement(index)} | |
| className="w-4 h-4 text-amber-600 border-gray-300 rounded focus:ring-amber-500 mt-0.5 cursor-pointer" | |
| /> | |
| )} | |
| <svg className="w-4 h-4 text-amber-500 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"> | |
| <path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" /> | |
| </svg> | |
| <span className="text-gray-600 flex-1">{area}</span> | |
| {!onMultipleImprovementsApply && onImprovementClick && ( | |
| <button | |
| type="button" | |
| onClick={() => onImprovementClick(area)} | |
| className="text-amber-500 hover:text-amber-600 transition-colors" | |
| > | |
| <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" /> | |
| </svg> | |
| </button> | |
| )} | |
| </div> | |
| </li> | |
| ))} | |
| </ul> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| ); | |
| }; | |