chore: ran linting

This commit is contained in:
Anish Sarkar 2026-02-10 19:06:21 +05:30
parent b44b4497a6
commit f1ce17bde4
15 changed files with 285 additions and 338 deletions

View file

@ -1,15 +1,7 @@
"use client";
import { useAtomValue } from "jotai";
import {
Bot,
Check,
ChevronDown,
Edit3,
ImageIcon,
Plus,
Zap,
} from "lucide-react";
import { Bot, Check, ChevronDown, Edit3, ImageIcon, Plus, Zap } from "lucide-react";
import { useCallback, useMemo, useState } from "react";
import { toast } from "sonner";
import {
@ -77,10 +69,10 @@ export function ModelSelector({
// Image data
const { data: imageGlobalConfigs, isLoading: imageGlobalLoading } =
useAtomValue(globalImageGenConfigsAtom);
const { data: imageUserConfigs, isLoading: imageUserLoading } =
useAtomValue(imageGenConfigsAtom);
const { data: imageUserConfigs, isLoading: imageUserLoading } = useAtomValue(imageGenConfigsAtom);
const isLoading = llmUserLoading || llmGlobalLoading || prefsLoading || imageGlobalLoading || imageUserLoading;
const isLoading =
llmUserLoading || llmGlobalLoading || prefsLoading || imageGlobalLoading || imageUserLoading;
// ─── LLM current config ───
const currentLLMConfig = useMemo(() => {
@ -108,7 +100,9 @@ export function ModelSelector({
}, [preferences, imageGlobalConfigs, imageUserConfigs]);
const isImageAutoMode = useMemo(() => {
return currentImageConfig && "is_auto_mode" in currentImageConfig && currentImageConfig.is_auto_mode;
return (
currentImageConfig && "is_auto_mode" in currentImageConfig && currentImageConfig.is_auto_mode
);
}, [currentImageConfig]);
// ─── LLM filtering ───
@ -244,7 +238,9 @@ export function ModelSelector({
{/* LLM section */}
{currentLLMConfig ? (
<>
{getProviderIcon(currentLLMConfig.provider, { isAutoMode: isLLMAutoMode ?? false })}
{getProviderIcon(currentLLMConfig.provider, {
isAutoMode: isLLMAutoMode ?? false,
})}
<span className="max-w-[100px] md:max-w-[120px] truncate hidden md:inline">
{currentLLMConfig.name}
</span>
@ -262,7 +258,9 @@ export function ModelSelector({
{/* Image section */}
{currentImageConfig ? (
<>
{getProviderIcon(currentImageConfig.provider, { isAutoMode: isImageAutoMode ?? false })}
{getProviderIcon(currentImageConfig.provider, {
isAutoMode: isImageAutoMode ?? false,
})}
<span className="max-w-[80px] md:max-w-[100px] truncate hidden md:inline">
{currentImageConfig.name}
</span>
@ -373,7 +371,9 @@ export function ModelSelector({
Recommended
</Badge>
)}
{isSelected && <Check className="size-3.5 text-primary shrink-0" />}
{isSelected && (
<Check className="size-3.5 text-primary shrink-0" />
)}
</div>
<div className="flex items-center gap-1.5 mt-0.5">
<span className="text-xs text-muted-foreground truncate">
@ -436,7 +436,9 @@ export function ModelSelector({
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="font-medium truncate">{config.name}</span>
{isSelected && <Check className="size-3.5 text-primary shrink-0" />}
{isSelected && (
<Check className="size-3.5 text-primary shrink-0" />
)}
</div>
<div className="flex items-center gap-1.5 mt-0.5">
<span className="text-xs text-muted-foreground truncate">
@ -489,7 +491,10 @@ export function ModelSelector({
{/* ─── Image Tab ─── */}
<TabsContent value="image" className="mt-0">
<Command shouldFilter={false} className="rounded-none rounded-b-lg [&_[data-slot=command-input-wrapper]]:border-0 [&_[data-slot=command-input-wrapper]]:px-0 [&_[data-slot=command-input-wrapper]]:gap-2">
<Command
shouldFilter={false}
className="rounded-none rounded-b-lg [&_[data-slot=command-input-wrapper]]:border-0 [&_[data-slot=command-input-wrapper]]:px-0 [&_[data-slot=command-input-wrapper]]:gap-2"
>
{totalImageModels > 3 && (
<div className="px-2 md:px-3 py-1.5 md:py-2">
<CommandInput
@ -573,7 +578,9 @@ export function ModelSelector({
{/* User Image Configs */}
{filteredImageUser.length > 0 && (
<>
{filteredImageGlobal.length > 0 && <CommandSeparator className="my-1 mx-4 bg-border/60" />}
{filteredImageGlobal.length > 0 && (
<CommandSeparator className="my-1 mx-4 bg-border/60" />
)}
<CommandGroup>
<div className="flex items-center gap-2 px-3 py-2 text-xs font-semibold text-muted-foreground tracking-wider">
Your Image Models
@ -591,13 +598,13 @@ export function ModelSelector({
)}
>
<div className="flex items-center gap-3 min-w-0 flex-1">
<div className="shrink-0">
{getProviderIcon(config.provider)}
</div>
<div className="shrink-0">{getProviderIcon(config.provider)}</div>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="font-medium truncate">{config.name}</span>
{isSelected && <Check className="size-3.5 text-primary shrink-0" />}
{isSelected && (
<Check className="size-3.5 text-primary shrink-0" />
)}
</div>
<span className="text-xs text-muted-foreground truncate block">
{config.model_name}

View file

@ -50,9 +50,7 @@ export function PublicChatSnapshotRow({
day: "numeric",
});
const member = snapshot.created_by_user_id
? memberMap.get(snapshot.created_by_user_id)
: null;
const member = snapshot.created_by_user_id ? memberMap.get(snapshot.created_by_user_id) : null;
return (
<Card className="group relative overflow-hidden transition-all duration-200 border-border/60 hover:shadow-md h-full">
@ -77,11 +75,7 @@ export function PublicChatSnapshotRow({
asChild
className="h-7 w-7 text-muted-foreground hover:text-foreground"
>
<a
href={snapshot.public_url}
target="_blank"
rel="noopener noreferrer"
>
<a href={snapshot.public_url} target="_blank" rel="noopener noreferrer">
<ExternalLink className="h-3 w-3" />
</a>
</Button>
@ -110,51 +104,49 @@ export function PublicChatSnapshotRow({
</div>
</div>
{/* Message count badge */}
<div className="flex items-center gap-1.5">
<Badge
variant="outline"
className="text-[10px] px-1.5 py-0.5 border-muted-foreground/20 text-muted-foreground"
>
<MessageSquare className="h-2.5 w-2.5 mr-1" />
{snapshot.message_count} messages
</Badge>
</div>
{/* Message count badge */}
<div className="flex items-center gap-1.5">
<Badge
variant="outline"
className="text-[10px] px-1.5 py-0.5 border-muted-foreground/20 text-muted-foreground"
>
<MessageSquare className="h-2.5 w-2.5 mr-1" />
{snapshot.message_count} messages
</Badge>
</div>
{/* Public URL selectable fallback for manual copy */}
<div className="flex items-center gap-2 rounded-md border border-border/60 bg-muted/30 px-2.5 py-1.5">
<p
className="min-w-0 flex-1 text-[10px] font-mono text-muted-foreground break-all select-all cursor-text"
title={snapshot.public_url}
>
{snapshot.public_url}
</p>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={handleCopyClick}
className="h-6 w-6 shrink-0 text-muted-foreground hover:text-foreground"
>
{copied ? (
<Check className="h-3 w-3 text-green-500" />
) : (
<Copy className="h-3 w-3" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>{copied ? "Copied!" : "Copy link"}</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
{/* Public URL selectable fallback for manual copy */}
<div className="flex items-center gap-2 rounded-md border border-border/60 bg-muted/30 px-2.5 py-1.5">
<p
className="min-w-0 flex-1 text-[10px] font-mono text-muted-foreground break-all select-all cursor-text"
title={snapshot.public_url}
>
{snapshot.public_url}
</p>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={handleCopyClick}
className="h-6 w-6 shrink-0 text-muted-foreground hover:text-foreground"
>
{copied ? (
<Check className="h-3 w-3 text-green-500" />
) : (
<Copy className="h-3 w-3" />
)}
</Button>
</TooltipTrigger>
<TooltipContent>{copied ? "Copied!" : "Copy link"}</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
{/* Footer: Date + Creator */}
{/* Footer: Date + Creator */}
<div className="flex items-center gap-2 pt-2 border-t border-border/40 mt-auto">
<span className="text-[11px] text-muted-foreground/60">
{formattedDate}
</span>
<span className="text-[11px] text-muted-foreground/60">{formattedDate}</span>
{member && (
<>
<span className="text-muted-foreground/30">·</span>
@ -182,9 +174,7 @@ export function PublicChatSnapshotRow({
</span>
</div>
</TooltipTrigger>
<TooltipContent side="bottom">
{member.email || member.name}
</TooltipContent>
<TooltipContent side="bottom">{member.email || member.name}</TooltipContent>
</Tooltip>
</TooltipProvider>
</>

View file

@ -97,13 +97,13 @@ export function PublicChatSnapshotsManager({
<div className="flex items-start justify-between gap-2">
<Skeleton className="h-4 w-36 md:w-44" />
</div>
{/* Message count badge */}
<div className="flex items-center gap-1.5">
<Skeleton className="h-5 w-24 rounded-full" />
</div>
{/* URL skeleton */}
<Skeleton className="h-3 w-full rounded" />
{/* Footer: Date + Creator */}
{/* Message count badge */}
<div className="flex items-center gap-1.5">
<Skeleton className="h-5 w-24 rounded-full" />
</div>
{/* URL skeleton */}
<Skeleton className="h-3 w-full rounded" />
{/* Footer: Date + Creator */}
<div className="flex items-center gap-2 pt-2 border-t border-border/40">
<Skeleton className="h-3 w-20" />
<Skeleton className="h-4 w-4 rounded-full" />

View file

@ -348,16 +348,16 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
{/* Global info */}
{globalConfigs.filter((g) => !("is_auto_mode" in g && g.is_auto_mode)).length > 0 && (
<Alert className="bg-muted/50 py-3">
<Info className="h-3 w-3 md:h-4 md:w-4 shrink-0" />
<AlertDescription className="text-xs md:text-sm">
<span className="font-medium">
{globalConfigs.filter((g) => !("is_auto_mode" in g && g.is_auto_mode)).length} global
image model(s)
</span>{" "}
available from your administrator.
</AlertDescription>
</Alert>
<Alert className="bg-muted/50 py-3">
<Info className="h-3 w-3 md:h-4 md:w-4 shrink-0" />
<AlertDescription className="text-xs md:text-sm">
<span className="font-medium">
{globalConfigs.filter((g) => !("is_auto_mode" in g && g.is_auto_mode)).length} global
image model(s)
</span>{" "}
available from your administrator.
</AlertDescription>
</Alert>
)}
{/* Loading Skeleton */}
@ -417,7 +417,11 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
: "No image models have been added to this space yet. Contact a space owner to add one."}
</p>
{canCreate && (
<Button onClick={openNewDialog} size="lg" className="gap-2 text-xs md:text-sm h-9 md:h-10">
<Button
onClick={openNewDialog}
size="lg"
className="gap-2 text-xs md:text-sm h-9 md:h-10"
>
<Plus className="h-3 w-3 md:h-4 md:w-4" />
Add First Image Model
</Button>
@ -457,43 +461,43 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
)}
</div>
{(canUpdate || canDelete) && (
<div className="flex items-center gap-0.5 shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity duration-150">
{canUpdate && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => openEditDialog(config)}
className="h-7 w-7 text-muted-foreground hover:text-foreground"
>
<Edit3 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Edit</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{canDelete && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => setConfigToDelete(config)}
className="h-7 w-7 text-muted-foreground hover:text-destructive"
>
<Trash2 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Delete</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
)}
<div className="flex items-center gap-0.5 shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity duration-150">
{canUpdate && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => openEditDialog(config)}
className="h-7 w-7 text-muted-foreground hover:text-foreground"
>
<Edit3 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Edit</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{canDelete && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => setConfigToDelete(config)}
className="h-7 w-7 text-muted-foreground hover:text-destructive"
>
<Trash2 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Delete</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
)}
</div>
{/* Provider + Model */}
@ -507,14 +511,11 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
{/* Footer: Date + Creator */}
<div className="flex items-center gap-2 pt-2 border-t border-border/40 mt-auto">
<span className="text-[11px] text-muted-foreground/60">
{new Date(config.created_at).toLocaleDateString(
undefined,
{
year: "numeric",
month: "short",
day: "numeric",
}
)}
{new Date(config.created_at).toLocaleDateString(undefined, {
year: "numeric",
month: "short",
day: "numeric",
})}
</span>
{member && (
<>
@ -574,13 +575,11 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
}}
>
<DialogContent
className="max-w-lg max-h-[90vh] overflow-y-auto"
onOpenAutoFocus={(e) => e.preventDefault()}
>
className="max-w-lg max-h-[90vh] overflow-y-auto"
onOpenAutoFocus={(e) => e.preventDefault()}
>
<DialogHeader>
<DialogTitle>
{editingConfig ? "Edit Image Model" : "Add Image Model"}
</DialogTitle>
<DialogTitle>{editingConfig ? "Edit Image Model" : "Add Image Model"}</DialogTitle>
<DialogDescription>
{editingConfig
? "Update your image generation model"

View file

@ -210,8 +210,18 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
...(userImageConfigs ?? []).filter((config) => config.id && config.id.toString().trim() !== ""),
];
const isLoading = configsLoading || preferencesLoading || globalConfigsLoading || imageConfigsLoading || globalImageConfigsLoading;
const hasError = configsError || preferencesError || globalConfigsError || imageConfigsError || globalImageConfigsError;
const isLoading =
configsLoading ||
preferencesLoading ||
globalConfigsLoading ||
imageConfigsLoading ||
globalImageConfigsLoading;
const hasError =
configsError ||
preferencesError ||
globalConfigsError ||
imageConfigsError ||
globalImageConfigsError;
const hasAnyConfigs = allLLMConfigs.length > 0 || allImageConfigs.length > 0;
return (
@ -253,8 +263,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
<AlertDescription className="text-xs md:text-sm">
{(configsError?.message ?? "Failed to load LLM configurations") ||
(preferencesError?.message ?? "Failed to load preferences") ||
(globalConfigsError?.message ??
"Failed to load global configurations")}
(globalConfigsError?.message ?? "Failed to load global configurations")}
</AlertDescription>
</Alert>
</motion.div>
@ -305,8 +314,8 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
<Alert variant="destructive" className="py-3 md:py-4">
<AlertCircle className="h-3 w-3 md:h-4 md:w-4 shrink-0" />
<AlertDescription className="text-xs md:text-sm">
No configurations found. Please add at least one LLM provider or image model
in the respective settings tabs before assigning roles.
No configurations found. Please add at least one LLM provider or image model in the
respective settings tabs before assigning roles.
</AlertDescription>
</Alert>
)}
@ -322,8 +331,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
{Object.entries(ROLE_DESCRIPTIONS).map(([key, role], index) => {
const IconComponent = role.icon;
const isImageRole = role.configType === "image";
const currentAssignment =
assignments[role.prefKey as keyof typeof assignments];
const currentAssignment = assignments[role.prefKey as keyof typeof assignments];
// Pick the right config lists based on role type
const roleGlobalConfigs = isImageRole ? globalImageConfigs : globalConfigs;
@ -332,17 +340,13 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
: newLLMConfigs.filter((c) => c.id && c.id.toString().trim() !== "");
const roleAllConfigs = isImageRole ? allImageConfigs : allLLMConfigs;
const assignedConfig = roleAllConfigs.find(
(config) => config.id === currentAssignment
);
const assignedConfig = roleAllConfigs.find((config) => config.id === currentAssignment);
const isAssigned =
currentAssignment !== "" &&
currentAssignment !== null &&
currentAssignment !== undefined;
const isAutoMode =
assignedConfig &&
"is_auto_mode" in assignedConfig &&
assignedConfig.is_auto_mode;
assignedConfig && "is_auto_mode" in assignedConfig && assignedConfig.is_auto_mode;
return (
<motion.div
@ -362,14 +366,10 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
role.bgColor
)}
>
<IconComponent
className={cn("w-4 h-4", role.color)}
/>
<IconComponent className={cn("w-4 h-4", role.color)} />
</div>
<div className="min-w-0">
<h4 className="text-sm font-semibold tracking-tight">
{role.title}
</h4>
<h4 className="text-sm font-semibold tracking-tight">{role.title}</h4>
<p className="text-[11px] text-muted-foreground/70 mt-0.5">
{role.description}
</p>
@ -389,9 +389,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
</Label>
<Select
value={currentAssignment?.toString() || "unassigned"}
onValueChange={(value) =>
handleRoleAssignment(role.prefKey, value)
}
onValueChange={(value) => handleRoleAssignment(role.prefKey, value)}
>
<SelectTrigger className="w-full h-9 md:h-10 text-xs md:text-sm">
<SelectValue placeholder="Select a configuration" />
@ -401,9 +399,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
value="unassigned"
className="text-xs md:text-sm py-1.5 md:py-2"
>
<span className="text-muted-foreground">
Unassigned
</span>
<span className="text-muted-foreground">Unassigned</span>
</SelectItem>
{/* Global Configurations */}
@ -413,9 +409,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
Global Configurations
</SelectLabel>
{roleGlobalConfigs.map((config) => {
const isAuto =
"is_auto_mode" in config &&
config.is_auto_mode;
const isAuto = "is_auto_mode" in config && config.is_auto_mode;
return (
<SelectItem
key={config.id}
@ -432,18 +426,16 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
AUTO
</Badge>
) : (
getProviderIcon(config.provider, { className: "size-3 md:size-3.5 shrink-0" })
getProviderIcon(config.provider, {
className: "size-3 md:size-3.5 shrink-0",
})
)}
<span className="truncate text-xs md:text-sm">
{config.name}
</span>
{!isAuto && (
<span className="text-muted-foreground text-[10px] md:text-[11px] truncate">
(
{
config.model_name
}
)
({config.model_name})
</span>
)}
{isAuto && (
@ -468,42 +460,40 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
Your Configurations
</SelectLabel>
{roleUserConfigs.map((config) => (
<SelectItem
key={config.id}
value={config.id.toString()}
className="text-xs md:text-sm py-1.5 md:py-2"
>
<div className="flex items-center gap-1 md:gap-1.5 flex-wrap min-w-0">
{getProviderIcon(config.provider, { className: "size-3 md:size-3.5 shrink-0" })}
<span className="truncate text-xs md:text-sm">
{config.name}
</span>
<span className="text-muted-foreground text-[10px] md:text-[11px] truncate">
(
{
config.model_name
}
)
</span>
</div>
</SelectItem>
))}
<SelectItem
key={config.id}
value={config.id.toString()}
className="text-xs md:text-sm py-1.5 md:py-2"
>
<div className="flex items-center gap-1 md:gap-1.5 flex-wrap min-w-0">
{getProviderIcon(config.provider, {
className: "size-3 md:size-3.5 shrink-0",
})}
<span className="truncate text-xs md:text-sm">
{config.name}
</span>
<span className="text-muted-foreground text-[10px] md:text-[11px] truncate">
({config.model_name})
</span>
</div>
</SelectItem>
))}
</SelectGroup>
)}
</SelectContent>
</Select>
</div>
{/* Assigned Config Summary */}
{assignedConfig && (
<div
className={cn(
"rounded-lg p-3 border",
isAutoMode
? "bg-violet-50 dark:bg-violet-900/10 border-violet-200/50 dark:border-violet-800/30"
: "bg-muted/40 border-border/50"
)}
>
{/* Assigned Config Summary */}
{assignedConfig && (
<div
className={cn(
"rounded-lg p-3 border",
isAutoMode
? "bg-violet-50 dark:bg-violet-900/10 border-violet-200/50 dark:border-violet-800/30"
: "bg-muted/40 border-border/50"
)}
>
{isAutoMode ? (
<div className="flex items-center gap-2">
<Shuffle
@ -525,21 +515,17 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
<IconComponent className="w-3.5 h-3.5 shrink-0 mt-0.5 text-muted-foreground" />
<div className="min-w-0 flex-1">
<div className="flex items-center gap-1.5 flex-wrap">
<span className="text-xs font-medium">
{assignedConfig.name}
</span>
{"is_global" in assignedConfig &&
assignedConfig.is_global && (
<Badge
variant="secondary"
className="text-[9px] px-1.5 py-0"
>
🌐 Global
</Badge>
)}
<span className="text-xs font-medium">{assignedConfig.name}</span>
{"is_global" in assignedConfig && assignedConfig.is_global && (
<Badge variant="secondary" className="text-[9px] px-1.5 py-0">
🌐 Global
</Badge>
)}
</div>
<div className="flex items-center gap-1.5 mt-1">
{getProviderIcon(assignedConfig.provider, { className: "size-3 shrink-0" })}
{getProviderIcon(assignedConfig.provider, {
className: "size-3 shrink-0",
})}
<code className="text-[10px] text-muted-foreground font-mono truncate">
{assignedConfig.model_name}
</code>
@ -552,9 +538,9 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
</div>
</div>
)}
</div>
)}
</CardContent>
</div>
)}
</CardContent>
</Card>
</motion.div>
);
@ -572,9 +558,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
transition={{ duration: 0.2 }}
className="flex items-center justify-between gap-3 rounded-lg border border-border bg-muted/50 p-3 md:p-4"
>
<p className="text-xs md:text-sm text-muted-foreground">
You have unsaved changes
</p>
<p className="text-xs md:text-sm text-muted-foreground">You have unsaved changes</p>
<div className="flex items-center gap-2">
<Button
variant="outline"

View file

@ -83,18 +83,15 @@ function getInitials(name: string): string {
export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
// Mutations
const {
mutateAsync: createConfig,
isPending: isCreating,
} = useAtomValue(createNewLLMConfigMutationAtom);
const {
mutateAsync: updateConfig,
isPending: isUpdating,
} = useAtomValue(updateNewLLMConfigMutationAtom);
const {
mutateAsync: deleteConfig,
isPending: isDeleting,
} = useAtomValue(deleteNewLLMConfigMutationAtom);
const { mutateAsync: createConfig, isPending: isCreating } = useAtomValue(
createNewLLMConfigMutationAtom
);
const { mutateAsync: updateConfig, isPending: isUpdating } = useAtomValue(
updateNewLLMConfigMutationAtom
);
const { mutateAsync: deleteConfig, isPending: isDeleting } = useAtomValue(
deleteNewLLMConfigMutationAtom
);
// Queries
const {
@ -380,43 +377,43 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
)}
</div>
{(canUpdate || canDelete) && (
<div className="flex items-center gap-0.5 shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity duration-150">
{canUpdate && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => openEditDialog(config)}
className="h-7 w-7 text-muted-foreground hover:text-foreground"
>
<Edit3 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Edit</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{canDelete && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => setConfigToDelete(config)}
className="h-7 w-7 text-muted-foreground hover:text-destructive"
>
<Trash2 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Delete</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
)}
<div className="flex items-center gap-0.5 shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity duration-150">
{canUpdate && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => openEditDialog(config)}
className="h-7 w-7 text-muted-foreground hover:text-foreground"
>
<Edit3 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Edit</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{canDelete && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => setConfigToDelete(config)}
className="h-7 w-7 text-muted-foreground hover:text-destructive"
>
<Trash2 className="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>Delete</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
)}
</div>
{/* Provider + Model */}
@ -453,14 +450,11 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
{/* Footer: Date + Creator */}
<div className="flex items-center gap-2 pt-2 border-t border-border/40 mt-auto">
<span className="text-[11px] text-muted-foreground/60">
{new Date(config.created_at).toLocaleDateString(
undefined,
{
year: "numeric",
month: "short",
day: "numeric",
}
)}
{new Date(config.created_at).toLocaleDateString(undefined, {
year: "numeric",
month: "short",
day: "numeric",
})}
</span>
{member && (
<>