Files
goodgo-platform/docs/audits/IMAGE_QUICK_REFERENCE.md
Ho Ngoc Hai b8512ebff4 docs: consolidate audit and analysis reports into docs/audits/
Move 36 root-level audit/analysis documents and 7 web app audit documents
into docs/audits/ directory to declutter the project root. Remove stale
EXPLORATION_SUMMARY.txt.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-11 01:37:50 +07:00

4.9 KiB

Image Usage - Quick Reference Card

🎯 At a Glance

Item Status Details
HTML <img> Tags 0 found All replaced with next/image
next/image Used 8 files Proper implementation across app
Image Components 3 specialized Gallery, Lightbox, Upload
Configuration Configured remotePatterns + CSP headers
Accessibility Full support Alt text, keyboard nav, ARIA
Security HTTPS only CSP configured, blob URLs for preview

📁 Where Images Are Used

Core Image Components

components/listings/image-gallery.tsx      ← Main gallery viewer
components/listings/image-lightbox.tsx     ← Fullscreen view
components/listings/image-upload.tsx       ← Upload with preview

Components That Display Images

components/search/property-card.tsx        → Thumbnail in search results
components/agents/agent-profile-client.tsx → Avatar + agent's listings
components/comparison/comparison-table.tsx → Comparison images
components/listings/listing-detail-client.tsx → Integrates ImageGallery

Page Components

app/[locale]/(public)/listings/[id]/page.tsx       → Listing detail (uses ImageGallery)
app/[locale]/(public)/search/page.tsx              → Search results (uses PropertyCard)
app/[locale]/(public)/agents/[id]/page.tsx         → Agent profile
app/[locale]/(dashboard)/listings/page.tsx         → Dashboard listings
app/[locale]/(dashboard)/listings/new/page.tsx     → Upload new listing

🔧 Configuration

next.config.js

images: {
  remotePatterns: [
    {
      protocol: 'https',
      hostname: '**',
    },
  ],
}

CSP Headers

img-src 'self' data: blob: https://*.mapbox.com https://*.tiles.mapbox.com https:
  • Allows blob: (file preview)
  • Allows data: (inline images)
  • Allows all HTTPS

📊 Image Component Details

ImageGallery

<ImageGallery 
  media={propertyMedia}  // PropertyMedia[]
  className="w-full"
/>

Features: Main + thumbnails, navigation, counter, lightbox integration

ImageLightbox

<ImageLightbox 
  images={images}
  initialIndex={0}
  open={isOpen}
  onClose={() => setIsOpen(false)}
/>

Features: Keyboard nav, swipe, preloading, focus trap

ImageUpload

<ImageUpload 
  images={uploadedImages}
  onChange={setUploadedImages}
  maxFiles={20}
/>

Features: Drag-drop, validation (JPEG/PNG/WebP), preview, cleanup


🎨 Image Data Types

interface PropertyMedia {
  id: string;
  url: string;              // Image URL
  type: 'image' | 'video';  // Media type
  order: number;            // Display order
  caption?: string;         // Optional caption
}

interface ImageFile {
  file: File;               // Browser File
  preview: string;          // blob: URL
}

Performance Features

Feature Status
Responsive sizing (sizes prop) Implemented
Priority loading for above-fold Implemented
Image preloading in lightbox Implemented
Blob URL cleanup (memory) Implemented
Skeleton placeholders ⚠️ Not implemented
Image compression on upload ⚠️ Not implemented

Accessibility Features

Feature Status
Alt text on images Vietnamese
ARIA labels Implemented
Keyboard navigation Arrow keys + Escape
Focus trap in modal Implemented
Tab trapping Implemented

🔒 Security Checklist

  • HTTPS-only remote patterns
  • CSP headers configured
  • blob: URLs only for client-side preview
  • ⚠️ API image URL validation - TO DO
  • ⚠️ User upload scanning - TO DO

📝 Common Tasks

Adding Images to a Component

import Image from 'next/image';

<Image
  src={imageUrl}
  alt="Descriptive text in Vietnamese"
  fill
  sizes="(max-width: 768px) 100vw, 50vw"
  className="object-cover"
/>
import { ImageGallery } from '@/components/listings/image-gallery';

<ImageGallery 
  media={property.media}
/>

File Upload

import { ImageUpload } from '@/components/listings/image-upload';

const [images, setImages] = useState<ImageFile[]>([]);

<ImageUpload 
  images={images}
  onChange={setImages}
  maxFiles={20}
/>

🚨 Important Notes

  1. Never use HTML <img> tags - Use next/image instead
  2. Exception: Blob URL preview in image-upload is OK
  3. Always provide alt text - Use Vietnamese text
  4. Use sizes prop - For responsive images
  5. Set priority - For above-fold images
  6. Revoke blob URLs - On unmount to prevent memory leaks
  7. Validate image URLs - At API layer before returning

📞 Questions?

See IMAGE_AUDIT_REPORT.md for complete details and recommendations.