This commit is contained in:
2026-06-18 22:07:50 +03:30
parent d4b6d85cb5
commit 13c6de66b3
12 changed files with 236 additions and 1 deletions
+1
View File
@@ -26,6 +26,7 @@ export const Pages = {
add: "/orders/add",
detail: "/orders/detail/",
foodReport: "/orders/report/by-food",
dailyReport: "/orders/report/by-day",
},
customers: {
list: "/customers/list",
+1
View File
@@ -98,6 +98,7 @@
"categories": "دسته بندی ها",
"orders_list": "لیست سفارشات",
"orders_food_report": "گزارش فروش به تفکیک غذا",
"orders_daily_report": "گزارش فروش روزانه",
"add_order": "افزودن سفارش",
"discount_list": "لیست تخفیف",
"add_discount": "افزودن تخفیف",
+81
View File
@@ -0,0 +1,81 @@
import { type FC, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { DocumentDownload } from 'iconsax-react';
import Table from '@/components/Table';
import Filters from '@/components/Filters';
import Button from '@/components/Button';
import { useGetDailyOrderReport } from './hooks/useDailyOrderReportData';
import { useDailyOrderReportFilters } from './hooks/useDailyOrderReportFilters';
import { getDailyOrderReportTableColumns } from './components/DailyOrderReportTableColumns';
import { useDailyOrderReportFiltersFields } from './components/DailyOrderReportFiltersFields';
import { getDailyOrderReport } from './service/OrderService';
import { exportDailyOrderReportExcel } from './utils/exportDailyOrderReportExcel';
import type { DailyOrderReportItem } from './types/Types';
import type { RowDataType } from '@/components/types/TableTypes';
const DailyOrderReport: FC = () => {
const { t } = useTranslation('global');
const { filters, apiParams, handleFiltersChange } = useDailyOrderReportFilters();
const { data: reportData, isLoading } = useGetDailyOrderReport(apiParams);
const [isExporting, setIsExporting] = useState(false);
const columns = getDailyOrderReportTableColumns();
const filterFields = useDailyOrderReportFiltersFields();
const reportItems = useMemo(() => {
return (reportData?.data || []).map((item) => ({
...item,
id: item.date,
}));
}, [reportData?.data]);
const handleExportExcel = async () => {
setIsExporting(true);
try {
const response = await getDailyOrderReport(apiParams);
const items = (response?.data || []).map((item) => ({
...item,
id: item.date,
}));
exportDailyOrderReportExcel(items);
} finally {
setIsExporting(false);
}
};
return (
<div className='mt-5'>
<div className='flex justify-between items-center'>
<h1 className='text-lg font-light'>{t('submenu.orders_daily_report')}</h1>
<Button
className='w-fit px-4'
isloading={isExporting}
disabled={isLoading}
onClick={handleExportExcel}
>
<div className='flex items-center gap-2'>
<DocumentDownload size={18} color='#fff' />
<span>خروجی Excel</span>
</div>
</Button>
</div>
<div className='mt-8'>
<Filters
fields={filterFields}
onChange={handleFiltersChange}
initialValues={filters}
showClearButton
/>
</div>
<Table<DailyOrderReportItem & RowDataType>
columns={columns}
data={reportItems as (DailyOrderReportItem & RowDataType)[]}
isloading={isLoading}
/>
</div>
);
};
export default DailyOrderReport;
@@ -0,0 +1,28 @@
import { useMemo } from 'react';
import type { FieldType } from '@/components/Filters';
export const useDailyOrderReportFiltersFields = (): FieldType[] => {
return useMemo(() => [
{
type: 'datetime',
name: 'from',
placeholder: 'از تاریخ',
},
{
type: 'datetime',
name: 'to',
placeholder: 'تا تاریخ',
},
{
type: 'select',
name: 'sortBy',
placeholder: 'مرتب‌سازی',
defaultValue: 'date',
options: [
{ label: 'تاریخ', value: 'date' },
{ label: 'مبلغ کل', value: 'totalAmount' },
{ label: 'تعداد سفارش', value: 'orderCount' },
],
},
], []);
};
@@ -0,0 +1,23 @@
import type { ColumnType } from '@/components/types/TableTypes';
import type { DailyOrderReportItem } from '../types/Types';
import { formatDateShort, formatFaNumber, formatPrice } from '@/helpers/func';
export const getDailyOrderReportTableColumns = (): ColumnType<DailyOrderReportItem>[] => {
return [
{
key: 'date',
title: 'تاریخ',
render: (item: DailyOrderReportItem) => formatDateShort(item.date),
},
{
key: 'orderCount',
title: 'تعداد سفارش',
render: (item: DailyOrderReportItem) => formatFaNumber(item.orderCount),
},
{
key: 'totalAmount',
title: 'مبلغ کل',
render: (item: DailyOrderReportItem) => formatPrice(item.totalAmount),
},
];
};
@@ -0,0 +1,10 @@
import { useQuery } from '@tanstack/react-query';
import * as api from '../service/OrderService';
import type { GetDailyOrderReportParams } from '../service/OrderService';
export const useGetDailyOrderReport = (params?: GetDailyOrderReportParams) => {
return useQuery({
queryKey: ['daily-order-report', params],
queryFn: () => api.getDailyOrderReport(params),
});
};
@@ -0,0 +1,37 @@
import { useState, useMemo } from 'react';
import type { FilterValues } from '@/components/Filters';
import type { GetDailyOrderReportParams } from '../service/OrderService';
export const useDailyOrderReportFilters = () => {
const [filters, setFilters] = useState<FilterValues>({
sortBy: 'date',
});
const apiParams = useMemo(() => {
const params: GetDailyOrderReportParams = {};
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 GetDailyOrderReportParams['sortBy'];
}
return params;
}, [filters]);
const handleFiltersChange = (newFilters: FilterValues) => {
setFilters(newFilters);
};
return {
filters,
apiParams,
handleFiltersChange,
};
};
+22 -1
View File
@@ -1,5 +1,10 @@
import axios from "@/config/axios";
import type { GetOrdersResponse, GetOrderByIdResponse, GetFoodOrderReportResponse } from "../types/Types";
import type {
GetOrdersResponse,
GetOrderByIdResponse,
GetFoodOrderReportResponse,
GetDailyOrderReportResponse,
} from "../types/Types";
export interface GetOrdersParams {
page?: number;
@@ -98,3 +103,19 @@ export const getFoodOrderReport = async (
);
return data;
};
export interface GetDailyOrderReportParams {
from?: string;
to?: string;
sortBy?: 'date' | 'totalAmount' | 'orderCount';
}
export const getDailyOrderReport = async (
params?: GetDailyOrderReportParams
) => {
const { data } = await axios.get<GetDailyOrderReportResponse>(
'/admin/orders/report/by-day',
{ params }
);
return data;
};
+8
View File
@@ -234,3 +234,11 @@ export interface FoodOrderReportItem extends RowDataType {
}
export type GetFoodOrderReportResponse = IResponse<FoodOrderReportItem[]>;
export interface DailyOrderReportItem extends RowDataType {
date: string;
orderCount: number;
totalAmount: number;
}
export type GetDailyOrderReportResponse = IResponse<DailyOrderReportItem[]>;
@@ -0,0 +1,18 @@
import * as XLSX from 'xlsx';
import type { DailyOrderReportItem } from '../types/Types';
import { formatDateShort } from '@/helpers/func';
export const exportDailyOrderReportExcel = (items: DailyOrderReportItem[]) => {
const rows = items.map((item) => ({
تاریخ: formatDateShort(item.date),
'تعداد سفارش': item.orderCount,
'مبلغ کل': item.totalAmount,
}));
const worksheet = XLSX.utils.json_to_sheet(rows);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, 'گزارش فروش روزانه');
const fileName = `daily-order-report-${new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-')}.xlsx`;
XLSX.writeFile(workbook, fileName);
};
+2
View File
@@ -16,6 +16,7 @@ import CreateCustomer from '@/pages/customers/Create'
import OrdersList from '@/pages/orders/List'
import OrderDetails from '@/pages/orders/Details'
import FoodOrderReport from '@/pages/orders/FoodOrderReport'
import DailyOrderReport from '@/pages/orders/DailyOrderReport'
import CommentsList from '@/pages/comments/List'
import CommentsDetails from '@/pages/comments/Details'
import ScheduleList from '@/pages/schedule/List'
@@ -73,6 +74,7 @@ const MainRouter: FC = () => {
<Route path={Pages.customers.add} element={<CreateCustomer />} />
<Route path={Pages.orders.list} element={<OrdersList />} />
<Route path={Pages.orders.foodReport} element={<FoodOrderReport />} />
<Route path={Pages.orders.dailyReport} element={<DailyOrderReport />} />
<Route path={Pages.orders.detail + ':id'} element={<OrderDetails />} />
<Route path={Pages.comments.list} element={<CommentsList />} />
<Route path={Pages.comments.detail + ':id'} element={<CommentsDetails />} />
+5
View File
@@ -38,6 +38,11 @@ const OrdersSubMenu: FC = () => {
isActive={isActive('report/by-food')}
link={Pages.orders.foodReport}
/>
<SubMenuItem
title={t('submenu.orders_daily_report')}
isActive={isActive('report/by-day')}
link={Pages.orders.dailyReport}
/>
{/* <SubMenuItem
title={t('submenu.add_order')}
isActive={isActive('add')}