Add user authentication and dynamic feature/category inputs
Implement Keycloak authentication, protected routes, and add combobox and autocomplete components for tool categories and features. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 776963d0-f75d-42e2-a57b-cc36bdff8495 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 0b145113-c016-4f54-b000-13bd3b0ba8f0 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0683fb79-a27c-485c-9333-5f4b288c4567/776963d0-f75d-42e2-a57b-cc36bdff8495/z4uWN6A Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import { useState } from "react";
|
||||
import { Check, ChevronsUpDown } 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";
|
||||
import { useListCategories } from "@workspace/api-client-react";
|
||||
|
||||
interface CategoryComboboxProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export function CategoryCombobox({ value, onChange, placeholder = "Select or type a category..." }: CategoryComboboxProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
const categories = useListCategories();
|
||||
|
||||
const known: string[] = categories.data ?? [];
|
||||
|
||||
const filtered = inputValue.trim()
|
||||
? known.filter((c) => c.toLowerCase().includes(inputValue.toLowerCase()))
|
||||
: known;
|
||||
|
||||
const showCreateOption = inputValue.trim() !== "" && !known.some(
|
||||
(c) => c.toLowerCase() === inputValue.toLowerCase()
|
||||
);
|
||||
|
||||
function select(val: string) {
|
||||
onChange(val);
|
||||
setInputValue(val);
|
||||
setOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className="w-full justify-between font-normal h-9"
|
||||
data-testid="button-category-combobox"
|
||||
>
|
||||
<span className={cn(!value && "text-muted-foreground")}>
|
||||
{value || placeholder}
|
||||
</span>
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-full p-0" align="start">
|
||||
<Command shouldFilter={false}>
|
||||
<CommandInput
|
||||
placeholder="Search or enter new category..."
|
||||
value={inputValue}
|
||||
onValueChange={(v) => {
|
||||
setInputValue(v);
|
||||
onChange(v);
|
||||
}}
|
||||
data-testid="input-category-search"
|
||||
/>
|
||||
<CommandList>
|
||||
{filtered.length === 0 && !showCreateOption && (
|
||||
<CommandEmpty>No categories found.</CommandEmpty>
|
||||
)}
|
||||
{filtered.length > 0 && (
|
||||
<CommandGroup heading="Known categories">
|
||||
{filtered.map((cat) => (
|
||||
<CommandItem
|
||||
key={cat}
|
||||
value={cat}
|
||||
onSelect={() => select(cat)}
|
||||
data-testid={`item-category-${cat}`}
|
||||
>
|
||||
<Check
|
||||
className={cn("mr-2 h-4 w-4", value === cat ? "opacity-100" : "opacity-0")}
|
||||
/>
|
||||
{cat}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
)}
|
||||
{showCreateOption && (
|
||||
<CommandGroup heading="Create new">
|
||||
<CommandItem
|
||||
value={inputValue}
|
||||
onSelect={() => select(inputValue.trim())}
|
||||
data-testid="item-category-create-new"
|
||||
>
|
||||
<span className="text-primary font-medium">+ Create</span>
|
||||
<span className="ml-2 text-muted-foreground">“{inputValue.trim()}”</span>
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
)}
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user