feat: add time

This commit is contained in:
2026-08-04 14:05:19 +03:30
parent ba7147ff59
commit e3071b8692
2 changed files with 51 additions and 7 deletions
+48 -4
View File
@@ -170,6 +170,10 @@ export default function AccountingPage() {
const [dateFrom, setDateFrom] = useState<string>(''); const [dateFrom, setDateFrom] = useState<string>('');
const [dateTo, setDateTo] = useState<string>(''); const [dateTo, setDateTo] = useState<string>('');
// Time range
const [timeFrom, setTimeFrom] = useState<string>('');
const [timeTo, setTimeTo] = useState<string>('');
// Status filter — both paid statuses selected by default // Status filter — both paid statuses selected by default
const [statuses, setStatuses] = useState<number[]>([...PAID_STATUSES]); const [statuses, setStatuses] = useState<number[]>([...PAID_STATUSES]);
@@ -259,14 +263,28 @@ export default function AccountingPage() {
} }
// Date range (created_at is "Y-m-d H:i:s") // Date range (created_at is "Y-m-d H:i:s")
if (dateFrom && p.created_at && p.created_at.slice(0, 10) < dateFrom) return false; // When time is also selected, compare full datetime strings;
if (dateTo && p.created_at && p.created_at.slice(0, 10) > dateTo) return false; // otherwise compare only the date portion (YYYY-MM-DD).
const created = p.created_at ?? '';
if (dateFrom) {
const datePart = created.slice(0, 10);
if (timeFrom) {
// Full datetime comparison: "YYYY-MM-DD HH:mm:ss"
if (created < `${dateFrom} ${timeFrom}`) return false;
} else if (datePart < dateFrom) return false;
}
if (dateTo) {
const datePart = created.slice(0, 10);
if (timeTo) {
if (created > `${dateTo} ${timeTo}`) return false;
} else if (datePart > dateTo) return false;
}
return true; return true;
}); });
setResult(computeAccounting(filtered, myket, cafe, fee)); setResult(computeAccounting(filtered, myket, cafe, fee));
}, [allPurchases, statuses, selectedProductId, selectedPackageName, dateFrom, dateTo, myketRate, cafeRate, transactionFee]); }, [allPurchases, statuses, selectedProductId, selectedPackageName, dateFrom, dateTo, timeFrom, timeTo, myketRate, cafeRate, transactionFee]);
// Recompute on filter/rate changes (no refetch needed once data is cached) // Recompute on filter/rate changes (no refetch needed once data is cached)
useEffect(() => { useEffect(() => {
@@ -278,6 +296,8 @@ export default function AccountingPage() {
setSelectedProductId(''); setSelectedProductId('');
setDateFrom(''); setDateFrom('');
setDateTo(''); setDateTo('');
setTimeFrom('');
setTimeTo('');
setStatuses([...PAID_STATUSES]); setStatuses([...PAID_STATUSES]);
setMyketRate('85'); setMyketRate('85');
setCafeRate('85'); setCafeRate('85');
@@ -323,7 +343,7 @@ export default function AccountingPage() {
}} }}
className="bg-white dark:bg-gray-900 ring-1 ring-gray-200 dark:ring-gray-800 rounded-2xl p-5 sm:p-6 mb-6 shadow-sm" className="bg-white dark:bg-gray-900 ring-1 ring-gray-200 dark:ring-gray-800 rounded-2xl p-5 sm:p-6 mb-6 shadow-sm"
> >
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4"> <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-6">
<div> <div>
<label htmlFor="package_name" className={labelClass}>پکیج</label> <label htmlFor="package_name" className={labelClass}>پکیج</label>
<select <select
@@ -368,6 +388,18 @@ export default function AccountingPage() {
/> />
</div> </div>
<div>
<label htmlFor="time_from" className={labelClass}>از ساعت</label>
<input
type="time"
id="time_from"
value={timeFrom}
onChange={(e) => setTimeFrom(e.target.value)}
className={inputClass}
placeholder="۰۰:۰۰"
/>
</div>
<div> <div>
<PersianDatePicker <PersianDatePicker
id="date_to" id="date_to"
@@ -377,6 +409,18 @@ export default function AccountingPage() {
onChange={setDateTo} onChange={setDateTo}
/> />
</div> </div>
<div>
<label htmlFor="time_to" className={labelClass}>تا ساعت</label>
<input
type="time"
id="time_to"
value={timeTo}
onChange={(e) => setTimeTo(e.target.value)}
className={inputClass}
placeholder="۲۳:۵۹"
/>
</div>
</div> </div>
{/* Status checkboxes */} {/* Status checkboxes */}
+3 -3
View File
@@ -15,7 +15,7 @@ import {
getPurchaseStatusLabel, getPurchaseStatusLabel,
} from '@/types/purchase'; } from '@/types/purchase';
import { PackageName } from '@/types/package'; import { PackageName } from '@/types/package';
import { formatPrice, formatDate, getProductTypeLabel, getPurchaseFinalPrice } from '@/lib/utils'; import { formatPrice, formatDate, formatTime, getProductTypeLabel, getPurchaseFinalPrice } from '@/lib/utils';
import { MagnifyingGlassIcon, ArrowPathIcon } from '@heroicons/react/24/outline'; import { MagnifyingGlassIcon, ArrowPathIcon } from '@heroicons/react/24/outline';
const statusBadgeClass = (status: number): string => { const statusBadgeClass = (status: number): string => {
@@ -212,7 +212,7 @@ export default function PurchasesPage() {
<th className="px-4 py-3">قیمت نهایی</th> <th className="px-4 py-3">قیمت نهایی</th>
<th className="px-4 py-3">منبع پرداخت</th> <th className="px-4 py-3">منبع پرداخت</th>
<th className="px-4 py-3">وضعیت</th> <th className="px-4 py-3">وضعیت</th>
<th className="px-4 py-3">تاریخ</th> <th className="px-4 py-3">تاریخ و ساعت</th>
</tr> </tr>
</thead> </thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-800"> <tbody className="divide-y divide-gray-200 dark:divide-gray-800">
@@ -254,7 +254,7 @@ export default function PurchasesPage() {
</span> </span>
</td> </td>
<td className="px-4 py-3.5 text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap"> <td className="px-4 py-3.5 text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap">
{formatDate(purchase.created_at)} {formatDate(purchase.created_at)} {formatTime(purchase.created_at)}
</td> </td>
</tr> </tr>
))} ))}