Auto-recheck parity after scrape + manual Recheck button + faster badge
Parity logic: - Extract _fetch_latest_alerts() and _upsert_alerts() helpers so the alert upsert loop is no longer duplicated between the daily job and per-date runs - Add run_parity_check_for_date(date) which runs the full comparison + alert upsert for a single date Scraper integration: - _safe_parity_recheck(date) wrapper (never raises) called after each successful date in both search-results and hotel-page workers; hotel-page mode waits until all hotels for the date are done before rechecking API: - POST /competitors/parity/check-date?rate_date=YYYY-MM-DD for manual recheck Frontend: - Recheck button on every parity alert row (all statuses); invalidates alerts list and badge count on success - parity-alert-count badge polls every 15s (was 60s) so new alerts from the scheduled job or post-scrape rechecks appear quickly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8a80c66a3b
commit
cab1a39503
5 changed files with 146 additions and 66 deletions
|
|
@ -23,7 +23,7 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|||
const { data: alertCount } = useQuery<number>({
|
||||
queryKey: ['parity-alert-count'],
|
||||
queryFn: () => api.get('/competitors/parity/alerts?status=active').then(r => r.data.length),
|
||||
refetchInterval: 60_000,
|
||||
refetchInterval: 15_000,
|
||||
enabled: can(user, 'view_competitors'),
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -843,6 +843,15 @@ const ParityAlertsTab: React.FC = () => {
|
|||
},
|
||||
})
|
||||
|
||||
const recheckMutation = useMutation({
|
||||
mutationFn: async (rateDate: string) =>
|
||||
(await api.post(`/competitors/parity/check-date?rate_date=${rateDate}`)).data,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['parity-alerts'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['parity-alert-count'] })
|
||||
},
|
||||
})
|
||||
|
||||
const markupUnit = parityConfig?.['parity_markup_unit'] ?? 'pct'
|
||||
const toleranceUnit = parityConfig?.['parity_tolerance_unit'] ?? 'pct'
|
||||
const markupVal = parityConfig?.['parity_markup_value'] ?? parityConfig?.['parity_expected_markup_pct'] ?? '0'
|
||||
|
|
@ -945,19 +954,29 @@ const ParityAlertsTab: React.FC = () => {
|
|||
{a.alert_status}
|
||||
</span>
|
||||
</td>
|
||||
<td style={tdStyle}>
|
||||
{a.alert_status === 'active' && (
|
||||
<td style={mergeStyles(tdStyle, { whiteSpace: 'nowrap' })}>
|
||||
<div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
|
||||
{a.alert_status === 'active' && (
|
||||
<button
|
||||
style={buttonStyle('outline', 'small')}
|
||||
disabled={ackMutation.isPending || recheckMutation.isPending}
|
||||
onClick={() => ackMutation.mutate(a.id)}
|
||||
>
|
||||
Acknowledge
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
style={buttonStyle('outline', 'small')}
|
||||
disabled={ackMutation.isPending}
|
||||
onClick={() => ackMutation.mutate(a.id)}
|
||||
disabled={recheckMutation.isPending || ackMutation.isPending}
|
||||
onClick={() => recheckMutation.mutate(a.rate_date)}
|
||||
title="Re-run parity check using the latest scraped rates for this date"
|
||||
>
|
||||
Acknowledge
|
||||
Recheck
|
||||
</button>
|
||||
)}
|
||||
{a.alert_status === 'acknowledged' && a.acknowledged_by && (
|
||||
<span style={{ fontSize: 11, color: 'var(--text-mid)' }}>by {a.acknowledged_by}</span>
|
||||
)}
|
||||
{a.alert_status === 'acknowledged' && a.acknowledged_by && (
|
||||
<span style={{ fontSize: 11, color: 'var(--text-mid)' }}>by {a.acknowledged_by}</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue