feat(scheduling): add 近期已干预 summary card (last 7 days)
Restore 替换建议 card and add a new emerald 近期已干预 card. Clicking opens the history modal pre-filtered to the last 7 days (excluding cancelled) via a toggle chip users can switch off. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -252,6 +252,7 @@ export function generateSuggestions(
|
||||
hopelessCount: hopeless.length,
|
||||
suggestionCount: filteredSuggestions.length,
|
||||
estimatedGain,
|
||||
recentInterventionCount: 0,
|
||||
};
|
||||
|
||||
return { suggestions: filteredSuggestions, summary };
|
||||
|
||||
@@ -34,6 +34,18 @@ function rowToRecord(row: any): NotificationRecord {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Count non-cancelled interventions created within the last 7 days.
|
||||
*/
|
||||
export async function fetchRecentInterventionCount(): Promise<number> {
|
||||
const [rows] = (await pool.execute(
|
||||
`SELECT COUNT(*) AS cnt FROM tab_scheduling_notifications
|
||||
WHERE status != 'cancelled'
|
||||
AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)`,
|
||||
)) as [any[], unknown];
|
||||
return rows.length > 0 ? Number(rows[0].cnt) || 0 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch notification status map for the currently-visible (suggestion, candidate) pairs.
|
||||
* Key: `${suggestionId}::${candidatePlate}` → latest non-cancelled notification.
|
||||
|
||||
@@ -5,7 +5,7 @@ import { fetchVehicleInfoMap } from '../mileage/vehicle-info.js';
|
||||
import { mapRegion } from '../vehicles.js';
|
||||
import { filterByPermission, maskCustomerNames } from '../../auth/permissions.js';
|
||||
import { classifyVehicle, generateSuggestions } from './algorithm.js';
|
||||
import { fetchActiveNotificationMap } from './notify.js';
|
||||
import { fetchActiveNotificationMap, fetchRecentInterventionCount } from './notify.js';
|
||||
import type { EnrichedVehicle, InventoryVehicle, SchedulingResponse, SchedulingSummary } from './types.js';
|
||||
import type { AuthUser } from '../../auth/types.js';
|
||||
|
||||
@@ -342,6 +342,7 @@ app.get('/', async (c) => {
|
||||
// Recalculate summary based on permission-filtered results
|
||||
const filteredQualified = masked.filter((s: any) => s.type === 'replace_qualified').length;
|
||||
const filteredHopeless = masked.filter((s: any) => s.type === 'rescue_hopeless').length;
|
||||
const recentInterventionCount = await fetchRecentInterventionCount();
|
||||
const filteredSummary: SchedulingSummary = {
|
||||
qualifiedCount: summary.qualifiedCount,
|
||||
hopelessCount: summary.hopelessCount,
|
||||
@@ -349,6 +350,7 @@ app.get('/', async (c) => {
|
||||
estimatedGain: masked.filter((s: any) =>
|
||||
s.candidates?.some((c: any) => c.canQualifyAfterSwap)
|
||||
).length,
|
||||
recentInterventionCount,
|
||||
};
|
||||
|
||||
const response: SchedulingResponse = {
|
||||
@@ -362,7 +364,7 @@ app.get('/', async (c) => {
|
||||
console.error('scheduling suggestions error:', e);
|
||||
return c.json(
|
||||
{
|
||||
summary: { qualifiedCount: 0, hopelessCount: 0, suggestionCount: 0, estimatedGain: 0 },
|
||||
summary: { qualifiedCount: 0, hopelessCount: 0, suggestionCount: 0, estimatedGain: 0, recentInterventionCount: 0 },
|
||||
suggestions: [],
|
||||
targets: [],
|
||||
} satisfies SchedulingResponse,
|
||||
|
||||
Reference in New Issue
Block a user