perf: optimize font payloads, enable responsive image optimization for uploads, and set high priority on top hero/product images
This commit is contained in:
parent
00839017ff
commit
3aaea0062b
@ -15,6 +15,13 @@ function getCandidateUrls(filePath: string): string[] {
|
||||
set.add(`${base}/uploads/${filePath}`);
|
||||
}
|
||||
|
||||
// Production backend port and domains
|
||||
set.add(`http://127.0.0.1:4400/uploads/${filePath}`);
|
||||
set.add(`http://localhost:4400/uploads/${filePath}`);
|
||||
set.add(`http://127.0.0.1:4000/uploads/${filePath}`);
|
||||
set.add(`http://localhost:4000/uploads/${filePath}`);
|
||||
set.add(`https://api.canina.ir/uploads/${filePath}`);
|
||||
|
||||
set.add(`http://backend_prod:3000/uploads/${filePath}`);
|
||||
set.add(`http://canino_backend_prod:3000/uploads/${filePath}`);
|
||||
set.add(`http://backend:3000/uploads/${filePath}`);
|
||||
|
||||
@ -15,6 +15,13 @@ function getCandidateUrls(filePath: string): string[] {
|
||||
set.add(`${base}/uploads/${filePath}`);
|
||||
}
|
||||
|
||||
// Production backend port and domains
|
||||
set.add(`http://127.0.0.1:4400/uploads/${filePath}`);
|
||||
set.add(`http://localhost:4400/uploads/${filePath}`);
|
||||
set.add(`http://127.0.0.1:4000/uploads/${filePath}`);
|
||||
set.add(`http://localhost:4000/uploads/${filePath}`);
|
||||
set.add(`https://api.canina.ir/uploads/${filePath}`);
|
||||
|
||||
// Docker internal service aliases
|
||||
set.add(`http://backend_prod:3000/uploads/${filePath}`);
|
||||
set.add(`http://canino_backend_prod:3000/uploads/${filePath}`);
|
||||
|
||||
@ -13,9 +13,9 @@ import { useSettingsStore } from "../lib/store/settingsStore";
|
||||
import { useCartStore } from "../lib/store/cartStore";
|
||||
import { toast } from "sonner";
|
||||
|
||||
function ProductCard({ product }: { product: Product }) {
|
||||
function ProductCard({ product, priority = false }: { product: Product; priority?: boolean }) {
|
||||
const router = useRouter();
|
||||
const getText = useSettingsStore((state) => state.getText);
|
||||
const { getText, isInitialized } = useSettingsStore();
|
||||
const { getActivePet } = usePetStore();
|
||||
const activePet = getActivePet();
|
||||
const { addItem } = useCartStore();
|
||||
@ -36,17 +36,16 @@ function ProductCard({ product }: { product: Product }) {
|
||||
return { type: "neutral", text: `مناسب برای ${activePet.name || 'پت شما'}` };
|
||||
}, [product, activePet]);
|
||||
|
||||
const isCatalogOnly = getText('CATALOG_ONLY_MODE', 'false') === 'true';
|
||||
const showPrices = !isCatalogOnly || getText('CATALOG_SHOW_PRICES', 'true') === 'true';
|
||||
const allowCart = !isCatalogOnly || getText('CATALOG_ALLOW_CART', 'false') === 'true';
|
||||
const showPreorderBtn = isCatalogOnly && getText('CATALOG_PREORDER_BTN', 'false') === 'true';
|
||||
const isCatalogOnly = isInitialized ? getText('CATALOG_ONLY_MODE', 'false') === 'true' : false;
|
||||
const showPreorderBtn = isCatalogOnly && (isInitialized ? getText('CATALOG_PREORDER_BTN', 'false') === 'true' : false);
|
||||
const allowCart = !isCatalogOnly && (isInitialized ? getText('allow_cart_purchase', 'true') === 'true' : true);
|
||||
const showPrices = !isCatalogOnly || (isInitialized ? getText('show_product_prices', 'true') === 'true' : true);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
initial={{ opacity: 0, y: 15 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
whileHover={{ y: -4 }}
|
||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||
onClick={() => router.push(`/shop/${product.slug || product.id}`)}
|
||||
className="group bg-white/90 backdrop-blur-md rounded-2xl sm:rounded-3xl border border-medical-gray-200/80 overflow-hidden hover:shadow-xl hover:shadow-canina-blue/10 hover:border-canina-blue/30 transition-all duration-300 flex flex-col cursor-pointer relative"
|
||||
@ -72,6 +71,7 @@ function ProductCard({ product }: { product: Product }) {
|
||||
<SafeImage
|
||||
src={product.image}
|
||||
alt={nameFa}
|
||||
priority={priority}
|
||||
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 25vw)"
|
||||
className="w-full h-full group-hover:scale-105 transition-transform duration-500"
|
||||
imgClassName="object-contain w-full h-full max-h-[160px] sm:max-h-[200px] mx-auto"
|
||||
@ -193,7 +193,7 @@ export default function FeaturedProducts() {
|
||||
{isLoading ? (
|
||||
Array.from({ length: 4 }).map((_, i) => <ProductCardSkeleton key={i} />)
|
||||
) : products.length > 0 ? (
|
||||
products.map((p) => <ProductCard key={p.id} product={p} />)
|
||||
products.map((p, i) => <ProductCard key={p.id} product={p} priority={i < 2} />)
|
||||
) : (
|
||||
<div className="col-span-full py-16 px-8 flex flex-col items-center justify-center text-center bg-white rounded-[2.5rem] border border-medical-gray-200 shadow-xl max-w-lg mx-auto">
|
||||
<AlertCircle className="w-12 h-12 text-canina-teal mb-4" />
|
||||
|
||||
@ -198,7 +198,7 @@ export default function Hero({ banners = [], onShopNavigate }: { banners?: Banne
|
||||
alt={banner.title || "Canina Pharma Germany"}
|
||||
fill
|
||||
priority={idx === 0}
|
||||
unoptimized={true}
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 600px"
|
||||
className="object-cover"
|
||||
/>
|
||||
</motion.div>
|
||||
@ -210,7 +210,7 @@ export default function Hero({ banners = [], onShopNavigate }: { banners?: Banne
|
||||
alt={activeBanner?.title || "Canina Pharma Germany"}
|
||||
fill
|
||||
priority
|
||||
unoptimized={true}
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 600px"
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -104,12 +104,7 @@ export default function SafeImage({
|
||||
unoptimized={
|
||||
isBlobOrData ||
|
||||
!isAllowedHost ||
|
||||
(typeof src === 'string' && (
|
||||
src.startsWith('/api/uploads/') ||
|
||||
src.startsWith('/api/media/') ||
|
||||
src.startsWith('/uploads/') ||
|
||||
src.toLowerCase().endsWith('.svg')
|
||||
))
|
||||
(typeof src === 'string' && src.toLowerCase().endsWith('.svg'))
|
||||
}
|
||||
className={cn(
|
||||
"transition-all duration-500 w-full h-full object-contain",
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { Lalezar } from 'next/font/google';
|
||||
import localFont from 'next/font/local';
|
||||
|
||||
export const vazirmatn = localFont({
|
||||
@ -13,16 +14,10 @@ export const vazirmatn = localFont({
|
||||
preload: true,
|
||||
});
|
||||
|
||||
export const lalezar = localFont({
|
||||
src: [
|
||||
{
|
||||
path: '../public/fonts/Lalezar-Font-fontyab.com_/Lalezar-Font/TTF/Lalezar-Regular.ttf',
|
||||
weight: '400',
|
||||
style: 'normal',
|
||||
},
|
||||
],
|
||||
export const lalezar = Lalezar({
|
||||
weight: '400',
|
||||
subsets: ['arabic'],
|
||||
variable: '--font-lalezar',
|
||||
display: 'swap',
|
||||
fallback: ['cursive', 'sans-serif'],
|
||||
preload: false,
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user