added channels products
This commit is contained in:
34
src/app/api/channelsApi.js
Normal file
34
src/app/api/channelsApi.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { baseApi } from "./baseApi";
|
||||
|
||||
export const channelsApi = baseApi.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
getChannelProducts: builder.query({
|
||||
query: (params) => {
|
||||
// params: { channelId, page, limit, min_price, max_price, sorting }
|
||||
const {
|
||||
channelId,
|
||||
page = 1,
|
||||
limit = 24,
|
||||
min_price,
|
||||
max_price,
|
||||
sorting,
|
||||
} = params;
|
||||
const urlParams = new URLSearchParams();
|
||||
urlParams.append("page", page);
|
||||
urlParams.append("limit", limit);
|
||||
if (min_price) urlParams.append("min_price", min_price);
|
||||
if (max_price) urlParams.append("max_price", max_price);
|
||||
if (sorting) urlParams.append("sorting", sorting);
|
||||
return `/channels/${channelId}/products?${urlParams.toString()}`;
|
||||
},
|
||||
transformResponse: (response) => ({
|
||||
data: response.data || response,
|
||||
pagination: response.pagination || {},
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
overrideExisting: true,
|
||||
});
|
||||
|
||||
export const { useGetChannelProductsQuery, useLazyGetChannelProductsQuery } =
|
||||
channelsApi;
|
||||
@@ -15,6 +15,9 @@ export const filtersApi = baseApi.injectEndpoints({
|
||||
if (params?.brand_id) {
|
||||
queryParams.append("brand_id", String(params.brand_id))
|
||||
}
|
||||
if (params?.channel_id) {
|
||||
queryParams.append("channel_id", String(params.channel_id))
|
||||
}
|
||||
|
||||
return `/filters?${queryParams.toString()}`
|
||||
},
|
||||
@@ -22,6 +25,7 @@ export const filtersApi = baseApi.injectEndpoints({
|
||||
return {
|
||||
categories: response.data?.categories || [],
|
||||
brands: response.data?.brands || [],
|
||||
channels: response.data?.channels || [],
|
||||
}
|
||||
},
|
||||
keepUnusedDataFor: 300,
|
||||
@@ -40,7 +44,9 @@ export const filtersApi = baseApi.injectEndpoints({
|
||||
if (queryArgs.brand_id) {
|
||||
parts.push(`brd:${queryArgs.brand_id}`);
|
||||
}
|
||||
|
||||
if (queryArgs.channel_id) {
|
||||
parts.push(`chn:${queryArgs.channel_id}`);
|
||||
}
|
||||
return parts.length > 0 ? parts.join('|') : 'no-params';
|
||||
},
|
||||
|
||||
@@ -66,7 +72,9 @@ export const filtersApi = baseApi.injectEndpoints({
|
||||
if (arg.brand_id) {
|
||||
tags.push({ type: "Filters", id: `brd-${arg.brand_id}` });
|
||||
}
|
||||
|
||||
if (arg.channel_id) {
|
||||
tags.push({ type: "Filters", id: `chn-${arg.channel_id}` });
|
||||
}
|
||||
return tags;
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -87,24 +87,31 @@
|
||||
overflow: hidden;
|
||||
width: 1336px;
|
||||
max-height: 520px;
|
||||
// max-width: calc(100vw - 32px);
|
||||
max-width: calc(100vw - 32px);
|
||||
}
|
||||
|
||||
// ---- LEFT LIST ----
|
||||
.categoriesList {
|
||||
width: 270px;
|
||||
flex-shrink: 0;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-right: 1px solid #e5e7eb;
|
||||
padding: 10px 0;
|
||||
max-height: 520px;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
width: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f9fafb;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 2px;
|
||||
background: #d1d5db;
|
||||
border-radius: 10px;
|
||||
|
||||
&:hover {
|
||||
background: #9ca3af;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,13 +126,13 @@
|
||||
transition: background-color 0.12s, color 0.12s;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
color: #000;
|
||||
background-color: #f3f4f6;
|
||||
color: #e63946;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
color: #000;
|
||||
background-color: #f3f4f6;
|
||||
color: #e63946;
|
||||
|
||||
.title {
|
||||
font-weight: 600;
|
||||
@@ -138,7 +145,7 @@
|
||||
}
|
||||
|
||||
.chevron {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
color: #9ca3af;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -151,11 +158,18 @@
|
||||
background: #ffffff;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
width: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f9fafb;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #d1d5db;
|
||||
border-radius: 2px;
|
||||
border-radius: 10px;
|
||||
|
||||
&:hover {
|
||||
background: #9ca3af;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ const useCategoryData = ({
|
||||
categoryId,
|
||||
collectionId,
|
||||
brandId,
|
||||
channelId, // EKLE
|
||||
selectedFilterCategory,
|
||||
searchQuery,
|
||||
}) => {
|
||||
@@ -23,8 +24,9 @@ const useCategoryData = ({
|
||||
if (categoryId) return { category_id: categoryId };
|
||||
if (collectionId) return { collection_id: collectionId };
|
||||
if (brandId) return { brand_id: brandId };
|
||||
if (channelId) return { channel_id: channelId };
|
||||
return null;
|
||||
}, [categoryId, collectionId, brandId, selectedFilterCategory, searchQuery]);
|
||||
}, [categoryId, collectionId, brandId, channelId, selectedFilterCategory, searchQuery]);
|
||||
|
||||
const {
|
||||
data: filtersData,
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import {
|
||||
useLazyGetAllCategoryProductsPaginatedQuery,
|
||||
useGetCategoryProductsQuery,
|
||||
} from "../../../app/api/categories";
|
||||
import { useLazyGetBrandProductsQuery } from "../../../app/api/brandsApi";
|
||||
import { useLazyGetCollectionProductsPaginatedQuery } from "../../../app/api/collectionsApi";
|
||||
import { useLazyGetChannelProductsQuery } from "../../../app/api/channelsApi"; // EKLE
|
||||
|
||||
const useCategoryProducts = ({
|
||||
categoryId,
|
||||
collectionId,
|
||||
brandId,
|
||||
channelId,
|
||||
selectedCategory,
|
||||
isSubCategory,
|
||||
currentPage,
|
||||
@@ -27,8 +29,6 @@ const useCategoryProducts = ({
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
|
||||
const activeRequestId = useRef(0);
|
||||
// Tüm parametreleri ref'te tut — stale closure'ı tamamen engelle
|
||||
const paramsRef = useRef({});
|
||||
|
||||
const shouldUseBaseQuery =
|
||||
categoryId &&
|
||||
@@ -36,7 +36,8 @@ const useCategoryProducts = ({
|
||||
!searchQuery &&
|
||||
!selectedFilterCategory &&
|
||||
!brandId &&
|
||||
!collectionId;
|
||||
!collectionId &&
|
||||
!channelId;
|
||||
|
||||
const { data: baseQueryData, isFetching: baseQueryFetching } =
|
||||
useGetCategoryProductsQuery(
|
||||
@@ -54,8 +55,17 @@ const useCategoryProducts = ({
|
||||
const [fetchCategoryPaginated] = useLazyGetAllCategoryProductsPaginatedQuery();
|
||||
const [fetchBrandPaginated] = useLazyGetBrandProductsQuery();
|
||||
const [fetchCollectionPaginated] = useLazyGetCollectionProductsPaginatedQuery();
|
||||
const [fetchChannelPaginated] = useLazyGetChannelProductsQuery();
|
||||
|
||||
// ✅ Ref'e al — dependency array'den çıkar, stale closure yok
|
||||
const fetchersRef = useRef({});
|
||||
fetchersRef.current = {
|
||||
fetchCategoryPaginated,
|
||||
fetchBrandPaginated,
|
||||
fetchCollectionPaginated,
|
||||
fetchChannelPaginated,
|
||||
};
|
||||
|
||||
// Base query handler
|
||||
useEffect(() => {
|
||||
if (!shouldUseBaseQuery || !baseQueryData) return;
|
||||
const data = baseQueryData.data || [];
|
||||
@@ -69,11 +79,23 @@ const useCategoryProducts = ({
|
||||
setHasMore(hasNextPage);
|
||||
}, [baseQueryData, currentPage, shouldUseBaseQuery]);
|
||||
|
||||
// Her fetch çağrısını doğrudan effect içinde yap — useCallback kaldırıldı
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldUseBaseQuery || searchQuery) return;
|
||||
|
||||
// Parametreleri snapshot al
|
||||
|
||||
console.log("🔥 LAZY EFFECT TRIGGERED", {
|
||||
shouldUseBaseQuery,
|
||||
categoryId,
|
||||
collectionId,
|
||||
brandId,
|
||||
channelId,
|
||||
isSubCategory,
|
||||
selectedFilterCategory,
|
||||
selectedCategory,
|
||||
});
|
||||
|
||||
const snapshot = {
|
||||
currentPage,
|
||||
selectedFilterCategory,
|
||||
@@ -81,6 +103,7 @@ const useCategoryProducts = ({
|
||||
isSubCategory,
|
||||
brandId,
|
||||
collectionId,
|
||||
channelId,
|
||||
selectedFilterBrand,
|
||||
minPrice,
|
||||
maxPrice,
|
||||
@@ -92,6 +115,13 @@ const useCategoryProducts = ({
|
||||
|
||||
const run = async () => {
|
||||
try {
|
||||
const {
|
||||
fetchCategoryPaginated,
|
||||
fetchBrandPaginated,
|
||||
fetchCollectionPaginated,
|
||||
fetchChannelPaginated,
|
||||
} = fetchersRef.current; // ✅ ref'ten oku
|
||||
|
||||
const params = {
|
||||
page: snapshot.currentPage,
|
||||
limit: 24,
|
||||
@@ -123,6 +153,11 @@ const useCategoryProducts = ({
|
||||
collectionId: snapshot.collectionId,
|
||||
...params,
|
||||
}).unwrap();
|
||||
} else if (snapshot.channelId) {
|
||||
result = await fetchChannelPaginated({
|
||||
channelId: snapshot.channelId,
|
||||
...params,
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
if (requestId !== activeRequestId.current) return;
|
||||
@@ -159,7 +194,6 @@ const useCategoryProducts = ({
|
||||
|
||||
run();
|
||||
}, [
|
||||
// useCallback YOK — her dependency değişince effect direkt çalışır
|
||||
shouldUseBaseQuery,
|
||||
searchQuery,
|
||||
currentPage,
|
||||
@@ -168,13 +202,12 @@ const useCategoryProducts = ({
|
||||
isSubCategory,
|
||||
brandId,
|
||||
collectionId,
|
||||
channelId,
|
||||
selectedFilterBrand,
|
||||
minPrice,
|
||||
maxPrice,
|
||||
sorting,
|
||||
fetchCategoryPaginated,
|
||||
fetchBrandPaginated,
|
||||
fetchCollectionPaginated,
|
||||
// ✅ fetcher fonksiyonlar dependency'den tamamen çıktı
|
||||
]);
|
||||
|
||||
const isLoading = shouldUseBaseQuery ? baseQueryFetching : isFetching;
|
||||
@@ -188,4 +221,5 @@ const useCategoryProducts = ({
|
||||
};
|
||||
};
|
||||
|
||||
export default useCategoryProducts;
|
||||
export default useCategoryProducts;
|
||||
|
||||
|
||||
@@ -19,16 +19,19 @@ import MobilePhoneCard from "./components/Mobilephonecard";
|
||||
|
||||
const CategoryPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const { categoryId, collectionId, brandId } = useParams();
|
||||
const { categoryId, collectionId, brandId, channelId } = useParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const routeKey = useMemo(
|
||||
() => `${categoryId || "x"}-${collectionId || "x"}-${brandId || "x"}`,
|
||||
[categoryId, collectionId, brandId],
|
||||
() => `${categoryId || "x"}-${collectionId || "x"}-${brandId || "x"}-${channelId || "x"}`,
|
||||
[categoryId, collectionId, brandId, channelId],
|
||||
);
|
||||
|
||||
const getSavedState = (key, defaultVal) => {
|
||||
if (location.state?.clearFilters) {
|
||||
return defaultVal;
|
||||
}
|
||||
try {
|
||||
const saved = sessionStorage.getItem(`category_${key}_${routeKey}`);
|
||||
if (saved) return JSON.parse(saved);
|
||||
@@ -92,6 +95,7 @@ const CategoryPage = () => {
|
||||
categoryId,
|
||||
collectionId,
|
||||
brandId,
|
||||
channelId,
|
||||
selectedFilterCategory: filterState.selectedFilterCategory,
|
||||
searchQuery,
|
||||
});
|
||||
@@ -105,6 +109,7 @@ const CategoryPage = () => {
|
||||
} = useCategoryProducts({
|
||||
categoryId,
|
||||
collectionId,
|
||||
channelId,
|
||||
brandId,
|
||||
selectedCategory,
|
||||
isSubCategory,
|
||||
@@ -137,10 +142,12 @@ const CategoryPage = () => {
|
||||
|
||||
prevRouteRef.current = routeKey;
|
||||
|
||||
const savedPageState = getSavedStateByKey(routeKey, "pageState");
|
||||
const savedFilterState = getSavedStateByKey(routeKey, "filterState");
|
||||
const savedProducts = getSavedStateByKey(routeKey, "products");
|
||||
const savedHasMore = getSavedStateByKey(routeKey, "hasMore");
|
||||
const shouldClear = location.state?.clearFilters;
|
||||
|
||||
const savedPageState = shouldClear ? null : getSavedStateByKey(routeKey, "pageState");
|
||||
const savedFilterState = shouldClear ? null : getSavedStateByKey(routeKey, "filterState");
|
||||
const savedProducts = shouldClear ? null : getSavedStateByKey(routeKey, "products");
|
||||
const savedHasMore = shouldClear ? null : getSavedStateByKey(routeKey, "hasMore");
|
||||
|
||||
if (savedPageState && savedFilterState && savedProducts) {
|
||||
setPageState(savedPageState);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { useParams, useNavigate, Link } from "react-router-dom";
|
||||
import styles from "./ProductPage.module.scss";
|
||||
import { IoMdHeartEmpty, IoMdHeart } from "react-icons/io";
|
||||
import { FaShoppingCart } from "react-icons/fa";
|
||||
@@ -366,12 +366,14 @@ const ProductPage = ({
|
||||
)}
|
||||
|
||||
{product.channel?.[0]?.name && (
|
||||
<div className={styles.metaItem}>
|
||||
|
||||
<Link to={`/channel/${product.channel[0].id}`} state={{ clearFilters: true }} className={styles.metaItem}>
|
||||
<span className={styles.metaLabel}>{t("order.channel")}</span>
|
||||
<span className={styles.metaValue}>
|
||||
{product.channel[0].name}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
)}
|
||||
|
||||
{product.properties?.length > 0 && (
|
||||
|
||||
@@ -40,6 +40,7 @@ export default function Router() {
|
||||
{ path: "/category/:categoryId", element: <Category /> },
|
||||
{ path: "/search", element: <Category /> },
|
||||
{ path: "/collections/:collectionId", element: <Category /> },
|
||||
{ path: "/channel/:channelId", element: <Category /> },
|
||||
{ path: "/product/:productId", element: <ProductDetail /> },
|
||||
{ path: "/profile", element: <ProfileMenu /> },
|
||||
{ path: "/orders", element: <Orders /> },
|
||||
|
||||
Reference in New Issue
Block a user