179 lines
5.0 KiB
TypeScript
179 lines
5.0 KiB
TypeScript
import { Booking } from '@/features/booking/booking';
|
|
import { ApiClientError, apiClient } from '@/lib/api-client';
|
|
import { useCurrentComplexStore } from '@/lib/stores/current-complex-store';
|
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
|
import { useNavigate } from '@tanstack/react-router';
|
|
import { useEffect } from 'react';
|
|
|
|
function extractMessage(error: unknown, fallback: string) {
|
|
if (error instanceof ApiClientError) {
|
|
return error.message || fallback;
|
|
}
|
|
|
|
return fallback;
|
|
}
|
|
|
|
export function HomePage() {
|
|
const queryClient = useQueryClient();
|
|
const navigate = useNavigate();
|
|
|
|
const currentComplexQuery = useQuery({
|
|
queryKey: ['current-complex'],
|
|
queryFn: () => apiClient.complexes.getCurrent(),
|
|
});
|
|
|
|
const { setCurrentComplex } = useCurrentComplexStore();
|
|
|
|
useEffect(() => {
|
|
if (currentComplexQuery.data) {
|
|
setCurrentComplex(currentComplexQuery.data.complexSlug);
|
|
}
|
|
}, [currentComplexQuery.data, setCurrentComplex]);
|
|
|
|
useEffect(() => {
|
|
const unsubscribe = useCurrentComplexStore.subscribe((state, prevState) => {
|
|
if (state.currentComplexSlug !== prevState.currentComplexSlug) {
|
|
queryClient.invalidateQueries({ queryKey: ['current-complex'] });
|
|
queryClient.invalidateQueries({ queryKey: ['admin-bookings'] });
|
|
}
|
|
});
|
|
|
|
return unsubscribe;
|
|
}, [queryClient]);
|
|
|
|
const myComplexesQuery = useQuery({
|
|
queryKey: ['my-complexes'],
|
|
queryFn: () => apiClient.complexes.listMine(),
|
|
});
|
|
|
|
useEffect(() => {
|
|
async function autoSelect() {
|
|
if (myComplexesQuery.data && myComplexesQuery.data.length === 1) {
|
|
try {
|
|
const result = await apiClient.complexes.select({
|
|
complexId: myComplexesQuery.data[0].id,
|
|
});
|
|
queryClient.setQueryData(['current-complex'], result);
|
|
} catch (error) {
|
|
console.error('Error al auto-seleccionar complejo:', error);
|
|
}
|
|
} else if (
|
|
myComplexesQuery.data &&
|
|
myComplexesQuery.data.length > 1 &&
|
|
!currentComplexQuery.data
|
|
) {
|
|
navigate({ to: '/select-complex' });
|
|
}
|
|
}
|
|
|
|
if (
|
|
!currentComplexQuery.isLoading &&
|
|
!currentComplexQuery.data &&
|
|
myComplexesQuery.data &&
|
|
!myComplexesQuery.isLoading
|
|
) {
|
|
if (myComplexesQuery.data.length === 0) {
|
|
navigate({ to: '/onboard/setup' });
|
|
return;
|
|
}
|
|
autoSelect();
|
|
}
|
|
}, [
|
|
currentComplexQuery.isLoading,
|
|
currentComplexQuery.data,
|
|
myComplexesQuery.data,
|
|
myComplexesQuery.isLoading,
|
|
navigate,
|
|
queryClient,
|
|
]);
|
|
|
|
const selectedComplex = currentComplexQuery.data ?? null;
|
|
|
|
useEffect(() => {
|
|
if (!selectedComplex?.id) return;
|
|
|
|
const channel = `complex-${selectedComplex.id}`;
|
|
let eventSource: EventSource | null = null;
|
|
let reconnectTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
let reconnectAttempts = 0;
|
|
const maxReconnectAttempts = 3;
|
|
let lastEventId: string | null = null;
|
|
let pollInterval: ReturnType<typeof setInterval> | null = null;
|
|
|
|
const startPolling = () => {
|
|
if (pollInterval) return;
|
|
|
|
pollInterval = setInterval(() => {
|
|
void queryClient.invalidateQueries({ queryKey: ['admin-bookings', selectedComplex.id] });
|
|
}, 10000);
|
|
};
|
|
|
|
const connect = () => {
|
|
if (eventSource) {
|
|
eventSource.close();
|
|
}
|
|
|
|
if (reconnectAttempts >= maxReconnectAttempts) {
|
|
startPolling();
|
|
return;
|
|
}
|
|
|
|
const baseUrl = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000';
|
|
const url = `${baseUrl}/api/events/${encodeURIComponent(channel)}`;
|
|
|
|
eventSource = new EventSource(url);
|
|
|
|
eventSource.onerror = () => {
|
|
if (eventSource?.readyState === EventSource.CLOSED) {
|
|
reconnectAttempts++;
|
|
reconnectTimeout = setTimeout(() => {
|
|
connect();
|
|
}, 2000);
|
|
}
|
|
};
|
|
|
|
eventSource.addEventListener('message', (event) => {
|
|
if (event.data && event.data !== 'connected' && event.data !== 'ping') {
|
|
if (event.data !== lastEventId) {
|
|
lastEventId = event.data;
|
|
reconnectAttempts = 0;
|
|
void queryClient.invalidateQueries({
|
|
queryKey: ['admin-bookings', selectedComplex.id],
|
|
});
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
connect();
|
|
|
|
return () => {
|
|
if (reconnectTimeout) {
|
|
clearTimeout(reconnectTimeout);
|
|
}
|
|
if (pollInterval) {
|
|
clearInterval(pollInterval);
|
|
}
|
|
eventSource?.close();
|
|
};
|
|
}, [selectedComplex?.id, queryClient]);
|
|
|
|
if (myComplexesQuery.isLoading) {
|
|
return <p className="text-sm text-muted-foreground">Cargando panel...</p>;
|
|
}
|
|
|
|
if (myComplexesQuery.isError) {
|
|
return (
|
|
<p className="text-sm text-destructive">
|
|
{extractMessage(myComplexesQuery.error, 'No pudimos cargar tus complejos.')}
|
|
</p>
|
|
);
|
|
}
|
|
|
|
if (!selectedComplex) {
|
|
return <p className="text-sm text-muted-foreground">No tienes complejos asignados todavía.</p>;
|
|
}
|
|
|
|
return <Booking complex={selectedComplex} />;
|
|
}
|