perf(images): enable Next.js image optimization with fast internal docker proxy routing and clean button text SSR
Some checks failed
Deploy Canina / deploy (push) Successful in 2m2s
E2E Playwright Tests / Run Full E2E & Security Suites (push) Failing after 7s

This commit is contained in:
parsa aghaei 2026-09-02 15:27:41 +03:30
parent 0ff9a6b886
commit e081441c22
2 changed files with 16 additions and 19 deletions

View File

@ -147,7 +147,7 @@ export default function Hero({ banners = [], onShopNavigate }: { banners?: Banne
}} }}
className="bg-canina-blue text-white px-3 sm:px-10 py-2 sm:py-5 rounded-full font-bold text-lg hover:bg-canina-blue/90 hover:shadow-2xl focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all flex items-center gap-2 group font-vazir min-h-[58px] shadow-lg shadow-canina-blue/20 cursor-pointer" className="bg-canina-blue text-white px-3 sm:px-10 py-2 sm:py-5 rounded-full font-bold text-lg hover:bg-canina-blue/90 hover:shadow-2xl focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all flex items-center gap-2 group font-vazir min-h-[58px] shadow-lg shadow-canina-blue/20 cursor-pointer"
> >
{mounted && isInitialized ? getText('hero_btn_advisor', "دستیار سلامت پت") : "دستیار سلامت پت"} {getText('hero_btn_advisor', "دستیار سلامت پت")}
<ChevronLeft className="w-5 h-5 group-hover:-translate-x-1 transition-transform" /> <ChevronLeft className="w-5 h-5 group-hover:-translate-x-1 transition-transform" />
</button> </button>
<button <button
@ -160,7 +160,7 @@ export default function Hero({ banners = [], onShopNavigate }: { banners?: Banne
}} }}
className="bg-white border-2 border-medical-gray-900 text-medical-gray-900 px-3 sm:px-10 py-2 sm:py-5 rounded-full font-bold text-lg hover:bg-medical-gray-50 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all font-vazir min-h-[58px] shadow-sm cursor-pointer" className="bg-white border-2 border-medical-gray-900 text-medical-gray-900 px-3 sm:px-10 py-2 sm:py-5 rounded-full font-bold text-lg hover:bg-medical-gray-50 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all font-vazir min-h-[58px] shadow-sm cursor-pointer"
> >
{mounted && isInitialized ? getText('hero_btn_products', "مشاهده محصولات") : "مشاهده محصولات"} {getText('hero_btn_products', "مشاهده محصولات")}
</button> </button>
</div> </div>
</div> </div>
@ -198,7 +198,8 @@ export default function Hero({ banners = [], onShopNavigate }: { banners?: Banne
alt={banner.title || "Canina Pharma Germany"} alt={banner.title || "Canina Pharma Germany"}
fill fill
priority={idx === 0} priority={idx === 0}
unoptimized={true} sizes="(max-width: 1024px) 100vw, 600px"
quality={85}
className="object-cover" className="object-cover"
/> />
</motion.div> </motion.div>
@ -210,7 +211,8 @@ export default function Hero({ banners = [], onShopNavigate }: { banners?: Banne
alt={activeBanner?.title || "Canina Pharma Germany"} alt={activeBanner?.title || "Canina Pharma Germany"}
fill fill
priority priority
unoptimized={true} sizes="(max-width: 1024px) 100vw, 600px"
quality={85}
className="object-cover" className="object-cover"
/> />
</div> </div>

View File

@ -19,10 +19,10 @@ interface SafeImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElement>,
height?: number; height?: number;
} }
export default function SafeImage({ export default function SafeImage({
src: rawSrc, src: rawSrc,
alt = "تصویر مکمل کنینا", alt = "تصویر مکمل کنینا",
className, className,
imgClassName, imgClassName,
fallbackText = "در حال بروزرسانی تصویر...", fallbackText = "در حال بروزرسانی تصویر...",
priority = false, priority = false,
@ -92,9 +92,9 @@ export default function SafeImage({
<Sparkles className="w-6 h-6 text-canina-blue/20" /> <Sparkles className="w-6 h-6 text-canina-blue/20" />
</div> </div>
)} )}
<Image <Image
src={src} src={src}
alt={alt} alt={alt}
width={width} width={width}
height={height} height={height}
priority={priority} priority={priority}
@ -102,20 +102,15 @@ export default function SafeImage({
sizes={sizes} sizes={sizes}
quality={quality} quality={quality}
unoptimized={ unoptimized={
isBlobOrData || isBlobOrData ||
!isAllowedHost || !isAllowedHost ||
(typeof src === 'string' && ( (typeof src === 'string' && src.toLowerCase().endsWith('.svg'))
src.startsWith('/api/uploads/') ||
src.startsWith('/api/media/') ||
src.startsWith('/uploads/') ||
src.toLowerCase().endsWith('.svg')
))
} }
className={cn( className={cn(
"transition-all duration-500 w-full h-full object-contain", "transition-all duration-500 w-full h-full object-contain",
imgClassName, imgClassName,
loading && !priority ? "opacity-0 scale-105" : "opacity-100 scale-100" loading && !priority ? "opacity-0 scale-105" : "opacity-100 scale-100"
)} )}
onLoad={() => setLoading(false)} onLoad={() => setLoading(false)}
onError={() => setError(true)} onError={() => setError(true)}
/> />