Smooth analytics page reloads
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 34s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 34s
This commit is contained in:
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Link, useOutletContext } from 'react-router-dom';
|
||||
import { Search, ChevronRight, Filter, ChevronLeft, X } from 'lucide-react';
|
||||
import type { ClientAnalyticsItem, ClientFilterOptions, ClientMetadataFilters, DateRange, RfmAnalytics, RfmClient } from '../types';
|
||||
import { fetchClientAnalytics, fetchClientFilterOptions, fetchRfmAnalytics } from '../dataService';
|
||||
import { fetchClientAnalytics, fetchClientFilterOptions, fetchRfmAnalytics, getCachedClientAnalytics, getCachedClientFilterOptions, getCachedRfmAnalytics } from '../dataService';
|
||||
import { endOfLocalDay, formatDateParam, parseLocalDateInput, rangeForDay, rangeForLastDays, rangeForPreviousDay, startOfLocalDay } from '../dateRanges';
|
||||
import type { ClientSortOption, ClientSummary } from '../analytics/clients';
|
||||
|
||||
@@ -114,11 +114,14 @@ const Clients = () => {
|
||||
const [sortBy, setSortBy] = useState<ClientSortOption>('recent');
|
||||
const [clientTypeFilter, setClientTypeFilter] = useState('all');
|
||||
const [metadataFilters, setMetadataFilters] = useState<ClientMetadataFilters>(emptyClientFilters);
|
||||
const [filterOptions, setFilterOptions] = useState<ClientFilterOptions>(emptyClientFilterOptions);
|
||||
const initialFilterOptions = getCachedClientFilterOptions(dateRange);
|
||||
const initialClientAnalytics = getCachedClientAnalytics(dateRange, emptyClientFilters);
|
||||
const initialRfmAnalytics = getCachedRfmAnalytics(dateRange, emptyClientFilters);
|
||||
const [filterOptions, setFilterOptions] = useState<ClientFilterOptions>(initialFilterOptions || emptyClientFilterOptions);
|
||||
const [isFilterMenuOpen, setIsFilterMenuOpen] = useState(false);
|
||||
const filterMenuRef = useRef<HTMLDivElement>(null);
|
||||
const [rfmAnalytics, setRfmAnalytics] = useState<RfmAnalytics | null>(null);
|
||||
const [clientAnalytics, setClientAnalytics] = useState<ClientAnalyticsItem[]>([]);
|
||||
const [rfmAnalytics, setRfmAnalytics] = useState<RfmAnalytics | null>(initialRfmAnalytics || null);
|
||||
const [clientAnalytics, setClientAnalytics] = useState<ClientAnalyticsItem[]>(initialClientAnalytics || []);
|
||||
|
||||
// Pagination state
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
@@ -128,15 +131,27 @@ const Clients = () => {
|
||||
let isMounted = true;
|
||||
|
||||
const loadClients = async () => {
|
||||
const options = await fetchClientFilterOptions(dateRange);
|
||||
const clientsData = await fetchClientAnalytics(dateRange, metadataFilters);
|
||||
const cachedOptions = getCachedClientFilterOptions(dateRange);
|
||||
const cachedClients = getCachedClientAnalytics(dateRange, metadataFilters);
|
||||
const cachedRfm = getCachedRfmAnalytics(dateRange, metadataFilters);
|
||||
|
||||
if (isMounted) {
|
||||
if (cachedOptions) setFilterOptions(cachedOptions);
|
||||
if (cachedClients) setClientAnalytics(cachedClients);
|
||||
setRfmAnalytics(cachedRfm || null);
|
||||
}
|
||||
|
||||
const filterOptionsPromise = fetchClientFilterOptions(dateRange);
|
||||
const clientsPromise = fetchClientAnalytics(dateRange, metadataFilters);
|
||||
const rfmPromise = fetchRfmAnalytics(dateRange, metadataFilters);
|
||||
const [options, clientsData] = await Promise.all([filterOptionsPromise, clientsPromise]);
|
||||
|
||||
if (isMounted) {
|
||||
setFilterOptions(options);
|
||||
setClientAnalytics(clientsData);
|
||||
setRfmAnalytics(null);
|
||||
}
|
||||
|
||||
const rfmData = await fetchRfmAnalytics(dateRange, metadataFilters);
|
||||
const rfmData = await rfmPromise;
|
||||
if (isMounted) {
|
||||
setRfmAnalytics(rfmData);
|
||||
}
|
||||
|
||||
@@ -55,9 +55,9 @@ const Dashboard = () => {
|
||||
const [serverMetrics, setServerMetrics] = useState<DashboardAnalytics | null>(null);
|
||||
const [isMetricsLoading, setIsMetricsLoading] = useState(true);
|
||||
|
||||
const loadDashboardMetrics = useCallback(async (range: DateRange) => {
|
||||
const loadDashboardMetrics = useCallback(async (range: DateRange, options?: { force?: boolean }) => {
|
||||
setIsMetricsLoading(true);
|
||||
const metrics = await fetchDashboardAnalytics(range);
|
||||
const metrics = await fetchDashboardAnalytics(range, options);
|
||||
setServerMetrics(metrics);
|
||||
setIsMetricsLoading(false);
|
||||
}, []);
|
||||
@@ -72,7 +72,7 @@ const Dashboard = () => {
|
||||
if (refreshInterval === 0) return;
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
void loadDashboardMetrics(dateRange);
|
||||
void loadDashboardMetrics(dateRange, { force: true });
|
||||
}, refreshInterval);
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
@@ -84,7 +84,7 @@ const Dashboard = () => {
|
||||
}, [dateRange, ordersData, serverMetrics]);
|
||||
|
||||
const handleManualRefresh = () => {
|
||||
void loadDashboardMetrics(dateRange);
|
||||
void loadDashboardMetrics(dateRange, { force: true });
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Link, useOutletContext } from 'react-router-dom';
|
||||
import { ChevronLeft, ChevronRight, Download, Filter, Loader2, Search, Users } from 'lucide-react';
|
||||
import DateRangePicker from '../components/DateRangePicker';
|
||||
import { exportToCSV, fetchRfmAnalytics } from '../dataService';
|
||||
import { exportToCSV, fetchRfmAnalytics, getCachedRfmAnalytics } from '../dataService';
|
||||
import type { DateRange, RfmAnalytics, RfmClient, RfmSegment } from '../types';
|
||||
|
||||
const emptyClients: RfmClient[] = [];
|
||||
@@ -127,17 +127,25 @@ const Rfm = () => {
|
||||
setRefreshInterval: (interval: number) => void;
|
||||
}>();
|
||||
|
||||
const [analytics, setAnalytics] = useState<RfmAnalytics | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const initialCachedAnalytics = getCachedRfmAnalytics(dateRange);
|
||||
const [analytics, setAnalytics] = useState<RfmAnalytics | null>(initialCachedAnalytics || null);
|
||||
const [isLoading, setIsLoading] = useState(!initialCachedAnalytics);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [segmentFilter, setSegmentFilter] = useState('all');
|
||||
const [selectedSegmentKey, setSelectedSegmentKey] = useState('');
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [itemsPerPage, setItemsPerPage] = useState(20);
|
||||
|
||||
const loadRfm = useCallback(async (range: DateRange) => {
|
||||
const loadRfm = useCallback(async (range: DateRange, options?: { force?: boolean }) => {
|
||||
const cachedAnalytics = options?.force ? undefined : getCachedRfmAnalytics(range);
|
||||
if (cachedAnalytics) {
|
||||
setAnalytics(cachedAnalytics);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
const data = await fetchRfmAnalytics(range);
|
||||
const data = await fetchRfmAnalytics(range, undefined, options);
|
||||
setAnalytics(data);
|
||||
setIsLoading(false);
|
||||
}, []);
|
||||
@@ -152,7 +160,7 @@ const Rfm = () => {
|
||||
if (refreshInterval === 0) return;
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
void loadRfm(dateRange);
|
||||
void loadRfm(dateRange, { force: true });
|
||||
}, refreshInterval);
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
@@ -276,7 +284,7 @@ const Rfm = () => {
|
||||
onChange={setDateRange}
|
||||
refreshInterval={refreshInterval}
|
||||
setRefreshInterval={setRefreshInterval}
|
||||
onManualRefresh={() => void loadRfm(dateRange)}
|
||||
onManualRefresh={() => void loadRfm(dateRange, { force: true })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user