diff --git a/client/src/components/ai-chat.tsx b/client/src/components/ai-chat.tsx index bb2d530..55320bd 100644 --- a/client/src/components/ai-chat.tsx +++ b/client/src/components/ai-chat.tsx @@ -6,7 +6,7 @@ import { useMutation, useQuery } from "@tanstack/react-query"; import { apiRequest, queryClient } from "@/lib/queryClient"; import { Bot, User, Lightbulb, History, Zap, Sparkles, Loader2 } from "lucide-react"; import { useToast } from "@/hooks/use-toast"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { ModelCombobox } from "@/components/model-combobox"; interface Message { role: "user" | "assistant"; @@ -114,32 +114,16 @@ export default function AiChat() {
- +
diff --git a/client/src/components/model-combobox.tsx b/client/src/components/model-combobox.tsx new file mode 100644 index 0000000..90f6de2 --- /dev/null +++ b/client/src/components/model-combobox.tsx @@ -0,0 +1,134 @@ +import { useState } from "react"; +import { Check, ChevronsUpDown, Loader2 } from "lucide-react"; +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; + +interface Model { + id: string; + name: string; + pricing?: { + prompt: string; + completion: string; + }; +} + +interface ModelComboboxProps { + models: Model[]; + value: string; + onValueChange: (value: string) => void; + placeholder?: string; + isLoading?: boolean; + disabled?: boolean; + className?: string; + testId?: string; +} + +export function ModelCombobox({ + models, + value, + onValueChange, + placeholder = "Sélectionner un modèle", + isLoading = false, + disabled = false, + className, + testId, +}: ModelComboboxProps) { + const [open, setOpen] = useState(false); + const [searchQuery, setSearchQuery] = useState(""); + + const selectedModel = models.find((model) => model.id === value); + + const filteredModels = models.filter((model) => + model.name.toLowerCase().includes(searchQuery.toLowerCase()) || + model.id.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + return ( + + + + + + + + + Aucun modèle trouvé. + + {filteredModels.map((model) => ( + { + onValueChange(currentValue); + setOpen(false); + setSearchQuery(""); + }} + data-testid={`${testId}-option-${model.id}`} + > + +
+ {model.name} + {model.pricing && ( + + Prompt: ${model.pricing.prompt} | Completion: ${model.pricing.completion} + + )} +
+
+ ))} +
+
+
+
+
+ ); +} diff --git a/client/src/pages/settings.tsx b/client/src/pages/settings.tsx index 788aabb..e3bf3cb 100644 --- a/client/src/pages/settings.tsx +++ b/client/src/pages/settings.tsx @@ -8,10 +8,10 @@ import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Button } from "@/components/ui/button"; import { useToast } from "@/hooks/use-toast"; import { apiRequest, queryClient } from "@/lib/queryClient"; +import { ModelCombobox } from "@/components/model-combobox"; export default function Settings() { const [sidebarOpen, setSidebarOpen] = useState(false); @@ -33,7 +33,7 @@ export default function Settings() { queryKey: ['/api/openrouter/config'], }); - const { data: openrouterModels } = useQuery({ + const { data: openrouterModels, isLoading: modelsLoading } = useQuery({ queryKey: ['/api/openrouter/models'], }); @@ -227,18 +227,15 @@ export default function Settings() {
- +

Choisissez le modèle d'IA pour générer vos publications

diff --git a/replit.md b/replit.md index 37b5527..b527f0f 100644 --- a/replit.md +++ b/replit.md @@ -10,6 +10,9 @@ Preferred communication style: Simple, everyday language. ## Recent Changes +### October 11, 2025 +- **Model Selector Improvement**: Replaced basic Select dropdowns with advanced ModelCombobox component featuring search and scroll functionality. The new combobox allows users to search models by name across 326+ OpenRouter models, displays pricing information (prompt/completion costs), and provides smooth scrolling through a max-height limited list. Implemented in both Settings page (OpenRouter configuration) and AI Assistant page. The combobox prevents accidental deselection and includes loading states with spinner for better UX. + ### October 10, 2025 - **Post Type Visual Indicators**: Added visual icons throughout the app to distinguish post types at a glance. Feed posts display Image icon (🖼️), Story posts display Smartphone icon (📱), and posts configured for both show both icons side-by-side. Icons appear consistently across calendar view (desktop grid and mobile list), dashboard "Historique des publications" section, and history page for improved visual clarity. - **Calendar Auto-Refresh**: Implemented automatic calendar data refresh every 30 seconds using TanStack Query's `refetchInterval` option. Calendar continuously fetches updated scheduled posts without requiring page reload or manual refresh, ensuring users see the latest schedule data in real-time.