89 lines
3.1 KiB
TypeScript
89 lines
3.1 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import type { ImageAltData } from "@/app/actions";
|
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { ImageOff } from "lucide-react";
|
|
|
|
interface ImageAltDisplayProps {
|
|
images: ImageAltData[];
|
|
}
|
|
|
|
export function ImageAltDisplay({ images }: ImageAltDisplayProps) {
|
|
const [showMissingOnly, setShowMissingOnly] = useState(false);
|
|
const [imageErrors, setImageErrors] = useState<Record<string, boolean>>({});
|
|
|
|
const missingAltCount = images.filter((img) => !img.alt).length;
|
|
|
|
const filteredImages = showMissingOnly
|
|
? images.filter((img) => !img.alt)
|
|
: images;
|
|
|
|
const handleImageError = (src: string) => {
|
|
setImageErrors((prev) => ({ ...prev, [src]: true }));
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 p-4 bg-muted/50 rounded-lg">
|
|
<div className="flex items-center space-x-2">
|
|
<Checkbox
|
|
id="missing-alt"
|
|
checked={showMissingOnly}
|
|
onCheckedChange={(checked) => setShowMissingOnly(!!checked)}
|
|
/>
|
|
<Label htmlFor="missing-alt">Show only missing alt text</Label>
|
|
</div>
|
|
<div>
|
|
{missingAltCount > 0 ? (
|
|
<p className="text-sm font-medium text-destructive">
|
|
<span className="font-bold">{missingAltCount}</span> of{" "}
|
|
{images.length} images are missing alt text.
|
|
</p>
|
|
) : (
|
|
<p className="text-sm font-medium text-green-600">
|
|
Great! All {images.length} images have alt text.
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="space-y-4">
|
|
{filteredImages.map((image, index) => (
|
|
<div
|
|
key={index}
|
|
className="flex items-start gap-4 p-4 border rounded-lg"
|
|
>
|
|
<div className="w-24 h-24 flex-shrink-0 bg-muted rounded-md flex items-center justify-center overflow-hidden">
|
|
{imageErrors[image.src] ? (
|
|
<ImageOff className="h-8 w-8 text-muted-foreground" />
|
|
) : (
|
|
<img
|
|
src={image.src}
|
|
alt={image.alt || "Image preview"}
|
|
className="w-full h-full object-cover"
|
|
onError={() => handleImageError(image.src)}
|
|
/>
|
|
)}
|
|
</div>
|
|
<div className="flex-grow">
|
|
<p className="text-sm text-muted-foreground break-all">
|
|
{image.src}
|
|
</p>
|
|
<div className="mt-2">
|
|
{image.alt ? (
|
|
<p className="text-sm text-foreground bg-muted/50 p-2 rounded-md">
|
|
<span className="font-semibold">Alt:</span> {image.alt}
|
|
</p>
|
|
) : (
|
|
<Badge variant="destructive">Missing alt text</Badge>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
} |