fix: add client-side caching and smooth loading for traffic page

- Add 5-min in-memory cache in API layer keyed by request params
- Replace blocking spinner with animated progress bar during refresh
- Keep stale data visible while fetching (opacity dim)
- Prefetch all periods in background for instant switching
This commit is contained in:
Fringg
2026-02-07 09:47:02 +03:00
parent cfb7ce72f2
commit 471c37b7b3
2 changed files with 273 additions and 102 deletions

View File

@@ -34,18 +34,65 @@ export interface ExportCsvResponse {
message: string;
}
export type TrafficParams = {
period?: number;
limit?: number;
offset?: number;
search?: string;
sort_by?: string;
sort_desc?: boolean;
tariffs?: string;
};
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
const trafficCache = new Map<string, { data: TrafficUsageResponse; timestamp: number }>();
function buildCacheKey(params: TrafficParams): string {
return JSON.stringify({
p: params.period ?? 30,
l: params.limit ?? 50,
o: params.offset ?? 0,
s: params.search ?? '',
sb: params.sort_by ?? 'total_bytes',
sd: params.sort_desc ?? true,
t: params.tariffs ?? '',
});
}
export const adminTrafficApi = {
getTrafficUsage: async (params: {
period?: number;
limit?: number;
offset?: number;
search?: string;
sort_by?: string;
sort_desc?: boolean;
tariffs?: string;
}): Promise<TrafficUsageResponse> => {
getTrafficUsage: async (
params: TrafficParams,
options?: { skipCache?: boolean },
): Promise<TrafficUsageResponse> => {
const key = buildCacheKey(params);
if (!options?.skipCache) {
const cached = trafficCache.get(key);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.data;
}
}
const response = await apiClient.get('/cabinet/admin/traffic', { params });
return response.data;
const data: TrafficUsageResponse = response.data;
trafficCache.set(key, { data, timestamp: Date.now() });
return data;
},
getCached: (params: TrafficParams): TrafficUsageResponse | null => {
const key = buildCacheKey(params);
const cached = trafficCache.get(key);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.data;
}
return null;
},
invalidateCache: () => {
trafficCache.clear();
},
exportCsv: async (data: { period: number }): Promise<ExportCsvResponse> => {