date error for product edit
This commit is contained in:
parent
9af2fa5c7f
commit
8fe45ad63b
@ -156,20 +156,33 @@ const AddProductModal: React.FC<AddProductModalProps> = ({ isOpen, onClose, onPr
|
||||
}
|
||||
|
||||
// Validate date constraints
|
||||
const validFromDate = new Date(formData.valid_from);
|
||||
const today = new Date(currentDate);
|
||||
|
||||
if (validFromDate > today) {
|
||||
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})`);
|
||||
try {
|
||||
const validFromDate = new Date(formData.valid_from);
|
||||
if (isNaN(validFromDate.getTime())) {
|
||||
setError('Please enter a valid date');
|
||||
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 {
|
||||
@ -315,10 +328,17 @@ const AddProductModal: React.FC<AddProductModalProps> = ({ isOpen, onClose, onPr
|
||||
value={formData.valid_from}
|
||||
onChange={handleChange}
|
||||
required
|
||||
min={editProduct ? (() => {
|
||||
const nextDay = new Date(minValidFromDate);
|
||||
nextDay.setDate(nextDay.getDate() + 1);
|
||||
return nextDay.toISOString().split('T')[0];
|
||||
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"
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -176,8 +176,18 @@ const ShoppingEventList: React.FC = () => {
|
||||
bValue = b.shop.name;
|
||||
break;
|
||||
case 'date':
|
||||
aValue = new Date(a.date);
|
||||
bValue = new Date(b.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)}
|
||||
|
||||
102
frontend/src/utils/dateUtils.ts
Normal file
102
frontend/src/utils/dateUtils.ts
Normal 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 };
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user