report
This commit is contained in:
@@ -26,6 +26,7 @@ export const Pages = {
|
|||||||
add: "/orders/add",
|
add: "/orders/add",
|
||||||
detail: "/orders/detail/",
|
detail: "/orders/detail/",
|
||||||
foodReport: "/orders/report/by-food",
|
foodReport: "/orders/report/by-food",
|
||||||
|
dailyReport: "/orders/report/by-day",
|
||||||
},
|
},
|
||||||
customers: {
|
customers: {
|
||||||
list: "/customers/list",
|
list: "/customers/list",
|
||||||
|
|||||||
@@ -98,6 +98,7 @@
|
|||||||
"categories": "دسته بندی ها",
|
"categories": "دسته بندی ها",
|
||||||
"orders_list": "لیست سفارشات",
|
"orders_list": "لیست سفارشات",
|
||||||
"orders_food_report": "گزارش فروش به تفکیک غذا",
|
"orders_food_report": "گزارش فروش به تفکیک غذا",
|
||||||
|
"orders_daily_report": "گزارش فروش روزانه",
|
||||||
"add_order": "افزودن سفارش",
|
"add_order": "افزودن سفارش",
|
||||||
"discount_list": "لیست تخفیف",
|
"discount_list": "لیست تخفیف",
|
||||||
"add_discount": "افزودن تخفیف",
|
"add_discount": "افزودن تخفیف",
|
||||||
|
|||||||
@@ -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,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
import axios from "@/config/axios";
|
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 {
|
export interface GetOrdersParams {
|
||||||
page?: number;
|
page?: number;
|
||||||
@@ -98,3 +103,19 @@ export const getFoodOrderReport = async (
|
|||||||
);
|
);
|
||||||
return data;
|
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;
|
||||||
|
};
|
||||||
|
|||||||
@@ -234,3 +234,11 @@ export interface FoodOrderReportItem extends RowDataType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type GetFoodOrderReportResponse = IResponse<FoodOrderReportItem[]>;
|
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);
|
||||||
|
};
|
||||||
@@ -16,6 +16,7 @@ import CreateCustomer from '@/pages/customers/Create'
|
|||||||
import OrdersList from '@/pages/orders/List'
|
import OrdersList from '@/pages/orders/List'
|
||||||
import OrderDetails from '@/pages/orders/Details'
|
import OrderDetails from '@/pages/orders/Details'
|
||||||
import FoodOrderReport from '@/pages/orders/FoodOrderReport'
|
import FoodOrderReport from '@/pages/orders/FoodOrderReport'
|
||||||
|
import DailyOrderReport from '@/pages/orders/DailyOrderReport'
|
||||||
import CommentsList from '@/pages/comments/List'
|
import CommentsList from '@/pages/comments/List'
|
||||||
import CommentsDetails from '@/pages/comments/Details'
|
import CommentsDetails from '@/pages/comments/Details'
|
||||||
import ScheduleList from '@/pages/schedule/List'
|
import ScheduleList from '@/pages/schedule/List'
|
||||||
@@ -73,6 +74,7 @@ const MainRouter: FC = () => {
|
|||||||
<Route path={Pages.customers.add} element={<CreateCustomer />} />
|
<Route path={Pages.customers.add} element={<CreateCustomer />} />
|
||||||
<Route path={Pages.orders.list} element={<OrdersList />} />
|
<Route path={Pages.orders.list} element={<OrdersList />} />
|
||||||
<Route path={Pages.orders.foodReport} element={<FoodOrderReport />} />
|
<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.orders.detail + ':id'} element={<OrderDetails />} />
|
||||||
<Route path={Pages.comments.list} element={<CommentsList />} />
|
<Route path={Pages.comments.list} element={<CommentsList />} />
|
||||||
<Route path={Pages.comments.detail + ':id'} element={<CommentsDetails />} />
|
<Route path={Pages.comments.detail + ':id'} element={<CommentsDetails />} />
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ const OrdersSubMenu: FC = () => {
|
|||||||
isActive={isActive('report/by-food')}
|
isActive={isActive('report/by-food')}
|
||||||
link={Pages.orders.foodReport}
|
link={Pages.orders.foodReport}
|
||||||
/>
|
/>
|
||||||
|
<SubMenuItem
|
||||||
|
title={t('submenu.orders_daily_report')}
|
||||||
|
isActive={isActive('report/by-day')}
|
||||||
|
link={Pages.orders.dailyReport}
|
||||||
|
/>
|
||||||
{/* <SubMenuItem
|
{/* <SubMenuItem
|
||||||
title={t('submenu.add_order')}
|
title={t('submenu.add_order')}
|
||||||
isActive={isActive('add')}
|
isActive={isActive('add')}
|
||||||
|
|||||||
Reference in New Issue
Block a user