diff --git a/src/app/api/categories.js b/src/app/api/categories.js index dcdfc3e..1594e93 100644 --- a/src/app/api/categories.js +++ b/src/app/api/categories.js @@ -7,13 +7,14 @@ export const categoriesApi = baseApi.injectEndpoints({ }), getCategoryProducts: builder.query({ - query: ({ categoryId, page = 1, limit, brands, min_price, max_price }) => { + query: ({ categoryId, page = 1, limit, brands, min_price, max_price, sorting }) => { const params = new URLSearchParams(); params.append('page', page); if (limit) params.append('limit', limit); if (brands) params.append('brands', brands); if (min_price) params.append('min_price', min_price); if (max_price) params.append('max_price', max_price); + if (sorting) params.append('sorting', sorting); return `categories/${categoryId}/products?${params.toString()}`; }, diff --git a/src/app/api/collectionsApi.js b/src/app/api/collectionsApi.js index 2550502..e493ccf 100644 --- a/src/app/api/collectionsApi.js +++ b/src/app/api/collectionsApi.js @@ -30,13 +30,14 @@ export const collectionsApi = baseApi.injectEndpoints({ }), getCollectionProductsPaginated: builder.query({ - query: ({ collectionId, page = 1, limit = 6, brands, min_price, max_price }) => { + query: ({ collectionId, page = 1, limit = 6, brands, min_price, max_price, sorting }) => { const params = new URLSearchParams(); params.append('page', page); if (limit) params.append('limit', limit); if (brands) params.append('brands', brands); if (min_price) params.append('min_price', min_price); if (max_price) params.append('max_price', max_price); + if (sorting) params.append('sorting', sorting); return `/collections/${collectionId}/products?${params.toString()}`; }, diff --git a/src/components/Checkout/index.jsx b/src/components/Checkout/index.jsx index ed472e9..23e2312 100644 --- a/src/components/Checkout/index.jsx +++ b/src/components/Checkout/index.jsx @@ -304,11 +304,11 @@ const Checkout = ({ cartItems, shippingPrice, productIds, onBackToCart, onPlaceO "checkout.Delivery_is_carried_out_in_the_cities_of_Ashgabat_Buzmein_and_Anau" )} -
  • + {/*
  • {t( "checkout.The_minimum_order_amount_must_be_at_least_50_manat_for_orders_over_150_manat_delivery_is_free" )} -
  • + */}
  • {t( "checkout.After_you_place_an_order_on_the_website_the_operator_will_call_you_to_confirm_the_order_for_regular_customers_confirmation_is_carried_out_automatically_at_their_request" diff --git a/src/components/Layout/index.jsx b/src/components/Layout/index.jsx index 3925cf0..3183aaa 100644 --- a/src/components/Layout/index.jsx +++ b/src/components/Layout/index.jsx @@ -8,7 +8,7 @@ const Layout = () => { return ( <> - + {/* */}
    diff --git a/src/components/Navbar/Navbar.module.scss b/src/components/Navbar/Navbar.module.scss index ff821c9..326c85a 100644 --- a/src/components/Navbar/Navbar.module.scss +++ b/src/components/Navbar/Navbar.module.scss @@ -14,6 +14,9 @@ background-color: #fff; margin-bottom: 1px; border-bottom: 3px solid #f3f4f6; + position: sticky; + top: 0; + z-index: 100; } .btn{ @@ -38,7 +41,7 @@ background-color: #ffffff; max-width: 1366px; position: sticky; - top: 0; + top: 80px; // navbarUp yüksekliği kadar padding-top: 12px; padding-bottom: 12px; padding-left: 1.375rem; @@ -96,6 +99,9 @@ } } +.navLinks { + width: 100%; +} .navLinks ul { list-style: none; display: flex; diff --git a/src/components/Navbar/NavbarDown.jsx b/src/components/Navbar/NavbarDown.jsx index 4ca0466..c2bfa02 100644 --- a/src/components/Navbar/NavbarDown.jsx +++ b/src/components/Navbar/NavbarDown.jsx @@ -135,7 +135,7 @@ const NavbarDown = () => { ]; return ( -
    +
    +
    price_amount) { + calculatedDiscount = Math.round(((old_price_amount - price_amount) / old_price_amount) * 100); + } + return ( <>
    setIsHovered(false)} >
    - {product.discount && ( - -{product.discount}% + {(product.discount || calculatedDiscount) && ( + + -{product.discount ? product.discount : calculatedDiscount}% + )} {product.stock === 0 && ( diff --git a/src/pages/Category/CategoryPage.module.scss b/src/pages/Category/CategoryPage.module.scss index 4688df5..b15a351 100644 --- a/src/pages/Category/CategoryPage.module.scss +++ b/src/pages/Category/CategoryPage.module.scss @@ -1,3 +1,28 @@ +.sortingContainer { + display: flex; + align-items: center; + gap: 8px; + margin-left: 16px; +} + +.sortingLabel { + font-size: 14px; + color: #888; +} + +.sortingSelect { + padding: 6px 12px; + border-radius: 6px; + border: 1px solid #d1d5db; + font-size: 15px; + background: #fff; + color: #222; + outline: none; + transition: border-color 0.2s; +} +.sortingSelect:focus { + border-color: #6c63ff; +} .mobilePhoneGrid { display: flex !important; diff --git a/src/pages/Category/components/CategoryFilters.jsx b/src/pages/Category/components/CategoryFilters.jsx index 430d000..65eaa0d 100644 --- a/src/pages/Category/components/CategoryFilters.jsx +++ b/src/pages/Category/components/CategoryFilters.jsx @@ -18,6 +18,8 @@ const CategoryFilters = ({ onBrandSelect, onBrandDeselect, onBrandSearchChange, + sorting = "price_amount-descending", + onSortingChange = () => {}, className, }) => { const { t } = useTranslation(); @@ -123,6 +125,19 @@ const CategoryFilters = ({ />
    + {/* Sıralama dropdown'u */} +
    + + +
    ); diff --git a/src/pages/Category/hooks/useCategoryProducts.js b/src/pages/Category/hooks/useCategoryProducts.js index c1992ed..ffce4fc 100644 --- a/src/pages/Category/hooks/useCategoryProducts.js +++ b/src/pages/Category/hooks/useCategoryProducts.js @@ -17,6 +17,7 @@ const useCategoryProducts = ({ selectedFilterBrand, minPrice, maxPrice, + sorting, searchQuery, }) => { const [products, setProducts] = useState([]); @@ -35,6 +36,7 @@ const useCategoryProducts = ({ selectedFilterBrand && `fbrand-${selectedFilterBrand}`, minPrice && `min-${minPrice}`, maxPrice && `max-${maxPrice}`, + sorting && `sort-${sorting}`, ].filter(Boolean); return parts.join("|") || "none"; }, [ @@ -45,6 +47,7 @@ const useCategoryProducts = ({ selectedFilterBrand, minPrice, maxPrice, + sorting, ]); const fetchParams = useMemo( @@ -54,8 +57,9 @@ const useCategoryProducts = ({ brands: selectedFilterBrand || undefined, min_price: minPrice || undefined, max_price: maxPrice || undefined, + sorting: sorting || undefined, }), - [currentPage, selectedFilterBrand, minPrice, maxPrice], + [currentPage, selectedFilterBrand, minPrice, maxPrice, sorting], ); const fetchKey = `${contextId}-p${currentPage}`; diff --git a/src/pages/Category/index.jsx b/src/pages/Category/index.jsx index 4c024f9..f12150c 100644 --- a/src/pages/Category/index.jsx +++ b/src/pages/Category/index.jsx @@ -29,6 +29,7 @@ const CategoryPage = () => { currentPage: 1, minPrice: "", maxPrice: "", + sorting: "price_amount-descending", }); const [filterState, setFilterState] = useState({ @@ -94,6 +95,7 @@ const CategoryPage = () => { selectedFilterBrand: filterState.selectedFilterBrand, minPrice: pageState.minPrice, maxPrice: pageState.maxPrice, + sorting: pageState.sorting, searchQuery, }); const isMobilePhoneView = @@ -293,6 +295,22 @@ const CategoryPage = () => { > {t("category.filter")} +
    + + +
    { onBrandSearchChange={(query) => setFilterState((prev) => ({ ...prev, brandSearchQuery: query })) } + sorting={pageState.sorting} + onSortingChange={(value) => { + setPageState((prev) => ({ ...prev, sorting: value, currentPage: 1 })); + setAllProducts([]); + setHasMore(true); + }} /> @@ -373,6 +397,12 @@ const CategoryPage = () => { onBrandSearchChange={(query) => setFilterState((prev) => ({ ...prev, brandSearchQuery: query })) } + sorting={pageState.sorting} + onSortingChange={(value) => { + setPageState((prev) => ({ ...prev, sorting: value, currentPage: 1 })); + setAllProducts([]); + setHasMore(true); + }} />
    diff --git a/src/pages/ProductDetail/ProductPage.module.scss b/src/pages/ProductDetail/ProductPage.module.scss index 2c71626..f7a4c3d 100644 --- a/src/pages/ProductDetail/ProductPage.module.scss +++ b/src/pages/ProductDetail/ProductPage.module.scss @@ -286,6 +286,8 @@ justify-content: space-between; padding-bottom: 8px; border-bottom: 1px solid #f1f1f1; + text-decoration: none; + color: inherit; &:last-child { border-bottom: none; @@ -311,6 +313,27 @@ padding: 16px 20px; background: #fff; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08); + position: relative; + overflow: hidden; +} + +.descriptionCard::after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: 60px; + pointer-events: none; + background: linear-gradient(to top, #111 0%, #fff 100%); + opacity: 0.7; + z-index: 2; +} + +.productDescriptionCollapsed + div { + /* Read more butonunu gradient üstünde göstermek için z-index */ + position: relative; + z-index: 3; } .descriptionHeader { @@ -358,18 +381,28 @@ overflow: hidden; } +.productDescriptionWrapper { + display: flex; + flex-direction: column; + align-items: center; +} + .readMoreBtn { background: none; border: none; - color: #1890ff; - font-weight: 500; + color: #fff; + font-weight: bold; cursor: pointer; padding: 8px 0 0 0; - font-size: 14px; + font-size: 16px; display: inline-block; + text-shadow: 0 1px 8px #000, 0 1px 1px #000; + letter-spacing: 0.5px; + margin-top: 8px; &:hover { text-decoration: underline; + opacity: 0.85; } } diff --git a/src/pages/ProductDetail/index.jsx b/src/pages/ProductDetail/index.jsx index 4909ff2..1bb4c9c 100644 --- a/src/pages/ProductDetail/index.jsx +++ b/src/pages/ProductDetail/index.jsx @@ -26,6 +26,7 @@ import { import ImageCarousel from "../../components/ProductCard/imageCarousel/index"; import Loader from "../../components/Loader/index"; import { Result, Button } from "antd"; +import { div } from "framer-motion/client"; const ProductPage = ({ productProp, @@ -351,10 +352,14 @@ const ProductPage = ({ )} */} {product.brand?.name && ( - + )} {product.channel?.[0]?.name && ( @@ -385,29 +390,31 @@ const ProductPage = ({ {t("product.description")}

    -
    - {showReadMore && !isDescExpanded && ( - - )} - {showReadMore && isDescExpanded && ( - - )} +
    +
    + {showReadMore && !isDescExpanded && ( + + )} + {showReadMore && isDescExpanded && ( + + )} +
    )}