You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

page.tsx 11KB

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