feat(scheduling): batch filter as multi-select pills instead of dropdown
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -28,7 +28,7 @@ function fmtRate(rate: number): string {
|
|||||||
export default function SuggestionDetail({ suggestion: s, onClose, onNotifySuccess }: Props) {
|
export default function SuggestionDetail({ suggestion: s, onClose, onNotifySuccess }: Props) {
|
||||||
const [previewCandidate, setPreviewCandidate] = useState<CandidateVehicle | null>(null);
|
const [previewCandidate, setPreviewCandidate] = useState<CandidateVehicle | null>(null);
|
||||||
const [sentPlates, setSentPlates] = useState<Set<string>>(new Set());
|
const [sentPlates, setSentPlates] = useState<Set<string>>(new Set());
|
||||||
const [batchFilter, setBatchFilter] = useState<string>('');
|
const [batchFilter, setBatchFilter] = useState<Set<string>>(new Set());
|
||||||
const [sortKey, setSortKey] = useState<SortKey>('predicted');
|
const [sortKey, setSortKey] = useState<SortKey>('predicted');
|
||||||
const [sortDir, setSortDir] = useState<SortDir>('desc');
|
const [sortDir, setSortDir] = useState<SortDir>('desc');
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ export default function SuggestionDetail({ suggestion: s, onClose, onNotifySucce
|
|||||||
// Filtered + sorted candidates
|
// Filtered + sorted candidates
|
||||||
const displayCandidates = useMemo(() => {
|
const displayCandidates = useMemo(() => {
|
||||||
let list = s.candidates;
|
let list = s.candidates;
|
||||||
if (batchFilter) list = list.filter(c => c.targetName === batchFilter);
|
if (batchFilter.size > 0) list = list.filter(c => c.targetName != null && batchFilter.has(c.targetName));
|
||||||
return [...list].sort((a, b) => {
|
return [...list].sort((a, b) => {
|
||||||
const va = sortKey === 'predicted' ? a.predictedAfterSwap : a.totalMileage;
|
const va = sortKey === 'predicted' ? a.predictedAfterSwap : a.totalMileage;
|
||||||
const vb = sortKey === 'predicted' ? b.predictedAfterSwap : b.totalMileage;
|
const vb = sortKey === 'predicted' ? b.predictedAfterSwap : b.totalMileage;
|
||||||
@@ -164,15 +164,35 @@ export default function SuggestionDetail({ suggestion: s, onClose, onNotifySucce
|
|||||||
|
|
||||||
{/* Filter + Sort controls */}
|
{/* Filter + Sort controls */}
|
||||||
<div className="flex items-center gap-2 mb-2.5 flex-wrap">
|
<div className="flex items-center gap-2 mb-2.5 flex-wrap">
|
||||||
{/* Batch filter */}
|
{/* Batch multi-select pills */}
|
||||||
<select
|
<div className="flex items-center gap-1.5 flex-wrap">
|
||||||
value={batchFilter}
|
<button
|
||||||
onChange={e => setBatchFilter(e.target.value)}
|
onClick={() => setBatchFilter(new Set())}
|
||||||
className="text-[10px] px-2 py-1 rounded-lg border border-slate-200 bg-white text-slate-600 cursor-pointer outline-none"
|
className={`text-[10px] px-2 py-1 rounded-lg border cursor-pointer transition-colors ${
|
||||||
|
batchFilter.size === 0 ? 'border-blue-300 bg-blue-50 text-blue-700 font-bold' : 'border-slate-200 text-slate-500'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<option value="">全部</option>
|
全部
|
||||||
{batchOptions.map(b => <option key={b} value={b}>{b}</option>)}
|
</button>
|
||||||
</select>
|
{batchOptions.map(b => {
|
||||||
|
const active = batchFilter.has(b);
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={b}
|
||||||
|
onClick={() => setBatchFilter(prev => {
|
||||||
|
const next = new Set(prev);
|
||||||
|
if (active) next.delete(b); else next.add(b);
|
||||||
|
return next;
|
||||||
|
})}
|
||||||
|
className={`text-[10px] px-2 py-1 rounded-lg border cursor-pointer transition-colors ${
|
||||||
|
active ? 'border-blue-300 bg-blue-50 text-blue-700 font-bold' : 'border-slate-200 text-slate-500'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{b}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Sort buttons */}
|
{/* Sort buttons */}
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in New Issue
Block a user