SPB Git

spb/vquant Public MIT

VibeQuant — AI-powered institutional-grade financial intelligence platform.

TypeScript 84.3% Python 11.7% JavaScript 1.6% CSS 1.5% HTML 0.7%
3.0 KB · 86 lines tsx
Raw Blame History
1/*2 * =============================================================================3 *  VibeQuant (vquant) — AI-Powered Financial Intelligence Platform4 * -----------------------------------------------------------------------------5 *  File:      client/src/components/chat/search-bar.tsx6 *7 *  Author:    Simon-Pierre Boucher8 *  Contact:   contact@spboucher.ai9 *  Website:   https://www.spboucher.ai10 *  Demo:      https://www.vquant.ai11 *  License:   MIT (see LICENSE)12 *13 *  Copyright © 2026 Simon-Pierre Boucher. All rights reserved.14 * =============================================================================15 */1617import { useState, FormEvent } from "react";18import { Send, Loader2 } from "lucide-react";19import type { ClaudeModel } from "@/hooks/useChatSession";2021interface SearchBarProps {22  onSearch: (query: string, imageData?: string, imageMimeType?: string) => void;23  isLoading?: boolean;24  placeholder?: string;25  selectedModel?: ClaudeModel;26  onModelChange?: (model: ClaudeModel) => void;27}2829export function SearchBar({ onSearch, isLoading = false, placeholder = "Ask anything..." }: SearchBarProps) {30  const [query, setQuery] = useState("");31  const [isFocused, setIsFocused] = useState(false);3233  const handleSubmit = (e: FormEvent) => {34    e.preventDefault();35    if (query.trim() && !isLoading) {36      onSearch(query.trim());37      setQuery("");38    }39  };4041  const canSubmit = Boolean(query.trim()) && !isLoading;4243  return (44    <div className="w-full" data-testid="search-bar-container">45      <form onSubmit={handleSubmit} className="relative" data-testid="form-search">46        <div47          className={`relative flex items-center gap-3 px-4 py-3 bg-card border rounded-xl transition-colors ${48            isFocused49              ? "border-primary ring-2 ring-primary/10"50              : "border-border hover:border-primary/40"51          }`}52          data-testid="search-input-wrapper"53        >54          <input55            type="text"56            value={query}57            onChange={(e) => setQuery(e.target.value)}58            onFocus={() => setIsFocused(true)}59            onBlur={() => setIsFocused(false)}60            placeholder={placeholder}61            disabled={isLoading}62            data-testid="input-search-query"63            className="flex-1 min-w-0 bg-transparent border-none outline-none text-base font-normal text-foreground placeholder:text-muted-foreground disabled:opacity-50 disabled:cursor-not-allowed"64          />6566          <button67            type="submit"68            disabled={!canSubmit}69            className={`flex-shrink-0 p-2.5 rounded-lg transition-colors ${70              canSubmit71                ? "bg-primary text-primary-foreground hover:bg-primary/90"72                : "bg-muted text-muted-foreground cursor-not-allowed"73            }`}74          >75            {isLoading ? (76              <Loader2 className="w-4 h-4 animate-spin" data-testid="icon-search-loading" />77            ) : (78              <Send className="w-4 h-4" />79            )}80          </button>81        </div>82      </form>83    </div>84  );85}86