Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

page.tsx 11KB

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