fixed some bugs

This commit is contained in:
@jcarymuhammedow
2026-04-17 18:38:16 +05:00
parent 68382648a8
commit 6ef7aa3c47
15 changed files with 171 additions and 39 deletions

View File

@@ -7,13 +7,14 @@ export const categoriesApi = baseApi.injectEndpoints({
}), }),
getCategoryProducts: builder.query({ 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(); const params = new URLSearchParams();
params.append('page', page); params.append('page', page);
if (limit) params.append('limit', limit); if (limit) params.append('limit', limit);
if (brands) params.append('brands', brands); if (brands) params.append('brands', brands);
if (min_price) params.append('min_price', min_price); if (min_price) params.append('min_price', min_price);
if (max_price) params.append('max_price', max_price); if (max_price) params.append('max_price', max_price);
if (sorting) params.append('sorting', sorting);
return `categories/${categoryId}/products?${params.toString()}`; return `categories/${categoryId}/products?${params.toString()}`;
}, },

View File

@@ -30,13 +30,14 @@ export const collectionsApi = baseApi.injectEndpoints({
}), }),
getCollectionProductsPaginated: builder.query({ 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(); const params = new URLSearchParams();
params.append('page', page); params.append('page', page);
if (limit) params.append('limit', limit); if (limit) params.append('limit', limit);
if (brands) params.append('brands', brands); if (brands) params.append('brands', brands);
if (min_price) params.append('min_price', min_price); if (min_price) params.append('min_price', min_price);
if (max_price) params.append('max_price', max_price); if (max_price) params.append('max_price', max_price);
if (sorting) params.append('sorting', sorting);
return `/collections/${collectionId}/products?${params.toString()}`; return `/collections/${collectionId}/products?${params.toString()}`;
}, },

View File

@@ -304,11 +304,11 @@ const Checkout = ({ cartItems, shippingPrice, productIds, onBackToCart, onPlaceO
"checkout.Delivery_is_carried_out_in_the_cities_of_Ashgabat_Buzmein_and_Anau" "checkout.Delivery_is_carried_out_in_the_cities_of_Ashgabat_Buzmein_and_Anau"
)} )}
</li> </li>
<li> {/* <li>
{t( {t(
"checkout.The_minimum_order_amount_must_be_at_least_50_manat_for_orders_over_150_manat_delivery_is_free" "checkout.The_minimum_order_amount_must_be_at_least_50_manat_for_orders_over_150_manat_delivery_is_free"
)} )}
</li> </li> */}
<li> <li>
{t( {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" "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"

View File

@@ -8,7 +8,7 @@ const Layout = () => {
return ( return (
<> <>
<Navbar /> <Navbar />
<NavbarDown/> {/* <NavbarDown/> */}
<main> <main>
<Outlet /> <Outlet />
</main> </main>

View File

@@ -14,6 +14,9 @@
background-color: #fff; background-color: #fff;
margin-bottom: 1px; margin-bottom: 1px;
border-bottom: 3px solid #f3f4f6; border-bottom: 3px solid #f3f4f6;
position: sticky;
top: 0;
z-index: 100;
} }
.btn{ .btn{
@@ -38,7 +41,7 @@
background-color: #ffffff; background-color: #ffffff;
max-width: 1366px; max-width: 1366px;
position: sticky; position: sticky;
top: 0; top: 80px; // navbarUp yüksekliği kadar
padding-top: 12px; padding-top: 12px;
padding-bottom: 12px; padding-bottom: 12px;
padding-left: 1.375rem; padding-left: 1.375rem;
@@ -96,6 +99,9 @@
} }
} }
.navLinks {
width: 100%;
}
.navLinks ul { .navLinks ul {
list-style: none; list-style: none;
display: flex; display: flex;

View File

@@ -135,7 +135,7 @@ const NavbarDown = () => {
]; ];
return ( return (
<header className={styles.navbar}> <header className={styles.navbar} style={{ width: "100%" }}>
<div className={styles.navbarDown} style={{ position: "sticky" }}> <div className={styles.navbarDown} style={{ position: "sticky" }}>
<nav className={styles.navLinks}> <nav className={styles.navLinks}>
<ul> <ul>

View File

@@ -9,7 +9,7 @@ import { useTranslation } from "react-i18next";
import tm from "../../assets/tm.png"; import tm from "../../assets/tm.png";
import ru from "../../assets/ru.png"; import ru from "../../assets/ru.png";
import en from "../../assets/en.png"; import en from "../../assets/en.png";
import NavbarDown from "./NavbarDown";
const Navbar = () => { const Navbar = () => {
const [isModalVisible, setIsModalVisible] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
@@ -92,6 +92,7 @@ const Navbar = () => {
</div> </div>
</div> </div>
</div> </div>
<NavbarDown />
</header> </header>
<Modal <Modal
open={isModalVisible} open={isModalVisible}

View File

@@ -30,6 +30,7 @@
border-radius: 8px; border-radius: 8px;
font-size: 0.875rem; font-size: 0.875rem;
font-weight: 600; font-weight: 600;
z-index: 1;
@media screen and (max-width: 426px) { @media screen and (max-width: 426px) {
font-size: 12px; font-size: 12px;
} }

View File

@@ -225,6 +225,12 @@ const ProductCard = ({
const { name, price_amount, old_price_amount, media = [], reviews } = product; const { name, price_amount, old_price_amount, media = [], reviews } = product;
// Hesaplanmış indirim oranı
let calculatedDiscount = null;
if (!product.discount && old_price_amount && price_amount && old_price_amount > price_amount) {
calculatedDiscount = Math.round(((old_price_amount - price_amount) / old_price_amount) * 100);
}
return ( return (
<> <>
<div <div
@@ -234,8 +240,10 @@ const ProductCard = ({
onMouseLeave={() => setIsHovered(false)} onMouseLeave={() => setIsHovered(false)}
> >
<div className={styles.imageContainer}> <div className={styles.imageContainer}>
{product.discount && ( {(product.discount || calculatedDiscount) && (
<span className={styles.discountBadge}>-{product.discount}%</span> <span className={styles.discountBadge}>
-{product.discount ? product.discount : calculatedDiscount}%
</span>
)} )}
{product.stock === 0 && ( {product.stock === 0 && (
<span className={`${styles.discountBadge} ${styles.outOfStock}`}> <span className={`${styles.discountBadge} ${styles.outOfStock}`}>

View File

@@ -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 { .mobilePhoneGrid {
display: flex !important; display: flex !important;

View File

@@ -18,6 +18,8 @@ const CategoryFilters = ({
onBrandSelect, onBrandSelect,
onBrandDeselect, onBrandDeselect,
onBrandSearchChange, onBrandSearchChange,
sorting = "price_amount-descending",
onSortingChange = () => {},
className, className,
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -123,6 +125,19 @@ const CategoryFilters = ({
/> />
</div> </div>
</div> </div>
{/* Sıralama dropdown'u */}
<div className={styles.sortingContainer} style={{ marginTop: 12 }}>
<label htmlFor="sortingSidebar" className={styles.sortingLabel}>{t("category.sortBy")}: </label>
<select
id="sortingSidebar"
className={styles.sortingSelect}
value={sorting}
onChange={e => onSortingChange(e.target.value)}
>
<option value="price_amount-descending">{t("category.priceHighToLow")}</option>
<option value="price_amount-ascending">{t("category.priceLowToHigh")}</option>
</select>
</div>
</div> </div>
</aside> </aside>
); );

View File

@@ -17,6 +17,7 @@ const useCategoryProducts = ({
selectedFilterBrand, selectedFilterBrand,
minPrice, minPrice,
maxPrice, maxPrice,
sorting,
searchQuery, searchQuery,
}) => { }) => {
const [products, setProducts] = useState([]); const [products, setProducts] = useState([]);
@@ -35,6 +36,7 @@ const useCategoryProducts = ({
selectedFilterBrand && `fbrand-${selectedFilterBrand}`, selectedFilterBrand && `fbrand-${selectedFilterBrand}`,
minPrice && `min-${minPrice}`, minPrice && `min-${minPrice}`,
maxPrice && `max-${maxPrice}`, maxPrice && `max-${maxPrice}`,
sorting && `sort-${sorting}`,
].filter(Boolean); ].filter(Boolean);
return parts.join("|") || "none"; return parts.join("|") || "none";
}, [ }, [
@@ -45,6 +47,7 @@ const useCategoryProducts = ({
selectedFilterBrand, selectedFilterBrand,
minPrice, minPrice,
maxPrice, maxPrice,
sorting,
]); ]);
const fetchParams = useMemo( const fetchParams = useMemo(
@@ -54,8 +57,9 @@ const useCategoryProducts = ({
brands: selectedFilterBrand || undefined, brands: selectedFilterBrand || undefined,
min_price: minPrice || undefined, min_price: minPrice || undefined,
max_price: maxPrice || undefined, max_price: maxPrice || undefined,
sorting: sorting || undefined,
}), }),
[currentPage, selectedFilterBrand, minPrice, maxPrice], [currentPage, selectedFilterBrand, minPrice, maxPrice, sorting],
); );
const fetchKey = `${contextId}-p${currentPage}`; const fetchKey = `${contextId}-p${currentPage}`;

View File

@@ -29,6 +29,7 @@ const CategoryPage = () => {
currentPage: 1, currentPage: 1,
minPrice: "", minPrice: "",
maxPrice: "", maxPrice: "",
sorting: "price_amount-descending",
}); });
const [filterState, setFilterState] = useState({ const [filterState, setFilterState] = useState({
@@ -94,6 +95,7 @@ const CategoryPage = () => {
selectedFilterBrand: filterState.selectedFilterBrand, selectedFilterBrand: filterState.selectedFilterBrand,
minPrice: pageState.minPrice, minPrice: pageState.minPrice,
maxPrice: pageState.maxPrice, maxPrice: pageState.maxPrice,
sorting: pageState.sorting,
searchQuery, searchQuery,
}); });
const isMobilePhoneView = const isMobilePhoneView =
@@ -293,6 +295,22 @@ const CategoryPage = () => {
> >
{t("category.filter")} <LuFilter /> {t("category.filter")} <LuFilter />
</button> </button>
<div className={styles.sortingContainer}>
<label htmlFor="sorting" className={styles.sortingLabel}>{t("category.sortBy")}: </label>
<select
id="sorting"
className={styles.sortingSelect}
value={pageState.sorting}
onChange={e => {
setPageState(prev => ({ ...prev, sorting: e.target.value, currentPage: 1 }));
setAllProducts([]);
setHasMore(true);
}}
>
<option value="price_amount-descending">{t("category.priceHighToLow")}</option>
<option value="price_amount-ascending">{t("category.priceLowToHigh")}</option>
</select>
</div>
</div> </div>
<Drawer <Drawer
@@ -335,6 +353,12 @@ const CategoryPage = () => {
onBrandSearchChange={(query) => onBrandSearchChange={(query) =>
setFilterState((prev) => ({ ...prev, brandSearchQuery: query })) setFilterState((prev) => ({ ...prev, brandSearchQuery: query }))
} }
sorting={pageState.sorting}
onSortingChange={(value) => {
setPageState((prev) => ({ ...prev, sorting: value, currentPage: 1 }));
setAllProducts([]);
setHasMore(true);
}}
/> />
</Drawer> </Drawer>
@@ -373,6 +397,12 @@ const CategoryPage = () => {
onBrandSearchChange={(query) => onBrandSearchChange={(query) =>
setFilterState((prev) => ({ ...prev, brandSearchQuery: query })) setFilterState((prev) => ({ ...prev, brandSearchQuery: query }))
} }
sorting={pageState.sorting}
onSortingChange={(value) => {
setPageState((prev) => ({ ...prev, sorting: value, currentPage: 1 }));
setAllProducts([]);
setHasMore(true);
}}
/> />
<main className={styles.productsContainer}> <main className={styles.productsContainer}>

View File

@@ -286,6 +286,8 @@
justify-content: space-between; justify-content: space-between;
padding-bottom: 8px; padding-bottom: 8px;
border-bottom: 1px solid #f1f1f1; border-bottom: 1px solid #f1f1f1;
text-decoration: none;
color: inherit;
&:last-child { &:last-child {
border-bottom: none; border-bottom: none;
@@ -311,6 +313,27 @@
padding: 16px 20px; padding: 16px 20px;
background: #fff; background: #fff;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08); 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 { .descriptionHeader {
@@ -358,18 +381,28 @@
overflow: hidden; overflow: hidden;
} }
.productDescriptionWrapper {
display: flex;
flex-direction: column;
align-items: center;
}
.readMoreBtn { .readMoreBtn {
background: none; background: none;
border: none; border: none;
color: #1890ff; color: #fff;
font-weight: 500; font-weight: bold;
cursor: pointer; cursor: pointer;
padding: 8px 0 0 0; padding: 8px 0 0 0;
font-size: 14px; font-size: 16px;
display: inline-block; display: inline-block;
text-shadow: 0 1px 8px #000, 0 1px 1px #000;
letter-spacing: 0.5px;
margin-top: 8px;
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
opacity: 0.85;
} }
} }

View File

@@ -26,6 +26,7 @@ import {
import ImageCarousel from "../../components/ProductCard/imageCarousel/index"; import ImageCarousel from "../../components/ProductCard/imageCarousel/index";
import Loader from "../../components/Loader/index"; import Loader from "../../components/Loader/index";
import { Result, Button } from "antd"; import { Result, Button } from "antd";
import { div } from "framer-motion/client";
const ProductPage = ({ const ProductPage = ({
productProp, productProp,
@@ -351,10 +352,14 @@ const ProductPage = ({
)} */} )} */}
{product.brand?.name && ( {product.brand?.name && (
<div className={styles.metaItem}> <a
href={`/brands/${product.brand.id}`}
target="_blank"
className={styles.metaItem}
>
<span className={styles.metaLabel}>{t("order.brand")}</span> <span className={styles.metaLabel}>{t("order.brand")}</span>
<span className={styles.metaValue}>{product.brand.name}</span> <span className={styles.metaValue}>{product.brand.name}</span>
</div> </a>
)} )}
{product.channel?.[0]?.name && ( {product.channel?.[0]?.name && (
@@ -385,29 +390,31 @@ const ProductPage = ({
{t("product.description")} {t("product.description")}
</p> </p>
</div> </div>
<div <div className={styles.productDescriptionWrapper}>
ref={descRef} <div
className={`${styles.productDescription} ${ ref={descRef}
!isDescExpanded ? styles.productDescriptionCollapsed : "" className={`${styles.productDescription} ${
}`} !isDescExpanded ? styles.productDescriptionCollapsed : ""
dangerouslySetInnerHTML={{ __html: product.description }} }`}
/> dangerouslySetInnerHTML={{ __html: product.description }}
{showReadMore && !isDescExpanded && ( />
<button {showReadMore && !isDescExpanded && (
className={styles.readMoreBtn} <button
onClick={() => setIsDescExpanded(true)} className={styles.readMoreBtn}
> onClick={() => setIsDescExpanded(true)}
{t("product.readMore")} >
</button> {t("product.readMore")}
)} </button>
{showReadMore && isDescExpanded && ( )}
<button {showReadMore && isDescExpanded && (
className={styles.readMoreBtn} <button
onClick={() => setIsDescExpanded(false)} className={styles.readMoreBtn}
> onClick={() => setIsDescExpanded(false)}
{t("product.readLess")} >
</button> {t("product.readLess")}
)} </button>
)}
</div>
</div> </div>
)} )}
</div> </div>