您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

page.tsx 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. "use client";
  2. import { useState, useEffect } from 'react'
  3. import PageTitle from '@/components/ui/PageTitle';
  4. import { Input, Typography, Select, Switch, Upload, Form, Button, Tag, Col, Row, Flex } from 'antd'
  5. import AltLayout from '@/components/layout/AltLayout';
  6. import { UploadOutlined } from '@ant-design/icons';
  7. import type { SelectProps } from 'antd';
  8. import TextArea from 'antd/es/input/TextArea';
  9. import { useQuery } from '@tanstack/react-query';
  10. import { getPersonaStyle } from '@/app/api/user/personaService';
  11. import type { RcFile } from 'antd/es/upload';
  12. import PhoneInput from 'react-phone-input-2';
  13. import 'react-phone-input-2/lib/style.css';
  14. const { Text, Title } = Typography
  15. const CreatePersona: React.FC = () => {
  16. const [name, setName] = useState<string | undefined>("")
  17. const [phone, setPhone] = useState('');
  18. const [options, setOptions] = useState<SelectProps[] | undefined>()
  19. const [imageUrl, setImageUrl] = useState<string | null>(null);
  20. const [fileList, setFileList] = useState<any[]>([]);
  21. const [enabled, setEnabled] = useState({
  22. isManualKnowledgeEntry: false,
  23. isUploadTextDocument: false,
  24. isImportWebLink: false,
  25. isDallEImageGen: false,
  26. isSOPAutoLearn: false
  27. });
  28. const { data: personaStyleList, error, isLoading } = useQuery({
  29. queryKey: ["personaStyle"],
  30. queryFn: () => getPersonaStyle()
  31. })
  32. useEffect(() => {
  33. const latestData: SelectProps[] | undefined = personaStyleList?.map(persona => {
  34. return {
  35. label: persona.name,
  36. value: persona.id
  37. }
  38. })
  39. setOptions(latestData)
  40. }, [personaStyleList])
  41. const handleBeforeUpload = (file: RcFile) => {
  42. const isImage = file.type.startsWith('image/');
  43. if (!isImage) {
  44. // message.error('Only image files are allowed!');
  45. }
  46. const isLt2M = file.size / 1024 / 1024 < 2;
  47. if (!isLt2M) {
  48. //message.error('Image must be smaller than 2MB!');
  49. }
  50. if (isImage && isLt2M) {
  51. const reader = new FileReader();
  52. reader.onload = () => setImageUrl(reader.result as string);
  53. reader.readAsDataURL(file);
  54. }
  55. // prevent automatic upload
  56. return false;
  57. };
  58. return (
  59. <AltLayout header={<PageTitle backButton={true}>Create Persona</PageTitle>}>
  60. <Row className='px-4'>
  61. <Col span={24}>
  62. <Form layout="vertical">
  63. <Form.Item label={<p className='font-bold'>Persona Name</p>}>
  64. <Input
  65. style={{ padding: 10 }}
  66. value={name}
  67. onChange={(e) => setName(e.target.value)}
  68. />
  69. </Form.Item>
  70. <Form.Item label={<p className='font-bold'>Avatar Upload</p>}>
  71. <Upload
  72. beforeUpload={handleBeforeUpload}
  73. fileList={fileList}
  74. onRemove={() => {
  75. setFileList([]);
  76. setImageUrl(null);
  77. }}
  78. onChange={({ fileList }) => setFileList(fileList)}
  79. maxCount={1}
  80. accept="image/*"
  81. listType="picture"
  82. >
  83. <button className='border border-gray-300 px-4 py-2 rounded hover:bg-gray-50'>
  84. <UploadOutlined /> Click to Upload
  85. </button>
  86. </Upload>
  87. {imageUrl && (
  88. <img
  89. src={imageUrl}
  90. alt="Uploaded Preview"
  91. className="mt-2 w-32 h-32 object-cover border rounded"
  92. />
  93. )}
  94. </Form.Item>
  95. <Form.Item label={<p className='font-bold'>Role / Department</p>}>
  96. <Input
  97. style={{ padding: 10 }}
  98. value={name}
  99. onChange={(e) => setName(e.target.value)}
  100. />
  101. </Form.Item>
  102. <Form.Item label={<p className='font-bold'>Description</p>}>
  103. <TextArea
  104. style={{ padding: 10 }}
  105. rows={6}
  106. value={name}
  107. onChange={(e) => setName(e.target.value)}
  108. />
  109. </Form.Item>
  110. <Form.Item label={<p className='font-bold'>Persona Style</p>}>
  111. <Select
  112. mode="multiple"
  113. allowClear
  114. style={{ width: '100%' }}
  115. placeholder="Please select"
  116. options={options}
  117. />
  118. </Form.Item>
  119. <Form.Item label={<p className='font-bold'>Greeting Message</p>}>
  120. <TextArea
  121. style={{ padding: 10 }}
  122. rows={3}
  123. value={name}
  124. onChange={(e) => setName(e.target.value)}
  125. />
  126. </Form.Item>
  127. <Form.Item label={<p className='font-bold'>Persona Training Center</p>}>
  128. <Flex vertical gap={10}>
  129. <Flex gap={10}>
  130. <Switch
  131. checked={enabled.isManualKnowledgeEntry}
  132. onChange={(checked) =>
  133. setEnabled({ ...enabled, isManualKnowledgeEntry: checked })
  134. }
  135. />
  136. <Text className='font-bold'>Manual Knowledge Entry</Text>
  137. </Flex>
  138. <Flex gap={10}>
  139. <Switch
  140. checked={enabled.isUploadTextDocument}
  141. onChange={(checked) =>
  142. setEnabled({ ...enabled, isUploadTextDocument: checked })
  143. }
  144. />
  145. <Text className='font-bold'>Upload Text Document</Text>
  146. </Flex>
  147. <Flex gap={10}>
  148. <Switch
  149. checked={enabled.isImportWebLink}
  150. onChange={(checked) =>
  151. setEnabled({ ...enabled, isImportWebLink: checked })
  152. }
  153. />
  154. <Text className="font-bold">
  155. Import from Web Link{' '}
  156. <Tag color="blue" className="font-semibold text-xs px-2 py-[2px] !rounded-full">
  157. Premium
  158. </Tag>
  159. </Text>
  160. </Flex>
  161. <Flex gap={10}>
  162. <Switch
  163. checked={enabled.isDallEImageGen}
  164. onChange={(checked) =>
  165. setEnabled({ ...enabled, isDallEImageGen: checked })
  166. }
  167. />
  168. <Text className="font-bold">
  169. DALL-E Image Generation{' '}
  170. <Tag color="blue" className="font-semibold text-xs px-2 py-[2px] !rounded-full">
  171. Premium
  172. </Tag>
  173. </Text>
  174. </Flex>
  175. <Flex gap={10}>
  176. <Switch
  177. checked={enabled.isSOPAutoLearn}
  178. onChange={(checked) =>
  179. setEnabled({ ...enabled, isSOPAutoLearn: checked })
  180. }
  181. />
  182. <Text className="font-bold">
  183. Enable SOP Auto-Learning{' '}
  184. <Tag color="blue" className="font-semibold text-xs px-2 py-[2px] !rounded-full">
  185. Premium
  186. </Tag>
  187. </Text>
  188. </Flex>
  189. </Flex>
  190. </Form.Item>
  191. <Form.Item label={<p className='font-bold'>Assign Whatsapp Line</p>}>
  192. <PhoneInput
  193. country={'my'} // default country
  194. value={phone}
  195. onChange={(phone, countryData) => {
  196. setPhone(phone);
  197. console.log("Phone Number:", phone);
  198. console.log("Country Info:", countryData);
  199. }}
  200. enableSearch
  201. preferredCountries={['my', 'sg', 'id']}
  202. inputStyle={{ width: '100%' }}
  203. containerStyle={{ width: '100%' }}
  204. />
  205. </Form.Item>
  206. <Row >
  207. <Col span={24} className='!text-center'>
  208. <Button type="primary" className='!w-fit !py-3 !text-xs !font-bold !px-10'>CREATE PERSONA</Button>
  209. </Col>
  210. </Row>
  211. </Form>
  212. </Col>
  213. </Row>
  214. </AltLayout>
  215. )
  216. }
  217. export default CreatePersona