date error for product edit

This commit is contained in:
lasse 2025-05-30 12:59:13 +02:00
parent 9af2fa5c7f
commit 8fe45ad63b
4 changed files with 195 additions and 23 deletions

View File

@ -156,20 +156,33 @@ const AddProductModal: React.FC<AddProductModalProps> = ({ isOpen, onClose, onPr
} }
// Validate date constraints // Validate date constraints
const validFromDate = new Date(formData.valid_from); try {
const today = new Date(currentDate); const validFromDate = new Date(formData.valid_from);
if (isNaN(validFromDate.getTime())) {
if (validFromDate > today) { setError('Please enter a valid date');
setError('Valid from date cannot be in the future');
return;
}
if (editProduct) {
const minDate = new Date(minValidFromDate);
if (validFromDate <= minDate) {
setError(`Valid from date must be after the current product's valid from date (${minValidFromDate})`);
return; return;
} }
if (currentDate) {
const today = new Date(currentDate);
if (!isNaN(today.getTime()) && validFromDate > today) {
setError('Valid from date cannot be in the future');
return;
}
}
if (editProduct && minValidFromDate) {
// Only validate if minValidFromDate is set and valid
const minDate = new Date(minValidFromDate);
if (!isNaN(minDate.getTime()) && validFromDate <= minDate) {
setError(`Valid from date must be after the current product's valid from date (${minValidFromDate})`);
return;
}
}
} catch (dateError) {
console.error('Date validation error:', dateError);
setError('Please enter a valid date');
return;
} }
try { try {
@ -315,10 +328,17 @@ const AddProductModal: React.FC<AddProductModalProps> = ({ isOpen, onClose, onPr
value={formData.valid_from} value={formData.valid_from}
onChange={handleChange} onChange={handleChange}
required required
min={editProduct ? (() => { min={editProduct && minValidFromDate ? (() => {
const nextDay = new Date(minValidFromDate); try {
nextDay.setDate(nextDay.getDate() + 1); const nextDay = new Date(minValidFromDate);
return nextDay.toISOString().split('T')[0]; if (!isNaN(nextDay.getTime())) {
nextDay.setDate(nextDay.getDate() + 1);
return nextDay.toISOString().split('T')[0];
}
} catch (error) {
console.error('Error calculating min date:', error);
}
return undefined;
})() : undefined} })() : undefined}
max={currentDate} max={currentDate}
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"

View File

@ -6,7 +6,21 @@ import { shoppingEventApi } from '../services/api';
const Dashboard: React.FC = () => { const Dashboard: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [recentEvents, setRecentEvents] = useState<ShoppingEvent[]>([]); const [recentEvents, setRecentEvents] = useState<ShoppingEvent[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(false);
// Safe date formatting function
const formatDate = (dateString: string): string => {
try {
const date = new Date(dateString);
if (isNaN(date.getTime())) {
return 'Invalid Date';
}
return date.toLocaleDateString();
} catch (error) {
console.error('Error formatting date:', error);
return 'Invalid Date';
}
};
useEffect(() => { useEffect(() => {
fetchRecentEvents(); fetchRecentEvents();
@ -18,7 +32,19 @@ const Dashboard: React.FC = () => {
const response = await shoppingEventApi.getAll(); const response = await shoppingEventApi.getAll();
// Get the 3 most recent events // Get the 3 most recent events
const recent = response.data const recent = response.data
.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()) .sort((a, b) => {
try {
const dateA = new Date(b.created_at);
const dateB = new Date(a.created_at);
// Check if dates are valid
if (isNaN(dateA.getTime())) return 1;
if (isNaN(dateB.getTime())) return -1;
return dateA.getTime() - dateB.getTime();
} catch (error) {
console.error('Error sorting events by date:', error);
return 0;
}
})
.slice(0, 3); .slice(0, 3);
setRecentEvents(recent); setRecentEvents(recent);
} catch (error) { } catch (error) {
@ -179,7 +205,7 @@ const Dashboard: React.FC = () => {
<span className="text-sm text-gray-500">{event.shop.city}</span> <span className="text-sm text-gray-500">{event.shop.city}</span>
</div> </div>
<p className="text-sm text-gray-600 mt-1"> <p className="text-sm text-gray-600 mt-1">
{new Date(event.date).toLocaleDateString()} {formatDate(event.date)}
</p> </p>
{event.products.length > 0 && ( {event.products.length > 0 && (
<p className="text-sm text-gray-500 mt-1"> <p className="text-sm text-gray-500 mt-1">

View File

@ -176,8 +176,18 @@ const ShoppingEventList: React.FC = () => {
bValue = b.shop.name; bValue = b.shop.name;
break; break;
case 'date': case 'date':
aValue = new Date(a.date); // Safely handle date parsing with validation
bValue = new Date(b.date); try {
aValue = new Date(a.date);
bValue = new Date(b.date);
// Check if dates are valid
if (isNaN(aValue.getTime())) aValue = new Date(0); // fallback to epoch
if (isNaN(bValue.getTime())) bValue = new Date(0); // fallback to epoch
} catch (error) {
console.error('Error parsing dates for sorting:', error);
aValue = new Date(0);
bValue = new Date(0);
}
break; break;
case 'items': case 'items':
aValue = a.products.length; aValue = a.products.length;
@ -246,6 +256,20 @@ const ShoppingEventList: React.FC = () => {
} }
}; };
// Safe date formatting function
const formatDate = (dateString: string): string => {
try {
const date = new Date(dateString);
if (isNaN(date.getTime())) {
return 'Invalid Date';
}
return date.toLocaleDateString();
} catch (error) {
console.error('Error formatting date:', error);
return 'Invalid Date';
}
};
if (loading) { if (loading) {
return ( return (
<div className="flex justify-center items-center h-64"> <div className="flex justify-center items-center h-64">
@ -346,7 +370,7 @@ const ShoppingEventList: React.FC = () => {
<div className="text-xs text-gray-500">{event.shop.city}</div> <div className="text-xs text-gray-500">{event.shop.city}</div>
</td> </td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900"> <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{new Date(event.date).toLocaleDateString()} {formatDate(event.date)}
</td> </td>
<td <td
className={`items-cell px-6 py-4 whitespace-nowrap text-sm ${ className={`items-cell px-6 py-4 whitespace-nowrap text-sm ${
@ -414,7 +438,7 @@ const ShoppingEventList: React.FC = () => {
<p className="text-sm text-gray-500">{event.shop.city}</p> <p className="text-sm text-gray-500">{event.shop.city}</p>
</div> </div>
<div className="text-right flex-shrink-0 ml-4"> <div className="text-right flex-shrink-0 ml-4">
<p className="text-sm text-gray-600">{new Date(event.date).toLocaleDateString()}</p> <p className="text-sm text-gray-600">{formatDate(event.date)}</p>
{event.total_amount && ( {event.total_amount && (
<p className="font-semibold text-green-600 mt-1"> <p className="font-semibold text-green-600 mt-1">
${event.total_amount.toFixed(2)} ${event.total_amount.toFixed(2)}

View File

@ -0,0 +1,102 @@
/**
* Date utility functions for safe date handling throughout the application
*/
/**
* Safely formats a date string to a localized date string
* @param dateString - The date string to format
* @param fallback - The fallback value if the date is invalid (default: 'Invalid Date')
* @returns Formatted date string or fallback value
*/
export const formatDate = (dateString: string | null | undefined, fallback: string = 'Invalid Date'): string => {
if (!dateString) return fallback;
try {
const date = new Date(dateString);
if (isNaN(date.getTime())) {
return fallback;
}
return date.toLocaleDateString();
} catch (error) {
console.error('Error formatting date:', error);
return fallback;
}
};
/**
* Safely creates a Date object from a string
* @param dateString - The date string to parse
* @returns Date object or null if invalid
*/
export const safeParseDate = (dateString: string | null | undefined): Date | null => {
if (!dateString) return null;
try {
const date = new Date(dateString);
if (isNaN(date.getTime())) {
return null;
}
return date;
} catch (error) {
console.error('Error parsing date:', error);
return null;
}
};
/**
* Safely compares two dates for sorting
* @param dateA - First date string
* @param dateB - Second date string
* @param direction - Sort direction ('asc' or 'desc')
* @returns Comparison result (-1, 0, 1)
*/
export const compareDates = (
dateA: string | null | undefined,
dateB: string | null | undefined,
direction: 'asc' | 'desc' = 'asc'
): number => {
const parsedA = safeParseDate(dateA);
const parsedB = safeParseDate(dateB);
// Handle null/invalid dates
if (!parsedA && !parsedB) return 0;
if (!parsedA) return direction === 'asc' ? 1 : -1;
if (!parsedB) return direction === 'asc' ? -1 : 1;
const result = parsedA.getTime() - parsedB.getTime();
return direction === 'asc' ? result : -result;
};
/**
* Gets the current date in YYYY-MM-DD format
* @returns Current date string
*/
export const getCurrentDateString = (): string => {
return new Date().toISOString().split('T')[0];
};
/**
* Validates if a date string is valid and not in the future
* @param dateString - The date string to validate
* @param allowFuture - Whether to allow future dates (default: false)
* @returns Object with validation result and error message
*/
export const validateDate = (
dateString: string | null | undefined,
allowFuture: boolean = false
): { isValid: boolean; error?: string } => {
if (!dateString) {
return { isValid: false, error: 'Date is required' };
}
const date = safeParseDate(dateString);
if (!date) {
return { isValid: false, error: 'Invalid date format' };
}
if (!allowFuture && date > new Date()) {
return { isValid: false, error: 'Date cannot be in the future' };
}
return { isValid: true };
};