From 1869da4fcb6721e4d311f4258bad813ed6b84023 Mon Sep 17 00:00:00 2001
From: Michael Chihlas
Date: Fri, 13 Feb 2026 09:39:38 -0500
Subject: [PATCH] feat(frontend): add mid-session Copy for Ticket to navigation
page
Co-Authored-By: Claude Opus 4.6
---
frontend/src/pages/TreeNavigationPage.tsx | 54 ++++++++++++++++++++---
1 file changed, 47 insertions(+), 7 deletions(-)
diff --git a/frontend/src/pages/TreeNavigationPage.tsx b/frontend/src/pages/TreeNavigationPage.tsx
index 7449f4a1..3a11fb20 100644
--- a/frontend/src/pages/TreeNavigationPage.tsx
+++ b/frontend/src/pages/TreeNavigationPage.tsx
@@ -10,7 +10,8 @@ import { cn, safeGetItem, safeSetItem } from '@/lib/utils'
import { MarkdownContent } from '@/components/ui/MarkdownContent'
import { CustomStepModal } from '@/components/step-library/CustomStepModal'
import { PostStepActionModal, ContinuationModal, ForkTreeModal, ScratchpadSidebar, SessionOutcomeModal } from '@/components/session'
-import { Plus, CheckCircle, ArrowRight, Clock, Terminal, Clipboard, Check, HelpCircle } from 'lucide-react'
+import { Plus, CheckCircle, ArrowRight, Clock, Terminal, Clipboard, Check, Copy, HelpCircle } from 'lucide-react'
+import { toast } from '@/lib/toast'
import { Modal } from '@/components/common/Modal'
interface LocationState {
@@ -45,6 +46,8 @@ export function TreeNavigationPage() {
const [copiedCommand, setCopiedCommand] = useState(null)
const [shortcutsModalOpen, setShortcutsModalOpen] = useState(false)
const [selectingOption, setSelectingOption] = useState(null)
+ const [copiedForTicket, setCopiedForTicket] = useState(false)
+ const [isCopyingForTicket, setIsCopyingForTicket] = useState(false)
const handleCopyCommand = (text: string) => {
navigator.clipboard.writeText(text)
@@ -52,6 +55,29 @@ export function TreeNavigationPage() {
setTimeout(() => setCopiedCommand(null), 2000)
}
+ const handleCopyForTicket = async () => {
+ if (!session || isCopyingForTicket) return
+ setIsCopyingForTicket(true)
+ try {
+ const content = await sessionsApi.export(session.id, {
+ format: 'psa',
+ include_timestamps: true,
+ include_tree_info: true,
+ })
+ if (content) {
+ await navigator.clipboard.writeText(content)
+ setCopiedForTicket(true)
+ setTimeout(() => setCopiedForTicket(false), 2000)
+ toast.success('Copied progress notes to clipboard')
+ }
+ } catch (err) {
+ console.error('Copy for ticket failed:', err)
+ toast.error('Failed to copy notes')
+ } finally {
+ setIsCopyingForTicket(false)
+ }
+ }
+
// Session metadata (prefill from Repeat Last Session)
const [ticketNumber, setTicketNumber] = useState(locationState?.prefillTicketNumber || '')
const [clientName, setClientName] = useState(locationState?.prefillClientName || '')
@@ -549,12 +575,26 @@ export function TreeNavigationPage() {
)}
-
+
+
+
+
{/* Breadcrumb */}