feat: add featured event with automatic fallback
- Add featured_event_id to site_settings (schema + migration) - Backend: featured event logic in /events/next/upcoming with auto-unset when event ends - Site settings: PUT supports featuredEventId, add PUT /featured-event for admin - Admin events: Set as featured checkbox in editor, star toggle in list, featured badge - Admin settings: Featured Event section with current event and remove/change links - API: siteSettingsApi.setFeaturedEvent(), Event.isFeatured, SiteSettings.featuredEventId - Homepage/linktree unchanged: still use getNextUpcoming (now returns featured or fallback)
This commit is contained in:
@@ -110,43 +110,12 @@ export default function BookingPage() {
|
||||
|
||||
const [errors, setErrors] = useState<Partial<Record<keyof BookingFormData, string>>>({});
|
||||
|
||||
// RUC validation using modulo 11 algorithm
|
||||
const validateRucCheckDigit = (ruc: string): boolean => {
|
||||
const match = ruc.match(/^(\d{6,8})-(\d)$/);
|
||||
if (!match) return false;
|
||||
|
||||
const baseNumber = match[1];
|
||||
const checkDigit = parseInt(match[2], 10);
|
||||
|
||||
// Modulo 11 algorithm for Paraguayan RUC
|
||||
const weights = [2, 3, 4, 5, 6, 7, 2, 3];
|
||||
let sum = 0;
|
||||
const digits = baseNumber.split('').reverse();
|
||||
|
||||
for (let i = 0; i < digits.length; i++) {
|
||||
sum += parseInt(digits[i], 10) * weights[i];
|
||||
}
|
||||
|
||||
const remainder = sum % 11;
|
||||
const expectedCheckDigit = remainder < 2 ? 0 : 11 - remainder;
|
||||
|
||||
return checkDigit === expectedCheckDigit;
|
||||
};
|
||||
const rucPattern = /^\d{6,10}$/;
|
||||
|
||||
// Format RUC input: auto-insert hyphen before last digit
|
||||
// Format RUC input: digits only, max 10
|
||||
const formatRuc = (value: string): string => {
|
||||
// Remove non-numeric characters
|
||||
const digits = value.replace(/\D/g, '');
|
||||
|
||||
// Limit to 9 digits (8 base + 1 check)
|
||||
const limited = digits.slice(0, 9);
|
||||
|
||||
// Auto-insert hyphen before last digit if we have more than 6 digits
|
||||
if (limited.length > 6) {
|
||||
return `${limited.slice(0, -1)}-${limited.slice(-1)}`;
|
||||
}
|
||||
|
||||
return limited;
|
||||
const digits = value.replace(/\D/g, '').slice(0, 10);
|
||||
return digits;
|
||||
};
|
||||
|
||||
// Handle RUC input change
|
||||
@@ -160,19 +129,12 @@ export default function BookingPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// Validate RUC on blur
|
||||
// Validate RUC on blur (optional field: 6–10 digits)
|
||||
const handleRucBlur = () => {
|
||||
if (!formData.ruc) return; // Optional field, no validation if empty
|
||||
|
||||
const rucPattern = /^[0-9]{6,8}-[0-9]{1}$/;
|
||||
|
||||
if (!rucPattern.test(formData.ruc)) {
|
||||
if (!formData.ruc) return;
|
||||
const digits = formData.ruc.replace(/\D/g, '');
|
||||
if (digits.length > 0 && !rucPattern.test(digits)) {
|
||||
setErrors({ ...errors, ruc: t('booking.form.errors.rucInvalidFormat') });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateRucCheckDigit(formData.ruc)) {
|
||||
setErrors({ ...errors, ruc: t('booking.form.errors.rucInvalidCheckDigit') });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -275,13 +237,11 @@ export default function BookingPage() {
|
||||
newErrors.phone = t('booking.form.errors.phoneTooShort');
|
||||
}
|
||||
|
||||
// RUC validation (optional field - only validate if filled)
|
||||
// RUC validation (optional field - 6–10 digits if filled)
|
||||
if (formData.ruc.trim()) {
|
||||
const rucPattern = /^[0-9]{6,8}-[0-9]{1}$/;
|
||||
if (!rucPattern.test(formData.ruc)) {
|
||||
const digits = formData.ruc.replace(/\D/g, '');
|
||||
if (!/^\d{6,10}$/.test(digits)) {
|
||||
newErrors.ruc = t('booking.form.errors.rucInvalidFormat');
|
||||
} else if (!validateRucCheckDigit(formData.ruc)) {
|
||||
newErrors.ruc = t('booking.form.errors.rucInvalidCheckDigit');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,7 +389,7 @@ export default function BookingPage() {
|
||||
phone: formData.phone,
|
||||
preferredLanguage: formData.preferredLanguage,
|
||||
paymentMethod: formData.paymentMethod,
|
||||
...(formData.ruc.trim() && { ruc: formData.ruc }),
|
||||
...(formData.ruc.trim() && { ruc: formData.ruc.replace(/\D/g, '') }),
|
||||
// Include attendees array for multi-ticket bookings
|
||||
...(allAttendees.length > 1 && { attendees: allAttendees }),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user