Skip to content

Commit

Permalink
feat(admin): add update prompt function
Browse files Browse the repository at this point in the history
  • Loading branch information
JimmFly committed Aug 1, 2024
1 parent 107fc29 commit 382de20
Show file tree
Hide file tree
Showing 11 changed files with 284 additions and 56 deletions.
44 changes: 44 additions & 0 deletions packages/frontend/admin/src/modules/ai/discard-changes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Button } from '@affine/admin/components/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@affine/admin/components/ui/dialog';

export const DiscardChanges = ({
open,
onClose,
onConfirm,
onOpenChange,
}: {
open: boolean;
onClose: () => void;
onConfirm: () => void;
onOpenChange: (open: boolean) => void;
}) => {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:w-[460px]">
<DialogHeader>
<DialogTitle className="leading-7">Discard Changes</DialogTitle>
<DialogDescription className="leading-6">
Changes to this prompt will not be saved.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<div className="flex justify-end items-center w-full space-x-4">
<Button type="button" onClick={onClose} variant="outline">
<span>Cancel</span>
</Button>
<Button type="button" onClick={onConfirm} variant="destructive">
<span>Discard</span>
</Button>
</div>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
38 changes: 26 additions & 12 deletions packages/frontend/admin/src/modules/ai/edit-prompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,57 @@ import { ScrollArea } from '@affine/admin/components/ui/scroll-area';
import { Separator } from '@affine/admin/components/ui/separator';
import { Textarea } from '@affine/admin/components/ui/textarea';
import { CheckIcon, XIcon } from 'lucide-react';
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';

import { useRightPanel } from '../layout';
import type { Prompt } from './prompts';
import { usePrompt } from './use-prompt';

export function EditPrompt({ item }: { item: Prompt }) {
const { closePanel } = useRightPanel();

const [messages, setMessages] = useState(item.messages);
const { updatePrompt } = usePrompt();

const handleChange = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
console.log(e);
(e: React.ChangeEvent<HTMLTextAreaElement>, index: number) => {
const newMessages = [...messages];
newMessages[index] = {
...newMessages[index],
content: e.target.value,
};
setMessages(newMessages);
},
[]
[messages]
);
const handleClose = useCallback(() => {
setMessages(item.messages);
closePanel();
}, [closePanel, item.messages]);

const onConfirm = useCallback(() => {
closePanel();
}, [closePanel]);
updatePrompt({ name: item.name, messages });
handleClose();
}, [handleClose, item.name, messages, updatePrompt]);

//TODO(Jimmfly): add logic to save
const [disableSave, _] = useState(true);
const disableSave = useMemo(
() => JSON.stringify(messages) === JSON.stringify(item.messages),
[item.messages, messages]
);

useEffect(() => {
setMessages(item.messages);
}, [item.messages]);

return (
<div className="flex flex-col h-screen gap-1">
<div className="flex justify-between items-center py-[10px] px-6">
<div className="flex flex-col h-full gap-1">
<div className="flex-grow-0 flex-shrink-0 h-[56px] flex justify-between items-center py-[10px] px-6 ">
<Button
type="button"
size="icon"
className="w-7 h-7"
variant="ghost"
onClick={closePanel}
onClick={handleClose}
>
<XIcon size={20} />
</Button>
Expand Down Expand Up @@ -121,7 +135,7 @@ export function EditPrompt({ item }: { item: Prompt }) {
<Textarea
className=" min-h-48"
value={message.content}
onChange={handleChange}
onChange={e => handleChange(e, index)}
/>
</div>
))}
Expand Down
4 changes: 2 additions & 2 deletions packages/frontend/admin/src/modules/ai/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export function Ai() {

export function AiPage() {
return (
<div className=" h-screen flex-1 space-y-1 flex-col flex">
<div className="flex items-center justify-between px-6 py-3">
<div className=" h-screen flex-1 flex-col flex">
<div className="flex items-center justify-between px-6 py-3 max-md:ml-9 max-md:mt-[2px]">
<div className="text-base font-medium">AI</div>
</div>
<Separator />
Expand Down
6 changes: 3 additions & 3 deletions packages/frontend/admin/src/modules/ai/keys.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function Keys() {
placeholder="sk-xxxxxxxxxxxxx-xxxxxxxxxxxxxx"
onChange={e => setOpenAIKey(e.target.value)}
/>
<Button>Save</Button>
<Button disabled>Save</Button>
</div>
</div>
<Separator />
Expand All @@ -40,7 +40,7 @@ export function Keys() {
placeholder="00000000-0000-0000-00000000:xxxxxxxxxxxxxxxxx"
onChange={e => setFalAIKey(e.target.value)}
/>
<Button>Save</Button>
<Button disabled>Save</Button>
</div>
</div>
<Separator />
Expand All @@ -54,7 +54,7 @@ export function Keys() {
placeholder="00000000-0000-0000-00000000:xxxxxxxxxxxxxxxxx"
onChange={e => setUnsplashKey(e.target.value)}
/>
<Button>Save</Button>
<Button disabled>Save</Button>
</div>
</div>
<Separator />
Expand Down
105 changes: 72 additions & 33 deletions packages/frontend/admin/src/modules/ai/prompts.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { Button } from '@affine/admin/components/ui/button';
import { Separator } from '@affine/admin/components/ui/separator';
import { useQuery } from '@affine/core/hooks/use-query';
import type { CopilotModels, CopilotPromptMessageRole } from '@affine/graphql';
import { getPromptsQuery } from '@affine/graphql';
import { useCallback } from 'react';
import type { CopilotPromptMessageRole } from '@affine/graphql';
import { useCallback, useState } from 'react';

import { useRightPanel } from '../layout';
import { DiscardChanges } from './discard-changes';
import { EditPrompt } from './edit-prompt';
import { usePrompt } from './use-prompt';

export type Prompt = {
__typename?: 'CopilotPromptType';
name: string;
model: CopilotModels;
model: string;
action: string | null;
config: {
__typename?: 'CopilotPromptConfigType';
Expand All @@ -30,21 +30,7 @@ export type Prompt = {
};

export function Prompts() {
const { data } = useQuery({
query: getPromptsQuery,
});

const list = data.listCopilotPrompts;
const { setRightPanelContent, openPanel } = useRightPanel();

const handleEdit = useCallback(
(item: Prompt) => {
setRightPanelContent(<EditPrompt item={item} />);
openPanel();
},
[openPanel, setRightPanelContent]
);

const { prompts: list } = usePrompt();
return (
<div className="flex flex-col h-full gap-3 py-5 px-6 w-full">
<div className="flex items-center">
Expand All @@ -53,22 +39,75 @@ export function Prompts() {
<div className="flex-grow overflow-y-auto space-y-[10px]">
<div className="flex flex-col rounded-md border w-full">
{list.map((item, index) => (
<div key={item.name.concat(index.toString())}>
{index !== 0 && <Separator />}
<Button
variant="ghost"
className="flex flex-col gap-1 w-full items-start px-6 py-[14px] h-full "
onClick={() => handleEdit(item)}
>
<div>{item.name}</div>
<div className="text-left w-full opacity-50 overflow-hidden text-ellipsis whitespace-nowrap break-words text-nowrap">
{item.messages.flatMap(message => message.content).join(' ')}
</div>
</Button>
</div>
<PromptRow
key={item.name.concat(index.toString())}
item={item}
index={index}
/>
))}
</div>
</div>
</div>
);
}

export const PromptRow = ({ item, index }: { item: Prompt; index: number }) => {
const { setRightPanelContent, openPanel, isOpen } = useRightPanel();
const [dialogOpen, setDialogOpen] = useState(false);

const handleDiscardChangesCancel = useCallback(() => {
setDialogOpen(false);
}, []);

const handleConfirm = useCallback(
(item: Prompt) => {
setRightPanelContent(<EditPrompt item={item} />);
if (dialogOpen) {
handleDiscardChangesCancel();
}

if (!isOpen) {
openPanel();
}
},
[
dialogOpen,
handleDiscardChangesCancel,
isOpen,
openPanel,
setRightPanelContent,
]
);

const handleEdit = useCallback(
(item: Prompt) => {
if (isOpen) {
setDialogOpen(true);
} else {
handleConfirm(item);
}
},
[handleConfirm, isOpen]
);
return (
<div>
{index !== 0 && <Separator />}
<Button
variant="ghost"
className="flex flex-col gap-1 w-full items-start px-6 py-[14px] h-full "
onClick={() => handleEdit(item)}
>
<div>{item.name}</div>
<div className="text-left w-full opacity-50 overflow-hidden text-ellipsis whitespace-nowrap break-words text-nowrap">
{item.messages.flatMap(message => message.content).join(' ')}
</div>
</Button>
<DiscardChanges
open={dialogOpen}
onOpenChange={setDialogOpen}
onClose={handleDiscardChangesCancel}
onConfirm={() => handleConfirm(item)}
/>
</div>
);
};
51 changes: 51 additions & 0 deletions packages/frontend/admin/src/modules/ai/use-prompt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import {
useMutateQueryResource,
useMutation,
} from '@affine/core/hooks/use-mutation';
import { useQuery } from '@affine/core/hooks/use-query';
import { getPromptsQuery, updatePromptMutation } from '@affine/graphql';
import { toast } from 'sonner';

import type { Prompt } from './prompts';

export const usePrompt = () => {
const { data } = useQuery({
query: getPromptsQuery,
});

const { trigger } = useMutation({
mutation: updatePromptMutation,
});

const revalidate = useMutateQueryResource();

const updatePrompt = useAsyncCallback(
async ({
name,
messages,
}: {
name: string;
messages: Prompt['messages'];
}) => {
await trigger({
name,
messages,
})
.then(async () => {
await revalidate(getPromptsQuery);
toast.success('Prompt updated successfully');
})
.catch(e => {
toast(e.message);
console.error(e);
});
},
[revalidate, trigger]
);

return {
prompts: data.listCopilotPrompts,
updatePrompt,
};
};
2 changes: 1 addition & 1 deletion packages/frontend/admin/src/modules/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export function Layout({ content }: LayoutProps) {
const [open, setOpen] = useState(false);
const rightPanelRef = useRef<ImperativePanelHandle>(null);

const [activeTab, setActiveTab] = useState('Accounts');
const [activeTab, setActiveTab] = useState('');
const [activeSubTab, setActiveSubTab] = useState('auth');
const [currentModule, setCurrentModule] = useState('auth');

Expand Down
3 changes: 0 additions & 3 deletions packages/frontend/admin/src/modules/nav/nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ const TabsMap: { [key: string]: string } = {
settings: 'Settings',
};

const defaultTab = 'Accounts';

export function Nav() {
const { moduleList } = useGetServerRuntimeConfig();
const { activeTab, setActiveTab, setCurrentModule } = useNav();
Expand All @@ -42,7 +40,6 @@ export function Nav() {
return;
}
}
setActiveTab(defaultTab);
}, [setActiveTab]);

return (
Expand Down
Loading

0 comments on commit 382de20

Please sign in to comment.