feat: session quick wins (#51-#55) (#72)
* feat: add session quick wins (#51-#55) - Session timer showing elapsed time in header (#51) - Tab keyboard shortcut to focus notes textarea (#52) - Repeat Last Session button on tree library page (#53) - Auto-recovery banner for incomplete sessions (#54) - Copy individual step to clipboard on session detail (#55) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add missing delete button to table and list tree views The onDeleteTree prop was accepted but never used in TreeTableView and TreeListView. Now both views show a trash icon (permission-gated) matching the existing grid view behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit was merged in pull request #72.
This commit is contained in:
@@ -4,15 +4,18 @@ import { treesApi } from '@/api/trees'
|
||||
import { sessionsApi } from '@/api/sessions'
|
||||
import { useTreeNavigationShortcuts } from '@/hooks/useKeyboardShortcuts'
|
||||
import { useCustomStepFlow } from '@/hooks/useCustomStepFlow'
|
||||
import { useSessionTimer } from '@/hooks/useSessionTimer'
|
||||
import type { Tree, Session, DecisionRecord, TreeStructure } from '@/types'
|
||||
import { cn, safeGetItem } from '@/lib/utils'
|
||||
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 } from '@/components/session'
|
||||
import { Plus, CheckCircle, ArrowRight } from 'lucide-react'
|
||||
import { Plus, CheckCircle, ArrowRight, Clock } from 'lucide-react'
|
||||
|
||||
interface LocationState {
|
||||
sessionId?: string
|
||||
prefillClientName?: string
|
||||
prefillTicketNumber?: string
|
||||
}
|
||||
|
||||
export function TreeNavigationPage() {
|
||||
@@ -31,11 +34,14 @@ export function TreeNavigationPage() {
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [isCompleting, setIsCompleting] = useState(false)
|
||||
|
||||
// Session metadata
|
||||
const [ticketNumber, setTicketNumber] = useState<string>('')
|
||||
const [clientName, setClientName] = useState<string>('')
|
||||
// Session metadata (prefill from Repeat Last Session)
|
||||
const [ticketNumber, setTicketNumber] = useState<string>(locationState?.prefillTicketNumber || '')
|
||||
const [clientName, setClientName] = useState<string>(locationState?.prefillClientName || '')
|
||||
const [showMetadataForm, setShowMetadataForm] = useState(true)
|
||||
|
||||
// Session timer
|
||||
const timerDisplay = useSessionTimer(session?.started_at)
|
||||
|
||||
// Scratchpad state
|
||||
const [scratchpadOpen, setScratchpadOpen] = useState(() => {
|
||||
return safeGetItem('scratchpad-collapsed') === 'false'
|
||||
@@ -120,6 +126,13 @@ export function TreeNavigationPage() {
|
||||
})
|
||||
setSession(newSession)
|
||||
setShowMetadataForm(false)
|
||||
// Save for "Repeat Last Session"
|
||||
safeSetItem('last-session', JSON.stringify({
|
||||
tree_id: tree.id,
|
||||
tree_name: tree.name,
|
||||
client_name: clientName || '',
|
||||
ticket_number: ticketNumber || '',
|
||||
}))
|
||||
} catch (err) {
|
||||
setError('Failed to start session')
|
||||
console.error(err)
|
||||
@@ -368,7 +381,15 @@ export function TreeNavigationPage() {
|
||||
{/* Header */}
|
||||
<div className="mb-6 flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-white">{tree.name}</h1>
|
||||
<div className="flex items-center gap-3">
|
||||
<h1 className="text-xl font-bold text-white">{tree.name}</h1>
|
||||
{timerDisplay && (
|
||||
<span className="flex items-center gap-1 text-sm text-white/40">
|
||||
<Clock className="h-3.5 w-3.5" />
|
||||
{timerDisplay}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{(ticketNumber || clientName) && (
|
||||
<p className="text-sm text-white/40">
|
||||
{ticketNumber && `Ticket: ${ticketNumber}`}
|
||||
@@ -665,6 +686,7 @@ export function TreeNavigationPage() {
|
||||
Notes (optional)
|
||||
</label>
|
||||
<textarea
|
||||
id="session-notes"
|
||||
value={notes}
|
||||
onChange={(e) => setNotes(e.target.value)}
|
||||
placeholder="Add any notes for this step..."
|
||||
@@ -698,6 +720,7 @@ export function TreeNavigationPage() {
|
||||
{(currentNode.type === 'action' || currentNode.type === 'solution') && (
|
||||
<span>, Enter {currentNode.type === 'solution' ? 'complete' : 'continue'}</span>
|
||||
)}
|
||||
<span>, Tab notes</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user