////////////////////////////////////////////////////////////
const useQuery = () => {
return new URLSearchParams(useLocation().search);
};
const ProductCategoriesMaster =(props)=>{
const { } =props
const [categories, setCategories] = useState([])
const [categorySelected, setCategorySelected] = useState()
const user_lang = commonFunc.getCookies('user_lang') || 'vi';
const [reloadPage, setReloadPage] = useState(_reload_page) // true : good for SEO
const [isMobile, setIsMobile] = useState(false);
const [loading, setLoading] = useState(false)
const [userInfo, setUserInfo] = useState()
const configDefault = { token: _token_encode, lang: user_lang, branch_no:(userInfo?.branch_no |0) };
const [config, setConfig] = useState(configDefault)
const scriptUrls = [
'/static/components/POS/product_categories/index.js',
];
useEffect(()=>{
if (window?.ProductCategoriesMaster ==null && _extension=='js'){
loadScripts(scriptUrls)
.then(() => {
console.log("All scripts loaded successfully.");
})
.catch(err => {
console.error(err); // Xử lý lỗi nếu có
});
}
const categoriesDefault = JSON.parse(document.getElementById('categories-data').textContent);
setCategories(categoriesDefault);
const handleResize = () => {
setIsMobile(window.innerWidth < 1280);
};
window.addEventListener('resize', handleResize);
handleResize(); // Gọi ngay một lần để thiết lập trạng thái ban đầu
fetchUserInfo()
return () => {
window.removeEventListener('resize', handleResize);
};
},[])
const fetchUserInfo = async () => {
try {
setLoading(true)
const url =`/api/get-user-info`
const resultObj = await commonApi.APIGETByURL(url, config)
if (resultObj.status == 200){
let dataTmp = resultObj?.data
setUserInfo(dataTmp)
const configDefault = { token: _token_encode, lang: user_lang, branch_no:(dataTmp?.branch_no |0) };
setConfig(configDefault)
}
//console.log(dataInit )
} catch (e) {
setUserInfo(null)
console.log("ERROR get-user-info" )
}finally{
setLoading(false)
}
};
const containerStyle = {
display: 'flex',
flexDirection: 'column',
width: '100%',
};
const productCategoryStyle = {
flex: isMobile ? '0 0 100%' : '0 0 60%', // 100% width on mobile, 60% on larger screens
padding: '0px 0px 0px 0px',
};
return (
);
}
const CategoryTreeComponent = (props) => {
const { categories } = props;
const { ListGroup, Tabs, Tab } = ReactBootstrap;
// Lấy type từ URL (ví dụ: ?type=product_tag) để xác định Tab đang active
const urlParams = new URLSearchParams(window.location.search);
const activeType = urlParams.get('type') || 'product_cat';
const rootCategories = useMemo(() => {
if (!categories) return [];
const allIds = new Set(categories.map(cat => cat.id?.toString()));
const roots = categories.filter(cat => !allIds.has(cat.parent_id?.toString()));
return [...roots].sort((a, b) =>
(a.name || "").localeCompare((b.name || ""), 'vi', {
sensitivity: 'base',
numeric: true
})
);
}, [categories]);
const isOnlyOneRoot = rootCategories?.length === 1;
// Hàm xử lý chuyển Tab và Redirect
const handleTabSelect = (key) => {
// Redirect sang URL mới với query type tương ứng
window.location.href = `/categories?type=${key}`;
};
if (!categories || categories.length === 0) {
return (
{/* Vẫn hiện Tabs ngay cả khi không có data để người dùng chuyển đổi lại */}
Không tìm thấy dữ liệu.
);
}
return (
{/* Hệ thống Tabs bao quanh ListGroup */}
Category}>
{rootCategories.map((cat) => (
))}
Tags}>
{/* Nội dung ListGroup cho Tags sẽ hiển thị ở đây.
Vì cùng dùng chung cấu trúc đệ quy, logic map sẽ tương tự như trên.
*/}
{rootCategories.map((cat) => (
))}
);
};
const CategoryNode = ({ category, allCategories, level = 0, isOnlyRoot = false }) => {
const [open, setOpen] = useState(isOnlyRoot);
const { ListGroup, Collapse } = ReactBootstrap;
// Lấy slug từ URL để highlight
const urlParams = new URLSearchParams(window.location.search);
const currentSlug = urlParams.get('slug');
const isActive = currentSlug === category.slug;
// Tìm và SORT các con đồng cấp theo tên
const children = useMemo(() => {
return allCategories
.filter(item => item.parent_id?.toString() === category.id?.toString())
.sort((a, b) =>
(a.name || "").localeCompare((b.name || ""), 'vi', {
sensitivity: 'base',
numeric: true
})
);
}, [category.id, allCategories]);
const hasChildren = children.length > 0;
const paddingLeft = level * 20;
// Tự động mở cấp cha nếu có con đang active (Tùy chọn)
useEffect(() => {
const hasActiveChild = children.some(child => child.slug === currentSlug);
if (hasActiveChild) setOpen(true);
}, [currentSlug, children]);
return (
hasChildren && setOpen(!open)}
>
{hasChildren ? (
) : (
)}
{isActive &&
}
{hasChildren && (
{children.map((child) => (
))}
)}
);
};