diff --git a/.env b/.env index a853243..28d3762 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -VITE_TOKEN_NAME = 'negareh_t' -VITE_REFRESH_TOKEN_NAME = 'negareh_rt' -# VITE_API_BASE_URL = 'https://negare-api.danakcorp.com' +VITE_TOKEN_NAME = negareh_t +VITE_REFRESH_TOKEN_NAME = negareh_rt +# VITE_API_BASE_URL = https://negare-api.danakcorp.com VITE_API_BASE_URL = 'http://localhost:4000' diff --git a/index.html b/index.html index 26f94ef..38a1cff 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,9 @@ - negareh-console + + پنل کاربری +
diff --git a/src/pages/invoice/Detail.tsx b/src/pages/invoice/Detail.tsx index bf95435..c2cc9f5 100644 --- a/src/pages/invoice/Detail.tsx +++ b/src/pages/invoice/Detail.tsx @@ -20,6 +20,7 @@ interface TableInvoiceItem extends RowDataType { id: string image: string | null title: string + description: string quantity: string unitPrice: string discountValue: string @@ -67,6 +68,7 @@ const InvoiceDetail: FC = () => { id: item.id, image: item.product?.images?.[0] ?? null, title: item.product?.title || '-', + description: item.description || '-', quantity: String(item.quantity), unitPrice: formatPrice(item.unitPrice), discountValue: discounts.value, @@ -95,6 +97,10 @@ const InvoiceDetail: FC = () => { key: 'title', title: 'عنوان', }, + { + key: 'description', + title: 'توضیحات', + }, { key: 'quantity', title: 'تعداد', diff --git a/src/pages/request/components/ProductsSelect.tsx b/src/pages/request/components/ProductsSelect.tsx index 49cb65d..e66151f 100644 --- a/src/pages/request/components/ProductsSelect.tsx +++ b/src/pages/request/components/ProductsSelect.tsx @@ -1,27 +1,184 @@ -import { type FC, type SelectHTMLAttributes } from 'react' -import { useGetProducts } from '../hooks/useRequestData' +import { type ChangeEvent, type FC, type SelectHTMLAttributes, useEffect, useMemo, useState } from 'react' +import { useGetCategories, useGetProducts } from '../hooks/useRequestData' import Select from '@/components/Select' +import type { CategoryType, ProductType } from '../type/Types' type Props = { error_text?: string, + onProductSelect?: (product: ProductType | undefined) => void, } & SelectHTMLAttributes -const ProductsSelect: FC = (props) => { +const findCategoryById = (categories: CategoryType[], targetId: string): CategoryType | null => { + for (const category of categories) { + if (category.id === targetId) { + return category + } - const { data } = useGetProducts() + if (category.children?.length) { + const found = findCategoryById(category.children, targetId) + if (found) return found + } + } + + return null +} + +const findCategoryPath = (categories: CategoryType[], targetId: string): string[] | null => { + for (const category of categories) { + if (category.id === targetId) { + return [category.id] + } + + if (category.children?.length) { + const childPath = findCategoryPath(category.children, targetId) + if (childPath) { + return [category.id, ...childPath] + } + } + } + + return null +} + +const getCategoriesAtLevel = ( + categories: CategoryType[], + level: number, + selectedPath: string[], +): CategoryType[] => { + if (level === 0) return categories + + const parentId = selectedPath[level - 1] + if (!parentId) return [] + + const parent = findCategoryById(categories, parentId) + return parent?.children ?? [] +} + +const ProductsSelect: FC = (props) => { + const { error_text, onProductSelect, value, onChange, ...rest } = props + const [selectedPath, setSelectedPath] = useState([]) + + const { data: categoriesData } = useGetCategories() + const categories = categoriesData?.data ?? [] + + const levelsToShow = useMemo(() => { + const levels = [0] + + for (let level = 0; level < selectedPath.length; level++) { + const category = findCategoryById(categories, selectedPath[level]) + if (category?.children?.length) { + levels.push(level + 1) + } + } + + return levels + }, [categories, selectedPath]) + + const activeCategoryId = selectedPath.at(-1) ?? '' + const activeCategory = activeCategoryId + ? findCategoryById(categories, activeCategoryId) + : null + const isLeafCategory = !!activeCategory && !(activeCategory.children?.length) + const canLoadProducts = isLeafCategory + + const { data: productsData } = useGetProducts(activeCategoryId || undefined, { + enabled: canLoadProducts, + }) + + const needsResolve = !!value && !selectedPath.length + const { data: resolveProductsData } = useGetProducts(undefined, { + enabled: needsResolve, + }) + + const productItems = useMemo( + () => (productsData?.data ?? []).map((product) => ({ + label: product.title, + value: product.id, + })), + [productsData], + ) + + useEffect(() => { + if (!value) { + setSelectedPath([]) + return + } + + if (selectedPath.length || !categories.length) return + + const product = resolveProductsData?.data?.find((item) => item.id === value) + if (!product?.category) return + + const categoryId = typeof product.category === 'string' + ? product.category + : product.category.id + + const path = findCategoryPath(categories, categoryId) + if (!path) return + + setSelectedPath(path) + }, [value, resolveProductsData, categories, selectedPath.length]) + + useEffect(() => { + if (!value) { + onProductSelect?.(undefined) + return + } + + const product = productsData?.data?.find((item) => item.id === value) + ?? resolveProductsData?.data?.find((item) => item.id === value) + + onProductSelect?.(product) + }, [value, productsData, resolveProductsData, onProductSelect]) + + const emitProductChange = (nextValue: string) => { + onChange?.({ + target: { value: nextValue }, + } as ChangeEvent) + } + + const handleCategoryChange = (level: number, nextValue: string) => { + setSelectedPath((prev) => [...prev.slice(0, level), nextValue]) + emitProductChange('') + } + + const handleProductChange = (e: ChangeEvent) => { + onChange?.(e) + + const product = productsData?.data?.find((item) => item.id === e.target.value) + onProductSelect?.(product) + } return ( - handleCategoryChange(level, e.target.value)} + /> + ) + })} +