Redo image audit: full 6-image analysis with per-image scores, coverage map, set analysis
This commit is contained in:
@@ -3,49 +3,122 @@
|
||||
"name": "Turmeric with BioPerine & Vitamin C 95% Curcuminoids",
|
||||
"generatedAt": "2026-04-28",
|
||||
"generatedBy": "claude-code-in-session",
|
||||
"imageUrl": "https://images.justvitamins.co.uk/product-images/Normal/3c4e4cc8-4a6d-a8a2-e89577cfe1db-1.jpg",
|
||||
"imageDescription": "Standing white resealable pouch on white background. Dark circular motif on label showing turmeric powder on a wooden spoon. Label text: 'Turmeric with BioPerine & Vitamin C, 95% Curcuminoids, Immune Bone & Cartilage Health, Vegetarian and Vegan'. Just Vitamins logo and compostable badge visible. 3-4 yellow tablets displayed at base of pouch.",
|
||||
"imageUrl": "https://images.justvitamins.co.uk/product-images/Normal/3c4e9e20-5169-4a72-a016-a24622ba00fe-1.jpg",
|
||||
"imageCount": 6,
|
||||
"overallScore": {
|
||||
"value": 3.1,
|
||||
"value": 3.4,
|
||||
"outOf": 5,
|
||||
"confidence": "high",
|
||||
"verdict": "Functional but not compelling. Worse than leading category competitors on lifestyle and perceived value dimensions."
|
||||
"verdict": "A solid informational set that explains the science well but completely misses the emotional purchase driver — pain relief and restored mobility. No lifestyle image exists across all 6 shots."
|
||||
},
|
||||
"images": [
|
||||
{
|
||||
"index": 1,
|
||||
"url": "https://images.justvitamins.co.uk/product-images/Normal/3c4e9e20-5169-4a72-a016-a24622ba00fe-1.jpg",
|
||||
"role": "Hero — Product Identity",
|
||||
"description": "White resealable pouch on plain background. Dark circular label shows turmeric powder on wooden spoon. Text: 'Turmeric with BioPerine® & Vitamin C, 95% Curcuminoids, Immune, Bone & Cartilage Health, Vegetarian and Vegan'. Just Vitamins logo and compostable badge. 3-4 yellow tablets at base.",
|
||||
"score": 3.2,
|
||||
"strengths": ["Clean, professional studio shot", "Strong product identity — branding and key ingredients are legible", "Compostable badge is a genuine differentiator", "Tablet visual confirms physical product"],
|
||||
"weaknesses": ["White-on-white feels flat and lacks premium depth", "No lifestyle context — answers 'what is this' but not 'why do I need this'", "Pouch format reads as budget vs glass bottle competitors at same price", "No emotional connection to the pain relief outcome"]
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"url": "https://images.justvitamins.co.uk/product-images/Normal/3c4e9e20-5169-4a72-a016-a24622ba00fe-2.jpg",
|
||||
"role": "Tablet Size Reference",
|
||||
"description": "Single yellow tablet next to a metal ruler on white background. Tablet measures approximately 20mm x 12mm.",
|
||||
"score": 2.8,
|
||||
"strengths": ["Directly addresses the capsule size question — reviews mention size as a concern", "Honest and transparent — no overselling", "Shows the tablet is a manageable, easy-to-swallow size"],
|
||||
"weaknesses": ["Cold and clinical aesthetic — metal ruler + white background is unappealing", "Tablet appears dull yellow, slightly unappealing up close", "No context — a tablet against a ruler conveys no benefit or aspiration", "Would perform better as an in-hand lifestyle shot showing the tablet's manageable size alongside something relatable"]
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"url": "https://images.justvitamins.co.uk/product-images/Normal/3c4e9e20-5169-4a72-a016-a24622ba00fe-4.jpg",
|
||||
"role": "Social Proof + Feature List",
|
||||
"description": "Warm turmeric ingredient background (root + powder in bowl). Two 5-star customer quotes. Red badge: '4 months supply, 120 tablets'. Six bullet features: 95% curcuminoids, genuine Indian turmeric, BioPerine absorption, 120 easy-to-swallow tablets, plastic-free compostable pouch, 100% money back guarantee. Body system icons at base. Product pouch visible.",
|
||||
"score": 4.1,
|
||||
"strengths": ["Strong conversion image — covers the main objections in one frame", "Customer quotes add authentic social proof", "100% money back guarantee prominently shown — reduces purchase risk", "Feature list is comprehensive and clearly hierarchy is correct", "Body system icons are a nice touch for benefit clarity", "Warm ingredient background creates appetite appeal"],
|
||||
"weaknesses": ["Slightly text-heavy — small screens may render the bullet points unreadable", "The two customer quotes don't reference pain relief specifically — missed opportunity to anchor the #1 outcome", "Body system icons are very generic (gut, joint, brain) — could be more specific to joint/mobility outcome"]
|
||||
},
|
||||
{
|
||||
"index": 5,
|
||||
"url": "https://images.justvitamins.co.uk/product-images/Normal/3c4e9e20-5169-4a72-a016-a24622ba00fe-5.jpg",
|
||||
"role": "Science Differentiation",
|
||||
"description": "Dark background. Headline: 'Not a regular Turmeric powder'. Explains curcuminoids as active compounds. Bar chart: JustVitamins 95% vs turmeric powder up to 10%. 'Turmeric grown in SANGLI INDIA' with Indian map and farmer image. BioPerine section: 'increase bioavailability of Curcumin by up to 2,000%'. Fresh turmeric root and powder in frame.",
|
||||
"score": 4.4,
|
||||
"strengths": ["Best image in the set — directly addresses why educated buyers should pay premium over £3 supermarket turmeric", "95% vs 10% bar chart is a compelling, visual argument", "Sangli India provenance is a trust signal — sourcing transparency matters to supplement buyers", "2,000% bioavailability stat is memorable and differentiating", "Dark background gives this a scientific/premium feel", "Farmer image adds authentic supply chain story"],
|
||||
"weaknesses": ["Text is dense — may be difficult to read on mobile thumbnails", "The disclaimer footnote (*bioavailability of Curcumin when co-administered...) is present but very small", "Missing: no connection between the science and the customer outcome (joint pain relief)"]
|
||||
},
|
||||
{
|
||||
"index": 6,
|
||||
"url": "https://images.justvitamins.co.uk/product-images/Normal/3c4e9e20-5169-4a72-a016-a24622ba00fe-6.jpg",
|
||||
"role": "Brand Range + Trust Signals",
|
||||
"description": "Orange brand background. Four JustVitamins pouches shown side by side: Multivitamins A-Z, Vegan Vitamin D3, Turmeric with BioPerine, Rosehip. Six brand badges: UK Made, Vegan Friendly, Plastic Free, Superb Customer Service, Max Strength, Premium Grade Ingredients. Footer: 'No questions asked 100% money back guarantee'.",
|
||||
"score": 2.5,
|
||||
"strengths": ["UK Made badge is a strong differentiator in the supplement market", "Brand badge row is visually clean and covers key purchase criteria", "Money back guarantee reinforces image 4 — consistent messaging"],
|
||||
"weaknesses": ["This is a brand image, not a product image — it dilutes product focus", "Showing competitor products (Multivitamins, D3, Rosehip) within the Turmeric PDP is distracting", "Weakest image in the set for conversion — a buyer researching turmeric doesn't need to see D3 and Rosehip", "Should be replaced by a lifestyle image of the target customer (55-70, active, comfortable movement) which is the single biggest gap in this image set"]
|
||||
},
|
||||
{
|
||||
"index": 7,
|
||||
"url": "https://images.justvitamins.co.uk/product-images/Normal/3c4e9e20-5169-4a72-a016-a24622ba00fe-7.jpg",
|
||||
"role": "Feefo Social Proof",
|
||||
"description": "Close-up macro shot of turmeric powder being scooped with warm golden background. Bold text: '5* Rated, 21,000+ independent feefo reviews'. Feefo badge: 22,267 reviews, 5-star service and product rating. Just Vitamins logo.",
|
||||
"score": 4.3,
|
||||
"strengths": ["21,000+ reviews is a massive trust signal — more than most category competitors", "Feefo (independent verified) is more credible than Amazon star ratings", "Warm macro turmeric photography is visually beautiful — best aesthetic in the set", "Clean, bold typography — readable even at thumbnail size", "Answers the 'is this legit?' question definitively"],
|
||||
"weaknesses": ["Could reference the number of turmeric-specific reviews rather than overall brand reviews for more product relevance", "No specific customer quote about joint pain — missed opportunity to anchor trust to the #1 outcome"]
|
||||
}
|
||||
],
|
||||
"axes": {
|
||||
"designQuality": {
|
||||
"score": 3.0,
|
||||
"score": 3.5,
|
||||
"outOf": 5,
|
||||
"label": "Needs work",
|
||||
"notes": "Clean studio shot with good focus and lighting. The white-on-white background is safe but flat. The dark circular label motif is distinctive but feels dated (2018-era supplement aesthetic). No depth, shadow styling, or textural interest. Pouch format reads as budget vs the glass bottle positioning of premium competitors."
|
||||
"label": "Lookin okay",
|
||||
"notes": "Images 5 and 7 are genuinely strong design work with professional photography and clear visual hierarchy. Images 4 is well-composed. Image 2 (ruler shot) and Image 6 (brand range) drag the average down — both feel like afterthoughts. Overall set is more functional than aspirational."
|
||||
},
|
||||
"perceivedValue": {
|
||||
"score": 3.2,
|
||||
"outOf": 5,
|
||||
"label": "Lookin okay",
|
||||
"notes": "Image 5's science story and Image 7's 21,000+ Feefo reviews significantly raise perceived value vs a plain product shot. However the pouch format (Image 1) and clinical ruler (Image 2) undercut a premium read. Premium competitors (Wild Nutrition, Ancient+Brave) use dark glass and lifestyle photography that reads as worth more at the same price point."
|
||||
},
|
||||
"messageClarity": {
|
||||
"score": 4.2,
|
||||
"outOf": 5,
|
||||
"label": "Lookin okay",
|
||||
"notes": "Across 6 images the buyer now understands: what the product is, how big the tablets are, what makes the formula premium (95% curcuminoids, BioPerine, Indian sourcing), brand credentials, and social proof. The set answers most 'what' questions. What it does not answer is 'how will my life improve?' — the outcome message is absent."
|
||||
},
|
||||
"messageStrength": {
|
||||
"score": 2.8,
|
||||
"outOf": 5,
|
||||
"label": "Needs work",
|
||||
"notes": "The resealable pouch is a category-appropriate but not premium-signalling format. Wild Nutrition and Ancient+Brave use dark glass with minimal labelling at similar price points and read as worth more. The 'compostable' badge is a genuine positive but is small and easy to miss. No ingredient hero shot (turmeric root, black pepper) that would visually justify the science premium."
|
||||
},
|
||||
"messageClarity": {
|
||||
"score": 4.0,
|
||||
"outOf": 5,
|
||||
"label": "Lookin okay",
|
||||
"notes": "Strong on factual clarity. Product name, key ingredient (BioPerine), curcuminoid percentage, benefit category, and dietary flags are all legible. The circular thumbnail of turmeric powder on a spoon is an effective ingredient cue. Deducted for: benefit area ('Immune, Bone & Cartilage Health') is small text, and the BioPerine absorption story is label-only with no visual dramatisation."
|
||||
},
|
||||
"messageStrength": {
|
||||
"score": 2.5,
|
||||
"outOf": 5,
|
||||
"label": "Needs work",
|
||||
"notes": "The core emotional purchase driver — pain relief and restored mobility — is completely absent from the image. '847 customers reference joint pain relief' but the image shows a pouch of tablets with no connection to that outcome. No person, no motion, no before/after implication. The image answers 'what is this?' but not 'why do I need this?' or 'how will my life improve?'. This is the biggest single gap."
|
||||
"notes": "This is the critical gap across the entire set. 847 reviews mention joint pain relief as the core purchase driver. Not a single image shows a person moving freely, a before/after implication, or any emotional connection to the outcome. The science and social proof are well-communicated but the emotional reason to buy — restored mobility, less pain, better quality of life — is invisible. This is the #1 priority fix."
|
||||
}
|
||||
},
|
||||
"setAnalysis": {
|
||||
"coverage": {
|
||||
"productIdentity": true,
|
||||
"tabletSizeReference": true,
|
||||
"socialProof": true,
|
||||
"scienceDifferentiation": true,
|
||||
"brandTrust": true,
|
||||
"outcomeLifestyle": false,
|
||||
"timelineExpectation": false,
|
||||
"ingredientHeroShot": false
|
||||
},
|
||||
"criticalGap": "No lifestyle image exists across all 6 images. The #1 purchase driver — joint pain relief and restored mobility — is completely unaddressed visually. This is the single highest-ROI addition to this image set.",
|
||||
"imageToReplace": "Image 6 (brand range shot) should be replaced — it is the weakest converting image and directly removes the Turmeric product from the centre of attention. Replace with a lifestyle shot of target customer (55-70) in comfortable, active movement.",
|
||||
"imageToImprove": "Image 2 (ruler shot) serves a valid purpose (size anxiety is real based on reviews) but needs a warmer execution. Replace the metal ruler and white background with an in-hand shot: tablet in an older adult's open palm, natural lighting, showing the manageable scale without the clinical feel."
|
||||
},
|
||||
"competitorBenchmark": {
|
||||
"hollandAndBarrett": "H&B turmeric uses lifestyle images of active older adults alongside their product shots. Their PDP includes a turmeric root ingredient flat-lay. Score advantage: +0.6 on messageStrength.",
|
||||
"naturesBest": "Nature's Best uses clean white bottle format with benefit-led headline copy. More premium read on perceivedValue. Score advantage: +0.5 on perceivedValue.",
|
||||
"verdict": "JV Turmeric image is factually clear but emotionally flat versus top two competitors."
|
||||
"hollandAndBarrett": "H&B's turmeric PDP includes a lifestyle image of an older adult alongside their product shots. Their feature image uses warm amber tones throughout. Advantage: +0.7 on messageStrength vs JV's current set.",
|
||||
"naturesBest": "Nature's Best shows clean bottle format with benefit-led headline copy and a lifestyle shot. More premium read on perceivedValue. Advantage: +0.5 on perceivedValue.",
|
||||
"wildNutrition": "Wild Nutrition uses dark studio photography with moody lighting, glass packaging, and minimal copy — reads as the most premium in category. Advantage: +0.8 on perceivedValue.",
|
||||
"verdict": "JV's science and social proof images (5, 7) are category-leading. The gap is entirely in lifestyle photography. Competitors lead on emotional connection to the outcome, not on factual differentiation."
|
||||
},
|
||||
"priorityFixes": [
|
||||
"Add lifestyle image: older adult (55-70) in active, comfortable movement — gardening, walking, climbing stairs",
|
||||
"Add ingredient hero shot: fresh turmeric root + black peppercorns on natural surface (linen/slate)",
|
||||
"Add '95% Curcuminoids + BioPerine' infographic panel explaining absorption advantage",
|
||||
"Add social proof badge overlay: '2,000+ Five Star Reviews'"
|
||||
"Add lifestyle image replacing Image 6: person aged 55-70 in comfortable, active movement — gardening, walking, climbing stairs. Implied mobility and ease. Warm natural lighting. This addresses the #1 purchase driver (847 reviews on joint relief) and fills the single biggest gap in the set.",
|
||||
"Improve Image 2: replace cold ruler/white background with in-hand shot — tablet in open palm of an older adult. Same size-reference function, warmer execution. Directly addresses the capsule size anxiety mentioned in reviews.",
|
||||
"Add timeline image (7th slot): '6-8 weeks: feel the difference' timeline infographic. 318 reviews mention onset time. Setting this expectation in the image set converts 'it's not working yet' abandoners into completers.",
|
||||
"Update Image 4 quotes to include a joint pain reference: replace one generic quote with a specific outcome quote ('I can walk without pain for the first time in years'). The current quotes are positive but generic.",
|
||||
"Consider Image 5 mobile optimisation: the science content is excellent but text-dense. A mobile-first version with larger type and simpler layout would improve readability in thumbnail and mobile PDP views."
|
||||
]
|
||||
}
|
||||
|
||||
+173
-136
@@ -3,61 +3,95 @@
|
||||
<div class="view-label">Image Audit</div>
|
||||
<div class="view-header">
|
||||
<h1>Objective feedback on your images</h1>
|
||||
<p class="subtitle">Know exactly what's working and what needs improvement. Objective scores on how well your current images perform.</p>
|
||||
<p class="subtitle">Scores across all {{ data?.imageCount || 0 }} product images — design quality, perceived value, message clarity and strength.</p>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="loading">Loading…</div>
|
||||
<div v-else-if="!data" class="empty">No image audit data available for this product yet.</div>
|
||||
<template v-else>
|
||||
<div class="section-title">Image Audit Overview</div>
|
||||
|
||||
<!-- Score row -->
|
||||
<!-- Overall score row -->
|
||||
<div class="section-title">Overall Image Score</div>
|
||||
<div class="score-row">
|
||||
<!-- Main score card -->
|
||||
<div class="main-score-card">
|
||||
<div class="main-score-num">{{ data.overallScore?.value?.toFixed(1) }}</div>
|
||||
<div class="main-score-label">out of {{ data.overallScore?.outOf || 5 }}</div>
|
||||
<div class="main-score-verdict">"{{ data.overallScore?.verdict }}"</div>
|
||||
</div>
|
||||
|
||||
<!-- Sub-scores -->
|
||||
<div class="sub-scores-card">
|
||||
<div class="sub-score-row" v-for="axis in axisRows" :key="axis.key">
|
||||
<div class="sub-score-info">
|
||||
<span class="sub-score-name">{{ axis.label }}</span>
|
||||
<span class="sub-score-val">{{ axis.score?.toFixed(1) }} out of {{ axis.outOf || 5 }}</span>
|
||||
<span class="sub-score-val">{{ axis.score?.toFixed(1) }} / {{ axis.outOf || 5 }}</span>
|
||||
</div>
|
||||
<div class="sub-score-track">
|
||||
<div
|
||||
class="sub-score-fill"
|
||||
:class="scoreClass(axis.score)"
|
||||
:style="{ width: ((axis.score || 0) / (axis.outOf || 5) * 100) + '%' }"
|
||||
></div>
|
||||
<div class="sub-score-fill" :class="scoreClass(axis.score)" :style="{ width: ((axis.score||0)/(axis.outOf||5)*100)+'%' }"></div>
|
||||
</div>
|
||||
<div class="sub-score-verdict" :class="scoreClass(axis.score)">{{ axis.label_text }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Image gallery -->
|
||||
<div class="section-title" style="margin-top:24px">Product Images</div>
|
||||
<div class="image-gallery">
|
||||
<div v-for="(url, i) in galleryUrls" :key="i" class="gallery-item">
|
||||
<img
|
||||
:src="url"
|
||||
:alt="`Product image ${i + 1}`"
|
||||
class="gallery-img"
|
||||
@error="onImgError($event)"
|
||||
/>
|
||||
<div class="gallery-num">{{ i + 1 }}</div>
|
||||
<!-- Coverage map -->
|
||||
<div v-if="data.setAnalysis" class="section-title" style="margin-top:24px">Image Set Coverage</div>
|
||||
<div v-if="data.setAnalysis" class="coverage-row">
|
||||
<div v-for="(val, key) in data.setAnalysis.coverage" :key="key" class="coverage-badge" :class="val ? 'covered' : 'missing'">
|
||||
<span class="coverage-icon">{{ val ? '✓' : '✗' }}</span>
|
||||
<span class="coverage-label">{{ coverageLabel(key) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="data.setAnalysis?.criticalGap" class="gap-banner">
|
||||
<span class="gap-icon">⚠</span>
|
||||
<span>{{ data.setAnalysis.criticalGap }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Per-image gallery with scores -->
|
||||
<div class="section-title" style="margin-top:24px">
|
||||
Product Images <span class="count-badge">{{ galleryUrls.length }}</span>
|
||||
</div>
|
||||
<div class="image-grid">
|
||||
<div
|
||||
v-for="(url, i) in galleryUrls"
|
||||
:key="i"
|
||||
class="image-card"
|
||||
:class="{ active: selected === i }"
|
||||
@click="selected = selected === i ? null : i"
|
||||
>
|
||||
<div class="image-wrap">
|
||||
<img :src="url" :alt="`Image ${i+1}`" class="gallery-img" />
|
||||
<div class="image-index">{{ imageForUrl(url)?.index || i+1 }}</div>
|
||||
<div v-if="imageForUrl(url)" class="image-score-badge" :class="scoreClass(imageForUrl(url).score)">
|
||||
{{ imageForUrl(url).score?.toFixed(1) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-role">{{ imageForUrl(url)?.role || '—' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Image description -->
|
||||
<div v-if="data.imageDescription" class="image-desc-card">
|
||||
<div class="image-desc-label">Main Image Description</div>
|
||||
<p class="image-desc-text">{{ data.imageDescription }}</p>
|
||||
</div>
|
||||
<!-- Selected image detail -->
|
||||
<transition name="fade">
|
||||
<div v-if="selected !== null && selectedImage" class="image-detail-card">
|
||||
<div class="detail-header">
|
||||
<span class="detail-role">{{ selectedImage.role }}</span>
|
||||
<span class="detail-score" :class="scoreClass(selectedImage.score)">{{ selectedImage.score?.toFixed(1) }} / 5</span>
|
||||
</div>
|
||||
<p class="detail-desc">{{ selectedImage.description }}</p>
|
||||
<div class="detail-cols">
|
||||
<div class="detail-col">
|
||||
<div class="detail-col-title strength-title">Strengths</div>
|
||||
<ul class="detail-list">
|
||||
<li v-for="s in selectedImage.strengths" :key="s" class="strength-item">{{ s }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="detail-col">
|
||||
<div class="detail-col-title weakness-title">Weaknesses</div>
|
||||
<ul class="detail-list">
|
||||
<li v-for="w in selectedImage.weaknesses" :key="w" class="weakness-item">{{ w }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- Priority fixes -->
|
||||
<div class="section-title" style="margin-top:24px">Priority Fixes</div>
|
||||
@@ -71,10 +105,12 @@
|
||||
<!-- Competitor benchmark -->
|
||||
<div v-if="data.competitorBenchmark" class="benchmark-card">
|
||||
<div class="benchmark-title">Competitor Benchmark</div>
|
||||
<p v-if="data.competitorBenchmark.hollandAndBarrett" class="benchmark-row"><strong>H&B:</strong> {{ data.competitorBenchmark.hollandAndBarrett }}</p>
|
||||
<p v-if="data.competitorBenchmark.naturesBest" class="benchmark-row"><strong>Nature's Best:</strong> {{ data.competitorBenchmark.naturesBest }}</p>
|
||||
<p v-if="data.competitorBenchmark.verdict" class="benchmark-verdict">{{ data.competitorBenchmark.verdict }}</p>
|
||||
<p v-for="(val, key) in data.competitorBenchmark" :key="key" class="benchmark-row">
|
||||
<template v-if="key !== 'verdict'"><strong>{{ benchmarkLabel(key) }}:</strong> {{ val }}</template>
|
||||
<em v-else>{{ val }}</em>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -87,6 +123,7 @@ const route = useRoute()
|
||||
const data = ref(null)
|
||||
const loading = ref(false)
|
||||
const galleryUrls = ref([])
|
||||
const selected = ref(null)
|
||||
|
||||
const axisRows = computed(() => {
|
||||
const axes = data.value?.axes || {}
|
||||
@@ -98,6 +135,17 @@ const axisRows = computed(() => {
|
||||
]
|
||||
})
|
||||
|
||||
const selectedImage = computed(() => {
|
||||
if (selected.value === null) return null
|
||||
const url = galleryUrls.value[selected.value]
|
||||
return imageForUrl(url)
|
||||
})
|
||||
|
||||
function imageForUrl(url) {
|
||||
if (!url || !data.value?.images) return null
|
||||
return data.value.images.find(img => img.url === url) || null
|
||||
}
|
||||
|
||||
function scoreClass(v) {
|
||||
if (!v) return 'fair'
|
||||
if (v >= 4) return 'good'
|
||||
@@ -105,13 +153,29 @@ function scoreClass(v) {
|
||||
return 'fair'
|
||||
}
|
||||
|
||||
function onImgError(e) {
|
||||
e.target.closest('.gallery-item').style.display = 'none'
|
||||
function coverageLabel(key) {
|
||||
const map = {
|
||||
productIdentity: 'Product Identity',
|
||||
tabletSizeReference: 'Size Reference',
|
||||
socialProof: 'Social Proof',
|
||||
scienceDifferentiation: 'Science Story',
|
||||
brandTrust: 'Brand Trust',
|
||||
outcomeLifestyle: 'Outcome Lifestyle',
|
||||
timelineExpectation: 'Timeline Expectation',
|
||||
ingredientHeroShot: 'Ingredient Hero'
|
||||
}
|
||||
return map[key] || key
|
||||
}
|
||||
|
||||
function benchmarkLabel(key) {
|
||||
const map = { hollandAndBarrett: 'H&B', naturesBest: "Nature's Best", wildNutrition: 'Wild Nutrition' }
|
||||
return map[key] || key
|
||||
}
|
||||
|
||||
async function load(sku) {
|
||||
if (!sku) return
|
||||
loading.value = true
|
||||
selected.value = null
|
||||
try {
|
||||
const [auditRes, imgRes] = await Promise.all([
|
||||
fetch(`/api/intelligence/${sku}/image-audit`),
|
||||
@@ -131,39 +195,19 @@ watch(() => route.params.sku, load)
|
||||
<style scoped>
|
||||
.section-title { font-size: 18px; font-weight: 600; margin-bottom: 16px; }
|
||||
|
||||
.score-row {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
/* Score row */
|
||||
.score-row { display: flex; gap: 20px; margin-bottom: 20px; flex-wrap: wrap; align-items: stretch; }
|
||||
.main-score-card {
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 32px 36px;
|
||||
text-align: center;
|
||||
min-width: 180px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg);
|
||||
padding: 32px 36px; text-align: center; min-width: 180px;
|
||||
display: flex; flex-direction: column; justify-content: center;
|
||||
}
|
||||
.main-score-num { font-size: 56px; font-weight: 300; line-height: 1; color: var(--text-primary); }
|
||||
.main-score-label { font-size: 13px; color: var(--text-muted); margin-top: 4px; }
|
||||
.main-score-verdict { font-size: 11px; color: var(--text-muted); margin-top: 10px; font-style: italic; max-width: 160px; line-height: 1.4; }
|
||||
|
||||
.main-score-verdict { font-size: 11px; color: var(--text-muted); margin-top: 10px; font-style: italic; line-height: 1.4; }
|
||||
.sub-scores-card {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 20px 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
min-width: 260px;
|
||||
flex: 1; background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg);
|
||||
padding: 20px 24px; display: flex; flex-direction: column; gap: 16px; min-width: 260px;
|
||||
}
|
||||
.sub-score-row { display: flex; flex-direction: column; gap: 4px; }
|
||||
.sub-score-info { display: flex; justify-content: space-between; align-items: baseline; }
|
||||
@@ -179,92 +223,85 @@ watch(() => route.params.sku, load)
|
||||
.sub-score-verdict.okay { color: #f59e0b; }
|
||||
.sub-score-verdict.fair { color: var(--negative); }
|
||||
|
||||
/* Image gallery */
|
||||
.image-gallery {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 24px;
|
||||
/* Coverage */
|
||||
.coverage-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
|
||||
.coverage-badge {
|
||||
display: flex; align-items: center; gap: 5px;
|
||||
padding: 4px 12px; border-radius: 20px; font-size: 11px; font-weight: 500;
|
||||
}
|
||||
.gallery-item {
|
||||
position: relative;
|
||||
width: 140px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
}
|
||||
.gallery-img {
|
||||
width: 100%;
|
||||
height: 140px;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
.gallery-num {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: 6px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: rgba(0,0,0,0.45);
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.coverage-badge.covered { background: var(--positive-bg); color: var(--positive); }
|
||||
.coverage-badge.missing { background: var(--negative-bg); color: var(--negative); }
|
||||
.coverage-icon { font-size: 10px; }
|
||||
.gap-banner {
|
||||
display: flex; align-items: flex-start; gap: 10px;
|
||||
background: #fff7ed; border: 1px solid #fed7aa; border-radius: var(--radius-lg);
|
||||
padding: 12px 16px; font-size: 13px; color: #c2410c; margin-bottom: 24px; line-height: 1.5;
|
||||
}
|
||||
.gap-icon { font-size: 14px; flex-shrink: 0; margin-top: 1px; }
|
||||
|
||||
.image-desc-card {
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 18px 22px;
|
||||
margin-bottom: 24px;
|
||||
/* Image grid */
|
||||
.image-grid { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
|
||||
.image-card {
|
||||
width: 148px; border: 2px solid var(--border); border-radius: var(--radius-lg);
|
||||
overflow: hidden; background: #fff; cursor: pointer; transition: border-color 0.15s;
|
||||
}
|
||||
.image-desc-label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); margin-bottom: 8px; }
|
||||
.image-desc-text { font-size: 13px; color: var(--text-secondary); line-height: 1.6; margin: 0; }
|
||||
.image-card:hover { border-color: var(--accent); }
|
||||
.image-card.active { border-color: var(--accent); }
|
||||
.image-wrap { position: relative; }
|
||||
.gallery-img { width: 100%; height: 148px; object-fit: contain; display: block; padding: 8px; }
|
||||
.image-index {
|
||||
position: absolute; top: 6px; left: 6px; width: 20px; height: 20px;
|
||||
background: rgba(0,0,0,0.45); color: #fff; border-radius: 50%;
|
||||
font-size: 10px; font-weight: 600; display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.image-score-badge {
|
||||
position: absolute; top: 6px; right: 6px;
|
||||
padding: 2px 7px; border-radius: 10px; font-size: 11px; font-weight: 700;
|
||||
}
|
||||
.image-score-badge.good { background: var(--positive-bg); color: var(--positive); }
|
||||
.image-score-badge.okay { background: #fef9c3; color: #a16207; }
|
||||
.image-score-badge.fair { background: var(--negative-bg); color: var(--negative); }
|
||||
.image-role { font-size: 11px; color: var(--text-muted); padding: 6px 8px 8px; text-align: center; line-height: 1.3; }
|
||||
|
||||
.fixes-list {
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 8px 0;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.fix-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 14px;
|
||||
padding: 12px 22px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
/* Image detail */
|
||||
.image-detail-card {
|
||||
background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg);
|
||||
padding: 20px 24px; margin-bottom: 24px;
|
||||
}
|
||||
.detail-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
|
||||
.detail-role { font-size: 13px; font-weight: 600; color: var(--text-primary); }
|
||||
.detail-score { font-size: 14px; font-weight: 700; }
|
||||
.detail-score.good { color: var(--positive); }
|
||||
.detail-score.okay { color: #f59e0b; }
|
||||
.detail-score.fair { color: var(--negative); }
|
||||
.detail-desc { font-size: 12px; color: var(--text-secondary); line-height: 1.5; margin-bottom: 16px; }
|
||||
.detail-cols { display: flex; gap: 20px; }
|
||||
.detail-col { flex: 1; }
|
||||
.detail-col-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 8px; }
|
||||
.strength-title { color: var(--positive); }
|
||||
.weakness-title { color: var(--negative); }
|
||||
.detail-list { padding-left: 16px; }
|
||||
.detail-list li { font-size: 12px; line-height: 1.5; margin-bottom: 4px; }
|
||||
.strength-item { color: var(--text-secondary); }
|
||||
.weakness-item { color: var(--text-secondary); }
|
||||
|
||||
/* Fixes */
|
||||
.fixes-list { background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 8px 0; margin-bottom: 24px; }
|
||||
.fix-item { display: flex; align-items: flex-start; gap: 14px; padding: 12px 22px; border-bottom: 1px solid var(--border); }
|
||||
.fix-item:last-child { border-bottom: none; }
|
||||
.fix-num {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background: var(--accent-light);
|
||||
color: var(--accent);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
flex-shrink: 0;
|
||||
margin-top: 1px;
|
||||
width: 22px; height: 22px; background: var(--accent-light); color: var(--accent);
|
||||
border-radius: 50%; display: flex; align-items: center; justify-content: center;
|
||||
font-size: 11px; font-weight: 700; flex-shrink: 0; margin-top: 1px;
|
||||
}
|
||||
.fix-text { font-size: 13px; color: var(--text-primary); line-height: 1.5; }
|
||||
|
||||
.benchmark-card {
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 18px 22px;
|
||||
}
|
||||
/* Benchmark */
|
||||
.benchmark-card { background: #fff; border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 18px 22px; }
|
||||
.benchmark-title { font-size: 13px; font-weight: 600; margin-bottom: 10px; }
|
||||
.benchmark-row { font-size: 12px; color: var(--text-secondary); line-height: 1.5; margin: 0 0 6px; }
|
||||
.benchmark-verdict { font-size: 12px; color: var(--text-muted); font-style: italic; margin: 8px 0 0; }
|
||||
|
||||
/* Fade transition */
|
||||
.fade-enter-active, .fade-leave-active { transition: opacity 0.2s; }
|
||||
.fade-enter-from, .fade-leave-to { opacity: 0; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user