"use client"; import { CalendarClock, ChevronDown, ChevronRight, ClipboardList, History, XCircle } from "lucide-react"; import { AnimatePresence, motion } from "motion/react"; import { useCallback, useEffect, useMemo, useState } from "react"; import { cn } from "@/lib/utils"; import { MAP_READABLE_SURFACE_STRONG_CLASS_NAME } from "@/features/map/core/components/map-control-styles"; import { showMapNotice } from "@/features/map/core/components/notice"; import { isGeneratedScheduledConditionSessionId } from "@/features/workbench/data/scheduled-conditions"; import { createConditionConversationPrompt } from "@/features/workbench/utils/scheduled-condition-prompts"; import type { ScheduledConditionItem } from "@/features/workbench/types"; import { DropdownMenu, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuTrigger } from "@/shared/ui/dropdown-menu"; import { ConditionDetailPanel, StatusPill } from "./scheduled-condition-detail-panel"; import { CONDITION_FILTER_ALL, createStatusFilterOptions, createTaskFilterOptions, formatTime, getConditionTime, getStatusIcon, getStatusIconClassName, groupScheduledConditions, type ConditionStatusFilterOption, type ConditionStatusFilterValue, type ConditionTaskFilterOption, type ConditionTaskFilterValue } from "./scheduled-condition-feed-utils"; type ScheduledConditionFeedProps = { conditions?: ScheduledConditionItem[]; expanded?: boolean; focusRequest?: ScheduledConditionFeedFocusRequest | null; loading?: boolean; selectedConditionId?: string | null; onExpandedChange?: (expanded: boolean) => void; onFocusRequestHandled?: (requestId: number) => void; onSelectedConditionChange?: (conditionId: string | null) => void; onExpandAgent: () => void; onLoadHistorySession: (sessionId: string) => void | Promise; onSubmitPrompt: (prompt: string) => void | Promise; }; type ScheduledConditionFeedFocusRequest = { conditionId: string; requestId: number; }; export function ScheduledConditionFeed({ conditions = [], expanded: controlledExpanded, focusRequest, loading = false, selectedConditionId: controlledSelectedConditionId, onExpandedChange, onFocusRequestHandled, onSelectedConditionChange, onExpandAgent, onLoadHistorySession, onSubmitPrompt }: ScheduledConditionFeedProps) { const [uncontrolledExpanded, setUncontrolledExpanded] = useState(false); const [uncontrolledSelectedConditionId, setUncontrolledSelectedConditionId] = useState(null); const [taskFilter, setTaskFilter] = useState(CONDITION_FILTER_ALL); const [statusFilter, setStatusFilter] = useState(CONDITION_FILTER_ALL); const expanded = controlledExpanded ?? uncontrolledExpanded; const selectedConditionId = controlledSelectedConditionId ?? uncontrolledSelectedConditionId; const { recentConditions: allRecentConditions, futureWorkOrders: allFutureWorkOrders } = useMemo( () => groupScheduledConditions(conditions), [conditions] ); const allTimelineConditions = useMemo( () => [...allRecentConditions, ...allFutureWorkOrders], [allFutureWorkOrders, allRecentConditions] ); const taskFilterOptions = useMemo(() => createTaskFilterOptions(conditions), [conditions]); const taskFilteredConditions = useMemo( () => conditions.filter((condition) => taskFilter === CONDITION_FILTER_ALL || condition.title === taskFilter), [conditions, taskFilter] ); const statusFilterOptions = useMemo(() => createStatusFilterOptions(taskFilteredConditions), [taskFilteredConditions]); const taskFilteredConditionCount = taskFilteredConditions.length; const filteredConditions = useMemo( () => taskFilteredConditions.filter( (condition) => statusFilter === CONDITION_FILTER_ALL || condition.status === statusFilter ), [statusFilter, taskFilteredConditions] ); const { recentConditions, futureWorkOrders } = useMemo(() => groupScheduledConditions(filteredConditions), [filteredConditions]); const timelineConditions = useMemo( () => [...recentConditions, ...futureWorkOrders], [futureWorkOrders, recentConditions] ); const filterActive = taskFilter !== CONDITION_FILTER_ALL || statusFilter !== CONDITION_FILTER_ALL; const totalConditionCount = allRecentConditions.length + allFutureWorkOrders.length; const filteredConditionCount = recentConditions.length + futureWorkOrders.length; const headerSummary = loading ? "正在加载工况任务" : expanded && filterActive ? `筛选 ${filteredConditionCount}/${totalConditionCount} 条 · 预约 ${futureWorkOrders.length}/${allFutureWorkOrders.length} 条` : expanded ? `最近 ${recentConditions.length} 条 · 预约 ${futureWorkOrders.length} 条` : `最近 ${recentConditions.length} 条`; const selectedCondition = timelineConditions.find((condition) => condition.id === selectedConditionId) ?? recentConditions[0] ?? futureWorkOrders[0] ?? null; const focusConditionId = focusRequest?.conditionId; const focusRequestId = focusRequest?.requestId; const updateExpanded = useCallback( (nextExpanded: boolean) => { if (controlledExpanded === undefined) { setUncontrolledExpanded(nextExpanded); } onExpandedChange?.(nextExpanded); }, [controlledExpanded, onExpandedChange] ); const updateSelectedConditionId = useCallback( (nextConditionId: string | null) => { if (controlledSelectedConditionId === undefined) { setUncontrolledSelectedConditionId(nextConditionId); } onSelectedConditionChange?.(nextConditionId); }, [controlledSelectedConditionId, onSelectedConditionChange] ); useEffect(() => { if (taskFilter === CONDITION_FILTER_ALL) { return; } if (!taskFilterOptions.some((option) => option.value === taskFilter)) { setTaskFilter(CONDITION_FILTER_ALL); } }, [taskFilter, taskFilterOptions]); useEffect(() => { if (!expanded) { return; } const nextSelectedConditionId = selectedCondition?.id ?? null; if (selectedConditionId !== nextSelectedConditionId) { updateSelectedConditionId(nextSelectedConditionId); } }, [expanded, selectedCondition?.id, selectedConditionId, updateSelectedConditionId]); useEffect(() => { if (!focusConditionId || focusRequestId === undefined) { return; } const targetCondition = allTimelineConditions.find((condition) => condition.id === focusConditionId); if (!targetCondition) { return; } setTaskFilter(CONDITION_FILTER_ALL); setStatusFilter(CONDITION_FILTER_ALL); updateSelectedConditionId(targetCondition.id); updateExpanded(true); onFocusRequestHandled?.(focusRequestId); }, [allTimelineConditions, focusConditionId, focusRequestId, onFocusRequestHandled, updateExpanded, updateSelectedConditionId]); function handleToggleExpanded() { updateExpanded(!expanded); updateSelectedConditionId(selectedConditionId ?? recentConditions[0]?.id ?? futureWorkOrders[0]?.id ?? null); } function handleSelectTimelineCondition(conditionId: string) { updateSelectedConditionId(conditionId); if (!expanded) { updateExpanded(true); } } function handleContinueConversation(condition: ScheduledConditionItem) { if (condition.kind === "work_order") { return; } onExpandAgent(); if (!condition.sessionId) { showMapNotice({ id: "scheduled-condition-missing-session", tone: "error", title: "无法打开历史会话", message: "这条工况没有可加载的会话记录。" }); return; } if (isGeneratedScheduledConditionSessionId(condition.sessionId)) { void Promise.resolve(onSubmitPrompt(createConditionConversationPrompt(condition))) .then(() => { showMapNotice({ id: "scheduled-condition-generated-session", tone: "success", title: "已发起工况对话", message: "已将当前工况上下文发送到 Agent 面板。" }); }) .catch((submitError) => { showMapNotice({ id: "scheduled-condition-generated-session", tone: "error", title: "工况对话发起失败", message: submitError instanceof Error ? submitError.message : "无法将当前工况发送到 Agent。" }); }); return; } void Promise.resolve(onLoadHistorySession(condition.sessionId)); } return (

工况任务

{headerSummary}

{loading && expanded ? ( ) : expanded && selectedCondition ? ( handleContinueConversation(selectedCondition)} /> ) : null}
{ setTaskFilter(CONDITION_FILTER_ALL); setStatusFilter(CONDITION_FILTER_ALL); }} onSelectCondition={handleSelectTimelineCondition} />
); } type ConditionTimelinePanelProps = { expanded: boolean; loading: boolean; recentConditions: ScheduledConditionItem[]; futureWorkOrders: ScheduledConditionItem[]; taskFilter: ConditionTaskFilterValue; statusFilter: ConditionStatusFilterValue; taskFilterOptions: ConditionTaskFilterOption[]; statusFilterOptions: ConditionStatusFilterOption[]; selectedConditionId: string | null; totalConditionCount: number; taskFilteredConditionCount: number; filteredConditionCount: number; onTaskFilterChange: (value: ConditionTaskFilterValue) => void; onStatusFilterChange: (value: ConditionStatusFilterValue) => void; onResetFilters: () => void; onSelectCondition: (conditionId: string) => void; }; function ConditionTimelinePanel({ expanded, loading, recentConditions, futureWorkOrders, taskFilter, statusFilter, taskFilterOptions, statusFilterOptions, selectedConditionId, totalConditionCount, taskFilteredConditionCount, filteredConditionCount, onTaskFilterChange, onStatusFilterChange, onResetFilters, onSelectCondition }: ConditionTimelinePanelProps) { const filterActive = taskFilter !== CONDITION_FILTER_ALL || statusFilter !== CONDITION_FILTER_ALL; return (
{expanded && !loading ? ( ) : null} {expanded && !loading && futureWorkOrders.length > 0 ? (
) : null}
{loading ? ( ) : recentConditions.length > 0 ? ( ) : ( )}
); } type ConditionFilterBarProps = { taskFilter: ConditionTaskFilterValue; statusFilter: ConditionStatusFilterValue; taskFilterOptions: ConditionTaskFilterOption[]; statusFilterOptions: ConditionStatusFilterOption[]; filteredConditionCount: number; totalConditionCount: number; taskFilteredConditionCount: number; onTaskFilterChange: (value: ConditionTaskFilterValue) => void; onStatusFilterChange: (value: ConditionStatusFilterValue) => void; onResetFilters: () => void; }; function ConditionFilterBar({ taskFilter, statusFilter, taskFilterOptions, statusFilterOptions, filteredConditionCount, totalConditionCount, taskFilteredConditionCount, onTaskFilterChange, onStatusFilterChange, onResetFilters }: ConditionFilterBarProps) { const filterActive = taskFilter !== CONDITION_FILTER_ALL || statusFilter !== CONDITION_FILTER_ALL; return (
筛选 {filteredConditionCount}/{totalConditionCount}
onStatusFilterChange(value as ConditionStatusFilterValue)} options={[ { value: CONDITION_FILTER_ALL, label: "全部状态", count: taskFilteredConditionCount }, ...statusFilterOptions ]} />
{filterActive ? ( ) : null}
); } type ConditionFilterDropdownProps = { label: string; value: TValue; options: { value: TValue; label: string; count: number; }[]; onValueChange: (value: TValue) => void; }; function ConditionFilterDropdown({ label, value, options, onValueChange }: ConditionFilterDropdownProps) { const selectedOption = options.find((option) => option.value === value) ?? options[0]; return (
{label} onValueChange(nextValue as TValue)}> {options.map((option) => ( {option.label} {option.count} ))}
); } function ConditionEmptyState({ title, description }: { title: string; description: string }) { return (

{title}

{description}

); } function ConditionTimelineSkeleton({ rows }: { rows: number }) { return (
{Array.from({ length: rows }).map((_, index) => (
{index < rows - 1 ? (
))}
); } function ConditionDetailSkeleton() { return (
); } function DetailScroll({ children }: { children: React.ReactNode }) { return (
{children}
); } type ConditionGroupHeaderProps = { icon: typeof History; title: string; count: number; compact?: boolean; }; function ConditionGroupHeader({ icon: Icon, title, count, compact = false }: ConditionGroupHeaderProps) { return (
{count}
); } type ConditionRowsProps = { conditions: ScheduledConditionItem[]; selectedConditionId: string | null; onSelectCondition: (conditionId: string) => void; }; function ConditionRows({ conditions, selectedConditionId, onSelectCondition }: ConditionRowsProps) { return (
{conditions.map((condition, index) => ( onSelectCondition(condition.id)} /> ))}
); } type ConditionTimelineRowProps = { condition: ScheduledConditionItem; selected: boolean; isLast: boolean; onSelect: () => void; }; function ConditionTimelineRow({ condition, selected, isLast, onSelect }: ConditionTimelineRowProps) { const StatusIcon = getStatusIcon(condition.status); const isWorkOrder = condition.kind === "work_order"; return ( ); }