From fff5cd9146f19fb6e876aef575127a5f55c79f1e Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Tue, 21 Jul 2026 11:16:33 +0330 Subject: [PATCH] refactor home --- src/locale/fa.ts | 4 +- src/pages/home/Home.tsx | 78 +--------- src/pages/home/components/CategoryTabs.tsx | 57 +++++++ src/pages/home/components/HomeSection.tsx | 34 +++++ src/pages/home/components/HomeSidebar.tsx | 14 ++ src/pages/home/components/NewOrderCard.tsx | 33 +++++ src/pages/home/components/Orders.tsx | 91 +++--------- src/pages/home/components/ServiceItem.tsx | 13 +- src/pages/home/components/Services.tsx | 140 ++++++++++++++---- .../home/components/SpecialSolutionCard.tsx | 63 ++++++++ src/pages/home/components/StatCard.tsx | 31 ++-- src/pages/home/components/Stats.tsx | 2 +- src/pages/home/components/orderColumns.tsx | 59 ++++++++ .../request/components/ProductsSelect.tsx | 61 +------- src/pages/request/utils/categoryUtils.ts | 94 ++++++++++++ 15 files changed, 524 insertions(+), 250 deletions(-) create mode 100644 src/pages/home/components/CategoryTabs.tsx create mode 100644 src/pages/home/components/HomeSection.tsx create mode 100644 src/pages/home/components/HomeSidebar.tsx create mode 100644 src/pages/home/components/NewOrderCard.tsx create mode 100644 src/pages/home/components/SpecialSolutionCard.tsx create mode 100644 src/pages/home/components/orderColumns.tsx create mode 100644 src/pages/request/utils/categoryUtils.ts diff --git a/src/locale/fa.ts b/src/locale/fa.ts index 1231ee8..333434f 100644 --- a/src/locale/fa.ts +++ b/src/locale/fa.ts @@ -35,7 +35,7 @@ export const fa = { factureCount: "پیش فاکتور تایید نشده", orderCount: "سفارش های در حال انجام", orderDoneCount: "سفارشات انجام شده", - services: "خدمات", + products: "محصولات", submitNewOrder: "درخواست خودرا ثبت کنید", submitNewOrderButton: "درخواست جدید", specialSolution: "راه‌حل اختصاصی برای شما در نگاره!", @@ -47,6 +47,8 @@ export const fa = { freeConsultation: "دریافت مشاوره رایگان", contactUs: "با ما تماس بگیرید", phoneNumber: "۰۸۶۹۱۰۰۹۰۰۱", + noProducts: "خدماتی برای نمایش وجود ندارد.", + inProgressOrders: "سفارش های در حال انجام", }, rial: "ریال", uploadBox: { diff --git a/src/pages/home/Home.tsx b/src/pages/home/Home.tsx index 491d5e5..2b20738 100644 --- a/src/pages/home/Home.tsx +++ b/src/pages/home/Home.tsx @@ -1,84 +1,20 @@ import { type FC } from 'react' -import Stats from './components/Stats' -import Services from './components/Services' +import HomeSidebar from './components/HomeSidebar' import Orders from './components/Orders' -import NewOrderImage from '@/assets/images/new_order.png' -import { t } from '@/locale' -import Button from '@/components/Button' -import { AddSquare, MessageQuestion } from 'iconsax-react' -import { COLORS } from '@/constants/colors' -import SupportImage from '@/assets/images/support1.png' -import { Paths } from '@/config/Paths' -import { Link } from 'react-router-dom' +import Services from './components/Services' +import Stats from './components/Stats' const Home: FC = () => { return ( -
-
+
+
-
-
- -

- {t('home.submitNewOrder')} -

- - - -
- -
-

- {t('home.specialSolution')} -

-

- {t('home.specialSolutionDescription')} -

- -
-
-
-
{t('home.specialSolutionFeature1')}
-
-
-
-
{t('home.specialSolutionFeature2')}
-
-
-
-
{t('home.specialSolutionFeature3')}
-
-
- -
-
+
) } -export default Home \ No newline at end of file +export default Home diff --git a/src/pages/home/components/CategoryTabs.tsx b/src/pages/home/components/CategoryTabs.tsx new file mode 100644 index 0000000..e76dc35 --- /dev/null +++ b/src/pages/home/components/CategoryTabs.tsx @@ -0,0 +1,57 @@ +import { clx } from '@/helpers/utils' +import type { CategoryType } from '@/pages/request/type/Types' +import { getCategoriesAtLevel } from '@/pages/request/utils/categoryUtils' +import { type FC } from 'react' + +type CategoryTabsProps = { + categories: CategoryType[] + levels: number[] + selectedPath: string[] + onSelect: (level: number, categoryId: string) => void +} + +const CategoryTabs: FC = ({ + categories, + levels, + selectedPath, + onSelect, +}) => { + return ( +
+ {levels.map((level) => { + const items = getCategoriesAtLevel(categories, level, selectedPath) + + if (!items.length) return null + + const activeId = selectedPath[level] ?? '' + + return ( +
+
+ {items.map((category) => ( + + ))} +
+
+ ) + })} +
+ ) +} + +export default CategoryTabs diff --git a/src/pages/home/components/HomeSection.tsx b/src/pages/home/components/HomeSection.tsx new file mode 100644 index 0000000..26c416f --- /dev/null +++ b/src/pages/home/components/HomeSection.tsx @@ -0,0 +1,34 @@ +import { clx } from '@/helpers/utils' +import { type FC, type ReactNode } from 'react' + +type HomeSectionProps = { + title?: string + children: ReactNode + className?: string + contentClassName?: string +} + +const HomeSection: FC = ({ + title, + children, + className, + contentClassName, +}) => { + return ( +
+ {title ? ( +

{title}

+ ) : null} +
+ {children} +
+
+ ) +} + +export default HomeSection diff --git a/src/pages/home/components/HomeSidebar.tsx b/src/pages/home/components/HomeSidebar.tsx new file mode 100644 index 0000000..a091d34 --- /dev/null +++ b/src/pages/home/components/HomeSidebar.tsx @@ -0,0 +1,14 @@ +import { type FC } from 'react' +import NewOrderCard from './NewOrderCard' +import SpecialSolutionCard from './SpecialSolutionCard' + +const HomeSidebar: FC = () => { + return ( + + ) +} + +export default HomeSidebar diff --git a/src/pages/home/components/NewOrderCard.tsx b/src/pages/home/components/NewOrderCard.tsx new file mode 100644 index 0000000..c73a04e --- /dev/null +++ b/src/pages/home/components/NewOrderCard.tsx @@ -0,0 +1,33 @@ +import NewOrderImage from '@/assets/images/new_order.png' +import Button from '@/components/Button' +import { Paths } from '@/config/Paths' +import { t } from '@/locale' +import { AddSquare } from 'iconsax-react' +import { type FC } from 'react' +import { Link } from 'react-router-dom' +import HomeSection from './HomeSection' + +const NewOrderCard: FC = () => { + return ( + + +

+ {t('home.submitNewOrder')} +

+ + + +
+ ) +} + +export default NewOrderCard diff --git a/src/pages/home/components/Orders.tsx b/src/pages/home/components/Orders.tsx index ae39546..472eb85 100644 --- a/src/pages/home/components/Orders.tsx +++ b/src/pages/home/components/Orders.tsx @@ -1,83 +1,30 @@ import Table from '@/components/Table' -import { type FC } from 'react' -import { type ColumnType } from '@/components/types/TableTypes' -import { type Order } from './types/OrderTypes' -import { useGetMyOrders } from '@/pages/order/hooks/useOrderData' +import { t } from '@/locale' import { TabMyOrdersEnum } from '@/pages/order/enum/OrderEnum' -import { orderStatusLabels } from '@/pages/order/constants/statusLabels' -import moment from 'moment-jalaali' -import { - OrderNumberRenderer, - OrderTitleRenderer, - OrderDateRenderer, - OrderStatusRenderer, - OrderMessagesRenderer, - OrderViewRenderer -} from './OrderTableComponents' -import type { OrderType } from '@/pages/order/type/Types' - -const mapOrderToRow = (order: OrderType): Order => ({ - id: order.id, - number: String(order.orderNumber ?? order.id), - title: order.title, - creationDate: { - gregorian: moment(order.createdAt).format('YYYY-MM-DD'), - persian: moment(order.createdAt).format('jYYYY/jMM/jDD') - }, - status: orderStatusLabels[order.status] ?? order.status, - unreadMessages: 0 -}) +import { useGetMyOrders } from '@/pages/order/hooks/useOrderData' +import { type FC } from 'react' +import HomeSection from './HomeSection' +import { mapOrderToRow, orderColumns } from './orderColumns' +import { type Order } from './types/OrderTypes' const Orders: FC = () => { const { data, isLoading } = useGetMyOrders(1, TabMyOrdersEnum.IN_PROGRESS) const orders: Order[] = (data?.data ?? []).map(mapOrderToRow) - const columns: ColumnType[] = [ - { - title: 'شماره', - key: 'number', - render: (order) => - }, - { - title: 'عنوان سفارش', - key: 'title', - render: (order) => - }, - { - title: 'تاریخ ایجاد', - key: 'creationDate', - render: (order) => - }, - { - title: 'وضعیت سفارش', - key: 'status', - render: (order) => - }, - { - title: 'پیام ها', - key: 'unreadMessages', - render: (order) => - }, - { - title: '', - key: 'actions', - render: (order) => , - width: '60px' - } - ] - return ( -
-

- سفارش های در حال انجام -

- - - columns={columns} - data={orders} - isLoading={isLoading} - /> -
+ +
+ + columns={orderColumns} + data={orders} + isLoading={isLoading} + /> +
+
) } diff --git a/src/pages/home/components/ServiceItem.tsx b/src/pages/home/components/ServiceItem.tsx index 4be3053..5e1d6b2 100644 --- a/src/pages/home/components/ServiceItem.tsx +++ b/src/pages/home/components/ServiceItem.tsx @@ -1,19 +1,22 @@ -import { type FC } from 'react' -import { type ProductType } from '@/pages/request/type/Types' import PresignedImage from '@/components/PresignedImage' +import { type ProductType } from '@/pages/request/type/Types' +import { type FC } from 'react' const ServiceItem: FC<{ product: ProductType }> = ({ product }) => { return ( -
-
+
+
{product.images?.[0] ? ( ) : null}
+ + {product.title} +
) } diff --git a/src/pages/home/components/Services.tsx b/src/pages/home/components/Services.tsx index 4d5fa6b..a0b7f3b 100644 --- a/src/pages/home/components/Services.tsx +++ b/src/pages/home/components/Services.tsx @@ -1,41 +1,121 @@ import { t } from '@/locale' -import { type FC } from 'react' -import { Swiper, SwiperSlide } from 'swiper/react' +import { + useGetCategories, + useGetProducts, +} from '@/pages/request/hooks/useRequestData' +import type { CategoryType } from '@/pages/request/type/Types' +import { + buildPathToFirstLeaf, + extractList, + getLevelsToShow, +} from '@/pages/request/utils/categoryUtils' +import { type FC, useEffect, useMemo, useState } from 'react' +import CategoryTabs from './CategoryTabs' +import HomeSection from './HomeSection' import ServiceItem from './ServiceItem' -import { useGetProducts } from '@/pages/request/hooks/useRequestData' +const SKELETON_COUNT = 6 + +const CategoryTabsSkeleton: FC = () => ( +
+ {Array.from({ length: 4 }).map((_, index) => ( +
+ ))} +
+) + +const ServicesSkeleton: FC = () => ( +
+ {Array.from({ length: SKELETON_COUNT }).map((_, index) => ( +
+
+
+
+ ))} +
+) const Services: FC = () => { - const { data } = useGetProducts(); + const { data: categoriesData, isLoading: isCategoriesLoading } = + useGetCategories() + const categories = useMemo( + () => extractList(categoriesData), + [categoriesData], + ) + + const [selectedPath, setSelectedPath] = useState([]) + + useEffect(() => { + if (!categories.length || selectedPath.length) return + + setSelectedPath( + buildPathToFirstLeaf(categories, categories[0].id), + ) + }, [categories, selectedPath.length]) + + const levelsToShow = useMemo( + () => getLevelsToShow(categories, selectedPath), + [categories, selectedPath], + ) + + const activeCategoryId = selectedPath.at(-1) ?? '' + + const { data: productsData, isLoading: isProductsLoading } = useGetProducts( + activeCategoryId || undefined, + { enabled: !!activeCategoryId }, + ) + const products = productsData?.data ?? [] + + const handleCategorySelect = (level: number, categoryId: string) => { + setSelectedPath( + buildPathToFirstLeaf( + categories, + categoryId, + selectedPath.slice(0, level), + ), + ) + } + + const isLoading = isCategoriesLoading || isProductsLoading + return ( -
-

- {t('home.services')} -

+ +
+ {isCategoriesLoading ? ( + + ) : categories.length > 0 ? ( + + ) : null} -
- - { - data?.data.map((item) => ( - - - - )) - } - +
+ {isLoading ? ( + + ) : products.length === 0 ? ( +

+ {t('home.noProducts')} +

+ ) : ( +
+ {products.map((item) => ( + + ))} +
+ )} +
- -
+
) } -export default Services \ No newline at end of file +export default Services diff --git a/src/pages/home/components/SpecialSolutionCard.tsx b/src/pages/home/components/SpecialSolutionCard.tsx new file mode 100644 index 0000000..073ae1e --- /dev/null +++ b/src/pages/home/components/SpecialSolutionCard.tsx @@ -0,0 +1,63 @@ +import SupportImage from '@/assets/images/support1.png' +import Button from '@/components/Button' +import { COLORS } from '@/constants/colors' +import { t } from '@/locale' +import { MessageQuestion } from 'iconsax-react' +import { type FC } from 'react' +import HomeSection from './HomeSection' + +const FEATURE_KEYS = [ + 'home.specialSolutionFeature1', + 'home.specialSolutionFeature2', + 'home.specialSolutionFeature3', +] as const + +const SpecialSolutionCard: FC = () => { + return ( + +

+ {t('home.specialSolution')} +

+

+ {t('home.specialSolutionDescription')} +

+ +
    + {FEATURE_KEYS.map((key) => ( +
  • + + {t(key)} +
  • + ))} +
+ +