Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

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