Skip to content

Commit

Permalink
feat(frontend): check linked chat engines before delete kb (#456)
Browse files Browse the repository at this point in the history
close #452
  • Loading branch information
634750802 authored Dec 4, 2024
1 parent 4928ae9 commit 9a5e236
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 6 deletions.
15 changes: 14 additions & 1 deletion frontend/app/src/api/knowledge-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ const kgIndexErrorSchema = z.object({
error: z.string().nullable(),
}) satisfies ZodType<DatasourceKgIndexError, any, any>;

const knowledgeBaseLinkedChatEngine = z.object({
id: z.number(),
name: z.string(),
is_default: z.boolean(),
})

export async function listKnowledgeBases ({ page = 1, size = 10 }: PageParams) {
return await fetch(requestUrl('/api/v1/admin/knowledge_bases', { page, size }), {
headers: await authenticationHeaders(),
Expand All @@ -173,7 +179,14 @@ export async function getKnowledgeBaseDocument (id: number, documentId: number)
return await fetch(requestUrl(`/api/v1/admin/knowledge_bases/${id}/documents/${documentId}`), {
headers: await authenticationHeaders(),
})
.then(handleResponse(documentSchema));
.then(handleResponse(documentSchema.omit({ knowledge_base: true, data_source: true })));
}

export async function getKnowledgeBaseLinkedChatEngines (id: number) {
return await fetch(requestUrl(`/api/v1/admin/knowledge_bases/${id}/linked_chat_engines`), {
headers: await authenticationHeaders(),
})
.then(handleResponse(knowledgeBaseLinkedChatEngine.array()));
}

export async function deleteKnowledgeBaseDocument (id: number, documentId: number) {
Expand Down
7 changes: 5 additions & 2 deletions frontend/app/src/components/dangerous-action-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ export interface DangerousActionButtonProps extends ButtonProps {
action: () => Promise<void>;
dialogTitle?: ReactNode;
dialogDescription?: ReactNode;
actionDisabled?: boolean;
actionDisabledReason?: ReactNode;
}

export const DangerousActionButton = forwardRef<HTMLButtonElement, DangerousActionButtonProps>(({ action, dialogDescription, dialogTitle, asChild, ...props }, ref) => {
export const DangerousActionButton = forwardRef<HTMLButtonElement, DangerousActionButtonProps>(({ action, dialogDescription, dialogTitle, actionDisabledReason, actionDisabled, asChild, ...props }, ref) => {
const [open, setOpen] = useState(false);
const [acting, setActing] = useState(false);
const [error, setError] = useState<unknown>();
Expand Down Expand Up @@ -49,9 +51,10 @@ export const DangerousActionButton = forwardRef<HTMLButtonElement, DangerousActi
<AlertTitle>Failed to perform operation</AlertTitle>
<AlertDescription>{getErrorMessage(error)}</AlertDescription>
</Alert>}
{actionDisabled && actionDisabledReason}
<AlertDialogFooter>
<AlertDialogCancel className={cn('border-none', buttonVariants({ variant: 'ghost' }))}>Cancel</AlertDialogCancel>
<AlertDialogAction className={buttonVariants({ variant: 'destructive' })} disabled={acting} onClick={handleClick}>
<AlertDialogAction className={buttonVariants({ variant: 'destructive' })} disabled={actionDisabled || acting} onClick={handleClick}>
{acting && <Loader2Icon className="size-4 mr-1 animate-spin repeat-infinite" />}
Continue
</AlertDialogAction>
Expand Down
22 changes: 19 additions & 3 deletions frontend/app/src/components/knowledge-base/knowledge-base-card.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { deleteKnowledgeBase, type KnowledgeBaseSummary } from '@/api/knowledge-base';
import { deleteKnowledgeBase, getKnowledgeBaseLinkedChatEngines, type KnowledgeBaseSummary } from '@/api/knowledge-base';
import { DangerousActionButton } from '@/components/dangerous-action-button';
import { mutateKnowledgeBases } from '@/components/knowledge-base/hooks';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardFooter, CardHeader } from '@/components/ui/card';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
import { Separator } from '@/components/ui/separator';
import { Skeleton } from '@/components/ui/skeleton';
import { cn } from '@/lib/utils';
import { AlertTriangleIcon, Book, Ellipsis } from 'lucide-react';
import { AlertTriangleIcon, Book, Ellipsis, TriangleAlertIcon } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { ReactNode, startTransition, useState } from 'react';
import useSWR from 'swr';

export function KnowledgeBaseCard ({ knowledgeBase, children }: { knowledgeBase: KnowledgeBaseSummary, children?: ReactNode }) {
const router = useRouter();
const [dropdownOpen, setDropdownOpen] = useState(false);
const { data: linkedChatEngines } = useSWR(`api.knowledge-bases.${knowledgeBase.id}.linked-chat-engines`, () => getKnowledgeBaseLinkedChatEngines(knowledgeBase.id));

const handleCardClick = () => {
startTransition(() => {
Expand Down Expand Up @@ -49,6 +52,9 @@ export function KnowledgeBaseCard ({ knowledgeBase, children }: { knowledgeBase:
<span className="shrink-0 mx-0.5 px-1">·</span>
<span>{(knowledgeBase.data_sources_total ?? 0) || <><AlertTriangleIcon className="size-3 inline-flex" /> No</>} data sources</span>
</div>
<div className="flex items-center text-xs text-muted-foreground">
<span>{linkedChatEngines?.length ?? <Skeleton className="inline-flex h-3 w-6 rounded" />} linked chat engines</span>
</div>
</div>
</div>
</CardHeader>
Expand All @@ -72,9 +78,19 @@ export function KnowledgeBaseCard ({ knowledgeBase, children }: { knowledgeBase:
<DropdownMenuContent className="w-56" align="end" alignOffset={-9} onClick={event => event.stopPropagation()}>
<DropdownMenuItem onSelect={handleMenuItemSettingSelect}>Settings</DropdownMenuItem>
<DropdownMenuSeparator />
<DangerousActionButton action={handleDelete} asChild>
<DangerousActionButton
action={handleDelete}
asChild
actionDisabled={(linkedChatEngines?.length ?? 0) > 0}
actionDisabledReason={<Alert variant="warning">
<TriangleAlertIcon />
<AlertTitle>Cannot delete this Knowledge Base</AlertTitle>
<AlertDescription>This Knowledge Base was linked to at least one Chat Engine(s). Please unlink all Chat Engines to continue.</AlertDescription>
</Alert>}
>
<DropdownMenuItem
className="text-destructive focus:text-destructive focus:bg-destructive/10"
disabled={linkedChatEngines == null}
onSelect={event => event.preventDefault()}
>
Delete
Expand Down

0 comments on commit 9a5e236

Please sign in to comment.