Big refactoring
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import * as adminApi from '../api/admin';
|
||||
import { useToast } from '../components/ui/Toast';
|
||||
import type { TickerUniverse } from '../lib/types';
|
||||
|
||||
// ── Users ──
|
||||
|
||||
@@ -89,13 +90,93 @@ export function useUpdateSetting() {
|
||||
});
|
||||
}
|
||||
|
||||
export function useRecommendationSettings() {
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'recommendation-settings'],
|
||||
queryFn: () => adminApi.getRecommendationSettings(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateRecommendationSettings() {
|
||||
const qc = useQueryClient();
|
||||
const { addToast } = useToast();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (payload: Record<string, number>) =>
|
||||
adminApi.updateRecommendationSettings(payload),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['admin', 'recommendation-settings'] });
|
||||
addToast('success', 'Recommendation settings updated');
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
addToast('error', error.message || 'Failed to update recommendation settings');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useTickerUniverseSetting() {
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'ticker-universe'],
|
||||
queryFn: () => adminApi.getTickerUniverseSetting(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateTickerUniverseSetting() {
|
||||
const qc = useQueryClient();
|
||||
const { addToast } = useToast();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (universe: TickerUniverse) => adminApi.updateTickerUniverseSetting(universe),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['admin', 'ticker-universe'] });
|
||||
addToast('success', 'Default ticker universe updated');
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
addToast('error', error.message || 'Failed to update default ticker universe');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useBootstrapTickers() {
|
||||
const qc = useQueryClient();
|
||||
const { addToast } = useToast();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ universe, pruneMissing }: { universe: TickerUniverse; pruneMissing: boolean }) =>
|
||||
adminApi.bootstrapTickers(universe, pruneMissing),
|
||||
onSuccess: (result) => {
|
||||
qc.invalidateQueries({ queryKey: ['tickers'] });
|
||||
qc.invalidateQueries({ queryKey: ['admin', 'ticker-universe'] });
|
||||
addToast(
|
||||
'success',
|
||||
`Bootstrap done: +${result.added}, existing ${result.already_tracked}, deleted ${result.deleted}`,
|
||||
);
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
addToast('error', error.message || 'Failed to bootstrap tickers');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ── Jobs ──
|
||||
|
||||
export function useJobs() {
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'jobs'],
|
||||
queryFn: () => adminApi.listJobs(),
|
||||
refetchInterval: 15_000,
|
||||
refetchInterval: (query) => {
|
||||
const jobs = (query.state.data ?? []) as adminApi.JobStatus[];
|
||||
const hasRunning = jobs.some((job) => job.running);
|
||||
return hasRunning ? 2_000 : 15_000;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function usePipelineReadiness() {
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'pipeline-readiness'],
|
||||
queryFn: () => adminApi.getPipelineReadiness(),
|
||||
refetchInterval: 20_000,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -121,9 +202,13 @@ export function useTriggerJob() {
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (jobName: string) => adminApi.triggerJob(jobName),
|
||||
onSuccess: () => {
|
||||
onSuccess: (result) => {
|
||||
qc.invalidateQueries({ queryKey: ['admin', 'jobs'] });
|
||||
addToast('success', 'Job triggered successfully');
|
||||
if (result.status === 'triggered') {
|
||||
addToast('success', result.message || 'Job triggered successfully');
|
||||
return;
|
||||
}
|
||||
addToast('info', result.message || 'Job could not be triggered');
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
addToast('error', error.message || 'Failed to trigger job');
|
||||
|
||||
42
frontend/src/hooks/useFetchSymbolData.ts
Normal file
42
frontend/src/hooks/useFetchSymbolData.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { fetchData, type FetchDataResult } from '../api/ingestion';
|
||||
import { useToast } from '../components/ui/Toast';
|
||||
import { summarizeIngestionResult } from '../lib/ingestionStatus';
|
||||
|
||||
interface UseFetchSymbolDataOptions {
|
||||
includeSymbolPrefix?: boolean;
|
||||
invalidatePipelineReadiness?: boolean;
|
||||
}
|
||||
|
||||
export function useFetchSymbolData(options: UseFetchSymbolDataOptions = {}) {
|
||||
const { includeSymbolPrefix = false, invalidatePipelineReadiness = false } = options;
|
||||
const queryClient = useQueryClient();
|
||||
const { addToast } = useToast();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (symbol: string) => fetchData(symbol),
|
||||
onSuccess: (result: FetchDataResult, symbol: string) => {
|
||||
const normalized = symbol.toUpperCase();
|
||||
const summary = summarizeIngestionResult(result, normalized);
|
||||
const toastMessage = includeSymbolPrefix
|
||||
? `${normalized}: ${summary.message}`
|
||||
: summary.message;
|
||||
addToast(summary.toastType, toastMessage);
|
||||
|
||||
queryClient.invalidateQueries({ queryKey: ['ohlcv', symbol] });
|
||||
queryClient.invalidateQueries({ queryKey: ['sentiment', symbol] });
|
||||
queryClient.invalidateQueries({ queryKey: ['fundamentals', symbol] });
|
||||
queryClient.invalidateQueries({ queryKey: ['sr-levels', symbol] });
|
||||
queryClient.invalidateQueries({ queryKey: ['scores', symbol] });
|
||||
|
||||
if (invalidatePipelineReadiness) {
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'pipeline-readiness'] });
|
||||
}
|
||||
},
|
||||
onError: (err: Error, symbol: string) => {
|
||||
const normalized = symbol.toUpperCase();
|
||||
const prefix = includeSymbolPrefix ? `${normalized}: ` : '';
|
||||
addToast('error', `${prefix}${err.message || 'Failed to fetch data'}`);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -38,8 +38,8 @@ export function useTickerDetail(symbol: string) {
|
||||
});
|
||||
|
||||
const trades = useQuery({
|
||||
queryKey: ['trades'],
|
||||
queryFn: () => tradesApi.list(),
|
||||
queryKey: ['trades', symbol],
|
||||
queryFn: () => tradesApi.bySymbol(symbol),
|
||||
enabled: !!symbol,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user