Split oversized frontend API client, email service, and admin/booking pages into focused modules while preserving import surfaces, and add Redis-backed queues, stale booking cleanup, stronger auth, and scale deployment configs. Co-authored-by: Cursor <cursoragent@cursor.com>
97 lines
3.4 KiB
TypeScript
97 lines
3.4 KiB
TypeScript
import { useState } from 'react';
|
|
import toast from 'react-hot-toast';
|
|
import { paymentOptionsApi, PaymentOptionsConfig } from '@/lib/api';
|
|
|
|
/**
|
|
* Manages the event-level payment override editor state: loading global +
|
|
* override config, computing effective values, editing, saving and resetting.
|
|
*/
|
|
export function usePaymentOverrides(eventId: string, locale: string) {
|
|
const [globalPaymentOptions, setGlobalPaymentOptions] = useState<PaymentOptionsConfig | null>(null);
|
|
const [paymentOverrides, setPaymentOverrides] = useState<Partial<PaymentOptionsConfig>>({});
|
|
const [hasPaymentOverrides, setHasPaymentOverrides] = useState(false);
|
|
const [savingPayments, setSavingPayments] = useState(false);
|
|
const [loadingPayments, setLoadingPayments] = useState(false);
|
|
|
|
const loadPaymentOptions = async () => {
|
|
if (globalPaymentOptions) return;
|
|
setLoadingPayments(true);
|
|
try {
|
|
const [globalRes, overridesRes] = await Promise.all([
|
|
paymentOptionsApi.getGlobal(),
|
|
paymentOptionsApi.getEventOverrides(eventId),
|
|
]);
|
|
setGlobalPaymentOptions(globalRes.paymentOptions);
|
|
if (overridesRes.overrides) {
|
|
setPaymentOverrides(overridesRes.overrides);
|
|
setHasPaymentOverrides(true);
|
|
}
|
|
} catch (error) {
|
|
toast.error('Failed to load payment options');
|
|
} finally {
|
|
setLoadingPayments(false);
|
|
}
|
|
};
|
|
|
|
const getEffectivePaymentOption = <K extends keyof PaymentOptionsConfig>(key: K): PaymentOptionsConfig[K] => {
|
|
if (paymentOverrides[key] !== undefined && paymentOverrides[key] !== null) {
|
|
return paymentOverrides[key] as PaymentOptionsConfig[K];
|
|
}
|
|
return globalPaymentOptions?.[key] as PaymentOptionsConfig[K];
|
|
};
|
|
|
|
const updatePaymentOverride = <K extends keyof PaymentOptionsConfig>(
|
|
key: K,
|
|
value: PaymentOptionsConfig[K] | null
|
|
) => {
|
|
setPaymentOverrides((prev) => ({ ...prev, [key]: value }));
|
|
setHasPaymentOverrides(true);
|
|
};
|
|
|
|
const handleSavePaymentOptions = async () => {
|
|
setSavingPayments(true);
|
|
try {
|
|
await paymentOptionsApi.updateEventOverrides(eventId, paymentOverrides);
|
|
toast.success(locale === 'es' ? 'Opciones de pago guardadas' : 'Payment options saved');
|
|
} catch (error: any) {
|
|
toast.error(error.message || 'Failed to save payment options');
|
|
} finally {
|
|
setSavingPayments(false);
|
|
}
|
|
};
|
|
|
|
const handleResetToGlobal = async () => {
|
|
if (!confirm(locale === 'es'
|
|
? '¿Resetear a la configuración global? Se eliminarán todas las personalizaciones de este evento.'
|
|
: 'Reset to global settings? This will remove all customizations for this event.')) {
|
|
return;
|
|
}
|
|
setSavingPayments(true);
|
|
try {
|
|
await paymentOptionsApi.deleteEventOverrides(eventId);
|
|
setPaymentOverrides({});
|
|
setHasPaymentOverrides(false);
|
|
toast.success(locale === 'es' ? 'Restablecido a configuración global' : 'Reset to global settings');
|
|
} catch (error: any) {
|
|
toast.error(error.message || 'Failed to reset payment options');
|
|
} finally {
|
|
setSavingPayments(false);
|
|
}
|
|
};
|
|
|
|
return {
|
|
globalPaymentOptions,
|
|
paymentOverrides,
|
|
hasPaymentOverrides,
|
|
savingPayments,
|
|
loadingPayments,
|
|
loadPaymentOptions,
|
|
getEffectivePaymentOption,
|
|
updatePaymentOverride,
|
|
handleSavePaymentOptions,
|
|
handleResetToGlobal,
|
|
};
|
|
}
|
|
|
|
export type PaymentOverridesController = ReturnType<typeof usePaymentOverrides>;
|