-
-
- Screen
-
-
- {product.screen}
-
-
-
-
- Capacity
-
-
- {product.capacity}
-
-
-
-
- RAM
-
-
- {product.ram}
-
-
+ {renderSpecs(compareSpecs)}
{isClient && isInCart ?
diff --git a/src/components/animate-ui/icons/x.tsx b/src/components/animate-ui/icons/x.tsx
new file mode 100644
index 0000000..2bfb466
--- /dev/null
+++ b/src/components/animate-ui/icons/x.tsx
@@ -0,0 +1,126 @@
+'use client';
+
+import * as React from 'react';
+import { motion, type Variants } from 'motion/react';
+
+import {
+ getVariants,
+ useAnimateIconContext,
+ IconWrapper,
+ type IconProps,
+} from '@/components/animate-ui/icons/icon';
+
+type XProps = IconProps;
+
+const animations = {
+ default: {
+ line1: {
+ initial: {
+ rotate: 0,
+ transition: { ease: 'easeInOut', duration: 0.4 },
+ },
+ animate: {
+ rotate: 90,
+ transition: { ease: 'easeInOut', duration: 0.4 },
+ },
+ },
+ line2: {
+ initial: {
+ rotate: 0,
+ transition: { ease: 'easeInOut', duration: 0.4, delay: 0.1 },
+ },
+ animate: {
+ rotate: 90,
+ transition: { ease: 'easeInOut', duration: 0.4, delay: 0.1 },
+ },
+ },
+ } satisfies Record,
+ plus: {
+ line1: {
+ initial: {
+ rotate: 0,
+ x1: 6,
+ y1: 18,
+ x2: 18,
+ y2: 6,
+ transition: { ease: 'easeInOut', duration: 0.3, delay: 0.1 },
+ },
+ animate: {
+ rotate: 45,
+ x1: 7.1,
+ y1: 16.9,
+ x2: 16.9,
+ y2: 7.1,
+ transition: { ease: 'easeInOut', duration: 0.3, delay: 0.1 },
+ },
+ },
+ line2: {
+ initial: {
+ rotate: 0,
+ x1: 6,
+ y1: 6,
+ x2: 18,
+ y2: 18,
+ transition: { ease: 'easeInOut', duration: 0.3 },
+ },
+ animate: {
+ rotate: 45,
+ x1: 7.1,
+ y1: 7.1,
+ x2: 16.9,
+ y2: 16.9,
+ transition: { ease: 'easeInOut', duration: 0.3 },
+ },
+ },
+ } satisfies Record,
+} as const;
+
+function IconComponent({ size, ...props }: XProps) {
+ const { controls } = useAnimateIconContext();
+ const variants = getVariants(animations);
+
+ return (
+
+
+
+
+ );
+}
+
+function X(props: XProps) {
+ return (
+
+ );
+}
+
+export { animations, X, X as XIcon, type XProps, type XProps as XIconProps };
diff --git a/src/components/pages/ProductDetails/ProductDetails.tsx b/src/components/pages/ProductDetails/ProductDetails.tsx
index b8da754..86fd904 100644
--- a/src/components/pages/ProductDetails/ProductDetails.tsx
+++ b/src/components/pages/ProductDetails/ProductDetails.tsx
@@ -66,7 +66,7 @@ const ProductDetails = ({
) => {
- const product = action.payload;
-
+ removeFromCompareByKey: (
+ state,
+ action: PayloadAction<{
+ id: string | number;
+ color: string;
+ capacity: string;
+ }>,
+ ) => {
+ const { id, color, capacity } = action.payload;
state.items = state.items.filter(
(i) =>
- !(
- i.id === product.id &&
- i.color === product.color &&
- i.capacity === product.capacity
- ),
+ !(i.itemId === id && i.color === color && i.capacity === capacity),
);
},
@@ -51,7 +53,7 @@ const compareSlice = createSlice({
export const {
addToCompare,
- removeFromCompare,
+ removeFromCompareByKey,
clearCompare,
clearCompareByCategory,
} = compareSlice.actions;
diff --git a/src/lib/store.ts b/src/lib/store.ts
index 00f78ff..574ca09 100644
--- a/src/lib/store.ts
+++ b/src/lib/store.ts
@@ -13,15 +13,13 @@ import { CategoryName } from '@/types/CategoryName';
const persistedReducers = combineReducers({
favourites: favouritesReducer,
cart: cartReducer,
-
- // 👇 додаємо сюди compare, якщо хочеш зберігати його у localStorage
compare: compareReducer,
});
const persistConfig = {
key: 'root',
storage,
- whitelist: [CategoryName.Favourites, CategoryName.Cart, 'compare'],
+ whitelist: [CategoryName.Favourites, CategoryName.Cart, CategoryName.Compare],
};
const persistedReducer = persistReducer(persistConfig, persistedReducers);
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 1d4fe45..b91cf6b 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -7,19 +7,29 @@ import { ProductType } from '@/types/CategoryType';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
+
export function mapProductTypeToProduct(product: ProductType): Product {
return {
- id: +`${product.id}-${product.color}-${product.capacity}`,
- itemId: String(product.itemId),
+ id: product.id,
+ itemId: product.itemId,
category: product.category,
name: product.name || 'Без назви',
image: Array.isArray(product.images) ? product.images[0] : product.image,
- price: product.price ?? 0,
- fullPrice: product.fullPrice ?? 0,
+ price: product.priceDiscount ?? 0,
+ fullPrice: product.priceRegular ?? 0,
screen: product.screen ?? '—',
capacity: product.capacity ?? '—',
ram: product.ram ?? '—',
color: product.color ?? '—',
year: product.year ?? 2023,
+
+ processor: product.processor ?? '—',
+ resolution: product.resolution ?? '—',
+ camera: product.camera ?? '—',
+ zoom: product.zoom ?? '—',
+ cell:
+ Array.isArray(product.cell) ?
+ product.cell.join(', ')
+ : (product.cell ?? '—'),
};
}
diff --git a/src/types/product.ts b/src/types/product.ts
index 53bf0ec..e3cbc2d 100644
--- a/src/types/product.ts
+++ b/src/types/product.ts
@@ -1,11 +1,11 @@
-import { CategoryName } from './CategoryName';
+import { CategoryName } from '@/types/CategoryName';
export const VALID_CATEGORIES = Object.values(CategoryName).slice(1, 4);
-type Category = (typeof VALID_CATEGORIES)[number];
+export type Category = (typeof VALID_CATEGORIES)[number];
export interface Product {
- id: number;
+ id: number | string;
category: Category;
itemId: string;
name: string;
@@ -17,6 +17,12 @@ export interface Product {
ram: string;
year: number;
image: string;
+
+ resolution?: string;
+ processor?: string;
+ camera?: string;
+ zoom?: string;
+ cell?: string[];
}
export function isProduct(item: unknown): item is Product {
From 4137916e7b0863d615bf203c1bcea50c8e0ec62e Mon Sep 17 00:00:00 2001
From: Yevhenii Kaliaiev
Date: Wed, 24 Sep 2025 15:21:16 +0300
Subject: [PATCH 03/11] third commit
---
src/app/compare/ComparePage.tsx | 5 +-
src/app/compare/CompareSlider.tsx | 2 +-
.../Products/AddToCompareButton.tsx | 97 +++++++++++++++----
src/components/Products/ProductCart.tsx | 21 +++-
src/components/UI/NavBar/CategoriesMenu.tsx | 2 +-
src/components/UI/NavBar/CompareLink.tsx | 34 +++++++
src/components/UI/icons/Scale(Black).svg | 7 ++
src/components/UI/icons/Scale(Grey).svg | 7 ++
src/components/UI/icons/Scale(White).svg | 7 ++
src/components/UI/icons/Scale(Yellow).svg | 7 ++
.../ProductDetailsOrderOptions.tsx | 14 ++-
src/lib/features/compare/compareSlice.tsx | 6 +-
src/lib/utils.ts | 4 +-
src/types/CategoryType.ts | 1 +
src/types/NavBarRightComponents.tsx | 15 +--
src/types/product.ts | 2 +-
16 files changed, 181 insertions(+), 50 deletions(-)
create mode 100644 src/components/UI/NavBar/CompareLink.tsx
create mode 100644 src/components/UI/icons/Scale(Black).svg
create mode 100644 src/components/UI/icons/Scale(Grey).svg
create mode 100644 src/components/UI/icons/Scale(White).svg
create mode 100644 src/components/UI/icons/Scale(Yellow).svg
diff --git a/src/app/compare/ComparePage.tsx b/src/app/compare/ComparePage.tsx
index 0d3fb05..c3e5e0d 100644
--- a/src/app/compare/ComparePage.tsx
+++ b/src/app/compare/ComparePage.tsx
@@ -32,11 +32,11 @@ const ComparePage = () => {
const [selectedCategory, setSelectedCategory] = useState(
'',
);
+
const dispatch = useDispatch();
useEffect(() => setMounted(true), []);
- // Групуємо товари по категоріях
const itemsByCategory = useMemo(() => {
return compareItems.reduce>(
(acc, item) => {
@@ -48,12 +48,10 @@ const ComparePage = () => {
);
}, [compareItems]);
- // Список доступних категорій з товарами
const availableCategories = Object.keys(itemsByCategory).filter(
(cat) => itemsByCategory[cat as CategoryName].length > 0,
) as CategoryName[];
- // Автоматичний вибір категорії при зміні товарів
useEffect(() => {
if (!selectedCategory && availableCategories.length > 0) {
setSelectedCategory(availableCategories[0]);
@@ -62,7 +60,6 @@ const ComparePage = () => {
(!itemsByCategory[selectedCategory] ||
itemsByCategory[selectedCategory].length === 0)
) {
- // Якщо поточна категорія порожня, беремо наступну доступну
setSelectedCategory(availableCategories[0] || '');
}
}, [selectedCategory, itemsByCategory, availableCategories]);
diff --git a/src/app/compare/CompareSlider.tsx b/src/app/compare/CompareSlider.tsx
index 5c4f797..82330ea 100644
--- a/src/app/compare/CompareSlider.tsx
+++ b/src/app/compare/CompareSlider.tsx
@@ -120,7 +120,7 @@ export default function CompareSlider({ title, products }: CompareSliderProps) {
key={`${product.itemId}-${product.color}-${product.capacity}`}
>
diff --git a/src/components/Products/AddToCompareButton.tsx b/src/components/Products/AddToCompareButton.tsx
index f7cc16f..fd19164 100644
--- a/src/components/Products/AddToCompareButton.tsx
+++ b/src/components/Products/AddToCompareButton.tsx
@@ -2,50 +2,113 @@
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
-import { addToCompare } from '@/lib/features/compare/compareSlice';
+import {
+ addToCompare,
+ removeFromCompareByKey,
+} from '@/lib/features/compare/compareSlice';
import { RootState } from '@/lib/store';
+import Image from 'next/image';
+import ScaleBlack from '@/components/UI/icons/Scale(Black).svg';
+import ScaleGrey from '@/components/UI/icons/Scale(Grey).svg';
+import ScaleYellow from '@/components/UI/icons/Scale(Yellow).svg';
import { ProductType } from '@/types/CategoryType';
+import { useGetProductByIdQuery } from '@/lib/features/api/apiSlice';
interface AddToCompareButtonProps {
product: ProductType;
onClick?: () => void;
+ isCart: boolean;
}
const AddToCompareButton: React.FC = ({
product,
onClick,
+ isCart,
}) => {
const dispatch = useDispatch();
const items = useSelector(
(state: RootState) => state.persisted.compare.items,
);
- const isAdded = items.some(
+ // Перевіряємо, чи товар вже в compare
+ const existsInCompare = items.some(
(i) =>
i.id === product.id &&
i.color === product.color &&
i.capacity === product.capacity,
);
- const handleAddToCompare = () => {
- if (!isAdded) {
- dispatch(addToCompare(product));
- onClick?.();
+ // @ts-expect-error TS2339: The “itemId” property does not exist in the “ProductType”
+ const { data: fullProduct } = useGetProductByIdQuery(product.itemId, {
+ skip: existsInCompare || !isCart,
+ });
+
+ const handleToggle = () => {
+ if (existsInCompare) {
+ dispatch(
+ removeFromCompareByKey({
+ id: product.id,
+ color: product.color,
+ capacity: product.capacity,
+ }),
+ );
+ } else {
+ const productToAdd = isCart && fullProduct ? fullProduct : product;
+ dispatch(addToCompare(productToAdd));
}
+ onClick?.();
};
return (
-
+ <>
+ {!isCart ?
+
+ :
+ }
+ >
);
};
diff --git a/src/components/Products/ProductCart.tsx b/src/components/Products/ProductCart.tsx
index f40e594..f52a88f 100644
--- a/src/components/Products/ProductCart.tsx
+++ b/src/components/Products/ProductCart.tsx
@@ -13,6 +13,7 @@ import { RootState } from '@/lib/store';
import CloseComponent from '@/components/UI/ShoppingCartIcons/CloseComponent';
import { removeFromCompareByKey } from '@/lib/features/compare/compareSlice';
import { CategoryName } from '@/types/CategoryName';
+import AddToCompareButton from './AddToCompareButton';
interface ProductCartProps {
product: Product;
@@ -91,31 +92,41 @@ const ProductCart = ({
}}
viewport={{ once: !disableOnce, amount: 0.3 }}
className="
- border border-light-theme-border-color
+ border border-light-theme-border-color group
rounded-2xl
dark:bg-item-bg dark:border-dark-theme-border-color
transition-shadow duration-700
hover:shadow-[0_3px_13px_0_rgba(23,32,49,0.4)]
"
>
+ {!isCompare && (
+
+ )}
{isCompare && (
)}
+
= ({ direction = 'row', onClose }) => {
pathname === `/${category}` ||
(category === CategoryName.Home && pathname === '/')
) ?
- 'after:absolute after:left-0 after:right-0 after:h-[3px] after:bg-light-theme-text-hover after:bottom-0 after:scale-x-100'
+ 'after:absolute after:left-0 after:right-0 after:h-[3px] after:bg-light-theme-text-hover after:bottom-0 after:scale-x-100'
: '';
return (
diff --git a/src/components/UI/NavBar/CompareLink.tsx b/src/components/UI/NavBar/CompareLink.tsx
new file mode 100644
index 0000000..c439a1e
--- /dev/null
+++ b/src/components/UI/NavBar/CompareLink.tsx
@@ -0,0 +1,34 @@
+// #region Imports
+import Image from 'next/image';
+import React from 'react';
+
+import ScaleBlack from '@/components/UI/icons/Scale(Black).svg';
+import ScaleWhite from '@/components/UI/icons/Scale(White).svg';
+import Link from 'next/link';
+import { CategoryName } from '@/types/CategoryName';
+// #endregion
+
+const CompareLink: React.FC = () => {
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+export default CompareLink;
diff --git a/src/components/UI/icons/Scale(Black).svg b/src/components/UI/icons/Scale(Black).svg
new file mode 100644
index 0000000..6fc8952
--- /dev/null
+++ b/src/components/UI/icons/Scale(Black).svg
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/src/components/UI/icons/Scale(Grey).svg b/src/components/UI/icons/Scale(Grey).svg
new file mode 100644
index 0000000..df6fcd5
--- /dev/null
+++ b/src/components/UI/icons/Scale(Grey).svg
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/src/components/UI/icons/Scale(White).svg b/src/components/UI/icons/Scale(White).svg
new file mode 100644
index 0000000..cb16e56
--- /dev/null
+++ b/src/components/UI/icons/Scale(White).svg
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/src/components/UI/icons/Scale(Yellow).svg b/src/components/UI/icons/Scale(Yellow).svg
new file mode 100644
index 0000000..2a27a46
--- /dev/null
+++ b/src/components/UI/icons/Scale(Yellow).svg
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/src/components/pages/ProductDetails/ProductDetailsOrderOptions.tsx b/src/components/pages/ProductDetails/ProductDetailsOrderOptions.tsx
index 0bcee58..c8d88f5 100644
--- a/src/components/pages/ProductDetails/ProductDetailsOrderOptions.tsx
+++ b/src/components/pages/ProductDetails/ProductDetailsOrderOptions.tsx
@@ -3,10 +3,10 @@
import Link from 'next/link';
import AddToCartButton from '@/components/Products/AddToCartButton';
import FavoriteButton from '@/components/Products/FavoriteButton';
-import AddToCompareButton from '@/components/Products/AddToCompareButton';
import OptionGroup from '@/components/pages/ProductDetails/ProductDetailsOptionGroup';
import { Product } from '@/types/product';
import { ProductType } from '@/types/CategoryType';
+import AddToCompareButton from '@/components/Products/AddToCompareButton';
interface Props {
product: Product;
@@ -62,6 +62,7 @@ const ProductDetailsOrderOptions: React.FC
= ({
@@ -92,6 +93,7 @@ const ProductDetailsOrderOptions: React.FC = ({
@@ -130,10 +132,14 @@ const ProductDetailsOrderOptions: React.FC = ({
-
-
+
+
-
diff --git a/src/lib/features/compare/compareSlice.tsx b/src/lib/features/compare/compareSlice.tsx
index 8ddac1e..c53ca53 100644
--- a/src/lib/features/compare/compareSlice.tsx
+++ b/src/lib/features/compare/compareSlice.tsx
@@ -34,10 +34,12 @@ const compareSlice = createSlice({
capacity: string;
}>,
) => {
+ console.log('removeFromCompareByKey called with', action.payload);
+ console.log('current items before', state.items);
+
const { id, color, capacity } = action.payload;
state.items = state.items.filter(
- (i) =>
- !(i.itemId === id && i.color === color && i.capacity === capacity),
+ (i) => !(i.id === id && i.color === color && i.capacity === capacity),
);
},
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index b91cf6b..23edc64 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -11,9 +11,10 @@ export function cn(...inputs: ClassValue[]) {
export function mapProductTypeToProduct(product: ProductType): Product {
return {
id: product.id,
- itemId: product.itemId,
+ itemId: product.id,
category: product.category,
name: product.name || 'Без назви',
+ // @ts-expect-error TS2332: The type “string | undefined” cannot be assigned to the type “string”.
image: Array.isArray(product.images) ? product.images[0] : product.image,
price: product.priceDiscount ?? 0,
fullPrice: product.priceRegular ?? 0,
@@ -21,7 +22,6 @@ export function mapProductTypeToProduct(product: ProductType): Product {
capacity: product.capacity ?? '—',
ram: product.ram ?? '—',
color: product.color ?? '—',
- year: product.year ?? 2023,
processor: product.processor ?? '—',
resolution: product.resolution ?? '—',
diff --git a/src/types/CategoryType.ts b/src/types/CategoryType.ts
index 54beeaf..7e7ed18 100644
--- a/src/types/CategoryType.ts
+++ b/src/types/CategoryType.ts
@@ -31,6 +31,7 @@ export type ProductType = {
camera?: string;
zoom?: string;
cell: string[];
+ image?: string;
};
export type ProductData = {
diff --git a/src/types/NavBarRightComponents.tsx b/src/types/NavBarRightComponents.tsx
index 411a291..c74f0e4 100644
--- a/src/types/NavBarRightComponents.tsx
+++ b/src/types/NavBarRightComponents.tsx
@@ -3,21 +3,10 @@ import ShoppingCartLink from '@/components/UI/NavBar/ShoppingCartLink';
import ThemeSwitcher from '@/components/UI/ThemeSwitcher';
import React from 'react';
import { CategoryName } from '@/types/CategoryName';
-import Link from 'next/link';
-
-export const Compare = () => {
- return (
-
- Compare
-
- );
-};
+import CompareLink from '@/components/UI/NavBar/CompareLink';
export const NavBarRightComponents = [
- { id: CategoryName.Compare, element:
},
+ { id: CategoryName.Compare, element:
},
{ id: 'theme', element:
},
{ id: CategoryName.Favourites, element:
},
{ id: CategoryName.Cart, element:
},
diff --git a/src/types/product.ts b/src/types/product.ts
index e3cbc2d..aec47b2 100644
--- a/src/types/product.ts
+++ b/src/types/product.ts
@@ -22,7 +22,7 @@ export interface Product {
processor?: string;
camera?: string;
zoom?: string;
- cell?: string[];
+ cell?: string[] | string;
}
export function isProduct(item: unknown): item is Product {
From cab7c4f44fde1d2ddba7faa42a5a1555920d555e Mon Sep 17 00:00:00 2001
From: Yevhenii Kaliaiev
Date: Wed, 24 Sep 2025 17:32:47 +0300
Subject: [PATCH 04/11] fifth commit
---
.../Products/AddToCompareButton.tsx | 37 +++++++++----------
src/components/Products/ProductCart.tsx | 18 +++++----
src/components/UI/NavBar/CompareLink.tsx | 28 ++++++++++++--
src/lib/utils.ts | 33 ++++++++++++-----
4 files changed, 76 insertions(+), 40 deletions(-)
diff --git a/src/components/Products/AddToCompareButton.tsx b/src/components/Products/AddToCompareButton.tsx
index fd19164..78861ed 100644
--- a/src/components/Products/AddToCompareButton.tsx
+++ b/src/components/Products/AddToCompareButton.tsx
@@ -1,6 +1,6 @@
'use client';
-import React from 'react';
+import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
addToCompare,
@@ -26,22 +26,21 @@ const AddToCompareButton: React.FC = ({
isCart,
}) => {
const dispatch = useDispatch();
- const items = useSelector(
- (state: RootState) => state.persisted.compare.items,
- );
- // Перевіряємо, чи товар вже в compare
- const existsInCompare = items.some(
- (i) =>
- i.id === product.id &&
- i.color === product.color &&
- i.capacity === product.capacity,
+ const existsInCompare = useSelector((state: RootState) =>
+ state.persisted.compare.items.some(
+ (i) =>
+ i.id === product.id &&
+ i.color === product.color &&
+ i.capacity === product.capacity,
+ ),
);
-
// @ts-expect-error TS2339: The “itemId” property does not exist in the “ProductType”
+
const { data: fullProduct } = useGetProductByIdQuery(product.itemId, {
- skip: existsInCompare || !isCart,
+ skip: !isCart,
});
+ console.log('fullProduct', fullProduct);
const handleToggle = () => {
if (existsInCompare) {
@@ -65,13 +64,13 @@ const AddToCompareButton: React.FC = ({
));
-
const compareSpecs =
isCompare ?
[
@@ -100,12 +99,17 @@ const ProductCart = ({
"
>
{!isCompare && (
-
-
+
)}
{isCompare && (
diff --git a/src/components/UI/NavBar/CompareLink.tsx b/src/components/UI/NavBar/CompareLink.tsx
index c439a1e..10d126b 100644
--- a/src/components/UI/NavBar/CompareLink.tsx
+++ b/src/components/UI/NavBar/CompareLink.tsx
@@ -1,31 +1,51 @@
// #region Imports
import Image from 'next/image';
-import React from 'react';
+import React, { useEffect, useState } from 'react';
+import { useSelector } from 'react-redux';
import ScaleBlack from '@/components/UI/icons/Scale(Black).svg';
import ScaleWhite from '@/components/UI/icons/Scale(White).svg';
import Link from 'next/link';
import { CategoryName } from '@/types/CategoryName';
+import { RootState } from '@/lib/store';
+import classNames from 'classnames';
+
// #endregion
const CompareLink: React.FC = () => {
+ const [isClient, setIsClient] = useState(false);
+ const compareCount = useSelector(
+ (state: RootState) => state.persisted.compare.items.length,
+ );
+
+ useEffect(() => {
+ setIsClient(true);
+ }, []);
return (
<>
-
+
+ {isClient && compareCount > 0 && (
+
+ {compareCount > 9 ? '9+' : compareCount}
+
+ )}
>
);
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 23edc64..56c8d84 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -1,7 +1,7 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
-import { Product } from '@/types/product';
+import { Category, Product } from '@/types/product';
import { ProductType } from '@/types/CategoryType';
export function cn(...inputs: ClassValue[]) {
@@ -10,26 +10,39 @@ export function cn(...inputs: ClassValue[]) {
export function mapProductTypeToProduct(product: ProductType): Product {
return {
- id: product.id,
- itemId: product.id,
- category: product.category,
+ // Ідентифікатори для синхронізації в Redux
+ id: product.id, // може бути string | number
+ itemId: product.id, // використовується для порівняння
+
+ category: product.category as Category,
name: product.name || 'Без назви',
- // @ts-expect-error TS2332: The type “string | undefined” cannot be assigned to the type “string”.
- image: Array.isArray(product.images) ? product.images[0] : product.image,
+
+ // Гарантуємо, що завжди є картинка
+ image:
+ Array.isArray(product.images) && product.images.length > 0 ?
+ product.images[0]
+ : (product.image ?? ''),
+
+ // Ціни
price: product.priceDiscount ?? 0,
fullPrice: product.priceRegular ?? 0,
+
+ // Основні характеристики
screen: product.screen ?? '—',
capacity: product.capacity ?? '—',
ram: product.ram ?? '—',
color: product.color ?? '—',
+ // Додаткові характеристики
processor: product.processor ?? '—',
resolution: product.resolution ?? '—',
camera: product.camera ?? '—',
zoom: product.zoom ?? '—',
- cell:
- Array.isArray(product.cell) ?
- product.cell.join(', ')
- : (product.cell ?? '—'),
+
+ // Залишаємо як масив для коректної синхронізації
+ cell: product.cell ?? [],
+
+ // Додаткові обов'язкові поля
+ year: new Date().getFullYear(), // або 0, якщо хочеш
};
}
From 767d75aa8827fcbfdab90593a0d8b53bbd82e551 Mon Sep 17 00:00:00 2001
From: Yevhenii Kaliaiev
Date: Wed, 24 Sep 2025 17:48:17 +0300
Subject: [PATCH 05/11] six commit
---
.../Products/AddToCompareButton.tsx | 44 ++++++++++++-------
src/components/Products/ProductCart.tsx | 7 ++-
src/components/UI/NavBar/CompareLink.tsx | 1 -
src/lib/features/compare/compareSlice.tsx | 21 +++++++--
4 files changed, 50 insertions(+), 23 deletions(-)
diff --git a/src/components/Products/AddToCompareButton.tsx b/src/components/Products/AddToCompareButton.tsx
index 78861ed..37b4e70 100644
--- a/src/components/Products/AddToCompareButton.tsx
+++ b/src/components/Products/AddToCompareButton.tsx
@@ -5,6 +5,7 @@ import { useDispatch, useSelector } from 'react-redux';
import {
addToCompare,
removeFromCompareByKey,
+ toggleCompare,
} from '@/lib/features/compare/compareSlice';
import { RootState } from '@/lib/store';
import Image from 'next/image';
@@ -13,6 +14,7 @@ import ScaleGrey from '@/components/UI/icons/Scale(Grey).svg';
import ScaleYellow from '@/components/UI/icons/Scale(Yellow).svg';
import { ProductType } from '@/types/CategoryType';
import { useGetProductByIdQuery } from '@/lib/features/api/apiSlice';
+import { toast } from 'sonner';
interface AddToCompareButtonProps {
product: ProductType;
@@ -36,28 +38,38 @@ const AddToCompareButton: React.FC = ({
),
);
// @ts-expect-error TS2339: The “itemId” property does not exist in the “ProductType”
-
- const { data: fullProduct } = useGetProductByIdQuery(product.itemId, {
- skip: !isCart,
- });
- console.log('fullProduct', fullProduct);
+ // const { data: fullProduct } = useGetProductByIdQuery(product.itemId, {
+ // skip: !isCart,
+ // });
const handleToggle = () => {
+ dispatch(toggleCompare(product));
+
if (existsInCompare) {
- dispatch(
- removeFromCompareByKey({
- id: product.id,
- color: product.color,
- capacity: product.capacity,
- }),
- );
+ toast(`${product.name} removed from compare`);
} else {
- const productToAdd = isCart && fullProduct ? fullProduct : product;
- dispatch(addToCompare(productToAdd));
+ toast(`${product.name} added to compare`);
}
+
onClick?.();
};
+ // const handleToggle = () => {
+ // if (existsInCompare) {
+ // dispatch(
+ // removeFromCompareByKey({
+ // id: product.id,
+ // color: product.color,
+ // capacity: product.capacity,
+ // }),
+ // );
+ // } else {
+ // const productToAdd = isCart && fullProduct ? fullProduct : product;
+ // dispatch(addToCompare(productToAdd));
+ // }
+ // onClick?.();
+ // };
+
return (
<>
{!isCart ?
@@ -77,14 +89,14 @@ const AddToCompareButton: React.FC = ({
width={30}
height={30}
alt={existsInCompare ? 'Remove from compare' : 'Add to compare'}
- className="dark:hidden"
+ className="dark:hidden transition-all duration-500 hover:scale-[1.2]"
/>
: