Add flexible date pickers and export options to stock dashboard
Introduces a reusable date picker component with support for single date, date range, and month selection. Updates the stock management page to allow exporting reports by daily, weekly, monthly, or custom date ranges using the new pickers. Refactors promotion form to use the new date picker for start and end dates. Adds more business quotes to the quotes config.
This commit is contained in:
@@ -33,6 +33,9 @@ import { Product } from "@/models/products";
|
||||
import { Package, RefreshCw, ChevronDown, CheckSquare, XSquare, Boxes, Download, Calendar } from "lucide-react";
|
||||
import { clientFetch } from "@/lib/api";
|
||||
import { toast } from "sonner";
|
||||
import { DatePicker, DateRangePicker, DateRangeDisplay, MonthPicker } from "@/components/ui/date-picker";
|
||||
import { DateRange } from "react-day-picker";
|
||||
import { addDays, startOfDay, endOfDay, format, isSameDay } from "date-fns";
|
||||
|
||||
interface StockData {
|
||||
currentStock: number;
|
||||
@@ -40,6 +43,8 @@ interface StockData {
|
||||
lowStockThreshold?: number;
|
||||
}
|
||||
|
||||
type ReportType = 'daily' | 'weekly' | 'monthly' | 'custom';
|
||||
|
||||
export default function StockManagementPage() {
|
||||
const router = useRouter();
|
||||
const [products, setProducts] = useState<Product[]>([]);
|
||||
@@ -50,7 +55,15 @@ export default function StockManagementPage() {
|
||||
const [selectedProducts, setSelectedProducts] = useState<string[]>([]);
|
||||
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false);
|
||||
const [bulkAction, setBulkAction] = useState<'enable' | 'disable' | null>(null);
|
||||
|
||||
// Export state
|
||||
const [exportDate, setExportDate] = useState<string>(new Date().toISOString().split('T')[0]);
|
||||
const [exportDateRange, setExportDateRange] = useState<DateRange | undefined>({
|
||||
from: startOfDay(addDays(new Date(), -6)),
|
||||
to: endOfDay(new Date())
|
||||
});
|
||||
const [selectedMonth, setSelectedMonth] = useState<Date>(new Date());
|
||||
const [reportType, setReportType] = useState<ReportType>('daily');
|
||||
const [isExporting, setIsExporting] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -259,7 +272,47 @@ export default function StockManagementPage() {
|
||||
const handleExportStock = async () => {
|
||||
setIsExporting(true);
|
||||
try {
|
||||
const response = await clientFetch(`/api/analytics/daily-stock-report?date=${exportDate}`);
|
||||
let response;
|
||||
let filename;
|
||||
|
||||
switch (reportType) {
|
||||
case 'daily':
|
||||
response = await clientFetch(`/api/analytics/daily-stock-report?date=${exportDate}`);
|
||||
filename = `daily-stock-report-${exportDate}.csv`;
|
||||
break;
|
||||
|
||||
case 'weekly':
|
||||
if (!exportDateRange?.from) {
|
||||
toast.error('Please select a date range for weekly report');
|
||||
return;
|
||||
}
|
||||
const weekStart = format(exportDateRange.from, 'yyyy-MM-dd');
|
||||
response = await clientFetch(`/api/analytics/weekly-stock-report?weekStart=${weekStart}`);
|
||||
filename = `weekly-stock-report-${weekStart}.csv`;
|
||||
break;
|
||||
|
||||
case 'monthly':
|
||||
const year = selectedMonth.getFullYear();
|
||||
const month = selectedMonth.getMonth() + 1;
|
||||
response = await clientFetch(`/api/analytics/monthly-stock-report?year=${year}&month=${month}`);
|
||||
filename = `monthly-stock-report-${year}-${month.toString().padStart(2, '0')}.csv`;
|
||||
break;
|
||||
|
||||
case 'custom':
|
||||
if (!exportDateRange?.from || !exportDateRange?.to) {
|
||||
toast.error('Please select a date range for custom report');
|
||||
return;
|
||||
}
|
||||
const startDate = format(exportDateRange.from, 'yyyy-MM-dd');
|
||||
const endDate = format(exportDateRange.to, 'yyyy-MM-dd');
|
||||
response = await clientFetch(`/api/analytics/daily-stock-report?startDate=${startDate}&endDate=${endDate}`);
|
||||
filename = `custom-stock-report-${startDate}-to-${endDate}.csv`;
|
||||
break;
|
||||
|
||||
default:
|
||||
toast.error('Invalid report type');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response || !response.products) {
|
||||
throw new Error('No data received from server');
|
||||
@@ -297,14 +350,19 @@ export default function StockManagementPage() {
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', `daily-stock-report-${exportDate}.csv`);
|
||||
link.setAttribute('download', filename);
|
||||
link.style.visibility = 'hidden';
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
toast.success(`Stock report for ${exportDate} exported successfully`);
|
||||
const periodText = reportType === 'daily' ? exportDate :
|
||||
reportType === 'weekly' ? `week starting ${format(exportDateRange?.from || new Date(), 'MMM dd')}` :
|
||||
reportType === 'monthly' ? `${response.monthName || 'current month'}` :
|
||||
`${format(exportDateRange?.from || new Date(), 'MMM dd')} to ${format(exportDateRange?.to || new Date(), 'MMM dd')}`;
|
||||
|
||||
toast.success(`${reportType.charAt(0).toUpperCase() + reportType.slice(1)} stock report for ${periodText} exported successfully`);
|
||||
} catch (error) {
|
||||
console.error('Error exporting stock report:', error);
|
||||
toast.error('Failed to export stock report');
|
||||
@@ -348,28 +406,60 @@ export default function StockManagementPage() {
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="gap-2"
|
||||
>
|
||||
|
||||
{/* Report Type Selector */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" className="gap-2">
|
||||
<Calendar className="h-4 w-4" />
|
||||
{exportDate}
|
||||
{reportType.charAt(0).toUpperCase() + reportType.slice(1)} Report
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-3" align="end">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">Select Date for Export</label>
|
||||
<Input
|
||||
type="date"
|
||||
value={exportDate}
|
||||
onChange={(e) => setExportDate(e.target.value)}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem onClick={() => setReportType('daily')}>
|
||||
Daily Report
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setReportType('weekly')}>
|
||||
Weekly Report
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setReportType('monthly')}>
|
||||
Monthly Report
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setReportType('custom')}>
|
||||
Custom Range
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
{/* Date Selection based on report type */}
|
||||
{reportType === 'daily' && (
|
||||
<DatePicker
|
||||
date={exportDate ? new Date(exportDate) : undefined}
|
||||
onDateChange={(date) => setExportDate(date ? date.toISOString().split('T')[0] : '')}
|
||||
placeholder="Select export date"
|
||||
className="w-auto"
|
||||
/>
|
||||
)}
|
||||
|
||||
{(reportType === 'weekly' || reportType === 'custom') && (
|
||||
<DateRangePicker
|
||||
dateRange={exportDateRange}
|
||||
onDateRangeChange={setExportDateRange}
|
||||
placeholder="Select date range"
|
||||
className="w-auto"
|
||||
/>
|
||||
)}
|
||||
|
||||
{reportType === 'monthly' && (
|
||||
<MonthPicker
|
||||
selectedMonth={selectedMonth}
|
||||
onMonthChange={(date) => setSelectedMonth(date || new Date())}
|
||||
placeholder="Select month"
|
||||
className="w-auto"
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleExportStock}
|
||||
@@ -383,6 +473,7 @@ export default function StockManagementPage() {
|
||||
)}
|
||||
{isExporting ? 'Exporting...' : 'Export CSV'}
|
||||
</Button>
|
||||
|
||||
{selectedProducts.length > 0 && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
||||
Reference in New Issue
Block a user