initial commit

This commit is contained in:
2025-09-24 20:32:28 +05:00
commit 1759326253
184 changed files with 23284 additions and 0 deletions

View File

@@ -0,0 +1,234 @@
.dropdownContainer {
@media screen and (max-width: 1023px) {
display: none;
}
}
.navButton {
display: flex;
gap: 5px;
border: none;
padding-top: 0.25rem;
padding-bottom: 0.25rem;
padding-left: 0.875rem;
padding-right: 0.875rem;
justify-content: center;
align-items: center;
border-radius: 0.5rem;
height: 2.5rem;
font-size: 0.875rem;
color: #4b5563;
background-color: transparent;
font-weight: 600;
cursor: pointer;
&:hover {
background-color: #f3f4f6;
}
}
.dropdownWrapper {
position: relative;
}
.dropdownPanel {
position: absolute;
top: 100%;
margin-top: 8px;
z-index: 50;
display: flex;
background: white;
border: 1px solid #e5e7eb;
border-radius: 6px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
box-sizing: border-box;
width: 1366px;
padding: 0 1.375rem;
}
.categoriesList {
flex: 1;
max-height: 500px;
overflow-y: auto;
border-right: 1px solid #ebe7eb;
padding: 20px;
display: flex;
flex-direction: column;
gap: 5px;
// &::-webkit-scrollbar {
// width: 6px;
// }
&::-webkit-scrollbar-track {
background: #e5e7eb;
}
&::-webkit-scrollbar-thumb {
background: #d1d5db;
}
.title {
&:hover {
color: #888888;
}
&:active {
color: #888888;
}
}
}
.categoryItem {
display: flex;
align-items: center;
gap: 8px;
padding: 6px;
cursor: pointer;
transition: background-color 0.2s;
border: 1px solid #3615371a;
border-radius: 6px;
&:hover {
background-color: #f9fafb;
}
&.active {
background-color: #f3f4f6;
}
.icon {
font-size: 14px;
}
.title {
font-size: 14px;
&:hover {
color: #888888;
}
}
}
.contentPanel {
flex: 3;
padding: 16px;
max-height: 400px;
overflow-y: hidden;
// &::-webkit-scrollbar {
// width: 6px;
// }
&::-webkit-scrollbar-track {
background: #e5e7eb;
}
&::-webkit-scrollbar-thumb {
background: #d1d5db;
// border-radius: 3px;
}
.title {
cursor: pointer;
color: #361517;
font-size: 24px;
font-weight: 600;
&:hover {
color: #888888;
}
}
}
.column {
display: flex;
flex-direction: column;
flex: 2;
text-align: left;
}
.sectionTitle {
font-size: 16px;
font-weight: 500;
margin-bottom: 12px;
color: #361517;
cursor: pointer;
&:hover {
color: #888888;
}
}
.subcategoryList {
margin-bottom: 24px;
display: flex;
}
.subcategoryItem {
font-size: 14px;
color: #361517;
padding: 4px 0;
cursor: pointer;
transition: color 0.2s;
&:hover {
color: #888888;
}
}
.subCategoriesContainer {
display: flex;
flex-direction: column;
max-height: 360px;
overflow-y: auto;
}
.nestedCategoryContainer:last-child {
margin-bottom: 16px;
}
.nestedCategoryContainer {
margin-bottom: 4px;
}
.nestedCategoryItem {
display: flex;
align-items: center;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background-color: #f3f4f6;
}
.categoryLabel {
flex: 1;
display: flex;
align-items: center;
}
.title {
font-size: 14px;
}
}
.expandButton,
.navigateButton {
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border-radius: 4px;
background-color: transparent;
border: none;
cursor: pointer;
&:hover {
background-color: #e5e7eb;
}
}
.nestedChildren {
margin-top: 4px;
}
.noSubcategories {
color: #6b7280;
font-style: italic;
}

View File

@@ -0,0 +1,204 @@
import React, { useState, useEffect, useRef } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import styles from "./DropdownMenu.module.scss";
import { useGetCategoriesQuery } from "../../app/api/categories";
import { CategoryIcon } from "../Icons";
import { ChevronRight, ChevronDown } from "lucide-react"; // Assuming you have access to lucide-react or similar
const NestedCategory = ({
category,
level = 0,
handleCategorySelect,
closeDropdown,
}) => {
const [isExpanded, setIsExpanded] = useState(false);
const hasChildren = category.children && category.children.length > 0;
const handleClick = (e) => {
e.stopPropagation();
if (hasChildren) {
setIsExpanded(!isExpanded);
} else {
handleCategorySelect(category);
closeDropdown();
}
};
const handleDirectNavigation = (e) => {
e.stopPropagation();
handleCategorySelect(category);
closeDropdown();
};
return (
<div
className={styles.nestedCategoryContainer}
style={{ paddingLeft: `${level * 16}px` }}
>
<div className={styles.nestedCategoryItem} onClick={handleClick}>
<div className={styles.categoryLabel}>
<span className={styles.title}>{category.name}</span>
</div>
{hasChildren && (
<button
className={styles.expandButton}
onClick={(e) => {
e.stopPropagation();
setIsExpanded(!isExpanded);
}}
>
{isExpanded ? (
<ChevronDown size={16} />
) : (
<ChevronRight size={16} />
)}
</button>
)}
{hasChildren && (
<button
className={styles.navigateButton}
onClick={handleDirectNavigation}
title="Go to category"
>
</button>
)}
</div>
{hasChildren && isExpanded && (
<div className={styles.nestedChildren}>
{category.children.map((child) => (
<NestedCategory
key={child.id}
category={child}
level={level + 1}
handleCategorySelect={handleCategorySelect}
closeDropdown={closeDropdown}
/>
))}
</div>
)}
</div>
);
};
const DropdownMenu = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const dropdownRef = useRef(null);
const {
data: categoriesData,
isLoading,
error,
} = useGetCategoriesQuery("tree");
const categories = categoriesData?.data || [];
const [isOpen, setIsOpen] = useState(false);
const [activeMainCategory, setActiveMainCategory] = useState(null);
useEffect(() => {
if (categories.length > 0) {
const defaultCategory =
categories.find((cat) => cat.name === "Aýallar üçin") || categories[0];
setActiveMainCategory(defaultCategory);
}
}, [categories]);
const handleToggle = () => {
setIsOpen(!isOpen);
};
const handleMouseLeave = () => {
if (categories.length > 0) {
const defaultCategory =
categories.find((cat) => cat.name === "Aýallar üçin") || categories[0];
setActiveMainCategory(defaultCategory);
}
};
const handleCategorySelect = (category) => {
navigate(`/category/${category.id}`, { state: { category } });
setIsOpen(false);
};
// Close dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error loading categories</div>;
return (
<div className={styles.dropdownContainer} ref={dropdownRef}>
<button onClick={handleToggle} className={styles.navButton}>
<CategoryIcon />
{t("navbar.category")}
</button>
{isOpen && (
<div className={styles.dropdownWrapper}>
<div className={styles.dropdownPanel} onMouseLeave={handleMouseLeave}>
<div className={styles.categoriesList}>
{categories.map((category) => (
<div
key={category.id}
className={`${styles.categoryItem} ${
activeMainCategory?.id === category.id ? styles.active : ""
}`}
onMouseEnter={() => setActiveMainCategory(category)}
onClick={() => handleCategorySelect(category)}
>
<span className={styles.title}>{category.name}</span>
</div>
))}
</div>
{activeMainCategory && (
<div className={styles.contentPanel}>
<h2
onClick={() => handleCategorySelect(activeMainCategory)}
className={styles.title}
>
{activeMainCategory.name}
</h2>
<div className={styles.subCategoriesContainer}>
{activeMainCategory.children &&
activeMainCategory.children.length > 0 ? (
activeMainCategory.children.map((subcategory) => (
<NestedCategory
key={subcategory.id}
category={subcategory}
handleCategorySelect={handleCategorySelect}
closeDropdown={() => setIsOpen(false)}
/>
))
) : (
<div className={styles.noSubcategories}>
{/* No subcategories available */}
</div>
)}
</div>
</div>
)}
</div>
</div>
)}
</div>
);
};
export default DropdownMenu;