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 (
-