feat: add temperature and top-p to ModelSetting and add ChatConfigurationModal and bind backend data to KnowledgeCard (#65)
* feat: bind backend data to KnowledgeCard * feat: add ChatConfigurationModal * feat: add temperature and top-p to ModelSetting
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { Form, Input } from 'antd';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { ISegmentedContentProps } from './interface';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
const AssistantSetting = ({ show }: ISegmentedContentProps) => {
|
||||
return (
|
||||
<section
|
||||
className={classNames({
|
||||
[styles.segmentedHidden]: !show,
|
||||
})}
|
||||
>
|
||||
<Form.Item
|
||||
name={'name'}
|
||||
label="Assistant name"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input placeholder="e.g. Resume Jarvis" />
|
||||
</Form.Item>
|
||||
<Form.Item name={'avatar'} label="Assistant avatar">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name={'keywords'} label="Keywords">
|
||||
<Input.TextArea autoSize={{ minRows: 3 }} />
|
||||
</Form.Item>
|
||||
<Form.Item name={'opener'} label="Set an opener">
|
||||
<Input.TextArea autoSize={{ minRows: 5 }} />
|
||||
</Form.Item>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default AssistantSetting;
|
||||
103
web/src/pages/chat/chat-configuration-modal/editable-cell.tsx
Normal file
103
web/src/pages/chat/chat-configuration-modal/editable-cell.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
import { Form, FormInstance, Input, InputRef } from 'antd';
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
|
||||
const EditableContext = React.createContext<FormInstance<any> | null>(null);
|
||||
|
||||
interface EditableRowProps {
|
||||
index: number;
|
||||
}
|
||||
|
||||
interface Item {
|
||||
key: string;
|
||||
name: string;
|
||||
age: string;
|
||||
address: string;
|
||||
}
|
||||
|
||||
export const EditableRow: React.FC<EditableRowProps> = ({
|
||||
index,
|
||||
...props
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
return (
|
||||
<Form form={form} component={false}>
|
||||
<EditableContext.Provider value={form}>
|
||||
<tr {...props} />
|
||||
</EditableContext.Provider>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
interface EditableCellProps {
|
||||
title: React.ReactNode;
|
||||
editable: boolean;
|
||||
children: React.ReactNode;
|
||||
dataIndex: keyof Item;
|
||||
record: Item;
|
||||
handleSave: (record: Item) => void;
|
||||
}
|
||||
|
||||
export const EditableCell: React.FC<EditableCellProps> = ({
|
||||
title,
|
||||
editable,
|
||||
children,
|
||||
dataIndex,
|
||||
record,
|
||||
handleSave,
|
||||
...restProps
|
||||
}) => {
|
||||
const [editing, setEditing] = useState(false);
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
const form = useContext(EditableContext)!;
|
||||
|
||||
useEffect(() => {
|
||||
if (editing) {
|
||||
inputRef.current!.focus();
|
||||
}
|
||||
}, [editing]);
|
||||
|
||||
const toggleEdit = () => {
|
||||
setEditing(!editing);
|
||||
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
|
||||
toggleEdit();
|
||||
handleSave({ ...record, ...values });
|
||||
} catch (errInfo) {
|
||||
console.log('Save failed:', errInfo);
|
||||
}
|
||||
};
|
||||
|
||||
let childNode = children;
|
||||
|
||||
if (editable) {
|
||||
childNode = editing ? (
|
||||
<Form.Item
|
||||
style={{ margin: 0 }}
|
||||
name={dataIndex}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `${title} is required.`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input ref={inputRef} onPressEnter={save} onBlur={save} />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div
|
||||
className="editable-cell-value-wrap"
|
||||
style={{ paddingRight: 24 }}
|
||||
onClick={toggleEdit}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <td {...restProps}>{childNode}</td>;
|
||||
};
|
||||
43
web/src/pages/chat/chat-configuration-modal/index.less
Normal file
43
web/src/pages/chat/chat-configuration-modal/index.less
Normal file
@@ -0,0 +1,43 @@
|
||||
.chatConfigurationDescription {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.variableContainer {
|
||||
padding-bottom: 20px;
|
||||
.variableAlign {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.variableLabel {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.variableTable {
|
||||
margin-top: 14px;
|
||||
}
|
||||
.editableRow {
|
||||
:global(.editable-cell) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:global(.editable-cell-value-wrap) {
|
||||
padding: 5px 12px;
|
||||
cursor: pointer;
|
||||
height: 22px !important;
|
||||
}
|
||||
&:hover {
|
||||
:global(.editable-cell-value-wrap) {
|
||||
padding: 4px 11px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.segmentedHidden {
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
width: 0;
|
||||
margin: 0;
|
||||
}
|
||||
107
web/src/pages/chat/chat-configuration-modal/index.tsx
Normal file
107
web/src/pages/chat/chat-configuration-modal/index.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import { ReactComponent as ChatConfigurationAtom } from '@/assets/svg/chat-configuration-atom.svg';
|
||||
import { IModalManagerChildrenProps } from '@/components/modal-manager';
|
||||
import { Divider, Flex, Form, Modal, Segmented } from 'antd';
|
||||
import { SegmentedValue } from 'antd/es/segmented';
|
||||
import { useState } from 'react';
|
||||
import AssistantSetting from './assistant-setting';
|
||||
import ModelSetting from './model-setting';
|
||||
import PromptEngine from './prompt-engine';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
enum ConfigurationSegmented {
|
||||
AssistantSetting = 'Assistant Setting',
|
||||
ModelSetting = 'Model Setting',
|
||||
PromptEngine = 'Prompt Engine',
|
||||
}
|
||||
|
||||
const segmentedMap = {
|
||||
[ConfigurationSegmented.AssistantSetting]: AssistantSetting,
|
||||
[ConfigurationSegmented.ModelSetting]: ModelSetting,
|
||||
[ConfigurationSegmented.PromptEngine]: PromptEngine,
|
||||
};
|
||||
|
||||
const layout = {
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 18 },
|
||||
};
|
||||
|
||||
const validateMessages = {
|
||||
required: '${label} is required!',
|
||||
types: {
|
||||
email: '${label} is not a valid email!',
|
||||
number: '${label} is not a valid number!',
|
||||
},
|
||||
number: {
|
||||
range: '${label} must be between ${min} and ${max}',
|
||||
},
|
||||
};
|
||||
|
||||
const ChatConfigurationModal = ({
|
||||
visible,
|
||||
hideModal,
|
||||
}: IModalManagerChildrenProps) => {
|
||||
const [form] = Form.useForm();
|
||||
const [value, setValue] = useState<ConfigurationSegmented>(
|
||||
ConfigurationSegmented.AssistantSetting,
|
||||
);
|
||||
|
||||
const handleOk = async () => {
|
||||
const x = await form.validateFields();
|
||||
console.info(x);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
hideModal();
|
||||
};
|
||||
|
||||
const handleSegmentedChange = (val: SegmentedValue) => {
|
||||
setValue(val as ConfigurationSegmented);
|
||||
};
|
||||
|
||||
const title = (
|
||||
<Flex gap={16}>
|
||||
<ChatConfigurationAtom></ChatConfigurationAtom>
|
||||
<div>
|
||||
<b>Chat Configuration</b>
|
||||
<div className={styles.chatConfigurationDescription}>
|
||||
Here, dress up a dedicated assistant for your special knowledge bases!
|
||||
💕
|
||||
</div>
|
||||
</div>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={title}
|
||||
width={688}
|
||||
open={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<Segmented
|
||||
size={'large'}
|
||||
value={value}
|
||||
onChange={handleSegmentedChange}
|
||||
options={Object.values(ConfigurationSegmented)}
|
||||
block
|
||||
/>
|
||||
<Divider></Divider>
|
||||
<Form
|
||||
{...layout}
|
||||
name="nest-messages"
|
||||
form={form}
|
||||
style={{ maxWidth: 600 }}
|
||||
validateMessages={validateMessages}
|
||||
colon={false}
|
||||
>
|
||||
{Object.entries(segmentedMap).map(([key, Element]) => (
|
||||
<Element key={key} show={key === value}></Element>
|
||||
))}
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatConfigurationModal;
|
||||
3
web/src/pages/chat/chat-configuration-modal/interface.ts
Normal file
3
web/src/pages/chat/chat-configuration-modal/interface.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface ISegmentedContentProps {
|
||||
show: boolean;
|
||||
}
|
||||
155
web/src/pages/chat/chat-configuration-modal/model-setting.tsx
Normal file
155
web/src/pages/chat/chat-configuration-modal/model-setting.tsx
Normal file
@@ -0,0 +1,155 @@
|
||||
import { Divider, Flex, Form, InputNumber, Select, Slider } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { ISegmentedContentProps } from './interface';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
const ModelSetting = ({ show }: ISegmentedContentProps) => {
|
||||
return (
|
||||
<section
|
||||
className={classNames({
|
||||
[styles.segmentedHidden]: !show,
|
||||
})}
|
||||
>
|
||||
<Form.Item
|
||||
label="Model"
|
||||
name="model"
|
||||
// rules={[{ required: true, message: 'Please input!' }]}
|
||||
>
|
||||
<Select />
|
||||
</Form.Item>
|
||||
<Divider></Divider>
|
||||
<Form.Item
|
||||
label="Parameters"
|
||||
name="parameters"
|
||||
// rules={[{ required: true, message: 'Please input!' }]}
|
||||
>
|
||||
<Select />
|
||||
</Form.Item>
|
||||
<Form.Item label="Temperature">
|
||||
<Flex gap={20}>
|
||||
<Flex flex={1}>
|
||||
<Form.Item
|
||||
name={['address', 'province']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Province is required' }]}
|
||||
>
|
||||
<Slider style={{ display: 'inline-block', width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<Form.Item
|
||||
name={['address', 'street']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Street is required' }]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{
|
||||
width: 50,
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
<Form.Item label="Top P">
|
||||
<Flex gap={20}>
|
||||
<Flex flex={1}>
|
||||
<Form.Item
|
||||
name={['address', 'province']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Province is required' }]}
|
||||
>
|
||||
<Slider style={{ display: 'inline-block', width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<Form.Item
|
||||
name={['address', 'street']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Street is required' }]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{
|
||||
width: 50,
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
<Form.Item label="Presence Penalty">
|
||||
<Flex gap={20}>
|
||||
<Flex flex={1}>
|
||||
<Form.Item
|
||||
name={['address', 'province']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Province is required' }]}
|
||||
>
|
||||
<Slider style={{ display: 'inline-block', width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<Form.Item
|
||||
name={['address', 'street']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Street is required' }]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{
|
||||
width: 50,
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
<Form.Item label="Frequency Penalty">
|
||||
<Flex gap={20}>
|
||||
<Flex flex={1}>
|
||||
<Form.Item
|
||||
name={['address', 'province']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Province is required' }]}
|
||||
>
|
||||
<Slider style={{ display: 'inline-block', width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<Form.Item
|
||||
name={['address', 'street']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Street is required' }]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{
|
||||
width: 50,
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
<Form.Item label="Max Tokens">
|
||||
<Flex gap={20}>
|
||||
<Flex flex={1}>
|
||||
<Form.Item
|
||||
name={['address', 'province']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Province is required' }]}
|
||||
>
|
||||
<Slider style={{ display: 'inline-block', width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<Form.Item
|
||||
name={['address', 'street']}
|
||||
noStyle
|
||||
rules={[{ required: true, message: 'Street is required' }]}
|
||||
>
|
||||
<InputNumber
|
||||
style={{
|
||||
width: 50,
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModelSetting;
|
||||
163
web/src/pages/chat/chat-configuration-modal/prompt-engine.tsx
Normal file
163
web/src/pages/chat/chat-configuration-modal/prompt-engine.tsx
Normal file
@@ -0,0 +1,163 @@
|
||||
import { DeleteOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Divider,
|
||||
Form,
|
||||
Input,
|
||||
Row,
|
||||
Select,
|
||||
Switch,
|
||||
Table,
|
||||
TableProps,
|
||||
} from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { EditableCell, EditableRow } from './editable-cell';
|
||||
import { ISegmentedContentProps } from './interface';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
interface DataType {
|
||||
key: string;
|
||||
optional: boolean;
|
||||
}
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
const PromptEngine = ({ show }: ISegmentedContentProps) => {
|
||||
const [dataSource, setDataSource] = useState<DataType[]>([]);
|
||||
|
||||
const components = {
|
||||
body: {
|
||||
row: EditableRow,
|
||||
cell: EditableCell,
|
||||
},
|
||||
};
|
||||
|
||||
const handleRemove = (key: string) => () => {
|
||||
const newData = dataSource.filter((item) => item.key !== key);
|
||||
setDataSource(newData);
|
||||
};
|
||||
|
||||
const handleSave = (row: DataType) => {
|
||||
const newData = [...dataSource];
|
||||
const index = newData.findIndex((item) => row.key === item.key);
|
||||
const item = newData[index];
|
||||
newData.splice(index, 1, {
|
||||
...item,
|
||||
...row,
|
||||
});
|
||||
setDataSource(newData);
|
||||
};
|
||||
|
||||
const columns: TableProps<DataType>['columns'] = [
|
||||
{
|
||||
title: 'key',
|
||||
dataIndex: 'variable',
|
||||
key: 'variable',
|
||||
onCell: (record: DataType) => ({
|
||||
record,
|
||||
editable: true,
|
||||
dataIndex: 'variable',
|
||||
title: 'key',
|
||||
handleSave,
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: 'optional',
|
||||
dataIndex: 'optional',
|
||||
key: 'optional',
|
||||
width: 40,
|
||||
align: 'center',
|
||||
render() {
|
||||
return <Switch size="small" />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'operation',
|
||||
dataIndex: 'operation',
|
||||
width: 30,
|
||||
key: 'operation',
|
||||
align: 'center',
|
||||
render(_, record) {
|
||||
return <DeleteOutlined onClick={handleRemove(record.key)} />;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const handleAdd = () => {
|
||||
setDataSource((state) => [
|
||||
...state,
|
||||
{
|
||||
key: uuid(),
|
||||
variable: '',
|
||||
optional: true,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
return (
|
||||
<section
|
||||
className={classNames({
|
||||
[styles.segmentedHidden]: !show,
|
||||
})}
|
||||
>
|
||||
<Form.Item
|
||||
label="Orchestrate"
|
||||
name="orchestrate"
|
||||
rules={[{ required: true, message: 'Please input!' }]}
|
||||
>
|
||||
<Input.TextArea autoSize={{ maxRows: 5, minRows: 5 }} />
|
||||
</Form.Item>
|
||||
<Divider></Divider>
|
||||
<section className={classNames(styles.variableContainer)}>
|
||||
<Row align={'middle'} justify="end">
|
||||
<Col span={6} className={styles.variableAlign}>
|
||||
<label className={styles.variableLabel}>Variables</label>
|
||||
</Col>
|
||||
<Col span={18} className={styles.variableAlign}>
|
||||
<Button size="small" onClick={handleAdd}>
|
||||
Add
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
{dataSource.length > 0 && (
|
||||
<Row>
|
||||
<Col span={6}></Col>
|
||||
<Col span={18}>
|
||||
<Table
|
||||
dataSource={dataSource}
|
||||
columns={columns}
|
||||
rowKey={'key'}
|
||||
className={styles.variableTable}
|
||||
components={components}
|
||||
rowClassName={() => styles.editableRow}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
</section>
|
||||
<Form.Item
|
||||
label="Select one context"
|
||||
name="context"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select your favourite colors!',
|
||||
type: 'array',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select mode="multiple" placeholder="Please select favourite colors">
|
||||
<Option value="red">Red</Option>
|
||||
<Option value="green">Green</Option>
|
||||
<Option value="blue">Blue</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default PromptEngine;
|
||||
3
web/src/pages/chat/chat-container/index.less
Normal file
3
web/src/pages/chat/chat-container/index.less
Normal file
@@ -0,0 +1,3 @@
|
||||
.chatContainer {
|
||||
padding: 0 24px 24px;
|
||||
}
|
||||
36
web/src/pages/chat/chat-container/index.tsx
Normal file
36
web/src/pages/chat/chat-container/index.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Button, Flex, Input } from 'antd';
|
||||
import { ChangeEventHandler, useState } from 'react';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
const ChatContainer = () => {
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
const handlePressEnter = () => {
|
||||
console.info(value);
|
||||
};
|
||||
|
||||
const handleInputChange: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
setValue(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex flex={1} className={styles.chatContainer} vertical>
|
||||
<Flex flex={1}>xx</Flex>
|
||||
<Input
|
||||
size="large"
|
||||
placeholder="Message Resume Assistant..."
|
||||
value={value}
|
||||
suffix={
|
||||
<Button type="primary" onClick={handlePressEnter}>
|
||||
Send
|
||||
</Button>
|
||||
}
|
||||
onPressEnter={handlePressEnter}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatContainer;
|
||||
25
web/src/pages/chat/index.less
Normal file
25
web/src/pages/chat/index.less
Normal file
@@ -0,0 +1,25 @@
|
||||
.chatWrapper {
|
||||
height: 100%;
|
||||
|
||||
.chatAppWrapper {
|
||||
width: 288px;
|
||||
padding: 26px;
|
||||
}
|
||||
.chatTitleWrapper {
|
||||
width: 220px;
|
||||
padding: 26px 0;
|
||||
}
|
||||
|
||||
.chatTitle {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.chatTitleContent {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,64 @@
|
||||
import { FormOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Divider, Flex, Space, Tag } from 'antd';
|
||||
import { useSelector } from 'umi';
|
||||
import ChatContainer from './chat-container';
|
||||
|
||||
import ModalManager from '@/components/modal-manager';
|
||||
import ChatConfigurationModal from './chat-configuration-modal';
|
||||
import styles from './index.less';
|
||||
|
||||
const Chat = () => {
|
||||
const { name } = useSelector((state: any) => state.chatModel);
|
||||
return <div>chat:{name} </div>;
|
||||
|
||||
return (
|
||||
<Flex className={styles.chatWrapper}>
|
||||
<Flex className={styles.chatAppWrapper}>
|
||||
<Flex flex={1} vertical>
|
||||
<ModalManager>
|
||||
{({ visible, showModal, hideModal }) => {
|
||||
return (
|
||||
<>
|
||||
<Button type="primary" onClick={() => showModal()}>
|
||||
Create an Assistant
|
||||
</Button>
|
||||
<ChatConfigurationModal
|
||||
visible={visible}
|
||||
showModal={showModal}
|
||||
hideModal={hideModal}
|
||||
></ChatConfigurationModal>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</ModalManager>
|
||||
|
||||
<Divider></Divider>
|
||||
<Card>
|
||||
<p>Card content</p>
|
||||
</Card>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Divider type={'vertical'} className={styles.divider}></Divider>
|
||||
<Flex className={styles.chatTitleWrapper}>
|
||||
<Flex flex={1} vertical>
|
||||
<Flex
|
||||
justify={'space-between'}
|
||||
align="center"
|
||||
className={styles.chatTitle}
|
||||
>
|
||||
<Space>
|
||||
<b>Chat</b>
|
||||
<Tag>25</Tag>
|
||||
</Space>
|
||||
<FormOutlined />
|
||||
</Flex>
|
||||
<Divider></Divider>
|
||||
<section className={styles.chatTitleContent}>today</section>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Divider type={'vertical'} className={styles.divider}></Divider>
|
||||
<ChatContainer></ChatContainer>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default Chat;
|
||||
|
||||
45
web/src/pages/chat/message-box.tsx
Normal file
45
web/src/pages/chat/message-box.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
// RCE CSS
|
||||
import { MessageList } from 'react-chat-elements';
|
||||
import 'react-chat-elements/dist/main.css';
|
||||
|
||||
const ChatBox = () => {
|
||||
return (
|
||||
<div style={{ width: 600 }}>
|
||||
{/* <MessageBox
|
||||
position={'left'}
|
||||
type={'photo'}
|
||||
text={'react.svg'}
|
||||
data={{
|
||||
uri: 'https://facebook.github.io/react/img/logo.svg',
|
||||
status: {
|
||||
click: false,
|
||||
loading: 0,
|
||||
},
|
||||
}}
|
||||
/> */}
|
||||
|
||||
<MessageList
|
||||
// referance={messageListReferance}
|
||||
className="message-list"
|
||||
lockable={true}
|
||||
toBottomHeight={'100%'}
|
||||
dataSource={[
|
||||
{
|
||||
position: 'right',
|
||||
type: 'text',
|
||||
text: 'Lorem ipsum dolor sit amet',
|
||||
date: new Date(),
|
||||
},
|
||||
{
|
||||
position: 'left',
|
||||
type: 'text',
|
||||
text: 'Lorem ipsum dolor sit amet',
|
||||
date: new Date(),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatBox;
|
||||
Reference in New Issue
Block a user