added new ui for mobile phones
This commit is contained in:
@@ -140,6 +140,7 @@ export default {
|
||||
photo: "Photo",
|
||||
brand: "Brand",
|
||||
code: "Code",
|
||||
channel: "Store",
|
||||
quantity: "Quantity",
|
||||
price: "Price",
|
||||
total: "Total",
|
||||
|
||||
@@ -137,6 +137,7 @@ export default {
|
||||
photo: "Фото",
|
||||
brand: "Бренд",
|
||||
code: "Код",
|
||||
channel: "Магазин",
|
||||
quantity: "Количество",
|
||||
price: "Цена",
|
||||
total: "Итого",
|
||||
|
||||
@@ -141,6 +141,7 @@ export default {
|
||||
brand: "Brend",
|
||||
code: "Kody",
|
||||
quantity: "Sany",
|
||||
channel: "Magazin",
|
||||
price: "Bahasy",
|
||||
total: "Jemi",
|
||||
orderNumber: "Sargyt belgisi",
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
|
||||
.mobilePhoneGrid {
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
// Price Filter Styles
|
||||
.priceFilterContainer {
|
||||
display: flex;
|
||||
|
||||
324
src/pages/Category/components/Mobilephonecard.jsx
Normal file
324
src/pages/Category/components/Mobilephonecard.jsx
Normal file
@@ -0,0 +1,324 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Modal } from "antd";
|
||||
import { IoMdHeartEmpty, IoMdHeart } from "react-icons/io";
|
||||
import { FaShoppingCart } from "react-icons/fa";
|
||||
import { DecreaseIcon, IncreaseIcon } from "../../../components/Icons";
|
||||
import {
|
||||
useAddFavoriteMutation,
|
||||
useRemoveFavoriteMutation,
|
||||
useGetFavoritesQuery,
|
||||
} from "../../../app/api/favoritesApi";
|
||||
import {
|
||||
useAddToCartMutation,
|
||||
useUpdateCartItemMutation,
|
||||
useRemoveFromCartMutation,
|
||||
} from "../../../app/api/cartApi";
|
||||
import { useCart } from "../../../app/api/useCart";
|
||||
import styles from "./MobilePhoneCard.module.scss";
|
||||
|
||||
/**
|
||||
* Parses product.description HTML into spec pairs.
|
||||
* Format inside HTML: "Label1: Value1; Label2: Value2; ..."
|
||||
*/
|
||||
const parseSpecs = (htmlString) => {
|
||||
if (!htmlString) return [];
|
||||
const div = document.createElement("div");
|
||||
div.innerHTML = htmlString;
|
||||
|
||||
let processedHtml = htmlString
|
||||
.replace(/<br\s*\/?>/gi, "\n")
|
||||
.replace(/<\/div>/gi, "\n")
|
||||
.replace(/<\/strong>/gi, ": ");
|
||||
|
||||
div.innerHTML = processedHtml;
|
||||
const text = (div.textContent || div.innerText || "").trim();
|
||||
|
||||
return text
|
||||
.split(/[;\n]+/)
|
||||
.map((chunk) => chunk.trim())
|
||||
.filter(Boolean)
|
||||
.map((chunk) => {
|
||||
const separatorIdx = chunk.search(/[:|]/);
|
||||
if (separatorIdx === -1) return null;
|
||||
|
||||
return {
|
||||
label: chunk.slice(0, separatorIdx).trim(),
|
||||
value: chunk.slice(separatorIdx + 1).trim(),
|
||||
};
|
||||
})
|
||||
.filter((item) => item !== null && item.value !== "");
|
||||
};
|
||||
|
||||
const MobilePhoneCard = ({
|
||||
product,
|
||||
showAddToCart = true,
|
||||
showFavoriteButton = true,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const [stockErrorModalVisible, setStockErrorModalVisible] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
// ── Favorites ──────────────────────────────────────────────────────────────
|
||||
const [addFavorite] = useAddFavoriteMutation();
|
||||
const [removeFavorite] = useRemoveFavoriteMutation();
|
||||
const { data: favoriteProducts = [] } = useGetFavoritesQuery();
|
||||
const [localIsFavorite, setLocalIsFavorite] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (Array.isArray(favoriteProducts)) {
|
||||
setLocalIsFavorite(
|
||||
favoriteProducts.some((fav) => fav.product?.id === product.id)
|
||||
);
|
||||
}
|
||||
}, [favoriteProducts, product.id]);
|
||||
|
||||
// ── Cart ───────────────────────────────────────────────────────────────────
|
||||
const { getCartItem } = useCart();
|
||||
const [addToCart] = useAddToCartMutation();
|
||||
const [updateCartItem] = useUpdateCartItemMutation();
|
||||
const [removeFromCart] = useRemoveFromCartMutation();
|
||||
|
||||
const cartItem = getCartItem(product.id);
|
||||
const [localQuantity, setLocalQuantity] = useState(0);
|
||||
const [pendingQuantity, setPendingQuantity] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const qty = parseInt(
|
||||
cartItem?.quantity || cartItem?.product_quantity || 0,
|
||||
10
|
||||
);
|
||||
setLocalQuantity(qty);
|
||||
setPendingQuantity(qty);
|
||||
}, [cartItem]);
|
||||
|
||||
// Debounced sync to server
|
||||
useEffect(() => {
|
||||
const serverQty = parseInt(
|
||||
cartItem?.quantity || cartItem?.product_quantity || 0,
|
||||
10
|
||||
);
|
||||
if (pendingQuantity === serverQty || pendingQuantity <= 0) return;
|
||||
|
||||
const timer = setTimeout(async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await updateCartItem({
|
||||
productId: product.id,
|
||||
quantity: pendingQuantity,
|
||||
}).unwrap();
|
||||
} catch {
|
||||
setLocalQuantity(serverQty);
|
||||
setPendingQuantity(serverQty);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [pendingQuantity, cartItem, product.id, updateCartItem]);
|
||||
|
||||
// ── Handlers ───────────────────────────────────────────────────────────────
|
||||
const handleCardClick = () => navigate(`/product/${product.id}`);
|
||||
|
||||
const handleAddToCart = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (product.stock <= 0) {
|
||||
setStockErrorModalVisible(true);
|
||||
return;
|
||||
}
|
||||
setLocalQuantity((p) => p + 1);
|
||||
setPendingQuantity((p) => p + 1);
|
||||
try {
|
||||
await addToCart({ productId: product.id, quantity: 1 }).unwrap();
|
||||
} catch {
|
||||
setLocalQuantity((p) => p - 1);
|
||||
setPendingQuantity((p) => p - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleIncrease = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (isLoading) return;
|
||||
if (localQuantity >= product.stock) {
|
||||
setStockErrorModalVisible(true);
|
||||
return;
|
||||
}
|
||||
setLocalQuantity((p) => p + 1);
|
||||
setPendingQuantity((p) => p + 1);
|
||||
};
|
||||
|
||||
const handleDecrease = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (isLoading) return;
|
||||
if (pendingQuantity <= 1) {
|
||||
setLocalQuantity(0);
|
||||
setPendingQuantity(0);
|
||||
setIsLoading(true);
|
||||
removeFromCart({ productId: product.id })
|
||||
.unwrap()
|
||||
.catch(() => {
|
||||
setLocalQuantity(1);
|
||||
setPendingQuantity(1);
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
} else {
|
||||
setLocalQuantity((p) => p - 1);
|
||||
setPendingQuantity((p) => p - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggleFavorite = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (isLoading) return;
|
||||
setIsLoading(true);
|
||||
setLocalIsFavorite((prev) => !prev);
|
||||
try {
|
||||
if (localIsFavorite) await removeFavorite(product.id).unwrap();
|
||||
else await addFavorite(product.id).unwrap();
|
||||
} catch {
|
||||
setLocalIsFavorite((prev) => !prev);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// ── Derived ────────────────────────────────────────────────────────────────
|
||||
const specs = parseSpecs(product.description);
|
||||
const thumbnail =
|
||||
product.media?.[0]?.images_400x400 || product.media?.[0]?.thumbnail;
|
||||
const hasDiscount =
|
||||
product.old_price_amount &&
|
||||
parseFloat(product.old_price_amount) > parseFloat(product.price_amount);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.card} onClick={handleCardClick}>
|
||||
{/* Image */}
|
||||
<div className={styles.imageCol}>
|
||||
{product.stock === 0 && (
|
||||
<span className={styles.outOfStockBadge}>
|
||||
{t("common.out_of_stock")}
|
||||
</span>
|
||||
)}
|
||||
{thumbnail ? (
|
||||
<img src={thumbnail} alt={product.name} className={styles.image} />
|
||||
) : (
|
||||
<div className={styles.imagePlaceholder}>📱</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Info */}
|
||||
<div className={styles.infoCol}>
|
||||
<div className={styles.titleRow}>
|
||||
<span className={styles.name}>{product.name}</span>
|
||||
{product.brand?.name && (
|
||||
<span className={styles.brand}>{product.brand.name}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Dense spec paragraph — mirrors reference image */}
|
||||
{specs.length > 0 && (
|
||||
<p className={styles.specLine}>
|
||||
{specs.map((s, i) => (
|
||||
<span key={i}>
|
||||
<span className={styles.specLabel}>{s.label}: </span>
|
||||
<span className={styles.specValue}>{s.value}</span>
|
||||
{i < specs.length - 1 && "; "}
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Footer */}
|
||||
<div className={styles.footer} onClick={(e) => e.stopPropagation()}>
|
||||
<div className={styles.priceBlock}>
|
||||
<span className={styles.price}>{product.price_amount} m.</span>
|
||||
{hasDiscount && (
|
||||
<span className={styles.oldPrice}>
|
||||
{product.old_price_amount} m.
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.actions}>
|
||||
{showFavoriteButton && (
|
||||
<button
|
||||
className={`${styles.iconBtn} ${
|
||||
localIsFavorite ? styles.favActive : ""
|
||||
}`}
|
||||
onClick={handleToggleFavorite}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{localIsFavorite ? <IoMdHeart /> : <IoMdHeartEmpty />}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{showAddToCart &&
|
||||
(localQuantity > 0 ? (
|
||||
<div className={styles.quantityControls}>
|
||||
<button
|
||||
className={styles.qtyBtn}
|
||||
onClick={handleDecrease}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<DecreaseIcon />
|
||||
</button>
|
||||
<span className={styles.qtyValue}>{localQuantity}</span>
|
||||
<button
|
||||
className={styles.qtyBtn}
|
||||
onClick={handleIncrease}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<IncreaseIcon />
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
className={styles.cartBtn}
|
||||
onClick={handleAddToCart}
|
||||
disabled={isLoading || product.stock === 0}
|
||||
>
|
||||
<FaShoppingCart />
|
||||
<span>Sebede goş</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
title={t("common.warning")}
|
||||
open={stockErrorModalVisible}
|
||||
onOk={() => setStockErrorModalVisible(false)}
|
||||
onCancel={() => setStockErrorModalVisible(false)}
|
||||
footer={[
|
||||
<button
|
||||
key="ok"
|
||||
onClick={() => setStockErrorModalVisible(false)}
|
||||
className={styles.modalButton}
|
||||
>
|
||||
{t("common.ok")}
|
||||
</button>,
|
||||
]}
|
||||
>
|
||||
<p>
|
||||
{t("common.not_enough_stock", {
|
||||
available: product.stock,
|
||||
requested: localQuantity + 1,
|
||||
})}
|
||||
</p>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const MOBILE_PHONE_CATEGORY_ID = 531;
|
||||
export default MobilePhoneCard;
|
||||
270
src/pages/Category/components/Mobilephonecard.module.scss
Normal file
270
src/pages/Category/components/Mobilephonecard.module.scss
Normal file
@@ -0,0 +1,270 @@
|
||||
// MobilePhoneCard.module.scss
|
||||
// Mimics the dense spec-row layout from the reference image (e.g. DNS/Citilink product list)
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease;
|
||||
|
||||
&:first-child {
|
||||
border-top: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #f5f8ff;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Image column ─────────────────────────────────────────────
|
||||
.imageCol {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 250px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 250px;
|
||||
height: 260px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.imagePlaceholder {
|
||||
width: 110px;
|
||||
height: 130px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 48px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.outOfStockBadge {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: #999;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
padding: 2px 5px;
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// ── Info column ──────────────────────────────────────────────
|
||||
.infoCol {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.titleRow {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #0645ad;
|
||||
cursor: pointer;
|
||||
line-height: 1.3;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.brand {
|
||||
font-size: 11px;
|
||||
color: #888;
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// Dense spec paragraph — key selling point of this card
|
||||
.specLine {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
color: #444;
|
||||
line-height: 1.6;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.specLabel {
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.specValue {
|
||||
font-weight: 400;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
// ── Footer ───────────────────────────────────────────────────
|
||||
.footer {
|
||||
margin-top: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding-top: 6px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.priceBlock {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.price {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #c0392b;
|
||||
}
|
||||
|
||||
.oldPrice {
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
// ── Actions ──────────────────────────────────────────────────
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.iconBtn {
|
||||
background: none;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
width: 34px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
transition: color 0.15s, border-color 0.15s;
|
||||
|
||||
&:hover {
|
||||
color: #888;
|
||||
border-color: #888;
|
||||
}
|
||||
|
||||
&.favActive {
|
||||
color: #888;
|
||||
border-color: #888;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.cartBtn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
background: #d32824;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
white-space: nowrap;
|
||||
height: 36px;
|
||||
&:hover {
|
||||
background: #c0392b;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Quantity controls ────────────────────────────────────────
|
||||
.quantityControls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
border: 1px solid #d32824;
|
||||
background-color: #d32824;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
min-width: 160px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.qtyBtn {
|
||||
background: none;
|
||||
border: none;
|
||||
width: 32px;
|
||||
height: 34px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
transition: background 0.1s;
|
||||
|
||||
&:hover {
|
||||
background: #e86064;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.qtyValue {
|
||||
min-width: 28px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
padding: 0 4px;
|
||||
line-height: 34px;
|
||||
}
|
||||
|
||||
// ── Modal ────────────────────────────────────────────────────
|
||||
.modalButton {
|
||||
padding: 6px 20px;
|
||||
background: #e74c3c;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
background: #c0392b;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@ import CategoryBreadcrumbs from "./components/CategoryBreadcrumbs";
|
||||
import useCategoryData from "./hooks/useCategoryData";
|
||||
import useCategoryProducts from "./hooks/useCategoryProducts";
|
||||
|
||||
import MobilePhoneCard from "./components/Mobilephonecard";
|
||||
|
||||
const CategoryPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const { categoryId, collectionId, brandId } = useParams();
|
||||
@@ -35,6 +37,13 @@ const CategoryPage = () => {
|
||||
});
|
||||
|
||||
const [isFilterDrawerOpen, setIsFilterDrawerOpen] = useState(false);
|
||||
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => setWindowWidth(window.innerWidth);
|
||||
window.addEventListener("resize", handleResize);
|
||||
return () => window.removeEventListener("resize", handleResize);
|
||||
}, []);
|
||||
|
||||
const routeKey = useMemo(
|
||||
() => `${categoryId || "x"}-${collectionId || "x"}-${brandId || "x"}`,
|
||||
@@ -86,7 +95,10 @@ const CategoryPage = () => {
|
||||
maxPrice: pageState.maxPrice,
|
||||
searchQuery,
|
||||
});
|
||||
|
||||
const isMobilePhoneView =
|
||||
(Number(categoryId) === 531 ||
|
||||
Number(filterState.selectedFilterCategory) === 531) &&
|
||||
windowWidth >= 768;
|
||||
useEffect(() => {
|
||||
if (isInitialMount.current) {
|
||||
isInitialMount.current = false;
|
||||
@@ -298,12 +310,20 @@ const CategoryPage = () => {
|
||||
minPrice={pageState.minPrice}
|
||||
maxPrice={pageState.maxPrice}
|
||||
onMinPriceChange={(value) => {
|
||||
setPageState((prev) => ({ ...prev, minPrice: value, currentPage: 1 }));
|
||||
setPageState((prev) => ({
|
||||
...prev,
|
||||
minPrice: value,
|
||||
currentPage: 1,
|
||||
}));
|
||||
setAllProducts([]);
|
||||
setHasMore(true);
|
||||
}}
|
||||
onMaxPriceChange={(value) => {
|
||||
setPageState((prev) => ({ ...prev, maxPrice: value, currentPage: 1 }));
|
||||
setPageState((prev) => ({
|
||||
...prev,
|
||||
maxPrice: value,
|
||||
currentPage: 1,
|
||||
}));
|
||||
setAllProducts([]);
|
||||
setHasMore(true);
|
||||
}}
|
||||
@@ -317,10 +337,9 @@ const CategoryPage = () => {
|
||||
/>
|
||||
</Drawer>
|
||||
|
||||
|
||||
<div className={styles.Container}>
|
||||
<CategoryFilters
|
||||
className={styles.sidebar}
|
||||
className={styles.sidebar}
|
||||
filtersData={filtersData}
|
||||
selectedFilterCategory={filterState.selectedFilterCategory}
|
||||
selectedFilterBrand={filterState.selectedFilterBrand}
|
||||
@@ -329,12 +348,20 @@ const CategoryPage = () => {
|
||||
minPrice={pageState.minPrice}
|
||||
maxPrice={pageState.maxPrice}
|
||||
onMinPriceChange={(value) => {
|
||||
setPageState((prev) => ({ ...prev, minPrice: value, currentPage: 1 }));
|
||||
setPageState((prev) => ({
|
||||
...prev,
|
||||
minPrice: value,
|
||||
currentPage: 1,
|
||||
}));
|
||||
setAllProducts([]);
|
||||
setHasMore(true);
|
||||
}}
|
||||
onMaxPriceChange={(value) => {
|
||||
setPageState((prev) => ({ ...prev, maxPrice: value, currentPage: 1 }));
|
||||
setPageState((prev) => ({
|
||||
...prev,
|
||||
maxPrice: value,
|
||||
currentPage: 1,
|
||||
}));
|
||||
setAllProducts([]);
|
||||
setHasMore(true);
|
||||
}}
|
||||
@@ -363,16 +390,27 @@ const CategoryPage = () => {
|
||||
<Loader />
|
||||
</div>
|
||||
}
|
||||
className={styles.productGrid}
|
||||
className={`${styles.productGrid} ${
|
||||
isMobilePhoneView ? styles.mobilePhoneGrid : ""
|
||||
}`}
|
||||
>
|
||||
{filteredProducts.map((product) => (
|
||||
<ProductCard
|
||||
key={product.id}
|
||||
product={product}
|
||||
showFavoriteButton
|
||||
showAddToCart
|
||||
/>
|
||||
))}
|
||||
{filteredProducts.map((product) =>
|
||||
isMobilePhoneView ? (
|
||||
<MobilePhoneCard
|
||||
key={product.id}
|
||||
product={product}
|
||||
showFavoriteButton
|
||||
showAddToCart
|
||||
/>
|
||||
) : (
|
||||
<ProductCard
|
||||
key={product.id}
|
||||
product={product}
|
||||
showFavoriteButton
|
||||
showAddToCart
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</InfiniteScroll>
|
||||
) : (
|
||||
<div>{t("search.noResults")}</div>
|
||||
|
||||
@@ -56,7 +56,7 @@ const ProductPage = ({
|
||||
const { data: favoriteProducts = [] } = useGetFavoritesQuery();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [localIsFavorite, setLocalIsFavorite] = useState(
|
||||
favoriteProducts.some((fav) => fav.product?.id === product?.id),
|
||||
favoriteProducts.some((fav) => fav.product?.id === product?.id)
|
||||
);
|
||||
|
||||
const { getCartItem } = useCart();
|
||||
@@ -73,7 +73,7 @@ const ProductPage = ({
|
||||
useEffect(() => {
|
||||
const qty = parseInt(
|
||||
cartItem?.quantity || cartItem?.product_quantity || 0,
|
||||
10,
|
||||
10
|
||||
);
|
||||
setLocalQuantity(qty);
|
||||
setPendingQuantity(qty);
|
||||
@@ -83,7 +83,7 @@ const ProductPage = ({
|
||||
useEffect(() => {
|
||||
if (Array.isArray(favoriteProducts)) {
|
||||
const isFav = favoriteProducts.some(
|
||||
(fav) => fav.product?.id === product?.id,
|
||||
(fav) => fav.product?.id === product?.id
|
||||
);
|
||||
setLocalIsFavorite(isFav);
|
||||
}
|
||||
@@ -180,7 +180,7 @@ const ProductPage = ({
|
||||
useEffect(() => {
|
||||
const serverQty = parseInt(
|
||||
cartItem?.quantity || cartItem?.product_quantity || 0,
|
||||
10,
|
||||
10
|
||||
);
|
||||
|
||||
// Sadece miktar değiştiyse ve 0'dan büyükse güncelle (0 ise Remove triggerlanır)
|
||||
@@ -278,6 +278,15 @@ const ProductPage = ({
|
||||
<span className={styles.metaValue}>{product.brand.name}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{product.channel?.[0]?.name && (
|
||||
<div className={styles.metaItem}>
|
||||
<span className={styles.metaLabel}>{t("order.channel")}</span>
|
||||
<span className={styles.metaValue}>
|
||||
{product.channel[0].name}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.productActions}>
|
||||
<div className={styles.priceContainer}>
|
||||
|
||||
Reference in New Issue
Block a user