Fix stats bar counts — use /api/stats endpoint instead of page slice
Added /api/stats to backend (returns route counts + total, respects same search/category/route filters as the main query). StatsBar now shows accurate counts across all records, not just the current page. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e97f620dff
commit
5fc745edd3
3 changed files with 27 additions and 18 deletions
13
src/App.tsx
13
src/App.tsx
|
|
@ -1,7 +1,7 @@
|
|||
import { useState, useCallback } from 'react'
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { Plus, LayoutGrid, Table2, ShoppingBag } from 'lucide-react'
|
||||
import { fetchStores, fetchFilters, createStore, updateStore, deleteStore } from './api'
|
||||
import { fetchStores, fetchFilters, fetchStats, createStore, updateStore, deleteStore } from './api'
|
||||
import type { Store, StoreFilters } from './types'
|
||||
import { StatsBar } from './components/StatsBar'
|
||||
import { FilterBar } from './components/FilterBar'
|
||||
|
|
@ -72,6 +72,15 @@ export default function App() {
|
|||
staleTime: 60_000,
|
||||
})
|
||||
|
||||
const { data: statsData } = useQuery({
|
||||
queryKey: ['stats', filters.search, filters.category, filters.route],
|
||||
queryFn: () => fetchStats({
|
||||
search: filters.search,
|
||||
category: filters.category,
|
||||
route: filters.route,
|
||||
}),
|
||||
})
|
||||
|
||||
const createMut = useMutation({
|
||||
mutationFn: createStore,
|
||||
onSuccess: () => {
|
||||
|
|
@ -163,7 +172,7 @@ export default function App() {
|
|||
</header>
|
||||
|
||||
<main className="max-w-7xl mx-auto px-4 py-5 space-y-4">
|
||||
<StatsBar stores={stores} total={pagination?.total ?? 0} />
|
||||
<StatsBar stats={statsData} />
|
||||
|
||||
<FilterBar
|
||||
filters={filters}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@ export async function fetchFilters(): Promise<FiltersResponse> {
|
|||
return data
|
||||
}
|
||||
|
||||
export async function fetchStats(params: Record<string, string> = {}): Promise<{
|
||||
auto: number; ask: number; verify: number; ignore: number; total: number
|
||||
}> {
|
||||
const { data } = await client.get('/stats', { params })
|
||||
return data
|
||||
}
|
||||
|
||||
export async function createStore(payload: Omit<Store, 'id' | 'created_at'>): Promise<{ id: number }> {
|
||||
const { data } = await client.post('/stores', payload)
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -1,28 +1,21 @@
|
|||
import { Database, Zap, HelpCircle, CheckCircle, EyeOff } from 'lucide-react'
|
||||
import type { Store } from '../types'
|
||||
|
||||
interface Props {
|
||||
stores: Store[]
|
||||
total: number
|
||||
stats: { auto: number; ask: number; verify: number; ignore: number; total: number } | undefined
|
||||
}
|
||||
|
||||
export function StatsBar({ stores, total }: Props) {
|
||||
const counts = stores.reduce(
|
||||
(acc, s) => ({ ...acc, [s.route]: (acc[s.route as keyof typeof acc] ?? 0) + 1 }),
|
||||
{ auto: 0, ask: 0, verify: 0, ignore: 0 } as Record<string, number>
|
||||
)
|
||||
|
||||
const stats = [
|
||||
{ label: 'Total', value: total, icon: Database, color: 'text-slate-400' },
|
||||
{ label: 'Auto', value: counts.auto, icon: Zap, color: 'text-green-400' },
|
||||
{ label: 'Ask', value: counts.ask, icon: HelpCircle, color: 'text-amber-400' },
|
||||
{ label: 'Verify', value: counts.verify, icon: CheckCircle, color: 'text-blue-400' },
|
||||
{ label: 'Ignore', value: counts.ignore, icon: EyeOff, color: 'text-slate-500' },
|
||||
export function StatsBar({ stats }: Props) {
|
||||
const entries = [
|
||||
{ label: 'Total', value: stats?.total ?? 0, icon: Database, color: 'text-slate-400' },
|
||||
{ label: 'Auto', value: stats?.auto ?? 0, icon: Zap, color: 'text-green-400' },
|
||||
{ label: 'Ask', value: stats?.ask ?? 0, icon: HelpCircle, color: 'text-amber-400' },
|
||||
{ label: 'Verify', value: stats?.verify ?? 0, icon: CheckCircle, color: 'text-blue-400' },
|
||||
{ label: 'Ignore', value: stats?.ignore ?? 0, icon: EyeOff, color: 'text-slate-500' },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2 sm:grid-cols-5 gap-2">
|
||||
{stats.map(({ label, value, icon: Icon, color }) => (
|
||||
{entries.map(({ label, value, icon: Icon, color }) => (
|
||||
<div key={label} className="glass rounded-xl px-4 py-3 flex items-center gap-3">
|
||||
<Icon size={16} className={`${color} shrink-0`} />
|
||||
<div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue