order report by food

This commit is contained in:
2026-06-18 21:51:39 +03:30
parent 3ba1290e56
commit e89a74035b
13 changed files with 282 additions and 2 deletions
+51
View File
@@ -0,0 +1,51 @@
import { type FC, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import Table from '@/components/Table';
import Filters from '@/components/Filters';
import { useGetFoodOrderReport } from './hooks/useFoodOrderReportData';
import { useFoodOrderReportFilters } from './hooks/useFoodOrderReportFilters';
import { getFoodOrderReportTableColumns } from './components/FoodOrderReportTableColumns';
import { useFoodOrderReportFiltersFields } from './components/FoodOrderReportFiltersFields';
import type { FoodOrderReportItem } from './types/Types';
import type { RowDataType } from '@/components/types/TableTypes';
const FoodOrderReport: FC = () => {
const { t } = useTranslation('global');
const { filters, apiParams, handleFiltersChange } = useFoodOrderReportFilters();
const { data: reportData, isLoading } = useGetFoodOrderReport(apiParams);
const columns = getFoodOrderReportTableColumns();
const filterFields = useFoodOrderReportFiltersFields();
const reportItems = useMemo(() => {
return (reportData?.data || []).map((item) => ({
...item,
id: item.food.id,
}));
}, [reportData?.data]);
return (
<div className='mt-5'>
<div className='flex justify-between items-center'>
<h1 className='text-lg font-light'>{t('submenu.orders_food_report')}</h1>
</div>
<div className='mt-8'>
<Filters
fields={filterFields}
onChange={handleFiltersChange}
initialValues={filters}
showClearButton
/>
</div>
<Table<FoodOrderReportItem & RowDataType>
columns={columns}
data={reportItems as (FoodOrderReportItem & RowDataType)[]}
isloading={isLoading}
/>
</div>
);
};
export default FoodOrderReport;
@@ -0,0 +1,27 @@
import { useMemo } from 'react';
import type { FieldType } from '@/components/Filters';
export const useFoodOrderReportFiltersFields = (): FieldType[] => {
return useMemo(() => [
{
type: 'datetime',
name: 'from',
placeholder: 'از تاریخ',
},
{
type: 'datetime',
name: 'to',
placeholder: 'تا تاریخ',
},
{
type: 'select',
name: 'sortBy',
placeholder: 'مرتب‌سازی',
defaultValue: 'count',
options: [
{ label: 'تعداد سفارش', value: 'count' },
{ label: 'قیمت', value: 'price' },
],
},
], []);
};
@@ -0,0 +1,28 @@
import type { ColumnType } from '@/components/types/TableTypes';
import type { FoodOrderReportItem } from '../types/Types';
import { formatFaNumber, formatPrice } from '@/helpers/func';
export const getFoodOrderReportTableColumns = (): ColumnType<FoodOrderReportItem>[] => {
return [
{
key: 'foodTitle',
title: 'غذا',
render: (item: FoodOrderReportItem) => item.food.title || '-',
},
{
key: 'count',
title: 'تعداد',
render: (item: FoodOrderReportItem) => formatFaNumber(item.count),
},
{
key: 'price',
title: 'قیمت',
render: (item: FoodOrderReportItem) => formatPrice(item.price),
},
{
key: 'totalPrice',
title: 'جمع کل',
render: (item: FoodOrderReportItem) => formatPrice(item.totalPrice),
},
];
};
@@ -0,0 +1,10 @@
import { useQuery } from '@tanstack/react-query';
import * as api from '../service/OrderService';
import type { GetFoodOrderReportParams } from '../service/OrderService';
export const useGetFoodOrderReport = (params?: GetFoodOrderReportParams) => {
return useQuery({
queryKey: ['food-order-report', params],
queryFn: () => api.getFoodOrderReport(params),
});
};
@@ -0,0 +1,37 @@
import { useState, useMemo } from 'react';
import type { FilterValues } from '@/components/Filters';
import type { GetFoodOrderReportParams } from '../service/OrderService';
export const useFoodOrderReportFilters = () => {
const [filters, setFilters] = useState<FilterValues>({
sortBy: 'count',
});
const apiParams = useMemo(() => {
const params: GetFoodOrderReportParams = {};
if (filters.from) {
params.from = filters.from as string;
}
if (filters.to) {
params.to = filters.to as string;
}
if (filters.sortBy) {
params.sortBy = filters.sortBy as GetFoodOrderReportParams['sortBy'];
}
return params;
}, [filters]);
const handleFiltersChange = (newFilters: FilterValues) => {
setFilters(newFilters);
};
return {
filters,
apiParams,
handleFiltersChange,
};
};
+17 -1
View File
@@ -1,5 +1,5 @@
import axios from "@/config/axios";
import type { GetOrdersResponse, GetOrderByIdResponse } from "../types/Types";
import type { GetOrdersResponse, GetOrderByIdResponse, GetFoodOrderReportResponse } from "../types/Types";
export interface GetOrdersParams {
page?: number;
@@ -82,3 +82,19 @@ export const refundPayment = async (
): Promise<void> => {
await axios.post(`/admin/orders/${orderId}/refund`, params);
};
export interface GetFoodOrderReportParams {
from?: string;
to?: string;
sortBy?: 'count' | 'price';
}
export const getFoodOrderReport = async (
params?: GetFoodOrderReportParams
) => {
const { data } = await axios.get<GetFoodOrderReportResponse>(
'/admin/orders/report/by-food',
{ params }
);
return data;
};
+14
View File
@@ -220,3 +220,17 @@ export interface Order extends RowDataType {
export type GetOrdersResponse = IResponse<Order[]>;
export type GetOrderByIdResponse = IResponse<Order>;
export interface FoodOrderReportFood {
id: string;
title: string;
}
export interface FoodOrderReportItem extends RowDataType {
food: FoodOrderReportFood;
count: number;
price: number;
totalPrice: number;
}
export type GetFoodOrderReportResponse = IResponse<FoodOrderReportItem[]>;