canina/frontend/application/components/SafeImage.tsx
parsa aghaei 7615aa0e51 fix: resolve seed encoding, search param, and wiki data source issues
- Fix seed-products.ts TS error (implicit any) and BOM handling
- Re-run full seed to restore correct Persian encoding in DB
- Fix productService query→search param mismatch
- Fix IngredientWiki hardcoded port 4000→use settingsStore
- Restore seed-products-data.json from git after accidental corruption
2026-07-11 15:40:49 +03:30

75 lines
2.5 KiB
TypeScript

"use client";
import React from "react";
import { ImageOff, Sparkles } from "lucide-react";
import { cn } from "../lib/utils";
interface SafeImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {
src?: string;
alt?: string;
className?: string;
imgClassName?: string;
fallbackText?: string;
}
export default function SafeImage({
src,
alt,
className,
imgClassName,
fallbackText = "در حال بروزرسانی تصویر...",
...props
}: SafeImageProps) {
const [error, setError] = React.useState(false);
const [loading, setLoading] = React.useState(true);
if (!src || error) {
return (
<div className={cn(
"flex flex-col items-center justify-center bg-white border border-medical-gray-100 p-4 text-center min-h-[120px] rounded-3xl shadow-inner relative overflow-hidden",
className
)}>
{/* Subtle Brand Pattern Background */}
<div className="absolute inset-0 opacity-[0.03] pointer-events-none">
<div className="grid grid-cols-6 h-full w-full">
{Array.from({length: 24}).map((_, i) => (
<div key={i} className="border border-canina-blue" />
))}
</div>
</div>
<div className="w-16 h-16 bg-canina-blue rounded-2xl flex items-center justify-center text-white font-black text-3xl mb-4 shadow-xl shadow-canina-blue/20 italic transform -rotate-6">C</div>
<div className="relative">
<ImageOff className="w-5 h-5 text-medical-gray-200 mb-2" />
<Sparkles className="w-3 h-3 text-canina-blue absolute -top-1 -right-1 animate-pulse" />
</div>
<span className="text-[9px] font-black text-canina-blue/60 uppercase tracking-[0.2em] font-vazir leading-relaxed">
{fallbackText}
</span>
</div>
);
}
return (
<div className={cn("relative overflow-hidden group flex items-center justify-center", className)}>
{loading && (
<div className="absolute inset-0 bg-medical-gray-50 animate-pulse flex items-center justify-center z-10">
<Sparkles className="w-6 h-6 text-canina-blue/20" />
</div>
)}
<img
src={src}
alt={alt}
className={cn(
"transition-all duration-700 w-full h-full",
imgClassName,
loading ? "opacity-0 scale-110" : "opacity-100 scale-100"
)}
onLoad={() => setLoading(false)}
onError={() => setError(true)}
referrerPolicy="no-referrer"
{...props}
/>
</div>
);
}