Polish Today dashboard loading, errors, and layout (Phase 4).

Add section error boundaries, retry banner with stale-while-revalidate, shared skeletons, responsive action layout, and org-switch refetching.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-11 00:53:40 +03:30
parent 3f2b332bd3
commit 2f8c9f0f4d
13 changed files with 312 additions and 45 deletions

View File

@@ -9,17 +9,19 @@ import type { ApiError } from '@/types/api';
interface UseTodaySummaryResult {
data: TodaySummaryData | null;
loading: boolean;
isInitialLoad: boolean;
error: ApiError | null;
reload: () => Promise<void>;
}
export function useTodaySummary(enabled = true): UseTodaySummaryResult {
export function useTodaySummary(organizationId?: string | null): UseTodaySummaryResult {
const enabled = Boolean(organizationId);
const [data, setData] = useState<TodaySummaryData | null>(null);
const [loading, setLoading] = useState(enabled);
const [error, setError] = useState<ApiError | null>(null);
const reload = useCallback(async () => {
if (!enabled) {
if (!organizationId) {
setData(null);
setLoading(false);
setError(null);
@@ -34,16 +36,28 @@ export function useTodaySummary(enabled = true): UseTodaySummaryResult {
const response = await todayApi.summary(range);
setData(response.data);
} catch (err) {
setData(null);
setError(err as ApiError);
} finally {
setLoading(false);
}
}, [enabled]);
}, [organizationId]);
useEffect(() => {
setData(null);
setError(null);
if (!organizationId) {
setLoading(false);
return;
}
setLoading(true);
void reload();
}, [reload]);
}, [organizationId, reload]);
return { data, loading, error, reload };
return {
data,
loading,
isInitialLoad: loading && !data,
error,
reload,
};
}