Skip to content

Commit af4dc07

Browse files
committed
Prompt editor rework
1 parent ef9642a commit af4dc07

File tree

12 files changed

+162
-268
lines changed

12 files changed

+162
-268
lines changed

e2e/prompts.spec.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,14 @@ test.describe('Prompts', () => {
5353

5454
const newPromptName = `testausprompti-${test.info().workerIndex}`
5555

56-
await page.getByRole('textbox', { name: 'Prompt name' }).fill(newPromptName)
57-
await page.getByRole('textbox', { name: 'e.g. You are a helpful' }).fill('mocktest kurssitesti onnistui')
56+
await page.getByTestId('create-prompt-button').click()
57+
58+
await page.getByTestId('prompt-name-input').fill(newPromptName)
59+
await page.getByTestId('system-message-input').fill('mocktest kurssitesti onnistui')
5860
await page.getByRole('button', { name: 'Save' }).click()
5961

62+
await page.getByTestId('close-prompt-editor').click()
63+
6064
// Prompt is created and link is visible
6165
await page.getByText(`Link to chat with the prompt '${newPromptName}' active`).click()
6266

src/client/components/ChatV2/ChatV2.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import { useCallback, useEffect, useRef, useState } from 'react'
88
import { useTranslation } from 'react-i18next'
99
import { useParams, useSearchParams } from 'react-router-dom'
1010
import { DEFAULT_MODEL, DEFAULT_MODEL_TEMPERATURE, FREE_MODEL, inProduction, type ValidModelName, ValidModelNameSchema, validModels } from '../../../config'
11-
import type { ChatMessage, MessageGenerationInfo, ToolCallResultEvent } from '../../../shared/chat'
12-
import { getLanguageValue } from '../../../shared/utils'
11+
import type { ChatMessage, MessageGenerationInfo, ToolCallResultEvent } from '@shared/chat'
12+
import { getLanguageValue } from '@shared/utils'
1313
import { useIsEmbedded } from '../../contexts/EmbeddedContext'
1414
import { useChatScroll } from './useChatScroll'
1515
import useCourse from '../../hooks/useCourse'

src/client/components/Courses/Course/Prompt.tsx

Lines changed: 44 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,40 @@
1-
import { useState } from 'react'
2-
import { Box, Paper, Typography, Button, Tooltip, TextField, Stack, FormControlLabel, Checkbox, IconButton, Link } from '@mui/material'
3-
import { ExpandLess, ExpandMore, Visibility, VisibilityOff, PriorityHigh, ContentCopyOutlined } from '@mui/icons-material'
1+
import { Box, Paper, Typography, Button, Tooltip, IconButton, Link } from '@mui/material'
2+
import { Visibility, VisibilityOff, PriorityHigh, ContentCopyOutlined, Delete, DeleteOutline, Book, BookOutlined, MenuBookOutlined } from '@mui/icons-material'
43
import { useTranslation } from 'react-i18next'
54

65
import { enqueueSnackbar } from 'notistack'
7-
import { Prompt as PromptType, SetState } from '../../../types'
8-
import { MessageItem } from '../../ChatV2/Conversation'
9-
import SystemMessage from '../../ChatV2/SystemMessage'
10-
import { useEditPromptMutation } from '../../../hooks/usePromptMutation'
6+
import type { Prompt as PromptType } from '../../../types'
7+
import { useDeletePromptMutation } from '../../../hooks/usePromptMutation'
118
import { useParams, Link as RouterLink } from 'react-router-dom'
129
import { IframeCopy } from '../../common/IframeCopy'
13-
import { PUBLIC_URL } from '../../../../config'
10+
import { PUBLIC_URL } from '@config'
11+
import { OutlineButtonBlack } from '../../ChatV2/general/Buttons'
1412

15-
const ExpandButton = ({ expand, setExpand }: { expand: boolean; setExpand: SetState<boolean> }) => (
16-
<Button onClick={() => setExpand(!expand)}>{expand ? <ExpandLess /> : <ExpandMore />}</Button>
17-
)
18-
19-
const Prompt = ({ prompt, handleDelete, mandatoryPromptId }: { prompt: PromptType; handleDelete: (promptId: string) => void; mandatoryPromptId?: string }) => {
13+
const Prompt = ({ prompt, handleEdit }: { prompt: PromptType; handleEdit: () => void }) => {
2014
const { t } = useTranslation()
2115
const { id: courseId } = useParams()
22-
const mutation = useEditPromptMutation()
2316

24-
const { id, name, systemMessage, messages, hidden, mandatory } = prompt
17+
const { id, name, hidden, mandatory, ragIndexId } = prompt
2518

26-
const [expand, setExpand] = useState(false)
27-
const [editPrompt, setEditPrompt] = useState(false)
28-
const [message, setMessage] = useState(systemMessage)
29-
const [updatedName, setUpdatedName] = useState(name)
30-
const [updatedHidden, setUpdatedHidden] = useState(hidden)
31-
const [updatedMandatory, setUpdatedMandatory] = useState(mandatory)
3219
const chatPath = `/${courseId}?promptId=${id}`
3320
const directLink = `${window.location.origin}${PUBLIC_URL}/${chatPath}`
3421

35-
const handleSave = async () => {
36-
const updatedPrompt = {
37-
...prompt,
38-
systemMessage: message,
39-
name: updatedName,
40-
hidden: updatedHidden,
41-
mandatory: updatedMandatory,
42-
}
22+
const deleteMutation = useDeletePromptMutation()
23+
24+
const handleDelete = (promptId: string) => {
25+
if (!window.confirm(t('confirmDeletePrompt') as string)) return
4326

4427
try {
45-
await mutation.mutateAsync(updatedPrompt)
46-
setEditPrompt(false)
47-
enqueueSnackbar('Prompt updated', { variant: 'success' })
28+
deleteMutation.mutate(promptId)
29+
enqueueSnackbar('Prompt deleted', { variant: 'success' })
4830
} catch (error: any) {
4931
enqueueSnackbar(error.message, { variant: 'error' })
5032
}
5133
}
5234

5335
return (
5436
<Box key={id} pt={2}>
55-
<Paper variant="outlined" sx={{ padding: '2%' }}>
37+
<Paper sx={{ py: '1rem', px: '2rem' }}>
5638
<Box display="flex" alignItems="center" justifyContent="space-between">
5739
<Box>
5840
<Box display="inline" mr={2}>
@@ -73,82 +55,40 @@ const Prompt = ({ prompt, handleDelete, mandatoryPromptId }: { prompt: PromptTyp
7355
</Tooltip>
7456
</Box>
7557
)}
76-
{!editPrompt ? (
77-
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
78-
<Typography variant="h6" display="inline">
79-
{name}
80-
</Typography>
81-
<Box sx={{ display: 'flex', alignItems: 'center' }}>
82-
<Link component={RouterLink} to={chatPath} variant="caption">
83-
{t('course:directPromptLink', { name: prompt.name })}
84-
</Link>
85-
<Tooltip title={t('course:copyDirectPromptLinkInfo', { name: prompt.name })}>
86-
<IconButton size="small" onClick={() => navigator.clipboard.writeText(directLink)}>
87-
<ContentCopyOutlined fontSize="small" />
88-
</IconButton>
89-
</Tooltip>
90-
<IframeCopy courseId={courseId!} promptId={prompt.id} />
91-
</Box>
58+
{ragIndexId && (
59+
<Box display="inline" mr={2}>
60+
<Tooltip title={t('rag:sourceMaterials')}>
61+
<MenuBookOutlined />
62+
</Tooltip>
9263
</Box>
93-
) : (
94-
<TextField defaultValue={updatedName} sx={{ width: '650px' }} onChange={(e) => setUpdatedName(e.target.value)} />
9564
)}
65+
66+
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
67+
<Typography variant="h6" display="inline">
68+
{name}
69+
</Typography>
70+
<Box sx={{ display: 'flex', alignItems: 'center' }}>
71+
<Link component={RouterLink} to={chatPath} variant="caption">
72+
{t('course:directPromptLink', { name: prompt.name })}
73+
</Link>
74+
<Tooltip title={t('course:copyDirectPromptLinkInfo', { name: prompt.name })}>
75+
<IconButton size="small" onClick={() => navigator.clipboard.writeText(directLink)}>
76+
<ContentCopyOutlined fontSize="small" />
77+
</IconButton>
78+
</Tooltip>
79+
<IframeCopy courseId={courseId!} promptId={prompt.id} />
80+
</Box>
81+
</Box>
9682
</Box>
97-
<Box>
98-
<Button onClick={() => handleDelete(prompt.id)} color="error" data-testid={`delete-prompt-${prompt.name}`}>
99-
{t('common:delete')}
100-
</Button>
101-
<ExpandButton expand={expand} setExpand={setExpand} />
83+
<Box sx={{ display: 'flex', alignItems: 'center' }}>
84+
<IconButton onClick={() => handleDelete(prompt.id)} color="error" data-testid={`delete-prompt-${prompt.name}`} aria-label={t('course:remove')}>
85+
<DeleteOutline />
86+
</IconButton>
87+
<OutlineButtonBlack onClick={handleEdit} color="primary" data-testid={`edit-prompt-${prompt.name}`} aria-label={t('common:edit')}>
88+
{t('common:edit')}
89+
</OutlineButtonBlack>
10290
</Box>
10391
</Box>
104-
105-
{expand && (
106-
<Box mt={2}>
107-
{!editPrompt ? (
108-
<>
109-
<Box width="80%">
110-
<SystemMessage system={systemMessage} setSystem={() => {}} showInfo={false} disabled />
111-
</Box>
112-
<Box>
113-
{messages.map((msg) => (
114-
<MessageItem key={msg.content} message={msg} setActiveToolResult={(_d) => {}} />
115-
))}
116-
</Box>
117-
</>
118-
) : (
119-
<>
120-
<TextField defaultValue={systemMessage} sx={{ width: '80%' }} multiline onChange={(e) => setMessage(e.target.value)} />
121-
{!mandatoryPromptId || mandatoryPromptId === prompt.id ? (
122-
<FormControlLabel
123-
control={<Checkbox checked={updatedMandatory} onChange={() => setUpdatedMandatory((prev) => !prev)} />}
124-
label="Tee alustuksesta pakollinen opiskelijoille"
125-
sx={{ mr: 5 }}
126-
/>
127-
) : (
128-
<Tooltip title="Kurssilla voi olla vain yksi pakollinen alustus">
129-
<FormControlLabel
130-
control={<Checkbox checked={updatedMandatory} disabled />}
131-
label="Tee alustuksesta pakollinen opiskelijoille"
132-
sx={{ mr: 5 }}
133-
/>
134-
</Tooltip>
135-
)}
136-
137-
<FormControlLabel control={<Checkbox checked={updatedHidden} onChange={() => setUpdatedHidden((prev) => !prev)} />} label={t('hidePrompt')} />
138-
</>
139-
)}
140-
<Stack direction="row" spacing={2} marginTop={2}>
141-
{editPrompt && (
142-
<Button onClick={handleSave} variant="outlined">
143-
{t('common:save')}
144-
</Button>
145-
)}
146-
<Button variant="outlined" onClick={() => setEditPrompt(!editPrompt)}>
147-
{editPrompt ? t('common:cancel') : t('common:edit')}
148-
</Button>
149-
</Stack>
150-
</Box>
151-
)}
15292
</Paper>
15393
</Box>
15494
)

0 commit comments

Comments
 (0)