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,21 +156,34 @@ const AddProductModal: React.FC<AddProductModalProps> = ({ isOpen, onClose, onPr
}
// Validate date constraints
try {
const validFromDate = new Date(formData.valid_from);
const today = new Date(currentDate);
if (isNaN(validFromDate.getTime())) {
setError('Please enter a valid date');
return;
}
if (validFromDate > today) {
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) {
if (editProduct && minValidFromDate) {
// Only validate if minValidFromDate is set and valid
const minDate = new Date(minValidFromDate);
if (validFromDate <= minDate) {
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 {
setLoading(true);
@ -315,10 +328,17 @@ const AddProductModal: React.FC<AddProductModalProps> = ({ isOpen, onClose, onPr
value={formData.valid_from}
onChange={handleChange}
required
min={editProduct ? (() => {
min={editProduct && minValidFromDate ? (() => {
try {
const nextDay = new Date(minValidFromDate);
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}
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"

View File

@ -6,7 +6,21 @@ import { shoppingEventApi } from '../services/api';
const Dashboard: React.FC = () => {
const navigate = useNavigate();
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(() => {
fetchRecentEvents();
@ -18,7 +32,19 @@ const Dashboard: React.FC = () => {
const response = await shoppingEventApi.getAll();
// Get the 3 most recent events
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);
setRecentEvents(recent);
} catch (error) {
@ -179,7 +205,7 @@ const Dashboard: React.FC = () => {
<span className="text-sm text-gray-500">{event.shop.city}</span>
</div>
<p className="text-sm text-gray-600 mt-1">
{new Date(event.date).toLocaleDateString()}
{formatDate(event.date)}
</p>
{event.products.length > 0 && (
<p className="text-sm text-gray-500 mt-1">

View File

@ -176,8 +176,18 @@ const ShoppingEventList: React.FC = () => {
bValue = b.shop.name;
break;
case 'date':
// Safely handle date parsing with validation
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;
case 'items':
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) {
return (
<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>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{new Date(event.date).toLocaleDateString()}
{formatDate(event.date)}
</td>
<td
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>
</div>
<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 && (
<p className="font-semibold text-green-600 mt-1">
${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 };
};