mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-17 18:35:19 +02:00
refactor: enhance layout and styling in settings components for improved UI consistency
This commit is contained in:
parent
ae3d254a2c
commit
f22d7434ce
4 changed files with 123 additions and 132 deletions
|
|
@ -189,26 +189,28 @@ export function GeneralSettingsManager({ searchSpaceId }: GeneralSettingsManager
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div className="border-t pt-6 space-y-2">
|
<div className="border-t pt-6 flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||||
|
<div className="space-y-1">
|
||||||
<Label>Export knowledge base</Label>
|
<Label>Export knowledge base</Label>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Download all documents in this search space as a ZIP of markdown files.
|
Download all documents in this search space as a ZIP of markdown files.
|
||||||
</p>
|
</p>
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="secondary"
|
|
||||||
size="sm"
|
|
||||||
disabled={isExporting}
|
|
||||||
onClick={handleExportKB}
|
|
||||||
className="relative"
|
|
||||||
>
|
|
||||||
<span className={isExporting ? "opacity-0" : ""}>
|
|
||||||
<FolderArchive className="h-3 w-3 opacity-60" />
|
|
||||||
</span>
|
|
||||||
<span className={isExporting ? "opacity-0" : ""}>Export</span>
|
|
||||||
{isExporting && <Spinner size="sm" className="absolute" />}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
disabled={isExporting}
|
||||||
|
onClick={handleExportKB}
|
||||||
|
className="relative w-fit shrink-0"
|
||||||
|
>
|
||||||
|
<span className={isExporting ? "opacity-0" : ""}>
|
||||||
|
<FolderArchive className="h-3 w-3 opacity-60" />
|
||||||
|
</span>
|
||||||
|
<span className={isExporting ? "opacity-0" : ""}>Export</span>
|
||||||
|
{isExporting && <Spinner size="sm" className="absolute" />}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { AlertCircle, Dot, Edit3, Info, RefreshCw, Trash2, Wand2 } from "lucide-react";
|
import { AlertCircle, Dot, Edit3, Info, RefreshCw, Trash2 } from "lucide-react";
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { deleteImageGenConfigMutationAtom } from "@/atoms/image-gen-config/image-gen-config-mutation.atoms";
|
import { deleteImageGenConfigMutationAtom } from "@/atoms/image-gen-config/image-gen-config-mutation.atoms";
|
||||||
import {
|
import {
|
||||||
|
|
@ -209,20 +209,20 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
|
||||||
{["skeleton-a", "skeleton-b", "skeleton-c"].map((key) => (
|
{["skeleton-a", "skeleton-b", "skeleton-c"].map((key) => (
|
||||||
<Card key={key} className="border-border/60">
|
<Card key={key} className="border-border/60">
|
||||||
<CardContent className="p-4 flex flex-col gap-3">
|
<CardContent className="p-4 flex flex-col gap-3">
|
||||||
<div className="flex items-start justify-between gap-2">
|
<div className="flex items-center gap-2.5">
|
||||||
|
<Skeleton className="size-4 rounded-full shrink-0" />
|
||||||
<div className="space-y-1.5 flex-1 min-w-0">
|
<div className="space-y-1.5 flex-1 min-w-0">
|
||||||
<Skeleton className="h-4 w-28 md:w-32" />
|
<Skeleton className="h-4 w-28 md:w-32" />
|
||||||
<Skeleton className="h-3 w-40 md:w-48" />
|
<Skeleton className="h-3 w-40 md:w-48" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center pt-2 border-t border-border/40">
|
||||||
<Skeleton className="h-5 w-16 rounded-full" />
|
<Skeleton className="h-3 w-20 flex-1" />
|
||||||
<Skeleton className="h-5 w-24 rounded-md" />
|
<Skeleton className="h-3 w-3 rounded-full shrink-0 mx-1" />
|
||||||
</div>
|
<div className="flex-1 flex items-center justify-end gap-1.5">
|
||||||
<div className="flex items-center gap-2 pt-2 border-t border-border/40">
|
<Skeleton className="h-4 w-4 rounded-full" />
|
||||||
<Skeleton className="h-3 w-20" />
|
<Skeleton className="h-3 w-16" />
|
||||||
<Skeleton className="h-4 w-4 rounded-full" />
|
</div>
|
||||||
<Skeleton className="h-3 w-16" />
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -255,20 +255,25 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
|
||||||
<div key={config.id}>
|
<div key={config.id}>
|
||||||
<Card className="group relative overflow-hidden transition-all duration-200 border-border/60 hover:shadow-md h-full">
|
<Card className="group relative overflow-hidden transition-all duration-200 border-border/60 hover:shadow-md h-full">
|
||||||
<CardContent className="p-4 flex flex-col gap-3 h-full">
|
<CardContent className="p-4 flex flex-col gap-3 h-full">
|
||||||
{/* Header: Name + Actions */}
|
{/* Header: Icon + Name + Actions */}
|
||||||
<div className="flex items-start justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="min-w-0 flex-1">
|
<div className="flex items-center gap-2.5 min-w-0 flex-1">
|
||||||
<h4 className="text-sm font-semibold tracking-tight truncate">
|
<div className="shrink-0">
|
||||||
{config.name}
|
{getProviderIcon(config.provider, { className: "size-4" })}
|
||||||
</h4>
|
</div>
|
||||||
{config.description && (
|
<div className="min-w-0 flex-1">
|
||||||
<p className="text-[11px] text-muted-foreground/70 truncate mt-0.5">
|
<h4 className="text-sm font-semibold tracking-tight truncate">
|
||||||
{config.description}
|
{config.name}
|
||||||
</p>
|
</h4>
|
||||||
)}
|
{config.description && (
|
||||||
|
<p className="text-[11px] text-muted-foreground/70 truncate mt-0.5">
|
||||||
|
{config.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{(canUpdate || canDelete) && (
|
{(canUpdate || canDelete) && (
|
||||||
<div className="flex items-center gap-0.5 shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity duration-150">
|
<div className="flex items-center gap-0 shrink-0 sm:w-0 sm:overflow-hidden sm:group-hover:w-auto sm:opacity-0 sm:group-hover:opacity-100 transition-all duration-150">
|
||||||
{canUpdate && (
|
{canUpdate && (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip open={isDesktop ? undefined : false}>
|
<Tooltip open={isDesktop ? undefined : false}>
|
||||||
|
|
@ -277,7 +282,7 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => openEditDialog(config)}
|
onClick={() => openEditDialog(config)}
|
||||||
className="h-7 w-7 text-muted-foreground hover:text-foreground"
|
className="h-6 w-6 text-muted-foreground hover:text-foreground"
|
||||||
>
|
>
|
||||||
<Edit3 className="h-3 w-3" />
|
<Edit3 className="h-3 w-3" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -294,7 +299,7 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => setConfigToDelete(config)}
|
onClick={() => setConfigToDelete(config)}
|
||||||
className="h-7 w-7 text-muted-foreground hover:text-destructive"
|
className="h-6 w-6 text-muted-foreground hover:text-destructive"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-3 w-3" />
|
<Trash2 className="h-3 w-3" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -307,17 +312,9 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Provider + Model */}
|
|
||||||
<div className="flex items-center gap-2 flex-wrap">
|
|
||||||
{getProviderIcon(config.provider, { className: "size-3.5 shrink-0" })}
|
|
||||||
<code className="text-[11px] font-mono text-muted-foreground bg-muted/60 px-2 py-0.5 rounded-md truncate max-w-[160px]">
|
|
||||||
{config.model_name}
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Footer: Date + Creator */}
|
{/* Footer: Date + Creator */}
|
||||||
<div className="flex items-center gap-2 pt-2 border-t border-border/40 mt-auto">
|
<div className="flex items-center pt-2 border-t border-border/40 mt-auto">
|
||||||
<span className="text-[11px] text-muted-foreground/60">
|
<span className="flex-1 min-w-0 text-[11px] text-muted-foreground/60 truncate">
|
||||||
{new Date(config.created_at).toLocaleDateString(undefined, {
|
{new Date(config.created_at).toLocaleDateString(undefined, {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "short",
|
month: "short",
|
||||||
|
|
@ -326,11 +323,11 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
|
||||||
</span>
|
</span>
|
||||||
{member && (
|
{member && (
|
||||||
<>
|
<>
|
||||||
<Dot className="h-4 w-4 text-muted-foreground/30" />
|
<Dot className="h-4 w-4 text-muted-foreground/30 shrink-0" />
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip open={isDesktop ? undefined : false}>
|
<Tooltip open={isDesktop ? undefined : false}>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div className="flex items-center gap-1.5 cursor-default">
|
<div className="flex-1 min-w-0 flex items-center justify-end gap-1.5 cursor-default">
|
||||||
<Avatar className="size-4.5 shrink-0">
|
<Avatar className="size-4.5 shrink-0">
|
||||||
{member.avatarUrl && (
|
{member.avatarUrl && (
|
||||||
<AvatarImage src={member.avatarUrl} alt={member.name} />
|
<AvatarImage src={member.avatarUrl} alt={member.name} />
|
||||||
|
|
@ -339,7 +336,7 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {
|
||||||
{getInitials(member.name)}
|
{getInitials(member.name)}
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<span className="text-[11px] text-muted-foreground/60 truncate max-w-[120px]">
|
<span className="text-[11px] text-muted-foreground/60 truncate">
|
||||||
{member.name}
|
{member.name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import {
|
||||||
MessageSquareQuote,
|
MessageSquareQuote,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
Trash2,
|
Trash2,
|
||||||
Wand2,
|
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { membersAtom, myAccessAtom } from "@/atoms/members/members-query.atoms";
|
import { membersAtom, myAccessAtom } from "@/atoms/members/members-query.atoms";
|
||||||
|
|
@ -208,28 +207,26 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
|
||||||
{["skeleton-a", "skeleton-b", "skeleton-c"].map((key) => (
|
{["skeleton-a", "skeleton-b", "skeleton-c"].map((key) => (
|
||||||
<Card key={key} className="border-border/60">
|
<Card key={key} className="border-border/60">
|
||||||
<CardContent className="p-4 flex flex-col gap-3">
|
<CardContent className="p-4 flex flex-col gap-3">
|
||||||
{/* Header */}
|
{/* Header: Icon + Name */}
|
||||||
<div className="flex items-start justify-between gap-2">
|
<div className="flex items-start gap-2.5">
|
||||||
|
<Skeleton className="size-4 rounded-full shrink-0 mt-0.5" />
|
||||||
<div className="space-y-1.5 flex-1 min-w-0">
|
<div className="space-y-1.5 flex-1 min-w-0">
|
||||||
<Skeleton className="h-4 w-28 md:w-32" />
|
<Skeleton className="h-4 w-28 md:w-32" />
|
||||||
<Skeleton className="h-3 w-40 md:w-48" />
|
<Skeleton className="h-3 w-40 md:w-48" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Provider + Model */}
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Skeleton className="h-5 w-16 rounded-full" />
|
|
||||||
<Skeleton className="h-5 w-24 rounded-md" />
|
|
||||||
</div>
|
|
||||||
{/* Feature badges */}
|
{/* Feature badges */}
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<Skeleton className="h-5 w-20 rounded-full" />
|
<Skeleton className="h-5 w-20 rounded-full" />
|
||||||
<Skeleton className="h-5 w-16 rounded-full" />
|
|
||||||
</div>
|
</div>
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="flex items-center gap-2 pt-2 border-t border-border/40">
|
<div className="flex items-center pt-2 border-t border-border/40">
|
||||||
<Skeleton className="h-3 w-20" />
|
<Skeleton className="h-3 w-20 flex-1" />
|
||||||
<Skeleton className="h-4 w-4 rounded-full" />
|
<Skeleton className="h-3 w-3 rounded-full shrink-0 mx-1" />
|
||||||
<Skeleton className="h-3 w-16" />
|
<div className="flex-1 flex items-center justify-end gap-1.5">
|
||||||
|
<Skeleton className="h-4 w-4 rounded-full" />
|
||||||
|
<Skeleton className="h-3 w-16" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -262,20 +259,25 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
|
||||||
<div key={config.id}>
|
<div key={config.id}>
|
||||||
<Card className="group relative overflow-hidden transition-all duration-200 border-border/60 hover:shadow-md h-full">
|
<Card className="group relative overflow-hidden transition-all duration-200 border-border/60 hover:shadow-md h-full">
|
||||||
<CardContent className="p-4 flex flex-col gap-3 h-full">
|
<CardContent className="p-4 flex flex-col gap-3 h-full">
|
||||||
{/* Header: Name + Actions */}
|
{/* Header: Icon + Name + Actions */}
|
||||||
<div className="flex items-start justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="min-w-0 flex-1">
|
<div className="flex items-center gap-2.5 min-w-0 flex-1">
|
||||||
<h4 className="text-sm font-semibold tracking-tight truncate">
|
<div className="shrink-0">
|
||||||
{config.name}
|
{getProviderIcon(config.provider, { className: "size-4" })}
|
||||||
</h4>
|
</div>
|
||||||
{config.description && (
|
<div className="min-w-0 flex-1">
|
||||||
<p className="text-[11px] text-muted-foreground/70 truncate mt-0.5">
|
<h4 className="text-sm font-semibold tracking-tight truncate">
|
||||||
{config.description}
|
{config.name}
|
||||||
</p>
|
</h4>
|
||||||
)}
|
{config.description && (
|
||||||
|
<p className="text-[11px] text-muted-foreground/70 truncate mt-0.5">
|
||||||
|
{config.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{(canUpdate || canDelete) && (
|
{(canUpdate || canDelete) && (
|
||||||
<div className="flex items-center gap-0.5 shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity duration-150">
|
<div className="flex items-center gap-0 shrink-0 sm:w-0 sm:overflow-hidden sm:group-hover:w-auto sm:opacity-0 sm:group-hover:opacity-100 transition-all duration-150">
|
||||||
{canUpdate && (
|
{canUpdate && (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip open={isDesktop ? undefined : false}>
|
<Tooltip open={isDesktop ? undefined : false}>
|
||||||
|
|
@ -284,7 +286,7 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => openEditDialog(config)}
|
onClick={() => openEditDialog(config)}
|
||||||
className="h-7 w-7 text-muted-foreground hover:text-foreground"
|
className="h-6 w-6 text-muted-foreground hover:text-foreground"
|
||||||
>
|
>
|
||||||
<Edit3 className="h-3 w-3" />
|
<Edit3 className="h-3 w-3" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -301,7 +303,7 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => setConfigToDelete(config)}
|
onClick={() => setConfigToDelete(config)}
|
||||||
className="h-7 w-7 text-muted-foreground hover:text-destructive"
|
className="h-6 w-6 text-muted-foreground hover:text-destructive"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-3 w-3" />
|
<Trash2 className="h-3 w-3" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -314,20 +316,12 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Provider + Model */}
|
|
||||||
<div className="flex items-center gap-2 flex-wrap">
|
|
||||||
{getProviderIcon(config.provider, { className: "size-3.5 shrink-0" })}
|
|
||||||
<code className="text-[11px] font-mono text-muted-foreground bg-muted/60 px-2 py-0.5 rounded-md truncate max-w-[160px]">
|
|
||||||
{config.model_name}
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Feature badges */}
|
{/* Feature badges */}
|
||||||
<div className="flex items-center gap-1.5 flex-wrap">
|
<div className="flex items-center gap-1.5 flex-wrap">
|
||||||
{config.citations_enabled && (
|
{config.citations_enabled && (
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="secondary"
|
||||||
className="text-[10px] px-1.5 py-0.5 border-emerald-500/30 text-emerald-700 dark:text-emerald-300 bg-emerald-500/5"
|
className="text-[10px] px-1.5 py-0.5 border-0 text-muted-foreground bg-muted"
|
||||||
>
|
>
|
||||||
<MessageSquareQuote className="h-2.5 w-2.5 mr-1" />
|
<MessageSquareQuote className="h-2.5 w-2.5 mr-1" />
|
||||||
Citations
|
Citations
|
||||||
|
|
@ -336,8 +330,8 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
|
||||||
{!config.use_default_system_instructions &&
|
{!config.use_default_system_instructions &&
|
||||||
config.system_instructions && (
|
config.system_instructions && (
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="secondary"
|
||||||
className="text-[10px] px-1.5 py-0.5 border-blue-500/30 text-blue-700 dark:text-blue-300 bg-blue-500/5"
|
className="text-[10px] px-1.5 py-0.5 border-0 text-muted-foreground bg-muted"
|
||||||
>
|
>
|
||||||
<FileText className="h-2.5 w-2.5 mr-1" />
|
<FileText className="h-2.5 w-2.5 mr-1" />
|
||||||
Custom
|
Custom
|
||||||
|
|
@ -346,8 +340,8 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer: Date + Creator */}
|
{/* Footer: Date + Creator */}
|
||||||
<div className="flex items-center gap-2 pt-2 border-t border-border/40 mt-auto">
|
<div className="flex items-center pt-2 border-t border-border/40 mt-auto">
|
||||||
<span className="text-[11px] text-muted-foreground/60">
|
<span className="flex-1 min-w-0 text-[11px] text-muted-foreground/60 truncate">
|
||||||
{new Date(config.created_at).toLocaleDateString(undefined, {
|
{new Date(config.created_at).toLocaleDateString(undefined, {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "short",
|
month: "short",
|
||||||
|
|
@ -356,11 +350,11 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
|
||||||
</span>
|
</span>
|
||||||
{member && (
|
{member && (
|
||||||
<>
|
<>
|
||||||
<Dot className="h-4 w-4 text-muted-foreground/30" />
|
<Dot className="h-4 w-4 text-muted-foreground/30 shrink-0" />
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip open={isDesktop ? undefined : false}>
|
<Tooltip open={isDesktop ? undefined : false}>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div className="flex items-center gap-1.5 cursor-default">
|
<div className="flex-1 min-w-0 flex items-center justify-end gap-1.5 cursor-default">
|
||||||
<Avatar className="size-4.5 shrink-0">
|
<Avatar className="size-4.5 shrink-0">
|
||||||
{member.avatarUrl && (
|
{member.avatarUrl && (
|
||||||
<AvatarImage src={member.avatarUrl} alt={member.name} />
|
<AvatarImage src={member.avatarUrl} alt={member.name} />
|
||||||
|
|
@ -369,7 +363,7 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
|
||||||
{getInitials(member.name)}
|
{getInitials(member.name)}
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<span className="text-[11px] text-muted-foreground/60 truncate max-w-[120px]">
|
<span className="text-[11px] text-muted-foreground/60 truncate">
|
||||||
{member.name}
|
{member.name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -208,20 +208,20 @@ export function VisionModelManager({ searchSpaceId }: VisionModelManagerProps) {
|
||||||
{["skeleton-a", "skeleton-b", "skeleton-c"].map((key) => (
|
{["skeleton-a", "skeleton-b", "skeleton-c"].map((key) => (
|
||||||
<Card key={key} className="border-border/60">
|
<Card key={key} className="border-border/60">
|
||||||
<CardContent className="p-4 flex flex-col gap-3">
|
<CardContent className="p-4 flex flex-col gap-3">
|
||||||
<div className="flex items-start justify-between gap-2">
|
<div className="flex items-center gap-2.5">
|
||||||
|
<Skeleton className="size-4 rounded-full shrink-0" />
|
||||||
<div className="space-y-1.5 flex-1 min-w-0">
|
<div className="space-y-1.5 flex-1 min-w-0">
|
||||||
<Skeleton className="h-4 w-28 md:w-32" />
|
<Skeleton className="h-4 w-28 md:w-32" />
|
||||||
<Skeleton className="h-3 w-40 md:w-48" />
|
<Skeleton className="h-3 w-40 md:w-48" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center pt-2 border-t border-border/40">
|
||||||
<Skeleton className="h-5 w-16 rounded-full" />
|
<Skeleton className="h-3 w-20 flex-1" />
|
||||||
<Skeleton className="h-5 w-24 rounded-md" />
|
<Skeleton className="h-3 w-3 rounded-full shrink-0 mx-1" />
|
||||||
</div>
|
<div className="flex-1 flex items-center justify-end gap-1.5">
|
||||||
<div className="flex items-center gap-2 pt-2 border-t border-border/40">
|
<Skeleton className="h-4 w-4 rounded-full" />
|
||||||
<Skeleton className="h-3 w-20" />
|
<Skeleton className="h-3 w-16" />
|
||||||
<Skeleton className="h-4 w-4 rounded-full" />
|
</div>
|
||||||
<Skeleton className="h-3 w-16" />
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -253,19 +253,25 @@ export function VisionModelManager({ searchSpaceId }: VisionModelManagerProps) {
|
||||||
<div key={config.id}>
|
<div key={config.id}>
|
||||||
<Card className="group relative overflow-hidden transition-all duration-200 border-border/60 hover:shadow-md h-full">
|
<Card className="group relative overflow-hidden transition-all duration-200 border-border/60 hover:shadow-md h-full">
|
||||||
<CardContent className="p-4 flex flex-col gap-3 h-full">
|
<CardContent className="p-4 flex flex-col gap-3 h-full">
|
||||||
<div className="flex items-start justify-between gap-2">
|
{/* Header: Icon + Name + Actions */}
|
||||||
<div className="min-w-0 flex-1">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<h4 className="text-sm font-semibold tracking-tight truncate">
|
<div className="flex items-center gap-2.5 min-w-0 flex-1">
|
||||||
{config.name}
|
<div className="shrink-0">
|
||||||
</h4>
|
{getProviderIcon(config.provider, { className: "size-4" })}
|
||||||
{config.description && (
|
</div>
|
||||||
<p className="text-[11px] text-muted-foreground/70 truncate mt-0.5">
|
<div className="min-w-0 flex-1">
|
||||||
{config.description}
|
<h4 className="text-sm font-semibold tracking-tight truncate">
|
||||||
</p>
|
{config.name}
|
||||||
)}
|
</h4>
|
||||||
|
{config.description && (
|
||||||
|
<p className="text-[11px] text-muted-foreground/70 truncate mt-0.5">
|
||||||
|
{config.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{(canUpdate || canDelete) && (
|
{(canUpdate || canDelete) && (
|
||||||
<div className="flex items-center gap-0.5 shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity duration-150">
|
<div className="flex items-center gap-0 shrink-0 sm:w-0 sm:overflow-hidden sm:group-hover:w-auto sm:opacity-0 sm:group-hover:opacity-100 transition-all duration-150">
|
||||||
{canUpdate && (
|
{canUpdate && (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip open={isDesktop ? undefined : false}>
|
<Tooltip open={isDesktop ? undefined : false}>
|
||||||
|
|
@ -274,7 +280,7 @@ export function VisionModelManager({ searchSpaceId }: VisionModelManagerProps) {
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => openEditDialog(config)}
|
onClick={() => openEditDialog(config)}
|
||||||
className="h-7 w-7 text-muted-foreground hover:text-foreground"
|
className="h-6 w-6 text-muted-foreground hover:text-foreground"
|
||||||
>
|
>
|
||||||
<Edit3 className="h-3 w-3" />
|
<Edit3 className="h-3 w-3" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -291,7 +297,7 @@ export function VisionModelManager({ searchSpaceId }: VisionModelManagerProps) {
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => setConfigToDelete(config)}
|
onClick={() => setConfigToDelete(config)}
|
||||||
className="h-7 w-7 text-muted-foreground hover:text-destructive"
|
className="h-6 w-6 text-muted-foreground hover:text-destructive"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-3 w-3" />
|
<Trash2 className="h-3 w-3" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -304,17 +310,9 @@ export function VisionModelManager({ searchSpaceId }: VisionModelManagerProps) {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 flex-wrap">
|
{/* Footer: Date + Creator */}
|
||||||
{getProviderIcon(config.provider, {
|
<div className="flex items-center pt-2 border-t border-border/40 mt-auto">
|
||||||
className: "size-3.5 shrink-0",
|
<span className="flex-1 min-w-0 text-[11px] text-muted-foreground/60 truncate">
|
||||||
})}
|
|
||||||
<code className="text-[11px] font-mono text-muted-foreground bg-muted/60 px-2 py-0.5 rounded-md truncate max-w-[160px]">
|
|
||||||
{config.model_name}
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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, {
|
{new Date(config.created_at).toLocaleDateString(undefined, {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "short",
|
month: "short",
|
||||||
|
|
@ -323,11 +321,11 @@ export function VisionModelManager({ searchSpaceId }: VisionModelManagerProps) {
|
||||||
</span>
|
</span>
|
||||||
{member && (
|
{member && (
|
||||||
<>
|
<>
|
||||||
<Dot className="h-4 w-4 text-muted-foreground/30" />
|
<Dot className="h-4 w-4 text-muted-foreground/30 shrink-0" />
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip open={isDesktop ? undefined : false}>
|
<Tooltip open={isDesktop ? undefined : false}>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div className="flex items-center gap-1.5 cursor-default">
|
<div className="flex-1 min-w-0 flex items-center justify-end gap-1.5 cursor-default">
|
||||||
<Avatar className="size-4.5 shrink-0">
|
<Avatar className="size-4.5 shrink-0">
|
||||||
{member.avatarUrl && (
|
{member.avatarUrl && (
|
||||||
<AvatarImage src={member.avatarUrl} alt={member.name} />
|
<AvatarImage src={member.avatarUrl} alt={member.name} />
|
||||||
|
|
@ -336,7 +334,7 @@ export function VisionModelManager({ searchSpaceId }: VisionModelManagerProps) {
|
||||||
{getInitials(member.name)}
|
{getInitials(member.name)}
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<span className="text-[11px] text-muted-foreground/60 truncate max-w-[120px]">
|
<span className="text-[11px] text-muted-foreground/60 truncate">
|
||||||
{member.name}
|
{member.name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue