import { computed, nextTick, ref } from 'vue' import { useRouter } from 'vue-router' import { ElDialog } from 'element-plus/es/components/dialog/index.mjs' import ConfirmDialog from '../../components/shared/ConfirmDialog.vue' import TravelReimbursementInsightPanel from '../../components/travel/TravelReimbursementInsightPanel.vue' import TravelReimbursementMessageItem from '../../components/travel/TravelReimbursementMessageItem.vue' import { useSystemState } from '../../composables/useSystemState.js' import { useToast } from '../../composables/useToast.js' import { useTravelReimbursementFlow } from './useTravelReimbursementFlow.js' import { useTravelReimbursementComposerTools } from './useTravelReimbursementComposerTools.js' import { useTravelReimbursementAttachments } from './useTravelReimbursementAttachments.js' import { useTravelReimbursementSessionState } from './useTravelReimbursementSessionState.js' import { useTravelReimbursementReviewDrawer } from './useTravelReimbursementReviewDrawer.js' import { useTravelReimbursementSubmitComposer } from './useTravelReimbursementSubmitComposer.js' import { useTravelReimbursementReviewActions } from './useTravelReimbursementReviewActions.js' import { useTravelReimbursementCreateViewLifecycle } from './useTravelReimbursementCreateViewLifecycle.js' import { useTravelReimbursementCreateViewControls } from './useTravelReimbursementCreateViewControls.js' import { useTravelReimbursementCreateViewDrawerControls } from './useTravelReimbursementCreateViewDrawerControls.js' import { useTravelReimbursementCreateViewMessageHandlers } from './useTravelReimbursementCreateViewMessageHandlers.js' import { useTravelReimbursementCreateViewOperationFeedback } from './useTravelReimbursementCreateViewOperationFeedback.js' import { useTravelReimbursementCreateViewScroll } from './useTravelReimbursementCreateViewScroll.js' import { useTravelReimbursementCreateViewSessionCleanup } from './useTravelReimbursementCreateViewSessionCleanup.js' import { useTravelReimbursementCreateViewState } from './useTravelReimbursementCreateViewState.js' import { useTravelReimbursementCreateViewSuggestedActionLock } from './useTravelReimbursementCreateViewSuggestedActionLock.js' import { useTravelReimbursementCreateViewTravelCalculator } from './useTravelReimbursementCreateViewTravelCalculator.js' import { useTravelReimbursementCreateViewUi } from './useTravelReimbursementCreateViewUi.js' import { useTravelReimbursementGuidedFlow } from './useTravelReimbursementGuidedFlow.js' import { useTravelReimbursementMessageActions } from './useTravelReimbursementMessageActions.js' import { useTravelReimbursementSuggestedActions } from './useTravelReimbursementSuggestedActions.js' import { useStewardPlanFlow } from './useStewardPlanFlow.js' import { useTravelReimbursementApplicationSubmitConfirm } from './useTravelReimbursementApplicationSubmitConfirm.js' import { useTravelReimbursementStewardRuntimeDecision } from './useTravelReimbursementStewardRuntimeDecision.js' import { useTravelReimbursementApplicationPreviewDateEditor } from './useTravelReimbursementApplicationPreviewDateEditor.js' import { buildStewardFieldItems, formatStewardMissingFieldList, formatStewardOntologyFields } from './stewardPlanModel.js' import { useApplicationPreviewEditor } from './useApplicationPreviewEditor.js' import { buildStewardFieldCompletionContinuation, buildStewardFieldCompletionRawText } from './stewardFieldCompletionModel.js' import { buildOperationFeedbackPayload } from '../../composables/useOperationFeedback.js' import { recognizeOcrFiles } from '../../services/ocr.js' import { fetchAgentRunDetail } from '../../services/agentAssets.js' import { createOperationFeedback } from '../../services/operationFeedback.js' import { deleteConversation, runOrchestrator } from '../../services/orchestrator.js' import { fetchStewardPlan, fetchStewardPlanStream } from '../../services/steward.js' import { renderMarkdown } from '../../utils/markdown.js' import { clearAssistantSessionSnapshot } from '../../utils/assistantSessionSnapshot.js' import { buildLocalExtractionProgressMessages, buildLocalIntentPreview, shouldRequestExpenseIntentConfirmation, shouldRequestExpenseSceneSelection, summarizeSemanticIntentDetail } from '../../utils/reimbursementTextInference.js' import { buildExpenseIntentConfirmationActions, buildExpenseSceneSelectionActions } from '../../utils/expenseAssistantActions.js' import { ASSISTANT_SCOPE_ACTION_SWITCH } from '../../utils/assistantSessionScope.js' import { mergeComposerPrefill, resolveSuggestedActionPrefill } from '../../utils/assistantSuggestedActionPrefill.js' import { APPLICATION_TRANSPORT_MODE_OPTIONS, buildApplicationPreviewFooterMessage, buildApplicationPreviewSubmitText, buildLocalApplicationPreviewMessage, normalizeTransportModeOption } from '../../utils/expenseApplicationPreview.js' import { TRAVEL_PLANNING_ACTION_GENERATE, TRAVEL_PLANNING_ACTION_SKIP } from '../../utils/travelApplicationPlanning.js' import { calculateTravelReimbursement, createExpenseClaimItem, fetchExpenseClaims, fetchExpenseClaimAttachmentAsset, fetchExpenseClaimDetail, fetchExpenseClaimItemAttachmentMeta, uploadExpenseClaimItemAttachment } from '../../services/reimbursements.js' import { EXPENSE_TYPE_LABELS, REVIEW_SLOT_CONFIG, REVIEW_CATEGORY_PRESET_OPTIONS, REVIEW_OTHER_CATEGORY_OPTIONS, REVIEW_SCENE_OTHER_OPTION, REVIEW_SCENE_OPTIONS, DATE_INPUT_FORMAT, cloneReviewDocumentDrafts, buildReviewDocumentDrafts, normalizeReviewDocumentComparableValue, buildReviewDocumentCorrectionMessage, buildReviewDocumentCorrectionContext, cloneReviewEditFields, buildReviewFormValues, resolveReviewRecognizedSlotCards, resolveReviewMissingSlotCards, resolveReviewExtraMissingLabels, formatConfidenceLabel, resolveDocumentTypeLabel, resolveExpenseTypeLabel, buildReviewRecognizedLines, buildReviewSlotMap, resolveExpenseTypeCode, isValidIsoDateString, parseAmountNumber, normalizeAmountValue, extractAmountInputValue, inferPresetSceneFromReview, summarizeReviewScene, buildInlineReviewState, resolveReviewCategoryConfidenceScore, buildReviewCategoryOptions, buildReviewPanelConfidence, buildLocallySyncedReviewPayload, buildInlineReviewChangedLines, buildLocalReviewSavedMessage, buildReviewSubmitUserText, mergeInlineReviewFields, buildClientTimeContext, formatDraftApplyTime, formatDateInputValue, buildDraftSavedPayload, buildReviewHeadline, buildReviewSubline, buildReviewStateLabel, buildReviewStateTone, buildReviewPlainFollowupCopy, buildReviewNextStepRichCopy, buildReviewRiskLevelCounts, resolveReviewFooterActions, resolveReviewSaveDraftAction, resolveReviewNextStepAction, buildReviewPrimaryButtonLabel, buildReviewIntentText, buildReviewSceneValue, buildMissingRiskLine, buildReviewRiskSummary as buildReviewRiskSummaryModel, buildLocalReviewCompletionMessage, buildReviewRecognitionNotes, buildReviewDocumentSummaries } from './travelReimbursementReviewModel.js' import { buildBusinessTimeContextFromReviewValues, buildReviewCorrectionMessage, buildReviewFactCards, buildReviewFormContextFromPayload, buildReviewMainMessageText, buildReviewRiskConversationText, buildReviewRiskItems, canExposeReviewPanelScope, isTravelReviewPayload, normalizeReviewPanelScope, resolveReviewRiskBriefs, resolveReviewTravelTransportType } from './travelReimbursementCreateReviewModel.js' import { buildDraftAssociationQueryPayload, buildExpenseQueryHint, buildExpenseQueryWindowLabel, getExpenseQueryActivePage, getExpenseQueryTotalPages, getExpenseQueryVisibleRecords, normalizeExpenseQueryPayload } from './travelReimbursementExpenseQueryModel.js' import { MAX_ATTACHMENTS, MAX_OCR_DOCUMENTS, VISIBLE_ATTACHMENT_CHIPS, buildAgentInsight, buildErrorInsight, buildFileIdentity, buildFilePreviews, buildOcrDocumentsFromReviewPayload, buildOcrSummaryFromDocuments, buildReviewFilePreviewsFromReviewPayload, extractReviewAttachmentNames, isTemporaryPreviewUrl, mergeFilePreviews, mergeFilesWithLimit, mergeUploadAttachmentNames, mergeUploadOcrDocuments, resolveAttachmentPreviewKind, resolveDocumentPreview } from './travelReimbursementAttachmentModel.js' import { ASSISTANT_SESSION_MODE_OPTIONS, ASSISTANT_DISPLAY_NAME, FLOW_STEP_FALLBACKS, HOT_KNOWLEDGE_QUESTIONS, INTENT_LABELS, SCENARIO_LABELS, SESSION_TYPE_BUDGET, SESSION_TYPE_APPLICATION, SESSION_TYPE_APPROVAL, SESSION_TYPE_EXPENSE, SESSION_TYPE_KNOWLEDGE, SESSION_TYPE_STEWARD, canUseBudgetAssistantSession, aiAvatar, buildExpenseIntentConfirmationMessage, buildExpenseSceneSelectionMessage, buildMessageMeta, buildWelcomeInsight, createMessage, filterAssistantSessionModes, hasMeaningfulSessionMessages, resolveAssistantSessionMode, resolveKnowledgeRankLabel, resolveKnowledgeRankTone, sanitizeRequest, summarizeSemanticParseDetail, userAvatar } from './travelReimbursementConversationModel.js' const STEWARD_ASSISTANT_NAME = '小财管家' const APPLICATION_PREVIEW_FIELD_ACTION_SET = 'set_application_preview_field' const COMPOSER_TEXTAREA_HEIGHT = 36 const COMPOSER_MAX_ROWS = 5 const REVIEW_DRAWER_MODE_REVIEW = 'review' const REVIEW_DRAWER_MODE_DOCUMENTS = 'documents' const REVIEW_DRAWER_MODE_RISK = 'risk' const REVIEW_DRAWER_MODE_FLOW = 'flow' const REVIEW_NEXT_STEP_HREF = '#review-next-step' const APPLICATION_SUBMIT_HREF = '#application-submit' const REVIEW_RISK_PANEL_HREF_PREFIX = '#review-risk' const REVIEW_QUICK_EDIT_HREF = '#review-quick-edit' const FLOW_STEP_STATUS_PENDING = 'pending' const FLOW_STEP_STATUS_RUNNING = 'running' const FLOW_STEP_STATUS_COMPLETED = 'completed' const FLOW_STEP_STATUS_FAILED = 'failed' export default { name: 'TravelReimbursementCreateView', components: { ElDialog, ConfirmDialog, TravelReimbursementInsightPanel, TravelReimbursementMessageItem }, props: { initialPrompt: { type: String, default: '' }, initialPromptAutoSubmit: { type: Boolean, default: true }, initialApplicationPreview: { type: Object, default: null }, initialFiles: { type: Array, default: () => [] }, initialConversation: { type: Object, default: null }, initialBudgetContext: { type: Object, default: null }, initialSessionType: { type: String, default: '' }, entrySource: { type: String, default: 'requests' }, requestContext: { type: Object, default: null }, invalidatedDraftClaimId: { type: String, default: '' }, reopenToken: { type: Number, default: 0 } }, emits: ['close', 'draft-saved', 'request-updated'], setup(props, { emit }) { const router = useRouter() const { currentUser, refreshCurrentUserFromBackend } = useSystemState() const { toast } = useToast() const fileInputRef = ref(null) const composerTextareaRef = ref(null) const messageListRef = ref(null) const composerDraft = ref('') const submitting = ref(false) const workbenchVisible = ref(false) const closeAfterBusy = ref(false) const linkedRequest = computed(() => sanitizeRequest(props.requestContext)) const hotKnowledgeQuestions = HOT_KNOWLEDGE_QUESTIONS let sessionRuntimeRefs = {} let scrollHandlers = {} function scrollToBottom() { return scrollHandlers.scrollToBottom?.() } function handleAssistantModalAfterEnter() { return scrollHandlers.handleAssistantModalAfterEnter?.() } function adjustComposerTextareaHeight() { return scrollHandlers.adjustComposerTextareaHeight?.() } function handleComposerInput() { return scrollHandlers.handleComposerInput?.() } function handleComposerEnter(event) { return scrollHandlers.handleComposerEnter?.(event) } let sessionCleanupHandlers = {} function resetCurrentSessionState() { return sessionCleanupHandlers.resetCurrentSessionState?.() } function clearExpenseSessionForDeletedClaim(claimId) { return sessionCleanupHandlers.clearExpenseSessionForDeletedClaim?.(claimId) } let suggestedActionLockHandlers = {} function lockSuggestedActionMessage(message, action) { return suggestedActionLockHandlers.lockSuggestedActionMessage?.(message, action) ?? false } function buildMessageActionRows(message) { return suggestedActionLockHandlers.buildMessageActionRows?.(message) ?? [] } const { activeSessionType, messages, conversationId, stewardState, draftClaimId, sessionSnapshots, currentInsight, reviewFilePreviews, composerUploadIntent, guidedFlowState, insightPanelCollapsed, sessionSwitchBusy, buildEmptySessionState, resolveCurrentUserId, persistSessionState, applySessionState, switchSessionType } = useTravelReimbursementSessionState({ props, currentUser, linkedRequest, toast, composerDraft, adjustComposerTextareaHeight, scrollToBottom, getSessionRuntimeRefs: () => sessionRuntimeRefs }) const deleteSessionDialogOpen = ref(false) const applicationSubmitConfirmDialog = ref({ open: false, message: null }) const nextStepConfirmDialog = ref({ open: false, message: null, action: null }) const reviewActionBusy = ref(false) const deleteSessionBusy = ref(false) const reviewDrawerMode = ref(REVIEW_DRAWER_MODE_REVIEW) const { applicationPreviewEditor, resolveApplicationPreviewRows, resolveApplicationPreviewEditorControl, resolveApplicationPreviewEditorOptions, isApplicationPreviewEditing, isApplicationPreviewDateEditorOpen, openApplicationPreviewEditor, commitApplicationPreviewEditor, commitApplicationPreviewDateEditor, cancelApplicationPreviewEditor, setApplicationPreviewDateMode, canApplyApplicationPreviewDateSelection, handleApplicationPreviewEditorKeydown } = useApplicationPreviewEditor({ persistSessionState, toast, calculateTravelReimbursement, currentUser }) let applyLinkedApplicationPreviewDateSelection = () => false let openApplicationPreviewEditorFromUi = openApplicationPreviewEditor const { activeReviewPanelScope, activeReviewPayload, assistantHeaderDescription, assistantHeaderTitle, canDeleteCurrentSession, composerPlaceholder, currentIntentLabel, hasInsightPanelContent, insightPanelToggleLabel, isApplicationSession, isKnowledgeSession, isStewardSession, latestReviewMessage, shortcuts, showInsightPanel, showStewardInitialRecognition } = useTravelReimbursementCreateViewState({ ASSISTANT_SESSION_MODE_OPTIONS, SESSION_TYPE_APPLICATION, SESSION_TYPE_APPROVAL, SESSION_TYPE_BUDGET, SESSION_TYPE_KNOWLEDGE, SESSION_TYPE_STEWARD, activeSessionType, canExposeReviewPanelScope, conversationId, currentInsight, currentUser, filterAssistantSessionModes, getActiveFlowSteps: () => activeFlowSteps.value, hasMeaningfulSessionMessages, insightPanelCollapsed, linkedRequest, messages, normalizeReviewPanelScope, props, resolveAssistantSessionMode, submitting, workbenchVisible }) const { flowRunId, flowSteps, activeFlowSteps, visibleFlowSteps, flowRefreshBusy, completedFlowStepCount, flowOverallStatusTone, flowOverallStatusText, flowTotalDurationText, clearFlowSimulationTimers, resetFlowRun, startFlowTick, stopFlowRuntime, startFlowStep, completeFlowStep, failCurrentFlowStep, startSemanticFlowPreview, startExpenseSceneSelectionFlowPreview, startExpenseIntentConfirmationFlowPreview, startExpenseSceneSelectionAfterIntentConfirmation, startReviewActionFlowStep, startExpenseClaimDraftFlowStep, completeFlowResult, refreshFlowRunDetail, formatFlowStepDuration, resolveFlowStepStatusLabel, resolveFlowStepDetail } = useTravelReimbursementFlow({ activeSessionType, reviewDrawerMode, insightPanelCollapsed, isKnowledgeSession, fetchAgentRunDetail, buildLocalIntentPreview, buildLocalExtractionProgressMessages, summarizeSemanticIntentDetail, summarizeSemanticParseDetail, SCENARIO_LABELS, INTENT_LABELS, EXPENSE_TYPE_LABELS, FLOW_STEP_FALLBACKS, REVIEW_DRAWER_MODE_FLOW, REVIEW_DRAWER_MODE_REVIEW, FLOW_STEP_STATUS_PENDING, FLOW_STEP_STATUS_RUNNING, FLOW_STEP_STATUS_COMPLETED, FLOW_STEP_STATUS_FAILED }) const reviewRiskBriefResolver = (payload) => resolveReviewRiskBriefs(payload) const buildReviewRiskSummary = (payload) => buildReviewRiskSummaryModel(payload, reviewRiskBriefResolver) const { reviewInlineForm, reviewInlineBaseForm, reviewInlineBaseFields, reviewInlinePendingFiles, reviewInlineEditorKey, reviewInlineErrors, reviewOtherCategoryOpen, reviewDocumentDrafts, reviewDocumentBaseDrafts, activeReviewDocumentIndex, documentPreviewDialog, activeReviewFilePreviews, reviewIntentText, reviewFactCards, reviewCategoryOptions, reviewOtherCategoryOptions, reviewSelectedOtherCategory, reviewInlineDirty, reviewPanelConfidence, reviewRiskSummary, reviewRiskItems, reviewRiskEmpty, reviewOverviewDrawerAvailable, reviewDocumentDrawerAvailable, reviewRiskDrawerAvailable, reviewFlowDrawerAvailable, recognizedNarratives, reviewRecognitionNotes, reviewDocumentSummaries, reviewDocumentCount, isReviewDocumentDrawer, isReviewRiskDrawer, isReviewFlowDrawer, reviewDrawerTitle, reviewDocumentDrawerLabel, reviewDocumentDrawerIcon, reviewRiskDrawerLabel, reviewRiskDrawerIcon, reviewFlowDrawerLabel, reviewFlowDrawerIcon, activeReviewDocument, activeReviewDocumentPreview, canPreviewActiveReviewDocument, reviewDocumentDirty, reviewHasUnsavedChanges, setInlineReviewFieldError, clearInlineReviewFieldError, resetReviewDrawerFromPayload, enforceReviewDrawerAvailability, openInlineReviewEditor, closeInlineReviewEditor, commitInlineReviewEditor, selectInlineScene, selectReviewCategory, selectReviewOtherCategory, goReviewDocument, openActiveReviewDocumentPreview, closeDocumentPreview } = useTravelReimbursementReviewDrawer({ activeReviewPayload, activeReviewPanelScope, reviewFilePreviews, flowSteps: activeFlowSteps, submitting, reviewActionBusy, triggerFileUpload: (...args) => triggerFileUpload(...args), resolveDocumentPreview, buildReviewFactCards, buildReviewRiskItems, buildReviewRiskSummary, buildReviewIntentText, resolveReviewRiskBriefs, reviewDrawerMode, REVIEW_DRAWER_MODE_REVIEW, REVIEW_DRAWER_MODE_DOCUMENTS, REVIEW_DRAWER_MODE_RISK, REVIEW_DRAWER_MODE_FLOW }) const { composerDatePickerOpen, composerDateMode, composerSingleDate, composerRangeStartDate, composerRangeEndDate, composerBusinessTimeTags, composerBusinessTimeDraftTouched, composerCanApplyDateSelection, travelCalculatorOpen, travelCalculatorBusy, travelCalculatorError, travelCalculatorResult, travelCalculatorForm, travelCalculatorCanSubmit, buildComposerBusinessTimeLabel, hasComposerBusinessTimeSelection, buildComposerBusinessTimeContext, mergeBusinessTimeIntoExtraContext, syncComposerBusinessTimeToReviewCard, resolveComposerSubmitText, resolveComposerDisplaySubmitText, toggleComposerDatePicker, closeComposerDatePicker, setComposerDateMode, handleComposerDateInputChange, removeComposerBusinessTimeTag, handleComposerDatePickerOutside, applyComposerDateSelection, resolveTravelCalculatorInitialDays, resolveTravelCalculatorInitialLocation, openTravelCalculator: openTravelCalculatorInternal, toggleTravelCalculator: toggleTravelCalculatorInternal, closeTravelCalculator, formatTravelCalculatorMoney, buildTravelCalculatorResultText, submitTravelCalculator: submitTravelCalculatorInternal } = useTravelReimbursementComposerTools({ currentUser, activeReviewPayload, reviewInlineForm, latestReviewMessage, currentInsight, messages, composerDraft, composerTextareaRef, adjustComposerTextareaHeight, scrollToBottom, toast, calculateTravelReimbursement, createMessage, buildReviewSlotMap, isValidIsoDateString, buildLocallySyncedReviewPayload, formatDateInputValue, onComposerDateSelection: (...args) => applyLinkedApplicationPreviewDateSelection(...args) }) ;({ applyLinkedApplicationPreviewDateSelection, openApplicationPreviewEditorFromUi } = useTravelReimbursementApplicationPreviewDateEditor({ applicationPreviewEditor, cancelApplicationPreviewEditor, commitApplicationPreviewDateEditor, composerDateMode, composerDatePickerOpen, composerRangeEndDate, composerRangeStartDate, composerSingleDate, formatDateInputValue, isApplicationPreviewEditing, messages, openApplicationPreviewEditor, travelCalculatorOpen })) const { fileInputMode, attachedFiles, composerFilesExpanded, visibleAttachedFiles, hiddenAttachedFileCount, rememberFilePreviews, buildComposerFilePreviews, resolveActiveClaimId, restorePersistedDraftAttachmentPreviews, syncComposerFilesToDraft, triggerFileUpload, handleFilesChange, toggleAttachedFilesExpanded, removeAttachedFile, clearAttachedFiles, stopAttachmentRuntime } = useTravelReimbursementAttachments({ isKnowledgeSession, reviewFilePreviews, linkedRequest, draftClaimId, activeReviewPayload, reviewInlinePendingFiles, reviewInlineForm, reviewInlineEditorKey, composerUploadIntent, submitting, reviewActionBusy, toast, fileInputRef, createExpenseClaimItem, fetchExpenseClaimDetail, fetchExpenseClaimItemAttachmentMeta, fetchExpenseClaimAttachmentAsset, uploadExpenseClaimItemAttachment, extractReviewAttachmentNames, mergeFilesWithLimit, mergeFilePreviews, isTemporaryPreviewUrl, resolveAttachmentPreviewKind, resolveDocumentPreview, buildFilePreviews, buildFileIdentity, MAX_ATTACHMENTS, VISIBLE_ATTACHMENT_CHIPS, clearInlineReviewFieldError }) sessionRuntimeRefs = { attachedFiles, composerFilesExpanded, guidedFlowState } const { emitOperationCompleted } = useTravelReimbursementCreateViewOperationFeedback({ activeSessionType, conversationId, currentUser, props, resolveCurrentUserId }) const { confirmPendingAttachmentAssociationInternal, submitComposerInternal } = useTravelReimbursementSubmitComposer({ MAX_ATTACHMENTS, activeReviewPayload, activeSessionType, adjustComposerTextareaHeight, attachedFiles, buildAgentInsight, buildClientTimeContext, buildComposerBusinessTimeContext, buildComposerFilePreviews, buildDraftAssociationQueryPayload, buildErrorInsight, buildExpenseIntentConfirmationActions, buildExpenseIntentConfirmationMessage, buildExpenseSceneSelectionActions, buildExpenseSceneSelectionMessage, buildMessageMeta, buildOcrDocumentsFromReviewPayload, buildOcrSummaryFromDocuments, buildReviewFormContextFromPayload, clearAttachedFiles, clearFlowSimulationTimers, completeFlowResult, completeFlowStep, composerBusinessTimeDraftTouched, composerBusinessTimeTags, composerDraft, composerUploadIntent, conversationId, createMessage, currentInsight, currentUser, refreshCurrentUserFromBackend, draftClaimId, extractReviewAttachmentNames, failCurrentFlowStep, fetchExpenseClaims, fileInputRef, flowRunId, insightPanelCollapsed, isKnowledgeSession, linkedRequest, mergeBusinessTimeIntoExtraContext, mergeFilePreviews, mergeFilesWithLimit, mergeUploadAttachmentNames, mergeUploadOcrDocuments, messages, nextTick, normalizeExpenseQueryPayload, persistSessionState, props, recognizeOcrFiles, refreshFlowRunDetail, rememberFilePreviews, replaceMessage, resolveComposerDisplaySubmitText, resetFlowRun, resolveComposerSubmitText, reviewInlineForm, runOrchestrator, scrollToBottom, sessionSwitchBusy, shouldRequestExpenseIntentConfirmation, shouldRequestExpenseSceneSelection, startExpenseClaimDraftFlowStep, startExpenseIntentConfirmationFlowPreview, startExpenseSceneSelectionFlowPreview, startFlowStep, startSemanticFlowPreview, submitting, syncComposerFilesToDraft, emitOperationCompleted, emitDraftSaved: (payload) => emit('draft-saved', payload), emitRequestUpdated: (payload) => emit('request-updated', payload), toast }) let submitComposerFromMessageHandlers = null async function submitComposer(options = {}) { if (submitComposerFromMessageHandlers) { return submitComposerFromMessageHandlers(options) } return submitComposerInternal(options) } const canSubmit = computed( () => !submitting.value && !sessionSwitchBusy.value && Boolean( composerDraft.value.trim() || attachedFiles.value.length || composerBusinessTimeTags.value.length ) ) const { handleGuidedShortcut, handleGuidedComposerSubmit, handleGuidedSuggestedAction, handleSceneSelectionApplicationGate, resetGuidedFlowState } = useTravelReimbursementGuidedFlow({ guidedFlowState, messages, composerDraft, attachedFiles, composerBusinessTimeTags, composerBusinessTimeDraftTouched, fileInputRef, submitting, reviewActionBusy, sessionSwitchBusy, createMessage, nextTick, scrollToBottom, persistSessionState, clearAttachedFiles, adjustComposerTextareaHeight, buildComposerBusinessTimeContext, openTravelCalculator, lockSuggestedActionMessage, submitExistingComposer: submitComposerInternal, currentUser, refreshCurrentUserFromBackend, toast }) const { appendExpenseQueryRiskToConversation, appendReviewRiskBriefToConversation, buildApplicationDraftSummaryItems, buildApplicationPreviewFooterText, buildMessageBubbleClass, buildReviewNextStepRichCopyForMessage, canOpenDraftDetail, closeReviewNextStepConfirm, confirmReviewNextStepSubmit, copyAssistantMessage, isApplicationDraftPayload, isMessageFeedbackSelected, openApplicationDraftDetail, openReviewNextStepConfirm, queryDraftByClaimNo, resolveApplicationDraftStatusLabel, resolveApplicationPreviewMissingFields, resolveReimbursementDraftClaimNo, shouldShowAssistantMessageActions, shouldShowDraftSavedCard, speakAssistantMessage, submitOperationFeedbackForMessage } = useTravelReimbursementMessageActions({ activeSessionType, buildMessageActionRows, conversationId, createMessage, currentInsight, currentUser, draftClaimId, emit, getHandleReviewActionInternal: () => handleReviewActionInternal, latestReviewMessage, linkedRequest, messages, nextStepConfirmDialog, nextTick, persistSessionState, props, resolveActiveClaimId, resolveCurrentUserId, reviewActionBusy, router, scrollToBottom, submitComposer, submitting, toast }) const { continueStewardApplicationFieldCompletion, handleSuggestedAction, isSuggestedActionSelected, runShortcut } = useTravelReimbursementSuggestedActions({ applicationPreviewEditor, attachedFiles, buildExpenseSceneSelectionActions, buildExpenseSceneSelectionMessage, commitApplicationPreviewEditor, composerDraft, composerFilesExpanded, composerTextareaRef, composerUploadIntent, createMessage, currentUser, draftClaimId, emit, fetchExpenseClaims, handleGuidedShortcut, handleGuidedSuggestedAction, handleSceneSelectionApplicationGate, lockSuggestedActionMessage, mergeFilesWithLimit, messages, nextTick, openApplicationPreviewEditor: openApplicationPreviewEditorFromUi, persistSessionState, resolveApplicationPreviewMissingFields, reviewActionBusy, router, scrollToBottom, sessionSwitchBusy, startExpenseSceneSelectionAfterIntentConfirmation, submitComposer, submitComposerInternal, submitting, switchSessionType, toast, adjustComposerTextareaHeight }) const { canShowTravelCalculator, openTravelCalculator, submitTravelCalculator, toggleTravelCalculator } = useTravelReimbursementCreateViewTravelCalculator({ SESSION_TYPE_EXPENSE, activeSessionType, closeTravelCalculator, openTravelCalculatorInternal, submitTravelCalculatorInternal, toggleTravelCalculatorInternal, travelCalculatorOpen }) const isReviewOverviewDrawer = computed(() => reviewDrawerMode.value === REVIEW_DRAWER_MODE_REVIEW) const { emitCloseAfterLeave, handleExpenseQueryRecordClick, maybeFinalizeDeferredClose, openDeleteSessionDialog, openExpenseQueryRecord, closeDeleteSessionDialog, confirmDeleteCurrentSession, requestCloseWorkbench, setExpenseQueryPage, shiftExpenseQueryPage } = useTravelReimbursementCreateViewControls({ activeSessionType, attachedFiles, clearAssistantSessionSnapshot, closeAfterBusy, conversationId, deleteConversation, deleteSessionBusy, deleteSessionDialogOpen, draftClaimId, emitClose: () => emit('close'), getExpenseQueryActivePage, getExpenseQueryTotalPages, persistSessionState, resetCurrentSessionState, reviewActionBusy, router, resolveCurrentUserId, sessionSwitchBusy, submitComposer, submitting, toast, workbenchVisible }) useTravelReimbursementCreateViewLifecycle({ activeFlowSteps, activeReviewPanelScope, activeReviewPayload, activeSessionType, adjustComposerTextareaHeight, attachedFiles, clearExpenseSessionForDeletedClaim, clearStewardThinkingTimers, closeAfterBusy, composerDraft, composerFilesExpanded, composerUploadIntent, conversationId, currentInsight, currentUser, draftClaimId, guidedFlowState, handleComposerDatePickerOutside, hasInsightPanelContent, insightPanelCollapsed, linkedRequest, maybeFinalizeDeferredClose, mergeFilesWithLimit, messages, persistSessionState, props, rememberFilePreviews, resetReviewDrawerFromPayload, resolveActiveClaimId, restorePersistedDraftAttachmentPreviews, reviewActionBusy, reviewDocumentDrawerAvailable, reviewDrawerMode, reviewFilePreviews, reviewFlowDrawerAvailable, reviewRiskDrawerAvailable, scrollToBottom, sessionSwitchBusy, startFlowTick, stewardState, stopAttachmentRuntime, stopFlowRuntime, submitComposer, submitting, toast, workbenchVisible, REVIEW_DRAWER_MODE_DOCUMENTS, REVIEW_DRAWER_MODE_FLOW, REVIEW_DRAWER_MODE_REVIEW, REVIEW_DRAWER_MODE_RISK, SESSION_TYPE_EXPENSE }) scrollHandlers = useTravelReimbursementCreateViewScroll({ COMPOSER_MAX_ROWS, COMPOSER_TEXTAREA_HEIGHT, composerTextareaRef, messageListRef, nextTick, reviewActionBusy, sessionSwitchBusy, submitComposer, submitting }) sessionCleanupHandlers = useTravelReimbursementCreateViewSessionCleanup({ SESSION_TYPE_EXPENSE, activeSessionType, applySessionState, buildEmptySessionState, clearAssistantSessionSnapshot, resetFlowRun, resetGuidedFlowState, resolveActiveClaimId, resolveCurrentUserId, sessionSnapshots, toast }) function replaceMessage(messageId, nextMessage) { const index = messages.value.findIndex((item) => item.id === messageId) if (index === -1) { messages.value.push(nextMessage) return } messages.value.splice(index, 1, nextMessage) } const { submitStewardPlan, clearStewardThinkingTimers } = useStewardPlanFlow({ activeSessionType, attachedFiles, composerDraft, conversationId, currentUser, fileInputRef, messages, createMessage, fetchStewardPlan, fetchStewardPlanStream, nextTick, persistSessionState, replaceMessage, scrollToBottom, adjustComposerTextareaHeight, executeStewardSuggestedAction: (message, action) => handleSuggestedAction(message, action), submitting, reviewActionBusy, sessionSwitchBusy, stewardState, toast }) suggestedActionLockHandlers = useTravelReimbursementCreateViewSuggestedActionLock({ messages, persistSessionState, resolveApplicationPreviewRows }) const { switchReviewDrawerMode, switchToReviewOverviewDrawer, toggleInsightPanel, toggleReviewDocumentDrawer, toggleReviewFlowDrawer, toggleReviewRiskDrawer } = useTravelReimbursementCreateViewDrawerControls({ REVIEW_DRAWER_MODE_DOCUMENTS, REVIEW_DRAWER_MODE_FLOW, REVIEW_DRAWER_MODE_REVIEW, REVIEW_DRAWER_MODE_RISK, hasInsightPanelContent, insightPanelCollapsed, reviewDocumentDrawerAvailable, reviewDrawerMode, reviewFlowDrawerAvailable, reviewOverviewDrawerAvailable, reviewRiskDrawerAvailable }) const { closeApplicationSubmitConfirm, confirmApplicationSubmit, openApplicationSubmitConfirm, resolveStewardMissingFieldItems } = useTravelReimbursementApplicationSubmitConfirm({ activeSessionType, applicationSubmitConfirmDialog, buildStewardFieldItems, createMessage, emitDraftSaved: (payload) => emit('draft-saved', payload), formatStewardMissingFieldList, formatStewardOntologyFields, linkedRequest, messages, nextTick, persistSessionState, reviewActionBusy, scrollToBottom, submitComposer, submitting, toast }) const { handleApplicationSubmitConfirmationText, handleStewardRuntimeDecision } = useTravelReimbursementStewardRuntimeDecision({ APPLICATION_PREVIEW_FIELD_ACTION_SET, activeSessionType, adjustComposerTextareaHeight, applicationSubmitConfirmDialog, attachedFiles, composerDraft, confirmApplicationSubmit, continueStewardApplicationFieldCompletion, conversationId, createMessage, handleSuggestedAction, isStewardSession, messages, nextTick, persistSessionState, props, reviewActionBusy, resolveCurrentUserId, scrollToBottom, stewardState, submitComposer, submitComposerInternal, submitStewardPlan, submitting }) const { handleReviewActionInternal, handleSaveDraftDirectlyInternal, saveInlineReviewChangesInternal } = useTravelReimbursementReviewActions({ activeReviewPayload, buildDraftSavedPayload, buildLocalReviewCompletionMessage, buildLocalReviewSavedMessage, buildReviewCorrectionMessage, buildReviewDocumentCorrectionContext, buildReviewDocumentCorrectionMessage, buildReviewFormValues, buildReviewRiskItems, buildReviewSubmitUserText, buildLocallySyncedReviewPayload, cloneReviewDocumentDrafts, cloneReviewEditFields, commitInlineReviewEditor, createMessage, currentInsight, currentUser, emit, latestReviewMessage, linkedRequest, mergeInlineReviewFields, messages, nextTick, reviewActionBusy, reviewDocumentBaseDrafts, reviewDocumentDrafts, reviewHasUnsavedChanges, reviewInlineBaseFields, reviewInlineBaseForm, reviewInlineEditorKey, reviewInlineForm, reviewInlinePendingFiles, scrollToBottom, sessionSwitchBusy, submitComposer, submitting }) const { askHotKnowledgeQuestion, buildReviewPlainFollowupForMessage, canUseInlineSaveDraft, handleAssistantMarkdownClick, handleInlineSaveDraft, handleReviewAction, handleSaveDraftDirectly, isDraftSavedReviewMessage, saveInlineReviewChanges, submitComposer: submitComposerMessageHandler } = useTravelReimbursementCreateViewMessageHandlers({ APPLICATION_SUBMIT_HREF, REVIEW_NEXT_STEP_HREF, REVIEW_QUICK_EDIT_HREF, REVIEW_RISK_PANEL_HREF_PREFIX, REVIEW_DRAWER_MODE_REVIEW, REVIEW_DRAWER_MODE_RISK, activeReviewPayload, buildReviewPlainFollowupCopy, confirmPendingAttachmentAssociationInternal, draftClaimId, handleApplicationSubmitConfirmationText, handleGuidedComposerSubmit, handleReviewActionInternal, handleSaveDraftDirectlyInternal, handleStewardRuntimeDecision, handleInlineSaveDraftDirectly: handleSaveDraftDirectlyInternal, handleGuidedStewardPlan: (options) => isStewardSession.value && !options.skipStewardPlan && submitStewardPlan(options), isKnowledgeSession, openApplicationSubmitConfirm, openReviewNextStepConfirm, reviewActionBusy, reviewHasUnsavedChanges, reviewOverviewDrawerAvailable, reviewRiskDrawerAvailable, resolveActiveClaimId, resolveReviewSaveDraftAction, router, saveInlineReviewChangesInternal, sessionSwitchBusy, submitComposerInternal, submitting, switchReviewDrawerMode, toast }) submitComposerFromMessageHandlers = submitComposerMessageHandler const { insightPanelUi, messageItemUi } = useTravelReimbursementCreateViewUi({ ASSISTANT_DISPLAY_NAME, DATE_INPUT_FORMAT, REVIEW_SCENE_OPTIONS, REVIEW_SCENE_OTHER_OPTION, activeFlowSteps, activeReviewDocument, activeReviewDocumentIndex, activeReviewDocumentPreview, activeReviewPayload, activeSessionType, aiAvatar, appendExpenseQueryRiskToConversation, appendReviewRiskBriefToConversation, applicationPreviewEditor, askHotKnowledgeQuestion, buildApplicationDraftSummaryItems, buildApplicationPreviewFooterText, buildExpenseQueryHint, buildExpenseQueryWindowLabel, buildMessageBubbleClass, buildReviewMainMessageText, buildReviewNextStepRichCopyForMessage, buildReviewPlainFollowupForMessage, buildReviewPrimaryButtonLabel, canApplyApplicationPreviewDateSelection, canOpenDraftDetail, canPreviewActiveReviewDocument, canUseInlineSaveDraft, clearInlineReviewFieldError, commitApplicationPreviewDateEditor, commitApplicationPreviewEditor, commitInlineReviewEditor, copyAssistantMessage, currentInsight, currentIntentLabel, deleteSessionBusy, flowOverallStatusText, flowOverallStatusTone, flowRefreshBusy, flowRunId, flowTotalDurationText, formatFlowStepDuration, getExpenseQueryActivePage, getExpenseQueryTotalPages, getExpenseQueryVisibleRecords, goReviewDocument, handleAssistantMarkdownClick, handleExpenseQueryRecordClick, handleInlineSaveDraft, handleReviewAction, handleSuggestedAction, isApplicationDraftPayload, isApplicationPreviewDateEditorOpen, isApplicationPreviewEditing, isKnowledgeSession, isMessageFeedbackSelected, isReviewDocumentDrawer, isReviewFlowDrawer, isReviewOverviewDrawer, isReviewRiskDrawer, isSuggestedActionSelected, openActiveReviewDocumentPreview, openApplicationDraftDetail, openApplicationPreviewEditorFromUi, openInlineReviewEditor, refreshFlowRunDetail, renderMarkdown, resolveApplicationDraftStatusLabel, resolveApplicationPreviewEditorControl, resolveApplicationPreviewEditorOptions, resolveApplicationPreviewMissingFields, resolveApplicationPreviewRows, resolveFlowStepDetail, resolveFlowStepStatusLabel, resolveKnowledgeRankLabel, resolveKnowledgeRankTone, resolveReimbursementDraftClaimNo, resolveReviewFooterActions, resolveStewardMissingFieldItems, reviewActionBusy, reviewCategoryOptions, reviewDocumentCount, reviewDocumentDrawerAvailable, reviewDocumentDrawerIcon, reviewDrawerMode, reviewDrawerTitle, reviewFactCards, reviewFlowDrawerAvailable, reviewFlowDrawerIcon, reviewHasUnsavedChanges, reviewInlineEditorKey, reviewInlineErrors, reviewInlineForm, reviewInlinePendingFiles, reviewOtherCategoryOpen, reviewOtherCategoryOptions, reviewOverviewDrawerAvailable, reviewPanelConfidence, reviewRiskDrawerAvailable, reviewRiskDrawerIcon, reviewRiskEmpty, reviewRiskItems, reviewRiskSummary, reviewSelectedOtherCategory, runShortcut, saveInlineReviewChanges, selectInlineScene, selectReviewCategory, selectReviewOtherCategory, sessionSwitchBusy, setApplicationPreviewDateMode, setExpenseQueryPage, shiftExpenseQueryPage, shouldShowAssistantMessageActions, shouldShowDraftSavedCard, speakAssistantMessage, submitOperationFeedbackForMessage, submitting, switchToReviewOverviewDrawer, toggleReviewDocumentDrawer, toggleReviewFlowDrawer, toggleReviewRiskDrawer, userAvatar, visibleFlowSteps }) return { emit, messageItemUi, insightPanelUi, ASSISTANT_DISPLAY_NAME, aiAvatar, userAvatar, fileInputRef, composerTextareaRef, messageListRef, composerDraft, composerDatePickerOpen, composerDateMode, composerSingleDate, composerRangeStartDate, composerRangeEndDate, composerBusinessTimeTags, composerCanApplyDateSelection, toggleComposerDatePicker, closeComposerDatePicker, setComposerDateMode, handleComposerDateInputChange, removeComposerBusinessTimeTag, flowSteps, flowRunId, flowRefreshBusy, completedFlowStepCount, flowOverallStatusTone, flowOverallStatusText, flowTotalDurationText, attachedFiles, composerFilesExpanded, visibleAttachedFiles, hiddenAttachedFileCount, submitting, sessionSwitchBusy, messages, currentInsight, linkedRequest, canSubmit, activeSessionType, isKnowledgeSession, isStewardSession, showStewardInitialRecognition, hotKnowledgeQuestions, hasInsightPanelContent, showInsightPanel, insightPanelToggleLabel, assistantHeaderTitle, assistantHeaderDescription, composerPlaceholder, currentIntentLabel, canDeleteCurrentSession, latestReviewMessage, activeReviewPayload, activeReviewPanelScope, activeReviewFilePreviews, reviewDrawerMode, isReviewOverviewDrawer, isReviewDocumentDrawer, isReviewRiskDrawer, isReviewFlowDrawer, reviewDrawerTitle, reviewOverviewDrawerAvailable, reviewDocumentDrawerAvailable, reviewRiskDrawerAvailable, reviewFlowDrawerAvailable, reviewDocumentDrawerLabel, reviewDocumentDrawerIcon, reviewRiskDrawerLabel, reviewRiskDrawerIcon, reviewFlowDrawerLabel, reviewFlowDrawerIcon, activeReviewDocument, activeReviewDocumentIndex, activeReviewDocumentPreview, canPreviewActiveReviewDocument, reviewIntentText, reviewFactCards, reviewCategoryOptions, reviewOtherCategoryOptions, reviewSelectedOtherCategory, reviewInlineDirty, reviewInlineForm, reviewInlineEditorKey, reviewInlineErrors, reviewOtherCategoryOpen, reviewInlinePendingFiles, DATE_INPUT_FORMAT, REVIEW_SCENE_OTHER_OPTION, REVIEW_SCENE_OPTIONS, REVIEW_OTHER_CATEGORY_OPTIONS, workbenchVisible, reviewPanelConfidence, reviewRiskSummary, reviewRiskItems, reviewRiskEmpty, recognizedNarratives, reviewRecognitionNotes, reviewDocumentSummaries, reviewDocumentCount, reviewDocumentDirty, reviewHasUnsavedChanges, travelCalculatorOpen, travelCalculatorBusy, travelCalculatorError, travelCalculatorResult, travelCalculatorForm, travelCalculatorCanSubmit, canShowTravelCalculator, deleteSessionDialogOpen, applicationSubmitConfirmDialog, applicationPreviewEditor, nextStepConfirmDialog, reviewActionBusy, deleteSessionBusy, documentPreviewDialog, shortcuts, resolveReviewMissingSlotCards, resolveReviewRiskBriefs, buildReviewHeadline, buildReviewSubline, buildReviewStateLabel, buildReviewStateTone, buildReviewPlainFollowupCopy, buildReviewPlainFollowupForMessage, buildReviewNextStepRichCopyForMessage, buildMessageBubbleClass, resolveReviewFooterActions, resolveReviewSaveDraftAction, buildReviewPrimaryButtonLabel, buildReviewMainMessageText, renderMarkdown, buildExpenseQueryWindowLabel, buildExpenseQueryHint, getExpenseQueryActivePage, getExpenseQueryTotalPages, getExpenseQueryVisibleRecords, resolveDocumentPreview, triggerFileUpload, applyComposerDateSelection, handleFilesChange, handleComposerInput, handleComposerEnter, runShortcut, runWelcomeQuickAction: runShortcut, handleSuggestedAction, isSuggestedActionSelected, askHotKnowledgeQuestion, resolveKnowledgeRankLabel, resolveKnowledgeRankTone, refreshFlowRunDetail, formatFlowStepDuration, resolveFlowStepStatusLabel, resolveFlowStepDetail, toggleInsightPanel, openTravelCalculator, toggleTravelCalculator, closeTravelCalculator, submitTravelCalculator, switchToReviewOverviewDrawer, toggleReviewDocumentDrawer, toggleReviewRiskDrawer, toggleReviewFlowDrawer, toggleAttachedFilesExpanded, removeAttachedFile, clearAttachedFiles, requestCloseWorkbench, emitCloseAfterLeave, handleAssistantModalAfterEnter, openExpenseQueryRecord, handleExpenseQueryRecordClick, setExpenseQueryPage, shiftExpenseQueryPage, openDeleteSessionDialog, closeDeleteSessionDialog, confirmDeleteCurrentSession, openInlineReviewEditor, closeInlineReviewEditor, commitInlineReviewEditor, clearInlineReviewFieldError, selectInlineScene, selectReviewCategory, selectReviewOtherCategory, queryDraftByClaimNo, appendReviewRiskBriefToConversation, appendExpenseQueryRiskToConversation, goReviewDocument, openActiveReviewDocumentPreview, closeDocumentPreview, saveInlineReviewChanges, submitComposer, handleAssistantMarkdownClick, handleReviewAction, handleSaveDraftDirectly, resolveApplicationPreviewRows, resolveApplicationPreviewEditorControl, resolveApplicationPreviewEditorOptions, isApplicationPreviewEditing, isApplicationPreviewDateEditorOpen, openApplicationPreviewEditor: openApplicationPreviewEditorFromUi, commitApplicationPreviewEditor, commitApplicationPreviewDateEditor, cancelApplicationPreviewEditor, setApplicationPreviewDateMode, canApplyApplicationPreviewDateSelection, handleApplicationPreviewEditorKeydown, buildApplicationPreviewFooterText, isApplicationDraftPayload, resolveApplicationDraftStatusLabel, buildApplicationDraftSummaryItems, shouldShowDraftSavedCard, resolveReimbursementDraftClaimNo, openApplicationDraftDetail, shouldShowAssistantMessageActions, copyAssistantMessage, speakAssistantMessage, isMessageFeedbackSelected, submitOperationFeedbackForMessage, closeApplicationSubmitConfirm, confirmApplicationSubmit, closeReviewNextStepConfirm, confirmReviewNextStepSubmit, isDraftSavedReviewMessage, canUseInlineSaveDraft, handleInlineSaveDraft } } }