Two consistency fixes requested after the payment-method work: 1. Registration status (pending/confirmed/partial_paid/paid/cancelled) was printed as a raw string on the user dashboard. Added RegistrationStatusBadge mirroring the existing EventStatusBadge pattern, using the same status colors already established on dashboard/admin/registrations. 2. Inline success/error banners across dashboard pages persisted indefinitely. Added a shared useDismissingState hook (drop-in useState replacement that auto-clears a truthy value after 7s, resetting the timer on each update) and swapped it in across ~24 dashboard files. Excluded: message-only modal dialogs (ticket- scanning's success/error confirmations) and two states that mix live form-validation feedback with async results inside actively- open forms (the registration-edit modal's editError, the event create/edit modal's error) - those keep persisting until the user acts, since auto-hiding a "fix this field" message mid-edit would be a regression. Also fixed at-the-door's existing bespoke auto-dismiss timers (10s/15s, one mislabeled as "5s") to the same consistent 7s, and removed admin/settings' manual x dismiss button in favor of the same auto-only behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
25 lines
984 B
TypeScript
25 lines
984 B
TypeScript
import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
|
/**
|
|
* Like useState, but a truthy value auto-clears back to the initial falsy value after
|
|
* `ms` milliseconds. Every call to the setter cancels any pending timer and (if the new
|
|
* value is truthy) arms a fresh one, so rapid-fire updates reset the countdown instead of
|
|
* cutting it short. Meant for post-action confirmation/error banners that shouldn't linger.
|
|
*/
|
|
export function useDismissingState<T>(initial: T, ms = 7000) {
|
|
const [value, setValue] = useState<T>(initial);
|
|
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
|
|
const set = useCallback((next: T) => {
|
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
setValue(next);
|
|
if (next) {
|
|
timeoutRef.current = setTimeout(() => setValue(initial), ms);
|
|
}
|
|
}, [ms, initial]);
|
|
|
|
useEffect(() => () => { if (timeoutRef.current) clearTimeout(timeoutRef.current); }, []);
|
|
|
|
return [value, set] as const;
|
|
}
|