feat(dashboard): focus same-page Start Session input from NextStep CTA and checklist
The "Start a session" CTAs on the NextStepCard and SetupChecklist used to Link-navigate, which left the user on the same page (the Start Session input lives on the dashboard) without any visible response. Replace those CTAs with a custom window-event dispatch (FOCUS_START_SESSION_EVENT) that the StartSessionInput listens for: scroll the input into view, focus the textarea, and pulse a ring for 900ms so the click feels intentional. The NextStepCard also locally hides itself after firing so the user isn't double-prompted while typing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -18,19 +18,38 @@ const SUGGESTIONS: { icon: LucideIcon; label: string }[] = [
|
||||
|
||||
const ACCEPTED_FILE_TYPES = 'image/png,image/jpeg,image/gif,image/webp,.txt,.log,.csv,.pdf,.docx'
|
||||
|
||||
export const FOCUS_START_SESSION_EVENT = 'rf:focus-start-session'
|
||||
|
||||
export function StartSessionInput() {
|
||||
const [value, setValue] = useState('')
|
||||
const [showLogs, setShowLogs] = useState(false)
|
||||
const [logContent, setLogContent] = useState('')
|
||||
const [pendingUploads, setPendingUploads] = useState<PendingUpload[]>([])
|
||||
const [isDragOver, setIsDragOver] = useState(false)
|
||||
const [nudge, setNudge] = useState(false)
|
||||
const navigate = useNavigate()
|
||||
const wrapperRef = useRef<HTMLDivElement>(null)
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
const dragCounterRef = useRef(0)
|
||||
|
||||
useEffect(() => { textareaRef.current?.focus() }, [])
|
||||
|
||||
// External "focus me" trigger (e.g. NextStepCard "Start a session" CTA on
|
||||
// the same page). Scrolls into view, focuses the textarea, and pulses a
|
||||
// ring so the click feels intentional even when the input was already
|
||||
// partially visible.
|
||||
useEffect(() => {
|
||||
const handler = () => {
|
||||
wrapperRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
textareaRef.current?.focus({ preventScroll: true })
|
||||
setNudge(true)
|
||||
window.setTimeout(() => setNudge(false), 900)
|
||||
}
|
||||
window.addEventListener(FOCUS_START_SESSION_EVENT, handler)
|
||||
return () => window.removeEventListener(FOCUS_START_SESSION_EVENT, handler)
|
||||
}, [])
|
||||
|
||||
// Auto-grow textarea
|
||||
useEffect(() => {
|
||||
const el = textareaRef.current
|
||||
@@ -190,7 +209,8 @@ export function StartSessionInput() {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-full"
|
||||
ref={wrapperRef}
|
||||
className="w-full scroll-mt-6"
|
||||
onDragOver={handleDragOver}
|
||||
onDragEnter={handleDragEnter}
|
||||
onDragLeave={handleDragLeave}
|
||||
@@ -198,9 +218,11 @@ export function StartSessionInput() {
|
||||
>
|
||||
{/* Main input area */}
|
||||
<div className={cn(
|
||||
'relative rounded-2xl border bg-card transition-all',
|
||||
'relative rounded-2xl border bg-card transition-all duration-300',
|
||||
isDragOver ? 'border-primary/50 bg-primary/5' : 'border-border',
|
||||
'focus-within:border-[rgba(96,165,250,0.25)] focus-within:ring-1 focus-within:ring-[rgba(96,165,250,0.1)]'
|
||||
nudge
|
||||
? 'border-[rgba(96,165,250,0.6)] ring-2 ring-[rgba(96,165,250,0.35)] shadow-[0_0_0_6px_rgba(96,165,250,0.12)]'
|
||||
: 'focus-within:border-[rgba(96,165,250,0.25)] focus-within:ring-1 focus-within:ring-[rgba(96,165,250,0.1)]'
|
||||
)}>
|
||||
{/* Drag overlay */}
|
||||
{isDragOver && (
|
||||
|
||||
Reference in New Issue
Block a user