diff --git a/frontend/src/components/session/StepFeedback.tsx b/frontend/src/components/session/StepFeedback.tsx new file mode 100644 index 00000000..bc34a244 --- /dev/null +++ b/frontend/src/components/session/StepFeedback.tsx @@ -0,0 +1,77 @@ +import { useState, useEffect } from 'react' +import { ThumbsUp, ThumbsDown } from 'lucide-react' +import { analyticsApi } from '@/api' +import { cn } from '@/lib/utils' + +interface StepFeedbackProps { + stepId: string + sessionId: string +} + +const HINT_KEY = 'rf-step-feedback-hint-dismissed' + +export function StepFeedback({ stepId, sessionId }: StepFeedbackProps) { + const [feedback, setFeedback] = useState(null) + const [submitting, setSubmitting] = useState(false) + const [showHint, setShowHint] = useState(false) + + useEffect(() => { + if (!localStorage.getItem(HINT_KEY)) { + setShowHint(true) + } + }, []) + + const handleFeedback = async (wasHelpful: boolean) => { + if (submitting) return + setSubmitting(true) + try { + const newValue = feedback === wasHelpful ? null : wasHelpful + if (newValue !== null) { + await analyticsApi.submitStepFeedback(stepId, sessionId, newValue) + } + setFeedback(newValue) + if (showHint) { + setShowHint(false) + localStorage.setItem(HINT_KEY, '1') + } + } catch { + // Silently fail — feedback is non-critical + } finally { + setSubmitting(false) + } + } + + return ( +
+ {showHint && ( + Was this step helpful? + )} + + +
+ ) +} diff --git a/frontend/src/pages/ProceduralNavigationPage.tsx b/frontend/src/pages/ProceduralNavigationPage.tsx index 7cd75cc6..d34c1949 100644 --- a/frontend/src/pages/ProceduralNavigationPage.tsx +++ b/frontend/src/pages/ProceduralNavigationPage.tsx @@ -11,6 +11,7 @@ import { ProgressBar } from '@/components/procedural/ProgressBar' import { CompletionSummary } from '@/components/procedural/CompletionSummary' import { cn } from '@/lib/utils' import { toast } from '@/lib/toast' +import { StepFeedback } from '@/components/session/StepFeedback' interface StepState { notes: string @@ -406,6 +407,11 @@ export function ProceduralNavigationPage() { isLast={currentStepIndex === procedureSteps.length - 1} /> )} + {session && currentStep && ( +
+ +
+ )} diff --git a/frontend/src/pages/TreeNavigationPage.tsx b/frontend/src/pages/TreeNavigationPage.tsx index d4a7414f..c03b1a0d 100644 --- a/frontend/src/pages/TreeNavigationPage.tsx +++ b/frontend/src/pages/TreeNavigationPage.tsx @@ -14,6 +14,7 @@ import { Plus, CheckCircle, ArrowRight, Clock, Terminal, Clipboard, Check, Copy, import { toast } from '@/lib/toast' import { Modal } from '@/components/common/Modal' import { ShareSessionModal } from '@/components/session/ShareSessionModal' +import { StepFeedback } from '@/components/session/StepFeedback' import { buildSessionShareUrl, getLatestActiveShareForSession } from '@/lib/sessionShare' interface LocationState { @@ -1089,6 +1090,13 @@ export function TreeNavigationPage() { )} + {/* Step Feedback */} + {session && (currentNode || currentCustomStep) && ( +
+ +
+ )} + {/* Notes */}