import React, { useState, useMemo } from 'react'; import { useSalesSummaryReport } from '../core/_hooks'; import { SalesSummaryFilters } from '../core/_models'; import { Button } from '@/components/ui/Button'; import { Input } from '@/components/ui/Input'; import { Table } from '@/components/ui/Table'; import { TableColumn } from '@/types'; import { JalaliDateTimePicker } from '@/components/ui/JalaliDateTimePicker'; import { PageContainer, PageTitle } from '@/components/ui/Typography'; import { Pagination } from '@/components/ui/Pagination'; import { Filter, TrendingUp, DollarSign, Package, ShoppingCart, X, Image as ImageIcon } from 'lucide-react'; import { formatWithThousands, parseFormattedNumber, persianToEnglish } from '@/utils/numberUtils'; import { formatCurrency } from '@/utils/formatters'; import { ReportSkeleton } from '@/components/common/ReportSkeleton'; import DateObject from 'react-date-object'; const toIsoString = (date: DateObject): string => { try { const g = date.convert(undefined); const yyyy = g.year.toString().padStart(4, '0'); const mm = g.month.toString().padStart(2, '0'); const dd = g.day.toString().padStart(2, '0'); const hh = (g.hour || 0).toString().padStart(2, '0'); const mi = (g.minute || 0).toString().padStart(2, '0'); const ss = (g.second || 0).toString().padStart(2, '0'); return `${yyyy}-${mm}-${dd}T${hh}:${mi}:${ss}Z`; } catch { const now = new Date(); return now.toISOString(); } }; const getDefaultDateRange = () => { const now = new DateObject(); const thirtyDaysAgo = new DateObject().subtract(30, 'days'); return { from: toIsoString(thirtyDaysAgo), to: toIsoString(now), }; }; const SalesSummaryReportPage = () => { const defaultDates = getDefaultDateRange(); const [filters, setFilters] = useState({ from: defaultDates.from, to: defaultDates.to, limit: 50, offset: 0, }); const [tempFilters, setTempFilters] = useState({ from: defaultDates.from, to: defaultDates.to, limit: 50, offset: 0, }); const { data, isLoading, error } = useSalesSummaryReport(filters); const handleTempFilterChange = (key: keyof SalesSummaryFilters, value: any) => { setTempFilters(prev => ({ ...prev, [key]: value, })); }; const handleDateChange = (key: 'from' | 'to', value: string | undefined) => { if (value) { handleTempFilterChange(key, value); } }; const handleNumericFilterChange = ( key: 'min_quantity' | 'max_quantity' | 'min_weight' | 'max_weight' | 'min_sales' | 'max_sales', raw: string ) => { const converted = persianToEnglish(raw); const numeric = parseFormattedNumber(converted); handleTempFilterChange(key, numeric || undefined); }; const handleApplyFilters = () => { setFilters({ ...tempFilters, offset: 0, }); }; const handlePageChange = (page: number) => { setFilters(prev => ({ ...prev, offset: (page - 1) * (prev.limit || 50), })); }; const handleClearFilters = () => { const defaultDates = getDefaultDateRange(); const clearedFilters = { from: defaultDates.from, to: defaultDates.to, limit: 50, offset: 0, }; setTempFilters(clearedFilters); setFilters(clearedFilters); }; const columns: TableColumn[] = useMemo(() => [ { key: 'product_name', label: 'نام محصول', align: 'right', render: (_val, row: any) => (
{row.image_url && ( {row.product_name} )}
{row.product_name}
{row.product_sku && (
کد محصول: {row.product_sku}
)}
), }, { key: 'total_quantity', label: 'تعداد فروش', align: 'right', render: (val: number) => formatWithThousands(val), }, { key: 'total_weight', label: 'وزن خالص (گرم)', align: 'right', render: (val: number) => formatWithThousands(val, 2), }, { key: 'total_final_weight', label: 'وزن با اجرت (گرم)', align: 'right', render: (val: number) => formatWithThousands(val, 2), }, { key: 'total_sales_amount', label: 'مجموع فروش', align: 'right', render: (val: number) => formatCurrency(val), }, { key: 'average_price', label: 'میانگین قیمت', align: 'right', render: (val: number) => formatCurrency(val), }, { key: 'average_weight', label: 'میانگین وزن', align: 'right', render: (val: number) => formatWithThousands(val, 2) + ' گرم', }, { key: 'variant_count', label: 'تعداد Variant', align: 'right', render: (val: number) => formatWithThousands(val), }, ], []); const tableData = (data?.products_breakdown || []).map(product => ({ ...product, total_quantity: product.total_quantity, total_weight: product.total_weight, total_final_weight: product.total_final_weight, total_sales_amount: product.total_sales_amount, average_price: product.average_price, average_weight: product.average_weight, variant_count: product.variant_count, })); const currentPage = Math.floor((filters.offset || 0) / (filters.limit || 50)) + 1; const totalPages = data?.products_pagination ? Math.ceil(data.products_pagination.total / (filters.limit || 50)) : 1; if (isLoading) { return ( ); } if (error) { return (

خطا در بارگذاری گزارش

); } return ( گزارش خلاصه فروش

فیلترها

handleDateChange('from', value)} placeholder="انتخاب تاریخ شروع" />
handleDateChange('to', value)} placeholder="انتخاب تاریخ پایان" />
handleTempFilterChange('product_sku', e.target.value || undefined)} placeholder="مثلاً RING-001" />
handleTempFilterChange('product_name', e.target.value || undefined)} placeholder="مثلاً انگشتر" />
handleNumericFilterChange('min_quantity', e.target.value)} placeholder="مثلاً ۱۰" numeric />
handleNumericFilterChange('max_quantity', e.target.value)} placeholder="مثلاً ۱۰۰" numeric />
handleNumericFilterChange('min_weight', e.target.value)} placeholder="مثلاً ۵.۵" numeric />
handleNumericFilterChange('max_weight', e.target.value)} placeholder="مثلاً ۵۰" numeric />
handleNumericFilterChange('min_sales', e.target.value)} placeholder="مثلاً ۱۰۰۰۰۰۰" numeric />
handleNumericFilterChange('max_sales', e.target.value)} placeholder="مثلاً ۵۰۰۰۰۰۰۰" numeric />
{data && ( <>

مجموع فروش

{formatCurrency(data.total_sales_amount)}

وزن خالص طلا

{formatWithThousands(data.total_gold_weight, 2)} گرم

تعداد سفارشات

{formatWithThousands(data.total_orders)}

میانگین سفارش

{formatCurrency(data.average_order_value)}

تفکیک محصولات

{data.products_pagination && totalPages > 1 && (
)} )} ); }; export default SalesSummaryReportPage;