feat(scheduling): history view, execute/cancel lifecycle, CSV export, 7d trend
- Add 调度记录 modal: lists notifications by status, supports 标记已执行 (with after-mileage + notes) and 取消 for open records - Add CSV export of filtered suggestions (UTF-8 BOM for Excel); top candidate per row picked by same-region > can-qualify preference - Compute customer 7-day average alongside 30-day baseline in a single query; show trend indicator (up/down/flat) next to 客户日均 in list and detail card Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||
import { Filter, RotateCcw, X, Search, ChevronDown, CheckSquare, Send } from 'lucide-react';
|
||||
import { Filter, RotateCcw, X, Search, ChevronDown, CheckSquare, Send, Clock, Download } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'motion/react';
|
||||
import { fetchSuggestions, sendNotifyBatch } from './api';
|
||||
import type { SchedulingResponse, SchedulingSuggestion, CandidateVehicle } from './types';
|
||||
import SuggestionList from './SuggestionList';
|
||||
import SuggestionDetail from './SuggestionDetail';
|
||||
import NotificationHistory from './NotificationHistory';
|
||||
import { exportSuggestionsCsv } from './csv-export';
|
||||
import Blur from '../../components/Blur';
|
||||
|
||||
type TypeFilter = 'all' | 'qualified' | 'hopeless';
|
||||
@@ -165,6 +167,7 @@ export default function SchedulingModule() {
|
||||
const [showBatchConfirm, setShowBatchConfirm] = useState(false);
|
||||
const [batchInFlight, setBatchInFlight] = useState(false);
|
||||
const [batchResultMsg, setBatchResultMsg] = useState<string | null>(null);
|
||||
const [showHistory, setShowHistory] = useState(false);
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
setLoading(true);
|
||||
@@ -346,6 +349,21 @@ export default function SchedulingModule() {
|
||||
className="p-1.5 text-slate-400 hover:text-slate-600 transition-colors rounded-lg hover:bg-slate-50 cursor-pointer">
|
||||
<RotateCcw size={15} className={loading ? 'animate-spin' : ''} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => exportSuggestionsCsv(filteredSuggestions)}
|
||||
disabled={filteredSuggestions.length === 0}
|
||||
className="p-1.5 text-slate-400 hover:text-slate-600 transition-colors rounded-lg hover:bg-slate-50 cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
title="导出 CSV"
|
||||
>
|
||||
<Download size={15} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowHistory(true)}
|
||||
className="p-1.5 text-slate-400 hover:text-slate-600 transition-colors rounded-lg hover:bg-slate-50 cursor-pointer"
|
||||
title="调度记录"
|
||||
>
|
||||
<Clock size={15} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (selectMode) exitSelectMode();
|
||||
@@ -486,6 +504,10 @@ export default function SchedulingModule() {
|
||||
<SuggestionDetail suggestion={selectedSuggestion} onClose={() => setSelectedSuggestion(null)} onNotifySuccess={handleNotifySuccess} />
|
||||
)}
|
||||
|
||||
{showHistory && (
|
||||
<NotificationHistory onClose={() => setShowHistory(false)} onChange={loadData} />
|
||||
)}
|
||||
|
||||
{/* Batch action bar */}
|
||||
<AnimatePresence>
|
||||
{selectMode && (
|
||||
|
||||
Reference in New Issue
Block a user