Add favicon icons and update product/about pages
BIN
frontend/public/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
frontend/public/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
frontend/public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
frontend/public/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 497 B |
BIN
frontend/public/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
frontend/public/favicon-48x48.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
frontend/public/favicon-64x64.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 523 B |
@@ -2,7 +2,17 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<!-- Favicon for all browsers -->
|
||||
<link rel="icon" type="image/x-icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="%PUBLIC_URL%/favicon-16x16.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="%PUBLIC_URL%/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="48x48" href="%PUBLIC_URL%/favicon-48x48.png" />
|
||||
<link rel="icon" type="image/png" sizes="64x64" href="%PUBLIC_URL%/favicon-64x64.png" />
|
||||
<!-- Apple Touch Icon -->
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/apple-touch-icon.png" />
|
||||
<!-- Android Chrome Icons -->
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="%PUBLIC_URL%/android-chrome-192x192.png" />
|
||||
<link rel="icon" type="image/png" sizes="512x512" href="%PUBLIC_URL%/android-chrome-512x512.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
|
||||
26
frontend/public/manifest.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"short_name": "PromptTech",
|
||||
"name": "PromptTech Solutions",
|
||||
"description": "Your trusted destination for premium electronics and professional repair services",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "16x16 32x32 48x48",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "android-chrome-192x192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "android-chrome-512x512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import { useCart } from "../../context/CartContext";
|
||||
import { useAuth } from "../../context/AuthContext";
|
||||
import { toast } from "sonner";
|
||||
|
||||
const ProductCard = ({ product }) => {
|
||||
const ProductCard = ({ product, viewMode = "grid" }) => {
|
||||
const { addToCart } = useCart();
|
||||
const { isAuthenticated } = useAuth();
|
||||
|
||||
@@ -41,6 +41,96 @@ const ProductCard = ({ product }) => {
|
||||
return product.image_url;
|
||||
};
|
||||
|
||||
// List View Layout
|
||||
if (viewMode === "list") {
|
||||
return (
|
||||
<Link
|
||||
to={`/products/${product.id}`}
|
||||
className="group flex gap-4 overflow-hidden rounded-xl border border-border/50 bg-card hover:border-primary/50 transition-all duration-300 hover-lift card-hover-border p-4"
|
||||
data-testid={`product-card-${product.id}`}
|
||||
>
|
||||
{/* Image Container - Fixed Size */}
|
||||
<div className="relative w-32 h-32 flex-shrink-0 overflow-hidden bg-muted rounded-lg">
|
||||
<img
|
||||
src={getImageUrl()}
|
||||
alt={product.name}
|
||||
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
|
||||
loading="lazy"
|
||||
/>
|
||||
{/* Stock Badge */}
|
||||
{product.stock <= 5 && product.stock > 0 && (
|
||||
<Badge className="absolute top-2 left-2 bg-orange-500 hover:bg-orange-600 text-xs">
|
||||
{product.stock} left
|
||||
</Badge>
|
||||
)}
|
||||
{product.stock === 0 && (
|
||||
<Badge variant="destructive" className="absolute top-2 left-2 text-xs">
|
||||
Out of Stock
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Content - Flex Container */}
|
||||
<div className="flex-1 flex flex-col justify-between min-w-0">
|
||||
<div className="space-y-2">
|
||||
{/* Category & Brand */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="secondary" className="text-xs capitalize">
|
||||
{product.category}
|
||||
</Badge>
|
||||
{product.brand && (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{product.brand}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h3 className="font-semibold text-base leading-tight line-clamp-1 group-hover:text-primary transition-colors font-['Outfit']">
|
||||
{product.name}
|
||||
</h3>
|
||||
|
||||
{/* Description if available */}
|
||||
{product.description && (
|
||||
<p className="text-sm text-muted-foreground line-clamp-2">
|
||||
{product.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Price & Actions */}
|
||||
<div className="flex items-center justify-between pt-2">
|
||||
<span className="text-xl font-bold font-['Outfit']">
|
||||
${product.price.toFixed(2)}
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="rounded-full"
|
||||
data-testid={`view-product-${product.id}`}
|
||||
>
|
||||
<Eye className="h-4 w-4 mr-1" />
|
||||
View
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
className="rounded-full px-4 btn-press"
|
||||
onClick={handleAddToCart}
|
||||
disabled={product.stock === 0}
|
||||
data-testid={`add-to-cart-${product.id}`}
|
||||
>
|
||||
<ShoppingCart className="h-4 w-4 mr-1" />
|
||||
Add to Cart
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
// Grid View Layout (Default)
|
||||
return (
|
||||
<Link
|
||||
to={`/products/${product.id}`}
|
||||
|
||||
@@ -221,8 +221,11 @@ const About = () => {
|
||||
<div className="relative">
|
||||
<img
|
||||
src={
|
||||
content.hero?.image_url ||
|
||||
"/uploads/media/aa5bcc15-3b1e-4ed8-8708-1a3dceb9494d.jpg"
|
||||
content.hero?.image_url
|
||||
? content.hero.image_url.startsWith('/uploads')
|
||||
? `${process.env.REACT_APP_BACKEND_URL}${content.hero.image_url}`
|
||||
: content.hero.image_url
|
||||
: `${process.env.REACT_APP_BACKEND_URL}/uploads/media/aa5bcc15-3b1e-4ed8-8708-1a3dceb9494d.jpg`
|
||||
}
|
||||
alt="Tech Repair Services"
|
||||
className="rounded-2xl shadow-2xl w-full h-auto max-h-[500px] object-cover"
|
||||
@@ -603,8 +606,11 @@ const About = () => {
|
||||
<div className="relative mb-4 overflow-hidden rounded-2xl aspect-square">
|
||||
<img
|
||||
src={
|
||||
member.image_url ||
|
||||
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=400"
|
||||
member.image_url
|
||||
? member.image_url.startsWith('/uploads')
|
||||
? `${process.env.REACT_APP_BACKEND_URL}${member.image_url}`
|
||||
: member.image_url
|
||||
: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=400"
|
||||
}
|
||||
alt={member.name}
|
||||
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
|
||||
|
||||
@@ -388,7 +388,7 @@ const Products = () => {
|
||||
}`}
|
||||
>
|
||||
{filteredProducts.map((product) => (
|
||||
<ProductCard key={product.id} product={product} />
|
||||
<ProductCard key={product.id} product={product} viewMode={viewMode} />
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
|
||||