diff --git a/frontend/src/components/pilot/ProposalBanner.tsx b/frontend/src/components/pilot/ProposalBanner.tsx index 7a760e79..264213fa 100644 --- a/frontend/src/components/pilot/ProposalBanner.tsx +++ b/frontend/src/components/pilot/ProposalBanner.tsx @@ -9,7 +9,9 @@ * Visual reference: docs/FlowAssist_Migration/mockups/06-slide-up-banner.html * + 07-verify-states.html. */ -import { Sparkles, Check, ChevronDown } from 'lucide-react' +import { useState } from 'react' +import { Sparkles, Check, ChevronDown, X, MoreHorizontal, Info } from 'lucide-react' +import { cn } from '@/lib/utils' import type { SessionSuggestedFix, FixOutcome, @@ -103,11 +105,147 @@ function ProposedBanner({ fix, onApply, onDismiss, onToggleCollapsed }: Proposal ) } -// Placeholder renderers — implemented in Tasks 8 & 9. -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function VerifyingBanner(_: ProposalBannerProps) { return null } -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function PartialBanner(_: ProposalBannerProps) { return null } +function VerifyingBanner({ fix, onOutcome }: ProposalBannerProps) { + const [showOverflow, setShowOverflow] = useState(false) + const appliedLabel = fix.applied_at + ? `Applied ${formatRelativeMinutes(fix.applied_at)}` + : 'Applied' + + return ( +
+
+
+
+ + + + + +
+
+
+ Verifying + + {appliedLabel} + +
+
+ Did "{fix.title}" work? +
+
+ Mark the outcome so the AI can either close the session with this as the resolution, or propose something else. +
+
+
+ + {showOverflow && ( +
+ +
+ )} + + +
+
+
+ ) +} + +function formatRelativeMinutes(iso: string): string { + const then = new Date(iso).getTime() + const mins = Math.max(0, Math.round((Date.now() - then) / 60000)) + if (mins === 0) return 'just now' + if (mins === 1) return '1m ago' + return `${mins}m ago` +} + +function PartialBanner({ fix, onOutcome, onApply }: ProposalBannerProps) { + return ( +
+
+
+
+ +
+
+
+ Partially applied + + Parked + +
+
+ {fix.title} +
+ {fix.partial_notes && ( +
+ Note + {fix.partial_notes} +
+ )} +
+
+ + + +
+
+
+ ) +} + +// Placeholder renderers — implemented in Task 9. // eslint-disable-next-line @typescript-eslint/no-unused-vars function AIConfirmingBanner(_: ProposalBannerProps) { return null } // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/frontend/src/index.css b/frontend/src/index.css index 4f148cc0..295de510 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -87,11 +87,17 @@ --animate-scale-in: scale-in 150ms ease-out both; --animate-fade: fadeIn 300ms ease both; --animate-slide-up: slide-up 320ms cubic-bezier(.22,.9,.28,1) both; + --animate-pulse-amber: pulse-amber 1.6s infinite; @keyframes slide-up { from { transform: translateY(14px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } + @keyframes pulse-amber { + 0% { box-shadow: 0 0 0 0 rgba(251,191,36,0.45); } + 70% { box-shadow: 0 0 0 10px rgba(251,191,36,0); } + 100% { box-shadow: 0 0 0 0 rgba(251,191,36,0); } + } @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }