fix: auto-refresh bookings silently instead of showing update banner
Polling detects new bookings and silently re-fetches tab data + dashboard counts. No more "press to update" banner — data appears automatically.
This commit is contained in:
@@ -699,29 +699,24 @@ function BookingsPageInner() {
|
|||||||
const [addOpen, setAddOpen] = useState(false);
|
const [addOpen, setAddOpen] = useState(false);
|
||||||
const [searchResults, setSearchResults] = useState<SearchResult[] | null>(null);
|
const [searchResults, setSearchResults] = useState<SearchResult[] | null>(null);
|
||||||
const [statusFilter, setStatusFilter] = useState<BookingFilter>("all");
|
const [statusFilter, setStatusFilter] = useState<BookingFilter>("all");
|
||||||
const [newBookingsBanner, setNewBookingsBanner] = useState(false);
|
const [refreshKey, setRefreshKey] = useState(0);
|
||||||
const lastTotalRef = useRef<number | null>(null);
|
const lastTotalRef = useRef<number | null>(null);
|
||||||
const { showError } = useToast();
|
const { showError } = useToast();
|
||||||
|
|
||||||
// #10: Pause polling when browser tab not visible
|
// Poll for new bookings, auto-refresh silently
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let id: ReturnType<typeof setInterval>;
|
const id = setInterval(() => {
|
||||||
function startPolling() {
|
|
||||||
id = setInterval(() => {
|
|
||||||
if (document.hidden) return;
|
if (document.hidden) return;
|
||||||
adminFetch("/api/admin/unread-counts")
|
adminFetch("/api/admin/unread-counts")
|
||||||
.then((r) => r.json())
|
.then((r) => r.json())
|
||||||
.then((data: { total: number }) => {
|
.then((data: { total: number }) => {
|
||||||
if (lastTotalRef.current !== null && data.total !== lastTotalRef.current) {
|
if (lastTotalRef.current !== null && data.total !== lastTotalRef.current) {
|
||||||
// #6: Show banner instead of remounting with key
|
setRefreshKey((k) => k + 1);
|
||||||
setNewBookingsBanner(true);
|
|
||||||
}
|
}
|
||||||
lastTotalRef.current = data.total;
|
lastTotalRef.current = data.total;
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}, 10000);
|
}, 10000);
|
||||||
}
|
|
||||||
startPolling();
|
|
||||||
return () => clearInterval(id);
|
return () => clearInterval(id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -772,16 +767,6 @@ function BookingsPageInner() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* #6: New bookings banner instead of full remount */}
|
|
||||||
{newBookingsBanner && (
|
|
||||||
<button
|
|
||||||
onClick={() => { setNewBookingsBanner(false); window.location.reload(); }}
|
|
||||||
className="mt-3 w-full rounded-lg border border-gold/30 bg-gold/10 px-4 py-2 text-sm text-gold hover:bg-gold/20 transition-all text-center"
|
|
||||||
>
|
|
||||||
Появились новые записи — нажмите для обновления
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Search */}
|
{/* Search */}
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<SearchBar
|
<SearchBar
|
||||||
@@ -829,7 +814,7 @@ function BookingsPageInner() {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{/* Dashboard — what needs attention */}
|
{/* Dashboard — what needs attention */}
|
||||||
<DashboardSummary onNavigate={setTab} />
|
<DashboardSummary key={refreshKey} onNavigate={setTab} />
|
||||||
|
|
||||||
{/* Tabs */}
|
{/* Tabs */}
|
||||||
<div className="mt-5 flex border-b border-white/10">
|
<div className="mt-5 flex border-b border-white/10">
|
||||||
@@ -851,8 +836,8 @@ function BookingsPageInner() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tab content — no key={refreshKey}, banner handles new data */}
|
{/* Tab content — auto-refreshes when new bookings detected */}
|
||||||
<div className="mt-4">
|
<div className="mt-4" key={refreshKey}>
|
||||||
{tab === "reminders" && <RemindersTab />}
|
{tab === "reminders" && <RemindersTab />}
|
||||||
{tab === "classes" && <GroupBookingsTab filter={statusFilter} />}
|
{tab === "classes" && <GroupBookingsTab filter={statusFilter} />}
|
||||||
{tab === "master-classes" && <McRegistrationsTab filter={statusFilter} />}
|
{tab === "master-classes" && <McRegistrationsTab filter={statusFilter} />}
|
||||||
@@ -864,7 +849,7 @@ function BookingsPageInner() {
|
|||||||
<AddBookingModal
|
<AddBookingModal
|
||||||
open={addOpen}
|
open={addOpen}
|
||||||
onClose={() => setAddOpen(false)}
|
onClose={() => setAddOpen(false)}
|
||||||
onAdded={() => setNewBookingsBanner(true)}
|
onAdded={() => setRefreshKey((k) => k + 1)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user