Add CSV export for orders and update UI symbols
Introduces an exportOrdersToCSV function in lib/api-client.ts to allow exporting orders by status as a CSV file. Updates various UI components to use the '•' (bullet) symbol instead of '·' (middle dot) and replaces some emoji/unicode characters for improved consistency and compatibility. Also normalizes the 'use client' directive to include a BOM in many files.
This commit is contained in:
@@ -377,6 +377,99 @@ export const getCustomerDetails = async (userId: string): Promise<CustomerStats>
|
||||
return clientFetch(`/customers/${userId}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Export orders by status to CSV
|
||||
* @param status Order status to filter by
|
||||
* @param storeId Optional store ID (uses authenticated user's store if not provided)
|
||||
* @returns Promise that resolves when download is triggered
|
||||
*/
|
||||
export const exportOrdersToCSV = async (status: string, storeId?: string): Promise<void> => {
|
||||
try {
|
||||
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
|
||||
const normalizedEndpoint = '/orders/export-csv';
|
||||
|
||||
// Handle API endpoint construction
|
||||
let url;
|
||||
if (apiUrl === '/api' || apiUrl.startsWith('/api')) {
|
||||
url = `/api${normalizedEndpoint}`;
|
||||
} else {
|
||||
url = `${apiUrl}${normalizedEndpoint}`;
|
||||
}
|
||||
|
||||
// Add query parameters
|
||||
const params = new URLSearchParams({ status });
|
||||
if (storeId) {
|
||||
params.append('storeId', storeId);
|
||||
}
|
||||
url = `${url}?${params.toString()}`;
|
||||
|
||||
// Get auth token
|
||||
const authToken = getAuthToken();
|
||||
|
||||
// Prepare headers
|
||||
const headers: Record<string, string> = {};
|
||||
if (authToken && authToken !== 'HTTP_ONLY_COOKIE') {
|
||||
headers['Authorization'] = `Bearer ${authToken}`;
|
||||
}
|
||||
|
||||
// Fetch the CSV file
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
const errorMessage = errorData.message || errorData.error || 'Failed to export orders';
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
// Get the CSV content
|
||||
const blob = await response.blob();
|
||||
|
||||
// Get filename from Content-Disposition header or generate one
|
||||
const contentDisposition = response.headers.get('Content-Disposition');
|
||||
let filename = `orders_${status}_${new Date().toISOString().split('T')[0]}.csv`;
|
||||
if (contentDisposition) {
|
||||
const filenameMatch = contentDisposition.match(/filename="?(.+)"?/);
|
||||
if (filenameMatch) {
|
||||
filename = filenameMatch[1];
|
||||
}
|
||||
}
|
||||
|
||||
// Create download link and trigger download
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = downloadUrl;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
} catch (error) {
|
||||
console.error('Error exporting orders to CSV:', error);
|
||||
const message = error instanceof Error ? error.message : 'Failed to export orders';
|
||||
|
||||
// Show error toast
|
||||
if (typeof window !== 'undefined') {
|
||||
if (toast?.title && toast?.description) {
|
||||
toast({
|
||||
title: 'Export Failed',
|
||||
description: message,
|
||||
variant: 'destructive',
|
||||
});
|
||||
} else if (typeof toast?.error === 'function') {
|
||||
toast.error(message);
|
||||
} else {
|
||||
console.error('CSV export error:', message);
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Enhanced client-side fetch function with caching and automatic invalidation
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user